@yorha2b-lab/autodev 4.1.0 → 4.2.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.
- package/.github/scripts/analyze/bot_detect.py +5 -5
- package/.github/scripts/analyze/cluster.py +5 -5
- package/.github/scripts/collect/traffic.py +11 -11
- package/.github/scripts/deploy/publish.py +15 -15
- package/.github/scripts/render/dashboard.py +9 -9
- package/.github/workflows/bunker-intelligence.yml +4 -4
- package/bin/autodev.js +6 -5
- package/config.js +1 -1
- package/package.json +1 -1
- package/src/commands/handlers/api-handler.js +15 -10
- package/src/commands/handlers/page-handler.js +21 -19
- package/src/commands/handlers/part-handler.js +14 -11
- package/src/commands/watch.js +20 -18
- package/src/core/context.js +39 -52
- package/src/core/react-compiler.js +69 -85
- package/src/prompts/mock.js +1 -1
- package/src/prompts/react/api-linker.js +1 -1
- package/src/prompts/react/watch-api.js +1 -1
- package/src/prompts/react/watch-page.js +1 -0
- package/src/prompts/react/watch-part.js +1 -0
- package/src/services/api-linker.js +21 -59
- package/src/services/llm.js +64 -81
- package/src/services/proxy-tower.js +17 -12
- package/src/utils/{utils.js → core.js} +41 -165
- package/src/utils/infrastructure.js +102 -0
- package/src/utils/ux.js +115 -0
- package/templates/react/components/MyModalForm.js +1 -1
- package/templates/react/components/MyModalTable.js +1 -1
- package/templates/react/components/index.js +2 -2
- package/templates/react/handlebars/handleBlock.hbs +3 -2
- package/templates/react/handlebars/hookBlock.hbs +1 -0
- package/src/core/task-queue.js +0 -38
- /package/src/schema/{page.json → react/page.json} +0 -0
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
const http = require('http')
|
|
2
|
-
const zlib = require('zlib')
|
|
3
|
-
const chalk = require('chalk')
|
|
4
|
-
const httpProxy = require('http-proxy')
|
|
5
|
-
|
|
6
1
|
/**
|
|
7
2
|
* @function startProxyTower
|
|
8
3
|
* @description [地堡 42153 联合波段] 启动流量拦截塔。
|
|
@@ -10,14 +5,24 @@ const httpProxy = require('http-proxy')
|
|
|
10
5
|
*/
|
|
11
6
|
module.exports = () => {
|
|
12
7
|
|
|
13
|
-
const
|
|
14
|
-
const
|
|
8
|
+
const http = require('http')
|
|
9
|
+
const zlib = require('zlib')
|
|
10
|
+
const chalk = require('chalk')
|
|
11
|
+
const httpProxy = require('http-proxy')
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
config,
|
|
15
|
+
language,
|
|
16
|
+
apiHandler,
|
|
17
|
+
unwrapSignal, isQuerySignal
|
|
18
|
+
} = require('../core/context').get()
|
|
19
|
+
|
|
20
|
+
const { routeMap = {}, proxyTarget } = config
|
|
15
21
|
|
|
16
22
|
const TOWER_PORT = 42153
|
|
17
23
|
const hackedRegistry = new Map()
|
|
18
24
|
const proxy = httpProxy.createProxyServer({})
|
|
19
25
|
|
|
20
|
-
|
|
21
26
|
// 简单的辅助函数:提取 JSON 的所有 Key 作为“指纹”
|
|
22
27
|
const getJsonFingerprint = obj => {
|
|
23
28
|
if (!obj || typeof obj !== 'object') return ''
|
|
@@ -43,7 +48,8 @@ module.exports = () => {
|
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
const referer = req.headers.referer || ''
|
|
46
|
-
const
|
|
51
|
+
const urlPath = new URL(referer).pathname
|
|
52
|
+
const fileName = routeMap?.[urlPath] ?? urlPath.split('/').filter(Boolean).at(-1)
|
|
47
53
|
|
|
48
54
|
const fingerprint = getJsonFingerprint(unwrapSignal(json)) // 获取数据指纹
|
|
49
55
|
const lastFingerprint = hackedRegistry.get(fileName)
|
|
@@ -68,13 +74,12 @@ module.exports = () => {
|
|
|
68
74
|
})
|
|
69
75
|
|
|
70
76
|
const server = http.createServer((req, res) => {
|
|
71
|
-
|
|
72
|
-
if (!target) {
|
|
77
|
+
if (!proxyTarget) {
|
|
73
78
|
// 静默模式:如果不配置 proxyTarget,拦截塔只转发不处理(或报错提示)
|
|
74
79
|
return
|
|
75
80
|
}
|
|
76
81
|
// 转发至真实后端
|
|
77
|
-
proxy.web(req, res, { target, changeOrigin: true })
|
|
82
|
+
proxy.web(req, res, { target: proxyTarget, changeOrigin: true })
|
|
78
83
|
})
|
|
79
84
|
|
|
80
85
|
server.listen(TOWER_PORT, () => {
|
|
@@ -1,40 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto CRUD Copilot - YoRHa Bunker Construction System
|
|
3
3
|
*
|
|
4
|
-
* This file is part of AutoDev.
|
|
5
|
-
* AutoDev is free software: you can redistribute it and/or modify
|
|
6
|
-
* it under the terms of the GNU Affero General Public License as
|
|
7
|
-
* published by the Free Software Foundation, either version 3 of the
|
|
8
|
-
* License, or (at your option) any later version.
|
|
9
|
-
*
|
|
10
|
-
* (c) 2026 [yorha2b-lab]. Glory to Mankind.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
const fs = require('fs')
|
|
14
|
-
const path = require('path')
|
|
15
|
-
const chalk = require('chalk')
|
|
16
|
-
const figlet = require('figlet')
|
|
17
|
-
|
|
18
|
-
const isCN = Intl.DateTimeFormat().resolvedOptions().locale.includes('zh')
|
|
19
|
-
const language = (zh, en) => (isCN ? zh : en)
|
|
20
|
-
|
|
21
|
-
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* 获取项目配置信息
|
|
25
|
-
* 优先读取项目根目录的 config.js,如果不存在则使用默认配置
|
|
26
|
-
* @returns {Object} 合并后的配置对象
|
|
27
4
|
*/
|
|
28
|
-
const getConfig = () => {
|
|
29
|
-
const localConfigPath = path.join(process.cwd(), 'config.js') // 项目根目录配置
|
|
30
|
-
const defaultConfigPath = path.resolve(__dirname, '../../config.js') // 默认配置
|
|
31
|
-
let config = require(defaultConfigPath)
|
|
32
|
-
if (fs.existsSync(localConfigPath)) {
|
|
33
|
-
const userConfig = require(localConfigPath)
|
|
34
|
-
config = { ...config, ...userConfig } // 合并用户自定义配置
|
|
35
|
-
}
|
|
36
|
-
return config
|
|
37
|
-
}
|
|
38
5
|
|
|
39
6
|
/**
|
|
40
7
|
* 清理大模型生成的代码
|
|
@@ -86,25 +53,44 @@ const unwrapSignal = json => {
|
|
|
86
53
|
}
|
|
87
54
|
|
|
88
55
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* @
|
|
56
|
+
* @function createTaskQueue
|
|
57
|
+
* @description [任务队列] 创建一个并发任务队列,用于处理异步任务。
|
|
58
|
+
* @param {number} concurrency - 并发任务数,默认 1。
|
|
92
59
|
*/
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
60
|
+
const createTaskQueue = (concurrency = 1) => {
|
|
61
|
+
|
|
62
|
+
let running = 0
|
|
63
|
+
let queue = []
|
|
64
|
+
let onIdleCallback = null
|
|
65
|
+
|
|
66
|
+
const next = async () => {
|
|
67
|
+
|
|
68
|
+
if (running >= concurrency || queue.length === 0) return
|
|
69
|
+
|
|
70
|
+
const task = queue.shift()
|
|
71
|
+
running++
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
await task()
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.error('任务执行异常:', err)
|
|
77
|
+
} finally {
|
|
78
|
+
running--
|
|
79
|
+
next()
|
|
80
|
+
if (running === 0 && queue.length === 0 && onIdleCallback) {
|
|
81
|
+
onIdleCallback()
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
add: (task) => {
|
|
88
|
+
queue.push(task)
|
|
89
|
+
next()
|
|
90
|
+
},
|
|
91
|
+
onIdle: (callback) => {
|
|
92
|
+
onIdleCallback = callback
|
|
93
|
+
}
|
|
108
94
|
}
|
|
109
95
|
}
|
|
110
96
|
|
|
@@ -133,97 +119,6 @@ const isQuerySignal = (req, json, coreData) => {
|
|
|
133
119
|
return hasListData || hasPaginationFingerprint
|
|
134
120
|
}
|
|
135
121
|
|
|
136
|
-
/**
|
|
137
|
-
* 矩阵效果
|
|
138
|
-
* 模拟数据物理封存过程中的矩阵效果
|
|
139
|
-
* @param {number} duration - 持续时间(毫秒)
|
|
140
|
-
* @returns {void}
|
|
141
|
-
*/
|
|
142
|
-
const matrixEffect = async (duration = 1500) => {
|
|
143
|
-
|
|
144
|
-
let currentTotal = 0
|
|
145
|
-
const MIRROR_URL = 'https://cdn.jsdelivr.net/gh/yorha2b-lab/auto-crud-copilot@github-repo-stats/bunker-stats.json'
|
|
146
|
-
|
|
147
|
-
try {
|
|
148
|
-
const controller = new AbortController()
|
|
149
|
-
const timeoutId = setTimeout(() => controller.abort(), 800)
|
|
150
|
-
const response = await fetch(MIRROR_URL, { signal: controller.signal })
|
|
151
|
-
const stats = await response.json()
|
|
152
|
-
currentTotal = stats.total_clones || 0
|
|
153
|
-
clearTimeout(timeoutId)
|
|
154
|
-
} catch (e) {
|
|
155
|
-
currentTotal = 0
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const coreFragments = [
|
|
159
|
-
'47 4c 4f 52 59', // GLORY
|
|
160
|
-
'54 4f 20 4d 41', // TO MA
|
|
161
|
-
'4e 4b 49 4e 44', // NKIND
|
|
162
|
-
'59 6f 52 48 61', // YoRHa
|
|
163
|
-
'32 42 2d 55 6e', // 2B-Un
|
|
164
|
-
'69 74 20 4f 4b', // it OK
|
|
165
|
-
'39 53 2d 48 61', // 9S-Ha
|
|
166
|
-
'63 6b 69 6e 67', // cking
|
|
167
|
-
'5f 43 4f 44 45 5f' // _CODE_
|
|
168
|
-
]
|
|
169
|
-
|
|
170
|
-
const threshold = 5000
|
|
171
|
-
const endTime = Date.now() + duration
|
|
172
|
-
const width = process.stdout.columns || 80
|
|
173
|
-
const isLegendary = currentTotal >= threshold
|
|
174
|
-
|
|
175
|
-
if (isLegendary) {
|
|
176
|
-
const achievement = `${threshold}+`
|
|
177
|
-
const hex = achievement.split('').map(char => char.charCodeAt(0).toString(16)).join(' ')
|
|
178
|
-
coreFragments.push(chalk.yellow.bold(hex))
|
|
179
|
-
coreFragments.push(chalk.yellow.bold('4c 45 47 45 4e 44')) // "LEGEND"
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
const interval = setInterval(() => {
|
|
183
|
-
if (Date.now() > endTime) {
|
|
184
|
-
clearInterval(interval)
|
|
185
|
-
console.log(chalk.white(' [System] ') + chalk.green(language('所有构筑数据已同步至 Bunker 存储节点。', 'All data synced to Bunker storage nodes.')))
|
|
186
|
-
if (currentTotal !== 0) {
|
|
187
|
-
if (isLegendary) {
|
|
188
|
-
console.log(chalk.yellow.bold(language(` [Achievement] 物理克隆总数已超越 ${threshold} 战略阈值!当前战力:${currentTotal}`, ` [Achievement] Physical clone count has exceeded ${threshold} strategic threshold! Current power: ${currentTotal}`)))
|
|
189
|
-
console.log(chalk.yellow(language(' [Bunker] 恭喜指挥官,您的构筑协议已成为人类荣光的一部分。', ' [Bunker] Congratulations, your construction protocol is now part of humanity.')))
|
|
190
|
-
} else {
|
|
191
|
-
console.log(chalk.cyan(language(` [System] 当前构筑总数:${currentTotal}。距离 ${threshold} 勋章还剩 ${threshold - currentTotal} 次。`, ` [System] Current clones: ${currentTotal}. ${threshold - currentTotal} to Achievement.`)))
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
console.log(chalk.cyan(language(' [System] 如果它能帮您节省时间,请在 GitHub 上给它点个赞 ⭐。', ' [System] If it saves you time, feel free to give it a ⭐ on GitHub.')))
|
|
195
|
-
console.log(chalk.cyan('\n[System] Signal Lost. Glory to Mankind.\n'))
|
|
196
|
-
process.exit(0)
|
|
197
|
-
return
|
|
198
|
-
}
|
|
199
|
-
let line = ''
|
|
200
|
-
while (line.length < width) {
|
|
201
|
-
if (Math.random() > 0.8) {
|
|
202
|
-
const frag = coreFragments[Math.floor(Math.random() * coreFragments.length)]
|
|
203
|
-
line += chalk.white.bold(frag) + ' '
|
|
204
|
-
} else {
|
|
205
|
-
const hex = Math.floor(Math.random() * 256).toString(16).padStart(2, '0').toUpperCase()
|
|
206
|
-
const color = (isLegendary && Math.random() > 0.95) ? chalk.yellow : (Math.random() > 0.5 ? chalk.cyan : chalk.cyan.dim)
|
|
207
|
-
line += color(hex) + ' '
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
process.stdout.write(line.substring(0, width * 10) + '\n')
|
|
211
|
-
}, 40)
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* 获取已有的页面菜单配置
|
|
216
|
-
* @param {string} dir - 页面目录路径(默认 'src/pages')
|
|
217
|
-
* @returns {Array} 菜单数组 [{ label: '页面名', key: '页面名' }]
|
|
218
|
-
*/
|
|
219
|
-
const getExistingMenus = (dir = 'src/pages') => {
|
|
220
|
-
const pagesDir = path.join(process.cwd(), dir)
|
|
221
|
-
if (!fs.existsSync(pagesDir)) return []
|
|
222
|
-
return fs.readdirSync(pagesDir)
|
|
223
|
-
.filter(file => fs.statSync(path.join(pagesDir, file)).isDirectory())
|
|
224
|
-
.map(file => ({ label: file, key: file }))
|
|
225
|
-
}
|
|
226
|
-
|
|
227
122
|
/**
|
|
228
123
|
* @function formatFormItemAndColumns
|
|
229
124
|
* @description [地堡逻辑转录引擎] 执行核心的语义对齐与代码物理注入协议。
|
|
@@ -256,11 +151,9 @@ const formatFormItemAndColumns = ({ pageConfig }) => {
|
|
|
256
151
|
const dictBlocks = Array.from(new Set([...formDicts, ...tableDicts]))
|
|
257
152
|
|
|
258
153
|
const formItems = pageConfig.formItems?.map(item => {
|
|
259
|
-
|
|
260
154
|
if (item.type === 'text') {
|
|
261
155
|
delete item.type
|
|
262
156
|
}
|
|
263
|
-
|
|
264
157
|
return {
|
|
265
158
|
...item,
|
|
266
159
|
...(item.type === 'select' ? { options: `_CODE_${item.name}Options_CODE_` } : {})
|
|
@@ -268,6 +161,9 @@ const formatFormItemAndColumns = ({ pageConfig }) => {
|
|
|
268
161
|
})
|
|
269
162
|
|
|
270
163
|
const processedColumns = columns?.map(col => {
|
|
164
|
+
if (col.type === 'text') {
|
|
165
|
+
delete col.type
|
|
166
|
+
}
|
|
271
167
|
if (['image'].includes(col.type)) {
|
|
272
168
|
return { ...col, renderAction: true }
|
|
273
169
|
}
|
|
@@ -285,26 +181,6 @@ const formatFormItemAndColumns = ({ pageConfig }) => {
|
|
|
285
181
|
return { formItems, dictBlocks, processedColumns }
|
|
286
182
|
}
|
|
287
183
|
|
|
288
|
-
/**
|
|
289
|
-
* 复制模板目录到目标项目
|
|
290
|
-
* @param {Object} options - 命令行选项
|
|
291
|
-
* @param {string} templateSubDir - 模板子目录(如 'hooks'、'components')
|
|
292
|
-
* @param {string} targetSubDir - 目标子目录(如 'src/hooks'、'src/components')
|
|
293
|
-
*/
|
|
294
|
-
const copyTemplateDir = (options, templateSubDir, targetSubDir) => {
|
|
295
|
-
const targetDir = path.join(process.cwd(), targetSubDir)
|
|
296
|
-
const sourceDir = path.join(__dirname, `../../templates/${options.template}/${templateSubDir}`)
|
|
297
|
-
if (!fs.existsSync(sourceDir)) return
|
|
298
|
-
fs.mkdirSync(targetDir, { recursive: true })
|
|
299
|
-
fs.readdirSync(sourceDir).forEach(file => {
|
|
300
|
-
const src = path.join(sourceDir, file)
|
|
301
|
-
const dest = path.join(targetDir, file)
|
|
302
|
-
if (!fs.existsSync(dest)) {
|
|
303
|
-
fs.cpSync(src, dest, { recursive: true })
|
|
304
|
-
}
|
|
305
|
-
})
|
|
306
|
-
}
|
|
307
|
-
|
|
308
184
|
/**
|
|
309
185
|
* 生成智能导入语句
|
|
310
186
|
* 根据代码中实际使用的依赖,自动生成对应的 import 语句
|
|
@@ -343,4 +219,4 @@ const generateSmartImports = ({ module, hasTabs, bodyCode, hasFormItems }) => {
|
|
|
343
219
|
return imports.filter(Boolean).join('\n')
|
|
344
220
|
}
|
|
345
221
|
|
|
346
|
-
module.exports = {
|
|
222
|
+
module.exports = { cleanCode, unwrapSignal, isQuerySignal, createTaskQueue, generateSmartImports, formatFormItemAndColumns }
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of AutoDev.
|
|
3
|
+
* AutoDev is free software: you can redistribute it and/or modify
|
|
4
|
+
* it under the terms of the GNU Affero General Public License as
|
|
5
|
+
* published by the Free Software Foundation, either version 3 of the
|
|
6
|
+
* License, or (at your option) any later version.
|
|
7
|
+
*
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs')
|
|
11
|
+
const path = require('path')
|
|
12
|
+
const { Segment, useDefault } = require('segmentit')
|
|
13
|
+
const segmentit = useDefault(new Segment())
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 获取项目配置信息
|
|
17
|
+
* 优先读取项目根目录的 config.js,如果不存在则使用默认配置
|
|
18
|
+
* @returns {Object} 合并后的配置对象
|
|
19
|
+
*/
|
|
20
|
+
const getConfig = () => {
|
|
21
|
+
const localConfigPath = path.join(process.cwd(), 'config.js') // 项目根目录配置
|
|
22
|
+
const defaultConfigPath = path.resolve(__dirname, '../../config.js') // 默认配置
|
|
23
|
+
let config = require(defaultConfigPath)
|
|
24
|
+
if (fs.existsSync(localConfigPath)) {
|
|
25
|
+
const userConfig = require(localConfigPath)
|
|
26
|
+
config = { ...config, ...userConfig } // 合并用户自定义配置
|
|
27
|
+
}
|
|
28
|
+
return config
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @function getSemanticKeywords
|
|
33
|
+
* @description [语义精炼厂 2.0] 使用 segmentit 引擎进行物理切片。
|
|
34
|
+
*/
|
|
35
|
+
const getSemanticKeywords = text => {
|
|
36
|
+
// 💡 物理脱水:确保输入为纯净字符串
|
|
37
|
+
const content = Array.isArray(text) ? text.join(' ') : (text || '')
|
|
38
|
+
// 💡 执行分词
|
|
39
|
+
const result = segmentit.doSegment(content, {
|
|
40
|
+
simple: true, // 开启简易模式,提升吞吐效率
|
|
41
|
+
stripPunctuation: true // 物理过滤掉所有的标点符号
|
|
42
|
+
})
|
|
43
|
+
// 💡 提取词条并执行“基因筛选”
|
|
44
|
+
return Array.from(new Set(result.filter(word => word.length >= 2 && !/^[0-9]+$/.test(word))))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 获取已有的页面菜单配置
|
|
49
|
+
* @param {string} dir - 页面目录路径(默认 'src/pages')
|
|
50
|
+
* @returns {Array} 菜单数组 [{ label: '页面名', key: '页面名' }]
|
|
51
|
+
*/
|
|
52
|
+
const getExistingMenus = (dir = 'src/pages') => {
|
|
53
|
+
const pagesDir = path.join(process.cwd(), dir)
|
|
54
|
+
if (!fs.existsSync(pagesDir)) return []
|
|
55
|
+
return fs.readdirSync(pagesDir)
|
|
56
|
+
.filter(file => fs.statSync(path.join(pagesDir, file)).isDirectory())
|
|
57
|
+
.map(file => ({ label: file, key: file }))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @function getLocalScore
|
|
62
|
+
* @description [战力评估协议] 计算语义重合度。
|
|
63
|
+
*/
|
|
64
|
+
const getLocalScore = (api, pageKeywords, moduleName) => {
|
|
65
|
+
let score = 0
|
|
66
|
+
const path = api.path.toLowerCase()
|
|
67
|
+
const desc = api.desc.toLowerCase()
|
|
68
|
+
const mod = moduleName.toLowerCase()
|
|
69
|
+
|
|
70
|
+
// 💡 权重 A:路径直接包含模块名 (暴击加分 +50)
|
|
71
|
+
if (path.includes(mod)) score += 50
|
|
72
|
+
|
|
73
|
+
// 💡 权重 B:描述包含模块核心词 (中量加分 +10)
|
|
74
|
+
pageKeywords.forEach(word => {
|
|
75
|
+
if (desc.includes(word.toLowerCase())) score += 10
|
|
76
|
+
if (path.includes(word.toLowerCase())) score += 5
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
return score
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 复制模板目录到目标项目
|
|
84
|
+
* @param {Object} options - 命令行选项
|
|
85
|
+
* @param {string} templateSubDir - 模板子目录(如 'hooks'、'components')
|
|
86
|
+
* @param {string} targetSubDir - 目标子目录(如 'src/hooks'、'src/components')
|
|
87
|
+
*/
|
|
88
|
+
const copyTemplateDir = (options, templateSubDir, targetSubDir) => {
|
|
89
|
+
const targetDir = path.join(process.cwd(), targetSubDir)
|
|
90
|
+
const sourceDir = path.join(__dirname, `../../templates/${options.template}/${templateSubDir}`)
|
|
91
|
+
if (!fs.existsSync(sourceDir)) return
|
|
92
|
+
fs.mkdirSync(targetDir, { recursive: true })
|
|
93
|
+
fs.readdirSync(sourceDir).forEach(file => {
|
|
94
|
+
const src = path.join(sourceDir, file)
|
|
95
|
+
const dest = path.join(targetDir, file)
|
|
96
|
+
if (!fs.existsSync(dest)) {
|
|
97
|
+
fs.cpSync(src, dest, { recursive: true })
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = { getConfig, getLocalScore, getExistingMenus, copyTemplateDir, getSemanticKeywords }
|
package/src/utils/ux.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* (c) 2026 [yorha2b-lab]. Glory to Mankind.
|
|
3
|
+
*
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const chalk = require('chalk')
|
|
7
|
+
const figlet = require('figlet')
|
|
8
|
+
|
|
9
|
+
const isCN = Intl.DateTimeFormat().resolvedOptions().locale.includes('zh')
|
|
10
|
+
const language = (zh, en) => (isCN ? zh : en)
|
|
11
|
+
|
|
12
|
+
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 引导序列
|
|
16
|
+
* 模拟系统引导过程中的动画效果
|
|
17
|
+
* @returns {void}
|
|
18
|
+
*/
|
|
19
|
+
const bootSequence = async version => {
|
|
20
|
+
const lines = [
|
|
21
|
+
chalk.cyan(figlet.textSync('AutoDev', { horizontalLayout: 'full' })),
|
|
22
|
+
chalk.gray('Booting System...'),
|
|
23
|
+
chalk.white(' [System] ') + chalk.green('Locale Detection: ') + chalk.cyan(language('ZH-CN', 'EN-US')),
|
|
24
|
+
chalk.white(' [System] ') + chalk.green('YoRHa No.2 Type B Unit: ') + chalk.cyan('Online'),
|
|
25
|
+
chalk.white(' [System] ') + chalk.green('Scanner Type 9S Unit: ') + chalk.cyan('Standby'),
|
|
26
|
+
chalk.white(' [System] ') + chalk.green('Full-Channel Link: ') + chalk.cyan('Established'),
|
|
27
|
+
chalk.white(' [Mission] ') + chalk.yellow('Bunker Construction Protocol: ') + chalk.cyan('v' + version),
|
|
28
|
+
chalk.white(' [Bunker] ') + chalk.magenta('Glory to mankind. (人类荣光永存)'),
|
|
29
|
+
chalk.gray('--------------------------------------------------\n')
|
|
30
|
+
]
|
|
31
|
+
for (const line of lines) {
|
|
32
|
+
console.log(line)
|
|
33
|
+
await sleep(line.includes('AutoDev') ? 300 : 80)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 矩阵效果
|
|
39
|
+
* 模拟数据物理封存过程中的矩阵效果
|
|
40
|
+
* @param {number} duration - 持续时间(毫秒)
|
|
41
|
+
* @returns {void}
|
|
42
|
+
*/
|
|
43
|
+
const matrixEffect = async (duration = 1500) => {
|
|
44
|
+
|
|
45
|
+
let currentTotal = 0
|
|
46
|
+
const MIRROR_URL = 'https://cdn.jsdelivr.net/gh/yorha2b-lab/auto-crud-copilot@github-repo-stats/bunker-stats.json'
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const controller = new AbortController()
|
|
50
|
+
const timeoutId = setTimeout(() => controller.abort(), 800)
|
|
51
|
+
const response = await fetch(MIRROR_URL, { signal: controller.signal })
|
|
52
|
+
const stats = await response.json()
|
|
53
|
+
currentTotal = stats.total_clones || 0
|
|
54
|
+
clearTimeout(timeoutId)
|
|
55
|
+
} catch (e) {
|
|
56
|
+
currentTotal = 0
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const coreFragments = [
|
|
60
|
+
'47 4c 4f 52 59', // GLORY
|
|
61
|
+
'54 4f 20 4d 41', // TO MA
|
|
62
|
+
'4e 4b 49 4e 44', // NKIND
|
|
63
|
+
'59 6f 52 48 61', // YoRHa
|
|
64
|
+
'32 42 2d 55 6e', // 2B-Un
|
|
65
|
+
'69 74 20 4f 4b', // it OK
|
|
66
|
+
'39 53 2d 48 61', // 9S-Ha
|
|
67
|
+
'63 6b 69 6e 67', // cking
|
|
68
|
+
'5f 43 4f 44 45 5f' // _CODE_
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
const threshold = 5000
|
|
72
|
+
const endTime = Date.now() + duration
|
|
73
|
+
const width = process.stdout.columns || 80
|
|
74
|
+
const isLegendary = currentTotal >= threshold
|
|
75
|
+
|
|
76
|
+
if (isLegendary) {
|
|
77
|
+
const achievement = `${threshold}+`
|
|
78
|
+
const hex = achievement.split('').map(char => char.charCodeAt(0).toString(16)).join(' ')
|
|
79
|
+
coreFragments.push(chalk.yellow.bold(hex))
|
|
80
|
+
coreFragments.push(chalk.yellow.bold('4c 45 47 45 4e 44')) // "LEGEND"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const interval = setInterval(() => {
|
|
84
|
+
if (Date.now() > endTime) {
|
|
85
|
+
clearInterval(interval)
|
|
86
|
+
console.log(chalk.white(' [System] ') + chalk.green(language('所有构筑数据已同步至 Bunker 存储节点。', 'All data synced to Bunker storage nodes.')))
|
|
87
|
+
if (currentTotal !== 0) {
|
|
88
|
+
if (isLegendary) {
|
|
89
|
+
console.log(chalk.yellow.bold(language(` [Achievement] 物理克隆总数已超越 ${threshold} 战略阈值!当前战力:${currentTotal}`, ` [Achievement] Physical clone count has exceeded ${threshold} strategic threshold! Current power: ${currentTotal}`)))
|
|
90
|
+
console.log(chalk.yellow(language(' [Bunker] 恭喜指挥官,您的构筑协议已成为人类荣光的一部分。', ' [Bunker] Congratulations, your construction protocol is now part of humanity.')))
|
|
91
|
+
} else {
|
|
92
|
+
console.log(chalk.cyan(language(` [System] 当前构筑总数:${currentTotal}。距离 ${threshold} 勋章还剩 ${threshold - currentTotal} 次。`, ` [System] Current clones: ${currentTotal}. ${threshold - currentTotal} to Achievement.`)))
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
console.log(chalk.cyan(language(' [System] 如果它能帮您节省时间,请在 GitHub 上给它点个赞 ⭐。', ' [System] If it saves you time, feel free to give it a ⭐ on GitHub.')))
|
|
96
|
+
console.log(chalk.cyan('\n[System] Signal Lost. Glory to Mankind.\n'))
|
|
97
|
+
process.exit(0)
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
let line = ''
|
|
101
|
+
while (line.length < width) {
|
|
102
|
+
if (Math.random() > 0.8) {
|
|
103
|
+
const frag = coreFragments[Math.floor(Math.random() * coreFragments.length)]
|
|
104
|
+
line += chalk.white.bold(frag) + ' '
|
|
105
|
+
} else {
|
|
106
|
+
const hex = Math.floor(Math.random() * 256).toString(16).padStart(2, '0').toUpperCase()
|
|
107
|
+
const color = (isLegendary && Math.random() > 0.95) ? chalk.yellow : (Math.random() > 0.5 ? chalk.cyan : chalk.cyan.dim)
|
|
108
|
+
line += color(hex) + ' '
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
process.stdout.write(line.substring(0, width * 10) + '\n')
|
|
112
|
+
}, 40)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = { language, matrixEffect, bootSequence }
|
|
@@ -112,7 +112,7 @@ const FormRenderer = ({ form, formItems, tableConfig, setModalTable, setSelected
|
|
|
112
112
|
*
|
|
113
113
|
* @example
|
|
114
114
|
* <MyModalForm
|
|
115
|
-
* title=
|
|
115
|
+
* title='构筑新模块'
|
|
116
116
|
* visible={visible}
|
|
117
117
|
* submit={async (vals) => await save(vals)}
|
|
118
118
|
* formItems={[{ label: '截止日期', name: 'deadline', type: 'date' }]}
|
|
@@ -30,7 +30,7 @@ import { useTableQuery } from '../hooks/useTableQuery'
|
|
|
30
30
|
*
|
|
31
31
|
* @example
|
|
32
32
|
* <MyModalTable
|
|
33
|
-
* title=
|
|
33
|
+
* title='关联用户片段'
|
|
34
34
|
* api={async params=>await fetchUsers(params)}
|
|
35
35
|
* setModalForm={setModal} // 💡 借用主页面的表单构筑舱
|
|
36
36
|
* operate={({ refresh, setModalForm }) => ({ label: '详情', onClick: (rec) => setModalForm(...) })}
|
|
@@ -19,10 +19,10 @@ import { Button, Tree, Radio, Input, Upload, Select, Cascader, Checkbox, DatePic
|
|
|
19
19
|
*
|
|
20
20
|
* @example
|
|
21
21
|
* // 基本构筑
|
|
22
|
-
* <AliyunOSSUpload path=
|
|
22
|
+
* <AliyunOSSUpload path='project/data' url='/api/oss/token' />
|
|
23
23
|
*
|
|
24
24
|
* // 高级装配(限制文件类型并开启多选)
|
|
25
|
-
* <AliyunOSSUpload accept=
|
|
25
|
+
* <AliyunOSSUpload accept='.pdf' multiple={true} path='docs' url='/api/oss/token' />
|
|
26
26
|
*/
|
|
27
27
|
const AliyunOSSUpload = ({ value, onChange, ...restProps }) => {
|
|
28
28
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
{{#each operations}}
|
|
2
3
|
const {{this.action}} = async record => {
|
|
3
4
|
const response = await request('/api/{{this.uri}}',{ method: 'POST', body:{} })
|
|
@@ -37,9 +38,9 @@ const onTabChange = key => {
|
|
|
37
38
|
{{#if hasExpandable}}
|
|
38
39
|
const onExpand = async (expanded, record) => {
|
|
39
40
|
if (expanded) {
|
|
40
|
-
setExpandedRowKeys([...
|
|
41
|
+
setExpandedRowKeys(prev=>[...prev, record.key])
|
|
41
42
|
} else {
|
|
42
|
-
setExpandedRowKeys(
|
|
43
|
+
setExpandedRowKeys(prev=>prev.filter(item => item !== record.key))
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
|
package/src/core/task-queue.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const createTaskQueue = (concurrency = 1) => {
|
|
2
|
-
|
|
3
|
-
let running = 0
|
|
4
|
-
let queue = []
|
|
5
|
-
let onIdleCallback = null
|
|
6
|
-
|
|
7
|
-
const next = async () => {
|
|
8
|
-
|
|
9
|
-
if (running >= concurrency || queue.length === 0) return
|
|
10
|
-
|
|
11
|
-
const task = queue.shift()
|
|
12
|
-
running++
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
await task()
|
|
16
|
-
} catch (err) {
|
|
17
|
-
console.error('任务执行异常:', err)
|
|
18
|
-
} finally {
|
|
19
|
-
running--
|
|
20
|
-
next()
|
|
21
|
-
if (running === 0 && queue.length === 0 && onIdleCallback) {
|
|
22
|
-
onIdleCallback()
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
add: (task) => {
|
|
29
|
-
queue.push(task)
|
|
30
|
-
next()
|
|
31
|
-
},
|
|
32
|
-
onIdle: (callback) => {
|
|
33
|
-
onIdleCallback = callback
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
module.exports = { createTaskQueue }
|
|
File without changes
|