llm-simple-router 0.5.0 → 0.5.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/dist/admin/routes.js +2 -0
- package/dist/admin/upgrade.d.ts +13 -0
- package/dist/admin/upgrade.js +113 -0
- package/dist/db/settings.d.ts +2 -0
- package/dist/db/settings.js +7 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/upgrade/checker.d.ts +25 -0
- package/dist/upgrade/checker.js +105 -0
- package/dist/upgrade/deployment.d.ts +2 -0
- package/dist/upgrade/deployment.js +20 -0
- package/dist/upgrade/version.d.ts +1 -0
- package/dist/upgrade/version.js +13 -0
- package/frontend-dist/assets/CardContent-Deyvo1TQ.js +1 -0
- package/frontend-dist/assets/CardTitle-DujSYXja.js +1 -0
- package/frontend-dist/assets/Checkbox-BJxf-QuV.js +1 -0
- package/frontend-dist/assets/CollapsibleTrigger-ByCvAsW0.js +1 -0
- package/frontend-dist/assets/Collection-V6gcBlwC.js +1 -0
- package/frontend-dist/assets/{Dashboard-DOEqP6gF.js → Dashboard-xqf6PcmE.js} +2 -2
- package/frontend-dist/assets/DialogTitle-D0nwX87v.js +1 -0
- package/frontend-dist/assets/{Input-l5ZurXX5.js → Input-D0kpZB31.js} +1 -1
- package/frontend-dist/assets/Label-BvYK0rd6.js +1 -0
- package/frontend-dist/assets/Login-C9oPKRcu.js +1 -0
- package/frontend-dist/assets/Logs-DVgenFav.js +1 -0
- package/frontend-dist/assets/ModelMappings-BoG2P9Rh.js +1 -0
- package/frontend-dist/assets/Monitor-W441wik3.js +1 -0
- package/frontend-dist/assets/PopperContent-DVJ4IxLF.js +1 -0
- package/frontend-dist/assets/Providers-D2rzb_Qk.js +1 -0
- package/frontend-dist/assets/ProxyEnhancement-DahQkV1g.js +5 -0
- package/frontend-dist/assets/RetryRules-Bg9p50oc.js +1 -0
- package/frontend-dist/assets/RouterKeys-C1LhXbqf.js +1 -0
- package/frontend-dist/assets/SelectValue-CAEBdE04.js +1 -0
- package/frontend-dist/assets/Settings-3lR8QVQt.js +6 -0
- package/frontend-dist/assets/Setup-Dzj1XvgF.js +1 -0
- package/frontend-dist/assets/Switch-CST3045A.js +1 -0
- package/frontend-dist/assets/TableHeader-CIrxcNRh.js +1 -0
- package/frontend-dist/assets/TabsContent-B4nroq3-.js +1 -0
- package/frontend-dist/assets/TabsTrigger-FsELRpyc.js +1 -0
- package/frontend-dist/assets/Teleport-DVgMe9KS.js +3 -0
- package/frontend-dist/assets/UnifiedRequestDialog-Fe2TfhTD.js +3 -0
- package/frontend-dist/assets/{VisuallyHidden-BwwTtzb9.js → VisuallyHidden-CjuTDGlC.js} +1 -1
- package/frontend-dist/assets/VisuallyHiddenInput-BaW-2aEF.js +1 -0
- package/frontend-dist/assets/alert-dialog-Bv6dVarS.js +1 -0
- package/frontend-dist/assets/badge-CEfcely6.js +1 -0
- package/frontend-dist/assets/button-BmxhlpN-.js +12 -0
- package/frontend-dist/assets/{createLucideIcon-Biq59l_W.js → createLucideIcon-UWoYUKtZ.js} +1 -1
- package/frontend-dist/assets/dialog-QaGxKbze.js +1 -0
- package/frontend-dist/assets/{file-text-DoRW0hQW.js → file-text-D38GtYz2.js} +1 -1
- package/frontend-dist/assets/index-CMBzqUyT.css +1 -0
- package/frontend-dist/assets/index-D484ZFa9.js +1 -0
- package/frontend-dist/assets/lib-CSYRBKqn.js +1 -0
- package/frontend-dist/assets/{ohash.D__AXeF1-BGxYMs6k.js → ohash.D__AXeF1-BUMsW586.js} +1 -1
- package/frontend-dist/assets/{useClipboard-vaHkvJHw.js → useClipboard-CuE5xXIg.js} +1 -1
- package/frontend-dist/assets/{useLogRetention-Cs_fiKql.js → useLogRetention-DesMKwIU.js} +1 -1
- package/frontend-dist/assets/useNonce-FLqOooWA.js +1 -0
- package/frontend-dist/assets/x-BEUXSxcj.js +1 -0
- package/frontend-dist/index.html +17 -8
- package/package.json +1 -1
- package/frontend-dist/assets/CardContent-CIO85eT6.js +0 -1
- package/frontend-dist/assets/CardTitle-DiqIReMT.js +0 -1
- package/frontend-dist/assets/Checkbox-C2u5pIp4.js +0 -1
- package/frontend-dist/assets/CollapsibleTrigger-RKFL41om.js +0 -1
- package/frontend-dist/assets/Collection-iiNnuTQj.js +0 -1
- package/frontend-dist/assets/DialogTitle-CEqndrf6.js +0 -1
- package/frontend-dist/assets/Label-PgGtS8v2.js +0 -1
- package/frontend-dist/assets/Login-DaN6ZcCx.js +0 -1
- package/frontend-dist/assets/Logs-CleRQ7Xk.js +0 -1
- package/frontend-dist/assets/ModelMappings-CacA_ua_.js +0 -1
- package/frontend-dist/assets/Monitor-LSMFOBN2.js +0 -1
- package/frontend-dist/assets/PopperContent-zLFHqQP0.js +0 -1
- package/frontend-dist/assets/Providers-NT5MUDU0.js +0 -1
- package/frontend-dist/assets/ProxyEnhancement-DhOy8nNy.js +0 -5
- package/frontend-dist/assets/RetryRules-7arWa3jB.js +0 -1
- package/frontend-dist/assets/RouterKeys-CdaZunRg.js +0 -1
- package/frontend-dist/assets/SelectValue-CSg-MKW_.js +0 -1
- package/frontend-dist/assets/Settings-1ntV9XE3.js +0 -6
- package/frontend-dist/assets/Setup-CXLTDhYJ.js +0 -1
- package/frontend-dist/assets/Switch-DivrIFE3.js +0 -1
- package/frontend-dist/assets/TableHeader-Bn0bodWx.js +0 -1
- package/frontend-dist/assets/TabsContent-MWvOH_LJ.js +0 -1
- package/frontend-dist/assets/TabsTrigger-WKkUfO2M.js +0 -1
- package/frontend-dist/assets/Teleport-B0PNXZbP.js +0 -3
- package/frontend-dist/assets/UnifiedRequestDialog-Ba2e7YuJ.js +0 -3
- package/frontend-dist/assets/VisuallyHiddenInput-EGZSP7s8.js +0 -1
- package/frontend-dist/assets/alert-dialog-CS1yFhdV.js +0 -1
- package/frontend-dist/assets/badge-C-QcC5n2.js +0 -1
- package/frontend-dist/assets/button-Dbz2Be22.js +0 -12
- package/frontend-dist/assets/dialog-Cr0YQlLW.js +0 -1
- package/frontend-dist/assets/index-0H2uCGbx.js +0 -1
- package/frontend-dist/assets/index-D-cdVNCb.css +0 -1
- package/frontend-dist/assets/lib-B0lieqgg.js +0 -1
- package/frontend-dist/assets/useForwardExpose-C2_ks3sW.js +0 -1
- package/frontend-dist/assets/useNonce-C9do0jOI.js +0 -1
- package/frontend-dist/assets/x-BlTnH_0_.js +0 -1
- /package/frontend-dist/assets/{format-DOVIVsQC.js → format-CPdJtjZ5.js} +0 -0
package/dist/admin/routes.js
CHANGED
|
@@ -13,6 +13,7 @@ import { adminMonitorRoutes } from "./monitor.js";
|
|
|
13
13
|
import { adminSettingsRoutes } from "./settings.js";
|
|
14
14
|
import { adminRecommendedRoutes } from "./recommended.js";
|
|
15
15
|
import { adminUsageRoutes } from "./usage.js";
|
|
16
|
+
import { adminUpgradeRoutes } from "./upgrade.js";
|
|
16
17
|
import { adminImportExportRoutes } from "./settings-import-export.js";
|
|
17
18
|
export const adminRoutes = (app, options, done) => {
|
|
18
19
|
// Setup 路由不需要 auth
|
|
@@ -33,5 +34,6 @@ export const adminRoutes = (app, options, done) => {
|
|
|
33
34
|
app.register(adminImportExportRoutes, { db: options.db, matcher: options.matcher, semaphoreManager: options.semaphoreManager });
|
|
34
35
|
app.register(adminRecommendedRoutes, { db: options.db });
|
|
35
36
|
app.register(adminUsageRoutes, { db: options.db });
|
|
37
|
+
app.register(adminUpgradeRoutes, { db: options.db });
|
|
36
38
|
done();
|
|
37
39
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FastifyPluginCallback } from 'fastify';
|
|
2
|
+
import Database from 'better-sqlite3';
|
|
3
|
+
import { CheckerOptions } from '../upgrade/checker.js';
|
|
4
|
+
interface UpgradeRoutesOptions {
|
|
5
|
+
db: Database.Database;
|
|
6
|
+
}
|
|
7
|
+
export declare function startUpgradeChecker(opts?: CheckerOptions): {
|
|
8
|
+
check: (sourceOverride?: string) => Promise<void>;
|
|
9
|
+
getStatus: () => import("../upgrade/checker.js").UpgradeStatus;
|
|
10
|
+
};
|
|
11
|
+
export declare function stopUpgradeChecker(): void;
|
|
12
|
+
export declare const adminUpgradeRoutes: FastifyPluginCallback<UpgradeRoutesOptions>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { getConfigSyncSource, setConfigSyncSource } from '../db/settings.js';
|
|
2
|
+
import { detectDeployment } from '../upgrade/deployment.js';
|
|
3
|
+
import { createUpgradeChecker, fetchJson } from '../upgrade/checker.js';
|
|
4
|
+
import { reloadConfig } from '../config/recommended.js';
|
|
5
|
+
import { execSync } from 'node:child_process';
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { HTTP_BAD_REQUEST, HTTP_INTERNAL_ERROR } from '../constants.js';
|
|
9
|
+
const GITHUB_CONFIG_BASE = 'https://raw.githubusercontent.com/zhushanwen321/llm-simple-router/main/config';
|
|
10
|
+
const GITEE_CONFIG_BASE = 'https://gitee.com/zzzzswszzzz/llm-simple-router/raw/main/config';
|
|
11
|
+
const CHECK_INTERVAL_MS = 60 * 60 * 1000; // eslint-disable-line no-magic-numbers
|
|
12
|
+
const JSON_INDENT = 2;
|
|
13
|
+
// 模块级单例:checker 和定时器
|
|
14
|
+
let checker = null;
|
|
15
|
+
let intervalId = null;
|
|
16
|
+
export function startUpgradeChecker(opts) {
|
|
17
|
+
if (checker)
|
|
18
|
+
return checker;
|
|
19
|
+
checker = createUpgradeChecker(opts);
|
|
20
|
+
// 启动时检查一次,之后每小时
|
|
21
|
+
checker.check();
|
|
22
|
+
intervalId = setInterval(() => checker.check(), CHECK_INTERVAL_MS);
|
|
23
|
+
return checker;
|
|
24
|
+
}
|
|
25
|
+
export function stopUpgradeChecker() {
|
|
26
|
+
if (intervalId)
|
|
27
|
+
clearInterval(intervalId);
|
|
28
|
+
checker = null;
|
|
29
|
+
intervalId = null;
|
|
30
|
+
}
|
|
31
|
+
function getConfigBaseUrl(source) {
|
|
32
|
+
return source === 'gitee' ? GITEE_CONFIG_BASE : GITHUB_CONFIG_BASE;
|
|
33
|
+
}
|
|
34
|
+
export const adminUpgradeRoutes = (app, options, done) => {
|
|
35
|
+
const { db } = options;
|
|
36
|
+
app.get('/admin/api/upgrade/status', async (_req, reply) => {
|
|
37
|
+
const c = checker ?? createUpgradeChecker();
|
|
38
|
+
const deployment = detectDeployment();
|
|
39
|
+
const syncSource = getConfigSyncSource(db);
|
|
40
|
+
return reply.send({ ...c.getStatus(), deployment, syncSource });
|
|
41
|
+
});
|
|
42
|
+
app.post('/admin/api/upgrade/check', async (_req, reply) => {
|
|
43
|
+
const c = checker ?? createUpgradeChecker();
|
|
44
|
+
const syncSource = getConfigSyncSource(db);
|
|
45
|
+
await c.check(getConfigBaseUrl(syncSource));
|
|
46
|
+
return reply.send({ ok: true });
|
|
47
|
+
});
|
|
48
|
+
app.put('/admin/api/upgrade/sync-source', async (req, reply) => {
|
|
49
|
+
const { source } = req.body;
|
|
50
|
+
if (source !== 'github' && source !== 'gitee') {
|
|
51
|
+
return reply.code(HTTP_BAD_REQUEST).send({ error: { message: 'source must be github or gitee' } });
|
|
52
|
+
}
|
|
53
|
+
setConfigSyncSource(db, source);
|
|
54
|
+
return reply.send({ ok: true });
|
|
55
|
+
});
|
|
56
|
+
app.post('/admin/api/upgrade/execute', async (req, reply) => {
|
|
57
|
+
const deployment = detectDeployment();
|
|
58
|
+
if (deployment !== 'npm') {
|
|
59
|
+
return reply.code(HTTP_BAD_REQUEST).send({ error: { message: '仅支持 npm 全局安装模式下自动升级' } });
|
|
60
|
+
}
|
|
61
|
+
const { version } = req.body;
|
|
62
|
+
if (!version) {
|
|
63
|
+
return reply.code(HTTP_BAD_REQUEST).send({ error: { message: 'version is required' } });
|
|
64
|
+
}
|
|
65
|
+
if (!/^\d+\.\d+\.\d+$/.test(version)) {
|
|
66
|
+
return reply.code(HTTP_BAD_REQUEST).send({ error: { message: '无效版本号格式' } });
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
execSync(`npm install -g llm-simple-router@${version}`, {
|
|
70
|
+
stdio: 'pipe',
|
|
71
|
+
timeout: 120_000,
|
|
72
|
+
});
|
|
73
|
+
return reply.send({ ok: true, version });
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
77
|
+
return reply.code(HTTP_INTERNAL_ERROR).send({ error: { message: `升级失败: ${msg}` } });
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
app.post('/admin/api/upgrade/sync-config', async (req, reply) => {
|
|
81
|
+
const { source } = req.body;
|
|
82
|
+
if (source !== 'github' && source !== 'gitee') {
|
|
83
|
+
return reply.code(HTTP_BAD_REQUEST).send({ error: { message: 'source must be github or gitee' } });
|
|
84
|
+
}
|
|
85
|
+
const base = getConfigBaseUrl(source);
|
|
86
|
+
const configDir = path.resolve(process.cwd(), 'config');
|
|
87
|
+
try {
|
|
88
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
89
|
+
const [providersResult, rulesResult] = await Promise.allSettled([
|
|
90
|
+
fetchJson(`${base}/recommended-providers.json`),
|
|
91
|
+
fetchJson(`${base}/recommended-retry-rules.json`),
|
|
92
|
+
]);
|
|
93
|
+
if (providersResult.status === 'fulfilled') {
|
|
94
|
+
fs.writeFileSync(path.join(configDir, 'recommended-providers.json'), JSON.stringify(providersResult.value, null, JSON_INDENT));
|
|
95
|
+
}
|
|
96
|
+
if (rulesResult.status === 'fulfilled') {
|
|
97
|
+
fs.writeFileSync(path.join(configDir, 'recommended-retry-rules.json'), JSON.stringify(rulesResult.value, null, JSON_INDENT));
|
|
98
|
+
}
|
|
99
|
+
if (providersResult.status === 'rejected' && rulesResult.status === 'rejected') {
|
|
100
|
+
throw new Error('同步失败: 无法获取 providers 和 retry-rules 配置');
|
|
101
|
+
}
|
|
102
|
+
reloadConfig();
|
|
103
|
+
if (checker)
|
|
104
|
+
await checker.check(getConfigBaseUrl(source));
|
|
105
|
+
return reply.send({ ok: true });
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
109
|
+
return reply.code(HTTP_INTERNAL_ERROR).send({ error: { message: `同步失败: ${msg}` } });
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
done();
|
|
113
|
+
};
|
package/dist/db/settings.d.ts
CHANGED
|
@@ -8,3 +8,5 @@ export declare function getDbMaxSizeMb(db: Database.Database): number;
|
|
|
8
8
|
export declare function setDbMaxSizeMb(db: Database.Database, mb: number): void;
|
|
9
9
|
export declare function getLogTableMaxSizeMb(db: Database.Database): number;
|
|
10
10
|
export declare function setLogTableMaxSizeMb(db: Database.Database, mb: number): void;
|
|
11
|
+
export declare function getConfigSyncSource(db: Database.Database): "github" | "gitee";
|
|
12
|
+
export declare function setConfigSyncSource(db: Database.Database, source: "github" | "gitee"): void;
|
package/dist/db/settings.js
CHANGED
|
@@ -32,3 +32,10 @@ export function getLogTableMaxSizeMb(db) {
|
|
|
32
32
|
export function setLogTableMaxSizeMb(db, mb) {
|
|
33
33
|
setSetting(db, "log_table_max_size_mb", String(mb));
|
|
34
34
|
}
|
|
35
|
+
export function getConfigSyncSource(db) {
|
|
36
|
+
const val = getSetting(db, "config_sync_source");
|
|
37
|
+
return val === "gitee" ? "gitee" : "github";
|
|
38
|
+
}
|
|
39
|
+
export function setConfigSyncSource(db, source) {
|
|
40
|
+
setSetting(db, "config_sync_source", source);
|
|
41
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
import { FastifyInstance } from "fastify";
|
|
3
3
|
import { Config } from "./config.js";
|
|
4
4
|
import { UsageWindowTracker } from "./proxy/usage-window-tracker.js";
|
|
5
|
+
import { CheckerOptions } from "./upgrade/checker.js";
|
|
5
6
|
import Database from "better-sqlite3";
|
|
6
7
|
export interface AppOptions {
|
|
7
8
|
config?: Config;
|
|
8
9
|
db?: Database.Database;
|
|
10
|
+
upgradeCheckerOptions?: CheckerOptions;
|
|
9
11
|
}
|
|
10
12
|
export declare function buildApp(options?: AppOptions): Promise<{
|
|
11
13
|
app: FastifyInstance;
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,7 @@ import { modelState } from "./proxy/model-state.js";
|
|
|
34
34
|
import { UsageWindowTracker } from "./proxy/usage-window-tracker.js";
|
|
35
35
|
import { scheduleLogCleanup } from "./db/log-cleaner.js";
|
|
36
36
|
import { scheduleDbSizeMonitor } from "./db/db-size-monitor.js";
|
|
37
|
+
import { startUpgradeChecker, stopUpgradeChecker } from "./admin/upgrade.js";
|
|
37
38
|
import fastifyStatic from "@fastify/static";
|
|
38
39
|
export async function buildApp(options) {
|
|
39
40
|
const config = options?.config ?? getBaseConfig();
|
|
@@ -107,6 +108,7 @@ export async function buildApp(options) {
|
|
|
107
108
|
return reply.code(status).send({ error: { message: fastifyError.message } });
|
|
108
109
|
});
|
|
109
110
|
loadRecommendedConfig();
|
|
111
|
+
startUpgradeChecker(options?.upgradeCheckerOptions);
|
|
110
112
|
// 启动时回填:补齐回退老版本期间缺失的 metrics 冗余列
|
|
111
113
|
if (shouldBackfill) {
|
|
112
114
|
const backfilled = backfillMetricsFromRequestMetrics(db);
|
|
@@ -193,6 +195,7 @@ export async function buildApp(options) {
|
|
|
193
195
|
db,
|
|
194
196
|
usageWindowTracker,
|
|
195
197
|
close: async () => {
|
|
198
|
+
stopUpgradeChecker();
|
|
196
199
|
logCleanup.stop();
|
|
197
200
|
dbSizeMonitor.stop();
|
|
198
201
|
tracker.stopPushInterval();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface NpmStatus {
|
|
2
|
+
hasUpdate: boolean;
|
|
3
|
+
currentVersion: string;
|
|
4
|
+
latestVersion: string | null;
|
|
5
|
+
}
|
|
6
|
+
export interface ConfigStatus {
|
|
7
|
+
hasUpdate: boolean;
|
|
8
|
+
providerChanges: number;
|
|
9
|
+
retryRuleChanges: number;
|
|
10
|
+
}
|
|
11
|
+
export interface UpgradeStatus {
|
|
12
|
+
npm: NpmStatus;
|
|
13
|
+
config: ConfigStatus;
|
|
14
|
+
lastCheckedAt: string | null;
|
|
15
|
+
}
|
|
16
|
+
export interface CheckerOptions {
|
|
17
|
+
npmRegistryUrl?: string;
|
|
18
|
+
configBaseUrl?: string;
|
|
19
|
+
configDir?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function fetchJson(url: string): Promise<unknown>;
|
|
22
|
+
export declare function createUpgradeChecker(options?: CheckerOptions): {
|
|
23
|
+
check: (sourceOverride?: string) => Promise<void>;
|
|
24
|
+
getStatus: () => UpgradeStatus;
|
|
25
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import https from 'node:https';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { getInstalledVersion } from './version.js';
|
|
6
|
+
const DEFAULT_NPM_REGISTRY = 'https://registry.npmjs.org/llm-simple-router';
|
|
7
|
+
const DEFAULT_GITHUB_CONFIG_BASE = 'https://raw.githubusercontent.com/zhushanwen321/llm-simple-router/main/config';
|
|
8
|
+
const CHECK_TIMEOUT_MS = 5000;
|
|
9
|
+
export async function fetchJson(url) {
|
|
10
|
+
const mod = url.startsWith('https') ? https : http;
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const timer = setTimeout(() => reject(new Error('timeout')), CHECK_TIMEOUT_MS);
|
|
13
|
+
mod.get(url, (res) => {
|
|
14
|
+
let data = '';
|
|
15
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
16
|
+
res.on('end', () => {
|
|
17
|
+
clearTimeout(timer);
|
|
18
|
+
try {
|
|
19
|
+
resolve(JSON.parse(data));
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
reject(new Error('invalid json'));
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}).on('error', (err) => {
|
|
26
|
+
clearTimeout(timer);
|
|
27
|
+
reject(err);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
export function createUpgradeChecker(options) {
|
|
32
|
+
const npmRegistryUrl = options?.npmRegistryUrl ?? DEFAULT_NPM_REGISTRY;
|
|
33
|
+
const configBaseUrl = options?.configBaseUrl ?? DEFAULT_GITHUB_CONFIG_BASE;
|
|
34
|
+
const configDir = options?.configDir ?? path.resolve(process.cwd(), 'config');
|
|
35
|
+
let npmStatus = {
|
|
36
|
+
hasUpdate: false,
|
|
37
|
+
currentVersion: getInstalledVersion(),
|
|
38
|
+
latestVersion: null,
|
|
39
|
+
};
|
|
40
|
+
let configStatus = {
|
|
41
|
+
hasUpdate: false,
|
|
42
|
+
providerChanges: 0,
|
|
43
|
+
retryRuleChanges: 0,
|
|
44
|
+
};
|
|
45
|
+
let lastCheckedAt = null;
|
|
46
|
+
function loadLocalJson(filename) {
|
|
47
|
+
const filePath = path.join(configDir, filename);
|
|
48
|
+
try {
|
|
49
|
+
if (!fs.existsSync(filePath))
|
|
50
|
+
return null;
|
|
51
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function checkNpm() {
|
|
58
|
+
try {
|
|
59
|
+
const data = await fetchJson(npmRegistryUrl);
|
|
60
|
+
const latest = data?.['dist-tags']?.latest ?? null;
|
|
61
|
+
npmStatus = {
|
|
62
|
+
hasUpdate: latest !== null && latest !== npmStatus.currentVersion,
|
|
63
|
+
currentVersion: npmStatus.currentVersion,
|
|
64
|
+
latestVersion: latest,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
process.stderr.write('[upgrade] failed to check npm version\n');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async function checkConfig(sourceOverride) {
|
|
72
|
+
try {
|
|
73
|
+
const base = sourceOverride ?? configBaseUrl;
|
|
74
|
+
const [providersResult, rulesResult] = await Promise.allSettled([
|
|
75
|
+
fetchJson(`${base}/recommended-providers.json`),
|
|
76
|
+
fetchJson(`${base}/recommended-retry-rules.json`),
|
|
77
|
+
]);
|
|
78
|
+
const remoteProviders = providersResult.status === 'fulfilled' ? providersResult.value : null;
|
|
79
|
+
const remoteRules = rulesResult.status === 'fulfilled' ? rulesResult.value : null;
|
|
80
|
+
if (!remoteProviders && !remoteRules) {
|
|
81
|
+
throw new Error('both providers and rules fetch failed');
|
|
82
|
+
}
|
|
83
|
+
const localProviders = loadLocalJson('recommended-providers.json');
|
|
84
|
+
const localRules = loadLocalJson('recommended-retry-rules.json');
|
|
85
|
+
const providersChanged = remoteProviders !== null && JSON.stringify(remoteProviders) !== JSON.stringify(localProviders);
|
|
86
|
+
const rulesChanged = remoteRules !== null && JSON.stringify(remoteRules) !== JSON.stringify(localRules);
|
|
87
|
+
configStatus = {
|
|
88
|
+
hasUpdate: providersChanged || rulesChanged,
|
|
89
|
+
providerChanges: providersChanged ? 1 : 0,
|
|
90
|
+
retryRuleChanges: rulesChanged ? 1 : 0,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
process.stderr.write('[upgrade] failed to check config update\n');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async function check(sourceOverride) {
|
|
98
|
+
await Promise.allSettled([checkNpm(), checkConfig(sourceOverride)]);
|
|
99
|
+
lastCheckedAt = new Date().toISOString();
|
|
100
|
+
}
|
|
101
|
+
function getStatus() {
|
|
102
|
+
return { npm: { ...npmStatus }, config: { ...configStatus }, lastCheckedAt };
|
|
103
|
+
}
|
|
104
|
+
return { check, getStatus };
|
|
105
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
let cachedDeployment = null;
|
|
4
|
+
export function detectDeployment() {
|
|
5
|
+
if (cachedDeployment)
|
|
6
|
+
return cachedDeployment;
|
|
7
|
+
if (existsSync('/.dockerenv') || existsSync('/run/.containerenv')) {
|
|
8
|
+
cachedDeployment = 'docker';
|
|
9
|
+
return cachedDeployment;
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
execSync('npm --version', { stdio: 'pipe', timeout: 3000 });
|
|
13
|
+
cachedDeployment = 'npm';
|
|
14
|
+
return cachedDeployment;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
cachedDeployment = 'unknown';
|
|
18
|
+
return cachedDeployment;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getInstalledVersion(): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
let cachedVersion = null;
|
|
5
|
+
export function getInstalledVersion() {
|
|
6
|
+
if (cachedVersion)
|
|
7
|
+
return cachedVersion;
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const pkgPath = path.resolve(__dirname, '../../package.json');
|
|
10
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
11
|
+
cachedVersion = pkg.version;
|
|
12
|
+
return cachedVersion;
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Ft as e,Nt as t,W as n,at as r,ct as i,q as a,r as o}from"./button-BmxhlpN-.js";var s=[`data-size`],c=a({__name:`Card`,props:{class:{type:[Boolean,null,String,Object,Array]},size:{default:`default`}},setup(a){let c=a;return(l,u)=>(r(),n(`div`,{"data-slot":`card`,"data-size":a.size,class:e(t(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=a({__name:`CardContent`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(a){let s=a;return(a,c)=>(r(),n(`div`,{"data-slot":`card-content`,class:e(t(o)(`px-4 group-data-[size=sm]/card:px-3`,s.class))},[i(a.$slots,`default`)],2))}});export{c as n,l as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Ft as e,Nt as t,W as n,at as r,ct as i,q as a,r as o}from"./button-BmxhlpN-.js";var s=a({__name:`CardHeader`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(a){let s=a;return(a,c)=>(r(),n(`div`,{"data-slot":`card-header`,class:e(t(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(a.$slots,`default`)],2))}}),c=a({__name:`CardTitle`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(a){let s=a;return(a,c)=>(r(),n(`div`,{"data-slot":`card-title`,class:e(t(o)(`text-base leading-snug font-medium group-data-[size=sm]/card:text-sm cn-font-heading`,s.class))},[i(a.$slots,`default`)],2))}});export{s as n,c as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{B as e,H as t,I as n,It as r,K as i,L as a,Nt as o,U as s,Y as c,at as l,ct as u,et as d,i as f,m as p,o as m,q as h,r as g,ut as _,vt as v,x as y}from"./button-BmxhlpN-.js";import{n as b,t as x}from"./ohash.D__AXeF1-BUMsW586.js";import{g as S,o as C,u as w,y as T}from"./Teleport-DVgMe9KS.js";import{n as E}from"./VisuallyHidden-CjuTDGlC.js";import{t as D}from"./VisuallyHiddenInput-BaW-2aEF.js";import{s as O}from"./TableHeader-CIrxcNRh.js";function k(e,t){return S(e)?!1:Array.isArray(e)?e.some(e=>x(e,t)):x(e,t)}var[A,j]=T(`CheckboxGroupRoot`);function M(e){return e===`indeterminate`}function N(e){return M(e)?`indeterminate`:e?`checked`:`unchecked`}var[P,F]=T(`CheckboxRoot`),I=h({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(r,{emit:i}){let c=r,h=i,{forwardRef:g,currentElement:y}=m(),b=A(null),C=p(c,`modelValue`,h,{defaultValue:c.defaultValue??c.falseValue,passive:c.modelValue===void 0}),w=e(()=>b?.disabled.value||c.disabled),T=e(()=>x(C.value,c.trueValue)),j=e(()=>S(b?.modelValue.value)?C.value===`indeterminate`?`indeterminate`:T.value:k(b.modelValue.value,c.value));function P(){if(S(b?.modelValue.value))C.value===`indeterminate`?C.value=c.trueValue:C.value=T.value?c.falseValue:c.trueValue;else{let e=[...b.modelValue.value||[]];if(k(e,c.value)){let t=e.findIndex(e=>x(e,c.value));e.splice(t,1)}else e.push(c.value);b.modelValue.value=e}}let I=E(y),L=e(()=>c.id&&y.value?document.querySelector(`[for="${c.id}"]`)?.innerText:void 0);return F({disabled:w,state:j}),(e,r)=>(l(),t(_(o(b)?.rovingFocus.value?o(O):o(f)),d(e.$attrs,{id:e.id,ref:o(g),role:`checkbox`,"as-child":e.asChild,as:e.as,type:e.as===`button`?`button`:void 0,"aria-checked":o(M)(j.value)?`mixed`:j.value,"aria-required":e.required,"aria-label":e.$attrs[`aria-label`]||L.value,"data-state":o(N)(j.value),"data-disabled":w.value?``:void 0,disabled:w.value,focusable:o(b)?.rovingFocus.value?!w.value:void 0,onKeydown:n(a(()=>{},[`prevent`]),[`enter`]),onClick:P}),{default:v(()=>[u(e.$slots,`default`,{modelValue:o(C),state:j.value}),o(I)&&e.name&&!o(b)?(l(),t(o(D),{key:0,type:`checkbox`,checked:!!j.value,name:e.name,value:e.value,disabled:w.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=h({__name:`CheckboxIndicator`,props:{forceMount:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`span`}},setup(e){let{forwardRef:n}=m(),r=P();return(e,a)=>(l(),t(o(C),{present:e.forceMount||o(M)(o(r).state.value)||o(r).state.value===!0},{default:v(()=>[i(o(f),d({ref:o(n),"data-state":o(N)(o(r).state.value),"data-disabled":o(r).disabled.value?``:void 0,style:{pointerEvents:`none`},"as-child":e.asChild,as:e.as},e.$attrs),{default:v(()=>[u(e.$slots,`default`)]),_:3},16,[`data-state`,`data-disabled`,`as-child`,`as`])]),_:3},8,[`present`]))}}),R=h({__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:n}){let a=e,s=n,f=w(y(a,`class`),s);return(e,n)=>(l(),t(o(I),d({"data-slot":`checkbox`},o(f),{class:o(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`,a.class)}),{default:v(t=>[i(o(L),{"data-slot":`checkbox-indicator`,class:`[&>svg]:size-3.5 grid place-content-center text-current transition-none`},{default:v(()=>[u(e.$slots,`default`,r(c(t)),()=>[i(o(b))])]),_:2},1024)]),_:3},16,[`class`]))}});export{R as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{B as e,Et as t,H as n,It as r,K as i,Nt as a,U as o,Y as s,at as c,ct as l,d as u,et as d,ht as f,i as p,jt as m,m as h,o as g,q as _,rt as v,tt as y,vt as b}from"./button-BmxhlpN-.js";import{c as x,o as S,u as C,y as w}from"./Teleport-DVgMe9KS.js";var[T,E]=w(`CollapsibleRoot`),D=_({__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:r}){let i=e,o=h(i,`open`,r,{defaultValue:i.defaultOpen,passive:i.open===void 0}),{disabled:s,unmountOnHide:u}=m(i);return E({contentId:``,disabled:s,open:o,unmountOnHide:u,onOpenToggle:()=>{s.value||(o.value=!o.value)}}),t({open:o}),g(),(e,t)=>(c(),n(a(p),{as:e.as,"as-child":i.asChild,"data-state":a(o)?`open`:`closed`,"data-disabled":a(s)?``:void 0},{default:b(()=>[l(e.$slots,`default`,{open:a(o)})]),_:3},8,[`as`,`as-child`,`data-state`,`data-disabled`]))}}),O=_({inheritAttrs:!1,__name:`CollapsibleContent`,props:{forceMount:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},emits:[`contentFound`],setup(r,{emit:s}){let m=r,h=s,_=T();_.contentId||=x(void 0,`reka-collapsible-content`);let C=t(),{forwardRef:w,currentElement:E}=g(),D=t(0),O=t(0),k=e(()=>_.open.value),A=t(k.value),j=t();f(()=>[k.value,C.value?.present],async()=>{await y();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=e(()=>A.value&&_.open.value);return v(()=>{requestAnimationFrame(()=>{A.value=!1})}),u(E,`beforematch`,e=>{requestAnimationFrame(()=>{_.onOpenToggle(),h(`contentFound`)})}),(e,t)=>(c(),n(a(S),{ref_key:`presentRef`,ref:C,present:e.forceMount||a(_).open.value,"force-mount":!0},{default:b(({present:t})=>[i(a(p),d(e.$attrs,{id:a(_).contentId,ref:a(w),"as-child":m.asChild,as:e.as,hidden:t?void 0:a(_).unmountOnHide.value?``:`until-found`,"data-state":M.value?void 0:a(_).open.value?`open`:`closed`,"data-disabled":a(_).disabled?.value?``:void 0,style:{"--reka-collapsible-content-height":`${O.value}px`,"--reka-collapsible-content-width":`${D.value}px`}}),{default:b(()=>[!a(_).unmountOnHide.value||t?l(e.$slots,`default`,{key:0}):o(`v-if`,!0)]),_:2},1040,[`id`,`as-child`,`as`,`hidden`,`data-state`,`data-disabled`,`style`])]),_:3},8,[`present`]))}}),k=_({__name:`CollapsibleTrigger`,props:{asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`button`}},setup(e){let t=e;g();let r=T();return(e,i)=>(c(),n(a(p),{type:e.as===`button`?`button`:void 0,as:e.as,"as-child":t.asChild,"aria-controls":a(r).contentId,"aria-expanded":a(r).open.value,"data-state":a(r).open.value?`open`:`closed`,"data-disabled":a(r).disabled?.value?``:void 0,disabled:a(r).disabled?.value,onClick:a(r).onOpenToggle},{default:b(()=>[l(e.$slots,`default`)]),_:3},8,[`type`,`as`,`as-child`,`aria-controls`,`aria-expanded`,`data-state`,`data-disabled`,`disabled`,`onClick`]))}}),A=_({__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 i=C(e,t);return(e,t)=>(c(),n(a(D),d({"data-slot":`collapsible`},a(i)),{default:b(t=>[l(e.$slots,`default`,r(s(t)))]),_:3},16))}}),j=_({__name:`CollapsibleContent`,props:{forceMount:{type:Boolean},asChild:{type:Boolean},as:{}},setup(e){let t=e;return(e,r)=>(c(),n(a(O),d({"data-slot":`collapsible-content`},t),{default:b(()=>[l(e.$slots,`default`)]),_:3},16))}}),M=_({__name:`CollapsibleTrigger`,props:{asChild:{type:Boolean},as:{}},setup(e){let t=e;return(e,r)=>(c(),n(a(k),d({"data-slot":`collapsible-trigger`},t),{default:b(()=>[l(e.$slots,`default`)]),_:3},16))}});export{j as n,A as r,M as t};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{B as e,Ct as t,Et as n,X as r,Z as i,a,gt as o,ht as s,ot as c,q as l,u}from"./button-BmxhlpN-.js";import{h as d}from"./Teleport-DVgMe9KS.js";function f(t){let r=d({dir:n(`ltr`)});return e(()=>t?.value||r.dir?.value||`ltr`)}function p(){let t=n();return{primitiveElement:t,currentElement:e(()=>[`#text`,`#comment`].includes(t.value?.$el.nodeName)?t.value?.$el.nextElementSibling:u(t))}}var m=`data-reka-collection-item`;function h(u={}){let{key:d=``,isProvider:f=!1}=u,h=`${d}CollectionProvider`,g;if(f){let e=n(new Map);g={collectionRef:n(),itemMap:e},c(h,g)}else g=i(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=l({name:`CollectionSlot`,inheritAttrs:!1,setup(e,{slots:t,attrs:n}){let{primitiveElement:i,currentElement:o}=p();return s(o,()=>{g.collectionRef.value=o.value}),()=>r(a,{ref:i,...n},t)}}),y=l({name:`CollectionItem`,inheritAttrs:!1,props:{value:{validator:()=>!0}},setup(e,{slots:n,attrs:i}){let{primitiveElement:s,currentElement:c}=p();return o(n=>{if(c.value){let r=t(c.value);g.itemMap.value.set(r,{ref:c.value,value:e.value}),n(()=>g.itemMap.value.delete(r))}}),()=>r(a,{...i,[m]:``,ref:s},n)}});return{getItems:_,reactiveItems:e(()=>Array.from(g.itemMap.value.values())),itemMapSize:e(()=>g.itemMap.value.size),CollectionSlot:v,CollectionItem:y}}export{p as n,f as r,h as t};
|