@allbluecn/kernel 0.10.0 → 0.10.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.
- package/dist/bin.js +8 -7
- package/dist/contract/infrastructure/crypto.js +16 -3
- package/dist/index.d.ts +121 -75
- package/dist/index.js +2 -2
- package/dist/{src-CKvw-8uC.js → src-CyBj_lD7.js} +239 -102
- package/package.json +3 -3
package/dist/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { $ as dumpModules, et as generateAssemblyCode, it as resolvePluginOrder,
|
|
2
|
+
import { $ as dumpModules, et as generateAssemblyCode, it as parseProfileImports, ot as resolvePluginOrder, rt as extractPluginLocalNames, tt as generateAssemblyFiles } from "./src-CyBj_lD7.js";
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { dirname, relative, resolve } from "node:path";
|
|
@@ -86,11 +86,6 @@ async function main() {
|
|
|
86
86
|
return;
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
-
function extractPluginLocalNames(source) {
|
|
90
|
-
const match = source.match(/plugins:\s*\[([^\]]*)\]/s);
|
|
91
|
-
if (!match) return [];
|
|
92
|
-
return match[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
93
|
-
}
|
|
94
89
|
async function writeAssemblyFiles(appDir, assembly) {
|
|
95
90
|
const { writeFile, readFile, unlink, mkdir } = await import("node:fs/promises");
|
|
96
91
|
const { join } = await import("node:path");
|
|
@@ -102,7 +97,13 @@ async function writeAssemblyFiles(appDir, assembly) {
|
|
|
102
97
|
await writeFile(join(pluginsDir, "ui.gen.ts"), assembly.uiGen + "\n");
|
|
103
98
|
const manifestPath = join(pluginsDir, ".gen-manifest.json");
|
|
104
99
|
const oldManifest = await readFile(manifestPath, "utf8").catch(() => null);
|
|
105
|
-
|
|
100
|
+
let oldFiles = [];
|
|
101
|
+
if (oldManifest) try {
|
|
102
|
+
const parsed = JSON.parse(oldManifest);
|
|
103
|
+
oldFiles = Array.isArray(parsed.routeFiles) ? parsed.routeFiles.filter((f) => typeof f === "string") : [];
|
|
104
|
+
} catch {
|
|
105
|
+
console.warn(`旧 gen-manifest 损坏(${manifestPath}),已忽略并重新生成`);
|
|
106
|
+
}
|
|
106
107
|
const newFiles = [...assembly.routeFiles.keys()];
|
|
107
108
|
for (const f of oldFiles) if (!newFiles.includes(f)) await unlink(join(appDir, f)).catch(() => {});
|
|
108
109
|
for (const [file, content] of assembly.routeFiles.entries()) {
|
|
@@ -7,10 +7,22 @@ const ALGO = "aes-256-gcm";
|
|
|
7
7
|
const IV_BYTES = 12;
|
|
8
8
|
const VERSION = "v1";
|
|
9
9
|
let cachedKey = null;
|
|
10
|
+
let randomFallbackWarned = false;
|
|
11
|
+
function resolveSecret() {
|
|
12
|
+
const secret = process.env.AUTH_SECRET;
|
|
13
|
+
if (secret) return secret;
|
|
14
|
+
if (process.env.DEPLOY_TARGET === "desktop") {
|
|
15
|
+
if (!randomFallbackWarned) {
|
|
16
|
+
randomFallbackWarned = true;
|
|
17
|
+
console.warn("[kernel/crypto] AUTH_SECRET 未设置:已派生进程级随机密钥(仅限桌面端;重启后历史密文将无法解密)");
|
|
18
|
+
}
|
|
19
|
+
return randomBytes(32).toString("base64url");
|
|
20
|
+
}
|
|
21
|
+
throw new Error("[kernel/crypto] 缺少 AUTH_SECRET 环境变量:拒绝使用可预测密钥加密敏感数据。生产/开发部署必须显式配置 AUTH_SECRET;仅桌面端可设 DEPLOY_TARGET=desktop 启用进程级随机密钥。");
|
|
22
|
+
}
|
|
10
23
|
function deriveKey() {
|
|
11
24
|
if (cachedKey) return cachedKey;
|
|
12
|
-
|
|
13
|
-
cachedKey = pbkdf2Sync(secret, KDF_SALT, KDF_ITERATIONS, KEY_BYTES, "sha256");
|
|
25
|
+
cachedKey = pbkdf2Sync(resolveSecret(), KDF_SALT, KDF_ITERATIONS, KEY_BYTES, "sha256");
|
|
14
26
|
return cachedKey;
|
|
15
27
|
}
|
|
16
28
|
/** 格式:v1.<iv-b64url>.<cipher-b64url>.<tag-b64url> */
|
|
@@ -23,10 +35,11 @@ function encryptSecret(plain) {
|
|
|
23
35
|
}
|
|
24
36
|
function decryptSecret(payload) {
|
|
25
37
|
if (!payload) return null;
|
|
38
|
+
const key = deriveKey();
|
|
26
39
|
try {
|
|
27
40
|
const [v, ivS, dataS, tagS] = payload.split(".");
|
|
28
41
|
if (v !== VERSION || !ivS || !dataS || !tagS) return null;
|
|
29
|
-
const decipher = createDecipheriv(ALGO,
|
|
42
|
+
const decipher = createDecipheriv(ALGO, key, Buffer.from(ivS, "base64url"));
|
|
30
43
|
decipher.setAuthTag(Buffer.from(tagS, "base64url"));
|
|
31
44
|
return Buffer.concat([decipher.update(Buffer.from(dataS, "base64url")), decipher.final()]).toString("utf8");
|
|
32
45
|
} catch {
|
package/dist/index.d.ts
CHANGED
|
@@ -398,7 +398,7 @@ interface AnalyticsService {
|
|
|
398
398
|
}
|
|
399
399
|
//#endregion
|
|
400
400
|
//#region src/pro-contract/global-search.d.ts
|
|
401
|
-
interface SearchResult
|
|
401
|
+
interface SearchResult {
|
|
402
402
|
id: string;
|
|
403
403
|
type: 'document' | 'project' | 'story' | 'comment' | 'user' | 'tag';
|
|
404
404
|
title: string;
|
|
@@ -411,13 +411,13 @@ interface SearchResult$1 {
|
|
|
411
411
|
interface SearchParams {
|
|
412
412
|
query: string;
|
|
413
413
|
workspaceId?: string | null;
|
|
414
|
-
types?: Array<SearchResult
|
|
414
|
+
types?: Array<SearchResult['type']>;
|
|
415
415
|
limit?: number;
|
|
416
416
|
offset?: number;
|
|
417
417
|
}
|
|
418
418
|
interface GlobalSearchService {
|
|
419
419
|
search(params: SearchParams): Promise<{
|
|
420
|
-
results: SearchResult
|
|
420
|
+
results: SearchResult[];
|
|
421
421
|
total: number;
|
|
422
422
|
}>;
|
|
423
423
|
index(documentId: string, content: string, metadata: Record<string, unknown>): Promise<void>;
|
|
@@ -463,7 +463,12 @@ interface ComplianceService {
|
|
|
463
463
|
//#endregion
|
|
464
464
|
//#region src/pro-contract/module.d.ts
|
|
465
465
|
type ProEdition = 'oss' | 'pro';
|
|
466
|
-
|
|
466
|
+
/**
|
|
467
|
+
* Pro 功能键单一来源(generate-assembly 的 implementedModules 生成亦消费此常量)。
|
|
468
|
+
* 新增 feature 只改这里;ProFeature 联合类型由本数组派生,杜绝双维护漂移。
|
|
469
|
+
*/
|
|
470
|
+
declare const PRO_FEATURES: readonly ['billing', 'hostedAi', 'sso', 'audit', 'permissions', 'license', 'versionHistory', 'automation', 'templates', 'analytics', 'globalSearch', 'compliance'];
|
|
471
|
+
type ProFeature = (typeof PRO_FEATURES)[number];
|
|
467
472
|
interface ProModule {
|
|
468
473
|
readonly isAvailable: boolean;
|
|
469
474
|
readonly edition: ProEdition;
|
|
@@ -582,6 +587,13 @@ declare function generateAssemblyFiles(input: AssembleInput): AssemblyFiles;
|
|
|
582
587
|
declare function dumpModules(profileId: string, resolved: ResolveResult): string;
|
|
583
588
|
//#endregion
|
|
584
589
|
//#region src/server-seam.d.ts
|
|
590
|
+
/**
|
|
591
|
+
* Seam 注入模式:宿主(start.ts)与 database 包各自注册实现,契约包只声明接口。
|
|
592
|
+
*
|
|
593
|
+
* ServerSeam —— 宿主注入(鉴权 / 会话 / 平台判定);
|
|
594
|
+
* PrismaSeam —— database 包注入(prisma 接入点,解决 shared → database → shared 循环依赖)。
|
|
595
|
+
* 两者同构的「模块级单例 + 注册 + 未注册抛错」逻辑由 createSeam 泛型工厂统一提供。
|
|
596
|
+
*/
|
|
585
597
|
interface HostSession {
|
|
586
598
|
user?: {
|
|
587
599
|
id?: string;
|
|
@@ -590,14 +602,20 @@ interface HostSession {
|
|
|
590
602
|
} | null;
|
|
591
603
|
[key: string]: unknown;
|
|
592
604
|
}
|
|
605
|
+
/** 鉴权成功后的会话形状:注册实现(web/admin requireAuth)保证 user.id 必在,消费方免 `session.user!.id!` 断言 */
|
|
606
|
+
type AuthedHostSession = HostSession & {
|
|
607
|
+
user: {
|
|
608
|
+
id: string;
|
|
609
|
+
role?: string | null;
|
|
610
|
+
[key: string]: unknown;
|
|
611
|
+
};
|
|
612
|
+
};
|
|
593
613
|
interface ServerSeam {
|
|
594
|
-
requireAuth: (request?: Request) => Promise<
|
|
614
|
+
requireAuth: (request?: Request) => Promise<AuthedHostSession>;
|
|
595
615
|
requireUserInDb: (userId: string) => Promise<void>;
|
|
596
616
|
getCurrentSession: (request?: Request) => Promise<HostSession | null>;
|
|
597
617
|
isDesktop: () => boolean;
|
|
598
618
|
}
|
|
599
|
-
declare function registerServerSeam(impl: ServerSeam): void;
|
|
600
|
-
declare function getServerSeam(): ServerSeam;
|
|
601
619
|
/**
|
|
602
620
|
* PrismaSeam — 注入式 prisma 接入点
|
|
603
621
|
*
|
|
@@ -610,8 +628,14 @@ interface PrismaSeam {
|
|
|
610
628
|
create: (data: unknown) => Promise<unknown>;
|
|
611
629
|
findMany: (where: unknown) => Promise<unknown[]>;
|
|
612
630
|
};
|
|
631
|
+
/**
|
|
632
|
+
* ⚠ 硬约束:`sql` 必须是常量字符串(模板内不允许出现任何来源的用户输入拼接),
|
|
633
|
+
* 用户输入只允许通过 `...params` 占位参数传入。违反即为 SQL 注入。
|
|
634
|
+
*/
|
|
613
635
|
$queryRawUnsafe: <T = Record<string, unknown>>(sql: string, ...params: unknown[]) => Promise<T[]>;
|
|
614
636
|
}
|
|
637
|
+
declare function registerServerSeam(impl: ServerSeam): void;
|
|
638
|
+
declare function getServerSeam(): ServerSeam;
|
|
615
639
|
declare function registerPrismaSeam(seam: PrismaSeam): void;
|
|
616
640
|
//#endregion
|
|
617
641
|
//#region src/contract/constants/perf.d.ts
|
|
@@ -623,7 +647,7 @@ declare const DND_STICKY_THRESHOLD = 500;
|
|
|
623
647
|
type AdapterKind = 'openai' | 'anthropic' | 'gemini' | 'groq' | 'grok' | 'openrouter' | 'ollama' | 'openai-compatible' | 'mistral' | 'bedrock' | 'byteplus' | 'fal' |
|
|
624
648
|
/** harness 型 provider(需沙箱上下文,不走 createProviderAdapter 普通路径) */
|
|
625
649
|
'harness-claude-code' | 'harness-codex' | 'harness-acp' | 'harness-opencode';
|
|
626
|
-
type ProviderCategory = 'recommended' | 'popular' | 'local' | 'harness'
|
|
650
|
+
type ProviderCategory = 'recommended' | 'popular' | 'local' | 'harness';
|
|
627
651
|
type ModelCapability = 'text' | 'vision' | 'tools' | 'reasoning' | 'audio';
|
|
628
652
|
interface AiModelMeta {
|
|
629
653
|
/** 模型 ID(传给 adapter 的 model 字符串) */
|
|
@@ -681,7 +705,13 @@ interface AiProviderRow extends AiProviderMeta {
|
|
|
681
705
|
/** 模型来源模式:平台托管 / 本地 Agent / 使用自己的 Key */
|
|
682
706
|
type AiSourceMode = 'hosted' | 'local-agent' | 'byok';
|
|
683
707
|
declare const AI_SOURCE_MODES: readonly ['hosted', 'local-agent', 'byok'];
|
|
684
|
-
/**
|
|
708
|
+
/**
|
|
709
|
+
* 卡片角标的预设颜色(按文案匹配),admin 也可自由输入文案。
|
|
710
|
+
*
|
|
711
|
+
* ⚠ 这里的类名字符串位于 npm 包 dist 内,app 侧 Tailwind v4 不会自动扫描
|
|
712
|
+
* node_modules——消费方 globals.css 必须显式 `@source` 指向本包 dist,
|
|
713
|
+
* 否则角标样式在生产构建被 tree-shake 静默丢失(不报错)。
|
|
714
|
+
*/
|
|
685
715
|
declare const PROVIDER_BADGE_STYLES: Record<string, string>;
|
|
686
716
|
//#endregion
|
|
687
717
|
//#region src/contract/modules/ai-providers/constants.d.ts
|
|
@@ -690,6 +720,11 @@ declare const ADAPTER_KINDS: Array<{
|
|
|
690
720
|
label: string;
|
|
691
721
|
description: string;
|
|
692
722
|
}>;
|
|
723
|
+
/**
|
|
724
|
+
* 服务商分类筛选项。有意不含 'harness'(沙箱 harness 型服务商需专用上下文,
|
|
725
|
+
* 不进常规筛选)与 'hosted'(已从 ProviderCategory 移除,托管语义归
|
|
726
|
+
* AiSourceMode);若未来放开筛选,在此补全映射。
|
|
727
|
+
*/
|
|
693
728
|
declare const PROVIDER_CATEGORIES: Array<{
|
|
694
729
|
value: ProviderCategory;
|
|
695
730
|
label: string;
|
|
@@ -716,19 +751,36 @@ declare const MODEL_TABS: Array<{
|
|
|
716
751
|
declare const LOBE_ICON_MAP: Record<string, string>;
|
|
717
752
|
//#endregion
|
|
718
753
|
//#region src/contract/modules/ai-providers/seed-data.d.ts
|
|
719
|
-
/**
|
|
754
|
+
/** 18 个 AI 服务商的种子数据(14 个常规 + 4 个 harness 沙箱型),admin 可在其基础上增删改 */
|
|
720
755
|
declare const SEED_AI_PROVIDERS: AiProviderMeta[];
|
|
721
756
|
//#endregion
|
|
722
|
-
//#region src/contract/modules/ai-prompts/
|
|
723
|
-
interface
|
|
757
|
+
//#region src/contract/modules/ai-prompts/builtin.d.ts
|
|
758
|
+
interface PromptSection {
|
|
724
759
|
id: string;
|
|
725
760
|
order: number;
|
|
726
761
|
template: string;
|
|
727
762
|
}
|
|
728
|
-
interface
|
|
763
|
+
interface PromptVariableDef {
|
|
729
764
|
name: string;
|
|
730
765
|
description: string;
|
|
731
766
|
}
|
|
767
|
+
interface PromptDefinition {
|
|
768
|
+
key: string;
|
|
769
|
+
name: string;
|
|
770
|
+
description?: string;
|
|
771
|
+
sections: PromptSection[];
|
|
772
|
+
variables: PromptVariableDef[];
|
|
773
|
+
isProtected: boolean;
|
|
774
|
+
}
|
|
775
|
+
declare const TIPTAP_ACTION_INSTRUCTIONS: Record<string, string>;
|
|
776
|
+
declare const PROMPT_DEFINITIONS: PromptDefinition[];
|
|
777
|
+
declare function listPromptDefinitions(): PromptDefinition[];
|
|
778
|
+
declare function getPromptDefinition(key: string): PromptDefinition | undefined;
|
|
779
|
+
declare function promptDefinitionKeys(): string[];
|
|
780
|
+
//#endregion
|
|
781
|
+
//#region src/contract/modules/ai-prompts/types.d.ts
|
|
782
|
+
type SystemPromptSectionDTO = PromptSection;
|
|
783
|
+
type PromptVariableDefDTO = PromptVariableDef;
|
|
732
784
|
interface SystemPromptTemplateDTO {
|
|
733
785
|
id: string;
|
|
734
786
|
key: string;
|
|
@@ -757,30 +809,6 @@ declare class UnknownPromptVariableError extends Error {
|
|
|
757
809
|
}
|
|
758
810
|
declare function interpolateTemplate(template: string, vars: Record<string, string>): string;
|
|
759
811
|
//#endregion
|
|
760
|
-
//#region src/contract/modules/ai-prompts/builtin.d.ts
|
|
761
|
-
interface PromptSection {
|
|
762
|
-
id: string;
|
|
763
|
-
order: number;
|
|
764
|
-
template: string;
|
|
765
|
-
}
|
|
766
|
-
interface PromptVariableDef {
|
|
767
|
-
name: string;
|
|
768
|
-
description: string;
|
|
769
|
-
}
|
|
770
|
-
interface PromptDefinition {
|
|
771
|
-
key: string;
|
|
772
|
-
name: string;
|
|
773
|
-
description?: string;
|
|
774
|
-
sections: PromptSection[];
|
|
775
|
-
variables: PromptVariableDef[];
|
|
776
|
-
isProtected: boolean;
|
|
777
|
-
}
|
|
778
|
-
declare const TIPTAP_ACTION_INSTRUCTIONS: Record<string, string>;
|
|
779
|
-
declare const PROMPT_DEFINITIONS: PromptDefinition[];
|
|
780
|
-
declare function listPromptDefinitions(): PromptDefinition[];
|
|
781
|
-
declare function getPromptDefinition(key: string): PromptDefinition | undefined;
|
|
782
|
-
declare function promptDefinitionKeys(): string[];
|
|
783
|
-
//#endregion
|
|
784
812
|
//#region src/contract/modules/dependencies/index.d.ts
|
|
785
813
|
/** 依赖分类 */
|
|
786
814
|
type DependencyCategory = 'framework' | 'ui' | 'editor' | 'ai' | 'state' | 'auth' | 'tool' | 'other';
|
|
@@ -876,7 +904,13 @@ declare const DEFAULT_WATCHED_DEPENDENCIES: DependencySeed[];
|
|
|
876
904
|
declare function resolveIpLocation(ip: string | null | undefined): Promise<string>;
|
|
877
905
|
/**
|
|
878
906
|
* 从 Request 中提取客户端真实 IP
|
|
879
|
-
*
|
|
907
|
+
*
|
|
908
|
+
* 优先级:CF-Connecting-IP(Cloudflare 平台注入的权威头,客户端不可伪造)
|
|
909
|
+
* > X-Real-IP > X-Forwarded-For 末段。
|
|
910
|
+
*
|
|
911
|
+
* ⚠ 前提声明:XFF 首值可由客户端任意伪造,因此取**末段**(最靠近本服务的
|
|
912
|
+
* 可信代理所追加的一跳)。部署平台若自身不覆写 XFF(Vercel 会覆写),
|
|
913
|
+
* 末段仍可能被上游代理链污染;Cloudflare 场景以 CF-Connecting-IP 为准。
|
|
880
914
|
*/
|
|
881
915
|
declare function getClientIp(request: Request): string | null;
|
|
882
916
|
//#endregion
|
|
@@ -895,10 +929,37 @@ interface ParsedUA {
|
|
|
895
929
|
}
|
|
896
930
|
declare function parseUserAgent(ua: string | null | undefined): ParsedUA;
|
|
897
931
|
/**
|
|
898
|
-
* 将 ParsedUA 拼接为可读字符串,如 "Chrome 120 / Windows 10/11 /
|
|
932
|
+
* 将 ParsedUA 拼接为可读字符串,如 "Chrome 120 / Windows 10/11 / Desktop"
|
|
933
|
+
* (输出为英文原文;中文文案由展示层经 paraglide 映射)
|
|
899
934
|
*/
|
|
900
935
|
declare function formatParsedUA(parsed: ParsedUA): string;
|
|
901
936
|
//#endregion
|
|
937
|
+
//#region src/contract/interfaces/references.d.ts
|
|
938
|
+
interface RefDisplay {
|
|
939
|
+
title: string;
|
|
940
|
+
status?: string;
|
|
941
|
+
priority?: string;
|
|
942
|
+
url?: string;
|
|
943
|
+
}
|
|
944
|
+
interface Reference {
|
|
945
|
+
id: string;
|
|
946
|
+
sourceModule: string;
|
|
947
|
+
sourceEntityId: string;
|
|
948
|
+
targetModule: string;
|
|
949
|
+
targetEntityId: string;
|
|
950
|
+
}
|
|
951
|
+
interface ResolvedRef extends Reference {
|
|
952
|
+
display: RefDisplay;
|
|
953
|
+
}
|
|
954
|
+
interface SearchResult$1 {
|
|
955
|
+
id: string;
|
|
956
|
+
module: string;
|
|
957
|
+
type: string;
|
|
958
|
+
title: string;
|
|
959
|
+
status?: string;
|
|
960
|
+
url?: string;
|
|
961
|
+
}
|
|
962
|
+
//#endregion
|
|
902
963
|
//#region src/contract/interfaces/module.d.ts
|
|
903
964
|
interface EntityDef {
|
|
904
965
|
type: string;
|
|
@@ -927,12 +988,7 @@ interface AICommandDef {
|
|
|
927
988
|
entityTypes: string[];
|
|
928
989
|
handler: AICommandHandler;
|
|
929
990
|
}
|
|
930
|
-
type ReferenceDisplayFn = (type: string, data: Record<string, unknown>) =>
|
|
931
|
-
title: string;
|
|
932
|
-
status?: string;
|
|
933
|
-
priority?: string;
|
|
934
|
-
url?: string;
|
|
935
|
-
};
|
|
991
|
+
type ReferenceDisplayFn = (type: string, data: Record<string, unknown>) => RefDisplay;
|
|
936
992
|
interface AllBlueModule {
|
|
937
993
|
id: string;
|
|
938
994
|
name: string;
|
|
@@ -943,32 +999,6 @@ interface AllBlueModule {
|
|
|
943
999
|
referenceDisplay: ReferenceDisplayFn;
|
|
944
1000
|
}
|
|
945
1001
|
//#endregion
|
|
946
|
-
//#region src/contract/interfaces/references.d.ts
|
|
947
|
-
interface RefDisplay {
|
|
948
|
-
title: string;
|
|
949
|
-
status?: string;
|
|
950
|
-
priority?: string;
|
|
951
|
-
url?: string;
|
|
952
|
-
}
|
|
953
|
-
interface Reference {
|
|
954
|
-
id: string;
|
|
955
|
-
sourceModule: string;
|
|
956
|
-
sourceEntityId: string;
|
|
957
|
-
targetModule: string;
|
|
958
|
-
targetEntityId: string;
|
|
959
|
-
}
|
|
960
|
-
interface ResolvedRef extends Reference {
|
|
961
|
-
display: RefDisplay;
|
|
962
|
-
}
|
|
963
|
-
interface SearchResult {
|
|
964
|
-
id: string;
|
|
965
|
-
module: string;
|
|
966
|
-
type: string;
|
|
967
|
-
title: string;
|
|
968
|
-
status?: string;
|
|
969
|
-
url?: string;
|
|
970
|
-
}
|
|
971
|
-
//#endregion
|
|
972
1002
|
//#region src/contract/interfaces/ai.d.ts
|
|
973
1003
|
interface CLIAgentInfo {
|
|
974
1004
|
id: string;
|
|
@@ -991,7 +1021,8 @@ interface AiAdapter {
|
|
|
991
1021
|
entityIds: string[];
|
|
992
1022
|
userInput?: string;
|
|
993
1023
|
}): AsyncGenerator<string>;
|
|
994
|
-
|
|
1024
|
+
/** 当前 agent id;未选择时为 null(与 infrastructure/ai/adapter.ts 实现一致) */
|
|
1025
|
+
getActiveAgent(): string | null;
|
|
995
1026
|
switchAgent(agentId: string): void;
|
|
996
1027
|
}
|
|
997
1028
|
//#endregion
|
|
@@ -1219,6 +1250,7 @@ declare function isResourceDefined(key: string): key is ResourceKey;
|
|
|
1219
1250
|
interface DeveloperLogEntry {
|
|
1220
1251
|
id: string;
|
|
1221
1252
|
date: string;
|
|
1253
|
+
/** DB 列投影(developerLog.year,供 orderBy/分组直用),恒等于 date 的年份——改列需同步迁移 */
|
|
1222
1254
|
year: string;
|
|
1223
1255
|
title: string;
|
|
1224
1256
|
desc: string;
|
|
@@ -1285,12 +1317,26 @@ declare function normalizeCategoryFrequencies(raw: unknown): CategoryFrequencies
|
|
|
1285
1317
|
* 部分缺失(只有部分 key)或缺失值非布尔(类型错误)时,该 key 用默认值。
|
|
1286
1318
|
*/
|
|
1287
1319
|
declare function normalizeEmailCategories(raw: unknown): CategoryEmailFlags;
|
|
1320
|
+
/**
|
|
1321
|
+
* 判定 now(HH:MM)是否落在勿扰窗口内(支持跨零点)。
|
|
1322
|
+
*
|
|
1323
|
+
* 畸形时间输入(空串 / 非数字,如 DB 脏数据)旧实现产出 NaN、比较恒 false、
|
|
1324
|
+
* DND 静默失效——现显式校验,畸形时告警一次并按「不在勿扰窗口」处理。
|
|
1325
|
+
*/
|
|
1288
1326
|
declare function isInDndWindow(prefs: Pick<NotificationPreferences, 'dndEnabled' | 'dndStart' | 'dndEnd'>, now: string): boolean;
|
|
1289
1327
|
//#endregion
|
|
1290
1328
|
//#region src/contract/modules/registry.d.ts
|
|
1291
1329
|
declare class ModuleRegistry {
|
|
1292
1330
|
private modules;
|
|
1293
|
-
|
|
1331
|
+
/**
|
|
1332
|
+
* 注册模块。同 id 重复注册的语义:
|
|
1333
|
+
* - 显式 `{ overwrite: true }`:警告并覆盖(有意的替换场景);
|
|
1334
|
+
* - 生产环境:视为装配期配置错误直接抛出,避免「两个模块意外同 id」被静默掩盖;
|
|
1335
|
+
* - dev/test/HMR:警告并覆盖(模块双执行 / 热更新场景保持宽松)。
|
|
1336
|
+
*/
|
|
1337
|
+
register(mod: AllBlueModule, options?: {
|
|
1338
|
+
overwrite?: boolean;
|
|
1339
|
+
}): void;
|
|
1294
1340
|
getModule(id: string): AllBlueModule | undefined;
|
|
1295
1341
|
getAll(): AllBlueModule[];
|
|
1296
1342
|
getAICommands(): AICommandDef[];
|
|
@@ -1400,8 +1446,8 @@ interface DesignVersionWithSemver extends DesignVersionBase {
|
|
|
1400
1446
|
minor: number;
|
|
1401
1447
|
patch: number;
|
|
1402
1448
|
}
|
|
1403
|
-
declare const ICON_MAP: Record<string,
|
|
1449
|
+
declare const ICON_MAP: Record<string, ComponentType<{
|
|
1404
1450
|
className?: string;
|
|
1405
1451
|
}>>;
|
|
1406
1452
|
//#endregion
|
|
1407
|
-
export { ADAPTER_KINDS, ADAPTER_KIND_VALUES, type AICommandContext, type AICommandDef, type AICommandHandler, type AICommandResult, AI_SOURCE_MODES, type ActivationResult, AdapterKind, type AiAdapter, AiModelMeta, AiProviderMeta, AiProviderRow, type AiQuota, AiSourceMode, type AllBlueModule, type AnalyticsQuery, type AnalyticsResult, type AnalyticsService, type AssembleInput, type AssemblyFiles, type AuditLogEntry, type AuditQuery, type AuditService, type AutomationRule, type AutomationService, type AvatarConfig, type AvatarPart, type BillingService, BillingUnavailableError, type CLIAgentInfo, type CanvasElement, CategoryEmailFlags, CategoryFrequencies, type CheckoutParams, type ComplianceJobSummary, type ComplianceService, type ComponentCategory, type ComponentSource, type CustomDashboard, DEFAULT_CATEGORIES, DEFAULT_EMAIL_CATEGORIES, DEFAULT_WATCHED_DEPENDENCIES, DEPENDENCY_CATEGORIES, DEPENDENCY_PRIORITIES, DEVICE_SIZES, DND_STICKY_THRESHOLD, DependencyCategory, DependencyPriority, DependencySeed, DependencyUsage, type Design, type DesignDetail, type DesignVersionWithPages, type DesignVersionWithSemver, type DeveloperLogEntry, type DeviceType, type DocumentTemplate, type DocumentVersion, type EditableFieldType, type EditablePropField, type EntityDef, type EntityUpdateEvent, FEATURE_KEYS, type FeatureContribution, type FeatureKey, type FeatureMeta, FeatureRegistry, type GlobalSearchService, type HostSession, type HostedAiChatParams, type HostedAiChatResult, HostedAiError, type HostedAiModel, type HostedAiService, ICON_MAP, type InvoiceInfo, LOAD_MORE_STEP, LOBE_ICON_MAP, type LicenseInfo, type LicenseIssueParams, type LicenseIssueResult, type LicenseService, type LicenseStatus, MODEL_CAPABILITY_OPTIONS, MODEL_TABS, type Middleware, ModelCapability, ModelTab, NOTIFICATION_CATEGORIES, NOTIFICATION_FREQUENCIES, NotificationCategory, NotificationFrequency, NotificationItem, NotificationListResult, NotificationPreferences, NotificationSsePayload, NotificationSseValue, OSS_BASE_FEATURES, type OutlineNode, PROMPT_DEFINITIONS, PROVIDER_BADGE_STYLES, PROVIDER_CATEGORIES, PROVIDER_CATEGORY_VALUES, type ParsedUA, type PermissionLevel, type PermissionsService, type PlanFeatures, PlanFeaturesSchema, type PlanInfo, type PluginContributions, type PluginDefinition, type PluginRouteContext, type PortalSessionParams, type PrismaContribution, type PrismaSeam, type ProEdition, type ProFeature, type ProModule, type ProServiceMap, type ProfileDefinition, PromptDefinition, PromptSection, PromptVariableDef, PromptVariableDefDTO, type PrototypeComponentConfig, ProviderCategory, type QueryFactoryMap, RESOURCE_KEYS, type RealtimeTransport, type RefDisplay, type Reference, type ReferenceDisplayFn, type ResizeDirection, type ResizeMode, type ResolveResult, type ResolvedRef, type ResourceKey, type ResourceMeta, ResourceRegistry, type ResourceSnapshot, type ResourceSnapshots, type RoleDefinition, type RouteContribution, SEED_AI_PROVIDERS, type SearchParams, type SearchResult, type SeatCheckResult, SeatsExceededError, type ServerSeam, type SettingsTabContribution, type ShapeType, type SsoProvider, type SsoService, type SubscriptionInfo, SyncDependencyResult, SystemPromptSectionDTO, SystemPromptTemplateDTO, SystemPromptVersionDTO, TIPTAP_ACTION_INSTRUCTIONS, type TemplatesService, ThirdPartyDependencyRow, type UiSlotContributions, UnknownPromptVariableError, UnreadSnapshotPayload, UpdateDependencyInput, VIRTUALIZE_THRESHOLD, type VersionHistoryService, VersionStatus, WebhookSignatureError, definePlugin, defineProfile, dumpModules, formatParsedUA, generateAssemblyCode, generateAssemblyFiles, getClientIp, getPromptDefinition, getServerSeam, interpolateTemplate, isInDndWindow, isOssFeature, isResourceDefined, listPromptDefinitions, moduleRegistry, normalizeCategoryFrequencies, normalizeEmailCategories, parseProfileImports, parseUserAgent, promptDefinitionKeys, registerPrismaSeam, registerServerSeam, resolveImportSpecifiers, resolveIpLocation, resolvePluginOrder };
|
|
1453
|
+
export { ADAPTER_KINDS, ADAPTER_KIND_VALUES, type AICommandContext, type AICommandDef, type AICommandHandler, type AICommandResult, AI_SOURCE_MODES, type ActivationResult, AdapterKind, type AiAdapter, AiModelMeta, AiProviderMeta, AiProviderRow, type AiQuota, AiSourceMode, type AllBlueModule, type AnalyticsQuery, type AnalyticsResult, type AnalyticsService, type AssembleInput, type AssemblyFiles, type AuditLogEntry, type AuditQuery, type AuditService, type AutomationRule, type AutomationService, type AvatarConfig, type AvatarPart, type BillingService, BillingUnavailableError, type CLIAgentInfo, type CanvasElement, CategoryEmailFlags, CategoryFrequencies, type CheckoutParams, type ComplianceJobSummary, type ComplianceService, type ComponentCategory, type ComponentSource, type CustomDashboard, DEFAULT_CATEGORIES, DEFAULT_EMAIL_CATEGORIES, DEFAULT_WATCHED_DEPENDENCIES, DEPENDENCY_CATEGORIES, DEPENDENCY_PRIORITIES, DEVICE_SIZES, DND_STICKY_THRESHOLD, DependencyCategory, DependencyPriority, DependencySeed, DependencyUsage, type Design, type DesignDetail, type DesignVersionWithPages, type DesignVersionWithSemver, type DeveloperLogEntry, type DeviceType, type DocumentTemplate, type DocumentVersion, type EditableFieldType, type EditablePropField, type EntityDef, type EntityUpdateEvent, FEATURE_KEYS, type FeatureContribution, type FeatureKey, type FeatureMeta, FeatureRegistry, type SearchResult as GlobalSearchResult, type GlobalSearchService, type HostSession, type HostedAiChatParams, type HostedAiChatResult, HostedAiError, type HostedAiModel, type HostedAiService, ICON_MAP, type InvoiceInfo, LOAD_MORE_STEP, LOBE_ICON_MAP, type LicenseInfo, type LicenseIssueParams, type LicenseIssueResult, type LicenseService, type LicenseStatus, MODEL_CAPABILITY_OPTIONS, MODEL_TABS, type Middleware, ModelCapability, ModelTab, NOTIFICATION_CATEGORIES, NOTIFICATION_FREQUENCIES, NotificationCategory, NotificationFrequency, NotificationItem, NotificationListResult, NotificationPreferences, NotificationSsePayload, NotificationSseValue, OSS_BASE_FEATURES, type OutlineNode, PROMPT_DEFINITIONS, PROVIDER_BADGE_STYLES, PROVIDER_CATEGORIES, PROVIDER_CATEGORY_VALUES, PRO_FEATURES, type ParsedUA, type PermissionLevel, type PermissionsService, type PlanFeatures, PlanFeaturesSchema, type PlanInfo, type PluginContributions, type PluginDefinition, type PluginRouteContext, type PortalSessionParams, type PrismaContribution, type PrismaSeam, type ProEdition, type ProFeature, type ProModule, type ProServiceMap, type ProfileDefinition, PromptDefinition, PromptSection, PromptVariableDef, PromptVariableDefDTO, type PrototypeComponentConfig, ProviderCategory, type QueryFactoryMap, RESOURCE_KEYS, type RealtimeTransport, type RefDisplay, type Reference, type ReferenceDisplayFn, type ResizeDirection, type ResizeMode, type ResolveResult, type ResolvedRef, type ResourceKey, type ResourceMeta, ResourceRegistry, type ResourceSnapshot, type ResourceSnapshots, type RoleDefinition, type RouteContribution, SEED_AI_PROVIDERS, type SearchParams, type SearchResult$1 as SearchResult, type SeatCheckResult, type SeatUpdateParams, SeatsExceededError, type ServerSeam, type SettingsTabContribution, type ShapeType, type SsoProvider, type SsoService, type SubscriptionInfo, SyncDependencyResult, SystemPromptSectionDTO, SystemPromptTemplateDTO, SystemPromptVersionDTO, TIPTAP_ACTION_INSTRUCTIONS, type TemplatesService, ThirdPartyDependencyRow, type UiSlotContributions, UnknownPromptVariableError, UnreadSnapshotPayload, UpdateDependencyInput, VIRTUALIZE_THRESHOLD, type VersionHistoryService, VersionStatus, WebhookSignatureError, definePlugin, defineProfile, dumpModules, formatParsedUA, generateAssemblyCode, generateAssemblyFiles, getClientIp, getPromptDefinition, getServerSeam, interpolateTemplate, isInDndWindow, isOssFeature, isResourceDefined, listPromptDefinitions, moduleRegistry, normalizeCategoryFrequencies, normalizeEmailCategories, parseProfileImports, parseUserAgent, promptDefinitionKeys, registerPrismaSeam, registerServerSeam, resolveImportSpecifiers, resolveIpLocation, resolvePluginOrder };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as dumpModules, A as promptDefinitionKeys, B as PROVIDER_CATEGORY_VALUES, C as DEFAULT_WATCHED_DEPENDENCIES, D as TIPTAP_ACTION_INSTRUCTIONS, E as PROMPT_DEFINITIONS, F as ADAPTER_KIND_VALUES, G as VIRTUALIZE_THRESHOLD, H as PROVIDER_BADGE_STYLES, I as LOBE_ICON_MAP, J as registerServerSeam, K as getServerSeam, L as MODEL_CAPABILITY_OPTIONS, M as interpolateTemplate, N as SEED_AI_PROVIDERS, O as getPromptDefinition, P as ADAPTER_KINDS, Q as WebhookSignatureError, R as MODEL_TABS, S as resolveIpLocation, T as DEPENDENCY_PRIORITIES, U as DND_STICKY_THRESHOLD, V as AI_SOURCE_MODES, W as LOAD_MORE_STEP, X as BillingUnavailableError, Y as HostedAiError, Z as SeatsExceededError, _ as OSS_BASE_FEATURES, a as DEFAULT_EMAIL_CATEGORIES, at as
|
|
2
|
-
export { ADAPTER_KINDS, ADAPTER_KIND_VALUES, AI_SOURCE_MODES, BillingUnavailableError, DEFAULT_CATEGORIES, DEFAULT_EMAIL_CATEGORIES, DEFAULT_WATCHED_DEPENDENCIES, DEPENDENCY_CATEGORIES, DEPENDENCY_PRIORITIES, DEVICE_SIZES, DND_STICKY_THRESHOLD, FEATURE_KEYS, FeatureRegistry, HostedAiError, ICON_MAP, LOAD_MORE_STEP, LOBE_ICON_MAP, MODEL_CAPABILITY_OPTIONS, MODEL_TABS, NOTIFICATION_CATEGORIES, NOTIFICATION_FREQUENCIES, OSS_BASE_FEATURES, PROMPT_DEFINITIONS, PROVIDER_BADGE_STYLES, PROVIDER_CATEGORIES, PROVIDER_CATEGORY_VALUES, PlanFeaturesSchema, RESOURCE_KEYS, ResourceRegistry, SEED_AI_PROVIDERS, SeatsExceededError, TIPTAP_ACTION_INSTRUCTIONS, UnknownPromptVariableError, VIRTUALIZE_THRESHOLD, WebhookSignatureError, definePlugin, defineProfile, dumpModules, formatParsedUA, generateAssemblyCode, generateAssemblyFiles, getClientIp, getPromptDefinition, getServerSeam, interpolateTemplate, isInDndWindow, isOssFeature, isResourceDefined, listPromptDefinitions, moduleRegistry, normalizeCategoryFrequencies, normalizeEmailCategories, parseProfileImports, parseUserAgent, promptDefinitionKeys, registerPrismaSeam, registerServerSeam, resolveImportSpecifiers, resolveIpLocation, resolvePluginOrder };
|
|
1
|
+
import { $ as dumpModules, A as promptDefinitionKeys, B as PROVIDER_CATEGORY_VALUES, C as DEFAULT_WATCHED_DEPENDENCIES, D as TIPTAP_ACTION_INSTRUCTIONS, E as PROMPT_DEFINITIONS, F as ADAPTER_KIND_VALUES, G as VIRTUALIZE_THRESHOLD, H as PROVIDER_BADGE_STYLES, I as LOBE_ICON_MAP, J as registerServerSeam, K as getServerSeam, L as MODEL_CAPABILITY_OPTIONS, M as interpolateTemplate, N as SEED_AI_PROVIDERS, O as getPromptDefinition, P as ADAPTER_KINDS, Q as WebhookSignatureError, R as MODEL_TABS, S as resolveIpLocation, T as DEPENDENCY_PRIORITIES, U as DND_STICKY_THRESHOLD, V as AI_SOURCE_MODES, W as LOAD_MORE_STEP, X as BillingUnavailableError, Y as HostedAiError, Z as SeatsExceededError, _ as OSS_BASE_FEATURES, a as DEFAULT_EMAIL_CATEGORIES, at as resolveImportSpecifiers, b as parseUserAgent, c as normalizeEmailCategories, ct as definePlugin, d as RESOURCE_KEYS, et as generateAssemblyCode, f as ResourceRegistry, g as FeatureRegistry, h as FEATURE_KEYS, i as DEFAULT_CATEGORIES, it as parseProfileImports, j as UnknownPromptVariableError, k as listPromptDefinitions, l as NOTIFICATION_CATEGORIES, m as PlanFeaturesSchema, n as DEVICE_SIZES, nt as PRO_FEATURES, o as isInDndWindow, ot as resolvePluginOrder, p as isResourceDefined, q as registerPrismaSeam, r as moduleRegistry, s as normalizeCategoryFrequencies, st as defineProfile, t as ICON_MAP, tt as generateAssemblyFiles, u as NOTIFICATION_FREQUENCIES, v as isOssFeature, w as DEPENDENCY_CATEGORIES, x as getClientIp, y as formatParsedUA, z as PROVIDER_CATEGORIES } from "./src-CyBj_lD7.js";
|
|
2
|
+
export { ADAPTER_KINDS, ADAPTER_KIND_VALUES, AI_SOURCE_MODES, BillingUnavailableError, DEFAULT_CATEGORIES, DEFAULT_EMAIL_CATEGORIES, DEFAULT_WATCHED_DEPENDENCIES, DEPENDENCY_CATEGORIES, DEPENDENCY_PRIORITIES, DEVICE_SIZES, DND_STICKY_THRESHOLD, FEATURE_KEYS, FeatureRegistry, HostedAiError, ICON_MAP, LOAD_MORE_STEP, LOBE_ICON_MAP, MODEL_CAPABILITY_OPTIONS, MODEL_TABS, NOTIFICATION_CATEGORIES, NOTIFICATION_FREQUENCIES, OSS_BASE_FEATURES, PROMPT_DEFINITIONS, PROVIDER_BADGE_STYLES, PROVIDER_CATEGORIES, PROVIDER_CATEGORY_VALUES, PRO_FEATURES, PlanFeaturesSchema, RESOURCE_KEYS, ResourceRegistry, SEED_AI_PROVIDERS, SeatsExceededError, TIPTAP_ACTION_INSTRUCTIONS, UnknownPromptVariableError, VIRTUALIZE_THRESHOLD, WebhookSignatureError, definePlugin, defineProfile, dumpModules, formatParsedUA, generateAssemblyCode, generateAssemblyFiles, getClientIp, getPromptDefinition, getServerSeam, interpolateTemplate, isInDndWindow, isOssFeature, isResourceDefined, listPromptDefinitions, moduleRegistry, normalizeCategoryFrequencies, normalizeEmailCategories, parseProfileImports, parseUserAgent, promptDefinitionKeys, registerPrismaSeam, registerServerSeam, resolveImportSpecifiers, resolveIpLocation, resolvePluginOrder };
|
|
@@ -2,9 +2,10 @@ import { z } from "zod";
|
|
|
2
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
3
|
//#region src/define-plugin.ts
|
|
4
4
|
const PLUGIN_ID_RE = /^@[a-z0-9-]+\/[a-z0-9-]+$/;
|
|
5
|
+
const SEMVER_RE = /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$/;
|
|
5
6
|
function definePlugin(def) {
|
|
6
7
|
if (!PLUGIN_ID_RE.test(def.id)) throw new Error(`插件 id 必须形如 '@scope/name'(小写),收到: ${def.id}`);
|
|
7
|
-
if (
|
|
8
|
+
if (!SEMVER_RE.test(def.version)) throw new Error(`插件 version 必须是语义化版本,收到: ${def.version}`);
|
|
8
9
|
return deepFreeze(def);
|
|
9
10
|
}
|
|
10
11
|
function deepFreeze(value) {
|
|
@@ -71,8 +72,7 @@ function findCyclePath(cyclicIds, byId) {
|
|
|
71
72
|
const visited = /* @__PURE__ */ new Set([start]);
|
|
72
73
|
let current = start;
|
|
73
74
|
while (true) {
|
|
74
|
-
const
|
|
75
|
-
const next = deps.find((d) => cyclicIds.includes(d)) ?? deps[0];
|
|
75
|
+
const next = (byId.get(current)?.dependencies ?? []).find((d) => cyclicIds.includes(d));
|
|
76
76
|
if (!next) break;
|
|
77
77
|
if (next === start || visited.has(next)) {
|
|
78
78
|
path.push(next);
|
|
@@ -82,10 +82,72 @@ function findCyclePath(cyclicIds, byId) {
|
|
|
82
82
|
path.push(next);
|
|
83
83
|
current = next;
|
|
84
84
|
}
|
|
85
|
-
return path;
|
|
85
|
+
return path.length > 1 ? path : cyclicIds;
|
|
86
86
|
}
|
|
87
87
|
//#endregion
|
|
88
88
|
//#region src/assemble-input.ts
|
|
89
|
+
const IDENTIFIER_RE = /^[A-Za-z_$][\w$]*$/;
|
|
90
|
+
/**
|
|
91
|
+
* 从 profile 源码的 `plugins: [...]` 数组提取本地标识符(按声明顺序)。
|
|
92
|
+
*
|
|
93
|
+
* 2026-10-28 修复:原实现把数组开括号也算作元素内容,首项被解析成
|
|
94
|
+
* `"[\n pluginA"` 而整体跳过 —— 空 plugins(OSS profile)时只打一条
|
|
95
|
+
* warning 无人注意,非空 plugins(Pro saas profile)直接装配失败。
|
|
96
|
+
* 现从开括号后一位开始扫描,并支持行/块注释与嵌套括号。
|
|
97
|
+
*/
|
|
98
|
+
function extractPluginLocalNames(source) {
|
|
99
|
+
const start = source.search(/plugins\s*:\s*\[/);
|
|
100
|
+
if (start === -1) {
|
|
101
|
+
console.warn("未能从 profile 源码解析 plugins 数组(约定写法:plugins: [pluginA, pluginB]),按空数组处理");
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
const arrayStart = source.indexOf("[", start);
|
|
105
|
+
let depth = 0;
|
|
106
|
+
let inString = null;
|
|
107
|
+
const names = [];
|
|
108
|
+
let current = "";
|
|
109
|
+
for (let i = arrayStart + 1; i < source.length; i++) {
|
|
110
|
+
const ch = source[i];
|
|
111
|
+
if (inString) {
|
|
112
|
+
if (ch === "\\") i++;
|
|
113
|
+
else if (ch === inString) inString = null;
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (ch === "\"" || ch === "'" || ch === "`") {
|
|
117
|
+
inString = ch;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (ch === "/" && source[i + 1] === "/") {
|
|
121
|
+
while (i < source.length && source[i] !== "\n") i++;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (ch === "/" && source[i + 1] === "*") {
|
|
125
|
+
i = source.indexOf("*/", i + 2);
|
|
126
|
+
if (i === -1) break;
|
|
127
|
+
i++;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (ch === "[" || ch === "{" || ch === "(") depth++;
|
|
131
|
+
if (ch === "]" || ch === "}" || ch === ")") {
|
|
132
|
+
if (depth === 0) break;
|
|
133
|
+
depth--;
|
|
134
|
+
}
|
|
135
|
+
if (ch === "," && depth === 0) {
|
|
136
|
+
addPluginLocalName(names, current);
|
|
137
|
+
current = "";
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
current += ch;
|
|
141
|
+
}
|
|
142
|
+
addPluginLocalName(names, current);
|
|
143
|
+
return names;
|
|
144
|
+
}
|
|
145
|
+
function addPluginLocalName(names, raw) {
|
|
146
|
+
const entry = raw.trim();
|
|
147
|
+
if (!entry) return;
|
|
148
|
+
if (IDENTIFIER_RE.test(entry)) names.push(entry);
|
|
149
|
+
else console.warn(`plugins 数组元素 ${JSON.stringify(entry)} 不是标识符(不支持 spread / 变量引用 / 表达式),已跳过`);
|
|
150
|
+
}
|
|
89
151
|
function parseProfileImports(source) {
|
|
90
152
|
const map = /* @__PURE__ */ new Map();
|
|
91
153
|
const re = /import\s+([A-Za-z_$][\w$]*)\s+from\s+['"]([^'"]+)['"]/g;
|
|
@@ -99,31 +161,18 @@ function resolveImportSpecifiers(input) {
|
|
|
99
161
|
const localName = input.pluginLocalNames[index];
|
|
100
162
|
if (!localName) throw new Error(`插件 ${plugin.id} 在 profile 源码中找不到对应标识符`);
|
|
101
163
|
const specifier = input.imports.get(localName);
|
|
102
|
-
if (!specifier) throw new Error(`标识符 ${localName} 没有 default import(约定:profile
|
|
164
|
+
if (!specifier) throw new Error(`标识符 ${localName} 没有 default import(约定:profile 中插件必须以单标识符 default import 单行引入,如 \`import tags from '@scope/tags'\`;不支持 \`import X, { y }\` / \`import * as X\` / \`import { default as X }\` / 跨行 import)`);
|
|
103
165
|
byId.set(plugin.id, specifier);
|
|
104
166
|
});
|
|
105
167
|
return byId;
|
|
106
168
|
}
|
|
107
169
|
//#endregion
|
|
108
|
-
//#region src/
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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 = [
|
|
170
|
+
//#region src/pro-contract/module.ts
|
|
171
|
+
/**
|
|
172
|
+
* Pro 功能键单一来源(generate-assembly 的 implementedModules 生成亦消费此常量)。
|
|
173
|
+
* 新增 feature 只改这里;ProFeature 联合类型由本数组派生,杜绝双维护漂移。
|
|
174
|
+
*/
|
|
175
|
+
const PRO_FEATURES = [
|
|
127
176
|
"billing",
|
|
128
177
|
"hostedAi",
|
|
129
178
|
"sso",
|
|
@@ -137,11 +186,40 @@ const ALL_FEATURES = [
|
|
|
137
186
|
"globalSearch",
|
|
138
187
|
"compliance"
|
|
139
188
|
];
|
|
189
|
+
//#endregion
|
|
190
|
+
//#region src/generate-assembly.ts
|
|
191
|
+
/**
|
|
192
|
+
* 生成单引号字符串字面量:转义反斜杠与单引号,
|
|
193
|
+
* 防止 profileId / 插件 id / 模块路径 / route path 中的特殊字符污染生成代码。
|
|
194
|
+
*/
|
|
195
|
+
const q = (s) => `'${s.replace(/[\\']/g, "\\$&")}'`;
|
|
196
|
+
/**
|
|
197
|
+
* route 贡献路径白名单:以 / 开头,仅允许小写字母、数字、-、_、/。
|
|
198
|
+
* 排除 `..`、绝对路径片段、反常字符,防止生成文件被映射到 src/routes 之外。
|
|
199
|
+
*/
|
|
200
|
+
const ROUTE_PATH_RE = /^\/[a-z0-9\-_/]*$/;
|
|
201
|
+
function assertRoutePath(path) {
|
|
202
|
+
if (!ROUTE_PATH_RE.test(path)) throw new Error(`[kernel] 非法 route path: ${JSON.stringify(path)}(须以 / 开头,仅允许小写字母/数字/-/_,不得含 .. 等穿越片段)`);
|
|
203
|
+
}
|
|
204
|
+
function generateAssemblyCode(input) {
|
|
205
|
+
const { profileId, profileModulePath, resolved } = input;
|
|
206
|
+
const ids = resolved.ids.map((id) => q(id)).join(", ");
|
|
207
|
+
return [
|
|
208
|
+
`// AUTO-GENERATED by @allbluecn/kernel — DO NOT EDIT`,
|
|
209
|
+
`// profile: ${profileId} (validated: ${resolved.order.length} plugins)`,
|
|
210
|
+
`// 拓扑序(被依赖者在前):${resolved.ids.join(" -> ")}`,
|
|
211
|
+
``,
|
|
212
|
+
`export { default as profile } from ${q(profileModulePath)}`,
|
|
213
|
+
``,
|
|
214
|
+
`export const pluginIds: string[] = [${ids}]`
|
|
215
|
+
].join("\n");
|
|
216
|
+
}
|
|
217
|
+
const HEADER = "// AUTO-GENERATED by @allbluecn/kernel — DO NOT EDIT";
|
|
140
218
|
function generateAssemblyFiles(input) {
|
|
141
219
|
const specifiers = resolveImportSpecifiers(input);
|
|
142
220
|
const { resolved } = input;
|
|
143
221
|
const importLines = resolved.order.map((p, i) => {
|
|
144
|
-
return `import ${input.pluginLocalNames[i]} from
|
|
222
|
+
return `import ${input.pluginLocalNames[i]} from ${q(specifiers.get(p.id) ?? "")}`;
|
|
145
223
|
});
|
|
146
224
|
const pluginList = input.pluginLocalNames.slice(0, resolved.order.length).join(", ");
|
|
147
225
|
const middlewareParts = resolved.order.map((p, i) => {
|
|
@@ -155,25 +233,25 @@ function generateAssemblyFiles(input) {
|
|
|
155
233
|
`import { defineProfile, type ProfileDefinition } from '@allbluecn/kernel'`,
|
|
156
234
|
``,
|
|
157
235
|
`export const profile: ProfileDefinition = defineProfile({`,
|
|
158
|
-
` id:
|
|
236
|
+
` id: ${q(input.profileId)},`,
|
|
159
237
|
` plugins: [${pluginList}],`,
|
|
160
238
|
`})`,
|
|
161
239
|
``,
|
|
162
|
-
`export const pluginIds: string[] = [${resolved.ids.map((i) =>
|
|
240
|
+
`export const pluginIds: string[] = [${resolved.ids.map((i) => q(i)).join(", ")}]`,
|
|
163
241
|
``,
|
|
164
242
|
`export const pluginMiddleware: unknown[] = [`,
|
|
165
243
|
middlewareParts,
|
|
166
244
|
`]`
|
|
167
245
|
].join("\n");
|
|
168
|
-
const implLines =
|
|
246
|
+
const implLines = PRO_FEATURES.map((f) => {
|
|
169
247
|
return ` ${f}: ${resolved.order.some((p) => p.contributes.services?.[f] !== void 0)},`;
|
|
170
248
|
});
|
|
171
249
|
const modulesGen = [
|
|
172
250
|
HEADER,
|
|
173
251
|
`import type { ProFeature } from '@allbluecn/kernel'`,
|
|
174
252
|
``,
|
|
175
|
-
`export const profileId =
|
|
176
|
-
`export const activePluginIds = [${resolved.ids.map((i) =>
|
|
253
|
+
`export const profileId = ${q(input.profileId)} as const`,
|
|
254
|
+
`export const activePluginIds = [${resolved.ids.map((i) => q(i)).join(", ")}] as const`,
|
|
177
255
|
`export type ActivePluginId = (typeof activePluginIds)[number]`,
|
|
178
256
|
``,
|
|
179
257
|
`export const implementedModules: Record<ProFeature, boolean> = {`,
|
|
@@ -190,16 +268,17 @@ function generateAssemblyFiles(input) {
|
|
|
190
268
|
const spec = specifiers.get(p.id);
|
|
191
269
|
const slots = p.contributes.uiSlots;
|
|
192
270
|
if (!slots) continue;
|
|
271
|
+
const uiSpec = q(`${spec}/ui`);
|
|
193
272
|
for (const [key] of Object.entries(slots.navItems ?? {})) {
|
|
194
|
-
uiImports.push(`import { ${key} } from
|
|
273
|
+
uiImports.push(`import { ${key} } from ${uiSpec}`);
|
|
195
274
|
navItems.push(key);
|
|
196
275
|
}
|
|
197
276
|
if (slots.settingsTabs?.length) {
|
|
198
|
-
uiImports.push(`import { ${local}SettingsTabs } from
|
|
277
|
+
uiImports.push(`import { ${local}SettingsTabs } from ${uiSpec}`);
|
|
199
278
|
settingsTabs.push(`...${local}SettingsTabs`);
|
|
200
279
|
}
|
|
201
280
|
for (const [key] of Object.entries(slots.dashboardWidgets ?? {})) {
|
|
202
|
-
uiImports.push(`import { ${key} } from
|
|
281
|
+
uiImports.push(`import { ${key} } from ${uiSpec}`);
|
|
203
282
|
widgets.push(key);
|
|
204
283
|
}
|
|
205
284
|
}
|
|
@@ -218,6 +297,7 @@ function generateAssemblyFiles(input) {
|
|
|
218
297
|
const p = resolved.order[i];
|
|
219
298
|
const spec = specifiers.get(p.id);
|
|
220
299
|
p.contributes.routes?.forEach((r, j) => {
|
|
300
|
+
assertRoutePath(r.path);
|
|
221
301
|
const file = routeContributionToFile(r.path);
|
|
222
302
|
const varName = `${input.pluginLocalNames[i]}_route_${j}`;
|
|
223
303
|
const routePath = r.path.endsWith("/") ? r.path.slice(0, -1) : r.path;
|
|
@@ -225,9 +305,9 @@ function generateAssemblyFiles(input) {
|
|
|
225
305
|
HEADER,
|
|
226
306
|
`import { createFileRoute } from '@tanstack/react-router'`,
|
|
227
307
|
...r.beforeLoad || r.loader ? [`import type { PluginRouteContext } from '@allbluecn/kernel'`] : [],
|
|
228
|
-
`import { ${varName} } from
|
|
308
|
+
`import { ${varName} } from ${q(`${spec}/routes`)}`,
|
|
229
309
|
``,
|
|
230
|
-
`export const Route = createFileRoute(
|
|
310
|
+
`export const Route = createFileRoute(${q(routePath)})({`,
|
|
231
311
|
...r.beforeLoad ? [` beforeLoad: ((ctx: PluginRouteContext) => ${varName}.beforeLoad(ctx)) as any,`] : [],
|
|
232
312
|
...r.loader ? [` loader: ((ctx: PluginRouteContext) => ${varName}.loader(ctx)) as any,`] : [],
|
|
233
313
|
...r.validateSearch ? [` validateSearch: ${varName}.validateSearch as any,`] : [],
|
|
@@ -295,27 +375,43 @@ var SeatsExceededError = class SeatsExceededError extends Error {
|
|
|
295
375
|
};
|
|
296
376
|
//#endregion
|
|
297
377
|
//#region src/pro-contract/hosted-ai.ts
|
|
298
|
-
var HostedAiError = class extends Error {
|
|
378
|
+
var HostedAiError = class HostedAiError extends Error {
|
|
299
379
|
code;
|
|
300
380
|
details;
|
|
301
381
|
constructor(code, message, details) {
|
|
302
|
-
super(message);
|
|
382
|
+
super(message, details?.cause !== void 0 ? { cause: details.cause } : void 0);
|
|
303
383
|
this.code = code;
|
|
304
384
|
this.details = details;
|
|
305
385
|
this.name = "HostedAiError";
|
|
386
|
+
Object.setPrototypeOf(this, HostedAiError.prototype);
|
|
306
387
|
}
|
|
307
388
|
};
|
|
308
389
|
//#endregion
|
|
309
390
|
//#region src/server-seam.ts
|
|
310
|
-
|
|
391
|
+
/** 「模块级单例 + 注册 + 未注册抛错」泛型工厂:新增 seam 时复用,避免复制三件套。 */
|
|
392
|
+
function createSeam(notRegisteredMessage) {
|
|
393
|
+
let impl = null;
|
|
394
|
+
return {
|
|
395
|
+
register: (value) => {
|
|
396
|
+
impl = value;
|
|
397
|
+
},
|
|
398
|
+
get: () => {
|
|
399
|
+
if (!impl) throw new Error(notRegisteredMessage);
|
|
400
|
+
return impl;
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
const serverSeam = createSeam("ServerSeam 未注册:宿主需在 start.ts 中调用 registerServerSeam()");
|
|
311
405
|
function registerServerSeam(impl) {
|
|
312
|
-
|
|
406
|
+
serverSeam.register(impl);
|
|
313
407
|
}
|
|
314
408
|
function getServerSeam() {
|
|
315
|
-
|
|
316
|
-
|
|
409
|
+
return serverSeam.get();
|
|
410
|
+
}
|
|
411
|
+
const prismaSeam = createSeam("[@allbluecn/kernel] PrismaSeam not registered. Call registerPrismaSeam(prisma) before using reference engine.");
|
|
412
|
+
function registerPrismaSeam(seam) {
|
|
413
|
+
prismaSeam.register(seam);
|
|
317
414
|
}
|
|
318
|
-
function registerPrismaSeam(seam) {}
|
|
319
415
|
//#endregion
|
|
320
416
|
//#region src/contract/constants/perf.ts
|
|
321
417
|
const VIRTUALIZE_THRESHOLD = 250;
|
|
@@ -328,7 +424,13 @@ const AI_SOURCE_MODES = [
|
|
|
328
424
|
"local-agent",
|
|
329
425
|
"byok"
|
|
330
426
|
];
|
|
331
|
-
/**
|
|
427
|
+
/**
|
|
428
|
+
* 卡片角标的预设颜色(按文案匹配),admin 也可自由输入文案。
|
|
429
|
+
*
|
|
430
|
+
* ⚠ 这里的类名字符串位于 npm 包 dist 内,app 侧 Tailwind v4 不会自动扫描
|
|
431
|
+
* node_modules——消费方 globals.css 必须显式 `@source` 指向本包 dist,
|
|
432
|
+
* 否则角标样式在生产构建被 tree-shake 静默丢失(不报错)。
|
|
433
|
+
*/
|
|
332
434
|
const PROVIDER_BADGE_STYLES = {
|
|
333
435
|
推荐: "bg-primary/10 text-primary border-primary/30",
|
|
334
436
|
热门: "bg-orange-500/10 text-orange-600 dark:text-orange-400 border-orange-500/30",
|
|
@@ -400,6 +502,11 @@ const ADAPTER_KINDS = [
|
|
|
400
502
|
description: "fal.ai 聚合平台,image / video / audio / speech"
|
|
401
503
|
}
|
|
402
504
|
];
|
|
505
|
+
/**
|
|
506
|
+
* 服务商分类筛选项。有意不含 'harness'(沙箱 harness 型服务商需专用上下文,
|
|
507
|
+
* 不进常规筛选)与 'hosted'(已从 ProviderCategory 移除,托管语义归
|
|
508
|
+
* AiSourceMode);若未来放开筛选,在此补全映射。
|
|
509
|
+
*/
|
|
403
510
|
const PROVIDER_CATEGORIES = [
|
|
404
511
|
{
|
|
405
512
|
value: "recommended",
|
|
@@ -453,7 +560,7 @@ const MODEL_TABS = [
|
|
|
453
560
|
},
|
|
454
561
|
{
|
|
455
562
|
value: "tools",
|
|
456
|
-
label: "
|
|
563
|
+
label: "工具"
|
|
457
564
|
},
|
|
458
565
|
{
|
|
459
566
|
value: "vision",
|
|
@@ -489,7 +596,7 @@ const LOBE_ICON_MAP = {
|
|
|
489
596
|
};
|
|
490
597
|
//#endregion
|
|
491
598
|
//#region src/contract/modules/ai-providers/seed-data.ts
|
|
492
|
-
/**
|
|
599
|
+
/** 18 个 AI 服务商的种子数据(14 个常规 + 4 个 harness 沙箱型),admin 可在其基础上增删改 */
|
|
493
600
|
const SEED_AI_PROVIDERS = [
|
|
494
601
|
{
|
|
495
602
|
providerId: "openai",
|
|
@@ -1545,7 +1652,7 @@ const PROMPT_DEFINITIONS = [
|
|
|
1545
1652
|
1. 每行输出一个独立的 JSON 对象,行与行以换行分隔;不要输出 markdown 代码块标记,不要输出任何解释文字
|
|
1546
1653
|
2. 每行只能是以下四种操作之一:
|
|
1547
1654
|
{"op":"frame","node":{"name":"画板名","x":0,"y":0,"width":375,"height":812}}
|
|
1548
|
-
{"op":"nodes","nodes":[{...},{
|
|
1655
|
+
{"op":"nodes","nodes":[{...},{...}]}
|
|
1549
1656
|
{"op":"replace","removeIds":["节点id"],"nodes":[{...}]}
|
|
1550
1657
|
{"op":"done","message":"给用户的中文完成说明"}
|
|
1551
1658
|
3. 节点结构必须符合下方 JSON Schema;节点一律不含 parentId 字段,禁止输出 parentId(节点归属由服务端按「最近一个 frame 操作」回填)
|
|
@@ -2038,7 +2145,7 @@ const DEPENDENCY_CATEGORIES = [
|
|
|
2038
2145
|
{
|
|
2039
2146
|
value: "ui",
|
|
2040
2147
|
label: "UI 组件",
|
|
2041
|
-
description: "radix / motion / lucide / tailwind 等界面库"
|
|
2148
|
+
description: "Base UI / radix / motion / lucide / tailwind 等界面库"
|
|
2042
2149
|
},
|
|
2043
2150
|
{
|
|
2044
2151
|
value: "editor",
|
|
@@ -2048,7 +2155,7 @@ const DEPENDENCY_CATEGORIES = [
|
|
|
2048
2155
|
{
|
|
2049
2156
|
value: "ai",
|
|
2050
2157
|
label: "AI 能力",
|
|
2051
|
-
description: "@tanstack/ai
|
|
2158
|
+
description: "@tanstack/ai 及其 provider 适配器等 LLM 集成"
|
|
2052
2159
|
},
|
|
2053
2160
|
{
|
|
2054
2161
|
value: "state",
|
|
@@ -2063,7 +2170,7 @@ const DEPENDENCY_CATEGORIES = [
|
|
|
2063
2170
|
{
|
|
2064
2171
|
value: "tool",
|
|
2065
2172
|
label: "工具库",
|
|
2066
|
-
description: "zod / nanoid /
|
|
2173
|
+
description: "zod / nanoid / paraglide 等通用工具"
|
|
2067
2174
|
},
|
|
2068
2175
|
{
|
|
2069
2176
|
value: "other",
|
|
@@ -2241,18 +2348,26 @@ const DEFAULT_WATCHED_DEPENDENCIES = [
|
|
|
2241
2348
|
{
|
|
2242
2349
|
name: "openai",
|
|
2243
2350
|
displayName: "OpenAI SDK",
|
|
2244
|
-
description: "OpenAI 官方 SDK
|
|
2351
|
+
description: "OpenAI 官方 SDK(项目 LLM 链路已统一走 @tanstack/ai 适配器,本包保留关注)",
|
|
2245
2352
|
homepage: "https://openai.com",
|
|
2246
2353
|
category: "ai",
|
|
2247
|
-
priority: "
|
|
2354
|
+
priority: "low"
|
|
2355
|
+
},
|
|
2356
|
+
{
|
|
2357
|
+
name: "@base-ui/react",
|
|
2358
|
+
displayName: "Base UI",
|
|
2359
|
+
description: "无样式可访问组件库(shadcn radix-nova 底座),项目 UI 基座",
|
|
2360
|
+
homepage: "https://base-ui.com",
|
|
2361
|
+
category: "ui",
|
|
2362
|
+
priority: "high"
|
|
2248
2363
|
},
|
|
2249
2364
|
{
|
|
2250
2365
|
name: "radix-ui",
|
|
2251
2366
|
displayName: "Radix UI",
|
|
2252
|
-
description: "
|
|
2367
|
+
description: "无样式可访问组件库统一入口(遗留:TipTap ui-primitive / magicui 仍在用,逐步迁移 Base UI)",
|
|
2253
2368
|
homepage: "https://radix-ui.com",
|
|
2254
2369
|
category: "ui",
|
|
2255
|
-
priority: "
|
|
2370
|
+
priority: "normal"
|
|
2256
2371
|
},
|
|
2257
2372
|
{
|
|
2258
2373
|
name: "motion",
|
|
@@ -2294,14 +2409,6 @@ const DEFAULT_WATCHED_DEPENDENCIES = [
|
|
|
2294
2409
|
category: "ui",
|
|
2295
2410
|
priority: "normal"
|
|
2296
2411
|
},
|
|
2297
|
-
{
|
|
2298
|
-
name: "sonner",
|
|
2299
|
-
displayName: "Sonner",
|
|
2300
|
-
description: "Toast 通知组件,项目消息提示",
|
|
2301
|
-
homepage: "https://sonner.emilkowal.ski",
|
|
2302
|
-
category: "ui",
|
|
2303
|
-
priority: "normal"
|
|
2304
|
-
},
|
|
2305
2412
|
{
|
|
2306
2413
|
name: "@tanstack/react-query",
|
|
2307
2414
|
displayName: "TanStack Query",
|
|
@@ -2327,20 +2434,12 @@ const DEFAULT_WATCHED_DEPENDENCIES = [
|
|
|
2327
2434
|
priority: "high"
|
|
2328
2435
|
},
|
|
2329
2436
|
{
|
|
2330
|
-
name: "
|
|
2331
|
-
displayName: "
|
|
2332
|
-
description: "
|
|
2333
|
-
homepage: "https://
|
|
2437
|
+
name: "@inlang/paraglide-js",
|
|
2438
|
+
displayName: "Paraglide JS",
|
|
2439
|
+
description: "编译期 i18n 框架,类型安全的多语言文案(项目 i18n 方案)",
|
|
2440
|
+
homepage: "https://inlang.com/m/gerre34r/library-inlang-paraglideJs",
|
|
2334
2441
|
category: "tool",
|
|
2335
|
-
priority: "
|
|
2336
|
-
},
|
|
2337
|
-
{
|
|
2338
|
-
name: "react-i18next",
|
|
2339
|
-
displayName: "React i18next",
|
|
2340
|
-
description: "i18next 的 React 绑定",
|
|
2341
|
-
homepage: "https://react.i18next.com",
|
|
2342
|
-
category: "tool",
|
|
2343
|
-
priority: "normal"
|
|
2442
|
+
priority: "high"
|
|
2344
2443
|
},
|
|
2345
2444
|
{
|
|
2346
2445
|
name: "nanoid",
|
|
@@ -2364,25 +2463,30 @@ const DEFAULT_WATCHED_DEPENDENCIES = [
|
|
|
2364
2463
|
/**
|
|
2365
2464
|
* IP 地理位置查询工具(服务端专用)
|
|
2366
2465
|
*
|
|
2367
|
-
* -
|
|
2368
|
-
* -
|
|
2369
|
-
*
|
|
2466
|
+
* - 内网/保留段 IP:返回「Local」
|
|
2467
|
+
* - 公网 IP:使用 ipwho.is 免费 HTTPS 查询(无 Key,返回英文地名),
|
|
2468
|
+
* 失败时返回「Unknown」,不阻塞登录流程
|
|
2469
|
+
* - 弃用 ip-api.com 免费档:其免费端点不支持 HTTPS(明文外发用户 IP,
|
|
2470
|
+
* 且响应可被中间人篡改污染审计数据),2026-10 审计后切换
|
|
2370
2471
|
*/
|
|
2371
|
-
const PRIVATE_IP_PATTERNS = /^(127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])
|
|
2472
|
+
const PRIVATE_IP_PATTERNS = /^(127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.|169\.254\.|100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.|0\.|198\.1[89]\.|2(2[4-9]|3\d)\.|2[45]\d\.)/;
|
|
2473
|
+
const PRIVATE_IPV6_PATTERNS = /^(::1$|f[cd][0-9a-f]{2}:|fe80:|::ffff:|localhost)/i;
|
|
2474
|
+
function isPrivateIp(ip) {
|
|
2475
|
+
const mapped = ip.match(/^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/i);
|
|
2476
|
+
if (mapped) return PRIVATE_IP_PATTERNS.test(mapped[1]);
|
|
2477
|
+
return PRIVATE_IP_PATTERNS.test(ip) || PRIVATE_IPV6_PATTERNS.test(ip);
|
|
2478
|
+
}
|
|
2372
2479
|
async function resolveIpLocation(ip) {
|
|
2373
2480
|
if (!ip) return "Unknown";
|
|
2374
|
-
if (
|
|
2481
|
+
if (isPrivateIp(ip)) return "Local";
|
|
2375
2482
|
try {
|
|
2376
|
-
const
|
|
2377
|
-
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
2378
|
-
const res = await fetch(`http://ip-api.com/json/${encodeURIComponent(ip)}?fields=status,country,regionName,city&lang=zh-CN`, { signal: controller.signal });
|
|
2379
|
-
clearTimeout(timeout);
|
|
2483
|
+
const res = await fetch(`https://ipwho.is/${encodeURIComponent(ip)}`, { signal: AbortSignal.timeout(3e3) });
|
|
2380
2484
|
if (!res.ok) return "Unknown";
|
|
2381
2485
|
const data = await res.json();
|
|
2382
|
-
if (data.
|
|
2486
|
+
if (data.success !== true) return "Unknown";
|
|
2383
2487
|
const parts = [
|
|
2384
2488
|
data.city,
|
|
2385
|
-
data.
|
|
2489
|
+
data.region,
|
|
2386
2490
|
data.country
|
|
2387
2491
|
].filter(Boolean);
|
|
2388
2492
|
return parts.length > 0 ? parts.join(", ") : "Unknown";
|
|
@@ -2392,18 +2496,25 @@ async function resolveIpLocation(ip) {
|
|
|
2392
2496
|
}
|
|
2393
2497
|
/**
|
|
2394
2498
|
* 从 Request 中提取客户端真实 IP
|
|
2395
|
-
*
|
|
2499
|
+
*
|
|
2500
|
+
* 优先级:CF-Connecting-IP(Cloudflare 平台注入的权威头,客户端不可伪造)
|
|
2501
|
+
* > X-Real-IP > X-Forwarded-For 末段。
|
|
2502
|
+
*
|
|
2503
|
+
* ⚠ 前提声明:XFF 首值可由客户端任意伪造,因此取**末段**(最靠近本服务的
|
|
2504
|
+
* 可信代理所追加的一跳)。部署平台若自身不覆写 XFF(Vercel 会覆写),
|
|
2505
|
+
* 末段仍可能被上游代理链污染;Cloudflare 场景以 CF-Connecting-IP 为准。
|
|
2396
2506
|
*/
|
|
2397
2507
|
function getClientIp(request) {
|
|
2508
|
+
const cfip = request.headers.get("cf-connecting-ip");
|
|
2509
|
+
if (cfip) return cfip.trim();
|
|
2510
|
+
const xri = request.headers.get("x-real-ip");
|
|
2511
|
+
if (xri) return xri.trim();
|
|
2398
2512
|
const xff = request.headers.get("x-forwarded-for");
|
|
2399
2513
|
if (xff) {
|
|
2400
|
-
const
|
|
2401
|
-
|
|
2514
|
+
const segments = xff.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2515
|
+
const last = segments[segments.length - 1];
|
|
2516
|
+
if (last) return last;
|
|
2402
2517
|
}
|
|
2403
|
-
const xri = request.headers.get("x-real-ip");
|
|
2404
|
-
if (xri) return xri.trim();
|
|
2405
|
-
const cfip = request.headers.get("cf-connecting-ip");
|
|
2406
|
-
if (cfip) return cfip.trim();
|
|
2407
2518
|
return null;
|
|
2408
2519
|
}
|
|
2409
2520
|
//#endregion
|
|
@@ -2465,7 +2576,8 @@ function parseUserAgent(ua) {
|
|
|
2465
2576
|
};
|
|
2466
2577
|
}
|
|
2467
2578
|
/**
|
|
2468
|
-
* 将 ParsedUA 拼接为可读字符串,如 "Chrome 120 / Windows 10/11 /
|
|
2579
|
+
* 将 ParsedUA 拼接为可读字符串,如 "Chrome 120 / Windows 10/11 / Desktop"
|
|
2580
|
+
* (输出为英文原文;中文文案由展示层经 paraglide 映射)
|
|
2469
2581
|
*/
|
|
2470
2582
|
function formatParsedUA(parsed) {
|
|
2471
2583
|
return [
|
|
@@ -2710,14 +2822,29 @@ function normalizeEmailCategories(raw) {
|
|
|
2710
2822
|
}
|
|
2711
2823
|
return result;
|
|
2712
2824
|
}
|
|
2825
|
+
let dndMalformedWarned = false;
|
|
2826
|
+
/**
|
|
2827
|
+
* 判定 now(HH:MM)是否落在勿扰窗口内(支持跨零点)。
|
|
2828
|
+
*
|
|
2829
|
+
* 畸形时间输入(空串 / 非数字,如 DB 脏数据)旧实现产出 NaN、比较恒 false、
|
|
2830
|
+
* DND 静默失效——现显式校验,畸形时告警一次并按「不在勿扰窗口」处理。
|
|
2831
|
+
*/
|
|
2713
2832
|
function isInDndWindow(prefs, now) {
|
|
2714
2833
|
if (!prefs.dndEnabled) return false;
|
|
2715
|
-
const
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
const
|
|
2720
|
-
const
|
|
2834
|
+
const parse = (s) => {
|
|
2835
|
+
const [h, m] = s.split(":").map(Number);
|
|
2836
|
+
return Number.isFinite(h) && Number.isFinite(m) ? h * 60 + m : NaN;
|
|
2837
|
+
};
|
|
2838
|
+
const start = parse(prefs.dndStart);
|
|
2839
|
+
const end = parse(prefs.dndEnd);
|
|
2840
|
+
const cur = parse(now);
|
|
2841
|
+
if (Number.isNaN(start) || Number.isNaN(end) || Number.isNaN(cur)) {
|
|
2842
|
+
if (!dndMalformedWarned) {
|
|
2843
|
+
dndMalformedWarned = true;
|
|
2844
|
+
console.warn(`[notifications] 勿扰时段格式非法(须为 HH:MM):start=${JSON.stringify(prefs.dndStart)} end=${JSON.stringify(prefs.dndEnd)},DND 判定按不生效处理`);
|
|
2845
|
+
}
|
|
2846
|
+
return false;
|
|
2847
|
+
}
|
|
2721
2848
|
if (start <= end) return cur >= start && cur < end;
|
|
2722
2849
|
return cur >= start || cur < end;
|
|
2723
2850
|
}
|
|
@@ -2725,8 +2852,18 @@ function isInDndWindow(prefs, now) {
|
|
|
2725
2852
|
//#region src/contract/modules/registry.ts
|
|
2726
2853
|
var ModuleRegistry = class {
|
|
2727
2854
|
modules = /* @__PURE__ */ new Map();
|
|
2728
|
-
|
|
2729
|
-
|
|
2855
|
+
/**
|
|
2856
|
+
* 注册模块。同 id 重复注册的语义:
|
|
2857
|
+
* - 显式 `{ overwrite: true }`:警告并覆盖(有意的替换场景);
|
|
2858
|
+
* - 生产环境:视为装配期配置错误直接抛出,避免「两个模块意外同 id」被静默掩盖;
|
|
2859
|
+
* - dev/test/HMR:警告并覆盖(模块双执行 / 热更新场景保持宽松)。
|
|
2860
|
+
*/
|
|
2861
|
+
register(mod, options) {
|
|
2862
|
+
if (this.modules.has(mod.id)) {
|
|
2863
|
+
const isProd = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
|
|
2864
|
+
if (!options?.overwrite && isProd) throw new Error(`[ModuleRegistry] "${mod.id}" already registered(同 id 模块重复注册;若确需覆盖请传 { overwrite: true })`);
|
|
2865
|
+
console.warn(`[ModuleRegistry] "${mod.id}" already registered, overwriting`);
|
|
2866
|
+
}
|
|
2730
2867
|
this.modules.set(mod.id, mod);
|
|
2731
2868
|
}
|
|
2732
2869
|
getModule(id) {
|
|
@@ -2790,4 +2927,4 @@ const ICON_MAP = {
|
|
|
2790
2927
|
music4: Music4
|
|
2791
2928
|
};
|
|
2792
2929
|
//#endregion
|
|
2793
|
-
export { dumpModules as $, promptDefinitionKeys as A, PROVIDER_CATEGORY_VALUES as B, DEFAULT_WATCHED_DEPENDENCIES as C, TIPTAP_ACTION_INSTRUCTIONS as D, PROMPT_DEFINITIONS as E, ADAPTER_KIND_VALUES as F, VIRTUALIZE_THRESHOLD as G, PROVIDER_BADGE_STYLES as H, LOBE_ICON_MAP as I, registerServerSeam as J, getServerSeam as K, MODEL_CAPABILITY_OPTIONS as L, interpolateTemplate as M, SEED_AI_PROVIDERS as N, getPromptDefinition as O, ADAPTER_KINDS as P, WebhookSignatureError as Q, MODEL_TABS as R, resolveIpLocation as S, DEPENDENCY_PRIORITIES as T, DND_STICKY_THRESHOLD as U, AI_SOURCE_MODES as V, LOAD_MORE_STEP as W, BillingUnavailableError as X, HostedAiError as Y, SeatsExceededError as Z, OSS_BASE_FEATURES as _, DEFAULT_EMAIL_CATEGORIES as a,
|
|
2930
|
+
export { dumpModules as $, promptDefinitionKeys as A, PROVIDER_CATEGORY_VALUES as B, DEFAULT_WATCHED_DEPENDENCIES as C, TIPTAP_ACTION_INSTRUCTIONS as D, PROMPT_DEFINITIONS as E, ADAPTER_KIND_VALUES as F, VIRTUALIZE_THRESHOLD as G, PROVIDER_BADGE_STYLES as H, LOBE_ICON_MAP as I, registerServerSeam as J, getServerSeam as K, MODEL_CAPABILITY_OPTIONS as L, interpolateTemplate as M, SEED_AI_PROVIDERS as N, getPromptDefinition as O, ADAPTER_KINDS as P, WebhookSignatureError as Q, MODEL_TABS as R, resolveIpLocation as S, DEPENDENCY_PRIORITIES as T, DND_STICKY_THRESHOLD as U, AI_SOURCE_MODES as V, LOAD_MORE_STEP as W, BillingUnavailableError as X, HostedAiError as Y, SeatsExceededError as Z, OSS_BASE_FEATURES as _, DEFAULT_EMAIL_CATEGORIES as a, resolveImportSpecifiers as at, parseUserAgent as b, normalizeEmailCategories as c, definePlugin as ct, RESOURCE_KEYS as d, generateAssemblyCode as et, ResourceRegistry as f, FeatureRegistry as g, FEATURE_KEYS as h, DEFAULT_CATEGORIES as i, parseProfileImports as it, UnknownPromptVariableError as j, listPromptDefinitions as k, NOTIFICATION_CATEGORIES as l, PlanFeaturesSchema as m, DEVICE_SIZES as n, PRO_FEATURES as nt, isInDndWindow as o, resolvePluginOrder as ot, isResourceDefined as p, registerPrismaSeam as q, moduleRegistry as r, extractPluginLocalNames as rt, normalizeCategoryFrequencies as s, defineProfile as st, ICON_MAP as t, generateAssemblyFiles as tt, NOTIFICATION_FREQUENCIES as u, isOssFeature as v, DEPENDENCY_CATEGORIES as w, getClientIp as x, formatParsedUA as y, PROVIDER_CATEGORIES as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allbluecn/kernel",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"license": "AGPL-3.0-or-later",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "AllBlue 插件协议:类型定义 + 构建期装配器(零运行时)",
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
"react": "^19",
|
|
57
57
|
"tsdown": "^0.22.0",
|
|
58
58
|
"typescript": "^7",
|
|
59
|
-
"vitest": "
|
|
60
|
-
"zod": "^4.
|
|
59
|
+
"vitest": "^5.0.5",
|
|
60
|
+
"zod": "^4.6.7"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {},
|
|
63
63
|
"publishConfig": {
|