momo-ai 1.0.25 → 1.0.26
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/package.json
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"executor": "executeOpenapi",
|
|
3
|
+
"description": "从各子项目 src/main/resources/openapi 提取 Operation/Schema 的 md,拷贝到 -ui/.r2mo/api/ 并保持结构",
|
|
4
|
+
"command": "openapi",
|
|
5
|
+
"options": [
|
|
6
|
+
{
|
|
7
|
+
"name": "dir",
|
|
8
|
+
"alias": "d",
|
|
9
|
+
"description": "项目根目录(默认当前目录)",
|
|
10
|
+
"type": "string"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* momo openapi:解析各子项目中的 src/main/resources/openapi,提取 Operation/Schema 的 md,
|
|
3
|
+
* 统一拷贝到 -ui/.r2mo/api/ 下(不包含子项目名),保持与源头一致的结构;重复文件名时确认是否覆盖。
|
|
4
|
+
*/
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const Ec = require('../epic');
|
|
8
|
+
const { parseOptional } = require('../utils/momo-args');
|
|
9
|
+
const { exists, ensureDir } = require('../utils/momo-file-utils');
|
|
10
|
+
const fsAsync = require('fs').promises;
|
|
11
|
+
|
|
12
|
+
const OPENAPI_REL = ['src', 'main', 'resources', 'openapi'];
|
|
13
|
+
const R2MO_API_REL = ['.r2mo', 'api'];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 解析 pom.xml 提取 artifactId(排除 parent 节点)
|
|
17
|
+
*/
|
|
18
|
+
const _parsePomXml = (pomPath) => {
|
|
19
|
+
try {
|
|
20
|
+
const content = fs.readFileSync(pomPath, 'utf8');
|
|
21
|
+
const withoutParent = content.replace(/<parent>[\s\S]*?<\/parent>/gi, '');
|
|
22
|
+
const match = withoutParent.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
23
|
+
return match && match[1] ? match[1].trim() : null;
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 递归收集目录下所有 .md 文件路径
|
|
31
|
+
*/
|
|
32
|
+
const _collectMdFiles = (dir) => {
|
|
33
|
+
const list = [];
|
|
34
|
+
if (!exists(dir) || !fs.statSync(dir).isDirectory()) return list;
|
|
35
|
+
const walk = (current) => {
|
|
36
|
+
const entries = fs.readdirSync(current, { withFileTypes: true });
|
|
37
|
+
for (const e of entries) {
|
|
38
|
+
const full = path.join(current, e.name);
|
|
39
|
+
if (e.isDirectory()) walk(full);
|
|
40
|
+
else if (e.name.endsWith('.md')) list.push(full);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
walk(dir);
|
|
44
|
+
return list;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 根据相对路径判断类型:Operation / Schema / 其他
|
|
49
|
+
*/
|
|
50
|
+
const _mdType = (relPath) => {
|
|
51
|
+
const lower = relPath.toLowerCase();
|
|
52
|
+
if (lower.includes('operation')) return 'Operation';
|
|
53
|
+
if (lower.includes('schema')) return 'Schema';
|
|
54
|
+
return null;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 规范化相对路径为 / 分隔(便于展示与去重)
|
|
59
|
+
*/
|
|
60
|
+
const _normalizeRel = (p) => p.split(path.sep).join('/');
|
|
61
|
+
|
|
62
|
+
module.exports = async (options) => {
|
|
63
|
+
try {
|
|
64
|
+
const dirArg = parseOptional('dir', 'd');
|
|
65
|
+
const targetDir = dirArg.hasFlag && dirArg.value
|
|
66
|
+
? path.resolve(dirArg.value)
|
|
67
|
+
: process.cwd();
|
|
68
|
+
|
|
69
|
+
if (!exists(targetDir) || !fs.statSync(targetDir).isDirectory()) {
|
|
70
|
+
Ec.error(`❌ 目录不存在或不是目录: ${targetDir}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const pomPath = path.join(targetDir, 'pom.xml');
|
|
75
|
+
if (!exists(pomPath)) {
|
|
76
|
+
Ec.error('❌ 当前目录下未找到 pom.xml,请在有根 pom 的项目根目录执行');
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const artifactId = _parsePomXml(pomPath);
|
|
81
|
+
if (!artifactId) {
|
|
82
|
+
Ec.error('❌ 无法从 pom.xml 解析 artifactId');
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const uiDir = path.join(targetDir, `${artifactId}-ui`);
|
|
87
|
+
const apiBase = path.join(uiDir, ...R2MO_API_REL);
|
|
88
|
+
if (!exists(uiDir)) {
|
|
89
|
+
Ec.error(`❌ 未找到 -ui 项目: ${uiDir}`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
Ec.info(`📋 momo openapi`);
|
|
94
|
+
Ec.info(` 项目根: ${targetDir}`);
|
|
95
|
+
Ec.info(` 统一输出路径: ${apiBase}(不包含子项目名)`);
|
|
96
|
+
Ec.info('');
|
|
97
|
+
|
|
98
|
+
// 子项目:当前目录下直接子目录
|
|
99
|
+
const entries = fs.readdirSync(targetDir, { withFileTypes: true });
|
|
100
|
+
const subprojects = entries
|
|
101
|
+
.filter(e => e.isDirectory())
|
|
102
|
+
.map(e => path.join(targetDir, e.name));
|
|
103
|
+
|
|
104
|
+
// 先收集所有 (relPath, mdPath, subName),统一按 relPath 输出
|
|
105
|
+
const items = [];
|
|
106
|
+
for (const subDir of subprojects) {
|
|
107
|
+
const openapiDir = path.join(subDir, ...OPENAPI_REL);
|
|
108
|
+
if (!exists(openapiDir) || !fs.statSync(openapiDir).isDirectory()) continue;
|
|
109
|
+
const subName = path.basename(subDir);
|
|
110
|
+
const mdFiles = _collectMdFiles(openapiDir);
|
|
111
|
+
for (const mdPath of mdFiles) {
|
|
112
|
+
const rel = path.relative(openapiDir, mdPath);
|
|
113
|
+
items.push({ relPath: _normalizeRel(rel), mdPath, subName });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 按 relPath 分组,检测重复
|
|
118
|
+
const byRel = new Map();
|
|
119
|
+
for (const it of items) {
|
|
120
|
+
if (!byRel.has(it.relPath)) byRel.set(it.relPath, []);
|
|
121
|
+
byRel.get(it.relPath).push(it);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let totalOperation = 0;
|
|
125
|
+
let totalSchema = 0;
|
|
126
|
+
let totalOther = 0;
|
|
127
|
+
let skipped = 0;
|
|
128
|
+
|
|
129
|
+
for (const [relPath, sources] of byRel) {
|
|
130
|
+
const destPath = path.join(apiBase, relPath);
|
|
131
|
+
const source = sources[sources.length - 1];
|
|
132
|
+
const needConfirm = sources.length > 1 || exists(destPath);
|
|
133
|
+
|
|
134
|
+
if (needConfirm) {
|
|
135
|
+
const msg = sources.length > 1
|
|
136
|
+
? `发现重复文件 ${relPath}(来源: ${sources.map(s => s.subName).join(', ')}),是否覆盖?(y/N): `
|
|
137
|
+
: `发现重复文件 ${relPath}(目标已存在),是否覆盖?(y/N): `;
|
|
138
|
+
const answer = await Ec.ask(msg);
|
|
139
|
+
if (answer.trim().toLowerCase() !== 'y') {
|
|
140
|
+
skipped++;
|
|
141
|
+
Ec.info(` 跳过 ${relPath}`);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (sources.length > 1) {
|
|
145
|
+
Ec.warn(` 使用来源: ${source.subName}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
await ensureDir(path.dirname(destPath));
|
|
150
|
+
await fsAsync.copyFile(source.mdPath, destPath);
|
|
151
|
+
|
|
152
|
+
const type = _mdType(relPath);
|
|
153
|
+
if (type === 'Operation') {
|
|
154
|
+
totalOperation++;
|
|
155
|
+
Ec.info(` ✓ [Operation] ${relPath}`);
|
|
156
|
+
} else if (type === 'Schema') {
|
|
157
|
+
totalSchema++;
|
|
158
|
+
Ec.info(` ✓ [Schema] ${relPath}`);
|
|
159
|
+
} else {
|
|
160
|
+
totalOther++;
|
|
161
|
+
Ec.info(` ✓ ${relPath}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const total = totalOperation + totalSchema + totalOther;
|
|
166
|
+
Ec.info('-'.repeat(40));
|
|
167
|
+
Ec.info(`🎉 处理完成: Operation ${totalOperation},Schema ${totalSchema},其他 ${totalOther},跳过 ${skipped},共 ${total} 个 md → ${apiBase}`);
|
|
168
|
+
try { Ec.askClose(); } catch (_) {}
|
|
169
|
+
process.exit(0);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
Ec.error(`❌ 执行失败: ${error.message}`);
|
|
172
|
+
try { Ec.askClose(); } catch (_) {}
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
};
|
package/src/executor/index.js
CHANGED
|
@@ -2,6 +2,7 @@ const executeHelp = require('./executeHelp');
|
|
|
2
2
|
const executeInit = require('./executeInit');
|
|
3
3
|
const executeEnv = require('./executeEnv');
|
|
4
4
|
const executeOpen = require('./executeOpen');
|
|
5
|
+
const executeOpenapi = require('./executeOpenapi');
|
|
5
6
|
const executeMcp = require('./executeMcp');
|
|
6
7
|
const executeApp = require('./executeApp');
|
|
7
8
|
const executeApply = require('./executeApply');
|
|
@@ -16,6 +17,7 @@ const exported = {
|
|
|
16
17
|
executeInit,
|
|
17
18
|
executeEnv,
|
|
18
19
|
executeOpen,
|
|
20
|
+
executeOpenapi,
|
|
19
21
|
executeMcp,
|
|
20
22
|
executeApp,
|
|
21
23
|
executeApply,
|