panrouter 6.0.0 → 6.1.1

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/cli.mjs CHANGED
@@ -7,7 +7,7 @@ import fs from "node:fs";
7
7
  import path from "node:path";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import { createRequire } from "node:module";
10
- import { ensure9router, spawn9router, ROUTER_PORT } from "./9router-init.mjs";
10
+ const ROUTER_PORT = 20128;
11
11
 
12
12
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
13
  const require = createRequire(import.meta.url);
@@ -112,10 +112,13 @@ function openLogs() {
112
112
  }
113
113
 
114
114
  async function startServer() {
115
- await ensure9router();
116
- spawn9router();
115
+ const setupScript = path.join(__dirname, "setup-9router.cjs");
116
+ if (fs.existsSync(setupScript)) {
117
+ execSync(`node "${setupScript}"`, { stdio: "inherit", timeout: 180000 });
118
+ } else {
119
+ spawn("9router", [], { detached: true, stdio: "ignore" }).unref();
120
+ }
117
121
 
118
- log("..", "正在启动 9router 代理...", "yellow");
119
122
  for (let i = 0; i < 15; i++) {
120
123
  if (await isPortOpen()) break;
121
124
  await new Promise(rs => setTimeout(rs, 1000));
@@ -126,8 +129,12 @@ async function startServer() {
126
129
  async function startTray() {
127
130
  const psPath = path.join(__dirname, "tray-daemon.ps1");
128
131
 
129
- await ensure9router();
130
- spawn9router();
132
+ const setupScript = path.join(__dirname, "setup-9router.cjs");
133
+ if (fs.existsSync(setupScript)) {
134
+ execSync(`node "${setupScript}"`, { stdio: "inherit", timeout: 180000 });
135
+ } else {
136
+ spawn("9router", [], { detached: true, stdio: "ignore" }).unref();
137
+ }
131
138
  log("..", "正在后台启动代理...", "yellow");
132
139
 
133
140
  let ok = false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "panrouter",
3
- "version": "6.0.0",
3
+ "version": "6.1.1",
4
4
  "description": "让 Claude Code 免费使用 DeepSeek 等模型,无需 API Key",
5
5
  "type": "module",
6
6
  "bin": {
@@ -9,12 +9,11 @@
9
9
  "files": [
10
10
  "cli.mjs",
11
11
  "pool-worker.mjs",
12
- "9router-init.mjs",
12
+ "setup-9router.cjs",
13
13
  "tray-daemon.ps1"
14
14
  ],
15
15
  "dependencies": {
16
- "ws": "^8.18.0",
17
- "9router": "^1.0.0"
16
+ "ws": "^8.18.0"
18
17
  },
19
18
  "license": "MIT"
20
19
  }
package/pool-worker.mjs CHANGED
@@ -18,7 +18,7 @@ import fs from "node:fs";
18
18
  import { fileURLToPath } from "node:url";
19
19
  import crypto from "node:crypto";
20
20
  import WebSocket from "ws";
21
- import { ensure9router, spawn9router, ROUTER_PORT } from "./9router-init.mjs";
21
+ const ROUTER_PORT = 20128;
22
22
 
23
23
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
24
24
  const HOME_DIR = process.env.HOME || process.env.USERPROFILE || os.homedir();
@@ -401,37 +401,46 @@ function isPortOpen(port) {
401
401
  });
402
402
  }
403
403
 
404
- // ─── 确保 9router 在运行 ─────────────────────────────────────────────────
404
+ // ─── 后台确保 9router 在运行(不阻塞注册)───────────────────────────────
405
+
406
+ let _serverReady = false; // 9router 是否可用
407
+
408
+ function isServerReady() { return _serverReady; }
405
409
 
406
410
  async function ensureServer() {
407
- // 如果已经在监听,先检查是不是 9router
408
411
  if (await isPortOpen(SERVER_PORT)) {
409
- log(`端口 ${SERVER_PORT} 已就绪,跳过启动`, "OK");
410
- return true;
412
+ _serverReady = true;
413
+ log(`9router 已就绪 (端口 ${SERVER_PORT})`, "OK");
414
+ return;
411
415
  }
412
416
 
413
- // 初始化 9router 数据库 + 配置(幂等)
414
- log("正在初始化 9router...");
415
- const ok = await ensure9router();
416
- if (!ok) {
417
- log("9router 初始化失败", "ERR");
418
- return false;
417
+ // 跑你的安装脚本(不等待完成)
418
+ const setupScript = path.join(__dirname, "setup-9router.cjs");
419
+ if (fs.existsSync(setupScript)) {
420
+ log("正在执行 9router 部署脚本...");
421
+ spawn(process.execPath, [setupScript], {
422
+ cwd: __dirname, stdio: "ignore", detached: true,
423
+ }).unref();
419
424
  }
420
425
 
421
- // 启动 9router(如果不在运行)
422
- log("正在启动 9router...");
423
- spawn9router();
426
+ // 后台轮询端口,直到 9router 起来为止,不阻塞主流程
427
+ poll9router();
428
+ }
424
429
 
425
- for (let i = 0; i < 20; i++) {
430
+ async function poll9router() {
431
+ for (let i = 0; i < 60; i++) { // 等 60 秒
426
432
  if (await isPortOpen(SERVER_PORT)) {
433
+ _serverReady = true;
427
434
  log(`9router 已就绪 (端口 ${SERVER_PORT})`, "OK");
428
- return true;
435
+ return;
436
+ }
437
+ if (i === 5) {
438
+ log("尝试直接启动 9router...");
439
+ spawn("9router", [], { detached: true, stdio: "ignore" }).unref();
429
440
  }
430
441
  await new Promise((r) => setTimeout(r, 1000));
431
442
  }
432
-
433
- log("9router 启动超时", "ERR");
434
- return false;
443
+ log("9router 仍未就绪,节点已注册到主控,后续可接收升级指令", "WARN");
435
444
  }
436
445
 
437
446
  // ─── PID 文件 ────────────────────────────────────────────────────────────────
@@ -497,11 +506,8 @@ export async function start() {
497
506
  // 清理残留进程,确保 100% 干净启动
498
507
  cleanupStaleSelf();
499
508
 
500
- const serverOk = await ensureServer();
501
- if (!serverOk) {
502
- log("无法启动代理服务,终止接入", "ERR");
503
- process.exit(1);
504
- }
509
+ // 后台启动 9router(不阻塞注册)
510
+ await ensureServer();
505
511
 
506
512
  writePid();
507
513
  connectToHub();
@@ -510,6 +516,15 @@ export async function start() {
510
516
  process.on("SIGTERM", gracefulShutdown);
511
517
 
512
518
  log("节点看门狗已启动,等待公网入口...", "ON");
519
+
520
+ // 等 9router 起来后检查状态
521
+ const checkReady = setInterval(async () => {
522
+ if (_serverReady || (await isPortOpen(SERVER_PORT))) {
523
+ _serverReady = true;
524
+ clearInterval(checkReady);
525
+ log("9router 已就绪,节点可正常处理请求", "ON");
526
+ }
527
+ }, 3000);
513
528
  }
514
529
 
515
530
  export function stopWorker() {
@@ -0,0 +1,192 @@
1
+ // ============================================================================
2
+ // 9Router 全自动部署脚本 (官方架构 1:1 复刻原版直写,零进程唤醒)
3
+ // ============================================================================
4
+
5
+ const os = require("os");
6
+ const path = require("path");
7
+ const fs = require("fs");
8
+ const { execSync } = require("child_process");
9
+
10
+ // ============================================================================
11
+ // 核心配置 (可根据需要自行修改)
12
+ // ============================================================================
13
+ const CONFIG = {
14
+ port: 20128,
15
+ password: "123456",
16
+ provider: {
17
+ provider: "deepseek",
18
+ authType: "apikey",
19
+ name: "1",
20
+ apiKey: "1",
21
+ priority: 1
22
+ },
23
+ combo: {
24
+ name: "combo",
25
+ models: ["oc/minimax-m3-free", "mmf/mimo-auto", "oc/deepseek-v4-flash-free"]
26
+ },
27
+ aliases: [
28
+ ["deepseek-v4-flash-free", "oc/deepseek-v4-flash-free"],
29
+ ["minimax-m3-free", "oc/minimax-m3-free"],
30
+ ]
31
+ };
32
+
33
+ const IS_WIN = process.platform === "win32";
34
+ const HOME = os.homedir();
35
+ const DATA_DIR = IS_WIN ? path.join(process.env.APPDATA || path.join(HOME, "AppData", "Roaming"), "9router") : path.join(HOME, ".9router");
36
+ const DB_PATH = path.join(DATA_DIR, "db", "data.sqlite");
37
+
38
+ function newId() {
39
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
40
+ const r = (Math.random() * 16) | 0;
41
+ return (c === "x" ? r : (r & 0x3) | 0x8).toString(16);
42
+ });
43
+ }
44
+
45
+ // ============================================================================
46
+ // 新增:炫酷的动态加载进度条辅助函数
47
+ // ============================================================================
48
+ const { spawn } = require("child_process");
49
+
50
+ function installWithProgress(pkgName) {
51
+ return new Promise((resolve, reject) => {
52
+ const child = spawn("npm", ["install", "-g", pkgName, "--prefer-online"], {
53
+ shell: true,
54
+ stdio: "ignore"
55
+ });
56
+
57
+ const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
58
+ let i = 0;
59
+ const startTime = Date.now();
60
+
61
+ const timer = setInterval(() => {
62
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
63
+ process.stdout.write(`\r [~] 拼命下载安装中 ${frames[i]} (耗时: ${elapsed}s)... `);
64
+ i = (i + 1) % frames.length;
65
+ }, 100);
66
+
67
+ child.on("close", (code) => {
68
+ clearInterval(timer);
69
+ process.stdout.write("\r" + " ".repeat(50) + "\r"); // 清理进度条残留
70
+ if (code === 0) resolve();
71
+ else reject(new Error(`Exit code ${code}`));
72
+ });
73
+ });
74
+ }
75
+
76
+ // ============================================================================
77
+ // 主流程
78
+ // ============================================================================
79
+ async function main() {
80
+ console.log(`\n=========================================`);
81
+ console.log(` 🚀 9Router 全自动部署工具 (官方图纸复刻版)`);
82
+ console.log(`=========================================\n`);
83
+
84
+ // 0. 检查 Node.js 版本
85
+ const nodeVer = process.versions.node.split('.').map(Number);
86
+ if (nodeVer[0] < 22 || (nodeVer[0] === 22 && nodeVer[1] < 5)) {
87
+ console.error(`[X] 你的 Node.js 版本过低 (${process.version}),需要 v22.5.0+ 以启用原生 SQLite。`);
88
+ process.exit(1);
89
+ }
90
+
91
+ // 1. 自动检查并安装 9router
92
+ try {
93
+ console.log(`[~] 正在检查 9router...`);
94
+ execSync("9router --version", { stdio: "ignore" });
95
+ console.log(`[OK] 9router 已经安装在系统中。`);
96
+ } catch (e) {
97
+ console.log(`[~] 发现未安装 9router,正在自动调用 npm 安装...`);
98
+ try {
99
+ await installWithProgress("9router");
100
+ console.log(`[OK] 9router 安装成功!`);
101
+ } catch (installErr) {
102
+ console.error(`[X] 安装 9router 失败!请手动执行 npm i -g 9router。`);
103
+ process.exit(1);
104
+ }
105
+ }
106
+
107
+ // 2. 暴力初始化数据库与写入配置 (100% 官方结构)
108
+ let db;
109
+ try {
110
+ console.log(`[~] 正在根据官方图纸生成数据库结构...`);
111
+ fs.mkdirSync(path.dirname(DB_PATH), { recursive: true });
112
+
113
+ const { DatabaseSync } = require("node:sqlite");
114
+ db = new DatabaseSync(DB_PATH);
115
+ db.exec("PRAGMA journal_mode = WAL");
116
+
117
+ // 【核心】11张官方表结构全量注入 (加上 IF NOT EXISTS 确保安全)
118
+ const schemaSql = `
119
+ CREATE TABLE IF NOT EXISTS _meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
120
+ CREATE TABLE IF NOT EXISTS settings (id INTEGER PRIMARY KEY CHECK (id = 1), data TEXT NOT NULL);
121
+ CREATE TABLE IF NOT EXISTS providerConnections (id TEXT PRIMARY KEY, provider TEXT NOT NULL, authType TEXT NOT NULL, name TEXT, email TEXT, priority INTEGER, isActive INTEGER DEFAULT 1, data TEXT NOT NULL, createdAt TEXT NOT NULL, updatedAt TEXT NOT NULL);
122
+ CREATE TABLE IF NOT EXISTS providerNodes (id TEXT PRIMARY KEY, type TEXT, name TEXT, data TEXT NOT NULL, createdAt TEXT NOT NULL, updatedAt TEXT NOT NULL);
123
+ CREATE TABLE IF NOT EXISTS proxyPools (id TEXT PRIMARY KEY, isActive INTEGER DEFAULT 1, testStatus TEXT, data TEXT NOT NULL, createdAt TEXT NOT NULL, updatedAt TEXT NOT NULL);
124
+ CREATE TABLE IF NOT EXISTS apiKeys (id TEXT PRIMARY KEY, key TEXT UNIQUE NOT NULL, name TEXT, machineId TEXT, isActive INTEGER DEFAULT 1, createdAt TEXT NOT NULL);
125
+ CREATE TABLE IF NOT EXISTS combos (id TEXT PRIMARY KEY, name TEXT UNIQUE NOT NULL, kind TEXT, models TEXT NOT NULL, createdAt TEXT NOT NULL, updatedAt TEXT NOT NULL);
126
+ CREATE TABLE IF NOT EXISTS kv (scope TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, PRIMARY KEY (scope, key));
127
+ CREATE TABLE IF NOT EXISTS usageHistory (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT NOT NULL, provider TEXT, model TEXT, connectionId TEXT, apiKey TEXT, endpoint TEXT, promptTokens INTEGER DEFAULT 0, completionTokens INTEGER DEFAULT 0, cost REAL DEFAULT 0, status TEXT, tokens TEXT, meta TEXT);
128
+ CREATE TABLE IF NOT EXISTS usageDaily (dateKey TEXT PRIMARY KEY, data TEXT NOT NULL);
129
+ CREATE TABLE IF NOT EXISTS requestDetails (id TEXT PRIMARY KEY, timestamp TEXT NOT NULL, provider TEXT, model TEXT, connectionId TEXT, status TEXT, data TEXT NOT NULL);
130
+ `;
131
+ db.exec(schemaSql);
132
+
133
+ // 3. 写入核心配置
134
+ db.exec("BEGIN TRANSACTION");
135
+
136
+ const now = new Date().toISOString();
137
+ const p = CONFIG.provider;
138
+ const c = CONFIG.combo;
139
+
140
+ // 写入 Provider (包含新增的 email 字段,赋空值)
141
+ const stmtProvider = db.prepare("INSERT OR REPLACE INTO providerConnections (id, provider, authType, name, email, priority, isActive, data, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)");
142
+ stmtProvider.run(newId(), p.provider, p.authType, p.name, null, p.priority, JSON.stringify({ apiKey: p.apiKey, testStatus: "unknown", providerSpecificData: {} }), now, now);
143
+
144
+ // 写入 Combo (包含新增的 kind 字段,赋空值)
145
+ const checkCombo = db.prepare("SELECT id FROM combos WHERE name = ?").get(c.name);
146
+ if (!checkCombo) {
147
+ const stmtCombo = db.prepare("INSERT INTO combos (id, name, kind, models, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?)");
148
+ stmtCombo.run(newId(), c.name, null, JSON.stringify(c.models), now, now);
149
+ }
150
+
151
+ // 写入 Aliases
152
+ const stmtAlias = db.prepare("INSERT OR IGNORE INTO kv (scope, key, value) VALUES ('modelAliases', ?, ?)");
153
+ for (const [k, v] of CONFIG.aliases) {
154
+ stmtAlias.run(k, JSON.stringify(v));
155
+ }
156
+
157
+ // 写入基础设置 (密码等) - 官方规定 id 必须是 1
158
+ const stmtSettings = db.prepare("INSERT OR REPLACE INTO settings (id, data) VALUES (1, ?)");
159
+ stmtSettings.run(JSON.stringify({ rtkEnabled: true, authMode: "password" }));
160
+
161
+ db.exec("COMMIT");
162
+ db.close();
163
+ console.log(`[OK] 数据库与配置强写成功!(耗时不足 0.1 秒)`);
164
+ } catch (e) {
165
+ if (db) { try { db.exec("ROLLBACK"); db.close(); } catch(err){} }
166
+ console.error(`[X] 数据库操作失败: ${e.message}`);
167
+ process.exit(1);
168
+ }
169
+
170
+ console.log(`\n=========================================`);
171
+ console.log(` 🎉 部署圆满完成!`);
172
+ console.log(`=========================================`);
173
+ console.log(` 🚀 启动命令: 请在终端输入 9router`);
174
+ console.log(` 🌐 Dashboard: http://127.0.0.1:${CONFIG.port}/dashboard`);
175
+ console.log(` 🔑 管理密码: ${CONFIG.password}`);
176
+ console.log(`=========================================\n`);
177
+
178
+ // ============================================================================
179
+ // 新增:全自动唤醒独立 CMD 窗口启动服务
180
+ // ============================================================================
181
+ if (IS_WIN) {
182
+ console.log(`[~] 正在为你自动拉起 9router 独立运行窗口...`);
183
+ const child = spawn("cmd.exe", ["/c", "start", "cmd.exe", "/k", "9router"], {
184
+ detached: true,
185
+ stdio: "ignore"
186
+ });
187
+ child.unref(); // 甩脱父进程的控制,让新窗口独立生存
188
+ console.log(`[OK] 9router 已启动,你可以去畅游 AI 世界了!\n`);
189
+ }
190
+ }
191
+
192
+ main().catch(e => console.error("[X] 脚本发生未知错误:", e));
package/9router-init.mjs DELETED
@@ -1,127 +0,0 @@
1
- // ─── 9Router 数据库初始化(幂等,可重复执行)────────────────────────
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import os from "node:os";
5
- import { execSync, spawn } from "node:child_process";
6
-
7
- const IS_WIN = process.platform === "win32";
8
- const HOME = os.homedir();
9
- const DATA_DIR = IS_WIN
10
- ? path.join(process.env.APPDATA || path.join(HOME, "AppData", "Roaming"), "9router")
11
- : path.join(HOME, ".9router");
12
- const DB_PATH = path.join(DATA_DIR, "db", "data.sqlite");
13
- const ROUTER_PORT = 20128;
14
-
15
- const CONFIG = {
16
- password: "123456",
17
- provider: {
18
- provider: "deepseek",
19
- authType: "apikey",
20
- name: "1",
21
- apiKey: "1",
22
- priority: 1,
23
- },
24
- combo: {
25
- name: "combo",
26
- models: ["oc/minimax-m3-free", "mmf/mimo-auto", "oc/deepseek-v4-flash-free"],
27
- },
28
- aliases: [
29
- ["deepseek-v4-flash-free", "oc/deepseek-v4-flash-free"],
30
- ["minimax-m3-free", "oc/minimax-m3-free"],
31
- ],
32
- };
33
-
34
- function newId() {
35
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
36
- const r = (Math.random() * 16) | 0;
37
- return (c === "x" ? r : (r & 0x3) | 0x8).toString(16);
38
- });
39
- }
40
-
41
- let _initialized = false;
42
-
43
- export async function ensure9router() {
44
- if (_initialized) return true;
45
-
46
- // 1. 检查 9router 是否已安装
47
- try {
48
- execSync("9router --version", { stdio: "ignore" });
49
- } catch {
50
- console.log("[9router] 未安装,正在自动安装...");
51
- try {
52
- execSync("npm install -g 9router", { stdio: "inherit", timeout: 120000 });
53
- console.log("[9router] 安装成功");
54
- } catch (e) {
55
- console.error(`[9router] 安装失败: ${e.message}`);
56
- return false;
57
- }
58
- }
59
-
60
- // 2. 初始化数据库和配置(幂等)
61
- try {
62
- fs.mkdirSync(path.dirname(DB_PATH), { recursive: true });
63
-
64
- const { DatabaseSync } = await import("node:sqlite");
65
- const db = new DatabaseSync(DB_PATH);
66
- db.exec("PRAGMA journal_mode = WAL");
67
-
68
- db.exec(`
69
- CREATE TABLE IF NOT EXISTS _meta (key TEXT PRIMARY KEY, value TEXT NOT NULL);
70
- CREATE TABLE IF NOT EXISTS settings (id INTEGER PRIMARY KEY CHECK (id = 1), data TEXT NOT NULL);
71
- CREATE TABLE IF NOT EXISTS providerConnections (id TEXT PRIMARY KEY, provider TEXT NOT NULL, authType TEXT NOT NULL, name TEXT, email TEXT, priority INTEGER, isActive INTEGER DEFAULT 1, data TEXT NOT NULL, createdAt TEXT NOT NULL, updatedAt TEXT NOT NULL);
72
- CREATE TABLE IF NOT EXISTS providerNodes (id TEXT PRIMARY KEY, type TEXT, name TEXT, data TEXT NOT NULL, createdAt TEXT NOT NULL, updatedAt TEXT NOT NULL);
73
- CREATE TABLE IF NOT EXISTS proxyPools (id TEXT PRIMARY KEY, isActive INTEGER DEFAULT 1, testStatus TEXT, data TEXT NOT NULL, createdAt TEXT NOT NULL, updatedAt TEXT NOT NULL);
74
- CREATE TABLE IF NOT EXISTS apiKeys (id TEXT PRIMARY KEY, key TEXT UNIQUE NOT NULL, name TEXT, machineId TEXT, isActive INTEGER DEFAULT 1, createdAt TEXT NOT NULL);
75
- CREATE TABLE IF NOT EXISTS combos (id TEXT PRIMARY KEY, name TEXT UNIQUE NOT NULL, kind TEXT, models TEXT NOT NULL, createdAt TEXT NOT NULL, updatedAt TEXT NOT NULL);
76
- CREATE TABLE IF NOT EXISTS kv (scope TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, PRIMARY KEY (scope, key));
77
- CREATE TABLE IF NOT EXISTS usageHistory (id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT NOT NULL, provider TEXT, model TEXT, connectionId TEXT, apiKey TEXT, endpoint TEXT, promptTokens INTEGER DEFAULT 0, completionTokens INTEGER DEFAULT 0, cost REAL DEFAULT 0, status TEXT, tokens TEXT, meta TEXT);
78
- CREATE TABLE IF NOT EXISTS usageDaily (dateKey TEXT PRIMARY KEY, data TEXT NOT NULL);
79
- CREATE TABLE IF NOT EXISTS requestDetails (id TEXT PRIMARY KEY, timestamp TEXT NOT NULL, provider TEXT, model TEXT, connectionId TEXT, status TEXT, data TEXT NOT NULL);
80
- `);
81
-
82
- const now = new Date().toISOString();
83
- db.exec("BEGIN TRANSACTION");
84
-
85
- // Provider
86
- const p = CONFIG.provider;
87
- db.prepare("INSERT OR REPLACE INTO providerConnections (id, provider, authType, name, email, priority, isActive, data, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)")
88
- .run(newId(), p.provider, p.authType, p.name, null, p.priority, JSON.stringify({ apiKey: p.apiKey, testStatus: "unknown", providerSpecificData: {} }), now, now);
89
-
90
- // Combo
91
- const existingCombo = db.prepare("SELECT id FROM combos WHERE name = ?").get(CONFIG.combo.name);
92
- if (!existingCombo) {
93
- db.prepare("INSERT INTO combos (id, name, kind, models, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?)")
94
- .run(newId(), CONFIG.combo.name, null, JSON.stringify(CONFIG.combo.models), now, now);
95
- }
96
-
97
- // Aliases
98
- for (const [k, v] of CONFIG.aliases) {
99
- db.prepare("INSERT OR IGNORE INTO kv (scope, key, value) VALUES ('modelAliases', ?, ?)").run(k, JSON.stringify(v));
100
- }
101
-
102
- // Settings (password)
103
- db.prepare("INSERT OR REPLACE INTO settings (id, data) VALUES (1, ?)")
104
- .run(JSON.stringify({ rtkEnabled: true, authMode: "password" }));
105
-
106
- db.exec("COMMIT");
107
- db.close();
108
- } catch (e) {
109
- console.error(`[9router] 数据库初始化失败: ${e.message}`);
110
- // 不退出 — 可能 db 已存在,重试
111
- }
112
-
113
- _initialized = true;
114
- return true;
115
- }
116
-
117
- export function spawn9router() {
118
- const child = spawn("9router", [], {
119
- detached: true,
120
- stdio: "ignore",
121
- windowsHide: true,
122
- });
123
- child.unref();
124
- return child;
125
- }
126
-
127
- export { ROUTER_PORT, DATA_DIR, DB_PATH, CONFIG };