@allbluecn/kernel 0.10.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +69 -5
- 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-Bo9e8qon.js} +177 -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,
|
|
2
|
+
import { $ as dumpModules, at as resolvePluginOrder, et as generateAssemblyCode, rt as parseProfileImports, tt as generateAssemblyFiles } from "./src-Bo9e8qon.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,10 +86,68 @@ async function main() {
|
|
|
86
86
|
return;
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
+
const IDENTIFIER_RE = /^[A-Za-z_$][\w$]*$/;
|
|
90
|
+
/**
|
|
91
|
+
* 从 profile 源码中提取 plugins 数组元素的 default import 标识符。
|
|
92
|
+
*
|
|
93
|
+
* 用括号配平扫描替代正则(旧正则 `[^\]]*` 无法处理元素内嵌套数组/对象、
|
|
94
|
+
* 注释、含 `]` 的字面量),支持任意嵌套与跨行写法;字符串与注释内容跳过。
|
|
95
|
+
* 非纯标识符元素(spread、变量引用、成员访问)无法对应 default import,
|
|
96
|
+
* 收集时跳过并告警——错误在装配早期显式暴露,而非延后到
|
|
97
|
+
* resolveImportSpecifiers 以「找不到标识符」的失真信息报错。
|
|
98
|
+
*/
|
|
89
99
|
function extractPluginLocalNames(source) {
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
92
|
-
|
|
100
|
+
const start = source.search(/plugins\s*:\s*\[/);
|
|
101
|
+
if (start === -1) {
|
|
102
|
+
console.warn("未能从 profile 源码解析 plugins 数组(约定写法:plugins: [pluginA, pluginB]),按空数组处理");
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
const arrayStart = source.indexOf("[", start);
|
|
106
|
+
let depth = 0;
|
|
107
|
+
let inString = null;
|
|
108
|
+
const names = [];
|
|
109
|
+
let current = "";
|
|
110
|
+
for (let i = arrayStart; i < source.length; i++) {
|
|
111
|
+
const ch = source[i];
|
|
112
|
+
if (inString) {
|
|
113
|
+
if (ch === "\\") i++;
|
|
114
|
+
else if (ch === inString) inString = null;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (ch === "\"" || ch === "'" || ch === "`") {
|
|
118
|
+
inString = ch;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (ch === "/" && source[i + 1] === "/") {
|
|
122
|
+
while (i < source.length && source[i] !== "\n") i++;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (ch === "/" && source[i + 1] === "*") {
|
|
126
|
+
i = source.indexOf("*/", i + 2);
|
|
127
|
+
if (i === -1) break;
|
|
128
|
+
i++;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (ch === "[" || ch === "{" || ch === "(") depth++;
|
|
132
|
+
if (ch === "]" || ch === "}" || ch === ")") {
|
|
133
|
+
depth--;
|
|
134
|
+
if (depth === 0) break;
|
|
135
|
+
}
|
|
136
|
+
if (ch === "," && depth === 1) {
|
|
137
|
+
addName(names, current);
|
|
138
|
+
current = "";
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
current += depth >= 1 ? ch : "";
|
|
142
|
+
}
|
|
143
|
+
addName(names, current);
|
|
144
|
+
return names;
|
|
145
|
+
}
|
|
146
|
+
function addName(names, raw) {
|
|
147
|
+
const entry = raw.trim();
|
|
148
|
+
if (!entry) return;
|
|
149
|
+
if (IDENTIFIER_RE.test(entry)) names.push(entry);
|
|
150
|
+
else console.warn(`plugins 数组元素 ${JSON.stringify(entry)} 不是标识符(不支持 spread / 变量引用 / 表达式),已跳过`);
|
|
93
151
|
}
|
|
94
152
|
async function writeAssemblyFiles(appDir, assembly) {
|
|
95
153
|
const { writeFile, readFile, unlink, mkdir } = await import("node:fs/promises");
|
|
@@ -102,7 +160,13 @@ async function writeAssemblyFiles(appDir, assembly) {
|
|
|
102
160
|
await writeFile(join(pluginsDir, "ui.gen.ts"), assembly.uiGen + "\n");
|
|
103
161
|
const manifestPath = join(pluginsDir, ".gen-manifest.json");
|
|
104
162
|
const oldManifest = await readFile(manifestPath, "utf8").catch(() => null);
|
|
105
|
-
|
|
163
|
+
let oldFiles = [];
|
|
164
|
+
if (oldManifest) try {
|
|
165
|
+
const parsed = JSON.parse(oldManifest);
|
|
166
|
+
oldFiles = Array.isArray(parsed.routeFiles) ? parsed.routeFiles.filter((f) => typeof f === "string") : [];
|
|
167
|
+
} catch {
|
|
168
|
+
console.warn(`旧 gen-manifest 损坏(${manifestPath}),已忽略并重新生成`);
|
|
169
|
+
}
|
|
106
170
|
const newFiles = [...assembly.routeFiles.keys()];
|
|
107
171
|
for (const f of oldFiles) if (!newFiles.includes(f)) await unlink(join(appDir, f)).catch(() => {});
|
|
108
172
|
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 resolvePluginOrder, b as parseUserAgent, c as normalizeEmailCategories, d as RESOURCE_KEYS, et as generateAssemblyCode, f as ResourceRegistry, g as FeatureRegistry, h as FEATURE_KEYS, i as DEFAULT_CATEGORIES, it as resolveImportSpecifiers, 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 defineProfile, p as isResourceDefined, q as registerPrismaSeam, r as moduleRegistry, rt as parseProfileImports, s as normalizeCategoryFrequencies, st as definePlugin, 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-Bo9e8qon.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,7 +82,7 @@ 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
|
|
@@ -99,31 +99,18 @@ function resolveImportSpecifiers(input) {
|
|
|
99
99
|
const localName = input.pluginLocalNames[index];
|
|
100
100
|
if (!localName) throw new Error(`插件 ${plugin.id} 在 profile 源码中找不到对应标识符`);
|
|
101
101
|
const specifier = input.imports.get(localName);
|
|
102
|
-
if (!specifier) throw new Error(`标识符 ${localName} 没有 default import(约定:profile
|
|
102
|
+
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
103
|
byId.set(plugin.id, specifier);
|
|
104
104
|
});
|
|
105
105
|
return byId;
|
|
106
106
|
}
|
|
107
107
|
//#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 = [
|
|
108
|
+
//#region src/pro-contract/module.ts
|
|
109
|
+
/**
|
|
110
|
+
* Pro 功能键单一来源(generate-assembly 的 implementedModules 生成亦消费此常量)。
|
|
111
|
+
* 新增 feature 只改这里;ProFeature 联合类型由本数组派生,杜绝双维护漂移。
|
|
112
|
+
*/
|
|
113
|
+
const PRO_FEATURES = [
|
|
127
114
|
"billing",
|
|
128
115
|
"hostedAi",
|
|
129
116
|
"sso",
|
|
@@ -137,11 +124,40 @@ const ALL_FEATURES = [
|
|
|
137
124
|
"globalSearch",
|
|
138
125
|
"compliance"
|
|
139
126
|
];
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/generate-assembly.ts
|
|
129
|
+
/**
|
|
130
|
+
* 生成单引号字符串字面量:转义反斜杠与单引号,
|
|
131
|
+
* 防止 profileId / 插件 id / 模块路径 / route path 中的特殊字符污染生成代码。
|
|
132
|
+
*/
|
|
133
|
+
const q = (s) => `'${s.replace(/[\\']/g, "\\$&")}'`;
|
|
134
|
+
/**
|
|
135
|
+
* route 贡献路径白名单:以 / 开头,仅允许小写字母、数字、-、_、/。
|
|
136
|
+
* 排除 `..`、绝对路径片段、反常字符,防止生成文件被映射到 src/routes 之外。
|
|
137
|
+
*/
|
|
138
|
+
const ROUTE_PATH_RE = /^\/[a-z0-9\-_/]*$/;
|
|
139
|
+
function assertRoutePath(path) {
|
|
140
|
+
if (!ROUTE_PATH_RE.test(path)) throw new Error(`[kernel] 非法 route path: ${JSON.stringify(path)}(须以 / 开头,仅允许小写字母/数字/-/_,不得含 .. 等穿越片段)`);
|
|
141
|
+
}
|
|
142
|
+
function generateAssemblyCode(input) {
|
|
143
|
+
const { profileId, profileModulePath, resolved } = input;
|
|
144
|
+
const ids = resolved.ids.map((id) => q(id)).join(", ");
|
|
145
|
+
return [
|
|
146
|
+
`// AUTO-GENERATED by @allbluecn/kernel — DO NOT EDIT`,
|
|
147
|
+
`// profile: ${profileId} (validated: ${resolved.order.length} plugins)`,
|
|
148
|
+
`// 拓扑序(被依赖者在前):${resolved.ids.join(" -> ")}`,
|
|
149
|
+
``,
|
|
150
|
+
`export { default as profile } from ${q(profileModulePath)}`,
|
|
151
|
+
``,
|
|
152
|
+
`export const pluginIds: string[] = [${ids}]`
|
|
153
|
+
].join("\n");
|
|
154
|
+
}
|
|
155
|
+
const HEADER = "// AUTO-GENERATED by @allbluecn/kernel — DO NOT EDIT";
|
|
140
156
|
function generateAssemblyFiles(input) {
|
|
141
157
|
const specifiers = resolveImportSpecifiers(input);
|
|
142
158
|
const { resolved } = input;
|
|
143
159
|
const importLines = resolved.order.map((p, i) => {
|
|
144
|
-
return `import ${input.pluginLocalNames[i]} from
|
|
160
|
+
return `import ${input.pluginLocalNames[i]} from ${q(specifiers.get(p.id) ?? "")}`;
|
|
145
161
|
});
|
|
146
162
|
const pluginList = input.pluginLocalNames.slice(0, resolved.order.length).join(", ");
|
|
147
163
|
const middlewareParts = resolved.order.map((p, i) => {
|
|
@@ -155,25 +171,25 @@ function generateAssemblyFiles(input) {
|
|
|
155
171
|
`import { defineProfile, type ProfileDefinition } from '@allbluecn/kernel'`,
|
|
156
172
|
``,
|
|
157
173
|
`export const profile: ProfileDefinition = defineProfile({`,
|
|
158
|
-
` id:
|
|
174
|
+
` id: ${q(input.profileId)},`,
|
|
159
175
|
` plugins: [${pluginList}],`,
|
|
160
176
|
`})`,
|
|
161
177
|
``,
|
|
162
|
-
`export const pluginIds: string[] = [${resolved.ids.map((i) =>
|
|
178
|
+
`export const pluginIds: string[] = [${resolved.ids.map((i) => q(i)).join(", ")}]`,
|
|
163
179
|
``,
|
|
164
180
|
`export const pluginMiddleware: unknown[] = [`,
|
|
165
181
|
middlewareParts,
|
|
166
182
|
`]`
|
|
167
183
|
].join("\n");
|
|
168
|
-
const implLines =
|
|
184
|
+
const implLines = PRO_FEATURES.map((f) => {
|
|
169
185
|
return ` ${f}: ${resolved.order.some((p) => p.contributes.services?.[f] !== void 0)},`;
|
|
170
186
|
});
|
|
171
187
|
const modulesGen = [
|
|
172
188
|
HEADER,
|
|
173
189
|
`import type { ProFeature } from '@allbluecn/kernel'`,
|
|
174
190
|
``,
|
|
175
|
-
`export const profileId =
|
|
176
|
-
`export const activePluginIds = [${resolved.ids.map((i) =>
|
|
191
|
+
`export const profileId = ${q(input.profileId)} as const`,
|
|
192
|
+
`export const activePluginIds = [${resolved.ids.map((i) => q(i)).join(", ")}] as const`,
|
|
177
193
|
`export type ActivePluginId = (typeof activePluginIds)[number]`,
|
|
178
194
|
``,
|
|
179
195
|
`export const implementedModules: Record<ProFeature, boolean> = {`,
|
|
@@ -190,16 +206,17 @@ function generateAssemblyFiles(input) {
|
|
|
190
206
|
const spec = specifiers.get(p.id);
|
|
191
207
|
const slots = p.contributes.uiSlots;
|
|
192
208
|
if (!slots) continue;
|
|
209
|
+
const uiSpec = q(`${spec}/ui`);
|
|
193
210
|
for (const [key] of Object.entries(slots.navItems ?? {})) {
|
|
194
|
-
uiImports.push(`import { ${key} } from
|
|
211
|
+
uiImports.push(`import { ${key} } from ${uiSpec}`);
|
|
195
212
|
navItems.push(key);
|
|
196
213
|
}
|
|
197
214
|
if (slots.settingsTabs?.length) {
|
|
198
|
-
uiImports.push(`import { ${local}SettingsTabs } from
|
|
215
|
+
uiImports.push(`import { ${local}SettingsTabs } from ${uiSpec}`);
|
|
199
216
|
settingsTabs.push(`...${local}SettingsTabs`);
|
|
200
217
|
}
|
|
201
218
|
for (const [key] of Object.entries(slots.dashboardWidgets ?? {})) {
|
|
202
|
-
uiImports.push(`import { ${key} } from
|
|
219
|
+
uiImports.push(`import { ${key} } from ${uiSpec}`);
|
|
203
220
|
widgets.push(key);
|
|
204
221
|
}
|
|
205
222
|
}
|
|
@@ -218,6 +235,7 @@ function generateAssemblyFiles(input) {
|
|
|
218
235
|
const p = resolved.order[i];
|
|
219
236
|
const spec = specifiers.get(p.id);
|
|
220
237
|
p.contributes.routes?.forEach((r, j) => {
|
|
238
|
+
assertRoutePath(r.path);
|
|
221
239
|
const file = routeContributionToFile(r.path);
|
|
222
240
|
const varName = `${input.pluginLocalNames[i]}_route_${j}`;
|
|
223
241
|
const routePath = r.path.endsWith("/") ? r.path.slice(0, -1) : r.path;
|
|
@@ -225,9 +243,9 @@ function generateAssemblyFiles(input) {
|
|
|
225
243
|
HEADER,
|
|
226
244
|
`import { createFileRoute } from '@tanstack/react-router'`,
|
|
227
245
|
...r.beforeLoad || r.loader ? [`import type { PluginRouteContext } from '@allbluecn/kernel'`] : [],
|
|
228
|
-
`import { ${varName} } from
|
|
246
|
+
`import { ${varName} } from ${q(`${spec}/routes`)}`,
|
|
229
247
|
``,
|
|
230
|
-
`export const Route = createFileRoute(
|
|
248
|
+
`export const Route = createFileRoute(${q(routePath)})({`,
|
|
231
249
|
...r.beforeLoad ? [` beforeLoad: ((ctx: PluginRouteContext) => ${varName}.beforeLoad(ctx)) as any,`] : [],
|
|
232
250
|
...r.loader ? [` loader: ((ctx: PluginRouteContext) => ${varName}.loader(ctx)) as any,`] : [],
|
|
233
251
|
...r.validateSearch ? [` validateSearch: ${varName}.validateSearch as any,`] : [],
|
|
@@ -295,27 +313,43 @@ var SeatsExceededError = class SeatsExceededError extends Error {
|
|
|
295
313
|
};
|
|
296
314
|
//#endregion
|
|
297
315
|
//#region src/pro-contract/hosted-ai.ts
|
|
298
|
-
var HostedAiError = class extends Error {
|
|
316
|
+
var HostedAiError = class HostedAiError extends Error {
|
|
299
317
|
code;
|
|
300
318
|
details;
|
|
301
319
|
constructor(code, message, details) {
|
|
302
|
-
super(message);
|
|
320
|
+
super(message, details?.cause !== void 0 ? { cause: details.cause } : void 0);
|
|
303
321
|
this.code = code;
|
|
304
322
|
this.details = details;
|
|
305
323
|
this.name = "HostedAiError";
|
|
324
|
+
Object.setPrototypeOf(this, HostedAiError.prototype);
|
|
306
325
|
}
|
|
307
326
|
};
|
|
308
327
|
//#endregion
|
|
309
328
|
//#region src/server-seam.ts
|
|
310
|
-
|
|
329
|
+
/** 「模块级单例 + 注册 + 未注册抛错」泛型工厂:新增 seam 时复用,避免复制三件套。 */
|
|
330
|
+
function createSeam(notRegisteredMessage) {
|
|
331
|
+
let impl = null;
|
|
332
|
+
return {
|
|
333
|
+
register: (value) => {
|
|
334
|
+
impl = value;
|
|
335
|
+
},
|
|
336
|
+
get: () => {
|
|
337
|
+
if (!impl) throw new Error(notRegisteredMessage);
|
|
338
|
+
return impl;
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
const serverSeam = createSeam("ServerSeam 未注册:宿主需在 start.ts 中调用 registerServerSeam()");
|
|
311
343
|
function registerServerSeam(impl) {
|
|
312
|
-
|
|
344
|
+
serverSeam.register(impl);
|
|
313
345
|
}
|
|
314
346
|
function getServerSeam() {
|
|
315
|
-
|
|
316
|
-
|
|
347
|
+
return serverSeam.get();
|
|
348
|
+
}
|
|
349
|
+
const prismaSeam = createSeam("[@allbluecn/kernel] PrismaSeam not registered. Call registerPrismaSeam(prisma) before using reference engine.");
|
|
350
|
+
function registerPrismaSeam(seam) {
|
|
351
|
+
prismaSeam.register(seam);
|
|
317
352
|
}
|
|
318
|
-
function registerPrismaSeam(seam) {}
|
|
319
353
|
//#endregion
|
|
320
354
|
//#region src/contract/constants/perf.ts
|
|
321
355
|
const VIRTUALIZE_THRESHOLD = 250;
|
|
@@ -328,7 +362,13 @@ const AI_SOURCE_MODES = [
|
|
|
328
362
|
"local-agent",
|
|
329
363
|
"byok"
|
|
330
364
|
];
|
|
331
|
-
/**
|
|
365
|
+
/**
|
|
366
|
+
* 卡片角标的预设颜色(按文案匹配),admin 也可自由输入文案。
|
|
367
|
+
*
|
|
368
|
+
* ⚠ 这里的类名字符串位于 npm 包 dist 内,app 侧 Tailwind v4 不会自动扫描
|
|
369
|
+
* node_modules——消费方 globals.css 必须显式 `@source` 指向本包 dist,
|
|
370
|
+
* 否则角标样式在生产构建被 tree-shake 静默丢失(不报错)。
|
|
371
|
+
*/
|
|
332
372
|
const PROVIDER_BADGE_STYLES = {
|
|
333
373
|
推荐: "bg-primary/10 text-primary border-primary/30",
|
|
334
374
|
热门: "bg-orange-500/10 text-orange-600 dark:text-orange-400 border-orange-500/30",
|
|
@@ -400,6 +440,11 @@ const ADAPTER_KINDS = [
|
|
|
400
440
|
description: "fal.ai 聚合平台,image / video / audio / speech"
|
|
401
441
|
}
|
|
402
442
|
];
|
|
443
|
+
/**
|
|
444
|
+
* 服务商分类筛选项。有意不含 'harness'(沙箱 harness 型服务商需专用上下文,
|
|
445
|
+
* 不进常规筛选)与 'hosted'(已从 ProviderCategory 移除,托管语义归
|
|
446
|
+
* AiSourceMode);若未来放开筛选,在此补全映射。
|
|
447
|
+
*/
|
|
403
448
|
const PROVIDER_CATEGORIES = [
|
|
404
449
|
{
|
|
405
450
|
value: "recommended",
|
|
@@ -453,7 +498,7 @@ const MODEL_TABS = [
|
|
|
453
498
|
},
|
|
454
499
|
{
|
|
455
500
|
value: "tools",
|
|
456
|
-
label: "
|
|
501
|
+
label: "工具"
|
|
457
502
|
},
|
|
458
503
|
{
|
|
459
504
|
value: "vision",
|
|
@@ -489,7 +534,7 @@ const LOBE_ICON_MAP = {
|
|
|
489
534
|
};
|
|
490
535
|
//#endregion
|
|
491
536
|
//#region src/contract/modules/ai-providers/seed-data.ts
|
|
492
|
-
/**
|
|
537
|
+
/** 18 个 AI 服务商的种子数据(14 个常规 + 4 个 harness 沙箱型),admin 可在其基础上增删改 */
|
|
493
538
|
const SEED_AI_PROVIDERS = [
|
|
494
539
|
{
|
|
495
540
|
providerId: "openai",
|
|
@@ -1545,7 +1590,7 @@ const PROMPT_DEFINITIONS = [
|
|
|
1545
1590
|
1. 每行输出一个独立的 JSON 对象,行与行以换行分隔;不要输出 markdown 代码块标记,不要输出任何解释文字
|
|
1546
1591
|
2. 每行只能是以下四种操作之一:
|
|
1547
1592
|
{"op":"frame","node":{"name":"画板名","x":0,"y":0,"width":375,"height":812}}
|
|
1548
|
-
{"op":"nodes","nodes":[{...},{
|
|
1593
|
+
{"op":"nodes","nodes":[{...},{...}]}
|
|
1549
1594
|
{"op":"replace","removeIds":["节点id"],"nodes":[{...}]}
|
|
1550
1595
|
{"op":"done","message":"给用户的中文完成说明"}
|
|
1551
1596
|
3. 节点结构必须符合下方 JSON Schema;节点一律不含 parentId 字段,禁止输出 parentId(节点归属由服务端按「最近一个 frame 操作」回填)
|
|
@@ -2038,7 +2083,7 @@ const DEPENDENCY_CATEGORIES = [
|
|
|
2038
2083
|
{
|
|
2039
2084
|
value: "ui",
|
|
2040
2085
|
label: "UI 组件",
|
|
2041
|
-
description: "radix / motion / lucide / tailwind 等界面库"
|
|
2086
|
+
description: "Base UI / radix / motion / lucide / tailwind 等界面库"
|
|
2042
2087
|
},
|
|
2043
2088
|
{
|
|
2044
2089
|
value: "editor",
|
|
@@ -2048,7 +2093,7 @@ const DEPENDENCY_CATEGORIES = [
|
|
|
2048
2093
|
{
|
|
2049
2094
|
value: "ai",
|
|
2050
2095
|
label: "AI 能力",
|
|
2051
|
-
description: "@tanstack/ai
|
|
2096
|
+
description: "@tanstack/ai 及其 provider 适配器等 LLM 集成"
|
|
2052
2097
|
},
|
|
2053
2098
|
{
|
|
2054
2099
|
value: "state",
|
|
@@ -2063,7 +2108,7 @@ const DEPENDENCY_CATEGORIES = [
|
|
|
2063
2108
|
{
|
|
2064
2109
|
value: "tool",
|
|
2065
2110
|
label: "工具库",
|
|
2066
|
-
description: "zod / nanoid /
|
|
2111
|
+
description: "zod / nanoid / paraglide 等通用工具"
|
|
2067
2112
|
},
|
|
2068
2113
|
{
|
|
2069
2114
|
value: "other",
|
|
@@ -2241,18 +2286,26 @@ const DEFAULT_WATCHED_DEPENDENCIES = [
|
|
|
2241
2286
|
{
|
|
2242
2287
|
name: "openai",
|
|
2243
2288
|
displayName: "OpenAI SDK",
|
|
2244
|
-
description: "OpenAI 官方 SDK
|
|
2289
|
+
description: "OpenAI 官方 SDK(项目 LLM 链路已统一走 @tanstack/ai 适配器,本包保留关注)",
|
|
2245
2290
|
homepage: "https://openai.com",
|
|
2246
2291
|
category: "ai",
|
|
2247
|
-
priority: "
|
|
2292
|
+
priority: "low"
|
|
2293
|
+
},
|
|
2294
|
+
{
|
|
2295
|
+
name: "@base-ui/react",
|
|
2296
|
+
displayName: "Base UI",
|
|
2297
|
+
description: "无样式可访问组件库(shadcn radix-nova 底座),项目 UI 基座",
|
|
2298
|
+
homepage: "https://base-ui.com",
|
|
2299
|
+
category: "ui",
|
|
2300
|
+
priority: "high"
|
|
2248
2301
|
},
|
|
2249
2302
|
{
|
|
2250
2303
|
name: "radix-ui",
|
|
2251
2304
|
displayName: "Radix UI",
|
|
2252
|
-
description: "
|
|
2305
|
+
description: "无样式可访问组件库统一入口(遗留:TipTap ui-primitive / magicui 仍在用,逐步迁移 Base UI)",
|
|
2253
2306
|
homepage: "https://radix-ui.com",
|
|
2254
2307
|
category: "ui",
|
|
2255
|
-
priority: "
|
|
2308
|
+
priority: "normal"
|
|
2256
2309
|
},
|
|
2257
2310
|
{
|
|
2258
2311
|
name: "motion",
|
|
@@ -2294,14 +2347,6 @@ const DEFAULT_WATCHED_DEPENDENCIES = [
|
|
|
2294
2347
|
category: "ui",
|
|
2295
2348
|
priority: "normal"
|
|
2296
2349
|
},
|
|
2297
|
-
{
|
|
2298
|
-
name: "sonner",
|
|
2299
|
-
displayName: "Sonner",
|
|
2300
|
-
description: "Toast 通知组件,项目消息提示",
|
|
2301
|
-
homepage: "https://sonner.emilkowal.ski",
|
|
2302
|
-
category: "ui",
|
|
2303
|
-
priority: "normal"
|
|
2304
|
-
},
|
|
2305
2350
|
{
|
|
2306
2351
|
name: "@tanstack/react-query",
|
|
2307
2352
|
displayName: "TanStack Query",
|
|
@@ -2327,20 +2372,12 @@ const DEFAULT_WATCHED_DEPENDENCIES = [
|
|
|
2327
2372
|
priority: "high"
|
|
2328
2373
|
},
|
|
2329
2374
|
{
|
|
2330
|
-
name: "
|
|
2331
|
-
displayName: "
|
|
2332
|
-
description: "
|
|
2333
|
-
homepage: "https://
|
|
2334
|
-
category: "tool",
|
|
2335
|
-
priority: "normal"
|
|
2336
|
-
},
|
|
2337
|
-
{
|
|
2338
|
-
name: "react-i18next",
|
|
2339
|
-
displayName: "React i18next",
|
|
2340
|
-
description: "i18next 的 React 绑定",
|
|
2341
|
-
homepage: "https://react.i18next.com",
|
|
2375
|
+
name: "@inlang/paraglide-js",
|
|
2376
|
+
displayName: "Paraglide JS",
|
|
2377
|
+
description: "编译期 i18n 框架,类型安全的多语言文案(项目 i18n 方案)",
|
|
2378
|
+
homepage: "https://inlang.com/m/gerre34r/library-inlang-paraglideJs",
|
|
2342
2379
|
category: "tool",
|
|
2343
|
-
priority: "
|
|
2380
|
+
priority: "high"
|
|
2344
2381
|
},
|
|
2345
2382
|
{
|
|
2346
2383
|
name: "nanoid",
|
|
@@ -2364,25 +2401,30 @@ const DEFAULT_WATCHED_DEPENDENCIES = [
|
|
|
2364
2401
|
/**
|
|
2365
2402
|
* IP 地理位置查询工具(服务端专用)
|
|
2366
2403
|
*
|
|
2367
|
-
* -
|
|
2368
|
-
* -
|
|
2369
|
-
*
|
|
2404
|
+
* - 内网/保留段 IP:返回「Local」
|
|
2405
|
+
* - 公网 IP:使用 ipwho.is 免费 HTTPS 查询(无 Key,返回英文地名),
|
|
2406
|
+
* 失败时返回「Unknown」,不阻塞登录流程
|
|
2407
|
+
* - 弃用 ip-api.com 免费档:其免费端点不支持 HTTPS(明文外发用户 IP,
|
|
2408
|
+
* 且响应可被中间人篡改污染审计数据),2026-10 审计后切换
|
|
2370
2409
|
*/
|
|
2371
|
-
const PRIVATE_IP_PATTERNS = /^(127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[01])
|
|
2410
|
+
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\.)/;
|
|
2411
|
+
const PRIVATE_IPV6_PATTERNS = /^(::1$|f[cd][0-9a-f]{2}:|fe80:|::ffff:|localhost)/i;
|
|
2412
|
+
function isPrivateIp(ip) {
|
|
2413
|
+
const mapped = ip.match(/^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/i);
|
|
2414
|
+
if (mapped) return PRIVATE_IP_PATTERNS.test(mapped[1]);
|
|
2415
|
+
return PRIVATE_IP_PATTERNS.test(ip) || PRIVATE_IPV6_PATTERNS.test(ip);
|
|
2416
|
+
}
|
|
2372
2417
|
async function resolveIpLocation(ip) {
|
|
2373
2418
|
if (!ip) return "Unknown";
|
|
2374
|
-
if (
|
|
2419
|
+
if (isPrivateIp(ip)) return "Local";
|
|
2375
2420
|
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);
|
|
2421
|
+
const res = await fetch(`https://ipwho.is/${encodeURIComponent(ip)}`, { signal: AbortSignal.timeout(3e3) });
|
|
2380
2422
|
if (!res.ok) return "Unknown";
|
|
2381
2423
|
const data = await res.json();
|
|
2382
|
-
if (data.
|
|
2424
|
+
if (data.success !== true) return "Unknown";
|
|
2383
2425
|
const parts = [
|
|
2384
2426
|
data.city,
|
|
2385
|
-
data.
|
|
2427
|
+
data.region,
|
|
2386
2428
|
data.country
|
|
2387
2429
|
].filter(Boolean);
|
|
2388
2430
|
return parts.length > 0 ? parts.join(", ") : "Unknown";
|
|
@@ -2392,18 +2434,25 @@ async function resolveIpLocation(ip) {
|
|
|
2392
2434
|
}
|
|
2393
2435
|
/**
|
|
2394
2436
|
* 从 Request 中提取客户端真实 IP
|
|
2395
|
-
*
|
|
2437
|
+
*
|
|
2438
|
+
* 优先级:CF-Connecting-IP(Cloudflare 平台注入的权威头,客户端不可伪造)
|
|
2439
|
+
* > X-Real-IP > X-Forwarded-For 末段。
|
|
2440
|
+
*
|
|
2441
|
+
* ⚠ 前提声明:XFF 首值可由客户端任意伪造,因此取**末段**(最靠近本服务的
|
|
2442
|
+
* 可信代理所追加的一跳)。部署平台若自身不覆写 XFF(Vercel 会覆写),
|
|
2443
|
+
* 末段仍可能被上游代理链污染;Cloudflare 场景以 CF-Connecting-IP 为准。
|
|
2396
2444
|
*/
|
|
2397
2445
|
function getClientIp(request) {
|
|
2446
|
+
const cfip = request.headers.get("cf-connecting-ip");
|
|
2447
|
+
if (cfip) return cfip.trim();
|
|
2448
|
+
const xri = request.headers.get("x-real-ip");
|
|
2449
|
+
if (xri) return xri.trim();
|
|
2398
2450
|
const xff = request.headers.get("x-forwarded-for");
|
|
2399
2451
|
if (xff) {
|
|
2400
|
-
const
|
|
2401
|
-
|
|
2452
|
+
const segments = xff.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2453
|
+
const last = segments[segments.length - 1];
|
|
2454
|
+
if (last) return last;
|
|
2402
2455
|
}
|
|
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
2456
|
return null;
|
|
2408
2457
|
}
|
|
2409
2458
|
//#endregion
|
|
@@ -2465,7 +2514,8 @@ function parseUserAgent(ua) {
|
|
|
2465
2514
|
};
|
|
2466
2515
|
}
|
|
2467
2516
|
/**
|
|
2468
|
-
* 将 ParsedUA 拼接为可读字符串,如 "Chrome 120 / Windows 10/11 /
|
|
2517
|
+
* 将 ParsedUA 拼接为可读字符串,如 "Chrome 120 / Windows 10/11 / Desktop"
|
|
2518
|
+
* (输出为英文原文;中文文案由展示层经 paraglide 映射)
|
|
2469
2519
|
*/
|
|
2470
2520
|
function formatParsedUA(parsed) {
|
|
2471
2521
|
return [
|
|
@@ -2710,14 +2760,29 @@ function normalizeEmailCategories(raw) {
|
|
|
2710
2760
|
}
|
|
2711
2761
|
return result;
|
|
2712
2762
|
}
|
|
2763
|
+
let dndMalformedWarned = false;
|
|
2764
|
+
/**
|
|
2765
|
+
* 判定 now(HH:MM)是否落在勿扰窗口内(支持跨零点)。
|
|
2766
|
+
*
|
|
2767
|
+
* 畸形时间输入(空串 / 非数字,如 DB 脏数据)旧实现产出 NaN、比较恒 false、
|
|
2768
|
+
* DND 静默失效——现显式校验,畸形时告警一次并按「不在勿扰窗口」处理。
|
|
2769
|
+
*/
|
|
2713
2770
|
function isInDndWindow(prefs, now) {
|
|
2714
2771
|
if (!prefs.dndEnabled) return false;
|
|
2715
|
-
const
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
const
|
|
2720
|
-
const
|
|
2772
|
+
const parse = (s) => {
|
|
2773
|
+
const [h, m] = s.split(":").map(Number);
|
|
2774
|
+
return Number.isFinite(h) && Number.isFinite(m) ? h * 60 + m : NaN;
|
|
2775
|
+
};
|
|
2776
|
+
const start = parse(prefs.dndStart);
|
|
2777
|
+
const end = parse(prefs.dndEnd);
|
|
2778
|
+
const cur = parse(now);
|
|
2779
|
+
if (Number.isNaN(start) || Number.isNaN(end) || Number.isNaN(cur)) {
|
|
2780
|
+
if (!dndMalformedWarned) {
|
|
2781
|
+
dndMalformedWarned = true;
|
|
2782
|
+
console.warn(`[notifications] 勿扰时段格式非法(须为 HH:MM):start=${JSON.stringify(prefs.dndStart)} end=${JSON.stringify(prefs.dndEnd)},DND 判定按不生效处理`);
|
|
2783
|
+
}
|
|
2784
|
+
return false;
|
|
2785
|
+
}
|
|
2721
2786
|
if (start <= end) return cur >= start && cur < end;
|
|
2722
2787
|
return cur >= start || cur < end;
|
|
2723
2788
|
}
|
|
@@ -2725,8 +2790,18 @@ function isInDndWindow(prefs, now) {
|
|
|
2725
2790
|
//#region src/contract/modules/registry.ts
|
|
2726
2791
|
var ModuleRegistry = class {
|
|
2727
2792
|
modules = /* @__PURE__ */ new Map();
|
|
2728
|
-
|
|
2729
|
-
|
|
2793
|
+
/**
|
|
2794
|
+
* 注册模块。同 id 重复注册的语义:
|
|
2795
|
+
* - 显式 `{ overwrite: true }`:警告并覆盖(有意的替换场景);
|
|
2796
|
+
* - 生产环境:视为装配期配置错误直接抛出,避免「两个模块意外同 id」被静默掩盖;
|
|
2797
|
+
* - dev/test/HMR:警告并覆盖(模块双执行 / 热更新场景保持宽松)。
|
|
2798
|
+
*/
|
|
2799
|
+
register(mod, options) {
|
|
2800
|
+
if (this.modules.has(mod.id)) {
|
|
2801
|
+
const isProd = typeof process !== "undefined" && process.env?.NODE_ENV === "production";
|
|
2802
|
+
if (!options?.overwrite && isProd) throw new Error(`[ModuleRegistry] "${mod.id}" already registered(同 id 模块重复注册;若确需覆盖请传 { overwrite: true })`);
|
|
2803
|
+
console.warn(`[ModuleRegistry] "${mod.id}" already registered, overwriting`);
|
|
2804
|
+
}
|
|
2730
2805
|
this.modules.set(mod.id, mod);
|
|
2731
2806
|
}
|
|
2732
2807
|
getModule(id) {
|
|
@@ -2790,4 +2865,4 @@ const ICON_MAP = {
|
|
|
2790
2865
|
music4: Music4
|
|
2791
2866
|
};
|
|
2792
2867
|
//#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,
|
|
2868
|
+
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, resolvePluginOrder as at, parseUserAgent as b, normalizeEmailCategories as c, RESOURCE_KEYS as d, generateAssemblyCode as et, ResourceRegistry as f, FeatureRegistry as g, FEATURE_KEYS as h, DEFAULT_CATEGORIES as i, resolveImportSpecifiers 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, defineProfile as ot, isResourceDefined as p, registerPrismaSeam as q, moduleRegistry as r, parseProfileImports as rt, normalizeCategoryFrequencies as s, definePlugin 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.1",
|
|
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": {
|