@spaceflow/core 0.1.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.
Files changed (116) hide show
  1. package/CHANGELOG.md +1176 -0
  2. package/README.md +105 -0
  3. package/nest-cli.json +10 -0
  4. package/package.json +128 -0
  5. package/rspack.config.mjs +62 -0
  6. package/src/__mocks__/@opencode-ai/sdk.js +9 -0
  7. package/src/__mocks__/c12.ts +3 -0
  8. package/src/app.module.ts +18 -0
  9. package/src/config/ci.config.ts +29 -0
  10. package/src/config/config-loader.ts +101 -0
  11. package/src/config/config-reader.module.ts +16 -0
  12. package/src/config/config-reader.service.ts +133 -0
  13. package/src/config/feishu.config.ts +35 -0
  14. package/src/config/git-provider.config.ts +29 -0
  15. package/src/config/index.ts +29 -0
  16. package/src/config/llm.config.ts +110 -0
  17. package/src/config/schema-generator.service.ts +129 -0
  18. package/src/config/spaceflow.config.ts +292 -0
  19. package/src/config/storage.config.ts +33 -0
  20. package/src/extension-system/extension.interface.ts +221 -0
  21. package/src/extension-system/index.ts +1 -0
  22. package/src/index.ts +80 -0
  23. package/src/locales/en/translation.json +11 -0
  24. package/src/locales/zh-cn/translation.json +11 -0
  25. package/src/shared/claude-setup/claude-setup.module.ts +8 -0
  26. package/src/shared/claude-setup/claude-setup.service.ts +131 -0
  27. package/src/shared/claude-setup/index.ts +2 -0
  28. package/src/shared/editor-config/index.ts +23 -0
  29. package/src/shared/feishu-sdk/feishu-sdk.module.ts +77 -0
  30. package/src/shared/feishu-sdk/feishu-sdk.service.ts +130 -0
  31. package/src/shared/feishu-sdk/fieshu-card.service.ts +139 -0
  32. package/src/shared/feishu-sdk/index.ts +4 -0
  33. package/src/shared/feishu-sdk/types/card-action.ts +132 -0
  34. package/src/shared/feishu-sdk/types/card.ts +64 -0
  35. package/src/shared/feishu-sdk/types/common.ts +22 -0
  36. package/src/shared/feishu-sdk/types/index.ts +46 -0
  37. package/src/shared/feishu-sdk/types/message.ts +35 -0
  38. package/src/shared/feishu-sdk/types/module.ts +21 -0
  39. package/src/shared/feishu-sdk/types/user.ts +77 -0
  40. package/src/shared/git-provider/adapters/gitea.adapter.spec.ts +473 -0
  41. package/src/shared/git-provider/adapters/gitea.adapter.ts +499 -0
  42. package/src/shared/git-provider/adapters/github.adapter.spec.ts +341 -0
  43. package/src/shared/git-provider/adapters/github.adapter.ts +830 -0
  44. package/src/shared/git-provider/adapters/gitlab.adapter.ts +839 -0
  45. package/src/shared/git-provider/adapters/index.ts +3 -0
  46. package/src/shared/git-provider/detect-provider.spec.ts +195 -0
  47. package/src/shared/git-provider/detect-provider.ts +112 -0
  48. package/src/shared/git-provider/git-provider.interface.ts +188 -0
  49. package/src/shared/git-provider/git-provider.module.ts +73 -0
  50. package/src/shared/git-provider/git-provider.service.spec.ts +282 -0
  51. package/src/shared/git-provider/git-provider.service.ts +309 -0
  52. package/src/shared/git-provider/index.ts +7 -0
  53. package/src/shared/git-provider/parse-repo-url.spec.ts +221 -0
  54. package/src/shared/git-provider/parse-repo-url.ts +155 -0
  55. package/src/shared/git-provider/types.ts +434 -0
  56. package/src/shared/git-sdk/git-sdk-diff.utils.spec.ts +344 -0
  57. package/src/shared/git-sdk/git-sdk-diff.utils.ts +151 -0
  58. package/src/shared/git-sdk/git-sdk.module.ts +8 -0
  59. package/src/shared/git-sdk/git-sdk.service.ts +235 -0
  60. package/src/shared/git-sdk/git-sdk.types.ts +25 -0
  61. package/src/shared/git-sdk/index.ts +4 -0
  62. package/src/shared/i18n/i18n.spec.ts +96 -0
  63. package/src/shared/i18n/i18n.ts +86 -0
  64. package/src/shared/i18n/index.ts +1 -0
  65. package/src/shared/i18n/locale-detect.ts +134 -0
  66. package/src/shared/llm-jsonput/index.ts +94 -0
  67. package/src/shared/llm-jsonput/types.ts +17 -0
  68. package/src/shared/llm-proxy/adapters/claude-code.adapter.spec.ts +131 -0
  69. package/src/shared/llm-proxy/adapters/claude-code.adapter.ts +208 -0
  70. package/src/shared/llm-proxy/adapters/index.ts +4 -0
  71. package/src/shared/llm-proxy/adapters/llm-adapter.interface.ts +23 -0
  72. package/src/shared/llm-proxy/adapters/open-code.adapter.ts +342 -0
  73. package/src/shared/llm-proxy/adapters/openai.adapter.spec.ts +215 -0
  74. package/src/shared/llm-proxy/adapters/openai.adapter.ts +153 -0
  75. package/src/shared/llm-proxy/index.ts +6 -0
  76. package/src/shared/llm-proxy/interfaces/config.interface.ts +32 -0
  77. package/src/shared/llm-proxy/interfaces/index.ts +4 -0
  78. package/src/shared/llm-proxy/interfaces/message.interface.ts +48 -0
  79. package/src/shared/llm-proxy/interfaces/session.interface.ts +28 -0
  80. package/src/shared/llm-proxy/llm-proxy.module.ts +140 -0
  81. package/src/shared/llm-proxy/llm-proxy.service.spec.ts +303 -0
  82. package/src/shared/llm-proxy/llm-proxy.service.ts +132 -0
  83. package/src/shared/llm-proxy/llm-session.spec.ts +111 -0
  84. package/src/shared/llm-proxy/llm-session.ts +109 -0
  85. package/src/shared/llm-proxy/stream-logger.ts +97 -0
  86. package/src/shared/logger/index.ts +11 -0
  87. package/src/shared/logger/logger.interface.ts +93 -0
  88. package/src/shared/logger/logger.spec.ts +178 -0
  89. package/src/shared/logger/logger.ts +175 -0
  90. package/src/shared/logger/renderers/plain.renderer.ts +116 -0
  91. package/src/shared/logger/renderers/tui.renderer.ts +162 -0
  92. package/src/shared/mcp/index.ts +332 -0
  93. package/src/shared/output/index.ts +2 -0
  94. package/src/shared/output/output.module.ts +9 -0
  95. package/src/shared/output/output.service.ts +97 -0
  96. package/src/shared/package-manager/index.ts +115 -0
  97. package/src/shared/parallel/index.ts +1 -0
  98. package/src/shared/parallel/parallel-executor.ts +169 -0
  99. package/src/shared/rspack-config/index.ts +1 -0
  100. package/src/shared/rspack-config/rspack-config.ts +157 -0
  101. package/src/shared/source-utils/index.ts +130 -0
  102. package/src/shared/spaceflow-dir/index.ts +158 -0
  103. package/src/shared/storage/adapters/file.adapter.ts +113 -0
  104. package/src/shared/storage/adapters/index.ts +3 -0
  105. package/src/shared/storage/adapters/memory.adapter.ts +50 -0
  106. package/src/shared/storage/adapters/storage-adapter.interface.ts +48 -0
  107. package/src/shared/storage/index.ts +4 -0
  108. package/src/shared/storage/storage.module.ts +150 -0
  109. package/src/shared/storage/storage.service.ts +293 -0
  110. package/src/shared/storage/types.ts +51 -0
  111. package/src/shared/verbose/index.ts +73 -0
  112. package/test/app.e2e-spec.ts +22 -0
  113. package/tsconfig.build.json +4 -0
  114. package/tsconfig.json +25 -0
  115. package/tsconfig.skill.json +18 -0
  116. package/vitest.config.ts +58 -0
@@ -0,0 +1,221 @@
1
+ import { Type, DynamicModule } from "@nestjs/common";
2
+
3
+ /** .spaceflow 目录名 */
4
+ export const SPACEFLOW_DIR = ".spaceflow";
5
+
6
+ /** package.json 文件名 */
7
+ export const PACKAGE_JSON = "package.json";
8
+
9
+ /**
10
+ * Extension 元数据
11
+ */
12
+ export interface SpaceflowExtensionMetadata {
13
+ /** Extension 名称 */
14
+ name: string;
15
+ /** 提供的命令列表 */
16
+ commands: string[];
17
+ /** 对应 spaceflow.json 中的配置 key(可选) */
18
+ configKey?: string;
19
+ /** 依赖的其他 Extension 配置 key 列表,读取其他 Extension 配置前必须在此声明 */
20
+ configDependencies?: string[];
21
+ /** 配置 schema 工厂函数,返回 zod schema,用于验证配置和生成 JSON Schema */
22
+ configSchema?: () => unknown;
23
+ /** Extension 版本 */
24
+ version?: string;
25
+ /** Extension 描述 */
26
+ description?: string;
27
+ }
28
+
29
+ /**
30
+ * Extension 模块类型,支持静态模块或动态模块
31
+ */
32
+ export type ExtensionModuleType = Type<any> | DynamicModule;
33
+
34
+ /**
35
+ * Extension 接口
36
+ */
37
+ export interface SpaceflowExtension {
38
+ /** 获取 Extension 元数据 */
39
+ getMetadata(): SpaceflowExtensionMetadata;
40
+ /**
41
+ * 获取 NestJS Module
42
+ * 可以返回静态 Module 类或 DynamicModule
43
+ * 如果需要动态配置,推荐返回 DynamicModule
44
+ */
45
+ getModule(): ExtensionModuleType;
46
+ }
47
+
48
+ /**
49
+ * Extension 类静态接口
50
+ */
51
+ export interface SpaceflowExtensionConstructor {
52
+ new (): SpaceflowExtension;
53
+ }
54
+
55
+ /**
56
+ * Extension 入口导出格式
57
+ */
58
+ export type SpaceflowExtensionExport =
59
+ | SpaceflowExtensionConstructor
60
+ | {
61
+ default: SpaceflowExtensionConstructor;
62
+ };
63
+
64
+ /**
65
+ * 已加载的 Extension 信息
66
+ */
67
+ export interface LoadedExtension {
68
+ /** Extension 名称 */
69
+ name: string;
70
+ /** Extension 来源(npm 包名) */
71
+ source: string;
72
+ /** NestJS 模块(静态或动态) */
73
+ module: ExtensionModuleType;
74
+ /** 包的完整导出(用于 MCP 服务发现) */
75
+ exports?: Record<string, unknown>;
76
+ /** 提供的命令列表 */
77
+ commands: string[];
78
+ /** 配置 key */
79
+ configKey?: string;
80
+ /** 依赖的其他 Extension 配置 key 列表 */
81
+ configDependencies?: string[];
82
+ /** 配置 schema 工厂函数 */
83
+ configSchema?: () => unknown;
84
+ /** Extension 版本 */
85
+ version?: string;
86
+ /** Extension 描述 */
87
+ description?: string;
88
+ }
89
+
90
+ /**
91
+ * .spaceflow/package.json 中的 dependencies
92
+ */
93
+ export type ExtensionDependencies = Record<string, string>;
94
+
95
+ /**
96
+ * Spaceflow 导出类型
97
+ * - flow: 子命令(默认),需要构建,注册为 CLI 子命令
98
+ * - command: 编辑器命令,复制到 .claude/commands/ 等目录
99
+ * - skill: 技能包,复制到 .claude/skills/ 等目录
100
+ * - mcp: MCP Server,注册到编辑器的 mcp.json 配置
101
+ */
102
+ export type SpaceflowExportType = "flow" | "command" | "skill" | "mcp";
103
+
104
+ /**
105
+ * MCP Server 配置
106
+ */
107
+ export interface McpServerConfig {
108
+ /** 启动命令,如 "node", "python" */
109
+ command: string;
110
+ /** 启动参数,如 ["dist/index.js"] */
111
+ args?: string[];
112
+ /** 需要的环境变量名列表,安装时会提示用户配置 */
113
+ env?: string[];
114
+ }
115
+
116
+ /**
117
+ * 单个导出项配置
118
+ */
119
+ export interface SpaceflowExportConfig {
120
+ /** 导出类型,默认为 flow */
121
+ type?: SpaceflowExportType;
122
+ /** 入口路径,相对于包根目录 */
123
+ entry: string;
124
+ /** 描述(可选) */
125
+ description?: string;
126
+ /** MCP Server 配置(仅 type 为 mcp 时有效) */
127
+ mcp?: McpServerConfig;
128
+ }
129
+
130
+ /**
131
+ * package.json 中的 spaceflow 配置
132
+ *
133
+ * 完整格式:
134
+ * ```json
135
+ * "spaceflow": {
136
+ * "exports": {
137
+ * "review": { "type": "flow", "entry": "." },
138
+ * "review-rules": { "type": "skill", "entry": "./skills" },
139
+ * "my-mcp": { "type": "mcp", "entry": ".", "mcp": { "command": "node", "args": ["dist/index.js"] } }
140
+ * }
141
+ * }
142
+ * ```
143
+ *
144
+ * 简化格式(单导出):
145
+ * ```json
146
+ * "spaceflow": {
147
+ * "type": "mcp",
148
+ * "entry": ".",
149
+ * "mcp": { "command": "node", "args": ["dist/index.js"] }
150
+ * }
151
+ * ```
152
+ */
153
+ export interface SpaceflowPackageConfig {
154
+ /** 多导出配置 */
155
+ exports?: Record<string, SpaceflowExportConfig>;
156
+ /** 简化格式:导出类型 */
157
+ type?: SpaceflowExportType;
158
+ /** 简化格式:入口路径 */
159
+ entry?: string;
160
+ /** 简化格式:描述 */
161
+ description?: string;
162
+ /** 简化格式:MCP 配置 */
163
+ mcp?: McpServerConfig;
164
+ }
165
+
166
+ /**
167
+ * 解析后的导出项
168
+ */
169
+ export interface ResolvedSpaceflowExport {
170
+ /** 导出名称 */
171
+ name: string;
172
+ /** 导出类型 */
173
+ type: SpaceflowExportType;
174
+ /** 入口路径(绝对路径) */
175
+ entry: string;
176
+ /** 描述 */
177
+ description?: string;
178
+ /** MCP 配置(仅 type 为 mcp 时有效) */
179
+ mcp?: McpServerConfig;
180
+ }
181
+
182
+ /**
183
+ * 解析 spaceflow 配置,返回所有导出项
184
+ */
185
+ export function resolveSpaceflowConfig(
186
+ config: SpaceflowPackageConfig | undefined,
187
+ packageName: string,
188
+ packagePath: string,
189
+ ): ResolvedSpaceflowExport[] {
190
+ const { join } = require("path");
191
+
192
+ if (!config) {
193
+ return [];
194
+ }
195
+
196
+ // 完整格式:有 exports 字段
197
+ if (config.exports) {
198
+ return Object.entries(config.exports).map(([name, exportConfig]) => ({
199
+ name,
200
+ type: exportConfig.type || "flow",
201
+ entry: join(packagePath, exportConfig.entry),
202
+ description: exportConfig.description,
203
+ mcp: exportConfig.mcp,
204
+ }));
205
+ }
206
+
207
+ // 简化格式:直接有 type/entry 字段
208
+ if (config.entry) {
209
+ return [
210
+ {
211
+ name: packageName,
212
+ type: config.type || "flow",
213
+ entry: join(packagePath, config.entry),
214
+ description: config.description,
215
+ mcp: config.mcp,
216
+ },
217
+ ];
218
+ }
219
+
220
+ return [];
221
+ }
@@ -0,0 +1 @@
1
+ export * from "./extension.interface";
package/src/index.ts ADDED
@@ -0,0 +1,80 @@
1
+ // ============ 插件系统 ============
2
+ export * from "./extension-system";
3
+
4
+ // ============ 基础能力模块 ============
5
+ // Git Provider - 多平台 Git 托管 API 操作(GitHub、Gitea、GitLab)
6
+ export * from "./shared/git-provider";
7
+
8
+ // Git SDK - Git 命令操作
9
+ export * from "./shared/git-sdk";
10
+
11
+ // LLM Proxy - 多 LLM 适配器
12
+ export * from "./shared/llm-proxy";
13
+
14
+ // Feishu SDK - 飞书 API 操作
15
+ export * from "./shared/feishu-sdk";
16
+
17
+ // Storage - 存储服务
18
+ export * from "./shared/storage";
19
+
20
+ // Claude Setup - Claude Agent 配置
21
+ export * from "./shared/claude-setup";
22
+
23
+ // Parallel - 并行执行工具
24
+ export * from "./shared/parallel";
25
+
26
+ // Output - 输出服务
27
+ export * from "./shared/output";
28
+
29
+ // Verbose - 日志级别
30
+ export * from "./shared/verbose";
31
+
32
+ // Editor Config - 编辑器配置
33
+ export * from "./shared/editor-config";
34
+
35
+ // LLM JsonPut - JSON 结构化输出
36
+ export * from "./shared/llm-jsonput";
37
+
38
+ // Source Utils - 源类型判断工具
39
+ export * from "./shared/source-utils";
40
+
41
+ // Package Manager - 包管理器检测
42
+ export * from "./shared/package-manager";
43
+
44
+ // Spaceflow Dir - .spaceflow 目录管理
45
+ export * from "./shared/spaceflow-dir";
46
+
47
+ // Rspack Config - Rspack 配置工具
48
+ export * from "./shared/rspack-config";
49
+
50
+ // MCP - Model Context Protocol 支持
51
+ export * from "./shared/mcp";
52
+
53
+ // I18n - 国际化
54
+ export * from "./shared/i18n";
55
+
56
+ // Logger - 全局日志工具
57
+ export * from "./shared/logger";
58
+
59
+ // ============ 配置相关 ============
60
+ export * from "./config";
61
+
62
+ // ============ NestJS 重导出 ============
63
+ export { Command, CommandRunner, Option, SubCommand } from "nest-commander";
64
+ export { Module, Injectable, Inject, Global } from "@nestjs/common";
65
+ export { ConfigModule, ConfigService } from "@nestjs/config";
66
+
67
+ // ============ Zod 重导出 ============
68
+ export { z } from "zod";
69
+
70
+ // ============ Swagger DTO 重导出 ============
71
+ export { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
72
+ export {
73
+ IsString,
74
+ IsNumber,
75
+ IsBoolean,
76
+ IsOptional,
77
+ IsArray,
78
+ IsEnum,
79
+ IsNotEmpty,
80
+ } from "class-validator";
@@ -0,0 +1,11 @@
1
+ {
2
+ "common.executionFailed": "Execution failed: {{error}}",
3
+ "common.stackTrace": "\nStack trace:\n{{stack}}",
4
+ "common.options.dryRun": "Only print actions without executing",
5
+ "common.options.verbose": "Show verbose logs (-v: basic, -vv: detailed)",
6
+ "common.options.verboseDebug": "Show verbose logs (-v: basic, -vv: detailed, -vvv: debug)",
7
+ "common.options.ci": "Run in CI environment",
8
+ "config.parseWarning": "Warning: unable to parse config file {{path}}",
9
+ "config.validationFailed": "Spaceflow config validation failed:\n{{errors}}",
10
+ "extensionLoader.loadFailed": "⚠️ Failed to load Extension {{name}}: {{error}}"
11
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "common.executionFailed": "执行失败: {{error}}",
3
+ "common.stackTrace": "\n堆栈信息:\n{{stack}}",
4
+ "common.options.dryRun": "仅打印将要执行的操作,不实际执行",
5
+ "common.options.verbose": "显示详细日志 (-v: 基本日志, -vv: 详细日志)",
6
+ "common.options.verboseDebug": "显示详细日志 (-v: 基本日志, -vv: 详细日志, -vvv: 调试日志)",
7
+ "common.options.ci": "是否在 CI 环境中运行",
8
+ "config.parseWarning": "警告: 无法解析配置文件 {{path}}",
9
+ "config.validationFailed": "Spaceflow 配置验证失败:\n{{errors}}",
10
+ "extensionLoader.loadFailed": "⚠️ 加载 Extension {{name}} 失败: {{error}}"
11
+ }
@@ -0,0 +1,8 @@
1
+ import { Module } from "@nestjs/common";
2
+ import { ClaudeSetupService } from "./claude-setup.service";
3
+
4
+ @Module({
5
+ providers: [ClaudeSetupService],
6
+ exports: [ClaudeSetupService],
7
+ })
8
+ export class ClaudeSetupModule {}
@@ -0,0 +1,131 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import { ConfigService } from "@nestjs/config";
3
+ import { readFile, writeFile, mkdir, copyFile, unlink } from "fs/promises";
4
+ import { join } from "path";
5
+ import { homedir } from "os";
6
+ import { LlmConfig } from "../../config";
7
+
8
+ import { shouldLog, type VerboseLevel } from "../verbose";
9
+
10
+ @Injectable()
11
+ export class ClaudeSetupService {
12
+ constructor(protected readonly configService: ConfigService) {}
13
+
14
+ private getPaths() {
15
+ const claudeDir = join(homedir(), ".claude");
16
+ return {
17
+ claudeDir,
18
+ settingsPath: join(claudeDir, "settings.json"),
19
+ settingsBackupPath: join(claudeDir, "settings.json.bak"),
20
+ claudeJsonPath: join(homedir(), ".claude.json"),
21
+ claudeJsonBackupPath: join(homedir(), ".claude.json.bak"),
22
+ };
23
+ }
24
+
25
+ async backup(): Promise<void> {
26
+ const paths = this.getPaths();
27
+ try {
28
+ await copyFile(paths.settingsPath, paths.settingsBackupPath);
29
+ } catch (e) {
30
+ // 忽略文件不存在的情况
31
+ }
32
+ try {
33
+ await copyFile(paths.claudeJsonPath, paths.claudeJsonBackupPath);
34
+ } catch (e) {
35
+ // 忽略文件不存在的情况
36
+ }
37
+ }
38
+
39
+ async restore(): Promise<void> {
40
+ const paths = this.getPaths();
41
+ try {
42
+ await copyFile(paths.settingsBackupPath, paths.settingsPath);
43
+ await unlink(paths.settingsBackupPath);
44
+ } catch (e) {
45
+ // 忽略备份文件不存在的情况
46
+ }
47
+ try {
48
+ await copyFile(paths.claudeJsonBackupPath, paths.claudeJsonPath);
49
+ await unlink(paths.claudeJsonBackupPath);
50
+ } catch (e) {
51
+ // 忽略备份文件不存在的情况
52
+ }
53
+ }
54
+
55
+ /**
56
+ * 使用临时配置执行操作
57
+ * 自动备份现有配置,执行完成后恢复
58
+ */
59
+ async withTemporaryConfig<T>(fn: () => Promise<T>, verbose?: VerboseLevel): Promise<T> {
60
+ await this.backup();
61
+ try {
62
+ await this.configure(verbose);
63
+ return await fn();
64
+ } finally {
65
+ await this.restore();
66
+ }
67
+ }
68
+
69
+ async configure(verbose?: VerboseLevel): Promise<void> {
70
+ const { claudeDir, settingsPath, claudeJsonPath } = this.getPaths();
71
+
72
+ const llmConf = this.configService.get<LlmConfig>("llm");
73
+ const claudeCode = llmConf?.claudeCode;
74
+
75
+ if (!claudeCode) {
76
+ if (shouldLog(verbose, 1)) {
77
+ console.log("未配置 claude 设置,跳过");
78
+ }
79
+ return;
80
+ }
81
+
82
+ try {
83
+ await mkdir(claudeDir, { recursive: true });
84
+ } catch {
85
+ // ignore if exists
86
+ }
87
+
88
+ let existingSettings = {};
89
+ try {
90
+ const content = await readFile(settingsPath, "utf-8");
91
+ existingSettings = JSON.parse(content);
92
+ } catch {
93
+ // file doesn't exist or invalid JSON
94
+ }
95
+
96
+ const existing = existingSettings as Record<string, Record<string, unknown>>;
97
+ const env: Record<string, string> = { ...(existing.env as Record<string, string>) };
98
+ if (claudeCode.baseUrl) env.ANTHROPIC_BASE_URL = claudeCode.baseUrl;
99
+ if (claudeCode.authToken) {
100
+ env.ANTHROPIC_AUTH_TOKEN = claudeCode.authToken;
101
+ } else {
102
+ throw new Error("未配置 claudeCode.authToken 设置");
103
+ }
104
+ if (claudeCode.model) env.ANTHROPIC_MODEL = claudeCode.model;
105
+
106
+ const mergedSettings = {
107
+ ...existingSettings,
108
+ env,
109
+ };
110
+
111
+ await writeFile(settingsPath, JSON.stringify(mergedSettings, null, 2), "utf-8");
112
+ if (shouldLog(verbose, 1)) {
113
+ console.log(`✅ 已写入 ${settingsPath}`);
114
+ }
115
+
116
+ if (claudeCode.hasCompletedOnboarding !== undefined) {
117
+ let claudeJson: Record<string, unknown> = {};
118
+ try {
119
+ const content = await readFile(claudeJsonPath, "utf-8");
120
+ claudeJson = JSON.parse(content);
121
+ } catch {
122
+ // file doesn't exist or invalid JSON
123
+ }
124
+ claudeJson.hasCompletedOnboarding = claudeCode.hasCompletedOnboarding;
125
+ await writeFile(claudeJsonPath, JSON.stringify(claudeJson, null, 2), "utf-8");
126
+ if (shouldLog(verbose, 1)) {
127
+ console.log(`✅ 已写入 ${claudeJsonPath}`);
128
+ }
129
+ }
130
+ }
131
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./claude-setup.service";
2
+ export * from "./claude-setup.module";
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 编辑器配置目录映射
3
+ * key: 编辑器名称(用于配置文件)
4
+ * value: 编辑器配置目录名(以 . 开头)
5
+ */
6
+ export const EDITOR_DIR_MAPPING: Record<string, string> = {
7
+ claudeCode: ".claude",
8
+ windsurf: ".windsurf",
9
+ cursor: ".cursor",
10
+ opencode: ".opencode",
11
+ };
12
+
13
+ /**
14
+ * 默认支持的编辑器
15
+ */
16
+ export const DEFAULT_EDITOR = "claudeCode";
17
+
18
+ /**
19
+ * 根据编辑器名称获取配置目录名
20
+ */
21
+ export function getEditorDirName(editor: string): string {
22
+ return EDITOR_DIR_MAPPING[editor] || `.${editor}`;
23
+ }
@@ -0,0 +1,77 @@
1
+ import { DynamicModule, Module } from "@nestjs/common";
2
+ import { ConfigModule, ConfigService } from "@nestjs/config";
3
+ import { EventEmitterModule } from "@nestjs/event-emitter";
4
+ import { FeishuSdkService } from "./feishu-sdk.service";
5
+ import { FeishuCardService } from "./fieshu-card.service";
6
+ import { FeishuModuleOptions, FeishuModuleAsyncOptions, FEISHU_MODULE_OPTIONS } from "./types";
7
+ import { feishuConfig, FeishuConfig } from "../../config";
8
+
9
+ @Module({})
10
+ export class FeishuSdkModule {
11
+ /**
12
+ * 同步注册模块
13
+ */
14
+ static forRoot(options: FeishuModuleOptions): DynamicModule {
15
+ return {
16
+ module: FeishuSdkModule,
17
+ imports: [EventEmitterModule.forRoot()],
18
+ providers: [
19
+ {
20
+ provide: FEISHU_MODULE_OPTIONS,
21
+ useValue: options,
22
+ },
23
+ FeishuSdkService,
24
+ FeishuCardService,
25
+ ],
26
+ exports: [FeishuSdkService, FeishuCardService],
27
+ };
28
+ }
29
+
30
+ /**
31
+ * 异步注册模块 - 支持从环境变量等动态获取配置
32
+ */
33
+ static forRootAsync(options: FeishuModuleAsyncOptions): DynamicModule {
34
+ return {
35
+ module: FeishuSdkModule,
36
+ imports: [EventEmitterModule.forRoot()],
37
+ providers: [
38
+ {
39
+ provide: FEISHU_MODULE_OPTIONS,
40
+ useFactory: options.useFactory,
41
+ inject: options.inject || [],
42
+ },
43
+ FeishuSdkService,
44
+ FeishuCardService,
45
+ ],
46
+ exports: [FeishuSdkService, FeishuCardService],
47
+ };
48
+ }
49
+
50
+ /**
51
+ * 使用 ConfigService 注册模块
52
+ */
53
+ static forFeature(): DynamicModule {
54
+ return {
55
+ module: FeishuSdkModule,
56
+ imports: [ConfigModule.forFeature(feishuConfig), EventEmitterModule.forRoot()],
57
+ providers: [
58
+ {
59
+ provide: FEISHU_MODULE_OPTIONS,
60
+ useFactory: (configService: ConfigService): FeishuModuleOptions => {
61
+ const config = configService.get<FeishuConfig>("feishu");
62
+ return {
63
+ appId: config?.appId || "",
64
+ appSecret: config?.appSecret || "",
65
+ appType: config?.appType,
66
+ domain: config?.domain,
67
+ };
68
+ },
69
+ inject: [ConfigService],
70
+ },
71
+ FeishuSdkService,
72
+ FeishuCardService,
73
+ ],
74
+ exports: [FeishuSdkService, FeishuCardService],
75
+ };
76
+ }
77
+ }