apifox-workspace-mcp 2.1.0 → 2.1.4
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/core/cache/unified-cache-manager.d.ts +16 -0
- package/dist/core/cache/unified-cache-manager.d.ts.map +1 -1
- package/dist/core/cache/unified-cache-manager.js +57 -0
- package/dist/core/cache/unified-cache-manager.js.map +1 -1
- package/dist/core/client/mcp-bridge.d.ts +8 -0
- package/dist/core/client/mcp-bridge.d.ts.map +1 -1
- package/dist/core/client/mcp-bridge.js +86 -0
- package/dist/core/client/mcp-bridge.js.map +1 -1
- package/dist/core/config/manager.js +1 -1
- package/dist/core/config/manager.js.map +1 -1
- package/dist/core/generator/artifact.d.ts +2 -0
- package/dist/core/generator/artifact.d.ts.map +1 -1
- package/dist/core/generator/artifact.js +70 -6
- package/dist/core/generator/artifact.js.map +1 -1
- package/dist/core/generator/cursor-rules-loader.d.ts +38 -0
- package/dist/core/generator/cursor-rules-loader.d.ts.map +1 -0
- package/dist/core/generator/cursor-rules-loader.js +200 -0
- package/dist/core/generator/cursor-rules-loader.js.map +1 -0
- package/dist/core/generator/index.d.ts +3 -0
- package/dist/core/generator/index.d.ts.map +1 -1
- package/dist/core/generator/index.js +2 -0
- package/dist/core/generator/index.js.map +1 -1
- package/dist/core/generator/type-transformer.d.ts +18 -0
- package/dist/core/generator/type-transformer.d.ts.map +1 -0
- package/dist/core/generator/type-transformer.js +103 -0
- package/dist/core/generator/type-transformer.js.map +1 -0
- package/dist/server/handlers/generate-artifacts.d.ts.map +1 -1
- package/dist/server/handlers/generate-artifacts.js +28 -31
- package/dist/server/handlers/generate-artifacts.js.map +1 -1
- package/dist/server/handlers/get-interface-detail.d.ts.map +1 -1
- package/dist/server/handlers/get-interface-detail.js +35 -63
- package/dist/server/handlers/get-interface-detail.js.map +1 -1
- package/dist/server/handlers/list-project-interfaces.d.ts.map +1 -1
- package/dist/server/handlers/list-project-interfaces.js +0 -1
- package/dist/server/handlers/list-project-interfaces.js.map +1 -1
- package/dist/server/handlers/parse-and-add-project.d.ts.map +1 -1
- package/dist/server/handlers/parse-and-add-project.js.map +1 -1
- package/dist/server/handlers/refresh-project-cache.d.ts.map +1 -1
- package/dist/server/handlers/refresh-project-cache.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor Rules 加载器
|
|
3
|
+
* 读取并解析项目中的 Cursor Rules 规则,提取类型命名规范
|
|
4
|
+
*
|
|
5
|
+
* 规则来源(新优先级):
|
|
6
|
+
* 1) 项目目录下 `.cursor/rules/*.mcd` (新的 Cursor Rules 规范)
|
|
7
|
+
* 2) 兼容性后备:根目录 `.cursorrules`(旧格式,已废弃)
|
|
8
|
+
*/
|
|
9
|
+
import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
|
|
10
|
+
import { join, extname } from 'path';
|
|
11
|
+
import { logger } from '../../lib/logger.js';
|
|
12
|
+
/**
|
|
13
|
+
* 从项目根目录读取 Cursor Rules
|
|
14
|
+
* @param projectRoot 项目根目录
|
|
15
|
+
* @returns 类型命名规范,如果文件不存在或解析失败则返回 null
|
|
16
|
+
*/
|
|
17
|
+
export function loadCursorRules(projectRoot) {
|
|
18
|
+
// 新规范:.cursor/rules/*.mcd
|
|
19
|
+
const rulesDir = join(projectRoot, '.cursor', 'rules');
|
|
20
|
+
const ruleFiles = [];
|
|
21
|
+
if (existsSync(rulesDir)) {
|
|
22
|
+
try {
|
|
23
|
+
const entries = readdirSync(rulesDir);
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
const full = join(rulesDir, entry);
|
|
26
|
+
try {
|
|
27
|
+
const stat = statSync(full);
|
|
28
|
+
if (stat.isFile() && extname(entry).toLowerCase() === '.mcd') {
|
|
29
|
+
ruleFiles.push(full);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// 忽略单个文件的错误
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
logger.warn(`[cursor-rules] 读取规则目录失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// 如果找到新的 .mcd 规则文件,合并解析
|
|
42
|
+
if (ruleFiles.length > 0) {
|
|
43
|
+
try {
|
|
44
|
+
const mergedContent = ruleFiles
|
|
45
|
+
.map((file) => {
|
|
46
|
+
try {
|
|
47
|
+
return readFileSync(file, 'utf-8');
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return '';
|
|
51
|
+
}
|
|
52
|
+
})
|
|
53
|
+
.filter(Boolean)
|
|
54
|
+
.join('\n');
|
|
55
|
+
if (!mergedContent.trim()) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
logger.debug(`[cursor-rules] 使用 .cursor/rules/*.mcd 规则,共 ${ruleFiles.length} 个文件`);
|
|
59
|
+
return parseCursorRules(mergedContent);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
logger.warn(`[cursor-rules] 解析 .cursor/rules 下的规则失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// 兼容:旧的 .cursorrules(已废弃,但保持向后兼容)
|
|
66
|
+
const legacyRulesFile = join(projectRoot, '.cursorrules');
|
|
67
|
+
if (existsSync(legacyRulesFile)) {
|
|
68
|
+
try {
|
|
69
|
+
const content = readFileSync(legacyRulesFile, 'utf-8');
|
|
70
|
+
logger.debug('[cursor-rules] 使用兼容的 .cursorrules 文件(已废弃格式)');
|
|
71
|
+
return parseCursorRules(content);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
logger.warn(`[cursor-rules] 读取 .cursorrules 文件失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
logger.debug('[cursor-rules] 未找到 Cursor Rules 配置(.cursor/rules/*.mcd 或 .cursorrules)');
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 解析 Cursor Rules 内容
|
|
83
|
+
* @param content .cursorrules 文件内容
|
|
84
|
+
* @returns 类型命名规范
|
|
85
|
+
*/
|
|
86
|
+
function parseCursorRules(content) {
|
|
87
|
+
const rules = {};
|
|
88
|
+
// 解析接口命名风格
|
|
89
|
+
const interfaceStyleMatch = content.match(/(?:interface|type|命名).*?(?:style|风格).*?[::]\s*(PascalCase|camelCase|snake_case|kebab-case)/i);
|
|
90
|
+
if (interfaceStyleMatch) {
|
|
91
|
+
rules.interfaceStyle = interfaceStyleMatch[1];
|
|
92
|
+
}
|
|
93
|
+
// 解析请求类型后缀
|
|
94
|
+
const requestSuffixMatch = content.match(/(?:request|请求).*?(?:suffix|后缀).*?[::]\s*(\w+)/i);
|
|
95
|
+
if (requestSuffixMatch) {
|
|
96
|
+
rules.requestSuffix = requestSuffixMatch[1];
|
|
97
|
+
}
|
|
98
|
+
// 解析响应类型后缀
|
|
99
|
+
const responseSuffixMatch = content.match(/(?:response|响应).*?(?:suffix|后缀).*?[::]\s*(\w+)/i);
|
|
100
|
+
if (responseSuffixMatch) {
|
|
101
|
+
rules.responseSuffix = responseSuffixMatch[1];
|
|
102
|
+
}
|
|
103
|
+
// 解析禁止使用的名称
|
|
104
|
+
const forbiddenMatch = content.match(/(?:forbidden|禁止|避免).*?(?:name|名称).*?[::]\s*([^\n]+)/i);
|
|
105
|
+
if (forbiddenMatch) {
|
|
106
|
+
rules.forbiddenNames = forbiddenMatch[1]
|
|
107
|
+
.split(/[,,、]/)
|
|
108
|
+
.map((n) => n.trim())
|
|
109
|
+
.filter((n) => n.length > 0);
|
|
110
|
+
}
|
|
111
|
+
// 解析优先使用的业务语义
|
|
112
|
+
const preferredMatch = content.match(/(?:preferred|优先|推荐).*?(?:semantic|语义).*?[::]\s*([^\n]+)/i);
|
|
113
|
+
if (preferredMatch) {
|
|
114
|
+
rules.preferredSemantics = preferredMatch[1]
|
|
115
|
+
.split(/[,,、]/)
|
|
116
|
+
.map((s) => s.trim())
|
|
117
|
+
.filter((s) => s.length > 0);
|
|
118
|
+
}
|
|
119
|
+
return rules;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* 应用 Cursor Rules 到类型名称
|
|
123
|
+
* @param typeName 原始类型名称
|
|
124
|
+
* @param rules 类型命名规范
|
|
125
|
+
* @param isRequest 是否为请求类型
|
|
126
|
+
* @returns 格式化后的类型名称
|
|
127
|
+
*/
|
|
128
|
+
export function applyCursorRules(typeName, rules, isRequest = false) {
|
|
129
|
+
if (!rules) {
|
|
130
|
+
return typeName;
|
|
131
|
+
}
|
|
132
|
+
let formattedName = typeName;
|
|
133
|
+
// 应用命名风格
|
|
134
|
+
if (rules.interfaceStyle) {
|
|
135
|
+
formattedName = applyNamingStyle(formattedName, rules.interfaceStyle);
|
|
136
|
+
}
|
|
137
|
+
// 应用后缀
|
|
138
|
+
if (isRequest && rules.requestSuffix) {
|
|
139
|
+
if (!formattedName.endsWith(rules.requestSuffix)) {
|
|
140
|
+
formattedName = formattedName + rules.requestSuffix;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else if (!isRequest && rules.responseSuffix) {
|
|
144
|
+
if (!formattedName.endsWith(rules.responseSuffix)) {
|
|
145
|
+
formattedName = formattedName + rules.responseSuffix;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// 检查禁止使用的名称
|
|
149
|
+
if (rules.forbiddenNames) {
|
|
150
|
+
for (const forbidden of rules.forbiddenNames) {
|
|
151
|
+
if (formattedName.includes(forbidden)) {
|
|
152
|
+
logger.warn(`[cursor-rules] 类型名称包含禁止使用的名称: ${forbidden}`);
|
|
153
|
+
// 可以在这里进行替换或警告
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return formattedName;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* 应用命名风格
|
|
161
|
+
* @param name 原始名称
|
|
162
|
+
* @param style 命名风格
|
|
163
|
+
* @returns 格式化后的名称
|
|
164
|
+
*/
|
|
165
|
+
function applyNamingStyle(name, style) {
|
|
166
|
+
switch (style) {
|
|
167
|
+
case 'PascalCase':
|
|
168
|
+
return pascalCase(name);
|
|
169
|
+
case 'camelCase':
|
|
170
|
+
return camelCase(name);
|
|
171
|
+
case 'snake_case':
|
|
172
|
+
return snakeCase(name);
|
|
173
|
+
case 'kebab-case':
|
|
174
|
+
return kebabCase(name);
|
|
175
|
+
default:
|
|
176
|
+
return name;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function pascalCase(input) {
|
|
180
|
+
return input
|
|
181
|
+
.replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase())
|
|
182
|
+
.replace(/^[a-z]/, (chr) => chr.toUpperCase());
|
|
183
|
+
}
|
|
184
|
+
function camelCase(input) {
|
|
185
|
+
const pascal = pascalCase(input);
|
|
186
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
187
|
+
}
|
|
188
|
+
function snakeCase(input) {
|
|
189
|
+
return input
|
|
190
|
+
.replace(/([A-Z])/g, '_$1')
|
|
191
|
+
.toLowerCase()
|
|
192
|
+
.replace(/^_/, '');
|
|
193
|
+
}
|
|
194
|
+
function kebabCase(input) {
|
|
195
|
+
return input
|
|
196
|
+
.replace(/([A-Z])/g, '-$1')
|
|
197
|
+
.toLowerCase()
|
|
198
|
+
.replace(/^-/, '');
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=cursor-rules-loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor-rules-loader.js","sourceRoot":"","sources":["../../../src/core/generator/cursor-rules-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,4 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export { generateArtifact } from './artifact.js';
|
|
5
5
|
export type { ArtifactInput, GeneratedArtifact, ArtifactType } from './artifact.js';
|
|
6
|
+
export { transformTypeNames } from './type-transformer.js';
|
|
7
|
+
export { loadCursorRules, applyCursorRules } from './cursor-rules-loader.js';
|
|
8
|
+
export type { TypeNamingRules } from './cursor-rules-loader.js';
|
|
6
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/generator/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/generator/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpF,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC7E,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/generator/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/generator/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 类型名称语义化转换器
|
|
3
|
+
* 将通用类型名称(如 "ApifoxModel")转换为语义化名称(如 "CreateOrderRequest")
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 转换类型定义中的类型名称
|
|
7
|
+
* @param typeCode 原始类型代码
|
|
8
|
+
* @param interfaceDetail 接口详情(name 等字段可能为 undefined)
|
|
9
|
+
* @returns 转换后的类型代码
|
|
10
|
+
*/
|
|
11
|
+
export declare function transformTypeNames(typeCode: string, interfaceDetail: {
|
|
12
|
+
id: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
method?: string;
|
|
15
|
+
path?: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
}): string;
|
|
18
|
+
//# sourceMappingURL=type-transformer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-transformer.d.ts","sourceRoot":"","sources":["../../../src/core/generator/type-transformer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GACA,MAAM,CAcR"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 类型名称语义化转换器
|
|
3
|
+
* 将通用类型名称(如 "ApifoxModel")转换为语义化名称(如 "CreateOrderRequest")
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 转换类型定义中的类型名称
|
|
7
|
+
* @param typeCode 原始类型代码
|
|
8
|
+
* @param interfaceDetail 接口详情(name 等字段可能为 undefined)
|
|
9
|
+
* @returns 转换后的类型代码
|
|
10
|
+
*/
|
|
11
|
+
export function transformTypeNames(typeCode, interfaceDetail) {
|
|
12
|
+
// 提取接口的语义化名称
|
|
13
|
+
const semanticName = generateSemanticName(interfaceDetail);
|
|
14
|
+
// 替换通用类型名称
|
|
15
|
+
let transformedCode = typeCode;
|
|
16
|
+
// 替换 "ApifoxModel" 为语义化名称
|
|
17
|
+
transformedCode = transformedCode.replace(/ApifoxModel/g, semanticName);
|
|
18
|
+
// 替换其他通用类型名称(如果它们出现在不合适的位置)
|
|
19
|
+
// 这里可以根据实际需要进行更复杂的替换逻辑
|
|
20
|
+
return transformedCode;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 根据接口信息生成语义化名称
|
|
24
|
+
* @param interfaceDetail 接口详情(name 等字段可能为 undefined)
|
|
25
|
+
* @returns 语义化名称(PascalCase)
|
|
26
|
+
*/
|
|
27
|
+
function generateSemanticName(interfaceDetail) {
|
|
28
|
+
const { name, method, path } = interfaceDetail;
|
|
29
|
+
// 从接口名称提取关键词
|
|
30
|
+
const nameKeywords = extractKeywords(name || '');
|
|
31
|
+
// 从路径提取关键词
|
|
32
|
+
const pathKeywords = extractKeywords(path || '');
|
|
33
|
+
// 根据 HTTP 方法确定操作类型
|
|
34
|
+
const operationType = getOperationType(method || 'GET');
|
|
35
|
+
// 组合生成语义化名称
|
|
36
|
+
let semanticName = '';
|
|
37
|
+
if (nameKeywords.length > 0) {
|
|
38
|
+
// 优先使用接口名称中的关键词
|
|
39
|
+
semanticName = nameKeywords.join('');
|
|
40
|
+
}
|
|
41
|
+
else if (pathKeywords.length > 0) {
|
|
42
|
+
// 其次使用路径中的关键词
|
|
43
|
+
semanticName = pathKeywords.join('');
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// 最后使用默认名称
|
|
47
|
+
semanticName = 'Api';
|
|
48
|
+
}
|
|
49
|
+
// 添加操作类型前缀(如果需要)
|
|
50
|
+
if (operationType && !semanticName.toLowerCase().includes(operationType.toLowerCase())) {
|
|
51
|
+
semanticName = operationType + semanticName;
|
|
52
|
+
}
|
|
53
|
+
return pascalCase(semanticName);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 从文本中提取关键词
|
|
57
|
+
* @param text 文本
|
|
58
|
+
* @returns 关键词数组(已转换为 PascalCase)
|
|
59
|
+
*/
|
|
60
|
+
function extractKeywords(text) {
|
|
61
|
+
// 移除特殊字符,保留中英文、数字
|
|
62
|
+
const cleaned = text.replace(/[^\u4e00-\u9fa5a-zA-Z0-9\s]/g, ' ');
|
|
63
|
+
// 分割为单词
|
|
64
|
+
const words = cleaned
|
|
65
|
+
.split(/\s+/)
|
|
66
|
+
.filter((w) => w.length > 0)
|
|
67
|
+
.map((w) => pascalCase(w));
|
|
68
|
+
return words;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 根据 HTTP 方法确定操作类型
|
|
72
|
+
* @param method HTTP 方法
|
|
73
|
+
* @returns 操作类型(如 "Create", "Update", "Get" 等)
|
|
74
|
+
*/
|
|
75
|
+
function getOperationType(method) {
|
|
76
|
+
const upperMethod = method.toUpperCase();
|
|
77
|
+
const operationMap = {
|
|
78
|
+
GET: 'Get',
|
|
79
|
+
POST: 'Create',
|
|
80
|
+
PUT: 'Update',
|
|
81
|
+
PATCH: 'Update',
|
|
82
|
+
DELETE: 'Delete',
|
|
83
|
+
};
|
|
84
|
+
return operationMap[upperMethod] || '';
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 转换为 PascalCase
|
|
88
|
+
* @param input 输入字符串
|
|
89
|
+
* @returns PascalCase 字符串
|
|
90
|
+
*/
|
|
91
|
+
function pascalCase(input) {
|
|
92
|
+
if (!input)
|
|
93
|
+
return '';
|
|
94
|
+
// 处理中文:直接返回(中文通常不需要转换)
|
|
95
|
+
if (/[\u4e00-\u9fa5]/.test(input)) {
|
|
96
|
+
return input;
|
|
97
|
+
}
|
|
98
|
+
// 处理英文:转换为 PascalCase
|
|
99
|
+
return input
|
|
100
|
+
.replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase())
|
|
101
|
+
.replace(/^[a-z]/, (chr) => chr.toUpperCase());
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=type-transformer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-transformer.js","sourceRoot":"","sources":["../../../src/core/generator/type-transformer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-artifacts.d.ts","sourceRoot":"","sources":["../../../src/server/handlers/generate-artifacts.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,aAAa,EACb,SAAS,EACT,YAAY,
|
|
1
|
+
{"version":3,"file":"generate-artifacts.d.ts","sourceRoot":"","sources":["../../../src/server/handlers/generate-artifacts.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,aAAa,EACb,SAAS,EACT,YAAY,EAGb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAIxD,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,YAAY,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,aAAa,CAAC;IAC7B,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,YAAY,CAAC;CAC5B;AAYD;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,qBAAqB,EAC3B,GAAG,EAAE,wBAAwB;;;;;;;;;;;GAoK9B"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { writeFileSync } from 'fs';
|
|
2
2
|
import { join } from 'path';
|
|
3
|
-
import {
|
|
3
|
+
import { UnifiedCacheManager, generateArtifact, } from '../../core/index.js';
|
|
4
4
|
import { resolveProjectInfo, buildTextResponse, buildMissingTokenResponse } from './utils.js';
|
|
5
5
|
import { logger } from '../../lib/logger.js';
|
|
6
6
|
/**
|
|
@@ -25,6 +25,7 @@ export async function handleGenerateArtifacts(args, ctx) {
|
|
|
25
25
|
if (!accessToken) {
|
|
26
26
|
return buildMissingTokenResponse();
|
|
27
27
|
}
|
|
28
|
+
const cacheManager = new UnifiedCacheManager(projectRoot);
|
|
28
29
|
let interfaceIds = (args.interfaceIds || []).filter(Boolean);
|
|
29
30
|
// 尝试从选择中获取接口 ID
|
|
30
31
|
if (interfaceIds.length === 0) {
|
|
@@ -50,43 +51,39 @@ export async function handleGenerateArtifacts(args, ctx) {
|
|
|
50
51
|
}
|
|
51
52
|
const artifacts = [];
|
|
52
53
|
const errors = [];
|
|
53
|
-
const cacheManager = new UnifiedCacheManager(projectRoot);
|
|
54
|
-
let cacheHitCount = 0;
|
|
55
54
|
let mcpCallCount = 0;
|
|
56
55
|
for (const id of interfaceIds) {
|
|
57
56
|
try {
|
|
58
|
-
|
|
59
|
-
//
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
// 生成类型文件时,始终调用 Apifox MCP Server 获取准确的接口详情
|
|
58
|
+
// 使用官方能力,确保类型信息的准确性和完整性
|
|
59
|
+
logger.debug(`[generate_artifacts] 调用 Apifox MCP Server 获取接口 ${id} 的详情(消耗 token)`);
|
|
60
|
+
const detail = await mcpBridge.getInterfaceDetailViaMcp(accessToken, projectInfo.projectName, projectInfo.projectId, id);
|
|
61
|
+
mcpCallCount++;
|
|
62
|
+
// 回写接口基础信息到缓存(docUrl 可能缺失,后续 llms 刷新补齐)
|
|
63
|
+
cacheManager.upsertInterfaceDetail(projectInfo.projectName, projectInfo.projectId, {
|
|
64
|
+
id: detail.id,
|
|
65
|
+
name: detail.name,
|
|
66
|
+
});
|
|
67
|
+
// 如果是生成类型文件,尝试从官方 MCP Server 获取 OpenAPI Spec
|
|
68
|
+
let rawTypeCode;
|
|
69
|
+
if (args.artifactType === 'ts_types') {
|
|
63
70
|
try {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
logger.warn(`[generate_artifacts] ⚠️ 从 md 文件解析接口 ${id} 失败,回退到 MCP Server`);
|
|
71
|
-
throw new Error('从 md 文件解析失败');
|
|
72
|
-
}
|
|
71
|
+
logger.debug(`[generate_artifacts] 尝试从官方 MCP Server 获取 OpenAPI Spec...`);
|
|
72
|
+
await mcpBridge.readOpenApiSpecViaMcp(accessToken, projectInfo.projectId);
|
|
73
|
+
// 如果获取成功,尝试从 OpenAPI Spec 中提取该接口的类型定义
|
|
74
|
+
// 注意:这里需要解析 OpenAPI Spec,暂时先跳过,使用原有逻辑
|
|
75
|
+
// TODO: 实现 OpenAPI Spec 解析逻辑
|
|
76
|
+
logger.debug(`[generate_artifacts] OpenAPI Spec 获取成功,但类型提取功能待实现`);
|
|
73
77
|
}
|
|
74
|
-
catch (
|
|
75
|
-
|
|
76
|
-
logger.debug(`[generate_artifacts]
|
|
77
|
-
// 回退到 MCP Server
|
|
78
|
-
detail = await mcpBridge.getInterfaceDetailViaMcp(accessToken, projectInfo.projectName, projectInfo.projectId, id);
|
|
79
|
-
mcpCallCount++;
|
|
78
|
+
catch (specError) {
|
|
79
|
+
// OpenAPI Spec 读取失败,使用原有逻辑生成类型
|
|
80
|
+
logger.debug(`[generate_artifacts] OpenAPI Spec 读取失败,使用接口详情生成类型: ${specError instanceof Error ? specError.message : String(specError)}`);
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
|
-
else {
|
|
83
|
-
logger.debug(`[generate_artifacts] 接口 ${id} 不在缓存中,调用 MCP Server(消耗 token)`);
|
|
84
|
-
// 缓存中没有,调用 MCP Server
|
|
85
|
-
detail = await mcpBridge.getInterfaceDetailViaMcp(accessToken, projectInfo.projectName, projectInfo.projectId, id);
|
|
86
|
-
mcpCallCount++;
|
|
87
|
-
}
|
|
88
83
|
artifacts.push(generateArtifact(args.artifactType ?? 'ts_types', {
|
|
89
84
|
interfaceDetail: detail,
|
|
85
|
+
projectRoot,
|
|
86
|
+
rawTypeCode,
|
|
90
87
|
}));
|
|
91
88
|
}
|
|
92
89
|
catch (error) {
|
|
@@ -94,8 +91,8 @@ export async function handleGenerateArtifacts(args, ctx) {
|
|
|
94
91
|
}
|
|
95
92
|
}
|
|
96
93
|
// 记录 token 消耗情况
|
|
97
|
-
if (
|
|
98
|
-
logger.info(`[generate_artifacts] Token 消耗统计:
|
|
94
|
+
if (mcpCallCount > 0) {
|
|
95
|
+
logger.info(`[generate_artifacts] Token 消耗统计: MCP 调用 ${mcpCallCount} 个接口(消耗 token)`);
|
|
99
96
|
}
|
|
100
97
|
if (errors.length > 0) {
|
|
101
98
|
if (artifacts.length === 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-artifacts.js","sourceRoot":"","sources":["../../../src/server/handlers/generate-artifacts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAIL,
|
|
1
|
+
{"version":3,"file":"generate-artifacts.js","sourceRoot":"","sources":["../../../src/server/handlers/generate-artifacts.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-interface-detail.d.ts","sourceRoot":"","sources":["../../../src/server/handlers/get-interface-detail.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,SAAS,EACT,YAAY,
|
|
1
|
+
{"version":3,"file":"get-interface-detail.d.ts","sourceRoot":"","sources":["../../../src/server/handlers/get-interface-detail.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,SAAS,EACT,YAAY,EAOb,MAAM,qBAAqB,CAAC;AAU7B,MAAM,WAAW,sBAAsB;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,aAAa,CAAC;IAC7B,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,sBAAsB,EAC5B,GAAG,EAAE,yBAAyB;;;;;;;;;;;;GAuP/B"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { generateArtifact, UnifiedCacheManager, parseApifoxUrl, isValidApifoxUrl,
|
|
1
|
+
import { generateArtifact, UnifiedCacheManager, parseApifoxUrl, isValidApifoxUrl, fetchAndParseLlmsWithHash, parseInterfaceFromMd, } from '../../core/index.js';
|
|
2
2
|
import { logger } from '../../lib/logger.js';
|
|
3
3
|
import { resolveProjectInfo, buildTextResponse, buildMissingTokenResponse, buildCurlSnippet, buildFetchSnippet, } from './utils.js';
|
|
4
4
|
/**
|
|
@@ -62,78 +62,50 @@ export async function handleGetInterfaceDetail(args, ctx) {
|
|
|
62
62
|
logger.info(`缓存已刷新: ${refreshResult.reason}`);
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
//
|
|
65
|
+
// 读取接口信息时,优先从 MD 文件读取(不消耗 Token)
|
|
66
|
+
// 仅在 MD 解析失败时回退到官方 MCP Server
|
|
66
67
|
const cachedInterface = cacheManager.findInterface(projectInfo.projectName, args.interfaceId, projectInfo.projectId);
|
|
68
|
+
let detail;
|
|
67
69
|
if (cachedInterface && cachedInterface.docUrl) {
|
|
68
70
|
try {
|
|
69
|
-
|
|
71
|
+
// 优先从 MD 文件读取(不消耗 Token)
|
|
72
|
+
logger.debug(`[get_interface_detail] 接口 ${args.interfaceId} 在缓存中,从 MD 文件读取(不消耗 token)`);
|
|
73
|
+
detail = await parseInterfaceFromMd(cachedInterface.docUrl);
|
|
70
74
|
if (detail) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const fetchSnippet = buildFetchSnippet(detail);
|
|
77
|
-
const description = detail.description ? `\n- 描述: ${detail.description}` : '';
|
|
78
|
-
// 构建详细的请求参数展示
|
|
79
|
-
const requestParts = [];
|
|
80
|
-
if (detail.request) {
|
|
81
|
-
if (detail.request.headers && Object.keys(detail.request.headers).length > 0) {
|
|
82
|
-
requestParts.push(`**请求头 (Headers):**\n\`\`\`json\n${JSON.stringify(detail.request.headers, null, 2)}\n\`\`\``);
|
|
83
|
-
}
|
|
84
|
-
if (detail.request.cookies && Object.keys(detail.request.cookies).length > 0) {
|
|
85
|
-
requestParts.push(`**Cookie 参数:**\n\`\`\`json\n${JSON.stringify(detail.request.cookies, null, 2)}\n\`\`\``);
|
|
86
|
-
}
|
|
87
|
-
if (detail.request.query && Object.keys(detail.request.query).length > 0) {
|
|
88
|
-
requestParts.push(`**查询参数 (Query):**\n\`\`\`json\n${JSON.stringify(detail.request.query, null, 2)}\n\`\`\``);
|
|
89
|
-
}
|
|
90
|
-
if (detail.request.path && Object.keys(detail.request.path).length > 0) {
|
|
91
|
-
requestParts.push(`**路径参数 (Path):**\n\`\`\`json\n${JSON.stringify(detail.request.path, null, 2)}\n\`\`\``);
|
|
92
|
-
}
|
|
93
|
-
if (detail.request.body) {
|
|
94
|
-
const bodyJson = JSON.stringify(detail.request.body, null, 2);
|
|
95
|
-
requestParts.push(`**请求体 (Body):**\n\`\`\`json\n${bodyJson}\n\`\`\``);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
const requestSection = requestParts.length > 0
|
|
99
|
-
? `### 请求参数\n\n${requestParts.join('\n\n')}\n\n`
|
|
100
|
-
: `### 请求参数\n\`\`\`json\n${JSON.stringify(detail.request || {}, null, 2)}\n\`\`\`\n\n`;
|
|
101
|
-
// 构建响应展示
|
|
102
|
-
const responseJson = JSON.stringify(detail.response || {}, null, 2);
|
|
103
|
-
const responseSection = `### 响应示例\n\`\`\`json\n${responseJson}\n\`\`\`\n\n`;
|
|
104
|
-
const text = `# ${detail.name || '未命名接口'}\n\n` +
|
|
105
|
-
`**基本信息**\n` +
|
|
106
|
-
`- 接口 ID: ${detail.id}\n` +
|
|
107
|
-
`- 请求方法: ${detail.method || 'N/A'}\n` +
|
|
108
|
-
`- 接口路径: ${detail.path || 'N/A'}\n` +
|
|
109
|
-
`${description}\n\n` +
|
|
110
|
-
`${requestSection}` +
|
|
111
|
-
`${responseSection}` +
|
|
112
|
-
`### curl 示例\n\`\`\`bash\n${curlSnippet}\n\`\`\`\n\n` +
|
|
113
|
-
`### fetch 示例\n\`\`\`ts\n${fetchSnippet}\n\`\`\`\n\n` +
|
|
114
|
-
`### TypeScript 类型草稿\n\`\`\`ts\n${tsArtifact.content}\n\`\`\`\n\n` +
|
|
115
|
-
`如果需要,我还能继续为你生成 Mock 数据或请求封装。`;
|
|
116
|
-
return {
|
|
117
|
-
content: [
|
|
118
|
-
{
|
|
119
|
-
type: 'text',
|
|
120
|
-
text,
|
|
121
|
-
},
|
|
122
|
-
],
|
|
123
|
-
};
|
|
75
|
+
logger.info(`[get_interface_detail] ✅ 从缓存 md 文件成功获取接口 ${args.interfaceId} 的详情`);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
logger.warn(`[get_interface_detail] ⚠️ 从 md 文件解析接口 ${args.interfaceId} 失败,回退到 MCP Server`);
|
|
79
|
+
throw new Error('从 md 文件解析失败');
|
|
124
80
|
}
|
|
125
81
|
}
|
|
126
|
-
catch {
|
|
127
|
-
|
|
82
|
+
catch (mdError) {
|
|
83
|
+
logger.warn(`[get_interface_detail] 从 md 文件读取接口 ${args.interfaceId} 失败: ${mdError instanceof Error ? mdError.message : String(mdError)}`);
|
|
84
|
+
logger.debug(`[get_interface_detail] 回退到官方 MCP Server 获取接口 ${args.interfaceId} 的详情(消耗 token)`);
|
|
85
|
+
// 回退到官方 MCP Server
|
|
86
|
+
const accessToken = configManager.getAccessToken();
|
|
87
|
+
if (!accessToken) {
|
|
88
|
+
return buildMissingTokenResponse();
|
|
89
|
+
}
|
|
90
|
+
detail = await mcpBridge.getInterfaceDetailViaMcp(accessToken, projectInfo.projectName, projectInfo.projectId, args.interfaceId);
|
|
128
91
|
}
|
|
129
92
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
93
|
+
else {
|
|
94
|
+
// 缓存中没有,调用官方 MCP Server
|
|
95
|
+
logger.debug(`[get_interface_detail] 接口 ${args.interfaceId} 不在缓存中,调用官方 MCP Server(消耗 token)`);
|
|
96
|
+
const accessToken = configManager.getAccessToken();
|
|
97
|
+
if (!accessToken) {
|
|
98
|
+
return buildMissingTokenResponse();
|
|
99
|
+
}
|
|
100
|
+
detail = await mcpBridge.getInterfaceDetailViaMcp(accessToken, projectInfo.projectName, projectInfo.projectId, args.interfaceId);
|
|
134
101
|
}
|
|
135
102
|
try {
|
|
136
|
-
|
|
103
|
+
// 回写接口基础信息到缓存(即便 docUrl 不存在,也先记录,后续刷新补齐)
|
|
104
|
+
cacheManager.upsertInterfaceDetail(projectInfo.projectName, projectInfo.projectId, {
|
|
105
|
+
id: detail.id,
|
|
106
|
+
name: detail.name,
|
|
107
|
+
docUrl: cachedInterface?.docUrl,
|
|
108
|
+
});
|
|
137
109
|
contextStore.setLastProject(projectInfo.projectName, projectInfo.projectId);
|
|
138
110
|
const tsArtifact = generateArtifact('ts_types', {
|
|
139
111
|
interfaceDetail: detail,
|