llm-simple-router 0.9.24 → 0.9.25
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/providers.d.ts +2 -0
- package/dist/admin/providers.js +42 -1
- package/dist/admin/routes.d.ts +2 -0
- package/dist/admin/routes.js +1 -1
- package/dist/core/container.d.ts +1 -0
- package/dist/core/container.js +1 -0
- package/dist/db/migrations/041_add_provider_proxy.sql +7 -0
- package/dist/db/providers.d.ts +9 -1
- package/dist/db/providers.js +3 -3
- package/dist/index.js +6 -1
- package/dist/proxy/handler/anthropic.js +1 -1
- package/dist/proxy/handler/openai.js +1 -1
- package/dist/proxy/handler/proxy-handler.d.ts +2 -0
- package/dist/proxy/handler/proxy-handler.js +1 -0
- package/dist/proxy/handler/responses.js +1 -1
- package/dist/proxy/transport/http.d.ts +4 -3
- package/dist/proxy/transport/http.js +8 -7
- package/dist/proxy/transport/proxy-agent.d.ts +16 -0
- package/dist/proxy/transport/proxy-agent.js +54 -0
- package/dist/proxy/transport/stream.d.ts +2 -1
- package/dist/proxy/transport/stream.js +2 -2
- package/dist/proxy/transport/transport-fn.d.ts +2 -0
- package/dist/proxy/transport/transport-fn.js +3 -2
- package/frontend-dist/assets/{CardContent-rBUVGzRR.js → CardContent-D2E8XPMF.js} +1 -1
- package/frontend-dist/assets/{CardTitle-DhlJArbg.js → CardTitle-Bvn47Yr0.js} +1 -1
- package/frontend-dist/assets/{Checkbox-uHKeZW1P.js → Checkbox-CHMJbyg6.js} +1 -1
- package/frontend-dist/assets/{CollapsibleContent-B1v6hP95.js → CollapsibleContent-5Mrc8gGt.js} +1 -1
- package/frontend-dist/assets/{CollapsibleTrigger-BmuPdmL7.js → CollapsibleTrigger-DaAYAs8_.js} +1 -1
- package/frontend-dist/assets/{Dashboard-DzhKhSr8.js → Dashboard-JuNvaAgL.js} +1 -1
- package/frontend-dist/assets/{Input-CjLowMgU.js → Input-D34hdiws.js} +1 -1
- package/frontend-dist/assets/{Label-UyzkNwCC.js → Label-NPWP7UVZ.js} +1 -1
- package/frontend-dist/assets/{Login-DkFE8Cwv.js → Login-CiMHu-aw.js} +1 -1
- package/frontend-dist/assets/{Logs-BpPwbhfY.js → Logs-RRwgGUbN.js} +1 -1
- package/frontend-dist/assets/{MappingEntryEditor-CCRjU4OE.js → MappingEntryEditor-B4fiJi8Q.js} +1 -1
- package/frontend-dist/assets/{ModelCard-BVDPemId.js → ModelCard-DSpT9oxm.js} +1 -1
- package/frontend-dist/assets/{ModelMappings-YZj4_coo.js → ModelMappings-CsgtxPOH.js} +1 -1
- package/frontend-dist/assets/{Monitor-ufpuUH7M.js → Monitor-KQ4-zFJ3.js} +1 -1
- package/frontend-dist/assets/Providers-B8kM2PFx.js +1 -0
- package/frontend-dist/assets/{ProxyEnhancement-YzWODq0h.js → ProxyEnhancement-Cczah5af.js} +1 -1
- package/frontend-dist/assets/{QuickSetup-DotjBKIE.js → QuickSetup-BPa2psLw.js} +1 -1
- package/frontend-dist/assets/{RetryRules-CWWOgcSI.js → RetryRules-B4kfx7KE.js} +1 -1
- package/frontend-dist/assets/{RouterKeys-fB8t51xY.js → RouterKeys-B6gaOE5V.js} +1 -1
- package/frontend-dist/assets/{RovingFocusItem-BpkzLwee.js → RovingFocusItem-ZGq4Eu8v.js} +1 -1
- package/frontend-dist/assets/{Schedules-CO4oIdFI.js → Schedules-tkI3OZrg.js} +1 -1
- package/frontend-dist/assets/{Settings--ALxwdhl.js → Settings-DRcVz0VH.js} +1 -1
- package/frontend-dist/assets/{Setup-BdyHuJPP.js → Setup-CPx8uTQg.js} +1 -1
- package/frontend-dist/assets/{Switch-BIriILc9.js → Switch-BgKvsuZd.js} +1 -1
- package/frontend-dist/assets/{TooltipTrigger-Qtuo87Qk.js → TooltipTrigger-C1VLFDy4.js} +1 -1
- package/frontend-dist/assets/{TransformRulesForm-Be1kdxM3.js → TransformRulesForm-CxUVIzWH.js} +1 -1
- package/frontend-dist/assets/{UnifiedRequestDialog-B_xm5uVS.js → UnifiedRequestDialog-D-sQqFxg.js} +1 -1
- package/frontend-dist/assets/{VisuallyHiddenInput-BMd5Xv9A.js → VisuallyHiddenInput-WLtFW_E8.js} +1 -1
- package/frontend-dist/assets/{button-CU50mAde.js → button-CGbcdJgN.js} +2 -2
- package/frontend-dist/assets/{copy-5_1ty9pl.js → copy-CpYWP1uM.js} +1 -1
- package/frontend-dist/assets/{dialog-BP3UfrX6.js → dialog-CWAFinoK.js} +1 -1
- package/frontend-dist/assets/{index-CgpVhJTP.js → index-DDiesvp7.js} +1 -1
- package/frontend-dist/assets/providers-BsmC9XuH.js +1 -0
- package/frontend-dist/assets/providers-DWTdDUnh.js +1 -0
- package/frontend-dist/assets/{trash-2-BtGlrJP_.js → trash-2-CAPUkICH.js} +1 -1
- package/frontend-dist/assets/{useClipboard-eO8uMHXU.js → useClipboard-CLRvABjT.js} +1 -1
- package/frontend-dist/assets/{useLogRetention-Dz-o7K7g.js → useLogRetention-B7v1HgoB.js} +1 -1
- package/frontend-dist/index.html +2 -2
- package/package.json +5 -3
- package/frontend-dist/assets/Providers-C1WoYsgN.js +0 -1
- package/frontend-dist/assets/providers-BS8I-elL.js +0 -1
- package/frontend-dist/assets/providers-Bgp5lP_R.js +0 -1
|
@@ -3,11 +3,13 @@ import Database from "better-sqlite3";
|
|
|
3
3
|
import type { StateRegistry } from "../core/registry.js";
|
|
4
4
|
import type { AdaptiveController } from "@llm-router/core/concurrency";
|
|
5
5
|
import type { RequestTracker } from "@llm-router/core/monitor";
|
|
6
|
+
import type { ProxyAgentFactory } from "../proxy/transport/proxy-agent.js";
|
|
6
7
|
interface ProviderRoutesOptions {
|
|
7
8
|
db: Database.Database;
|
|
8
9
|
stateRegistry?: StateRegistry;
|
|
9
10
|
tracker?: RequestTracker;
|
|
10
11
|
adaptiveController?: AdaptiveController;
|
|
12
|
+
proxyAgentFactory?: ProxyAgentFactory;
|
|
11
13
|
}
|
|
12
14
|
export declare const adminProviderRoutes: FastifyPluginCallback<ProviderRoutesOptions>;
|
|
13
15
|
export {};
|
package/dist/admin/providers.js
CHANGED
|
@@ -96,6 +96,10 @@ const CreateProviderSchema = Type.Object({
|
|
|
96
96
|
queue_timeout_ms: Type.Optional(Type.Integer({ minimum: 0 })),
|
|
97
97
|
max_queue_size: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
98
98
|
adaptive_enabled: Type.Optional(Type.Integer({ minimum: 0, maximum: 1 })),
|
|
99
|
+
proxy_type: Type.Optional(Type.Union([Type.Literal("http"), Type.Literal("socks5")])),
|
|
100
|
+
proxy_url: Type.Optional(Type.String({ minLength: 1 })),
|
|
101
|
+
proxy_username: Type.Optional(Type.String()),
|
|
102
|
+
proxy_password: Type.Optional(Type.String()),
|
|
99
103
|
});
|
|
100
104
|
const UpdateProviderSchema = Type.Object({
|
|
101
105
|
name: Type.Optional(Type.String({ minLength: 1 })),
|
|
@@ -113,9 +117,13 @@ const UpdateProviderSchema = Type.Object({
|
|
|
113
117
|
queue_timeout_ms: Type.Optional(Type.Integer({ minimum: 0 })),
|
|
114
118
|
max_queue_size: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
115
119
|
adaptive_enabled: Type.Optional(Type.Integer({ minimum: 0, maximum: 1 })),
|
|
120
|
+
proxy_type: Type.Optional(Type.Union([Type.Literal("http"), Type.Literal("socks5")])),
|
|
121
|
+
proxy_url: Type.Optional(Type.String({ minLength: 1 })),
|
|
122
|
+
proxy_username: Type.Optional(Type.String()),
|
|
123
|
+
proxy_password: Type.Optional(Type.String()),
|
|
116
124
|
});
|
|
117
125
|
export const adminProviderRoutes = (app, options, done) => {
|
|
118
|
-
const { db, stateRegistry, tracker, adaptiveController } = options;
|
|
126
|
+
const { db, stateRegistry, tracker, adaptiveController, proxyAgentFactory } = options;
|
|
119
127
|
app.get("/admin/api/providers", async (_request, reply) => {
|
|
120
128
|
const encryptionKey = getSetting(db, "encryption_key");
|
|
121
129
|
const providers = getAllProviders(db);
|
|
@@ -134,6 +142,10 @@ export const adminProviderRoutes = (app, options, done) => {
|
|
|
134
142
|
queue_timeout_ms: s.queue_timeout_ms,
|
|
135
143
|
max_queue_size: s.max_queue_size,
|
|
136
144
|
adaptive_enabled: s.adaptive_enabled,
|
|
145
|
+
proxy_type: s.proxy_type,
|
|
146
|
+
proxy_url: s.proxy_url,
|
|
147
|
+
proxy_username: s.proxy_username ? decrypt(s.proxy_username, encryptionKey) : null,
|
|
148
|
+
proxy_password: s.proxy_password ? decrypt(s.proxy_password, encryptionKey) : null,
|
|
137
149
|
concurrency_status: stateRegistry?.getProviderStatus(s.id) ?? { active: 0, queued: 0 },
|
|
138
150
|
created_at: s.created_at,
|
|
139
151
|
updated_at: s.updated_at,
|
|
@@ -152,6 +164,11 @@ export const adminProviderRoutes = (app, options, done) => {
|
|
|
152
164
|
const encryptedKey = encrypt(body.api_key, getSetting(db, "encryption_key"));
|
|
153
165
|
const { entries: normalizedModels, overrides: contextOverrides } = extractModelOverrides((body.models ?? []));
|
|
154
166
|
const isAdaptiveEnabled = body.adaptive_enabled ?? 0;
|
|
167
|
+
if (body.proxy_type && !body.proxy_url) {
|
|
168
|
+
return reply.code(HTTP_BAD_REQUEST).send(apiError(API_CODE.VALIDATION_FAILED, "proxy_url is required when proxy_type is set"));
|
|
169
|
+
}
|
|
170
|
+
const encryptedProxyUsername = body.proxy_username ? encrypt(body.proxy_username, getSetting(db, "encryption_key")) : null;
|
|
171
|
+
const encryptedProxyPassword = body.proxy_password ? encrypt(body.proxy_password, getSetting(db, "encryption_key")) : null;
|
|
155
172
|
const id = createProvider(db, {
|
|
156
173
|
name: body.name,
|
|
157
174
|
api_type: body.api_type,
|
|
@@ -165,6 +182,10 @@ export const adminProviderRoutes = (app, options, done) => {
|
|
|
165
182
|
queue_timeout_ms: body.queue_timeout_ms ?? PROVIDER_CONCURRENCY_DEFAULTS.queue_timeout_ms,
|
|
166
183
|
max_queue_size: body.max_queue_size ?? PROVIDER_CONCURRENCY_DEFAULTS.max_queue_size,
|
|
167
184
|
adaptive_enabled: isAdaptiveEnabled,
|
|
185
|
+
proxy_type: body.proxy_type ?? null,
|
|
186
|
+
proxy_url: body.proxy_type ? body.proxy_url : null,
|
|
187
|
+
proxy_username: encryptedProxyUsername,
|
|
188
|
+
proxy_password: encryptedProxyPassword,
|
|
168
189
|
});
|
|
169
190
|
if (contextOverrides.length > 0) {
|
|
170
191
|
setModelInfoForProvider(db, id, contextOverrides.map(o => ({ model_name: o.name, context_window: o.context_window })));
|
|
@@ -234,7 +255,26 @@ export const adminProviderRoutes = (app, options, done) => {
|
|
|
234
255
|
fields.api_key = encrypt(body.api_key, getSetting(db, "encryption_key"));
|
|
235
256
|
fields.api_key_preview = body.api_key.length > API_KEY_PREVIEW_MIN_LENGTH ? `${body.api_key.slice(0, API_KEY_PREVIEW_PREFIX_LEN)}...${body.api_key.slice(-API_KEY_PREVIEW_PREFIX_LEN)}` : "****";
|
|
236
257
|
}
|
|
258
|
+
// Proxy field handling
|
|
259
|
+
if (body.proxy_type !== undefined) {
|
|
260
|
+
fields.proxy_type = body.proxy_type || null;
|
|
261
|
+
if (!body.proxy_type) {
|
|
262
|
+
fields.proxy_url = null;
|
|
263
|
+
fields.proxy_username = null;
|
|
264
|
+
fields.proxy_password = null;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
if (body.proxy_url !== undefined && body.proxy_type) {
|
|
268
|
+
fields.proxy_url = body.proxy_url;
|
|
269
|
+
}
|
|
270
|
+
if (body.proxy_username !== undefined && body.proxy_type) {
|
|
271
|
+
fields.proxy_username = body.proxy_username ? encrypt(body.proxy_username, getSetting(db, "encryption_key")) : null;
|
|
272
|
+
}
|
|
273
|
+
if (body.proxy_password !== undefined && body.proxy_type) {
|
|
274
|
+
fields.proxy_password = body.proxy_password ? encrypt(body.proxy_password, getSetting(db, "encryption_key")) : null;
|
|
275
|
+
}
|
|
237
276
|
updateProvider(db, id, fields);
|
|
277
|
+
proxyAgentFactory?.invalidate(id);
|
|
238
278
|
const updated = getProviderById(db, id);
|
|
239
279
|
let cascade;
|
|
240
280
|
if (existing.is_active === 1 && body.is_active === 0) {
|
|
@@ -327,6 +367,7 @@ export const adminProviderRoutes = (app, options, done) => {
|
|
|
327
367
|
continue;
|
|
328
368
|
}
|
|
329
369
|
}
|
|
370
|
+
proxyAgentFactory?.invalidate(id);
|
|
330
371
|
deleteProvider(db, id);
|
|
331
372
|
stateRegistry?.removeProvider(id);
|
|
332
373
|
adaptiveController?.remove(id);
|
package/dist/admin/routes.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import Database from "better-sqlite3";
|
|
|
3
3
|
import type { StateRegistry } from "../core/registry.js";
|
|
4
4
|
import type { RequestTracker } from "@llm-router/core/monitor";
|
|
5
5
|
import type { AdaptiveController } from "@llm-router/core/concurrency";
|
|
6
|
+
import type { ProxyAgentFactory } from "../proxy/transport/proxy-agent.js";
|
|
6
7
|
interface AdminRoutesOptions {
|
|
7
8
|
db: Database.Database;
|
|
8
9
|
stateRegistry: StateRegistry;
|
|
@@ -12,6 +13,7 @@ interface AdminRoutesOptions {
|
|
|
12
13
|
logsDir?: string;
|
|
13
14
|
pluginRegistry?: import("../proxy/transform/plugin-registry.js").PluginRegistry;
|
|
14
15
|
closeFn?: () => Promise<void>;
|
|
16
|
+
proxyAgentFactory?: ProxyAgentFactory;
|
|
15
17
|
}
|
|
16
18
|
export declare const adminRoutes: FastifyPluginCallback<AdminRoutesOptions>;
|
|
17
19
|
export {};
|
package/dist/admin/routes.js
CHANGED
|
@@ -23,7 +23,7 @@ export const adminRoutes = (app, options, done) => {
|
|
|
23
23
|
app.register(adminSetupRoutes, { db: options.db });
|
|
24
24
|
app.register(adminAuthPlugin, { db: options.db });
|
|
25
25
|
app.register(adminLoginRoutes, { db: options.db });
|
|
26
|
-
app.register(adminProviderRoutes, { db: options.db, stateRegistry: options.stateRegistry, tracker: options.tracker, adaptiveController: options.adaptiveController });
|
|
26
|
+
app.register(adminProviderRoutes, { db: options.db, stateRegistry: options.stateRegistry, tracker: options.tracker, adaptiveController: options.adaptiveController, proxyAgentFactory: options.proxyAgentFactory });
|
|
27
27
|
app.register(adminMappingRoutes, { db: options.db });
|
|
28
28
|
app.register(adminGroupRoutes, { db: options.db });
|
|
29
29
|
app.register(adminScheduleRoutes, { db: options.db });
|
package/dist/core/container.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export declare const SERVICE_KEYS: {
|
|
|
9
9
|
readonly adaptiveController: "adaptiveController";
|
|
10
10
|
readonly pluginRegistry: "pluginRegistry";
|
|
11
11
|
readonly logFileWriter: "logFileWriter";
|
|
12
|
+
readonly proxyAgentFactory: "proxyAgentFactory";
|
|
12
13
|
};
|
|
13
14
|
export type ServiceKey = (typeof SERVICE_KEYS)[keyof typeof SERVICE_KEYS];
|
|
14
15
|
/**
|
package/dist/core/container.js
CHANGED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
-- 041_add_provider_proxy.sql
|
|
2
|
+
-- Add per-provider proxy support (SOCKS5 / HTTP CONNECT)
|
|
3
|
+
|
|
4
|
+
ALTER TABLE providers ADD COLUMN proxy_type TEXT DEFAULT NULL;
|
|
5
|
+
ALTER TABLE providers ADD COLUMN proxy_url TEXT DEFAULT NULL;
|
|
6
|
+
ALTER TABLE providers ADD COLUMN proxy_username TEXT DEFAULT NULL;
|
|
7
|
+
ALTER TABLE providers ADD COLUMN proxy_password TEXT DEFAULT NULL;
|
package/dist/db/providers.d.ts
CHANGED
|
@@ -13,6 +13,10 @@ export interface Provider {
|
|
|
13
13
|
queue_timeout_ms: number;
|
|
14
14
|
max_queue_size: number;
|
|
15
15
|
adaptive_enabled: number;
|
|
16
|
+
proxy_type: string | null;
|
|
17
|
+
proxy_url: string | null;
|
|
18
|
+
proxy_username: string | null;
|
|
19
|
+
proxy_password: string | null;
|
|
16
20
|
created_at: string;
|
|
17
21
|
updated_at: string;
|
|
18
22
|
}
|
|
@@ -41,8 +45,12 @@ export declare function createProvider(db: Database.Database, provider: {
|
|
|
41
45
|
queue_timeout_ms?: number;
|
|
42
46
|
max_queue_size?: number;
|
|
43
47
|
adaptive_enabled?: number;
|
|
48
|
+
proxy_type?: string | null;
|
|
49
|
+
proxy_url?: string | null;
|
|
50
|
+
proxy_username?: string | null;
|
|
51
|
+
proxy_password?: string | null;
|
|
44
52
|
}): string;
|
|
45
|
-
export declare function updateProvider(db: Database.Database, id: string, fields: Partial<Pick<Provider, "name" | "api_type" | "base_url" | "upstream_path" | "api_key" | "api_key_preview" | "models" | "is_active" | "max_concurrency" | "queue_timeout_ms" | "max_queue_size" | "adaptive_enabled">>): void;
|
|
53
|
+
export declare function updateProvider(db: Database.Database, id: string, fields: Partial<Pick<Provider, "name" | "api_type" | "base_url" | "upstream_path" | "api_key" | "api_key_preview" | "models" | "is_active" | "max_concurrency" | "queue_timeout_ms" | "max_queue_size" | "adaptive_enabled" | "proxy_type" | "proxy_url" | "proxy_username" | "proxy_password">>): void;
|
|
46
54
|
export declare function deleteProvider(db: Database.Database, id: string): void;
|
|
47
55
|
export declare function getActiveProviderByName(db: Database.Database, name: string): {
|
|
48
56
|
id: string;
|
package/dist/db/providers.js
CHANGED
|
@@ -37,7 +37,7 @@ export const PROVIDER_CONCURRENCY_DEFAULTS = {
|
|
|
37
37
|
max_queue_size: 100,
|
|
38
38
|
};
|
|
39
39
|
const PROVIDER_FIELDS = new Set([
|
|
40
|
-
"name", "api_type", "base_url", "upstream_path", "api_key", "api_key_preview", "models", "is_active", "max_concurrency", "queue_timeout_ms", "max_queue_size", "adaptive_enabled",
|
|
40
|
+
"name", "api_type", "base_url", "upstream_path", "api_key", "api_key_preview", "models", "is_active", "max_concurrency", "queue_timeout_ms", "max_queue_size", "adaptive_enabled", "proxy_type", "proxy_url", "proxy_username", "proxy_password",
|
|
41
41
|
]);
|
|
42
42
|
export function getActiveProviders(db, apiType) {
|
|
43
43
|
return db
|
|
@@ -53,8 +53,8 @@ export function getProviderById(db, id) {
|
|
|
53
53
|
export function createProvider(db, provider) {
|
|
54
54
|
const id = randomUUID();
|
|
55
55
|
const now = new Date().toISOString();
|
|
56
|
-
db.prepare(`INSERT INTO providers (id, name, api_type, base_url, upstream_path, api_key, api_key_preview, models, is_active, max_concurrency, queue_timeout_ms, max_queue_size, adaptive_enabled, created_at, updated_at)
|
|
57
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, provider.name, provider.api_type, provider.base_url, provider.upstream_path ?? null, provider.api_key, provider.api_key_preview ?? null, provider.models ?? "[]", provider.is_active ?? 1, provider.max_concurrency ?? PROVIDER_CONCURRENCY_DEFAULTS.max_concurrency, provider.queue_timeout_ms ?? PROVIDER_CONCURRENCY_DEFAULTS.queue_timeout_ms, provider.max_queue_size ?? PROVIDER_CONCURRENCY_DEFAULTS.max_queue_size, provider.adaptive_enabled ?? 0, now, now);
|
|
56
|
+
db.prepare(`INSERT INTO providers (id, name, api_type, base_url, upstream_path, api_key, api_key_preview, models, is_active, max_concurrency, queue_timeout_ms, max_queue_size, adaptive_enabled, proxy_type, proxy_url, proxy_username, proxy_password, created_at, updated_at)
|
|
57
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, provider.name, provider.api_type, provider.base_url, provider.upstream_path ?? null, provider.api_key, provider.api_key_preview ?? null, provider.models ?? "[]", provider.is_active ?? 1, provider.max_concurrency ?? PROVIDER_CONCURRENCY_DEFAULTS.max_concurrency, provider.queue_timeout_ms ?? PROVIDER_CONCURRENCY_DEFAULTS.queue_timeout_ms, provider.max_queue_size ?? PROVIDER_CONCURRENCY_DEFAULTS.max_queue_size, provider.adaptive_enabled ?? 0, provider.proxy_type ?? null, provider.proxy_url ?? null, provider.proxy_username ?? null, provider.proxy_password ?? null, now, now);
|
|
58
58
|
return id;
|
|
59
59
|
}
|
|
60
60
|
export function updateProvider(db, id, fields) {
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ import { startUpgradeChecker, stopUpgradeChecker } from "./admin/upgrade.js";
|
|
|
31
31
|
import fastifyStatic from "@fastify/static";
|
|
32
32
|
import { ServiceContainer, SERVICE_KEYS } from "./core/container.js";
|
|
33
33
|
import { LogFileWriter } from "./storage/log-file-writer.js";
|
|
34
|
+
import { ProxyAgentFactory } from "./proxy/transport/proxy-agent.js";
|
|
34
35
|
import { scheduleLogFileMaintenance } from "./storage/log-file-compressor.js";
|
|
35
36
|
import { getDetailLogEnabled, getLogFileRetentionDays } from "./db/settings.js";
|
|
36
37
|
import { dirname, join } from "node:path";
|
|
@@ -215,12 +216,15 @@ export async function buildApp(options) {
|
|
|
215
216
|
const pluginsDir = path.resolve(__dirname, "../plugins/transform");
|
|
216
217
|
pluginRegistry.scanPluginsDir(pluginsDir);
|
|
217
218
|
container.register(SERVICE_KEYS.pluginRegistry, () => pluginRegistry);
|
|
219
|
+
// 注册 ProxyAgentFactory
|
|
220
|
+
container.register(SERVICE_KEYS.proxyAgentFactory, () => new ProxyAgentFactory());
|
|
218
221
|
// 从容器解析所有服务
|
|
219
222
|
const matcher = container.resolve(SERVICE_KEYS.matcher);
|
|
220
223
|
const semaphoreManager = container.resolve(SERVICE_KEYS.semaphoreManager);
|
|
221
224
|
const tracker = container.resolve(SERVICE_KEYS.tracker);
|
|
222
225
|
const usageWindowTracker = container.resolve(SERVICE_KEYS.usageWindowTracker);
|
|
223
226
|
const adaptiveController = container.resolve(SERVICE_KEYS.adaptiveController);
|
|
227
|
+
const proxyAgentFactory = container.resolve(SERVICE_KEYS.proxyAgentFactory);
|
|
224
228
|
// Wire adaptive controller to tracker
|
|
225
229
|
tracker.setAdaptiveStatusProvider(adaptiveController);
|
|
226
230
|
// 从 DB 读取已有 provider 的并发配置,初始化信号量/adaptive/tracker(共享逻辑)
|
|
@@ -247,7 +251,7 @@ export async function buildApp(options) {
|
|
|
247
251
|
// Late-bound close ref — close 函数在 adminRoutes 注册之后才定义,
|
|
248
252
|
// 但 restart API 需要在运行时调用它
|
|
249
253
|
const closeRef = { fn: async () => { } };
|
|
250
|
-
app.register(adminRoutes, { db, stateRegistry, tracker, adaptiveController, logFileWriter, logsDir, closeFn: () => closeRef.fn(), pluginRegistry });
|
|
254
|
+
app.register(adminRoutes, { db, stateRegistry, tracker, adaptiveController, logFileWriter, logsDir, closeFn: () => closeRef.fn(), pluginRegistry, proxyAgentFactory });
|
|
251
255
|
// 前端静态文件服务(生产环境)
|
|
252
256
|
const frontendDist = path.resolve(process.env.FRONTEND_DIST || path.join(__dirname, "../frontend-dist"));
|
|
253
257
|
if (existsSync(frontendDist)) {
|
|
@@ -287,6 +291,7 @@ export async function buildApp(options) {
|
|
|
287
291
|
// 关闭所有 SSE 长连接,防止 app.close() 因 hijack 的连接无限等待
|
|
288
292
|
tracker.closeAllClients();
|
|
289
293
|
semaphoreManager.removeAll();
|
|
294
|
+
proxyAgentFactory.invalidateAll();
|
|
290
295
|
const sessionTracker = container.resolve(SERVICE_KEYS.sessionTracker);
|
|
291
296
|
sessionTracker.stop();
|
|
292
297
|
await app.close();
|
|
@@ -35,7 +35,7 @@ const anthropicProxyRaw = (app, opts, done) => {
|
|
|
35
35
|
const e = anthropicErrors.providerUnavailable();
|
|
36
36
|
return reply.code(e.statusCode).send(e.body);
|
|
37
37
|
}
|
|
38
|
-
const deps = { db, orchestrator, container };
|
|
38
|
+
const deps = { db, orchestrator, container, proxyAgentFactory: container.resolve(SERVICE_KEYS.proxyAgentFactory) };
|
|
39
39
|
return handleProxyRequest(request, reply, "anthropic", MESSAGES_PATH, anthropicErrors, deps);
|
|
40
40
|
});
|
|
41
41
|
done();
|
|
@@ -41,7 +41,7 @@ const openaiProxyRaw = (app, opts, done) => {
|
|
|
41
41
|
});
|
|
42
42
|
return sendError(reply, openaiErrors.providerUnavailable());
|
|
43
43
|
}
|
|
44
|
-
const deps = { db, orchestrator, container };
|
|
44
|
+
const deps = { db, orchestrator, container, proxyAgentFactory: container.resolve(SERVICE_KEYS.proxyAgentFactory) };
|
|
45
45
|
return handleProxyRequest(request, reply, "openai", CHAT_COMPLETIONS_PATH, openaiErrors, deps, {
|
|
46
46
|
beforeSendProxy: (body, isStream) => {
|
|
47
47
|
if (isStream && !body.stream_options) {
|
|
@@ -2,10 +2,12 @@ import type { FastifyReply, FastifyRequest } from "fastify";
|
|
|
2
2
|
import Database from "better-sqlite3";
|
|
3
3
|
import type { ProxyOrchestrator } from "../orchestration/orchestrator.js";
|
|
4
4
|
import type { ProxyErrorFormatter } from "../proxy-core.js";
|
|
5
|
+
import type { ProxyAgentFactory } from "../transport/proxy-agent.js";
|
|
5
6
|
export interface RouteHandlerDeps {
|
|
6
7
|
db: Database.Database;
|
|
7
8
|
orchestrator: ProxyOrchestrator;
|
|
8
9
|
container: ServiceContainer;
|
|
10
|
+
proxyAgentFactory?: ProxyAgentFactory;
|
|
9
11
|
}
|
|
10
12
|
import type { ServiceContainer } from "../../core/container.js";
|
|
11
13
|
export declare function handleProxyRequest(request: FastifyRequest, reply: FastifyReply, apiType: "openai" | "openai-responses" | "anthropic", upstreamPath: string, errors: ProxyErrorFormatter, deps: RouteHandlerDeps, options?: {
|
|
@@ -303,6 +303,7 @@ async function executeFailoverLoop(ctx) {
|
|
|
303
303
|
streamTimeoutMs: getModelStreamTimeout(provider, resolved.backend_model), tracker, matcher, request,
|
|
304
304
|
streamLoopEnabled, formatTransform, responseTransform, injectedHeaders,
|
|
305
305
|
timeoutContext: { modelId: resolved.backend_model, providerId: provider.id },
|
|
306
|
+
proxyAgentFactory: deps.proxyAgentFactory,
|
|
306
307
|
});
|
|
307
308
|
const pipelineSnapshot = iterationSnapshot.toJSON();
|
|
308
309
|
try {
|
|
@@ -38,7 +38,7 @@ const responsesProxyRaw = (app, opts, done) => {
|
|
|
38
38
|
});
|
|
39
39
|
return sendError(reply, responsesErrors.providerUnavailable());
|
|
40
40
|
}
|
|
41
|
-
const deps = { db, orchestrator, container };
|
|
41
|
+
const deps = { db, orchestrator, container, proxyAgentFactory: container.resolve(SERVICE_KEYS.proxyAgentFactory) };
|
|
42
42
|
return handleProxyRequest(request, reply, "openai-responses", RESPONSES_PATH, responsesErrors, deps);
|
|
43
43
|
};
|
|
44
44
|
app.post(RESPONSES_PATH, handleResponses);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Agent } from "http";
|
|
1
2
|
import type { RawHeaders, TransportResult } from "../types.js";
|
|
2
3
|
export { callStream } from "./stream.js";
|
|
3
4
|
export interface UpstreamRequestOptions {
|
|
@@ -8,13 +9,13 @@ export interface UpstreamRequestOptions {
|
|
|
8
9
|
headers: Record<string, string>;
|
|
9
10
|
}
|
|
10
11
|
export declare const _transportInternals: {
|
|
11
|
-
createUpstreamRequest(url: URL, options: UpstreamRequestOptions): import("http").ClientRequest;
|
|
12
|
+
createUpstreamRequest(url: URL, options: UpstreamRequestOptions, agent?: Agent): import("http").ClientRequest;
|
|
12
13
|
};
|
|
13
14
|
export declare function buildRequestOptions(url: URL, headers: Record<string, string>, method?: string): UpstreamRequestOptions;
|
|
14
15
|
export type BuildHeadersFn = (cliHdrs: RawHeaders, key: string, bytes?: number) => Record<string, string>;
|
|
15
16
|
export declare function callNonStream(backend: {
|
|
16
17
|
base_url: string;
|
|
17
|
-
}, apiKey: string, body: Record<string, unknown>, clientHeaders: RawHeaders, upstreamPath: string, buildHeaders: BuildHeadersFn): Promise<TransportResult>;
|
|
18
|
+
}, apiKey: string, body: Record<string, unknown>, clientHeaders: RawHeaders, upstreamPath: string, buildHeaders: BuildHeadersFn, agent?: Agent): Promise<TransportResult>;
|
|
18
19
|
export interface GetTransportResult {
|
|
19
20
|
statusCode: number;
|
|
20
21
|
body: string;
|
|
@@ -22,4 +23,4 @@ export interface GetTransportResult {
|
|
|
22
23
|
}
|
|
23
24
|
export declare function callGet(backend: {
|
|
24
25
|
base_url: string;
|
|
25
|
-
}, apiKey: string, clientHeaders: RawHeaders, upstreamPath: string, buildHeaders: (cliHdrs: RawHeaders, key: string) => Record<string, string
|
|
26
|
+
}, apiKey: string, clientHeaders: RawHeaders, upstreamPath: string, buildHeaders: (cliHdrs: RawHeaders, key: string) => Record<string, string>, agent?: Agent): Promise<GetTransportResult>;
|
|
@@ -10,10 +10,11 @@ const UPSTREAM_SUCCESS_RANGE = 100;
|
|
|
10
10
|
const HTTPS_DEFAULT_PORT = 443;
|
|
11
11
|
const HTTP_DEFAULT_PORT = 80;
|
|
12
12
|
export const _transportInternals = {
|
|
13
|
-
createUpstreamRequest(url, options) {
|
|
13
|
+
createUpstreamRequest(url, options, agent) {
|
|
14
|
+
const opts = agent ? { ...options, agent } : options;
|
|
14
15
|
return url.protocol === "https:"
|
|
15
|
-
? httpsRequestFn(
|
|
16
|
-
: httpRequestFn(
|
|
16
|
+
? httpsRequestFn(opts)
|
|
17
|
+
: httpRequestFn(opts);
|
|
17
18
|
},
|
|
18
19
|
};
|
|
19
20
|
export function buildRequestOptions(url, headers, method = "POST") {
|
|
@@ -27,13 +28,13 @@ export function buildRequestOptions(url, headers, method = "POST") {
|
|
|
27
28
|
};
|
|
28
29
|
}
|
|
29
30
|
// ---------- callNonStream ----------
|
|
30
|
-
export function callNonStream(backend, apiKey, body, clientHeaders, upstreamPath, buildHeaders) {
|
|
31
|
+
export function callNonStream(backend, apiKey, body, clientHeaders, upstreamPath, buildHeaders, agent) {
|
|
31
32
|
return new Promise((resolve) => {
|
|
32
33
|
const url = new URL(buildUpstreamUrl(backend.base_url, upstreamPath));
|
|
33
34
|
const payload = JSON.stringify(body);
|
|
34
35
|
const upstreamHeaders = buildHeaders(clientHeaders, apiKey, Buffer.byteLength(payload));
|
|
35
36
|
const options = buildRequestOptions(url, upstreamHeaders);
|
|
36
|
-
const req = _transportInternals.createUpstreamRequest(url, options);
|
|
37
|
+
const req = _transportInternals.createUpstreamRequest(url, options, agent);
|
|
37
38
|
req.on("response", (res) => {
|
|
38
39
|
const chunks = [];
|
|
39
40
|
res.on("data", (chunk) => chunks.push(chunk));
|
|
@@ -68,12 +69,12 @@ export function callNonStream(backend, apiKey, body, clientHeaders, upstreamPath
|
|
|
68
69
|
req.end();
|
|
69
70
|
});
|
|
70
71
|
}
|
|
71
|
-
export function callGet(backend, apiKey, clientHeaders, upstreamPath, buildHeaders) {
|
|
72
|
+
export function callGet(backend, apiKey, clientHeaders, upstreamPath, buildHeaders, agent) {
|
|
72
73
|
return new Promise((resolve, reject) => {
|
|
73
74
|
const url = new URL(buildUpstreamUrl(backend.base_url, upstreamPath));
|
|
74
75
|
const headers = buildHeaders(clientHeaders, apiKey);
|
|
75
76
|
const options = buildRequestOptions(url, headers, "GET");
|
|
76
|
-
const req = _transportInternals.createUpstreamRequest(url, options);
|
|
77
|
+
const req = _transportInternals.createUpstreamRequest(url, options, agent);
|
|
77
78
|
req.on("response", (res) => {
|
|
78
79
|
const chunks = [];
|
|
79
80
|
res.on("data", (chunk) => chunks.push(chunk));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Agent } from "http";
|
|
2
|
+
export interface ProxyConfig {
|
|
3
|
+
id: string;
|
|
4
|
+
proxy_type: string | null;
|
|
5
|
+
proxy_url: string | null;
|
|
6
|
+
proxy_username: string | null;
|
|
7
|
+
proxy_password: string | null;
|
|
8
|
+
}
|
|
9
|
+
export declare class ProxyAgentFactory {
|
|
10
|
+
private readonly cache;
|
|
11
|
+
getAgent(provider: ProxyConfig): Agent | undefined;
|
|
12
|
+
invalidate(providerId: string): void;
|
|
13
|
+
invalidateAll(): void;
|
|
14
|
+
private createAgent;
|
|
15
|
+
private buildProxyUrl;
|
|
16
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { HttpsProxyAgent } from "https-proxy-agent";
|
|
2
|
+
import { SocksProxyAgent } from "socks-proxy-agent";
|
|
3
|
+
export class ProxyAgentFactory {
|
|
4
|
+
cache = new Map();
|
|
5
|
+
getAgent(provider) {
|
|
6
|
+
if (!provider.proxy_type || !provider.proxy_url) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
const fullUrl = this.buildProxyUrl(provider);
|
|
10
|
+
const cached = this.cache.get(provider.id);
|
|
11
|
+
if (cached && cached.proxyUrl === fullUrl) {
|
|
12
|
+
return cached.agent;
|
|
13
|
+
}
|
|
14
|
+
if (cached) {
|
|
15
|
+
cached.agent.destroy();
|
|
16
|
+
this.cache.delete(provider.id);
|
|
17
|
+
}
|
|
18
|
+
const agent = this.createAgent(provider.proxy_type, fullUrl);
|
|
19
|
+
this.cache.set(provider.id, { agent, proxyUrl: fullUrl });
|
|
20
|
+
return agent;
|
|
21
|
+
}
|
|
22
|
+
invalidate(providerId) {
|
|
23
|
+
const cached = this.cache.get(providerId);
|
|
24
|
+
if (cached) {
|
|
25
|
+
cached.agent.destroy();
|
|
26
|
+
this.cache.delete(providerId);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
invalidateAll() {
|
|
30
|
+
for (const cached of this.cache.values()) {
|
|
31
|
+
cached.agent.destroy();
|
|
32
|
+
}
|
|
33
|
+
this.cache.clear();
|
|
34
|
+
}
|
|
35
|
+
createAgent(proxyType, proxyUrl) {
|
|
36
|
+
if (proxyType === "socks5") {
|
|
37
|
+
return new SocksProxyAgent(proxyUrl);
|
|
38
|
+
}
|
|
39
|
+
return new HttpsProxyAgent(proxyUrl);
|
|
40
|
+
}
|
|
41
|
+
buildProxyUrl(provider) {
|
|
42
|
+
let url = provider.proxy_url;
|
|
43
|
+
const username = provider.proxy_username;
|
|
44
|
+
const password = provider.proxy_password;
|
|
45
|
+
if (username) {
|
|
46
|
+
const parsed = new URL(url);
|
|
47
|
+
parsed.username = username;
|
|
48
|
+
if (password)
|
|
49
|
+
parsed.password = password;
|
|
50
|
+
url = parsed.toString();
|
|
51
|
+
}
|
|
52
|
+
return url;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Agent } from "http";
|
|
1
2
|
import type { FastifyReply } from "fastify";
|
|
2
3
|
import type { RawHeaders, TransportResult } from "../types.js";
|
|
3
4
|
import type { SSEMetricsTransform } from "../../metrics/sse-metrics-transform.js";
|
|
@@ -8,4 +9,4 @@ export declare function callStream(backend: {
|
|
|
8
9
|
}, apiKey: string, body: Record<string, unknown>, clientHeaders: RawHeaders, reply: FastifyReply, timeoutMs: number, upstreamPath: string, buildHeaders: BuildHeadersFn, metricsTransform?: SSEMetricsTransform, checkEarlyError?: (bufferedData: string) => boolean, compatResolve?: (result: TransportResult) => void, loopGuard?: StreamLoopGuard, formatTransform?: import("stream").Transform, timeoutContext?: {
|
|
9
10
|
modelId: string;
|
|
10
11
|
providerId: string;
|
|
11
|
-
}, onTimeoutAbort?: () => void): Promise<TransportResult>;
|
|
12
|
+
}, onTimeoutAbort?: () => void, agent?: Agent): Promise<TransportResult>;
|
|
@@ -307,14 +307,14 @@ class StreamProxy {
|
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
// ---------- callStream ----------
|
|
310
|
-
export function callStream(backend, apiKey, body, clientHeaders, reply, timeoutMs, upstreamPath, buildHeaders, metricsTransform, checkEarlyError, compatResolve, loopGuard, formatTransform, timeoutContext, onTimeoutAbort) {
|
|
310
|
+
export function callStream(backend, apiKey, body, clientHeaders, reply, timeoutMs, upstreamPath, buildHeaders, metricsTransform, checkEarlyError, compatResolve, loopGuard, formatTransform, timeoutContext, onTimeoutAbort, agent) {
|
|
311
311
|
return new Promise((resolve) => {
|
|
312
312
|
const effectiveResolve = compatResolve ?? resolve;
|
|
313
313
|
const url = new URL(buildUpstreamUrl(backend.base_url, upstreamPath));
|
|
314
314
|
const payload = JSON.stringify(body);
|
|
315
315
|
const upstreamHeaders = buildHeaders(clientHeaders, apiKey, Buffer.byteLength(payload));
|
|
316
316
|
const options = buildRequestOptions(url, upstreamHeaders);
|
|
317
|
-
const upstreamReq = _transportInternals.createUpstreamRequest(url, options);
|
|
317
|
+
const upstreamReq = _transportInternals.createUpstreamRequest(url, options, agent);
|
|
318
318
|
upstreamReq.on("response", (upstreamRes) => {
|
|
319
319
|
const statusCode = upstreamRes.statusCode || UPSTREAM_BAD_GATEWAY;
|
|
320
320
|
if (statusCode !== UPSTREAM_SUCCESS) {
|
|
@@ -4,6 +4,7 @@ import type { RawHeaders, TransportResult } from "../types.js";
|
|
|
4
4
|
import type { Target } from "../../core/types.js";
|
|
5
5
|
import type { RequestTracker } from "@llm-router/core/monitor";
|
|
6
6
|
import type { RetryRuleMatcher } from "../orchestration/retry-rules.js";
|
|
7
|
+
import type { ProxyAgentFactory } from "./proxy-agent.js";
|
|
7
8
|
export interface TransportFnParams {
|
|
8
9
|
provider: NonNullable<ReturnType<typeof getProviderById>>;
|
|
9
10
|
apiKey: string;
|
|
@@ -28,5 +29,6 @@ export interface TransportFnParams {
|
|
|
28
29
|
modelId: string;
|
|
29
30
|
providerId: string;
|
|
30
31
|
};
|
|
32
|
+
proxyAgentFactory?: ProxyAgentFactory;
|
|
31
33
|
}
|
|
32
34
|
export declare function buildTransportFn(p: TransportFnParams): (target: Target) => Promise<TransportResult>;
|
|
@@ -35,6 +35,7 @@ export function buildTransportFn(p) {
|
|
|
35
35
|
const base = buildUpstreamHeaders(cliHdrs, key, bytes, p.apiType);
|
|
36
36
|
return p.injectedHeaders ? { ...base, ...p.injectedHeaders } : base;
|
|
37
37
|
};
|
|
38
|
+
const agent = p.proxyAgentFactory?.getAgent(p.provider);
|
|
38
39
|
// _target 未使用 — resilience 层始终传入当前 resolved target;
|
|
39
40
|
// 跨 target failover 由外层 executeFailoverLoop 的 ProviderSwitchNeeded 处理
|
|
40
41
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -52,14 +53,14 @@ export function buildTransportFn(p) {
|
|
|
52
53
|
onContentDelta: streamLoopGuard ? (text) => streamLoopGuard.feed(text) : undefined,
|
|
53
54
|
});
|
|
54
55
|
const checkEarlyError = p.matcher ? (data) => p.matcher.test(UPSTREAM_SUCCESS, data) : undefined;
|
|
55
|
-
const streamResult = await callStream(p.provider, p.apiKey, p.body, p.cliHdrs, p.reply, p.streamTimeoutMs, p.upstreamPath, buildHeaders, metricsTransform, checkEarlyError, undefined, streamLoopGuard, p.formatTransform, p.timeoutContext);
|
|
56
|
+
const streamResult = await callStream(p.provider, p.apiKey, p.body, p.cliHdrs, p.reply, p.streamTimeoutMs, p.upstreamPath, buildHeaders, metricsTransform, checkEarlyError, undefined, streamLoopGuard, p.formatTransform, p.timeoutContext, undefined, agent);
|
|
56
57
|
const m = (streamResult.kind === "stream_success" || streamResult.kind === "stream_abort")
|
|
57
58
|
? streamResult.metrics : undefined;
|
|
58
59
|
if (m)
|
|
59
60
|
p.tracker?.update(p.logId, { streamMetrics: toStreamMetrics(m) });
|
|
60
61
|
return streamResult;
|
|
61
62
|
}
|
|
62
|
-
let result = await callNonStream(p.provider, p.apiKey, p.body, p.cliHdrs, p.upstreamPath, buildHeaders);
|
|
63
|
+
let result = await callNonStream(p.provider, p.apiKey, p.body, p.cliHdrs, p.upstreamPath, buildHeaders, agent);
|
|
63
64
|
if (result.kind === "success") {
|
|
64
65
|
const mr = MetricsExtractor.fromNonStreamResponse(p.apiType, result.body);
|
|
65
66
|
if (mr)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{$ as e,Vt as t,X as n,dt as r,mt as i,r as a,zt as o}from"./button-
|
|
1
|
+
import{$ as e,Vt as t,X as n,dt as r,mt as i,r as a,zt as o}from"./button-CGbcdJgN.js";var s=[`data-size`],c=e({__name:`Card`,props:{class:{type:[Boolean,null,String,Object,Array]},size:{default:`default`}},setup(e){let c=e;return(l,u)=>(r(),n(`div`,{"data-slot":`card`,"data-size":e.size,class:t(o(a)(`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=e({__name:`CardContent`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let s=e;return(e,c)=>(r(),n(`div`,{"data-slot":`card-content`,class:t(o(a)(`px-4 group-data-[size=sm]/card:px-3`,s.class))},[i(e.$slots,`default`)],2))}});export{c as n,l as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{$ as e,Vt as t,X as n,dt as r,mt as i,r as a,zt as o}from"./button-
|
|
1
|
+
import{$ as e,Vt as t,X as n,dt as r,mt as i,r as a,zt as o}from"./button-CGbcdJgN.js";var s=e({__name:`CardHeader`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let s=e;return(e,c)=>(r(),n(`div`,{"data-slot":`card-header`,class:t(o(a)(`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(e.$slots,`default`)],2))}}),c=e({__name:`CardTitle`,props:{class:{type:[Boolean,null,String,Object,Array]}},setup(e){let s=e;return(e,c)=>(r(),n(`div`,{"data-slot":`card-title`,class:t(o(a)(`text-base leading-snug font-medium group-data-[size=sm]/card:text-sm cn-font-heading`,s.class))},[i(e.$slots,`default`)],2))}});export{s as n,c as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{$ as e,H as t,Ht as n,J as r,K as i,Q as a,U as o,Y as s,dt as c,gt as l,i as u,m as d,mt as f,o as p,ot as m,r as h,tt as g,wt as _,x as v,zt as y}from"./button-
|
|
1
|
+
import{$ as e,H as t,Ht as n,J as r,K as i,Q as a,U as o,Y as s,dt as c,gt as l,i as u,m as d,mt as f,o as p,ot as m,r as h,tt as g,wt as _,x as v,zt as y}from"./button-CGbcdJgN.js";import{t as b}from"./VisuallyHiddenInput-WLtFW_E8.js";import{t as x}from"./RovingFocusItem-ZGq4Eu8v.js";import{B as S,G as C,H as w,L as T,Y as E,nt as D,q as O}from"./index-DDiesvp7.js";function k(e,t){return C(e)?!1:Array.isArray(e)?e.some(e=>E(e,t)):E(e,t)}var[A,j]=O(`CheckboxGroupRoot`);function M(e){return e===`indeterminate`}function N(e){return M(e)?`indeterminate`:e?`checked`:`unchecked`}var[P,F]=O(`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 a=e,h=n,{forwardRef:g,currentElement:v}=p(),S=A(null),T=d(a,`modelValue`,h,{defaultValue:a.defaultValue??a.falseValue,passive:a.modelValue===void 0}),D=i(()=>S?.disabled.value||a.disabled),O=i(()=>E(T.value,a.trueValue)),j=i(()=>C(S?.modelValue.value)?T.value===`indeterminate`?`indeterminate`:O.value:k(S.modelValue.value,a.value));function P(){if(C(S?.modelValue.value))T.value===`indeterminate`?T.value=a.trueValue:T.value=O.value?a.falseValue:a.trueValue;else{let e=[...S.modelValue.value||[]];if(k(e,a.value)){let t=e.findIndex(e=>E(e,a.value));e.splice(t,1)}else e.push(a.value);S.modelValue.value=e}}let I=w(v),L=i(()=>a.id&&v.value?document.querySelector(`[for="${a.id}"]`)?.innerText:void 0);return F({disabled:D,state:j}),(e,n)=>(c(),r(l(y(S)?.rovingFocus.value?y(x):y(u)),m(e.$attrs,{id:e.id,ref:y(g),role:`checkbox`,"as-child":e.asChild,as:e.as,type:e.as===`button`?`button`:void 0,"aria-checked":y(M)(j.value)?`mixed`:j.value,"aria-required":e.required,"aria-label":e.$attrs[`aria-label`]||L.value,"data-state":y(N)(j.value),"data-disabled":D.value?``:void 0,disabled:D.value,focusable:y(S)?.rovingFocus.value?!D.value:void 0,onKeydown:t(o(()=>{},[`prevent`]),[`enter`]),onClick:P}),{default:_(()=>[f(e.$slots,`default`,{modelValue:y(T),state:j.value}),y(I)&&e.name&&!y(S)?(c(),r(y(b),{key:0,type:`checkbox`,checked:!!j.value,name:e.name,value:e.value,disabled:D.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)=>(c(),r(y(T),{present:e.forceMount||y(M)(y(n).state.value)||y(n).state.value===!0},{default:_(()=>[a(y(u),m({ref:y(t),"data-state":y(N)(y(n).state.value),"data-disabled":y(n).disabled.value?``:void 0,style:{pointerEvents:`none`},"as-child":e.asChild,as:e.as},e.$attrs),{default:_(()=>[f(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)=>(c(),r(y(I),m({"data-slot":`checkbox`},y(s),{class:y(h)(`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:_(t=>[a(y(L),{"data-slot":`checkbox-indicator`,class:`[&>svg]:size-3.5 grid place-content-center text-current transition-none`},{default:_(()=>[f(e.$slots,`default`,n(g(t)),()=>[a(y(D))])]),_:2},1024)]),_:3},16,[`class`]))}});export{R as t};
|
package/frontend-dist/assets/{CollapsibleContent-B1v6hP95.js → CollapsibleContent-5Mrc8gGt.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{$ as e,Ht as t,J as n,K as r,Lt as i,Mt as a,Q as o,Y as s,d as c,dt as l,i as u,lt as d,m as f,mt as p,o as m,ot as h,st as g,tt as _,wt as v,xt as y,zt as b}from"./button-
|
|
1
|
+
import{$ as e,Ht as t,J as n,K as r,Lt as i,Mt as a,Q as o,Y as s,d as c,dt as l,i as u,lt as d,m as f,mt as p,o as m,ot as h,st as g,tt as _,wt as v,xt as y,zt as b}from"./button-CGbcdJgN.js";import{B as x,L as S,q as C,z as w}from"./index-DDiesvp7.js";var[T,E]=C(`CollapsibleRoot`),D=e({__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 a=e,o=f(a,`open`,r,{defaultValue:a.defaultOpen,passive:a.open===void 0}),{disabled:s,unmountOnHide:c}=i(a);return E({contentId:``,disabled:s,open:o,unmountOnHide:c,onOpenToggle:()=>{s.value||(o.value=!o.value)}}),t({open:o}),m(),(e,t)=>(l(),n(b(u),{as:e.as,"as-child":a.asChild,"data-state":b(o)?`open`:`closed`,"data-disabled":b(s)?``:void 0},{default:v(()=>[p(e.$slots,`default`,{open:b(o)})]),_:3},8,[`as`,`as-child`,`data-state`,`data-disabled`]))}}),O=e({inheritAttrs:!1,__name:`CollapsibleContent`,props:{forceMount:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1}},emits:[`contentFound`],setup(e,{emit:t}){let i=e,f=t,_=T();_.contentId||=w(void 0,`reka-collapsible-content`);let x=a(),{forwardRef:C,currentElement:E}=m(),D=a(0),O=a(0),k=r(()=>_.open.value),A=a(k.value),j=a();y(()=>[k.value,x.value?.present],async()=>{await g();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=r(()=>A.value&&_.open.value);return d(()=>{requestAnimationFrame(()=>{A.value=!1})}),c(E,`beforematch`,e=>{requestAnimationFrame(()=>{_.onOpenToggle(),f(`contentFound`)})}),(e,t)=>(l(),n(b(S),{ref_key:`presentRef`,ref:x,present:e.forceMount||b(_).open.value,"force-mount":!0},{default:v(({present:t})=>[o(b(u),h(e.$attrs,{id:b(_).contentId,ref:b(C),"as-child":i.asChild,as:e.as,hidden:t?void 0:b(_).unmountOnHide.value?``:`until-found`,"data-state":M.value?void 0:b(_).open.value?`open`:`closed`,"data-disabled":b(_).disabled?.value?``:void 0,style:{"--reka-collapsible-content-height":`${O.value}px`,"--reka-collapsible-content-width":`${D.value}px`}}),{default:v(()=>[!b(_).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=e({__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:r}){let i=x(e,r);return(e,r)=>(l(),n(b(D),h({"data-slot":`collapsible`},b(i)),{default:v(n=>[p(e.$slots,`default`,t(_(n)))]),_:3},16))}}),A=e({__name:`CollapsibleContent`,props:{forceMount:{type:Boolean},asChild:{type:Boolean},as:{}},setup(e){let t=e;return(e,r)=>(l(),n(b(O),h({"data-slot":`collapsible-content`},t),{default:v(()=>[p(e.$slots,`default`)]),_:3},16))}});export{k as n,T as r,A as t};
|
package/frontend-dist/assets/{CollapsibleTrigger-BmuPdmL7.js → CollapsibleTrigger-DaAYAs8_.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{$ as e,J as t,dt as n,i as r,mt as i,o as a,ot as o,wt as s,zt as c}from"./button-
|
|
1
|
+
import{$ as e,J as t,dt as n,i as r,mt as i,o as a,ot as o,wt as s,zt as c}from"./button-CGbcdJgN.js";import{r as l}from"./CollapsibleContent-5Mrc8gGt.js";var u=e({__name:`CollapsibleTrigger`,props:{asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:`button`}},setup(e){let o=e;a();let u=l();return(e,a)=>(n(),t(c(r),{type:e.as===`button`?`button`:void 0,as:e.as,"as-child":o.asChild,"aria-controls":c(u).contentId,"aria-expanded":c(u).open.value,"data-state":c(u).open.value?`open`:`closed`,"data-disabled":c(u).disabled?.value?``:void 0,disabled:c(u).disabled?.value,onClick:c(u).onOpenToggle},{default:s(()=>[i(e.$slots,`default`)]),_:3},8,[`type`,`as`,`as-child`,`aria-controls`,`aria-expanded`,`data-state`,`data-disabled`,`disabled`,`onClick`]))}}),d=e({__name:`CollapsibleTrigger`,props:{asChild:{type:Boolean},as:{}},setup(e){let r=e;return(e,a)=>(n(),t(c(u),o({"data-slot":`collapsible-trigger`},r),{default:s(()=>[i(e.$slots,`default`)]),_:3},16))}});export{d as t};
|