@spaceflow/core 0.8.0 → 0.10.0
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/README.md +75 -56
- package/dist/index.js +5589 -970
- package/dist/index.js.map +1 -1
- package/package.json +9 -3
- package/src/cli-runtime/di/config.ts +157 -0
- package/src/cli-runtime/di/container.ts +120 -0
- package/src/cli-runtime/di/index.ts +3 -0
- package/src/cli-runtime/di/services.ts +53 -0
- package/src/cli-runtime/extension-loader.ts +74 -0
- package/src/{shared → cli-runtime}/i18n/index.ts +7 -1
- package/src/cli-runtime/i18n/init.ts +117 -0
- package/src/cli-runtime/index.ts +131 -0
- package/src/cli-runtime/internal-extensions.ts +33 -0
- package/src/commands/build/build.service.ts +323 -0
- package/src/commands/build/index.ts +49 -0
- package/src/commands/clear/clear.service.ts +159 -0
- package/src/commands/clear/index.ts +35 -0
- package/src/commands/commit/commit.config.ts +168 -0
- package/src/commands/commit/commit.service.ts +950 -0
- package/src/commands/commit/index.ts +58 -0
- package/src/commands/create/create.service.ts +318 -0
- package/src/commands/create/index.ts +42 -0
- package/src/commands/dev/index.ts +30 -0
- package/src/commands/install/index.ts +65 -0
- package/src/commands/install/install.service.ts +1539 -0
- package/src/commands/list/index.ts +33 -0
- package/src/commands/list/list.service.ts +127 -0
- package/src/commands/mcp/index.ts +37 -0
- package/src/commands/mcp/mcp.service.ts +246 -0
- package/src/commands/runx/index.ts +47 -0
- package/src/commands/runx/runx.service.ts +142 -0
- package/src/commands/runx/runx.utils.ts +83 -0
- package/src/commands/schema/index.ts +30 -0
- package/src/commands/setup/index.ts +34 -0
- package/src/commands/setup/setup.service.ts +234 -0
- package/src/commands/uninstall/index.ts +42 -0
- package/src/commands/uninstall/uninstall.service.ts +166 -0
- package/src/commands/update/index.ts +42 -0
- package/src/commands/update/update.service.ts +373 -0
- package/src/config/index.ts +1 -30
- package/src/config/spaceflow.config.ts +226 -278
- package/src/index.ts +11 -1
- package/src/locales/en/build.json +22 -0
- package/src/locales/en/clear.json +16 -0
- package/src/locales/en/commit.json +45 -0
- package/src/locales/en/create.json +27 -0
- package/src/locales/en/dev.json +5 -0
- package/src/locales/en/install.json +71 -0
- package/src/locales/en/list.json +8 -0
- package/src/locales/en/mcp.json +19 -0
- package/src/locales/en/runx.json +13 -0
- package/src/locales/en/schema.json +4 -0
- package/src/locales/en/setup.json +14 -0
- package/src/locales/en/uninstall.json +18 -0
- package/src/locales/en/update.json +28 -0
- package/src/locales/zh-cn/build.json +22 -0
- package/src/locales/zh-cn/clear.json +16 -0
- package/src/locales/zh-cn/commit.json +45 -0
- package/src/locales/zh-cn/create.json +27 -0
- package/src/locales/zh-cn/dev.json +5 -0
- package/src/locales/zh-cn/install.json +71 -0
- package/src/locales/zh-cn/list.json +8 -0
- package/src/locales/zh-cn/mcp.json +19 -0
- package/src/locales/zh-cn/runx.json +13 -0
- package/src/locales/zh-cn/schema.json +4 -0
- package/src/locales/zh-cn/setup.json +14 -0
- package/src/locales/zh-cn/uninstall.json +18 -0
- package/src/locales/zh-cn/update.json +28 -0
- package/src/shared/editor-config/index.ts +2 -21
- package/src/shared/llm-proxy/adapters/openai.adapter.ts +3 -1
- package/src/shared/package-manager/index.ts +5 -76
- package/src/shared/source-utils/index.ts +12 -130
- package/src/shared/spaceflow-dir/index.ts +13 -135
- package/src/shared/verbose/index.ts +10 -87
- package/dist/524.js +0 -9
- package/src/config/ci.config.ts +0 -29
- package/src/config/config-loader.ts +0 -100
- package/src/config/config-reader.service.ts +0 -128
- package/src/config/config-reader.ts +0 -75
- package/src/config/feishu.config.ts +0 -35
- package/src/config/git-provider.config.ts +0 -29
- package/src/config/llm.config.ts +0 -110
- package/src/config/load-env.ts +0 -15
- package/src/config/storage.config.ts +0 -33
- /package/src/{shared → cli-runtime}/i18n/i18n.spec.ts +0 -0
- /package/src/{shared → cli-runtime}/i18n/i18n.ts +0 -0
- /package/src/{shared → cli-runtime}/i18n/locale-detect.ts +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spaceflow/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Spaceflow 核心能力库",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Lydanne",
|
|
@@ -69,6 +69,10 @@
|
|
|
69
69
|
"./parallel": {
|
|
70
70
|
"types": "./src/shared/parallel/index.ts",
|
|
71
71
|
"import": "./dist/index.js"
|
|
72
|
+
},
|
|
73
|
+
"./cli-runtime": {
|
|
74
|
+
"types": "./src/cli-runtime/index.ts",
|
|
75
|
+
"import": "./dist/index.js"
|
|
72
76
|
}
|
|
73
77
|
},
|
|
74
78
|
"dependencies": {
|
|
@@ -92,7 +96,10 @@
|
|
|
92
96
|
"release-it-gitea": "^1.8.0",
|
|
93
97
|
"rxjs": "^7.8.1",
|
|
94
98
|
"zod": "^4.3.6",
|
|
95
|
-
"zod-to-json-schema": "^3.25.1"
|
|
99
|
+
"zod-to-json-schema": "^3.25.1",
|
|
100
|
+
"commander": "^12.1.0",
|
|
101
|
+
"i18next": "^25.8.4",
|
|
102
|
+
"@spaceflow/shared": "0.2.0"
|
|
96
103
|
},
|
|
97
104
|
"devDependencies": {
|
|
98
105
|
"@swc/core": "1.15.3",
|
|
@@ -108,7 +115,6 @@
|
|
|
108
115
|
"typescript": "^5.7.3",
|
|
109
116
|
"typescript-eslint": "^8.20.0",
|
|
110
117
|
"@vitest/coverage-v8": "^4.0.18",
|
|
111
|
-
"i18next": "^25.8.4",
|
|
112
118
|
"unplugin-swc": "^1.5.9",
|
|
113
119
|
"vitest": "^4.0.18"
|
|
114
120
|
},
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { type IConfigReader, loadSpaceflowConfig } from "@spaceflow/core";
|
|
2
|
+
import type { ZodSchema } from "zod";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 环境变量映射配置
|
|
6
|
+
* key: 配置路径(如 "gitProvider.token")
|
|
7
|
+
* value: 环境变量名或环境变量名数组(按优先级)
|
|
8
|
+
*/
|
|
9
|
+
const ENV_MAPPINGS: Record<string, string | string[]> = {
|
|
10
|
+
// Git Provider
|
|
11
|
+
"gitProvider.token": ["GIT_PROVIDER_TOKEN", "GITHUB_TOKEN", "GITLAB_TOKEN", "GITEA_TOKEN"],
|
|
12
|
+
"gitProvider.serverUrl": ["GIT_PROVIDER_SERVER_URL", "GITHUB_API_URL"],
|
|
13
|
+
// OpenAI
|
|
14
|
+
"llm.openai.apiKey": "OPENAI_API_KEY",
|
|
15
|
+
"llm.openai.baseUrl": "OPENAI_BASE_URL",
|
|
16
|
+
"llm.openai.model": "OPENAI_MODEL",
|
|
17
|
+
// Gemini
|
|
18
|
+
"llm.gemini.apiKey": "GEMINI_API_KEY",
|
|
19
|
+
"llm.gemini.model": "GEMINI_MODEL",
|
|
20
|
+
// Claude
|
|
21
|
+
"llm.claudeCode.authToken": "ANTHROPIC_API_KEY",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 从环境变量获取值
|
|
26
|
+
*/
|
|
27
|
+
function getEnvValue(envNames: string | string[]): string | undefined {
|
|
28
|
+
const names = Array.isArray(envNames) ? envNames : [envNames];
|
|
29
|
+
for (const name of names) {
|
|
30
|
+
const value = process.env[name];
|
|
31
|
+
if (value) return value;
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 设置嵌套对象的值
|
|
38
|
+
*/
|
|
39
|
+
function setNestedValue(obj: Record<string, any>, path: string, value: any): void {
|
|
40
|
+
const parts = path.split(".");
|
|
41
|
+
let current = obj;
|
|
42
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
43
|
+
const part = parts[i];
|
|
44
|
+
if (!(part in current)) {
|
|
45
|
+
current[part] = {};
|
|
46
|
+
}
|
|
47
|
+
current = current[part];
|
|
48
|
+
}
|
|
49
|
+
current[parts[parts.length - 1]] = value;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 获取嵌套对象的值
|
|
54
|
+
*/
|
|
55
|
+
function getNestedValue(obj: Record<string, any>, path: string): any {
|
|
56
|
+
const parts = path.split(".");
|
|
57
|
+
let current = obj;
|
|
58
|
+
for (const part of parts) {
|
|
59
|
+
if (current === null || current === undefined) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
current = current[part];
|
|
63
|
+
}
|
|
64
|
+
return current;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 统一配置服务
|
|
69
|
+
* 负责从配置文件和环境变量读取配置
|
|
70
|
+
*/
|
|
71
|
+
export class UnifiedConfigReader implements IConfigReader {
|
|
72
|
+
private mergedConfig: Record<string, unknown>;
|
|
73
|
+
private schemas = new Map<string, ZodSchema>();
|
|
74
|
+
|
|
75
|
+
constructor(cwd?: string) {
|
|
76
|
+
const config = loadSpaceflowConfig(cwd) as Record<string, unknown>;
|
|
77
|
+
this.mergedConfig = this.mergeEnvConfig(config);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 合并环境变量到配置
|
|
82
|
+
*/
|
|
83
|
+
private mergeEnvConfig(config: Record<string, unknown>): Record<string, unknown> {
|
|
84
|
+
const merged = JSON.parse(JSON.stringify(config));
|
|
85
|
+
for (const [path, envNames] of Object.entries(ENV_MAPPINGS)) {
|
|
86
|
+
const envValue = getEnvValue(envNames);
|
|
87
|
+
if (envValue && !getNestedValue(merged, path)) {
|
|
88
|
+
setNestedValue(merged, path, envValue);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// 特殊处理:如果有 OPENAI_API_KEY 但没有 llm.openai,创建默认配置
|
|
92
|
+
if (process.env.OPENAI_API_KEY && !merged.llm) {
|
|
93
|
+
merged.llm = {
|
|
94
|
+
openai: {
|
|
95
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
96
|
+
baseUrl: process.env.OPENAI_BASE_URL || "https://api.openai.com/v1",
|
|
97
|
+
model: process.env.OPENAI_MODEL,
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return merged;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 获取配置值
|
|
106
|
+
*/
|
|
107
|
+
get<T>(key: string): T | undefined {
|
|
108
|
+
const parts = key.split(".");
|
|
109
|
+
let current: unknown = this.mergedConfig;
|
|
110
|
+
for (const part of parts) {
|
|
111
|
+
if (current === null || current === undefined) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
current = (current as Record<string, unknown>)[part];
|
|
115
|
+
}
|
|
116
|
+
return current as T | undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 获取插件配置(带 schema 验证)
|
|
121
|
+
*/
|
|
122
|
+
getPluginConfig<T>(key: string): T | undefined {
|
|
123
|
+
const rawConfig = this.mergedConfig[key] ?? {};
|
|
124
|
+
const schema = this.schemas.get(key);
|
|
125
|
+
if (!schema) {
|
|
126
|
+
return rawConfig as T;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
return schema.parse(rawConfig) as T;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.warn(`⚠️ 配置 "${key}" 验证失败:`, error);
|
|
132
|
+
return rawConfig as T;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 注册配置 schema
|
|
138
|
+
*/
|
|
139
|
+
registerSchema(key: string, schema: ZodSchema): void {
|
|
140
|
+
this.schemas.set(key, schema);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 重新加载配置
|
|
145
|
+
*/
|
|
146
|
+
reload(cwd?: string): void {
|
|
147
|
+
const config = loadSpaceflowConfig(cwd) as Record<string, unknown>;
|
|
148
|
+
this.mergedConfig = this.mergeEnvConfig(config);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 获取原始配置对象
|
|
153
|
+
*/
|
|
154
|
+
getRawConfig(): Record<string, unknown> {
|
|
155
|
+
return this.mergedConfig;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SpaceflowContext,
|
|
3
|
+
IConfigReader,
|
|
4
|
+
IOutputService,
|
|
5
|
+
IStorageService,
|
|
6
|
+
} from "@spaceflow/core";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 服务工厂函数类型
|
|
10
|
+
*/
|
|
11
|
+
export type ServiceFactory<T = unknown> = (container: ServiceContainer) => T;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 服务注册信息
|
|
15
|
+
*/
|
|
16
|
+
interface ServiceRegistration<T = unknown> {
|
|
17
|
+
factory: ServiceFactory<T>;
|
|
18
|
+
instance?: T;
|
|
19
|
+
initialized: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 服务容器
|
|
24
|
+
* 提供懒加载的依赖注入功能
|
|
25
|
+
*/
|
|
26
|
+
export class ServiceContainer implements SpaceflowContext {
|
|
27
|
+
private registrations = new Map<string, ServiceRegistration>();
|
|
28
|
+
private _config!: IConfigReader;
|
|
29
|
+
private _output!: IOutputService;
|
|
30
|
+
private _storage!: IStorageService;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 设置核心服务(config, output, storage)
|
|
34
|
+
*/
|
|
35
|
+
setCoreServices(config: IConfigReader, output: IOutputService, storage: IStorageService): void {
|
|
36
|
+
this._config = config;
|
|
37
|
+
this._output = output;
|
|
38
|
+
this._storage = storage;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 注册服务工厂(懒加载)
|
|
43
|
+
*/
|
|
44
|
+
registerFactory<T>(key: string, factory: ServiceFactory<T>): void {
|
|
45
|
+
this.registrations.set(key, {
|
|
46
|
+
factory: factory as ServiceFactory,
|
|
47
|
+
initialized: false,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 注册服务实例(立即可用)
|
|
53
|
+
*/
|
|
54
|
+
registerService(key: string, service: unknown): void {
|
|
55
|
+
this.registrations.set(key, {
|
|
56
|
+
factory: () => service,
|
|
57
|
+
instance: service,
|
|
58
|
+
initialized: true,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 获取服务(懒加载)
|
|
64
|
+
*/
|
|
65
|
+
getService<T = unknown>(key: string): T {
|
|
66
|
+
const registration = this.registrations.get(key);
|
|
67
|
+
if (!registration) {
|
|
68
|
+
throw new Error(`服务 "${key}" 未注册`);
|
|
69
|
+
}
|
|
70
|
+
if (!registration.initialized) {
|
|
71
|
+
registration.instance = registration.factory(this);
|
|
72
|
+
registration.initialized = true;
|
|
73
|
+
}
|
|
74
|
+
return registration.instance as T;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 检查服务是否已注册
|
|
79
|
+
*/
|
|
80
|
+
hasService(key: string): boolean {
|
|
81
|
+
return this.registrations.has(key);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 尝试获取服务(不抛错)
|
|
86
|
+
*/
|
|
87
|
+
tryGetService<T = unknown>(key: string): T | undefined {
|
|
88
|
+
if (!this.hasService(key)) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
return this.getService<T>(key);
|
|
93
|
+
} catch {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get config(): IConfigReader {
|
|
99
|
+
return this._config;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get output(): IOutputService {
|
|
103
|
+
return this._output;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
get storage(): IStorageService {
|
|
107
|
+
return this._storage;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 销毁容器
|
|
112
|
+
*/
|
|
113
|
+
async destroy(): Promise<void> {
|
|
114
|
+
// 销毁 storage 服务(清理定时器)
|
|
115
|
+
if (this._storage && typeof (this._storage as any).destroy === "function") {
|
|
116
|
+
(this._storage as any).destroy();
|
|
117
|
+
}
|
|
118
|
+
this.registrations.clear();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OutputService,
|
|
3
|
+
StorageService,
|
|
4
|
+
GitSdkService,
|
|
5
|
+
GitProviderService,
|
|
6
|
+
LlmProxyService,
|
|
7
|
+
FileAdapter,
|
|
8
|
+
} from "@spaceflow/core";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import type { ServiceContainer } from "./container";
|
|
11
|
+
import { UnifiedConfigReader } from "./config";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 初始化服务容器
|
|
15
|
+
*/
|
|
16
|
+
export function initializeContainer(container: ServiceContainer, cwd?: string): void {
|
|
17
|
+
const workDir = cwd || process.cwd();
|
|
18
|
+
// 初始化核心服务(.env 已在 CLI 壳子阶段加载)
|
|
19
|
+
const config = new UnifiedConfigReader(workDir);
|
|
20
|
+
const output = new OutputService();
|
|
21
|
+
const storageDir = join(workDir, ".spaceflow", "cache");
|
|
22
|
+
const storage = new StorageService(new FileAdapter(storageDir));
|
|
23
|
+
container.setCoreServices(config, output, storage);
|
|
24
|
+
// 注册服务工厂
|
|
25
|
+
registerServiceFactories(container);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 注册所有服务工厂
|
|
30
|
+
*/
|
|
31
|
+
function registerServiceFactories(container: ServiceContainer): void {
|
|
32
|
+
// Config - 返回已初始化的 config
|
|
33
|
+
container.registerFactory("config", (c) => c.config);
|
|
34
|
+
// GitSdk - 无依赖
|
|
35
|
+
container.registerFactory("gitSdk", () => new GitSdkService());
|
|
36
|
+
// GitProvider - 依赖配置(环境变量已在 UnifiedConfigReader 中合并)
|
|
37
|
+
container.registerFactory("gitProvider", (c) => {
|
|
38
|
+
const config = c.config.get<any>("gitProvider");
|
|
39
|
+
if (!config?.provider) {
|
|
40
|
+
throw new Error("缺少 gitProvider 配置");
|
|
41
|
+
}
|
|
42
|
+
const baseUrl = config.serverUrl || config.baseUrl;
|
|
43
|
+
return new GitProviderService({ ...config, baseUrl });
|
|
44
|
+
});
|
|
45
|
+
// LlmProxy - 依赖配置(环境变量已在 UnifiedConfigReader 中合并)
|
|
46
|
+
container.registerFactory("llmProxy", (c) => {
|
|
47
|
+
const config = c.config.get<any>("llm");
|
|
48
|
+
if (!config || (!config.openai && !config.gemini && !config.claudeCode && !config.openCode)) {
|
|
49
|
+
throw new Error("缺少 llm 配置,请在 spaceflow.json 中配置或设置 OPENAI_API_KEY 环境变量");
|
|
50
|
+
}
|
|
51
|
+
return new LlmProxyService(config);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ExtensionDefinition, CommandDefinition } from "@spaceflow/core";
|
|
2
|
+
import type { SpaceflowContext } from "@spaceflow/core";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 扩展加载器
|
|
6
|
+
* 加载新格式的扩展(使用 defineExtension)
|
|
7
|
+
*/
|
|
8
|
+
export class ExtensionLoader {
|
|
9
|
+
private extensions = new Map<string, ExtensionDefinition>();
|
|
10
|
+
private commands = new Map<string, CommandDefinition>();
|
|
11
|
+
|
|
12
|
+
constructor(private readonly ctx: SpaceflowContext) {}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 注册扩展
|
|
16
|
+
*/
|
|
17
|
+
registerExtension(extension: ExtensionDefinition): void {
|
|
18
|
+
this.extensions.set(extension.name, extension);
|
|
19
|
+
|
|
20
|
+
// 注册配置 schema
|
|
21
|
+
if (extension.configSchema && extension.configKey) {
|
|
22
|
+
this.ctx.config.registerSchema(extension.configKey, extension.configSchema());
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// 注册命令
|
|
26
|
+
for (const cmd of extension.commands) {
|
|
27
|
+
this.commands.set(cmd.name, cmd);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 注册服务
|
|
31
|
+
if (extension.services) {
|
|
32
|
+
for (const svc of extension.services) {
|
|
33
|
+
const instance = svc.factory(this.ctx);
|
|
34
|
+
this.ctx.registerService(svc.key, instance);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 调用初始化钩子
|
|
39
|
+
if (extension.onInit) {
|
|
40
|
+
extension.onInit(this.ctx);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 获取所有命令
|
|
46
|
+
*/
|
|
47
|
+
getCommands(): CommandDefinition[] {
|
|
48
|
+
return Array.from(this.commands.values());
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 获取所有扩展
|
|
53
|
+
*/
|
|
54
|
+
getExtensions(): ExtensionDefinition[] {
|
|
55
|
+
return Array.from(this.extensions.values());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 获取所有 MCP 服务
|
|
60
|
+
* 返回扩展中定义的 mcp 字段
|
|
61
|
+
*/
|
|
62
|
+
getMcpServers(): Array<{ extensionName: string; mcp: NonNullable<ExtensionDefinition["mcp"]> }> {
|
|
63
|
+
const mcpServers: Array<{
|
|
64
|
+
extensionName: string;
|
|
65
|
+
mcp: NonNullable<ExtensionDefinition["mcp"]>;
|
|
66
|
+
}> = [];
|
|
67
|
+
for (const ext of this.extensions.values()) {
|
|
68
|
+
if (ext.mcp) {
|
|
69
|
+
mcpServers.push({ extensionName: ext.name, mcp: ext.mcp });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return mcpServers;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// ---- 基础 i18n 设施(globalThis 桥接) ----
|
|
1
2
|
export {
|
|
2
3
|
t,
|
|
3
4
|
setGlobalT,
|
|
@@ -7,8 +8,13 @@ export {
|
|
|
7
8
|
type TranslateFn,
|
|
8
9
|
type AddLocaleResourcesFn,
|
|
9
10
|
} from "./i18n";
|
|
11
|
+
|
|
12
|
+
// ---- 语言检测 ----
|
|
10
13
|
export { detectLocale } from "./locale-detect";
|
|
11
14
|
|
|
12
|
-
//
|
|
15
|
+
// ---- CLI i18n 初始化(i18next) ----
|
|
16
|
+
export { initCliI18n, addI18nextResources } from "./init";
|
|
17
|
+
|
|
18
|
+
// ---- core 基础翻译资源 ----
|
|
13
19
|
export { default as coreZhCN } from "../../locales/zh-cn/translation.json";
|
|
14
20
|
export { default as coreEn } from "../../locales/en/translation.json";
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import * as i18nextModule from "i18next";
|
|
2
|
+
import type { i18n } from "i18next";
|
|
3
|
+
import { setGlobalT, setGlobalAddLocaleResources } from "./i18n";
|
|
4
|
+
import { detectLocale } from "./locale-detect";
|
|
5
|
+
|
|
6
|
+
// core 基础翻译资源
|
|
7
|
+
import coreZhCN from "../../locales/zh-cn/translation.json";
|
|
8
|
+
import coreEn from "../../locales/en/translation.json";
|
|
9
|
+
|
|
10
|
+
// 兼容 CJS/ESM 混合环境
|
|
11
|
+
const i18next: i18n =
|
|
12
|
+
(i18nextModule as unknown as { default: i18n }).default || (i18nextModule as unknown as i18n);
|
|
13
|
+
|
|
14
|
+
// ---- CLI 命令翻译 ----
|
|
15
|
+
import buildZhCN from "../../locales/zh-cn/build.json";
|
|
16
|
+
import buildEn from "../../locales/en/build.json";
|
|
17
|
+
import clearZhCN from "../../locales/zh-cn/clear.json";
|
|
18
|
+
import clearEn from "../../locales/en/clear.json";
|
|
19
|
+
import commitZhCN from "../../locales/zh-cn/commit.json";
|
|
20
|
+
import commitEn from "../../locales/en/commit.json";
|
|
21
|
+
import createZhCN from "../../locales/zh-cn/create.json";
|
|
22
|
+
import createEn from "../../locales/en/create.json";
|
|
23
|
+
import devZhCN from "../../locales/zh-cn/dev.json";
|
|
24
|
+
import devEn from "../../locales/en/dev.json";
|
|
25
|
+
import installZhCN from "../../locales/zh-cn/install.json";
|
|
26
|
+
import installEn from "../../locales/en/install.json";
|
|
27
|
+
import listZhCN from "../../locales/zh-cn/list.json";
|
|
28
|
+
import listEn from "../../locales/en/list.json";
|
|
29
|
+
import mcpZhCN from "../../locales/zh-cn/mcp.json";
|
|
30
|
+
import mcpEn from "../../locales/en/mcp.json";
|
|
31
|
+
import runxZhCN from "../../locales/zh-cn/runx.json";
|
|
32
|
+
import runxEn from "../../locales/en/runx.json";
|
|
33
|
+
import schemaZhCN from "../../locales/zh-cn/schema.json";
|
|
34
|
+
import schemaEn from "../../locales/en/schema.json";
|
|
35
|
+
import setupZhCN from "../../locales/zh-cn/setup.json";
|
|
36
|
+
import setupEn from "../../locales/en/setup.json";
|
|
37
|
+
import uninstallZhCN from "../../locales/zh-cn/uninstall.json";
|
|
38
|
+
import uninstallEn from "../../locales/en/uninstall.json";
|
|
39
|
+
import updateZhCN from "../../locales/zh-cn/update.json";
|
|
40
|
+
import updateEn from "../../locales/en/update.json";
|
|
41
|
+
|
|
42
|
+
type LocaleResource = Record<string, Record<string, string>>;
|
|
43
|
+
|
|
44
|
+
/** 所有内部命令 i18n 资源映射(命名空间 → 语言 → 翻译) */
|
|
45
|
+
const allLocales: Record<string, LocaleResource> = {
|
|
46
|
+
build: { "zh-CN": buildZhCN, en: buildEn },
|
|
47
|
+
clear: { "zh-CN": clearZhCN, en: clearEn },
|
|
48
|
+
commit: { "zh-CN": commitZhCN, en: commitEn },
|
|
49
|
+
create: { "zh-CN": createZhCN, en: createEn },
|
|
50
|
+
dev: { "zh-CN": devZhCN, en: devEn },
|
|
51
|
+
install: { "zh-CN": installZhCN, en: installEn },
|
|
52
|
+
list: { "zh-CN": listZhCN, en: listEn },
|
|
53
|
+
mcp: { "zh-CN": mcpZhCN, en: mcpEn },
|
|
54
|
+
runx: { "zh-CN": runxZhCN, en: runxEn },
|
|
55
|
+
schema: { "zh-CN": schemaZhCN, en: schemaEn },
|
|
56
|
+
setup: { "zh-CN": setupZhCN, en: setupEn },
|
|
57
|
+
uninstall: { "zh-CN": uninstallZhCN, en: uninstallEn },
|
|
58
|
+
update: { "zh-CN": updateZhCN, en: updateEn },
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/** 默认命名空间 */
|
|
62
|
+
const DEFAULT_NS = "translation";
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 初始化 CLI 的 i18n 系统
|
|
66
|
+
* 1. 初始化 i18next(core 基础翻译 + CLI 命令翻译)
|
|
67
|
+
* 2. 通过 setGlobalT 挂载翻译函数到 globalThis
|
|
68
|
+
*/
|
|
69
|
+
export function initCliI18n(lang?: string): void {
|
|
70
|
+
const lng = lang || detectLocale();
|
|
71
|
+
|
|
72
|
+
void i18next.init({
|
|
73
|
+
lng,
|
|
74
|
+
fallbackLng: "zh-CN",
|
|
75
|
+
defaultNS: DEFAULT_NS,
|
|
76
|
+
ns: [DEFAULT_NS, ...Object.keys(allLocales)],
|
|
77
|
+
resources: {
|
|
78
|
+
"zh-CN": { [DEFAULT_NS]: coreZhCN },
|
|
79
|
+
en: { [DEFAULT_NS]: coreEn },
|
|
80
|
+
},
|
|
81
|
+
interpolation: { escapeValue: false },
|
|
82
|
+
returnNull: false,
|
|
83
|
+
returnEmptyString: false,
|
|
84
|
+
initImmediate: false,
|
|
85
|
+
showSupportNotice: false,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// 注册 CLI 命令翻译到各自命名空间
|
|
89
|
+
for (const [ns, resources] of Object.entries(allLocales)) {
|
|
90
|
+
for (const [lngKey, translations] of Object.entries(resources)) {
|
|
91
|
+
i18next.addResourceBundle(lngKey, ns, translations, true, true);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 挂载到 globalThis,让 core 和扩展的 t() / addLocaleResources() 调用都能生效
|
|
96
|
+
setGlobalT((key, options) => i18next.t(key, options) as string);
|
|
97
|
+
setGlobalAddLocaleResources(addI18nextResources);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 为外部 Extension 注册语言资源(i18next 实现)
|
|
102
|
+
* @param ns 命名空间(通常为 Extension name)
|
|
103
|
+
* @param resources 语言资源,key 为语言代码,值为翻译对象
|
|
104
|
+
*/
|
|
105
|
+
export function addI18nextResources(
|
|
106
|
+
ns: string,
|
|
107
|
+
resources: Record<string, Record<string, unknown>>,
|
|
108
|
+
): void {
|
|
109
|
+
for (const [lng, translations] of Object.entries(resources)) {
|
|
110
|
+
i18next.addResourceBundle(lng, ns, translations, true, true);
|
|
111
|
+
}
|
|
112
|
+
if (!i18next.options.ns) {
|
|
113
|
+
i18next.options.ns = [DEFAULT_NS, ns];
|
|
114
|
+
} else if (Array.isArray(i18next.options.ns) && !i18next.options.ns.includes(ns)) {
|
|
115
|
+
i18next.options.ns.push(ns);
|
|
116
|
+
}
|
|
117
|
+
}
|