@spaceflow/core 0.7.0 → 0.9.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.
Files changed (88) hide show
  1. package/dist/index.js +5621 -1027
  2. package/dist/index.js.map +1 -1
  3. package/package.json +9 -3
  4. package/src/cli-runtime/di/config.ts +157 -0
  5. package/src/cli-runtime/di/container.ts +120 -0
  6. package/src/cli-runtime/di/index.ts +3 -0
  7. package/src/cli-runtime/di/services.ts +53 -0
  8. package/src/cli-runtime/extension-loader.ts +74 -0
  9. package/src/cli-runtime/i18n/i18n.ts +89 -0
  10. package/src/cli-runtime/i18n/index.ts +20 -0
  11. package/src/cli-runtime/i18n/init.ts +117 -0
  12. package/src/cli-runtime/index.ts +131 -0
  13. package/src/cli-runtime/internal-extensions.ts +33 -0
  14. package/src/commands/build/build.service.ts +323 -0
  15. package/src/commands/build/index.ts +49 -0
  16. package/src/commands/clear/clear.service.ts +159 -0
  17. package/src/commands/clear/index.ts +35 -0
  18. package/src/commands/commit/commit.config.ts +168 -0
  19. package/src/commands/commit/commit.service.ts +950 -0
  20. package/src/commands/commit/index.ts +58 -0
  21. package/src/commands/create/create.service.ts +318 -0
  22. package/src/commands/create/index.ts +42 -0
  23. package/src/commands/dev/index.ts +30 -0
  24. package/src/commands/install/index.ts +65 -0
  25. package/src/commands/install/install.service.ts +1539 -0
  26. package/src/commands/list/index.ts +33 -0
  27. package/src/commands/list/list.service.ts +127 -0
  28. package/src/commands/mcp/index.ts +37 -0
  29. package/src/commands/mcp/mcp.service.ts +246 -0
  30. package/src/commands/runx/index.ts +47 -0
  31. package/src/commands/runx/runx.service.ts +142 -0
  32. package/src/commands/runx/runx.utils.ts +83 -0
  33. package/src/commands/schema/index.ts +30 -0
  34. package/src/commands/setup/index.ts +34 -0
  35. package/src/commands/setup/setup.service.ts +234 -0
  36. package/src/commands/uninstall/index.ts +42 -0
  37. package/src/commands/uninstall/uninstall.service.ts +166 -0
  38. package/src/commands/update/index.ts +42 -0
  39. package/src/commands/update/update.service.ts +373 -0
  40. package/src/config/index.ts +1 -30
  41. package/src/config/spaceflow.config.ts +226 -278
  42. package/src/index.ts +11 -1
  43. package/src/locales/en/build.json +22 -0
  44. package/src/locales/en/clear.json +16 -0
  45. package/src/locales/en/commit.json +45 -0
  46. package/src/locales/en/create.json +27 -0
  47. package/src/locales/en/dev.json +5 -0
  48. package/src/locales/en/install.json +71 -0
  49. package/src/locales/en/list.json +8 -0
  50. package/src/locales/en/mcp.json +19 -0
  51. package/src/locales/en/runx.json +13 -0
  52. package/src/locales/en/schema.json +4 -0
  53. package/src/locales/en/setup.json +14 -0
  54. package/src/locales/en/uninstall.json +18 -0
  55. package/src/locales/en/update.json +28 -0
  56. package/src/locales/zh-cn/build.json +22 -0
  57. package/src/locales/zh-cn/clear.json +16 -0
  58. package/src/locales/zh-cn/commit.json +45 -0
  59. package/src/locales/zh-cn/create.json +27 -0
  60. package/src/locales/zh-cn/dev.json +5 -0
  61. package/src/locales/zh-cn/install.json +71 -0
  62. package/src/locales/zh-cn/list.json +8 -0
  63. package/src/locales/zh-cn/mcp.json +19 -0
  64. package/src/locales/zh-cn/runx.json +13 -0
  65. package/src/locales/zh-cn/schema.json +4 -0
  66. package/src/locales/zh-cn/setup.json +14 -0
  67. package/src/locales/zh-cn/uninstall.json +18 -0
  68. package/src/locales/zh-cn/update.json +28 -0
  69. package/src/shared/editor-config/index.ts +2 -21
  70. package/src/shared/llm-proxy/adapters/openai.adapter.ts +3 -1
  71. package/src/shared/package-manager/index.ts +5 -76
  72. package/src/shared/source-utils/index.ts +12 -130
  73. package/src/shared/spaceflow-dir/index.ts +13 -135
  74. package/src/shared/verbose/index.ts +10 -87
  75. package/dist/524.js +0 -9
  76. package/src/config/ci.config.ts +0 -29
  77. package/src/config/config-loader.ts +0 -100
  78. package/src/config/config-reader.service.ts +0 -128
  79. package/src/config/config-reader.ts +0 -75
  80. package/src/config/feishu.config.ts +0 -35
  81. package/src/config/git-provider.config.ts +0 -29
  82. package/src/config/llm.config.ts +0 -110
  83. package/src/config/load-env.ts +0 -15
  84. package/src/config/storage.config.ts +0 -33
  85. package/src/shared/i18n/i18n.ts +0 -112
  86. package/src/shared/i18n/index.ts +0 -1
  87. /package/src/{shared → cli-runtime}/i18n/i18n.spec.ts +0 -0
  88. /package/src/{shared → cli-runtime}/i18n/locale-detect.ts +0 -0
@@ -0,0 +1,373 @@
1
+ import { execSync } from "child_process";
2
+ import { readFile } from "fs/promises";
3
+ import { join } from "path";
4
+ import { existsSync } from "fs";
5
+ import { shouldLog, type VerboseLevel, t } from "@spaceflow/core";
6
+ import { getDependencies } from "@spaceflow/core";
7
+ import type { ExtensionConfig } from "../install/install.service";
8
+
9
+ export interface UpdateOptions {
10
+ name?: string;
11
+ self?: boolean;
12
+ verbose?: VerboseLevel;
13
+ }
14
+
15
+ export class UpdateService {
16
+ protected getPackageManager(): string {
17
+ const cwd = process.cwd();
18
+
19
+ if (existsSync(join(cwd, "pnpm-lock.yaml"))) {
20
+ try {
21
+ execSync("pnpm --version", { stdio: "ignore" });
22
+ return "pnpm";
23
+ } catch {
24
+ // pnpm 不可用
25
+ }
26
+ }
27
+
28
+ if (existsSync(join(cwd, "yarn.lock"))) {
29
+ try {
30
+ execSync("yarn --version", { stdio: "ignore" });
31
+ return "yarn";
32
+ } catch {
33
+ // yarn 不可用
34
+ }
35
+ }
36
+
37
+ return "npm";
38
+ }
39
+
40
+ protected isPnpmWorkspace(): boolean {
41
+ return existsSync(join(process.cwd(), "pnpm-workspace.yaml"));
42
+ }
43
+
44
+ isGitUrl(source: string): boolean {
45
+ return (
46
+ source.startsWith("git@") ||
47
+ (source.startsWith("https://") && source.endsWith(".git")) ||
48
+ source.endsWith(".git")
49
+ );
50
+ }
51
+
52
+ isLocalPath(source: string): boolean {
53
+ return (
54
+ source.startsWith("./") ||
55
+ source.startsWith("../") ||
56
+ source.startsWith("/") ||
57
+ source.startsWith("skills/")
58
+ );
59
+ }
60
+
61
+ getSourceType(source: string): "npm" | "git" | "local" {
62
+ if (this.isLocalPath(source)) return "local";
63
+ if (this.isGitUrl(source)) return "git";
64
+ return "npm";
65
+ }
66
+
67
+ parseExtensionConfig(config: ExtensionConfig): { source: string; ref?: string } {
68
+ if (typeof config === "string") {
69
+ return { source: config };
70
+ }
71
+ return { source: config.source, ref: config.ref };
72
+ }
73
+
74
+ async getLatestNpmVersion(packageName: string): Promise<string | null> {
75
+ try {
76
+ const result = execSync(`npm view ${packageName} version`, {
77
+ encoding: "utf-8",
78
+ stdio: ["pipe", "pipe", "pipe"],
79
+ }).trim();
80
+ return result;
81
+ } catch {
82
+ return null;
83
+ }
84
+ }
85
+
86
+ async getCurrentNpmVersion(packageName: string): Promise<string | null> {
87
+ const packageJsonPath = join(process.cwd(), "package.json");
88
+
89
+ if (!existsSync(packageJsonPath)) {
90
+ return null;
91
+ }
92
+
93
+ try {
94
+ const content = await readFile(packageJsonPath, "utf-8");
95
+ const pkg = JSON.parse(content);
96
+ const version = pkg.dependencies?.[packageName] || pkg.devDependencies?.[packageName];
97
+ if (version) {
98
+ return version.replace(/^[\^~>=<]+/, "");
99
+ }
100
+ return null;
101
+ } catch {
102
+ return null;
103
+ }
104
+ }
105
+
106
+ async updateNpmPackage(packageName: string, verbose: VerboseLevel = 1): Promise<boolean> {
107
+ const pm = this.getPackageManager();
108
+ const latestVersion = await this.getLatestNpmVersion(packageName);
109
+ const currentVersion = await this.getCurrentNpmVersion(packageName);
110
+
111
+ if (!latestVersion) {
112
+ if (shouldLog(verbose, 1)) console.log(t("update:cannotGetLatest", { package: packageName }));
113
+ return false;
114
+ }
115
+
116
+ if (currentVersion === latestVersion) {
117
+ if (shouldLog(verbose, 1))
118
+ console.log(t("update:alreadyLatest", { package: packageName, version: currentVersion }));
119
+ return true;
120
+ }
121
+
122
+ if (shouldLog(verbose, 1)) {
123
+ console.log(
124
+ t("update:versionChange", {
125
+ package: packageName,
126
+ current: currentVersion || t("update:unknown"),
127
+ latest: latestVersion,
128
+ }),
129
+ );
130
+ }
131
+
132
+ let cmd: string;
133
+ if (pm === "pnpm") {
134
+ cmd = this.isPnpmWorkspace()
135
+ ? `pnpm add -wD ${packageName}@latest`
136
+ : `pnpm add -D ${packageName}@latest`;
137
+ } else if (pm === "yarn") {
138
+ cmd = `yarn add -D ${packageName}@latest`;
139
+ } else {
140
+ cmd = `npm install -D ${packageName}@latest`;
141
+ }
142
+
143
+ try {
144
+ execSync(cmd, {
145
+ cwd: process.cwd(),
146
+ stdio: verbose ? "inherit" : "pipe",
147
+ });
148
+ return true;
149
+ } catch (error) {
150
+ if (shouldLog(verbose, 1)) {
151
+ console.error(
152
+ t("update:npmUpdateFailed", { package: packageName }),
153
+ error instanceof Error ? error.message : error,
154
+ );
155
+ }
156
+ return false;
157
+ }
158
+ }
159
+
160
+ async updateGitRepo(depPath: string, name: string, verbose: VerboseLevel = 1): Promise<boolean> {
161
+ const gitDir = join(depPath, ".git");
162
+
163
+ if (!existsSync(gitDir)) {
164
+ if (shouldLog(verbose, 1)) {
165
+ console.log(t("update:notGitRepo", { name }));
166
+ console.log(t("update:reinstallHint"));
167
+ }
168
+ return false;
169
+ }
170
+
171
+ try {
172
+ if (shouldLog(verbose, 1)) console.log(t("update:pullingLatest"));
173
+ execSync("git pull", {
174
+ cwd: depPath,
175
+ stdio: verbose ? "inherit" : "pipe",
176
+ });
177
+ return true;
178
+ } catch (error) {
179
+ if (shouldLog(verbose, 1)) {
180
+ console.error(
181
+ t("update:gitUpdateFailed", { name }),
182
+ error instanceof Error ? error.message : error,
183
+ );
184
+ }
185
+ return false;
186
+ }
187
+ }
188
+
189
+ /**
190
+ * 检测 CLI 的安装方式
191
+ * 返回: { isGlobal: boolean, pm: 'pnpm' | 'npm' | 'yarn', cwd?: string }
192
+ */
193
+ protected detectCliInstallation(): { isGlobal: boolean; pm: string; cwd?: string } {
194
+ try {
195
+ // 获取当前执行的 CLI 路径
196
+ const cliPath = process.argv[1];
197
+
198
+ // 检查是否在 node_modules 中(本地安装)
199
+ if (cliPath.includes("node_modules")) {
200
+ // 提取项目根目录(node_modules 的父目录)
201
+ const nodeModulesIndex = cliPath.indexOf("node_modules");
202
+ const projectRoot = cliPath.substring(0, nodeModulesIndex - 1);
203
+
204
+ // 检测项目使用的包管理器
205
+ let pm = "npm";
206
+ if (existsSync(join(projectRoot, "pnpm-lock.yaml"))) {
207
+ pm = "pnpm";
208
+ } else if (existsSync(join(projectRoot, "yarn.lock"))) {
209
+ pm = "yarn";
210
+ }
211
+
212
+ return { isGlobal: false, pm, cwd: projectRoot };
213
+ }
214
+
215
+ // 检查是否是全局安装
216
+ // 尝试检测 pnpm 全局
217
+ try {
218
+ const pnpmGlobalDir = execSync("pnpm root -g", {
219
+ encoding: "utf-8",
220
+ stdio: ["pipe", "pipe", "pipe"],
221
+ }).trim();
222
+ if (cliPath.startsWith(pnpmGlobalDir) || cliPath.includes(".pnpm")) {
223
+ return { isGlobal: true, pm: "pnpm" };
224
+ }
225
+ } catch {
226
+ // pnpm 不可用
227
+ }
228
+
229
+ // 尝试检测 npm 全局
230
+ try {
231
+ const npmGlobalDir = execSync("npm root -g", {
232
+ encoding: "utf-8",
233
+ stdio: ["pipe", "pipe", "pipe"],
234
+ }).trim();
235
+ if (cliPath.startsWith(npmGlobalDir)) {
236
+ return { isGlobal: true, pm: "npm" };
237
+ }
238
+ } catch {
239
+ // npm 不可用
240
+ }
241
+
242
+ // 默认认为是全局安装
243
+ return { isGlobal: true, pm: "npm" };
244
+ } catch {
245
+ return { isGlobal: true, pm: "npm" };
246
+ }
247
+ }
248
+
249
+ async updateSelf(verbose: VerboseLevel = 1): Promise<boolean> {
250
+ if (shouldLog(verbose, 1)) console.log(t("update:updatingCli"));
251
+
252
+ const cliPackageName = "@spaceflow/cli";
253
+ const installation = this.detectCliInstallation();
254
+
255
+ if (shouldLog(verbose, 1)) {
256
+ console.log(
257
+ t("update:installMethod", {
258
+ method: installation.isGlobal ? t("update:installGlobal") : t("update:installLocal"),
259
+ pm: installation.pm,
260
+ }),
261
+ );
262
+ }
263
+
264
+ try {
265
+ if (installation.isGlobal) {
266
+ // 全局安装:使用对应包管理器的全局更新命令
267
+ const cmd =
268
+ installation.pm === "pnpm"
269
+ ? `pnpm update -g ${cliPackageName}`
270
+ : installation.pm === "yarn"
271
+ ? `yarn global upgrade ${cliPackageName}`
272
+ : `npm update -g ${cliPackageName}`;
273
+
274
+ if (shouldLog(verbose, 1)) console.log(t("update:executing", { cmd }));
275
+ execSync(cmd, { stdio: verbose ? "inherit" : "pipe" });
276
+ } else {
277
+ // 本地安装:在项目目录中更新
278
+ const cwd = installation.cwd || process.cwd();
279
+ let cmd: string;
280
+
281
+ if (installation.pm === "pnpm") {
282
+ const isPnpmWorkspace = existsSync(join(cwd, "pnpm-workspace.yaml"));
283
+ cmd = isPnpmWorkspace
284
+ ? `pnpm add -wD ${cliPackageName}@latest`
285
+ : `pnpm add -D ${cliPackageName}@latest`;
286
+ } else if (installation.pm === "yarn") {
287
+ cmd = `yarn add -D ${cliPackageName}@latest`;
288
+ } else {
289
+ cmd = `npm install -D ${cliPackageName}@latest`;
290
+ }
291
+
292
+ if (shouldLog(verbose, 1)) console.log(t("update:executing", { cmd }));
293
+ execSync(cmd, { cwd, stdio: verbose ? "inherit" : "pipe" });
294
+ }
295
+
296
+ if (shouldLog(verbose, 1)) console.log(t("update:cliUpdateDone"));
297
+ return true;
298
+ } catch (error) {
299
+ if (shouldLog(verbose, 1)) {
300
+ console.error(t("update:cliUpdateFailed"), error instanceof Error ? error.message : error);
301
+ }
302
+ return false;
303
+ }
304
+ }
305
+
306
+ async updateDependency(name: string, verbose: VerboseLevel = 1): Promise<boolean> {
307
+ const cwd = process.cwd();
308
+ const dependencies = getDependencies(cwd);
309
+
310
+ if (!dependencies[name]) {
311
+ if (shouldLog(verbose, 1)) console.log(t("update:depNotFound", { name }));
312
+ return false;
313
+ }
314
+
315
+ const { source } = this.parseExtensionConfig(dependencies[name]);
316
+ const sourceType = this.getSourceType(source);
317
+
318
+ if (shouldLog(verbose, 1)) console.log(t("update:updating", { name }));
319
+
320
+ if (sourceType === "npm") {
321
+ return this.updateNpmPackage(source, verbose);
322
+ } else if (sourceType === "git") {
323
+ const depPath = join(cwd, ".spaceflow", "deps", name);
324
+ return this.updateGitRepo(depPath, name, verbose);
325
+ } else {
326
+ if (shouldLog(verbose, 1)) console.log(t("update:localNoUpdate"));
327
+ return true;
328
+ }
329
+ }
330
+
331
+ async updateAll(verbose: VerboseLevel = 1): Promise<void> {
332
+ const cwd = process.cwd();
333
+ const dependencies = getDependencies(cwd);
334
+
335
+ if (Object.keys(dependencies).length === 0) {
336
+ if (shouldLog(verbose, 1)) console.log(t("update:noDeps"));
337
+ return;
338
+ }
339
+
340
+ if (shouldLog(verbose, 1)) {
341
+ console.log(t("update:updatingAll", { count: Object.keys(dependencies).length }));
342
+ }
343
+
344
+ let successCount = 0;
345
+ let failCount = 0;
346
+
347
+ for (const [name, config] of Object.entries(dependencies)) {
348
+ const { source } = this.parseExtensionConfig(config);
349
+ const sourceType = this.getSourceType(source);
350
+
351
+ console.log(`\n📦 ${name}`);
352
+
353
+ let success = false;
354
+ if (sourceType === "npm") {
355
+ success = await this.updateNpmPackage(source, verbose);
356
+ } else if (sourceType === "git") {
357
+ const depPath = join(cwd, ".spaceflow", "deps", name);
358
+ success = await this.updateGitRepo(depPath, name, verbose);
359
+ } else {
360
+ if (shouldLog(verbose, 1)) console.log(t("update:localNoUpdate"));
361
+ success = true;
362
+ }
363
+
364
+ if (success) {
365
+ successCount++;
366
+ } else {
367
+ failCount++;
368
+ }
369
+ }
370
+
371
+ console.log("\n" + t("update:updateComplete", { success: successCount, fail: failCount }));
372
+ }
373
+ }
@@ -1,32 +1,3 @@
1
- export * from "./git-provider.config";
2
- export * from "./ci.config";
3
- export * from "./llm.config";
4
- export * from "./feishu.config";
5
- export * from "./storage.config";
6
1
  export * from "./spaceflow.config";
7
- export * from "./config-reader.service";
8
2
  export * from "./schema-generator.service";
9
- export * from "./config-loader";
10
-
11
- // 新版配置系统(不依赖 NestJS)
12
- export { ConfigReader } from "./config-reader";
13
- export { loadEnvFiles } from "./load-env";
14
-
15
- import { gitProviderConfig } from "./git-provider.config";
16
- import { ciConfig } from "./ci.config";
17
- import { llmConfig } from "./llm.config";
18
- import { feishuConfig } from "./feishu.config";
19
- import { storageConfig } from "./storage.config";
20
- import { spaceflowConfig } from "./spaceflow.config";
21
-
22
- /**
23
- * 所有配置加载器
24
- */
25
- export const configLoaders = [
26
- gitProviderConfig,
27
- ciConfig,
28
- llmConfig,
29
- feishuConfig,
30
- storageConfig,
31
- spaceflowConfig,
32
- ];
3
+ export { loadEnvFiles, getEnvFilePaths } from "@spaceflow/shared";