llm-simple-router 0.7.0 → 0.7.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/README.md +69 -0
- package/dist/admin/routes.d.ts +1 -0
- package/dist/admin/routes.js +1 -1
- package/dist/admin/upgrade.d.ts +1 -0
- package/dist/admin/upgrade.js +36 -3
- package/dist/index.js +6 -1
- package/dist/upgrade/deployment.d.ts +13 -0
- package/dist/upgrade/deployment.js +40 -0
- package/frontend-dist/assets/{CardContent-B_EIvwon.js → CardContent-CxOF1feY.js} +1 -1
- package/frontend-dist/assets/{CardTitle-DHU-obrV.js → CardTitle-BSEFcEOM.js} +1 -1
- package/frontend-dist/assets/{CascadingModelSelect-BFbt3-xP.js → CascadingModelSelect-DTwksDPZ.js} +1 -1
- package/frontend-dist/assets/{Checkbox-CNGgDj55.js → Checkbox-RfsERG07.js} +1 -1
- package/frontend-dist/assets/{CollapsibleTrigger-BIBFqVyy.js → CollapsibleTrigger-Dsjo7QlC.js} +1 -1
- package/frontend-dist/assets/{Collection-CYT_tSZn.js → Collection-rQ4eIYfa.js} +1 -1
- package/frontend-dist/assets/{Dashboard-DHzoPggK.js → Dashboard-YejfAPiB.js} +1 -1
- package/frontend-dist/assets/{DialogTitle-C1gk4GN_.js → DialogTitle-DeFTnmgC.js} +1 -1
- package/frontend-dist/assets/{Input-CXpbIxXJ.js → Input-CENz_g9t.js} +1 -1
- package/frontend-dist/assets/{Label-BbOaRyGK.js → Label-BAciBrrd.js} +1 -1
- package/frontend-dist/assets/{Login-Cc2ocWjt.js → Login-DQkYFq7R.js} +1 -1
- package/frontend-dist/assets/{Logs-DMMr-_-8.js → Logs-Dol8AX7z.js} +1 -1
- package/frontend-dist/assets/{ModelMappings-Cg8svYGw.js → ModelMappings-VEYW1TrW.js} +1 -1
- package/frontend-dist/assets/{Monitor-ps-VVzq3.js → Monitor-C0r9WefB.js} +1 -1
- package/frontend-dist/assets/{PopoverTrigger-BA1XOIO0.js → PopoverTrigger-Cyqik5SE.js} +1 -1
- package/frontend-dist/assets/{PopperContent--3HCLUEp.js → PopperContent-B7IuAHeq.js} +1 -1
- package/frontend-dist/assets/{Providers-Bjhhw2O4.js → Providers-D8Z97edN.js} +1 -1
- package/frontend-dist/assets/{ProxyEnhancement-BhlZ3pYo.js → ProxyEnhancement-Kn8r2SN6.js} +1 -1
- package/frontend-dist/assets/{RetryRules-BxCAMxNM.js → RetryRules-F0295m4_.js} +1 -1
- package/frontend-dist/assets/{RouterKeys-N-5AHCZf.js → RouterKeys-CFbPtUE_.js} +1 -1
- package/frontend-dist/assets/{RovingFocusItem-BXHXOE-y.js → RovingFocusItem-D291Vjh8.js} +1 -1
- package/frontend-dist/assets/{Schedules-oxUdLakf.js → Schedules-DWhF3uod.js} +1 -1
- package/frontend-dist/assets/{SelectValue-RQZKO7OV.js → SelectValue-BWlgUZa3.js} +1 -1
- package/frontend-dist/assets/{Settings-BlIRQ9y-.js → Settings-BnIzEF_k.js} +1 -1
- package/frontend-dist/assets/{Setup-CmpQtMiu.js → Setup-BglKyQKq.js} +1 -1
- package/frontend-dist/assets/{Switch-B3sVDDC7.js → Switch-DyCR-CPu.js} +1 -1
- package/frontend-dist/assets/{TableHeader-Bpen86Ix.js → TableHeader-DVUlBL35.js} +1 -1
- package/frontend-dist/assets/{TabsTrigger-VNURbZR2.js → TabsTrigger-BU1DY-C8.js} +1 -1
- package/frontend-dist/assets/{Teleport-CsHgwC1b.js → Teleport-BQgusr9g.js} +1 -1
- package/frontend-dist/assets/{TooltipTrigger-BHwxX9h-.js → TooltipTrigger-Bv_QoBns.js} +1 -1
- package/frontend-dist/assets/{UnifiedRequestDialog-3rdh4fhf.js → UnifiedRequestDialog-f_evI835.js} +1 -1
- package/frontend-dist/assets/{VisuallyHidden-DCZ9DkoD.js → VisuallyHidden-Con10z4F.js} +1 -1
- package/frontend-dist/assets/{VisuallyHiddenInput-BxgnyBWj.js → VisuallyHiddenInput-yrDtxucb.js} +1 -1
- package/frontend-dist/assets/{alert-dialog-LaLkz7c6.js → alert-dialog-2Db6Z7JQ.js} +1 -1
- package/frontend-dist/assets/arrow-down-WyouvE7T.js +1 -0
- package/frontend-dist/assets/{badge-BYEw6ni_.js → badge-DEhZfeI0.js} +1 -1
- package/frontend-dist/assets/button-Cnkbp_6J.js +12 -0
- package/frontend-dist/assets/check-BuqB5Nyb.js +1 -0
- package/frontend-dist/assets/{copy-CmWRpuG-.js → copy-CwqZSuIG.js} +1 -1
- package/frontend-dist/assets/{dialog-BEKceMtO.js → dialog-CVMKSdPr.js} +1 -1
- package/frontend-dist/assets/{file-text-C_jIlYPS.js → file-text-D0K8Hovo.js} +1 -1
- package/frontend-dist/assets/index-Ct718O93.js +1 -0
- package/frontend-dist/assets/{lib-Cj3cGvin.js → lib-H3YI7EK4.js} +1 -1
- package/frontend-dist/assets/loader-circle-Be82FnVY.js +1 -0
- package/frontend-dist/assets/{useClipboard-ZCpnVKiU.js → useClipboard-Cd7k-5Yq.js} +1 -1
- package/frontend-dist/assets/{useFocusGuards-BjqTo_uk.js → useFocusGuards-luoLXnwV.js} +1 -1
- package/frontend-dist/assets/useFormControl-Da4ViGZF.js +1 -0
- package/frontend-dist/assets/{useLogRetention-BZZvG6jh.js → useLogRetention-DB4Iu6o_.js} +1 -1
- package/frontend-dist/assets/useNonce-DvAdQ48J.js +1 -0
- package/frontend-dist/assets/x-DB22csQl.js +1 -0
- package/frontend-dist/index.html +19 -19
- package/package.json +1 -1
- package/frontend-dist/assets/arrow-down-BpGORRs9.js +0 -1
- package/frontend-dist/assets/button-C5VcfpgV.js +0 -12
- package/frontend-dist/assets/check-Bqx_fTQX.js +0 -1
- package/frontend-dist/assets/index-C2aljBfM.js +0 -1
- package/frontend-dist/assets/loader-circle-BLhRyZzy.js +0 -1
- package/frontend-dist/assets/useFormControl-D7vjbPSC.js +0 -1
- package/frontend-dist/assets/useNonce-BuusARRu.js +0 -1
- package/frontend-dist/assets/x-CUQFnWz8.js +0 -1
package/README.md
CHANGED
|
@@ -170,6 +170,75 @@ docker compose up -d
|
|
|
170
170
|
|
|
171
171
|
环境变量通过 Setup 页面设置,不需要 `.env` 文件。
|
|
172
172
|
|
|
173
|
+
## 进程管理
|
|
174
|
+
|
|
175
|
+
通过 Web UI 一键升级后,服务需要重启才能生效。推荐使用以下方式部署,确保进程崩溃或升级重启后自动恢复。
|
|
176
|
+
|
|
177
|
+
### PM2(推荐)
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
# 安装 PM2
|
|
181
|
+
npm install -g pm2
|
|
182
|
+
|
|
183
|
+
# 全局安装 Router
|
|
184
|
+
npm install -g llm-simple-router
|
|
185
|
+
|
|
186
|
+
# 启动(PM2 自动重启崩溃的进程)
|
|
187
|
+
pm2 start llm-simple-router --name llm-router
|
|
188
|
+
|
|
189
|
+
# 查看日志
|
|
190
|
+
pm2 logs llm-router
|
|
191
|
+
|
|
192
|
+
# 设置开机自启
|
|
193
|
+
pm2 startup
|
|
194
|
+
pm2 save
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
升级流程:Web UI 一键升级 → 点击重启 → PM2 自动拉起新进程(< 1s 中断)。
|
|
198
|
+
|
|
199
|
+
### systemd(Linux 服务器)
|
|
200
|
+
|
|
201
|
+
创建服务文件 `/etc/systemd/system/llm-simple-router.service`:
|
|
202
|
+
|
|
203
|
+
```ini
|
|
204
|
+
[Unit]
|
|
205
|
+
Description=LLM Simple Router
|
|
206
|
+
After=network.target
|
|
207
|
+
|
|
208
|
+
[Service]
|
|
209
|
+
Type=simple
|
|
210
|
+
ExecStart=/usr/local/bin/llm-simple-router
|
|
211
|
+
Restart=always
|
|
212
|
+
RestartSec=3
|
|
213
|
+
Environment=PORT=9981
|
|
214
|
+
Environment=LOG_LEVEL=info
|
|
215
|
+
# 按需配置其他环境变量
|
|
216
|
+
# Environment=DB_PATH=/var/lib/llm-simple-router/router.db
|
|
217
|
+
|
|
218
|
+
[Install]
|
|
219
|
+
WantedBy=multi-user.target
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
> **注意**:`ExecStart` 路径取决于 Node.js 安装方式。用 `which llm-simple-router` 确认实际路径。
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
# 启用并启动
|
|
226
|
+
sudo systemctl enable llm-simple-router
|
|
227
|
+
sudo systemctl start llm-simple-router
|
|
228
|
+
|
|
229
|
+
# 查看状态和日志
|
|
230
|
+
sudo systemctl status llm-simple-router
|
|
231
|
+
journalctl -u llm-simple-router -f
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
升级流程:Web UI 一键升级 → 点击重启 → systemd 自动重启(< 1s 中断)。
|
|
235
|
+
|
|
236
|
+
### npx / 手动启动
|
|
237
|
+
|
|
238
|
+
无需额外配置。Web UI 升级并点击重启后,Router 会自动 spawn 新进程并退出旧进程。短暂中断约 1-2 秒。
|
|
239
|
+
|
|
240
|
+
> **注意**:如果直接 `Ctrl+C` 或终端关闭,服务不会自动恢复。建议生产环境使用 PM2 或 systemd。
|
|
241
|
+
|
|
173
242
|
## 工作原理
|
|
174
243
|
|
|
175
244
|
```
|
package/dist/admin/routes.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ interface AdminRoutesOptions {
|
|
|
10
10
|
adaptiveController?: AdaptiveConcurrencyController;
|
|
11
11
|
logFileWriter?: import("../storage/log-file-writer.js").LogFileWriter | null;
|
|
12
12
|
logsDir?: string;
|
|
13
|
+
closeFn?: () => Promise<void>;
|
|
13
14
|
}
|
|
14
15
|
export declare const adminRoutes: FastifyPluginCallback<AdminRoutesOptions>;
|
|
15
16
|
export {};
|
package/dist/admin/routes.js
CHANGED
|
@@ -36,6 +36,6 @@ export const adminRoutes = (app, options, done) => {
|
|
|
36
36
|
app.register(adminImportExportRoutes, { db: options.db, stateRegistry: options.stateRegistry });
|
|
37
37
|
app.register(adminRecommendedRoutes, { db: options.db });
|
|
38
38
|
app.register(adminUsageRoutes, { db: options.db });
|
|
39
|
-
app.register(adminUpgradeRoutes, { db: options.db });
|
|
39
|
+
app.register(adminUpgradeRoutes, { db: options.db, closeFn: options.closeFn ?? (async () => { }) });
|
|
40
40
|
done();
|
|
41
41
|
};
|
package/dist/admin/upgrade.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import Database from 'better-sqlite3';
|
|
|
3
3
|
import { CheckerOptions } from '../upgrade/checker.js';
|
|
4
4
|
interface UpgradeRoutesOptions {
|
|
5
5
|
db: Database.Database;
|
|
6
|
+
closeFn: () => Promise<void>;
|
|
6
7
|
}
|
|
7
8
|
export declare function startUpgradeChecker(opts?: CheckerOptions): {
|
|
8
9
|
check: (sourceOverride?: string) => Promise<void>;
|
package/dist/admin/upgrade.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getConfigSyncSource, setConfigSyncSource } from '../db/settings.js';
|
|
2
|
-
import { detectDeployment } from '../upgrade/deployment.js';
|
|
2
|
+
import { detectDeployment, hasProcessManager, resolveRestartBinPath, getRestartMethod } from '../upgrade/deployment.js';
|
|
3
3
|
import { createUpgradeChecker, fetchJson } from '../upgrade/checker.js';
|
|
4
4
|
import { reloadConfig } from '../config/recommended.js';
|
|
5
|
-
import { execSync } from 'node:child_process';
|
|
5
|
+
import { execSync, spawn } from 'node:child_process';
|
|
6
6
|
import fs from 'node:fs';
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
import { HTTP_BAD_REQUEST, HTTP_INTERNAL_ERROR } from '../core/constants.js';
|
|
@@ -38,7 +38,8 @@ export const adminUpgradeRoutes = (app, options, done) => {
|
|
|
38
38
|
const c = checker ?? createUpgradeChecker();
|
|
39
39
|
const deployment = detectDeployment();
|
|
40
40
|
const syncSource = getConfigSyncSource(db);
|
|
41
|
-
|
|
41
|
+
const restartMethod = getRestartMethod();
|
|
42
|
+
return reply.send({ ...c.getStatus(), deployment, syncSource, restartMethod });
|
|
42
43
|
});
|
|
43
44
|
app.post('/admin/api/upgrade/check', async (_req, reply) => {
|
|
44
45
|
const c = checker ?? createUpgradeChecker();
|
|
@@ -78,6 +79,38 @@ export const adminUpgradeRoutes = (app, options, done) => {
|
|
|
78
79
|
return reply.code(HTTP_INTERNAL_ERROR).send(apiError(API_CODE.INTERNAL_ERROR, `升级失败: ${msg}`));
|
|
79
80
|
}
|
|
80
81
|
});
|
|
82
|
+
app.post('/admin/api/upgrade/restart', async (req, reply) => {
|
|
83
|
+
const managed = hasProcessManager();
|
|
84
|
+
const method = getRestartMethod();
|
|
85
|
+
// 先回复客户端,再执行重启(否则客户端收不到响应)
|
|
86
|
+
reply.send({ ok: true, method });
|
|
87
|
+
// 给响应发送窗口
|
|
88
|
+
await new Promise((resolve) => setTimeout(resolve, 300)); // eslint-disable-line no-magic-numbers
|
|
89
|
+
try {
|
|
90
|
+
req.log.info({ method, managed }, 'Restarting server...');
|
|
91
|
+
// 优雅关闭(释放端口、等待活跃请求完成)
|
|
92
|
+
await options.closeFn();
|
|
93
|
+
if (!managed) {
|
|
94
|
+
// 无进程管理器(npx / 手动 node):自 spawn 新进程
|
|
95
|
+
const binPath = resolveRestartBinPath();
|
|
96
|
+
const args = process.argv.slice(2); // eslint-disable-line no-magic-numbers
|
|
97
|
+
req.log.info({ binPath, args }, 'Spawning new process before exit');
|
|
98
|
+
const child = spawn(binPath, args, {
|
|
99
|
+
detached: true,
|
|
100
|
+
stdio: 'ignore',
|
|
101
|
+
env: { ...process.env },
|
|
102
|
+
});
|
|
103
|
+
child.unref();
|
|
104
|
+
}
|
|
105
|
+
req.log.info('Exiting current process');
|
|
106
|
+
process.exit(0);
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
// 重启失败时记录错误,保持服务运行
|
|
110
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
111
|
+
req.log.error({ err }, `Restart failed: ${msg}`);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
81
114
|
app.post('/admin/api/upgrade/sync-config', async (req, reply) => {
|
|
82
115
|
const { source } = req.body;
|
|
83
116
|
if (source !== 'github' && source !== 'gitee') {
|
package/dist/index.js
CHANGED
|
@@ -241,7 +241,10 @@ export async function buildApp(options) {
|
|
|
241
241
|
initializeProviderState(db, semaphoreManager, adaptiveController, tracker);
|
|
242
242
|
},
|
|
243
243
|
};
|
|
244
|
-
|
|
244
|
+
// Late-bound close ref — close 函数在 adminRoutes 注册之后才定义,
|
|
245
|
+
// 但 restart API 需要在运行时调用它
|
|
246
|
+
const closeRef = { fn: async () => { } };
|
|
247
|
+
app.register(adminRoutes, { db, stateRegistry, tracker, adaptiveController, logFileWriter, logsDir, closeFn: () => closeRef.fn() });
|
|
245
248
|
// 前端静态文件服务(生产环境)
|
|
246
249
|
const frontendDist = path.resolve(process.env.FRONTEND_DIST || path.join(__dirname, "../frontend-dist"));
|
|
247
250
|
if (existsSync(frontendDist)) {
|
|
@@ -294,6 +297,8 @@ export async function buildApp(options) {
|
|
|
294
297
|
await prevClose();
|
|
295
298
|
};
|
|
296
299
|
}
|
|
300
|
+
// 将最终版 close 函数绑定到 late-bound ref(供 restart API 运行时调用)
|
|
301
|
+
closeRef.fn = close;
|
|
297
302
|
return {
|
|
298
303
|
app,
|
|
299
304
|
db,
|
|
@@ -1,2 +1,15 @@
|
|
|
1
1
|
export type DeploymentType = 'npm' | 'docker' | 'unknown';
|
|
2
|
+
export type RestartMethod = 'process_manager' | 'self_spawn';
|
|
2
3
|
export declare function detectDeployment(): DeploymentType;
|
|
4
|
+
/**
|
|
5
|
+
* 检测是否有外部进程管理器(PM2 / systemd / Docker)。
|
|
6
|
+
* 有管理器时 process.exit(0) 后会自动重启;没有时需要自 spawn。
|
|
7
|
+
*/
|
|
8
|
+
export declare function hasProcessManager(): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* 解析重启时应该使用的可执行文件路径。
|
|
11
|
+
* npx 启动时 process.argv[1] 指向 npm cache 中的旧路径,
|
|
12
|
+
* 升级后全局 bin 已指向新版本,需要重新解析。
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveRestartBinPath(): string;
|
|
15
|
+
export declare function getRestartMethod(): RestartMethod;
|
|
@@ -18,3 +18,43 @@ export function detectDeployment() {
|
|
|
18
18
|
return 'unknown';
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* 检测是否有外部进程管理器(PM2 / systemd / Docker)。
|
|
23
|
+
* 有管理器时 process.exit(0) 后会自动重启;没有时需要自 spawn。
|
|
24
|
+
*/
|
|
25
|
+
export function hasProcessManager() {
|
|
26
|
+
// Docker — restart policy
|
|
27
|
+
if (existsSync('/.dockerenv') || existsSync('/run/.containerenv'))
|
|
28
|
+
return true;
|
|
29
|
+
// PM2 — 注入 pm_id 环境变量
|
|
30
|
+
if (process.env.pm_id !== undefined)
|
|
31
|
+
return true;
|
|
32
|
+
// systemd — 注入 INVOCATION_ID 环境变量
|
|
33
|
+
if (process.env.INVOCATION_ID !== undefined)
|
|
34
|
+
return true;
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 解析重启时应该使用的可执行文件路径。
|
|
39
|
+
* npx 启动时 process.argv[1] 指向 npm cache 中的旧路径,
|
|
40
|
+
* 升级后全局 bin 已指向新版本,需要重新解析。
|
|
41
|
+
*/
|
|
42
|
+
export function resolveRestartBinPath() {
|
|
43
|
+
try {
|
|
44
|
+
const bin = execSync('which llm-simple-router', {
|
|
45
|
+
encoding: 'utf-8',
|
|
46
|
+
timeout: 3000,
|
|
47
|
+
}).trim();
|
|
48
|
+
if (bin)
|
|
49
|
+
return bin;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// which 命令不可用或未安装全局包,fallback 到当前进程入口
|
|
53
|
+
return process.argv[1] ?? 'node';
|
|
54
|
+
}
|
|
55
|
+
// fallback: 当前进程入口(PM2/systemd 场景下通常是正确的)
|
|
56
|
+
return process.argv[1];
|
|
57
|
+
}
|
|
58
|
+
export function getRestartMethod() {
|
|
59
|
+
return hasProcessManager() ? 'process_manager' : 'self_spawn';
|
|
60
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{G as e,It as t,J as n,Pt as r,lt as i,ot as a,r as o}from"./button-
|
|
1
|
+
import{G as e,It as t,J as n,Pt as r,lt as i,ot as a,r as o}from"./button-Cnkbp_6J.js";var s=[`data-size`],c=n({__name:`Card`,props:{class:{type:[Boolean,null,String,Object,Array]},size:{default:`default`}},setup(n){let c=n;return(l,u)=>(a(),e(`div`,{"data-slot":`card`,"data-size":n.size,class:t(r(o)(`ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-lg py-4 text-sm ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg group/card flex flex-col`,c.class))},[i(l.$slots,`default`)],10,s))}}),l=n({__name:`CardContent`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(n){let s=n;return(n,c)=>(a(),e(`div`,{"data-slot":`card-content`,class:t(r(o)(`px-4 group-data-[size=sm]/card:px-3`,s.class))},[i(n.$slots,`default`)],2))}});export{c as n,l as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{G as e,It as t,J as n,Pt as r,lt as i,ot as a,r as o}from"./button-
|
|
1
|
+
import{G as e,It as t,J as n,Pt as r,lt as i,ot as a,r as o}from"./button-Cnkbp_6J.js";var s=n({__name:`CardHeader`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(n){let s=n;return(n,c)=>(a(),e(`div`,{"data-slot":`card-header`,class:t(r(o)(`gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]`,s.class))},[i(n.$slots,`default`)],2))}}),c=n({__name:`CardTitle`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(n){let s=n;return(n,c)=>(a(),e(`div`,{"data-slot":`card-title`,class:t(r(o)(`text-base leading-snug font-medium group-data-[size=sm]/card:text-sm cn-font-heading`,s.class))},[i(n.$slots,`default`)],2))}});export{s as n,c as t};
|
package/frontend-dist/assets/{CascadingModelSelect-BFbt3-xP.js → CascadingModelSelect-DTwksDPZ.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Dt as e,G as t,H as n,It as r,J as i,Pt as a,U as o,V as s,W as c,ct as l,ot as u,q as d,yt as f,z as p,zt as m}from"./button-
|
|
1
|
+
import{Dt as e,G as t,H as n,It as r,J as i,Pt as a,U as o,V as s,W as c,ct as l,ot as u,q as d,yt as f,z as p,zt as m}from"./button-Cnkbp_6J.js";import{b as h}from"./Teleport-BQgusr9g.js";import{a as g}from"./PopperContent-B7IuAHeq.js";import{n as _,r as v,t as y}from"./PopoverTrigger-Cyqik5SE.js";var b=h(`chevron-right`,[[`path`,{d:`m9 18 6-6-6-6`,key:`mthhwq`}]]),x=[`onMouseenter`],S={class:`truncate max-w-40`},C=[`onMouseenter`],w=[`onClick`],T={class:`truncate`},E={key:0,class:`shrink-0 text-xs text-muted-foreground`},D={key:0,class:`px-2 py-1.5 text-sm text-muted-foreground`},O=i({__name:`CascadingSelect`,props:{groups:{},modelValue:{},placeholder:{default:`请选择...`}},emits:[`update:modelValue`],setup(i,{emit:h}){let O=i,k=h,A=e(!1),j=e(null),M=s(()=>{if(!O.modelValue)return``;let e=O.groups.find(e=>e.key===O.modelValue.groupKey);if(!e)return``;let t=e.options.find(e=>e.value===O.modelValue.value);return t?`${e.label} / ${t.label}`:``});function N(e,t){k(`update:modelValue`,{groupKey:e,value:t}),A.value=!1}function P(e){A.value=e,e||(j.value=null)}return(e,s)=>(u(),o(a(v),{open:A.value,"onUpdate:open":P},{default:f(()=>[d(a(y),{"as-child":``},{default:f(()=>[n(`div`,{class:r([`flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background cursor-pointer hover:bg-accent hover:text-accent-foreground`,{"ring-2 ring-ring ring-offset-2":A.value}])},[n(`span`,{class:r([`truncate`,i.modelValue?`text-foreground`:`text-muted-foreground`])},m(M.value||i.placeholder),3),d(a(g),{class:`h-4 w-4 shrink-0 opacity-50`})],2)]),_:1}),d(a(_),{align:`start`,"side-offset":4,class:`z-[200] w-auto min-w-56 overflow-visible p-1`},{default:f(()=>[(u(!0),t(p,null,l(i.groups,e=>(u(),t(`div`,{key:e.key,class:r([`relative flex cursor-pointer items-center justify-between rounded-sm px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground`,{"bg-accent text-accent-foreground z-10":j.value===e.key}]),onMouseenter:t=>j.value=e.key},[n(`span`,S,m(e.label),1),d(a(b),{class:`ml-1 h-4 w-4 shrink-0 opacity-50`}),j.value===e.key&&e.options.length>0?(u(),t(`div`,{key:0,class:`absolute left-full top-0 ml-0.5 min-w-48 rounded-md border bg-popover p-1 text-popover-foreground shadow-md`,onMouseenter:t=>j.value=e.key},[(u(!0),t(p,null,l(e.options,a=>(u(),t(`div`,{key:a.value,class:r([`flex cursor-pointer items-center justify-between gap-2 rounded-sm px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground`,{"bg-accent text-accent-foreground":i.modelValue?.groupKey===e.key&&i.modelValue?.value===a.value}]),onClick:t=>N(e.key,a.value)},[n(`span`,T,m(a.label),1),a.tag?(u(),t(`span`,E,m(a.tag),1)):c(``,!0)],10,w))),128))],40,C)):c(``,!0)],42,x))),128)),i.groups.length===0?(u(),t(`div`,D,` 暂无选项 `)):c(``,!0)]),_:1})]),_:1},8,[`open`]))}}),k=i({__name:`CascadingModelSelect`,props:{providers:{},modelValue:{},placeholder:{default:`选择供应商 / 模型`}},emits:[`update:modelValue`],setup(e,{emit:t}){let n=e,r=t;function i(e){return e>=1e6?`${e/1e6}M`:`${e/1e3}K`}let a=s(()=>n.providers.map(e=>({key:e.provider.id,label:e.provider.name,options:e.models.map(e=>({value:e.name,label:e.name,tag:i(e.contextWindow)}))}))),c=s(()=>n.modelValue?{groupKey:n.modelValue.provider_id,value:n.modelValue.model}:void 0);function l(e){r(`update:modelValue`,{provider_id:e.groupKey,model:e.value})}return(t,n)=>(u(),o(O,{groups:a.value,"model-value":c.value,placeholder:e.placeholder,"onUpdate:modelValue":l},null,8,[`groups`,`model-value`,`placeholder`]))}});export{k as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{J as e,L as t,Lt as n,Pt as r,R as i,U as a,V as o,W as s,X as c,dt as l,i as u,lt as d,m as f,o as p,ot as m,q as h,r as g,tt as _,x as v,yt as y}from"./button-
|
|
1
|
+
import{J as e,L as t,Lt as n,Pt as r,R as i,U as a,V as o,W as s,X as c,dt as l,i as u,lt as d,m as f,o as p,ot as m,q as h,r as g,tt as _,x as v,yt as y}from"./button-Cnkbp_6J.js";import{g as b,o as x,u as S,y as C}from"./Teleport-BQgusr9g.js";import{t as w}from"./check-BuqB5Nyb.js";import{t as T}from"./ohash.D__AXeF1-D5e5Wyzx.js";import{t as E}from"./useFormControl-Da4ViGZF.js";import{t as D}from"./VisuallyHiddenInput-yrDtxucb.js";import{t as O}from"./RovingFocusItem-D291Vjh8.js";function k(e,t){return b(e)?!1:Array.isArray(e)?e.some(e=>T(e,t)):T(e,t)}var[A,j]=C(`CheckboxGroupRoot`);function M(e){return e===`indeterminate`}function N(e){return M(e)?`indeterminate`:e?`checked`:`unchecked`}var[P,F]=C(`CheckboxRoot`),I=e({inheritAttrs:!1,__name:`CheckboxRoot`,props:{defaultValue:{type:null,required:!1},modelValue:{type:null,required:!1,default:void 0},disabled:{type:Boolean,required:!1},value:{type:null,required:!1,default:`on`},id:{type:String,required:!1},trueValue:{type:null,required:!1,default:()=>!0},falseValue:{type:null,required:!1,default:()=>!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`button`},name:{type:String,required:!1},required:{type:Boolean,required:!1}},emits:[`update:modelValue`],setup(e,{emit:n}){let c=e,h=n,{forwardRef:g,currentElement:v}=p(),x=A(null),S=f(c,`modelValue`,h,{defaultValue:c.defaultValue??c.falseValue,passive:c.modelValue===void 0}),C=o(()=>x?.disabled.value||c.disabled),w=o(()=>T(S.value,c.trueValue)),j=o(()=>b(x?.modelValue.value)?S.value===`indeterminate`?`indeterminate`:w.value:k(x.modelValue.value,c.value));function P(){if(b(x?.modelValue.value))S.value===`indeterminate`?S.value=c.trueValue:S.value=w.value?c.falseValue:c.trueValue;else{let e=[...x.modelValue.value||[]];if(k(e,c.value)){let t=e.findIndex(e=>T(e,c.value));e.splice(t,1)}else e.push(c.value);x.modelValue.value=e}}let I=E(v),L=o(()=>c.id&&v.value?document.querySelector(`[for="${c.id}"]`)?.innerText:void 0);return F({disabled:C,state:j}),(e,n)=>(m(),a(l(r(x)?.rovingFocus.value?r(O):r(u)),_(e.$attrs,{id:e.id,ref:r(g),role:`checkbox`,"as-child":e.asChild,as:e.as,type:e.as===`button`?`button`:void 0,"aria-checked":r(M)(j.value)?`mixed`:j.value,"aria-required":e.required,"aria-label":e.$attrs[`aria-label`]||L.value,"data-state":r(N)(j.value),"data-disabled":C.value?``:void 0,disabled:C.value,focusable:r(x)?.rovingFocus.value?!C.value:void 0,onKeydown:t(i(()=>{},[`prevent`]),[`enter`]),onClick:P}),{default:y(()=>[d(e.$slots,`default`,{modelValue:r(S),state:j.value}),r(I)&&e.name&&!r(x)?(m(),a(r(D),{key:0,type:`checkbox`,checked:!!j.value,name:e.name,value:e.value,disabled:C.value,required:e.required},null,8,[`checked`,`name`,`value`,`disabled`,`required`])):s(`v-if`,!0)]),_:3},16,[`id`,`as-child`,`as`,`type`,`aria-checked`,`aria-required`,`aria-label`,`data-state`,`data-disabled`,`disabled`,`focusable`,`onKeydown`]))}}),L=e({__name:`CheckboxIndicator`,props:{forceMount:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`span`}},setup(e){let{forwardRef:t}=p(),n=P();return(e,i)=>(m(),a(r(x),{present:e.forceMount||r(M)(r(n).state.value)||r(n).state.value===!0},{default:y(()=>[h(r(u),_({ref:r(t),"data-state":r(N)(r(n).state.value),"data-disabled":r(n).disabled.value?``:void 0,style:{pointerEvents:`none`},"as-child":e.asChild,as:e.as},e.$attrs),{default:y(()=>[d(e.$slots,`default`)]),_:3},16,[`data-state`,`data-disabled`,`as-child`,`as`])]),_:3},8,[`present`]))}}),R=e({__name:`Checkbox`,props:{defaultValue:{},modelValue:{},disabled:{type:Boolean},value:{},id:{},trueValue:{},falseValue:{},asChild:{type:Boolean},as:{},name:{},required:{type:Boolean},class:{type:[Boolean,null,String,Object,Array]}},emits:[`update:modelValue`],setup(e,{emit:t}){let i=e,o=t,s=S(v(i,`class`),o);return(e,t)=>(m(),a(r(I),_({"data-slot":`checkbox`},r(s),{class:r(g)(`border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-md border transition-colors group-has-disabled/field:opacity-50 focus-visible:ring-3 aria-invalid:ring-3 peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50`,i.class)}),{default:y(t=>[h(r(L),{"data-slot":`checkbox-indicator`,class:`[&>svg]:size-3.5 grid place-content-center text-current transition-none`},{default:y(()=>[d(e.$slots,`default`,n(c(t)),()=>[h(r(w))])]),_:2},1024)]),_:3},16,[`class`]))}});export{R as t};
|
package/frontend-dist/assets/{CollapsibleTrigger-BIBFqVyy.js → CollapsibleTrigger-Dsjo7QlC.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Dt as e,J as t,Lt as n,Mt as r,Pt as i,U as a,V as o,W as s,X as c,d as l,gt as u,i as d,it as f,lt as p,m,nt as h,o as g,ot as _,q as v,tt as y,yt as b}from"./button-
|
|
1
|
+
import{Dt as e,J as t,Lt as n,Mt as r,Pt as i,U as a,V as o,W as s,X as c,d as l,gt as u,i as d,it as f,lt as p,m,nt as h,o as g,ot as _,q as v,tt as y,yt as b}from"./button-Cnkbp_6J.js";import{c as x,o as S,u as C,y as w}from"./Teleport-BQgusr9g.js";var[T,E]=w(`CollapsibleRoot`),D=t({__name:`CollapsibleRoot`,props:{defaultOpen:{type:Boolean,required:!1,default:!1},open:{type:Boolean,required:!1,default:void 0},disabled:{type:Boolean,required:!1},unmountOnHide:{type:Boolean,required:!1,default:!0},asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},emits:[`update:open`],setup(e,{expose:t,emit:n}){let o=e,s=m(o,`open`,n,{defaultValue:o.defaultOpen,passive:o.open===void 0}),{disabled:c,unmountOnHide:l}=r(o);return E({contentId:``,disabled:c,open:s,unmountOnHide:l,onOpenToggle:()=>{c.value||(s.value=!s.value)}}),t({open:s}),g(),(e,t)=>(_(),a(i(d),{as:e.as,"as-child":o.asChild,"data-state":i(s)?`open`:`closed`,"data-disabled":i(c)?``:void 0},{default:b(()=>[p(e.$slots,`default`,{open:i(s)})]),_:3},8,[`as`,`as-child`,`data-state`,`data-disabled`]))}}),O=t({inheritAttrs:!1,__name:`CollapsibleContent`,props:{forceMount:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},emits:[`contentFound`],setup(t,{emit:n}){let r=t,c=n,m=T();m.contentId||=x(void 0,`reka-collapsible-content`);let C=e(),{forwardRef:w,currentElement:E}=g(),D=e(0),O=e(0),k=o(()=>m.open.value),A=e(k.value),j=e();u(()=>[k.value,C.value?.present],async()=>{await h();let e=E.value;if(!e)return;j.value=j.value||{transitionDuration:e.style.transitionDuration,animationName:e.style.animationName},e.style.transitionDuration=`0s`,e.style.animationName=`none`;let t=e.getBoundingClientRect();O.value=t.height,D.value=t.width,A.value||(e.style.transitionDuration=j.value.transitionDuration,e.style.animationName=j.value.animationName)},{immediate:!0});let M=o(()=>A.value&&m.open.value);return f(()=>{requestAnimationFrame(()=>{A.value=!1})}),l(E,`beforematch`,e=>{requestAnimationFrame(()=>{m.onOpenToggle(),c(`contentFound`)})}),(e,t)=>(_(),a(i(S),{ref_key:`presentRef`,ref:C,present:e.forceMount||i(m).open.value,"force-mount":!0},{default:b(({present:t})=>[v(i(d),y(e.$attrs,{id:i(m).contentId,ref:i(w),"as-child":r.asChild,as:e.as,hidden:t?void 0:i(m).unmountOnHide.value?``:`until-found`,"data-state":M.value?void 0:i(m).open.value?`open`:`closed`,"data-disabled":i(m).disabled?.value?``:void 0,style:{"--reka-collapsible-content-height":`${O.value}px`,"--reka-collapsible-content-width":`${D.value}px`}}),{default:b(()=>[!i(m).unmountOnHide.value||t?p(e.$slots,`default`,{key:0}):s(`v-if`,!0)]),_:2},1040,[`id`,`as-child`,`as`,`hidden`,`data-state`,`data-disabled`,`style`])]),_:3},8,[`present`]))}}),k=t({__name:`CollapsibleTrigger`,props:{asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`button`}},setup(e){let t=e;g();let n=T();return(e,r)=>(_(),a(i(d),{type:e.as===`button`?`button`:void 0,as:e.as,"as-child":t.asChild,"aria-controls":i(n).contentId,"aria-expanded":i(n).open.value,"data-state":i(n).open.value?`open`:`closed`,"data-disabled":i(n).disabled?.value?``:void 0,disabled:i(n).disabled?.value,onClick:i(n).onOpenToggle},{default:b(()=>[p(e.$slots,`default`)]),_:3},8,[`type`,`as`,`as-child`,`aria-controls`,`aria-expanded`,`data-state`,`data-disabled`,`disabled`,`onClick`]))}}),A=t({__name:`Collapsible`,props:{defaultOpen:{type:Boolean},open:{type:Boolean},disabled:{type:Boolean},unmountOnHide:{type:Boolean},asChild:{type:Boolean},as:{}},emits:[`update:open`],setup(e,{emit:t}){let r=C(e,t);return(e,t)=>(_(),a(i(D),y({"data-slot":`collapsible`},i(r)),{default:b(t=>[p(e.$slots,`default`,n(c(t)))]),_:3},16))}}),j=t({__name:`CollapsibleContent`,props:{forceMount:{type:Boolean},asChild:{type:Boolean},as:{}},setup(e){let t=e;return(e,n)=>(_(),a(i(O),y({"data-slot":`collapsible-content`},t),{default:b(()=>[p(e.$slots,`default`)]),_:3},16))}}),M=t({__name:`CollapsibleTrigger`,props:{asChild:{type:Boolean},as:{}},setup(e){let t=e;return(e,n)=>(_(),a(i(k),y({"data-slot":`collapsible-trigger`},t),{default:b(()=>[p(e.$slots,`default`)]),_:3},16))}});export{j as n,A as r,M as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Dt as e,J as t,Q as n,V as r,Z as i,_t as a,a as o,gt as s,st as c,u as l,wt as u}from"./button-
|
|
1
|
+
import{Dt as e,J as t,Q as n,V as r,Z as i,_t as a,a as o,gt as s,st as c,u as l,wt as u}from"./button-Cnkbp_6J.js";import{h as d}from"./Teleport-BQgusr9g.js";function f(t){let n=d({dir:e(`ltr`)});return r(()=>t?.value||n.dir?.value||`ltr`)}function p(){let t=e();return{primitiveElement:t,currentElement:r(()=>[`#text`,`#comment`].includes(t.value?.$el.nodeName)?t.value?.$el.nextElementSibling:l(t))}}var m=`data-reka-collection-item`;function h(l={}){let{key:d=``,isProvider:f=!1}=l,h=`${d}CollectionProvider`,g;if(f){let t=e(new Map);g={collectionRef:e(),itemMap:t},c(h,g)}else g=n(h);let _=(e=!1)=>{let t=g.collectionRef.value;if(!t)return[];let n=Array.from(t.querySelectorAll(`[${m}]`)),r=Array.from(g.itemMap.value.values()).sort((e,t)=>n.indexOf(e.ref)-n.indexOf(t.ref));return e?r:r.filter(e=>e.ref.dataset.disabled!==``)},v=t({name:`CollectionSlot`,inheritAttrs:!1,setup(e,{slots:t,attrs:n}){let{primitiveElement:r,currentElement:a}=p();return s(a,()=>{g.collectionRef.value=a.value}),()=>i(o,{ref:r,...n},t)}}),y=t({name:`CollectionItem`,inheritAttrs:!1,props:{value:{validator:()=>!0}},setup(e,{slots:t,attrs:n}){let{primitiveElement:r,currentElement:s}=p();return a(t=>{if(s.value){let n=u(s.value);g.itemMap.value.set(n,{ref:s.value,value:e.value}),t(()=>g.itemMap.value.delete(n))}}),()=>i(o,{...n,[m]:``,ref:r},t)}});return{getItems:_,reactiveItems:r(()=>Array.from(g.itemMap.value.values())),itemMapSize:r(()=>g.itemMap.value.size),CollectionSlot:v,CollectionItem:y}}export{p as n,f as r,h as t};
|