activo 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/core/tools/dependencyAnalysis.d.ts +3 -0
- package/dist/core/tools/dependencyAnalysis.d.ts.map +1 -0
- package/dist/core/tools/dependencyAnalysis.js +295 -0
- package/dist/core/tools/dependencyAnalysis.js.map +1 -0
- package/dist/core/tools/index.d.ts +3 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +7 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/openapiAnalysis.d.ts +3 -0
- package/dist/core/tools/openapiAnalysis.d.ts.map +1 -0
- package/dist/core/tools/openapiAnalysis.js +356 -0
- package/dist/core/tools/openapiAnalysis.js.map +1 -0
- package/dist/core/tools/pythonAnalysis.d.ts +3 -0
- package/dist/core/tools/pythonAnalysis.d.ts.map +1 -0
- package/dist/core/tools/pythonAnalysis.js +387 -0
- package/dist/core/tools/pythonAnalysis.js.map +1 -0
- package/package.json +1 -1
- package/src/core/tools/dependencyAnalysis.ts +363 -0
- package/src/core/tools/index.ts +7 -1
- package/src/core/tools/openapiAnalysis.ts +431 -0
- package/src/core/tools/pythonAnalysis.ts +477 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { Tool, ToolResult } from "./types.js";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
|
|
5
|
+
interface Dependency {
|
|
6
|
+
name: string;
|
|
7
|
+
version: string;
|
|
8
|
+
type: "dependency" | "devDependency" | "peerDependency" | "compile" | "runtime" | "test" | "provided";
|
|
9
|
+
issues: string[];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface DependencyAnalysisResult {
|
|
13
|
+
file: string;
|
|
14
|
+
type: "npm" | "maven" | "gradle";
|
|
15
|
+
projectName?: string;
|
|
16
|
+
projectVersion?: string;
|
|
17
|
+
dependencies: Dependency[];
|
|
18
|
+
summary: {
|
|
19
|
+
total: number;
|
|
20
|
+
withIssues: number;
|
|
21
|
+
outdatedPatterns: number;
|
|
22
|
+
securityConcerns: number;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 알려진 취약점/문제 패턴
|
|
27
|
+
const knownIssues: Record<string, { pattern: RegExp; message: string }[]> = {
|
|
28
|
+
npm: [
|
|
29
|
+
{ pattern: /^lodash@[0-3]\./, message: "lodash 4.x 이전 버전 - 보안 취약점" },
|
|
30
|
+
{ pattern: /^moment@/, message: "moment.js deprecated - dayjs/date-fns 권장" },
|
|
31
|
+
{ pattern: /^request@/, message: "request deprecated - axios/node-fetch 권장" },
|
|
32
|
+
{ pattern: /^jquery@[12]\./, message: "jQuery 3.x 이전 버전 - XSS 취약점" },
|
|
33
|
+
{ pattern: /^angular@1\./, message: "AngularJS 1.x EOL - Angular 권장" },
|
|
34
|
+
{ pattern: /^react@1[0-5]\./, message: "React 16 이전 버전 - 업데이트 권장" },
|
|
35
|
+
{ pattern: /^vue@[12]\./, message: "Vue 2.x - Vue 3.x 마이그레이션 검토" },
|
|
36
|
+
{ pattern: /^webpack@[1-3]\./, message: "Webpack 4 이전 버전 - 업데이트 권장" },
|
|
37
|
+
{ pattern: /^node-sass@/, message: "node-sass deprecated - sass(dart-sass) 권장" },
|
|
38
|
+
{ pattern: /^tslint@/, message: "TSLint deprecated - ESLint 권장" },
|
|
39
|
+
{ pattern: /^crypto-js@[0-3]\./, message: "crypto-js 구버전 - 보안 취약점" },
|
|
40
|
+
{ pattern: /^express@[0-3]\./, message: "Express 4 이전 버전 - 보안 취약점" },
|
|
41
|
+
{ pattern: /^axios@0\.[0-1]/, message: "axios 0.21 이전 - SSRF 취약점" },
|
|
42
|
+
],
|
|
43
|
+
maven: [
|
|
44
|
+
{ pattern: /log4j.*1\./, message: "Log4j 1.x EOL - Log4j 2.x 또는 Logback 권장" },
|
|
45
|
+
{ pattern: /log4j.*2\.[0-9]\./, message: "Log4j 2.0-2.14 - Log4Shell 취약점 (CVE-2021-44228)" },
|
|
46
|
+
{ pattern: /log4j.*2\.1[0-4]\./, message: "Log4j 2.10-2.14 - Log4Shell 취약점" },
|
|
47
|
+
{ pattern: /commons-collections.*[0-3]\./, message: "Commons Collections 3.x - 역직렬화 취약점" },
|
|
48
|
+
{ pattern: /spring-core.*[0-4]\./, message: "Spring 5 이전 버전 - 업데이트 권장" },
|
|
49
|
+
{ pattern: /jackson-databind.*2\.[0-8]\./, message: "Jackson 2.9 이전 - 역직렬화 취약점" },
|
|
50
|
+
{ pattern: /struts.*1\./, message: "Struts 1.x EOL - 보안 취약점 다수" },
|
|
51
|
+
{ pattern: /struts2.*2\.[0-4]\./, message: "Struts 2.5 이전 - 원격 코드 실행 취약점" },
|
|
52
|
+
{ pattern: /hibernate.*[0-4]\./, message: "Hibernate 5 이전 버전 - 업데이트 권장" },
|
|
53
|
+
{ pattern: /mysql-connector.*5\./, message: "MySQL Connector 8.x 권장" },
|
|
54
|
+
{ pattern: /fastjson.*1\.[12]\.[0-5]/, message: "Fastjson 1.2.68 이전 - 원격 코드 실행 취약점" },
|
|
55
|
+
{ pattern: /commons-fileupload.*1\.[0-3]\./, message: "Commons FileUpload 1.4 이전 - DoS 취약점" },
|
|
56
|
+
{ pattern: /shiro.*1\.[0-5]\./, message: "Apache Shiro 1.6 이전 - 인증 우회 취약점" },
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// 버전 패턴 검사
|
|
61
|
+
const versionPatterns = {
|
|
62
|
+
range: /^[\^~><=]/,
|
|
63
|
+
latest: /^(latest|\*)$/,
|
|
64
|
+
git: /^(git|github|gitlab)/,
|
|
65
|
+
file: /^file:/,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// package.json 분석
|
|
69
|
+
function analyzePackageJson(filePath: string): DependencyAnalysisResult {
|
|
70
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
71
|
+
const pkg = JSON.parse(content);
|
|
72
|
+
const dependencies: Dependency[] = [];
|
|
73
|
+
|
|
74
|
+
const analyzeDeps = (deps: Record<string, string> | undefined, type: Dependency["type"]) => {
|
|
75
|
+
if (!deps) return;
|
|
76
|
+
|
|
77
|
+
Object.entries(deps).forEach(([name, version]) => {
|
|
78
|
+
const issues: string[] = [];
|
|
79
|
+
const fullName = `${name}@${version}`;
|
|
80
|
+
|
|
81
|
+
// 알려진 이슈 검사
|
|
82
|
+
knownIssues.npm.forEach(({ pattern, message }) => {
|
|
83
|
+
if (pattern.test(fullName)) {
|
|
84
|
+
issues.push(message);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// 버전 패턴 검사
|
|
89
|
+
if (versionPatterns.latest.test(version)) {
|
|
90
|
+
issues.push("'latest' 또는 '*' 사용 - 버전 고정 권장");
|
|
91
|
+
}
|
|
92
|
+
if (versionPatterns.git.test(version)) {
|
|
93
|
+
issues.push("Git 의존성 - 버전 태그 사용 권장");
|
|
94
|
+
}
|
|
95
|
+
if (version.startsWith("^0.") || version.startsWith("~0.")) {
|
|
96
|
+
issues.push("0.x 버전 - 불안정 버전 주의");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
dependencies.push({ name, version, type, issues });
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
analyzeDeps(pkg.dependencies, "dependency");
|
|
104
|
+
analyzeDeps(pkg.devDependencies, "devDependency");
|
|
105
|
+
analyzeDeps(pkg.peerDependencies, "peerDependency");
|
|
106
|
+
|
|
107
|
+
const withIssues = dependencies.filter((d) => d.issues.length > 0);
|
|
108
|
+
const securityConcerns = dependencies.filter((d) =>
|
|
109
|
+
d.issues.some((i) => i.includes("취약점") || i.includes("보안"))
|
|
110
|
+
).length;
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
file: filePath,
|
|
114
|
+
type: "npm",
|
|
115
|
+
projectName: pkg.name,
|
|
116
|
+
projectVersion: pkg.version,
|
|
117
|
+
dependencies,
|
|
118
|
+
summary: {
|
|
119
|
+
total: dependencies.length,
|
|
120
|
+
withIssues: withIssues.length,
|
|
121
|
+
outdatedPatterns: dependencies.filter((d) =>
|
|
122
|
+
d.issues.some((i) => i.includes("deprecated") || i.includes("EOL") || i.includes("이전"))
|
|
123
|
+
).length,
|
|
124
|
+
securityConcerns,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// pom.xml 분석
|
|
130
|
+
function analyzePomXml(filePath: string): DependencyAnalysisResult {
|
|
131
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
132
|
+
const dependencies: Dependency[] = [];
|
|
133
|
+
|
|
134
|
+
// 프로젝트 정보 추출
|
|
135
|
+
const artifactIdMatch = content.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
136
|
+
const versionMatch = content.match(/<version>([^<]+)<\/version>/);
|
|
137
|
+
|
|
138
|
+
// 의존성 추출
|
|
139
|
+
const depRegex = /<dependency>\s*<groupId>([^<]+)<\/groupId>\s*<artifactId>([^<]+)<\/artifactId>(?:\s*<version>([^<]*)<\/version>)?(?:\s*<scope>([^<]*)<\/scope>)?/g;
|
|
140
|
+
|
|
141
|
+
let match;
|
|
142
|
+
while ((match = depRegex.exec(content)) !== null) {
|
|
143
|
+
const groupId = match[1];
|
|
144
|
+
const artifactId = match[2];
|
|
145
|
+
const version = match[3] || "미지정";
|
|
146
|
+
const scope = match[4] || "compile";
|
|
147
|
+
const name = `${groupId}:${artifactId}`;
|
|
148
|
+
const issues: string[] = [];
|
|
149
|
+
const fullName = `${artifactId}.*${version}`;
|
|
150
|
+
|
|
151
|
+
// 알려진 이슈 검사
|
|
152
|
+
knownIssues.maven.forEach(({ pattern, message }) => {
|
|
153
|
+
if (pattern.test(fullName) || pattern.test(`${groupId}.${artifactId}.*${version}`)) {
|
|
154
|
+
issues.push(message);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// 버전 미지정 검사
|
|
159
|
+
if (version === "미지정" || version.includes("${")) {
|
|
160
|
+
if (!version.includes("${")) {
|
|
161
|
+
issues.push("버전 미지정 - 명시적 버전 지정 권장");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// SNAPSHOT 버전 검사
|
|
166
|
+
if (version.includes("SNAPSHOT")) {
|
|
167
|
+
issues.push("SNAPSHOT 버전 - 프로덕션에서 릴리즈 버전 사용 권장");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const type: Dependency["type"] =
|
|
171
|
+
scope === "test" ? "test" :
|
|
172
|
+
scope === "provided" ? "provided" :
|
|
173
|
+
scope === "runtime" ? "runtime" : "compile";
|
|
174
|
+
|
|
175
|
+
dependencies.push({ name, version, type, issues });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const withIssues = dependencies.filter((d) => d.issues.length > 0);
|
|
179
|
+
const securityConcerns = dependencies.filter((d) =>
|
|
180
|
+
d.issues.some((i) => i.includes("취약점") || i.includes("보안") || i.includes("CVE"))
|
|
181
|
+
).length;
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
file: filePath,
|
|
185
|
+
type: "maven",
|
|
186
|
+
projectName: artifactIdMatch ? artifactIdMatch[1] : undefined,
|
|
187
|
+
projectVersion: versionMatch ? versionMatch[1] : undefined,
|
|
188
|
+
dependencies,
|
|
189
|
+
summary: {
|
|
190
|
+
total: dependencies.length,
|
|
191
|
+
withIssues: withIssues.length,
|
|
192
|
+
outdatedPatterns: dependencies.filter((d) =>
|
|
193
|
+
d.issues.some((i) => i.includes("EOL") || i.includes("이전"))
|
|
194
|
+
).length,
|
|
195
|
+
securityConcerns,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// build.gradle 분석 (간단한 버전)
|
|
201
|
+
function analyzeBuildGradle(filePath: string): DependencyAnalysisResult {
|
|
202
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
203
|
+
const dependencies: Dependency[] = [];
|
|
204
|
+
|
|
205
|
+
// 의존성 추출 (implementation, compile, testImplementation 등)
|
|
206
|
+
const depRegex = /(?:implementation|compile|testImplementation|testCompile|runtimeOnly|compileOnly|api)\s*['"(]([^'"()]+)['"()]/g;
|
|
207
|
+
|
|
208
|
+
let match;
|
|
209
|
+
while ((match = depRegex.exec(content)) !== null) {
|
|
210
|
+
const depString = match[1];
|
|
211
|
+
const parts = depString.split(":");
|
|
212
|
+
|
|
213
|
+
if (parts.length >= 2) {
|
|
214
|
+
const name = `${parts[0]}:${parts[1]}`;
|
|
215
|
+
const version = parts[2] || "미지정";
|
|
216
|
+
const issues: string[] = [];
|
|
217
|
+
|
|
218
|
+
// 알려진 이슈 검사
|
|
219
|
+
knownIssues.maven.forEach(({ pattern, message }) => {
|
|
220
|
+
if (pattern.test(`${parts[1]}.*${version}`)) {
|
|
221
|
+
issues.push(message);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
if (version === "미지정" || version.includes("$")) {
|
|
226
|
+
if (!version.includes("$")) {
|
|
227
|
+
issues.push("버전 미지정");
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
dependencies.push({
|
|
232
|
+
name,
|
|
233
|
+
version,
|
|
234
|
+
type: match[0].includes("test") ? "test" : "compile",
|
|
235
|
+
issues,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const withIssues = dependencies.filter((d) => d.issues.length > 0);
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
file: filePath,
|
|
244
|
+
type: "gradle",
|
|
245
|
+
dependencies,
|
|
246
|
+
summary: {
|
|
247
|
+
total: dependencies.length,
|
|
248
|
+
withIssues: withIssues.length,
|
|
249
|
+
outdatedPatterns: dependencies.filter((d) =>
|
|
250
|
+
d.issues.some((i) => i.includes("EOL") || i.includes("이전"))
|
|
251
|
+
).length,
|
|
252
|
+
securityConcerns: dependencies.filter((d) =>
|
|
253
|
+
d.issues.some((i) => i.includes("취약점") || i.includes("CVE"))
|
|
254
|
+
).length,
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// 도구 정의
|
|
260
|
+
export const dependencyTools: Tool[] = [
|
|
261
|
+
{
|
|
262
|
+
name: "dependency_check",
|
|
263
|
+
description:
|
|
264
|
+
"프로젝트 의존성을 분석합니다. package.json, pom.xml, build.gradle에서 의존성을 추출하고 알려진 취약점, deprecated 패키지, 버전 문제를 검사합니다.",
|
|
265
|
+
parameters: {
|
|
266
|
+
type: "object",
|
|
267
|
+
properties: {
|
|
268
|
+
path: {
|
|
269
|
+
type: "string",
|
|
270
|
+
description: "분석할 파일 또는 디렉토리 경로",
|
|
271
|
+
},
|
|
272
|
+
recursive: {
|
|
273
|
+
type: "boolean",
|
|
274
|
+
description: "디렉토리인 경우 하위 폴더 포함 여부 (기본: true)",
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
required: ["path"],
|
|
278
|
+
},
|
|
279
|
+
handler: async (args: Record<string, unknown>): Promise<ToolResult> => {
|
|
280
|
+
const targetPath = args.path as string;
|
|
281
|
+
const recursive = args.recursive !== false;
|
|
282
|
+
|
|
283
|
+
if (!fs.existsSync(targetPath)) {
|
|
284
|
+
return {
|
|
285
|
+
success: false,
|
|
286
|
+
content: "",
|
|
287
|
+
error: `경로를 찾을 수 없습니다: ${targetPath}`,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const results: DependencyAnalysisResult[] = [];
|
|
292
|
+
const stats = fs.statSync(targetPath);
|
|
293
|
+
const depFiles = ["package.json", "pom.xml", "build.gradle"];
|
|
294
|
+
|
|
295
|
+
const analyzeFile = (filePath: string) => {
|
|
296
|
+
const fileName = path.basename(filePath);
|
|
297
|
+
if (fileName === "package.json") {
|
|
298
|
+
try {
|
|
299
|
+
results.push(analyzePackageJson(filePath));
|
|
300
|
+
} catch (e) {
|
|
301
|
+
// JSON 파싱 실패 무시
|
|
302
|
+
}
|
|
303
|
+
} else if (fileName === "pom.xml") {
|
|
304
|
+
results.push(analyzePomXml(filePath));
|
|
305
|
+
} else if (fileName === "build.gradle") {
|
|
306
|
+
results.push(analyzeBuildGradle(filePath));
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
if (stats.isFile()) {
|
|
311
|
+
analyzeFile(targetPath);
|
|
312
|
+
} else if (stats.isDirectory()) {
|
|
313
|
+
const walkDir = (dir: string) => {
|
|
314
|
+
const files = fs.readdirSync(dir);
|
|
315
|
+
for (const file of files) {
|
|
316
|
+
const filePath = path.join(dir, file);
|
|
317
|
+
const fileStat = fs.statSync(filePath);
|
|
318
|
+
if (fileStat.isDirectory() && recursive) {
|
|
319
|
+
if (!file.startsWith(".") && file !== "node_modules" && file !== "target" && file !== "build") {
|
|
320
|
+
walkDir(filePath);
|
|
321
|
+
}
|
|
322
|
+
} else if (depFiles.includes(file)) {
|
|
323
|
+
analyzeFile(filePath);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
walkDir(targetPath);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// 전체 통계
|
|
331
|
+
const totalDeps = results.reduce((sum, r) => sum + r.summary.total, 0);
|
|
332
|
+
const totalWithIssues = results.reduce((sum, r) => sum + r.summary.withIssues, 0);
|
|
333
|
+
const totalSecurity = results.reduce((sum, r) => sum + r.summary.securityConcerns, 0);
|
|
334
|
+
|
|
335
|
+
const output = {
|
|
336
|
+
analyzedFiles: results.length,
|
|
337
|
+
totalDependencies: totalDeps,
|
|
338
|
+
dependenciesWithIssues: totalWithIssues,
|
|
339
|
+
securityConcerns: totalSecurity,
|
|
340
|
+
projects: results.map((r) => ({
|
|
341
|
+
file: r.file,
|
|
342
|
+
type: r.type,
|
|
343
|
+
projectName: r.projectName,
|
|
344
|
+
projectVersion: r.projectVersion,
|
|
345
|
+
summary: r.summary,
|
|
346
|
+
issuesFound: r.dependencies
|
|
347
|
+
.filter((d) => d.issues.length > 0)
|
|
348
|
+
.map((d) => ({
|
|
349
|
+
name: d.name,
|
|
350
|
+
version: d.version,
|
|
351
|
+
type: d.type,
|
|
352
|
+
issues: d.issues,
|
|
353
|
+
})),
|
|
354
|
+
})),
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
success: true,
|
|
359
|
+
content: JSON.stringify(output, null, 2),
|
|
360
|
+
};
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
];
|
package/src/core/tools/index.ts
CHANGED
|
@@ -11,6 +11,9 @@ import { sqlTools } from "./sqlAnalysis.js";
|
|
|
11
11
|
import { mybatisTools } from "./mybatisAnalysis.js";
|
|
12
12
|
import { cssTools } from "./cssAnalysis.js";
|
|
13
13
|
import { htmlTools } from "./htmlAnalysis.js";
|
|
14
|
+
import { dependencyTools } from "./dependencyAnalysis.js";
|
|
15
|
+
import { openapiTools } from "./openapiAnalysis.js";
|
|
16
|
+
import { pythonTools } from "./pythonAnalysis.js";
|
|
14
17
|
|
|
15
18
|
export * from "./types.js";
|
|
16
19
|
export * from "./builtIn.js";
|
|
@@ -25,10 +28,13 @@ export * from "./sqlAnalysis.js";
|
|
|
25
28
|
export * from "./mybatisAnalysis.js";
|
|
26
29
|
export * from "./cssAnalysis.js";
|
|
27
30
|
export * from "./htmlAnalysis.js";
|
|
31
|
+
export * from "./dependencyAnalysis.js";
|
|
32
|
+
export * from "./openapiAnalysis.js";
|
|
33
|
+
export * from "./pythonAnalysis.js";
|
|
28
34
|
|
|
29
35
|
// All available tools
|
|
30
36
|
export function getAllTools(): Tool[] {
|
|
31
|
-
return [...builtInTools, ...standardsTools, ...cacheTools, ...astTools, ...embeddingTools, ...memoryTools, ...javaTools, ...frontendTools, ...sqlTools, ...mybatisTools, ...cssTools, ...htmlTools];
|
|
37
|
+
return [...builtInTools, ...standardsTools, ...cacheTools, ...astTools, ...embeddingTools, ...memoryTools, ...javaTools, ...frontendTools, ...sqlTools, ...mybatisTools, ...cssTools, ...htmlTools, ...dependencyTools, ...openapiTools, ...pythonTools];
|
|
32
38
|
}
|
|
33
39
|
|
|
34
40
|
// Get tool by name
|