@allbluecn/kernel 0.5.0 → 0.5.2

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.
@@ -0,0 +1,1644 @@
1
+ import { z } from "zod";
2
+ import { BadgeDollarSign, BarChart3, Bell, Brain, Building2, Calendar, CircleStar, Database, Earth, FileText, Fish, Folder, GraduationCap, HandFist, Heart, HeartHandshake, Link, LoaderPinwheel, MessageSquare, Music4, Package, PartyPopper, PencilRuler, Settings, ShoppingCart, Shrimp, Sparkles, SquareChartGantt, ToolCase, Users, VenetianMask } from "lucide-react";
3
+ //#region src/define-plugin.ts
4
+ const PLUGIN_ID_RE = /^@[a-z0-9-]+\/[a-z0-9-]+$/;
5
+ function definePlugin(def) {
6
+ if (!PLUGIN_ID_RE.test(def.id)) throw new Error(`插件 id 必须形如 '@scope/name'(小写),收到: ${def.id}`);
7
+ if (!/^\d+\.\d+\.\d+/.test(def.version)) throw new Error(`插件 version 必须是语义化版本,收到: ${def.version}`);
8
+ return deepFreeze(def);
9
+ }
10
+ function deepFreeze(value) {
11
+ if (value && typeof value === "object" && !Object.isFrozen(value)) {
12
+ Object.freeze(value);
13
+ for (const key of Object.keys(value)) deepFreeze(value[key]);
14
+ }
15
+ return value;
16
+ }
17
+ //#endregion
18
+ //#region src/define-profile.ts
19
+ const PROFILE_ID_RE = /^[a-z0-9][a-z0-9-]*$/;
20
+ function defineProfile(profile) {
21
+ if (!PROFILE_ID_RE.test(profile.id)) throw new Error(`profile id 只允许小写字母/数字/连字符,收到: ${profile.id}`);
22
+ const seen = /* @__PURE__ */ new Set();
23
+ for (const plugin of profile.plugins) {
24
+ if (seen.has(plugin.id)) throw new Error(`profile 中存在重复插件 id: ${plugin.id}`);
25
+ seen.add(plugin.id);
26
+ }
27
+ return Object.freeze({
28
+ ...profile,
29
+ plugins: Object.freeze([...profile.plugins])
30
+ });
31
+ }
32
+ //#endregion
33
+ //#region src/resolve-order.ts
34
+ function resolvePluginOrder(plugins) {
35
+ const byId = /* @__PURE__ */ new Map();
36
+ for (const plugin of plugins) {
37
+ if (byId.has(plugin.id)) throw new Error(`重复插件 id: ${plugin.id}`);
38
+ byId.set(plugin.id, plugin);
39
+ }
40
+ for (const plugin of plugins) for (const dep of plugin.dependencies ?? []) if (!byId.has(dep)) throw new Error(`插件 ${plugin.id} 依赖的 ${dep} 未在 profile 中声明`);
41
+ const declarationIndex = new Map(plugins.map((p, i) => [p.id, i]));
42
+ const inDegree = /* @__PURE__ */ new Map();
43
+ const dependents = /* @__PURE__ */ new Map();
44
+ for (const plugin of plugins) {
45
+ inDegree.set(plugin.id, plugin.dependencies?.length ?? 0);
46
+ for (const dep of plugin.dependencies ?? []) dependents.set(dep, [...dependents.get(dep) ?? [], plugin.id]);
47
+ }
48
+ const ready = plugins.filter((p) => (inDegree.get(p.id) ?? 0) === 0).sort((a, b) => declarationIndex.get(a.id) - declarationIndex.get(b.id)).map((p) => p.id);
49
+ const orderIds = [];
50
+ while (ready.length > 0) {
51
+ const id = ready.shift();
52
+ orderIds.push(id);
53
+ for (const dependent of dependents.get(id) ?? []) {
54
+ const next = (inDegree.get(dependent) ?? 0) - 1;
55
+ inDegree.set(dependent, next);
56
+ if (next === 0) ready.push(dependent);
57
+ }
58
+ }
59
+ if (orderIds.length !== plugins.length) {
60
+ const cyclePath = findCyclePath(plugins.filter((p) => !orderIds.includes(p.id)).map((p) => p.id), byId);
61
+ throw new Error(`检测到依赖环: ${cyclePath.join(" -> ")}`);
62
+ }
63
+ return {
64
+ order: orderIds.map((id) => byId.get(id)),
65
+ ids: orderIds
66
+ };
67
+ }
68
+ function findCyclePath(cyclicIds, byId) {
69
+ const start = cyclicIds[0];
70
+ const path = [start];
71
+ const visited = /* @__PURE__ */ new Set([start]);
72
+ let current = start;
73
+ while (true) {
74
+ const deps = byId.get(current)?.dependencies ?? [];
75
+ const next = deps.find((d) => cyclicIds.includes(d)) ?? deps[0];
76
+ if (!next) break;
77
+ if (next === start || visited.has(next)) {
78
+ path.push(next);
79
+ return path;
80
+ }
81
+ visited.add(next);
82
+ path.push(next);
83
+ current = next;
84
+ }
85
+ return path;
86
+ }
87
+ //#endregion
88
+ //#region src/assemble-input.ts
89
+ function parseProfileImports(source) {
90
+ const map = /* @__PURE__ */ new Map();
91
+ const re = /import\s+([A-Za-z_$][\w$]*)\s+from\s+['"]([^'"]+)['"]/g;
92
+ let m;
93
+ while ((m = re.exec(source)) !== null) map.set(m[1], m[2]);
94
+ return map;
95
+ }
96
+ function resolveImportSpecifiers(input) {
97
+ const byId = /* @__PURE__ */ new Map();
98
+ input.resolved.order.forEach((plugin, index) => {
99
+ const localName = input.pluginLocalNames[index];
100
+ if (!localName) throw new Error(`插件 ${plugin.id} 在 profile 源码中找不到对应标识符`);
101
+ const specifier = input.imports.get(localName);
102
+ if (!specifier) throw new Error(`标识符 ${localName} 没有 default import(约定:profile 中插件必须以 default import 引入)`);
103
+ byId.set(plugin.id, specifier);
104
+ });
105
+ return byId;
106
+ }
107
+ //#endregion
108
+ //#region src/generate-assembly.ts
109
+ function generateAssemblyCode(input) {
110
+ const { profileId, profileModulePath, resolved } = input;
111
+ const ids = resolved.ids.map((id) => `'${id}'`).join(", ");
112
+ return [
113
+ `// AUTO-GENERATED by @allbluecn/kernel — DO NOT EDIT`,
114
+ `// profile: ${profileId} (validated: ${resolved.order.length} plugins)`,
115
+ `// 拓扑序(被依赖者在前):${resolved.ids.join(" -> ")}`,
116
+ `import type { ProfileDefinition } from '@allbluecn/kernel'`,
117
+ ``,
118
+ `export { default as profile } from '${profileModulePath}'`,
119
+ ``,
120
+ `export const pluginIds: string[] = [${ids}]`,
121
+ ``,
122
+ `declare const _profile: ProfileDefinition`
123
+ ].join("\n");
124
+ }
125
+ const HEADER = "// AUTO-GENERATED by @allbluecn/kernel — DO NOT EDIT";
126
+ const ALL_FEATURES = [
127
+ "billing",
128
+ "hostedAi",
129
+ "sso",
130
+ "audit",
131
+ "permissions",
132
+ "license",
133
+ "versionHistory",
134
+ "automation",
135
+ "templates",
136
+ "analytics",
137
+ "globalSearch",
138
+ "compliance"
139
+ ];
140
+ function generateAssemblyFiles(input) {
141
+ const specifiers = resolveImportSpecifiers(input);
142
+ const { resolved } = input;
143
+ const importLines = resolved.order.map((p, i) => {
144
+ return `import ${input.pluginLocalNames[i]} from '${specifiers.get(p.id)}'`;
145
+ });
146
+ const pluginList = input.pluginLocalNames.slice(0, resolved.order.length).join(", ");
147
+ const middlewareParts = resolved.order.map((p, i) => {
148
+ return ` ...${input.pluginLocalNames[i]}.contributes.serverMiddleware ?? [],`;
149
+ }).join("\n");
150
+ const pluginsGen = [
151
+ HEADER,
152
+ `// profile: ${input.profileId} (validated: ${resolved.order.length} plugins)`,
153
+ `// 拓扑序:${resolved.ids.join(" -> ")}`,
154
+ ...importLines,
155
+ `import { defineProfile, type ProfileDefinition } from '@allbluecn/kernel'`,
156
+ ``,
157
+ `export const profile: ProfileDefinition = defineProfile({`,
158
+ ` id: '${input.profileId}',`,
159
+ ` plugins: [${pluginList}],`,
160
+ `})`,
161
+ ``,
162
+ `export const pluginIds: string[] = [${resolved.ids.map((i) => `'${i}'`).join(", ")}]`,
163
+ ``,
164
+ `export const pluginMiddleware: unknown[] = [`,
165
+ middlewareParts,
166
+ `]`
167
+ ].join("\n");
168
+ const implLines = ALL_FEATURES.map((f) => {
169
+ return ` ${f}: ${resolved.order.some((p) => p.contributes.services?.[f] !== void 0)},`;
170
+ });
171
+ const modulesGen = [
172
+ HEADER,
173
+ `import type { ProFeature } from '@allbluecn/kernel'`,
174
+ ``,
175
+ `export const activePluginIds = [${resolved.ids.map((i) => `'${i}'`).join(", ")}] as const`,
176
+ `export type ActivePluginId = (typeof activePluginIds)[number]`,
177
+ ``,
178
+ `export const implementedModules: Record<ProFeature, boolean> = {`,
179
+ ...implLines,
180
+ `}`
181
+ ].join("\n");
182
+ const uiImports = [];
183
+ const navItems = [];
184
+ const settingsTabs = [];
185
+ const widgets = [];
186
+ for (let i = 0; i < resolved.order.length; i++) {
187
+ const p = resolved.order[i];
188
+ const local = input.pluginLocalNames[i];
189
+ const spec = specifiers.get(p.id);
190
+ const slots = p.contributes.uiSlots;
191
+ if (!slots) continue;
192
+ for (const [key] of Object.entries(slots.navItems ?? {})) {
193
+ uiImports.push(`import { ${key} } from '${spec}/ui'`);
194
+ navItems.push(key);
195
+ }
196
+ if (slots.settingsTabs?.length) {
197
+ uiImports.push(`import { ${local}SettingsTabs } from '${spec}/ui'`);
198
+ settingsTabs.push(`...${local}SettingsTabs`);
199
+ }
200
+ for (const [key] of Object.entries(slots.dashboardWidgets ?? {})) {
201
+ uiImports.push(`import { ${key} } from '${spec}/ui'`);
202
+ widgets.push(key);
203
+ }
204
+ }
205
+ const uiGen = [
206
+ HEADER,
207
+ ...uiImports,
208
+ `import { type ComponentType } from 'react'`,
209
+ `import type { SettingsTabContribution } from '@allbluecn/kernel'`,
210
+ ``,
211
+ `export const navItems: ComponentType[] = [${navItems.join(", ")}]`,
212
+ `export const settingsTabs: SettingsTabContribution[] = [${settingsTabs.join(", ")}]`,
213
+ `export const dashboardWidgets: ComponentType[] = [${widgets.join(", ")}]`
214
+ ].join("\n");
215
+ const routeFiles = /* @__PURE__ */ new Map();
216
+ for (let i = 0; i < resolved.order.length; i++) {
217
+ const p = resolved.order[i];
218
+ const spec = specifiers.get(p.id);
219
+ p.contributes.routes?.forEach((r, j) => {
220
+ const file = routeContributionToFile(r.path);
221
+ const varName = `${input.pluginLocalNames[i]}_route_${j}`;
222
+ const routePath = r.path.endsWith("/") ? r.path.slice(0, -1) : r.path;
223
+ const lines = [
224
+ HEADER,
225
+ `import { createFileRoute } from '@tanstack/react-router'`,
226
+ `import type { PluginRouteContext } from '@allbluecn/kernel'`,
227
+ `import { ${varName} } from '${spec}/routes'`,
228
+ ``,
229
+ `export const Route = createFileRoute('${routePath}')({`,
230
+ ...r.beforeLoad ? [` beforeLoad: ((ctx: PluginRouteContext) => ${varName}.beforeLoad(ctx)) as any,`] : [],
231
+ ...r.loader ? [` loader: ((ctx: PluginRouteContext) => ${varName}.loader(ctx)) as any,`] : [],
232
+ ...r.validateSearch ? [` validateSearch: ${varName}.validateSearch as any,`] : [],
233
+ ` component: ${varName}.Component as any,`,
234
+ `})`
235
+ ];
236
+ routeFiles.set(file, lines.join("\n"));
237
+ });
238
+ }
239
+ return {
240
+ pluginsGen,
241
+ modulesGen,
242
+ uiGen,
243
+ routeFiles
244
+ };
245
+ }
246
+ function routeContributionToFile(path) {
247
+ if (path.endsWith("/")) return `src/routes${path.slice(0, -1) || ""}/index.tsx`;
248
+ return `src/routes${path}/route.tsx`;
249
+ }
250
+ //#endregion
251
+ //#region src/dump-modules.ts
252
+ function dumpModules(profileId, resolved) {
253
+ const lines = [`profile: ${profileId} (${resolved.order.length} plugins)`];
254
+ for (const plugin of resolved.order) {
255
+ const c = plugin.contributes;
256
+ const parts = [];
257
+ if (c.routes?.length) parts.push(`routes:${c.routes.length}`);
258
+ if (c.serverMiddleware?.length) parts.push(`middleware:${c.serverMiddleware.length}`);
259
+ if (c.queryOptions && Object.keys(c.queryOptions).length) parts.push(`queries:${Object.keys(c.queryOptions).length}`);
260
+ if (c.uiSlots && Object.keys(c.uiSlots).length) parts.push(`ui-slots:${Object.keys(c.uiSlots).length}`);
261
+ if (c.features?.length) parts.push(`features:${c.features.length}`);
262
+ if (c.aiTools?.length) parts.push(`ai-tools:${c.aiTools.length}`);
263
+ if (c.prisma?.models.length) parts.push(`prisma-models:${c.prisma.models.length}`);
264
+ lines.push(`${plugin.id}@${plugin.version} — ${parts.length ? parts.join(" ") : "no contributions"}`);
265
+ }
266
+ return lines.join("\n");
267
+ }
268
+ //#endregion
269
+ //#region src/pro-contract/billing.ts
270
+ var BillingUnavailableError = class BillingUnavailableError extends Error {
271
+ constructor(message = "计费模块不可用") {
272
+ super(message);
273
+ this.name = "BillingUnavailableError";
274
+ Object.setPrototypeOf(this, BillingUnavailableError.prototype);
275
+ }
276
+ };
277
+ var WebhookSignatureError = class WebhookSignatureError extends Error {
278
+ constructor(message = "Webhook 签名验证失败") {
279
+ super(message);
280
+ this.name = "WebhookSignatureError";
281
+ Object.setPrototypeOf(this, WebhookSignatureError.prototype);
282
+ }
283
+ };
284
+ var SeatsExceededError = class SeatsExceededError extends Error {
285
+ current;
286
+ max;
287
+ constructor(current, max) {
288
+ super(`席位不足:当前 ${current}/${max}`);
289
+ this.name = "SeatsExceededError";
290
+ this.current = current;
291
+ this.max = max;
292
+ Object.setPrototypeOf(this, SeatsExceededError.prototype);
293
+ }
294
+ };
295
+ //#endregion
296
+ //#region src/pro-contract/hosted-ai.ts
297
+ var HostedAiError = class extends Error {
298
+ code;
299
+ details;
300
+ constructor(code, message, details) {
301
+ super(message);
302
+ this.code = code;
303
+ this.details = details;
304
+ this.name = "HostedAiError";
305
+ }
306
+ };
307
+ //#endregion
308
+ //#region src/server-seam.ts
309
+ let seam = null;
310
+ function registerServerSeam(impl) {
311
+ seam = impl;
312
+ }
313
+ function getServerSeam() {
314
+ if (!seam) throw new Error("ServerSeam 未注册:宿主需在 start.ts 中调用 registerServerSeam()");
315
+ return seam;
316
+ }
317
+ function registerPrismaSeam(seam) {}
318
+ //#endregion
319
+ //#region src/contract/constants/perf.ts
320
+ const VIRTUALIZE_THRESHOLD = 250;
321
+ const LOAD_MORE_STEP = 200;
322
+ const DND_STICKY_THRESHOLD = 500;
323
+ //#endregion
324
+ //#region src/contract/modules/ai-providers/types.ts
325
+ /** 卡片角标的预设颜色(按文案匹配),admin 也可自由输入文案 */
326
+ const PROVIDER_BADGE_STYLES = {
327
+ 推荐: "bg-primary/10 text-primary border-primary/30",
328
+ 热门: "bg-orange-500/10 text-orange-600 dark:text-orange-400 border-orange-500/30",
329
+ NEW: "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/30",
330
+ 本地: "bg-blue-500/10 text-blue-600 dark:text-blue-400 border-blue-500/30"
331
+ };
332
+ //#endregion
333
+ //#region src/contract/modules/ai-providers/constants.ts
334
+ const ADAPTER_KINDS = [
335
+ {
336
+ value: "openai",
337
+ label: "OpenAI 官方",
338
+ description: "GPT 系列,OpenAI 协议"
339
+ },
340
+ {
341
+ value: "anthropic",
342
+ label: "Anthropic",
343
+ description: "Claude 系列,Anthropic 协议"
344
+ },
345
+ {
346
+ value: "gemini",
347
+ label: "Google Gemini",
348
+ description: "Gemini 系列,Google 协议"
349
+ },
350
+ {
351
+ value: "groq",
352
+ label: "Groq",
353
+ description: "Groq 极速推理引擎"
354
+ },
355
+ {
356
+ value: "grok",
357
+ label: "xAI Grok",
358
+ description: "Grok 系列,xAI 协议"
359
+ },
360
+ {
361
+ value: "openrouter",
362
+ label: "OpenRouter 聚合",
363
+ description: "一个 Key 访问 200+ 模型"
364
+ },
365
+ {
366
+ value: "ollama",
367
+ label: "Ollama 本地",
368
+ description: "本地部署开源模型,无需 API Key"
369
+ },
370
+ {
371
+ value: "openai-compatible",
372
+ label: "OpenAI 兼容",
373
+ description: "DeepSeek/Moonshot/Mistral 等兼容协议"
374
+ }
375
+ ];
376
+ const PROVIDER_CATEGORIES = [
377
+ {
378
+ value: "recommended",
379
+ label: "推荐"
380
+ },
381
+ {
382
+ value: "popular",
383
+ label: "热门"
384
+ },
385
+ {
386
+ value: "local",
387
+ label: "本地"
388
+ }
389
+ ];
390
+ const ADAPTER_KIND_VALUES = ADAPTER_KINDS.map((k) => k.value);
391
+ const PROVIDER_CATEGORY_VALUES = PROVIDER_CATEGORIES.map((c) => c.value);
392
+ const MODEL_CAPABILITY_OPTIONS = [
393
+ {
394
+ value: "text",
395
+ label: "文本"
396
+ },
397
+ {
398
+ value: "vision",
399
+ label: "视觉"
400
+ },
401
+ {
402
+ value: "tools",
403
+ label: "工具"
404
+ },
405
+ {
406
+ value: "reasoning",
407
+ label: "推理"
408
+ },
409
+ {
410
+ value: "audio",
411
+ label: "音频"
412
+ }
413
+ ];
414
+ const MODEL_TABS = [
415
+ {
416
+ value: "all",
417
+ label: "全部"
418
+ },
419
+ {
420
+ value: "text",
421
+ label: "文本"
422
+ },
423
+ {
424
+ value: "reasoning",
425
+ label: "推理"
426
+ },
427
+ {
428
+ value: "tools",
429
+ label: "对话"
430
+ },
431
+ {
432
+ value: "vision",
433
+ label: "图片"
434
+ },
435
+ {
436
+ value: "video",
437
+ label: "视频"
438
+ }
439
+ ];
440
+ /**
441
+ * our providerId → @lobehub/icons PascalCase 导出名
442
+ * 未知 providerId 返回 null,调用方 fallback 到首字母
443
+ */
444
+ const LOBE_ICON_MAP = {
445
+ openai: "OpenAI",
446
+ anthropic: "Anthropic",
447
+ gemini: "Gemini",
448
+ deepseek: "DeepSeek",
449
+ groq: "Groq",
450
+ xai: "XAI",
451
+ openrouter: "OpenRouter",
452
+ mistral: "Mistral",
453
+ ollama: "Ollama",
454
+ moonshot: "Moonshot",
455
+ sensenova: "SenseNova",
456
+ agnes: "AgnesAI"
457
+ };
458
+ //#endregion
459
+ //#region src/contract/modules/ai-providers/seed-data.ts
460
+ /** 10 个最受欢迎 AI 服务商的种子数据,admin 可在其基础上增删改 */
461
+ const SEED_AI_PROVIDERS = [
462
+ {
463
+ providerId: "openai",
464
+ displayName: "OpenAI",
465
+ brandName: "OpenAI",
466
+ description: "GPT 系列模型,业界领先的通用对话与推理能力",
467
+ category: "recommended",
468
+ iconSlug: "openai",
469
+ brandColor: "#10A37F",
470
+ badge: "推荐",
471
+ adapterKind: "openai",
472
+ defaultBaseUrl: "https://api.openai.com/v1",
473
+ keyUrl: "https://platform.openai.com/api-keys",
474
+ docsUrl: "https://platform.openai.com/docs",
475
+ keyPlaceholder: "sk-...",
476
+ models: [
477
+ {
478
+ id: "gpt-4o",
479
+ displayName: "GPT-4o",
480
+ contextWindow: 128e3,
481
+ capabilities: [
482
+ "text",
483
+ "vision",
484
+ "tools"
485
+ ],
486
+ defaultEnabled: true
487
+ },
488
+ {
489
+ id: "gpt-4o-mini",
490
+ displayName: "GPT-4o mini",
491
+ contextWindow: 128e3,
492
+ capabilities: [
493
+ "text",
494
+ "vision",
495
+ "tools"
496
+ ],
497
+ defaultEnabled: true
498
+ },
499
+ {
500
+ id: "o1",
501
+ displayName: "o1",
502
+ contextWindow: 2e5,
503
+ capabilities: ["text", "reasoning"]
504
+ },
505
+ {
506
+ id: "o1-mini",
507
+ displayName: "o1-mini",
508
+ contextWindow: 128e3,
509
+ capabilities: ["text", "reasoning"]
510
+ },
511
+ {
512
+ id: "o3-mini",
513
+ displayName: "o3-mini",
514
+ contextWindow: 2e5,
515
+ capabilities: [
516
+ "text",
517
+ "reasoning",
518
+ "tools"
519
+ ],
520
+ defaultEnabled: true
521
+ }
522
+ ]
523
+ },
524
+ {
525
+ providerId: "anthropic",
526
+ displayName: "Anthropic",
527
+ brandName: "Claude",
528
+ description: "Claude 系列模型,擅长长文本理解与代码生成",
529
+ category: "recommended",
530
+ iconSlug: "anthropic",
531
+ brandColor: "#D97757",
532
+ badge: "推荐",
533
+ adapterKind: "anthropic",
534
+ defaultBaseUrl: "https://api.anthropic.com",
535
+ keyUrl: "https://console.anthropic.com/settings/keys",
536
+ docsUrl: "https://docs.anthropic.com",
537
+ keyPlaceholder: "sk-ant-...",
538
+ models: [
539
+ {
540
+ id: "claude-sonnet-4-5",
541
+ displayName: "Claude Sonnet 4.5",
542
+ contextWindow: 2e5,
543
+ capabilities: [
544
+ "text",
545
+ "vision",
546
+ "tools",
547
+ "reasoning"
548
+ ],
549
+ defaultEnabled: true
550
+ },
551
+ {
552
+ id: "claude-opus-4-1",
553
+ displayName: "Claude Opus 4.1",
554
+ contextWindow: 2e5,
555
+ capabilities: [
556
+ "text",
557
+ "vision",
558
+ "tools",
559
+ "reasoning"
560
+ ]
561
+ },
562
+ {
563
+ id: "claude-haiku-4-5",
564
+ displayName: "Claude Haiku 4.5",
565
+ contextWindow: 2e5,
566
+ capabilities: [
567
+ "text",
568
+ "vision",
569
+ "tools"
570
+ ],
571
+ defaultEnabled: true
572
+ }
573
+ ]
574
+ },
575
+ {
576
+ providerId: "gemini",
577
+ displayName: "Google Gemini",
578
+ brandName: "Gemini",
579
+ description: "Google 推出的多模态大模型,支持文本、图像、视频",
580
+ category: "recommended",
581
+ iconSlug: "gemini",
582
+ brandColor: "#4285F4",
583
+ badge: "推荐",
584
+ adapterKind: "gemini",
585
+ defaultBaseUrl: "https://generativelanguage.googleapis.com",
586
+ keyUrl: "https://aistudio.google.com/apikey",
587
+ docsUrl: "https://ai.google.dev/gemini-api/docs",
588
+ keyPlaceholder: "AIza...",
589
+ models: [{
590
+ id: "gemini-2.5-pro",
591
+ displayName: "Gemini 2.5 Pro",
592
+ contextWindow: 2e6,
593
+ capabilities: [
594
+ "text",
595
+ "vision",
596
+ "tools",
597
+ "reasoning"
598
+ ],
599
+ defaultEnabled: true
600
+ }, {
601
+ id: "gemini-2.5-flash",
602
+ displayName: "Gemini 2.5 Flash",
603
+ contextWindow: 1e6,
604
+ capabilities: [
605
+ "text",
606
+ "vision",
607
+ "tools"
608
+ ],
609
+ defaultEnabled: true
610
+ }]
611
+ },
612
+ {
613
+ providerId: "deepseek",
614
+ displayName: "DeepSeek",
615
+ brandName: "DeepSeek",
616
+ description: "国产开源模型,性价比极高,支持推理模式",
617
+ category: "popular",
618
+ iconSlug: "deepseek",
619
+ brandColor: "#4D6BFE",
620
+ badge: "热门",
621
+ adapterKind: "openai-compatible",
622
+ defaultBaseUrl: "https://api.deepseek.com/v1",
623
+ keyUrl: "https://platform.deepseek.com/api_keys",
624
+ docsUrl: "https://api-docs.deepseek.com",
625
+ keyPlaceholder: "sk-...",
626
+ models: [{
627
+ id: "deepseek-chat",
628
+ displayName: "DeepSeek Chat (V3)",
629
+ contextWindow: 64e3,
630
+ capabilities: ["text", "tools"],
631
+ defaultEnabled: true
632
+ }, {
633
+ id: "deepseek-reasoner",
634
+ displayName: "DeepSeek Reasoner (R1)",
635
+ contextWindow: 64e3,
636
+ capabilities: ["text", "reasoning"],
637
+ defaultEnabled: true
638
+ }]
639
+ },
640
+ {
641
+ providerId: "groq",
642
+ displayName: "Groq",
643
+ brandName: "Groq",
644
+ description: "基于 LPU 的极速推理引擎,延迟低至毫秒级",
645
+ category: "popular",
646
+ iconSlug: "groq",
647
+ brandColor: "#F55036",
648
+ badge: "热门",
649
+ adapterKind: "groq",
650
+ defaultBaseUrl: "https://api.groq.com/openai/v1",
651
+ keyUrl: "https://console.groq.com/keys",
652
+ docsUrl: "https://console.groq.com/docs",
653
+ keyPlaceholder: "gsk_...",
654
+ models: [{
655
+ id: "llama-3.3-70b-versatile",
656
+ displayName: "Llama 3.3 70B",
657
+ contextWindow: 128e3,
658
+ capabilities: ["text", "tools"],
659
+ defaultEnabled: true
660
+ }, {
661
+ id: "llama-3.1-8b-instant",
662
+ displayName: "Llama 3.1 8B Instant",
663
+ contextWindow: 128e3,
664
+ capabilities: ["text"]
665
+ }]
666
+ },
667
+ {
668
+ providerId: "xai",
669
+ displayName: "xAI",
670
+ brandName: "Grok",
671
+ description: "Elon Musk 的 xAI 推出的 Grok 系列模型",
672
+ category: "popular",
673
+ iconSlug: "x",
674
+ brandColor: "#000000",
675
+ badge: "热门",
676
+ adapterKind: "grok",
677
+ defaultBaseUrl: "https://api.x.ai",
678
+ keyUrl: "https://x.ai/api",
679
+ docsUrl: "https://docs.x.ai",
680
+ keyPlaceholder: "xai-...",
681
+ models: [{
682
+ id: "grok-4",
683
+ displayName: "Grok 4",
684
+ contextWindow: 256e3,
685
+ capabilities: [
686
+ "text",
687
+ "vision",
688
+ "tools",
689
+ "reasoning"
690
+ ],
691
+ defaultEnabled: true
692
+ }, {
693
+ id: "grok-3",
694
+ displayName: "Grok 3",
695
+ contextWindow: 131072,
696
+ capabilities: ["text", "tools"]
697
+ }]
698
+ },
699
+ {
700
+ providerId: "openrouter",
701
+ displayName: "OpenRouter",
702
+ brandName: "OpenRouter",
703
+ description: "聚合 200+ 模型的统一路由,一个 Key 访问所有厂商",
704
+ category: "recommended",
705
+ iconSlug: "openrouter",
706
+ brandColor: "#6467F2",
707
+ badge: "推荐",
708
+ adapterKind: "openrouter",
709
+ defaultBaseUrl: "https://openrouter.ai/api/v1",
710
+ keyUrl: "https://openrouter.ai/keys",
711
+ docsUrl: "https://openrouter.ai/docs",
712
+ keyPlaceholder: "sk-or-...",
713
+ models: [
714
+ {
715
+ id: "openai/gpt-4o-mini",
716
+ displayName: "GPT-4o mini",
717
+ contextWindow: 128e3,
718
+ capabilities: [
719
+ "text",
720
+ "vision",
721
+ "tools"
722
+ ],
723
+ defaultEnabled: true
724
+ },
725
+ {
726
+ id: "openai/gpt-4o",
727
+ displayName: "GPT-4o",
728
+ contextWindow: 128e3,
729
+ capabilities: [
730
+ "text",
731
+ "vision",
732
+ "tools"
733
+ ]
734
+ },
735
+ {
736
+ id: "anthropic/claude-sonnet-4.5",
737
+ displayName: "Claude Sonnet 4.5",
738
+ contextWindow: 2e5,
739
+ capabilities: [
740
+ "text",
741
+ "vision",
742
+ "tools",
743
+ "reasoning"
744
+ ],
745
+ defaultEnabled: true
746
+ },
747
+ {
748
+ id: "google/gemini-2.5-flash",
749
+ displayName: "Gemini 2.5 Flash",
750
+ contextWindow: 1e6,
751
+ capabilities: [
752
+ "text",
753
+ "vision",
754
+ "tools"
755
+ ],
756
+ defaultEnabled: true
757
+ },
758
+ {
759
+ id: "deepseek/deepseek-chat",
760
+ displayName: "DeepSeek V3",
761
+ contextWindow: 64e3,
762
+ capabilities: ["text", "tools"]
763
+ }
764
+ ]
765
+ },
766
+ {
767
+ providerId: "mistral",
768
+ displayName: "Mistral AI",
769
+ brandName: "Mistral",
770
+ description: "欧洲开源大模型,擅长多语言与代码",
771
+ category: "popular",
772
+ iconSlug: "mistralai",
773
+ brandColor: "#FF7000",
774
+ badge: "热门",
775
+ adapterKind: "openai-compatible",
776
+ defaultBaseUrl: "https://api.mistral.ai/v1",
777
+ keyUrl: "https://console.mistral.ai/api-keys",
778
+ docsUrl: "https://docs.mistral.ai",
779
+ keyPlaceholder: "...",
780
+ models: [{
781
+ id: "mistral-large-latest",
782
+ displayName: "Mistral Large",
783
+ contextWindow: 128e3,
784
+ capabilities: [
785
+ "text",
786
+ "vision",
787
+ "tools"
788
+ ],
789
+ defaultEnabled: true
790
+ }, {
791
+ id: "mistral-small-latest",
792
+ displayName: "Mistral Small",
793
+ contextWindow: 32e3,
794
+ capabilities: ["text", "tools"],
795
+ defaultEnabled: true
796
+ }]
797
+ },
798
+ {
799
+ providerId: "moonshot",
800
+ displayName: "Moonshot AI",
801
+ brandName: "Kimi",
802
+ description: "月之暗面 Kimi,国产长文本之王",
803
+ category: "popular",
804
+ iconSlug: "moonshot",
805
+ brandColor: "#16161A",
806
+ badge: "热门",
807
+ adapterKind: "openai-compatible",
808
+ defaultBaseUrl: "https://api.moonshot.cn/v1",
809
+ keyUrl: "https://platform.moonshot.cn/console/api-keys",
810
+ docsUrl: "https://platform.moonshot.cn/docs",
811
+ keyPlaceholder: "sk-...",
812
+ models: [{
813
+ id: "moonshot-v1-128k",
814
+ displayName: "Moonshot v1 128K",
815
+ contextWindow: 128e3,
816
+ capabilities: ["text", "tools"],
817
+ defaultEnabled: true
818
+ }, {
819
+ id: "kimi-k2-0905-preview",
820
+ displayName: "Kimi K2",
821
+ contextWindow: 128e3,
822
+ capabilities: ["text", "tools"],
823
+ defaultEnabled: true
824
+ }]
825
+ },
826
+ {
827
+ providerId: "sensenova",
828
+ displayName: "商汤日日新",
829
+ brandName: "SenseNova",
830
+ description: "商汤科技 SenseNova 大模型,支持文本对话与视觉理解",
831
+ category: "popular",
832
+ iconSlug: "sensenova",
833
+ brandColor: "#6B4EFF",
834
+ badge: "热门",
835
+ adapterKind: "openai-compatible",
836
+ defaultBaseUrl: "https://token.sensenova.cn/v1",
837
+ keyUrl: "https://platform.sensenova.cn/console/keys",
838
+ docsUrl: "https://platform.sensenova.cn/docs",
839
+ keyPlaceholder: "sk-...",
840
+ models: [
841
+ {
842
+ id: "SenseChat-6",
843
+ displayName: "SenseChat 6",
844
+ contextWindow: 8192,
845
+ capabilities: [
846
+ "text",
847
+ "vision",
848
+ "tools"
849
+ ],
850
+ defaultEnabled: true
851
+ },
852
+ {
853
+ id: "SenseChat-5",
854
+ displayName: "SenseChat 5",
855
+ contextWindow: 8192,
856
+ capabilities: [
857
+ "text",
858
+ "vision",
859
+ "tools"
860
+ ]
861
+ },
862
+ {
863
+ id: "SenseChat-5-Lite",
864
+ displayName: "SenseChat 5 Lite",
865
+ contextWindow: 8192,
866
+ capabilities: ["text", "tools"]
867
+ }
868
+ ]
869
+ },
870
+ {
871
+ providerId: "agnes",
872
+ displayName: "Agnes AI",
873
+ brandName: "Agnes",
874
+ description: "Agnes AI 多模态大模型,兼容 OpenAI 接口,支持文本、图像、视频",
875
+ category: "popular",
876
+ iconSlug: "agnesai",
877
+ brandColor: "#7C3AED",
878
+ badge: "NEW",
879
+ adapterKind: "openai-compatible",
880
+ defaultBaseUrl: "https://apihub.agnes-ai.com/v1",
881
+ keyUrl: "https://agnes-ai.com/api-keys",
882
+ docsUrl: "https://wiki.agnes-ai.com/en/docs/overview.md",
883
+ keyPlaceholder: "sk-...",
884
+ models: [
885
+ {
886
+ id: "agnes-2.5-pro",
887
+ displayName: "Agnes 2.5 Pro",
888
+ contextWindow: 1e6,
889
+ capabilities: [
890
+ "text",
891
+ "vision",
892
+ "tools",
893
+ "reasoning"
894
+ ],
895
+ defaultEnabled: true
896
+ },
897
+ {
898
+ id: "agnes-2.5-flash",
899
+ displayName: "Agnes 2.5 Flash",
900
+ contextWindow: 512e3,
901
+ capabilities: [
902
+ "text",
903
+ "vision",
904
+ "tools"
905
+ ],
906
+ defaultEnabled: true
907
+ },
908
+ {
909
+ id: "agnes-2.0-flash",
910
+ displayName: "Agnes 2.0 Flash",
911
+ contextWindow: 512e3,
912
+ capabilities: [
913
+ "text",
914
+ "vision",
915
+ "tools"
916
+ ]
917
+ }
918
+ ]
919
+ },
920
+ {
921
+ providerId: "ollama",
922
+ displayName: "Ollama",
923
+ brandName: "Ollama",
924
+ description: "本地部署开源模型,零成本零延迟(需先本地安装 Ollama)",
925
+ category: "local",
926
+ iconSlug: "ollama",
927
+ brandColor: "#FFFFFF",
928
+ badge: "本地",
929
+ adapterKind: "ollama",
930
+ defaultBaseUrl: "http://localhost:11434",
931
+ keyUrl: "https://ollama.com/download",
932
+ docsUrl: "https://ollama.com/library",
933
+ keyPlaceholder: "本地部署无需 API Key",
934
+ isLocal: true,
935
+ models: [
936
+ {
937
+ id: "llama3.3",
938
+ displayName: "Llama 3.3",
939
+ contextWindow: 128e3,
940
+ capabilities: ["text", "tools"],
941
+ defaultEnabled: true
942
+ },
943
+ {
944
+ id: "qwen2.5",
945
+ displayName: "Qwen 2.5",
946
+ contextWindow: 128e3,
947
+ capabilities: ["text"],
948
+ defaultEnabled: true
949
+ },
950
+ {
951
+ id: "deepseek-r1",
952
+ displayName: "DeepSeek R1",
953
+ contextWindow: 128e3,
954
+ capabilities: ["text", "reasoning"]
955
+ }
956
+ ]
957
+ }
958
+ ];
959
+ //#endregion
960
+ //#region src/contract/modules/dependencies/index.ts
961
+ /** 依赖分类元信息 */
962
+ const DEPENDENCY_CATEGORIES = [
963
+ {
964
+ value: "framework",
965
+ label: "框架核心",
966
+ description: "TanStack Start / React / Vite / Prisma 等基础设施"
967
+ },
968
+ {
969
+ value: "ui",
970
+ label: "UI 组件",
971
+ description: "radix / motion / lucide / tailwind 等界面库"
972
+ },
973
+ {
974
+ value: "editor",
975
+ label: "编辑器",
976
+ description: "TipTap / mind-elixir / lowlight 等富文本/思维导图"
977
+ },
978
+ {
979
+ value: "ai",
980
+ label: "AI 能力",
981
+ description: "@tanstack/ai / openai 等 LLM 集成"
982
+ },
983
+ {
984
+ value: "state",
985
+ label: "状态管理",
986
+ description: "react-query / react-form 等状态/数据层"
987
+ },
988
+ {
989
+ value: "auth",
990
+ label: "认证",
991
+ description: "start-authjs / @auth/core / bcryptjs"
992
+ },
993
+ {
994
+ value: "tool",
995
+ label: "工具库",
996
+ description: "zod / nanoid / i18next 等通用工具"
997
+ },
998
+ {
999
+ value: "other",
1000
+ label: "其他",
1001
+ description: "未分类依赖"
1002
+ }
1003
+ ];
1004
+ const DEPENDENCY_PRIORITIES = [
1005
+ {
1006
+ value: "high",
1007
+ label: "高"
1008
+ },
1009
+ {
1010
+ value: "normal",
1011
+ label: "中"
1012
+ },
1013
+ {
1014
+ value: "low",
1015
+ label: "低"
1016
+ }
1017
+ ];
1018
+ const DEFAULT_WATCHED_DEPENDENCIES = [
1019
+ {
1020
+ name: "@tanstack/react-start",
1021
+ displayName: "TanStack Start",
1022
+ description: "全栈 React 框架,文件路由 + SSR/SSG",
1023
+ homepage: "https://tanstack.com/start",
1024
+ category: "framework",
1025
+ priority: "high"
1026
+ },
1027
+ {
1028
+ name: "@tanstack/react-router",
1029
+ displayName: "TanStack Router",
1030
+ description: "类型安全的路由库,支持文件路由与代码拆分",
1031
+ homepage: "https://tanstack.com/router",
1032
+ category: "framework",
1033
+ priority: "high"
1034
+ },
1035
+ {
1036
+ name: "vite",
1037
+ displayName: "Vite",
1038
+ description: "下一代前端构建工具,ESM 原生 + 极速 HMR",
1039
+ homepage: "https://vite.dev",
1040
+ category: "framework",
1041
+ priority: "high"
1042
+ },
1043
+ {
1044
+ name: "react",
1045
+ displayName: "React",
1046
+ description: "声明式 UI 库,组件化开发基础",
1047
+ homepage: "https://react.dev",
1048
+ category: "framework",
1049
+ priority: "high"
1050
+ },
1051
+ {
1052
+ name: "react-dom",
1053
+ displayName: "React DOM",
1054
+ description: "React 的 DOM 渲染器",
1055
+ homepage: "https://react.dev",
1056
+ category: "framework",
1057
+ priority: "high"
1058
+ },
1059
+ {
1060
+ name: "prisma",
1061
+ displayName: "Prisma",
1062
+ description: "TypeScript ORM,Schema 优先的数据库工具链",
1063
+ homepage: "https://prisma.io",
1064
+ category: "framework",
1065
+ priority: "high"
1066
+ },
1067
+ {
1068
+ name: "@prisma/client",
1069
+ displayName: "Prisma Client",
1070
+ description: "Prisma 自动生成的类型安全查询客户端",
1071
+ homepage: "https://prisma.io",
1072
+ category: "framework",
1073
+ priority: "high"
1074
+ },
1075
+ {
1076
+ name: "start-authjs",
1077
+ displayName: "start-authjs",
1078
+ description: "TanStack Start 的 Auth.js 集成,提供登录态管理",
1079
+ homepage: "https://github.com/kophzeior/start-authjs",
1080
+ category: "auth",
1081
+ priority: "high"
1082
+ },
1083
+ {
1084
+ name: "@auth/core",
1085
+ displayName: "Auth.js Core",
1086
+ description: "Auth.js 核心,支持 Credentials/OAuth 等多种登录方式",
1087
+ homepage: "https://authjs.dev",
1088
+ category: "auth",
1089
+ priority: "normal"
1090
+ },
1091
+ {
1092
+ name: "@tiptap/core",
1093
+ displayName: "TipTap Core",
1094
+ description: "无头(headless)富文本编辑器核心,可扩展",
1095
+ homepage: "https://tiptap.dev",
1096
+ category: "editor",
1097
+ priority: "high"
1098
+ },
1099
+ {
1100
+ name: "@tiptap/react",
1101
+ displayName: "TipTap React",
1102
+ description: "TipTap 编辑器的 React 绑定",
1103
+ homepage: "https://tiptap.dev",
1104
+ category: "editor",
1105
+ priority: "high"
1106
+ },
1107
+ {
1108
+ name: "@tiptap/starter-kit",
1109
+ displayName: "TipTap Starter Kit",
1110
+ description: "TipTap 预置扩展集合(段落/列表/历史等)",
1111
+ homepage: "https://tiptap.dev",
1112
+ category: "editor",
1113
+ priority: "normal"
1114
+ },
1115
+ {
1116
+ name: "mind-elixir",
1117
+ displayName: "Mind Elixir",
1118
+ description: "轻量思维导图库,支持节点编辑与导出",
1119
+ homepage: "https://github.com/ssshooter/mind-elixir",
1120
+ category: "editor",
1121
+ priority: "high"
1122
+ },
1123
+ {
1124
+ name: "lowlight",
1125
+ displayName: "Lowlight",
1126
+ description: "基于 highlight.js 的语法高亮解析器",
1127
+ homepage: "https://github.com/wooorm/lowlight",
1128
+ category: "editor",
1129
+ priority: "normal"
1130
+ },
1131
+ {
1132
+ name: "@tanstack/ai",
1133
+ displayName: "TanStack AI",
1134
+ description: "类型安全的 LLM SDK,支持流式输出与工具调用",
1135
+ homepage: "https://tanstack.com/ai",
1136
+ category: "ai",
1137
+ priority: "high"
1138
+ },
1139
+ {
1140
+ name: "@tanstack/ai-react",
1141
+ displayName: "TanStack AI React",
1142
+ description: "TanStack AI 的 React hooks(useChat 等)",
1143
+ homepage: "https://tanstack.com/ai",
1144
+ category: "ai",
1145
+ priority: "normal"
1146
+ },
1147
+ {
1148
+ name: "@tanstack/ai-openai",
1149
+ displayName: "TanStack AI OpenAI Adapter",
1150
+ description: "TanStack AI 的 OpenAI 官方模型适配器",
1151
+ homepage: "https://tanstack.com/ai",
1152
+ category: "ai",
1153
+ priority: "normal"
1154
+ },
1155
+ {
1156
+ name: "@tanstack/ai-anthropic",
1157
+ displayName: "TanStack AI Anthropic Adapter",
1158
+ description: "TanStack AI 的 Anthropic Claude 适配器",
1159
+ homepage: "https://tanstack.com/ai",
1160
+ category: "ai",
1161
+ priority: "normal"
1162
+ },
1163
+ {
1164
+ name: "@tanstack/ai-openrouter",
1165
+ displayName: "TanStack AI OpenRouter Adapter",
1166
+ description: "OpenRouter 多模型聚合适配器",
1167
+ homepage: "https://tanstack.com/ai",
1168
+ category: "ai",
1169
+ priority: "normal"
1170
+ },
1171
+ {
1172
+ name: "openai",
1173
+ displayName: "OpenAI SDK",
1174
+ description: "OpenAI 官方 SDK,本项目用于 embedding 生成",
1175
+ homepage: "https://openai.com",
1176
+ category: "ai",
1177
+ priority: "normal"
1178
+ },
1179
+ {
1180
+ name: "radix-ui",
1181
+ displayName: "Radix UI",
1182
+ description: "无样式可访问组件库统一入口(dialog/menu 等)",
1183
+ homepage: "https://radix-ui.com",
1184
+ category: "ui",
1185
+ priority: "high"
1186
+ },
1187
+ {
1188
+ name: "motion",
1189
+ displayName: "Motion",
1190
+ description: "React 动画库(原 framer-motion),声明式动画",
1191
+ homepage: "https://motion.dev",
1192
+ category: "ui",
1193
+ priority: "normal"
1194
+ },
1195
+ {
1196
+ name: "lucide-react",
1197
+ displayName: "Lucide React",
1198
+ description: "Lucide 图标库的 React 版本",
1199
+ homepage: "https://lucide.dev",
1200
+ category: "ui",
1201
+ priority: "normal"
1202
+ },
1203
+ {
1204
+ name: "next-themes",
1205
+ displayName: "Next Themes",
1206
+ description: "主题切换库,支持暗黑模式持久化",
1207
+ homepage: "https://github.com/pacocoursey/next-themes",
1208
+ category: "ui",
1209
+ priority: "normal"
1210
+ },
1211
+ {
1212
+ name: "tailwindcss",
1213
+ displayName: "Tailwind CSS",
1214
+ description: "原子化 CSS 框架,工具类优先",
1215
+ homepage: "https://tailwindcss.com",
1216
+ category: "ui",
1217
+ priority: "high"
1218
+ },
1219
+ {
1220
+ name: "shadcn",
1221
+ displayName: "shadcn",
1222
+ description: "可复制粘贴的组件集合 CLI,项目 UI 组件来源",
1223
+ homepage: "https://ui.shadcn.com",
1224
+ category: "ui",
1225
+ priority: "normal"
1226
+ },
1227
+ {
1228
+ name: "sonner",
1229
+ displayName: "Sonner",
1230
+ description: "Toast 通知组件,项目消息提示",
1231
+ homepage: "https://sonner.emilkowal.ski",
1232
+ category: "ui",
1233
+ priority: "normal"
1234
+ },
1235
+ {
1236
+ name: "@tanstack/react-query",
1237
+ displayName: "TanStack Query",
1238
+ description: "服务端状态管理与缓存,数据获取利器",
1239
+ homepage: "https://tanstack.com/query",
1240
+ category: "state",
1241
+ priority: "high"
1242
+ },
1243
+ {
1244
+ name: "@tanstack/react-form",
1245
+ displayName: "TanStack Form",
1246
+ description: "类型安全的表单管理库",
1247
+ homepage: "https://tanstack.com/form",
1248
+ category: "state",
1249
+ priority: "normal"
1250
+ },
1251
+ {
1252
+ name: "zod",
1253
+ displayName: "Zod",
1254
+ description: "TypeScript 优先的 Schema 校验库",
1255
+ homepage: "https://zod.dev",
1256
+ category: "tool",
1257
+ priority: "high"
1258
+ },
1259
+ {
1260
+ name: "i18next",
1261
+ displayName: "i18next",
1262
+ description: "国际化框架,多语言文案管理",
1263
+ homepage: "https://www.i18next.com",
1264
+ category: "tool",
1265
+ priority: "normal"
1266
+ },
1267
+ {
1268
+ name: "react-i18next",
1269
+ displayName: "React i18next",
1270
+ description: "i18next 的 React 绑定",
1271
+ homepage: "https://react.i18next.com",
1272
+ category: "tool",
1273
+ priority: "normal"
1274
+ },
1275
+ {
1276
+ name: "nanoid",
1277
+ displayName: "Nano ID",
1278
+ description: "轻量 URL 友好的唯一 ID 生成器",
1279
+ homepage: "https://github.com/ai/nanoid",
1280
+ category: "tool",
1281
+ priority: "low"
1282
+ },
1283
+ {
1284
+ name: "bcryptjs",
1285
+ displayName: "bcryptjs",
1286
+ description: "密码哈希库,纯 JS 实现",
1287
+ homepage: "https://github.com/dcodeIO/bcrypt.js",
1288
+ category: "auth",
1289
+ priority: "normal"
1290
+ }
1291
+ ];
1292
+ //#endregion
1293
+ //#region src/contract/infrastructure/ip-location.ts
1294
+ /**
1295
+ * IP 地理位置查询工具(服务端专用)
1296
+ *
1297
+ * - 开发环境 / 内网 IP:返回「本地」
1298
+ * - 生产环境:使用 ip-api.com 免费查询(中文,每分钟 45 请求限制)
1299
+ * 失败时返回「未知」,不阻塞登录流程
1300
+ */
1301
+ const PRIVATE_IP_PATTERNS = /^(127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|::1$|fc00:|fd00:|fe80:|169\.254\.|localhost)/i;
1302
+ async function resolveIpLocation(ip) {
1303
+ if (!ip) return "Unknown";
1304
+ if (PRIVATE_IP_PATTERNS.test(ip)) return "Local";
1305
+ try {
1306
+ const controller = new AbortController();
1307
+ const timeout = setTimeout(() => controller.abort(), 3e3);
1308
+ const res = await fetch(`http://ip-api.com/json/${encodeURIComponent(ip)}?fields=status,country,regionName,city&lang=zh-CN`, { signal: controller.signal });
1309
+ clearTimeout(timeout);
1310
+ if (!res.ok) return "Unknown";
1311
+ const data = await res.json();
1312
+ if (data.status !== "success") return "Unknown";
1313
+ const parts = [
1314
+ data.city,
1315
+ data.regionName,
1316
+ data.country
1317
+ ].filter(Boolean);
1318
+ return parts.length > 0 ? parts.join(", ") : "Unknown";
1319
+ } catch {
1320
+ return "Unknown";
1321
+ }
1322
+ }
1323
+ /**
1324
+ * 从 Request 中提取客户端真实 IP
1325
+ * 优先级:X-Forwarded-For > X-Real-IP > CF-Connecting-IP
1326
+ */
1327
+ function getClientIp(request) {
1328
+ const xff = request.headers.get("x-forwarded-for");
1329
+ if (xff) {
1330
+ const first = xff.split(",")[0]?.trim();
1331
+ if (first) return first;
1332
+ }
1333
+ const xri = request.headers.get("x-real-ip");
1334
+ if (xri) return xri.trim();
1335
+ const cfip = request.headers.get("cf-connecting-ip");
1336
+ if (cfip) return cfip.trim();
1337
+ return null;
1338
+ }
1339
+ //#endregion
1340
+ //#region src/contract/infrastructure/ua-parser.ts
1341
+ function parseUserAgent(ua) {
1342
+ if (!ua) return {
1343
+ browser: "Unknown",
1344
+ os: "Unknown",
1345
+ device: "Unknown"
1346
+ };
1347
+ let browser = "Unknown";
1348
+ if (ua.includes("Edg/")) {
1349
+ const m = ua.match(/Edg\/(\d+)/);
1350
+ browser = `Edge${m ? " " + m[1] : ""}`;
1351
+ } else if (ua.includes("OPR/") || ua.includes("Opera")) {
1352
+ const m = ua.match(/(?:OPR|Opera)\/(\d+)/);
1353
+ browser = `Opera${m ? " " + m[1] : ""}`;
1354
+ } else if (ua.includes("Firefox/")) {
1355
+ const m = ua.match(/Firefox\/(\d+)/);
1356
+ browser = `Firefox${m ? " " + m[1] : ""}`;
1357
+ } else if (ua.includes("Chrome/")) {
1358
+ const m = ua.match(/Chrome\/(\d+)/);
1359
+ browser = `Chrome${m ? " " + m[1] : ""}`;
1360
+ } else if (ua.includes("Safari/") && !ua.includes("Chrome")) {
1361
+ const m = ua.match(/Version\/(\d+)/);
1362
+ browser = `Safari${m ? " " + m[1] : ""}`;
1363
+ }
1364
+ let os = "Unknown";
1365
+ if (ua.includes("Windows NT 10")) os = "Windows 10/11";
1366
+ else if (ua.includes("Windows NT 6.3")) os = "Windows 8.1";
1367
+ else if (ua.includes("Windows NT 6.2")) os = "Windows 8";
1368
+ else if (ua.includes("Windows NT 6.1")) os = "Windows 7";
1369
+ else if (ua.includes("Windows")) os = "Windows";
1370
+ else if (ua.includes("Mac OS X") || ua.includes("Macintosh")) {
1371
+ if (/Mac OS X 10_15/.test(ua)) os = "macOS";
1372
+ else {
1373
+ const m = ua.match(/Mac OS X (\d+)(?:[_.](\d+))?/);
1374
+ if (m) {
1375
+ const major = parseInt(m[1], 10);
1376
+ os = major >= 11 ? `macOS ${major}` : `Mac OS X ${m[1]}.${m[2] ?? "0"}`;
1377
+ } else os = "macOS";
1378
+ }
1379
+ } else if (ua.includes("Android")) {
1380
+ const m = ua.match(/Android ([\d.]+)/);
1381
+ os = `Android${m ? " " + m[1] : ""}`;
1382
+ } else if (/iPhone|iPad|iPod/.test(ua)) {
1383
+ const m = ua.match(/OS (\d+)[_ ](?:\d+)?/);
1384
+ os = `iOS${m ? " " + m[1] : ""}`;
1385
+ } else if (ua.includes("Linux")) os = "Linux";
1386
+ let device = "Desktop";
1387
+ if (ua.includes("iPad")) device = "iPad";
1388
+ else if (ua.includes("iPhone")) device = "iPhone";
1389
+ else if (ua.includes("Android") && ua.includes("Mobile")) device = "Android Phone";
1390
+ else if (ua.includes("Android")) device = "Android Tablet";
1391
+ return {
1392
+ browser,
1393
+ os,
1394
+ device
1395
+ };
1396
+ }
1397
+ /**
1398
+ * 将 ParsedUA 拼接为可读字符串,如 "Chrome 120 / Windows 10/11 / 桌面"
1399
+ */
1400
+ function formatParsedUA(parsed) {
1401
+ return [
1402
+ parsed.browser,
1403
+ parsed.os,
1404
+ parsed.device
1405
+ ].filter((s) => s && s !== "Unknown").join(" / ");
1406
+ }
1407
+ //#endregion
1408
+ //#region src/contract/modules/billing/feature-registry.ts
1409
+ const FeatureRegistry = {
1410
+ byokAi: {
1411
+ tier: "oss",
1412
+ module: null
1413
+ },
1414
+ hostedAi: {
1415
+ tier: "paid",
1416
+ module: "hostedAi"
1417
+ },
1418
+ multiProvider: {
1419
+ tier: "paid",
1420
+ module: null
1421
+ },
1422
+ largePageSize: {
1423
+ tier: "paid",
1424
+ module: null
1425
+ },
1426
+ versionHistory: {
1427
+ tier: "paid",
1428
+ module: "versionHistory"
1429
+ },
1430
+ automation: {
1431
+ tier: "paid",
1432
+ module: "automation"
1433
+ },
1434
+ templates: {
1435
+ tier: "paid",
1436
+ module: "templates"
1437
+ },
1438
+ globalSearch: {
1439
+ tier: "paid",
1440
+ module: "globalSearch"
1441
+ },
1442
+ analytics: {
1443
+ tier: "enterprise",
1444
+ module: "analytics"
1445
+ },
1446
+ sso: {
1447
+ tier: "enterprise",
1448
+ module: "sso"
1449
+ },
1450
+ audit: {
1451
+ tier: "enterprise",
1452
+ module: "audit"
1453
+ },
1454
+ advancedPermissions: {
1455
+ tier: "enterprise",
1456
+ module: "permissions"
1457
+ },
1458
+ compliance: {
1459
+ tier: "enterprise",
1460
+ module: "compliance"
1461
+ },
1462
+ customAgents: {
1463
+ tier: "paid",
1464
+ module: null
1465
+ }
1466
+ };
1467
+ const FEATURE_KEYS = Object.keys(FeatureRegistry);
1468
+ /** OSS 部署(billing 模块缺失)的基础白名单——唯一允许「无订阅即放行」的集合 */
1469
+ const OSS_BASE_FEATURES = ["byokAi", "customAgents"];
1470
+ function isOssFeature(key) {
1471
+ return FeatureRegistry[key].tier === "oss";
1472
+ }
1473
+ //#endregion
1474
+ //#region src/contract/modules/billing/plan-features-schema.ts
1475
+ const PlanFeaturesSchema = z.record(z.enum(FEATURE_KEYS), z.boolean());
1476
+ //#endregion
1477
+ //#region src/contract/modules/billing/resource-registry.ts
1478
+ const ResourceRegistry = {
1479
+ seats: {
1480
+ /** 成员席位池(billing 加购语义不变);访客池见 guestSeats */
1481
+ tier: "paid",
1482
+ unit: "per-seat",
1483
+ purchasable: true,
1484
+ step: 1,
1485
+ floor: "activeMembers"
1486
+ },
1487
+ guestSeats: {
1488
+ tier: "paid",
1489
+ unit: "guest-seats",
1490
+ purchasable: false,
1491
+ step: 1
1492
+ },
1493
+ aiQuotaMonthly: {
1494
+ tier: "paid",
1495
+ unit: "requests/month",
1496
+ purchasable: false,
1497
+ step: 1
1498
+ },
1499
+ agentQuota: {
1500
+ tier: "paid",
1501
+ unit: "agents",
1502
+ purchasable: false,
1503
+ step: 1
1504
+ },
1505
+ skillQuota: {
1506
+ tier: "paid",
1507
+ unit: "skills",
1508
+ purchasable: false,
1509
+ step: 1
1510
+ },
1511
+ workspaces: {
1512
+ tier: "paid",
1513
+ unit: "workspaces",
1514
+ purchasable: false,
1515
+ step: 1
1516
+ }
1517
+ };
1518
+ const RESOURCE_KEYS = Object.keys(ResourceRegistry);
1519
+ function isResourceDefined(key) {
1520
+ return key in ResourceRegistry;
1521
+ }
1522
+ //#endregion
1523
+ //#region src/contract/modules/notifications/types.ts
1524
+ const NOTIFICATION_CATEGORIES = [
1525
+ "team",
1526
+ "collaboration",
1527
+ "system",
1528
+ "billing",
1529
+ "security"
1530
+ ];
1531
+ const NOTIFICATION_FREQUENCIES = [
1532
+ "realtime",
1533
+ "digest-daily",
1534
+ "digest-weekly",
1535
+ "off"
1536
+ ];
1537
+ //#endregion
1538
+ //#region src/contract/modules/notifications/index.ts
1539
+ const DEFAULT_CATEGORIES = {
1540
+ team: "realtime",
1541
+ collaboration: "realtime",
1542
+ system: "realtime",
1543
+ billing: "realtime",
1544
+ security: "realtime"
1545
+ };
1546
+ /**
1547
+ * 归一化存储中的 categories 字段。
1548
+ * 存量数据为旧版布尔记录(true/false),读取时迁移为 frequency:
1549
+ * true → realtime,false → off;未知值回退默认 realtime。
1550
+ */
1551
+ function normalizeCategoryFrequencies(raw) {
1552
+ const result = { ...DEFAULT_CATEGORIES };
1553
+ if (!raw || typeof raw !== "object") return result;
1554
+ const record = raw;
1555
+ const valid = NOTIFICATION_FREQUENCIES;
1556
+ for (const c of NOTIFICATION_CATEGORIES) {
1557
+ const v = record[c];
1558
+ if (v === true) result[c] = "realtime";
1559
+ else if (v === false) result[c] = "off";
1560
+ else if (typeof v === "string" && valid.includes(v)) result[c] = v;
1561
+ }
1562
+ return result;
1563
+ }
1564
+ function isInDndWindow(prefs, now) {
1565
+ if (!prefs.dndEnabled) return false;
1566
+ const [sh, sm] = prefs.dndStart.split(":").map(Number);
1567
+ const [eh, em] = prefs.dndEnd.split(":").map(Number);
1568
+ const [nh, nm] = now.split(":").map(Number);
1569
+ const start = sh * 60 + sm;
1570
+ const end = eh * 60 + em;
1571
+ const cur = nh * 60 + nm;
1572
+ if (start <= end) return cur >= start && cur < end;
1573
+ return cur >= start || cur < end;
1574
+ }
1575
+ //#endregion
1576
+ //#region src/contract/modules/registry.ts
1577
+ var ModuleRegistry = class {
1578
+ modules = /* @__PURE__ */ new Map();
1579
+ register(mod) {
1580
+ if (this.modules.has(mod.id)) console.warn(`[ModuleRegistry] "${mod.id}" already registered, overwriting`);
1581
+ this.modules.set(mod.id, mod);
1582
+ }
1583
+ getModule(id) {
1584
+ return this.modules.get(id);
1585
+ }
1586
+ getAll() {
1587
+ return [...this.modules.values()];
1588
+ }
1589
+ getAICommands() {
1590
+ return [...this.modules.values()].flatMap((m) => m.aiCommands);
1591
+ }
1592
+ getSearchFields() {
1593
+ return [...this.modules.values()].flatMap((m) => m.entities.map((e) => ({
1594
+ module: m.id,
1595
+ type: e.type,
1596
+ fields: e.searchFields
1597
+ })));
1598
+ }
1599
+ };
1600
+ const moduleRegistry = new ModuleRegistry();
1601
+ //#endregion
1602
+ //#region src/contract/types/prototype.ts
1603
+ const DEVICE_SIZES = {
1604
+ desktop: null,
1605
+ tablet: 768,
1606
+ mobile: 375
1607
+ };
1608
+ //#endregion
1609
+ //#region src/contract/types/prd.ts
1610
+ const ICON_MAP = {
1611
+ folder: Folder,
1612
+ file: FileText,
1613
+ chart: BarChart3,
1614
+ users: Users,
1615
+ settings: Settings,
1616
+ package: Package,
1617
+ database: Database,
1618
+ cart: ShoppingCart,
1619
+ heart: Heart,
1620
+ sparkles: Sparkles,
1621
+ link: Link,
1622
+ message: MessageSquare,
1623
+ calendar: Calendar,
1624
+ bell: Bell,
1625
+ building2: Building2,
1626
+ squareChartGantt: SquareChartGantt,
1627
+ heartHandshake: HeartHandshake,
1628
+ shrimp: Shrimp,
1629
+ fish: Fish,
1630
+ venetianMask: VenetianMask,
1631
+ graduationCap: GraduationCap,
1632
+ handFist: HandFist,
1633
+ loaderPinwheel: LoaderPinwheel,
1634
+ toolCase: ToolCase,
1635
+ partyPopper: PartyPopper,
1636
+ pencilRuler: PencilRuler,
1637
+ circleStar: CircleStar,
1638
+ brain: Brain,
1639
+ badgeDollarSign: BadgeDollarSign,
1640
+ earth: Earth,
1641
+ music4: Music4
1642
+ };
1643
+ //#endregion
1644
+ export { PROVIDER_CATEGORIES as A, BillingUnavailableError as B, DEPENDENCY_PRIORITIES as C, LOBE_ICON_MAP as D, ADAPTER_KIND_VALUES as E, VIRTUALIZE_THRESHOLD as F, generateAssemblyFiles as G, WebhookSignatureError as H, getServerSeam as I, resolvePluginOrder as J, parseProfileImports as K, registerPrismaSeam as L, PROVIDER_BADGE_STYLES as M, DND_STICKY_THRESHOLD as N, MODEL_CAPABILITY_OPTIONS as O, LOAD_MORE_STEP as P, registerServerSeam as R, DEPENDENCY_CATEGORIES as S, ADAPTER_KINDS as T, dumpModules as U, SeatsExceededError as V, generateAssemblyCode as W, definePlugin as X, defineProfile as Y, formatParsedUA as _, isInDndWindow as a, resolveIpLocation as b, NOTIFICATION_FREQUENCIES as c, isResourceDefined as d, PlanFeaturesSchema as f, isOssFeature as g, OSS_BASE_FEATURES as h, DEFAULT_CATEGORIES as i, PROVIDER_CATEGORY_VALUES as j, MODEL_TABS as k, RESOURCE_KEYS as l, FeatureRegistry as m, DEVICE_SIZES as n, normalizeCategoryFrequencies as o, FEATURE_KEYS as p, resolveImportSpecifiers as q, moduleRegistry as r, NOTIFICATION_CATEGORIES as s, ICON_MAP as t, ResourceRegistry as u, parseUserAgent as v, SEED_AI_PROVIDERS as w, DEFAULT_WATCHED_DEPENDENCIES as x, getClientIp as y, HostedAiError as z };