@yorha2b-lab/autodev 7.3.6 → 7.4.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.
@@ -77,19 +77,19 @@
77
77
  ],
78
78
  "operation": [
79
79
  {
80
- "uri": "BUNKER_API_ANCHOR_viewProfession",
80
+ "request": "BUNKER_API_ANCHOR_viewJob",
81
81
  "label": "查看",
82
- "action": "viewProfession"
82
+ "action": "viewJob"
83
83
  },
84
84
  {
85
- "uri": "BUNKER_API_ANCHOR_editProfession",
85
+ "request": "BUNKER_API_ANCHOR_editJob",
86
86
  "label": "编辑",
87
- "action": "editProfession"
87
+ "action": "editJob"
88
88
  },
89
89
  {
90
- "uri": "BUNKER_API_ANCHOR_deleteProfession",
90
+ "request": "BUNKER_API_ANCHOR_deleteJob",
91
91
  "label": "删除",
92
- "action": "deleteProfession"
92
+ "action": "deleteJob"
93
93
  }
94
94
  ],
95
95
  "pagination": true,
@@ -97,8 +97,8 @@
97
97
  "rowSelection": true
98
98
  },
99
99
  "title": {
100
- "chinese": "职业觉醒管理",
101
- "english": "professionAwakeningManagement"
100
+ "chinese": "鬼剑士职业管理",
101
+ "english": "GhostSwordsmanManagement"
102
102
  },
103
103
  "formItems": [
104
104
  {
@@ -140,7 +140,7 @@
140
140
  },
141
141
  {
142
142
  "label": "太刀",
143
- "value": "longSword"
143
+ "value": "katana"
144
144
  },
145
145
  {
146
146
  "label": "巨剑",
@@ -170,13 +170,13 @@
170
170
  "functionButton": [
171
171
  {
172
172
  "btn": "批量删除",
173
- "uri": "BUNKER_API_ANCHOR_deleteProfessions",
174
- "action": "deleteProfessions"
173
+ "request": "BUNKER_API_ANCHOR_batchDeleteJobs",
174
+ "action": "batchDeleteJobs"
175
175
  },
176
176
  {
177
177
  "btn": "批量导出",
178
- "uri": "BUNKER_API_ANCHOR_exportProfessions",
179
- "action": "exportProfessions"
178
+ "request": "BUNKER_API_ANCHOR_exportJobs",
179
+ "action": "exportJobs"
180
180
  }
181
181
  ]
182
182
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yorha2b-lab/autodev",
3
- "version": "7.3.6",
3
+ "version": "7.4.1",
4
4
  "description": "基于 AI 视觉与语义协议的前端 (React+Antd) 代码构筑与旧系统重构引擎",
5
5
  "bin": {
6
6
  "bunker": "bin/bunker.js"
@@ -49,8 +49,8 @@
49
49
  "json-stringify-pretty-compact": "^4.0.0"
50
50
  },
51
51
  "devDependencies": {
52
- "segmentit": "^2.0.3",
52
+ "http-proxy": "^1.18.1",
53
53
  "puppeteer": "^25.4.0",
54
- "http-proxy": "^1.18.1"
54
+ "segmentit": "^2.0.3"
55
55
  }
56
56
  }
@@ -12,7 +12,7 @@ ${realApis}
12
12
  - 识别后缀(如 edit, delete, add)。
13
13
  - 结合 UI 语义,在 API 库中寻找对应路径。
14
14
  - **输出格式**:请只输出一个 JSON 映射表。
15
- - **例如**:{"BUNKER_API_ANCHOR_editByRecord": "[method] /v1/update-user"}
15
+ - **例如**:{"BUNKER_API_ANCHOR_editByRecord": {"uri": "/api/records/{recordId}", "method": "POST"}}
16
16
 
17
17
  ⚠️ 严禁解释,严禁输出 JSON 之外的内容。
18
18
  `.trim()
@@ -14,18 +14,15 @@ module.exports = async ({ llm, yorha, dialog, logistics }) => {
14
14
  pod153.report(dialog.pod153.autonomousAddressing, 'magenta')
15
15
 
16
16
  try {
17
- // 1. 抓取情报
18
17
  const apiData = await fetch(apiDoc).then(res => res.json())
19
18
  const refinedApis = Object.entries(apiData.paths).flatMap(([apiUrl, methods]) => {
20
- // 💡 适配多方法接口:有的路径下可能既有 GET 也有 POST
21
19
  return Object.entries(methods).map(([method, info]) => ({
22
20
  path: apiUrl,
23
- method: method.toUpperCase(),
24
- desc: info.summary || info.description || 'N/A'
21
+ method: method?.toUpperCase() || '',
22
+ desc: info.summary || info.description || 'N/A',
25
23
  }))
26
24
  })
27
25
 
28
- // 2. 战场侦察
29
26
  const pages = path.join(process.cwd(), pagesDir)
30
27
  const files = fs.readdirSync(pages)
31
28
  const enumParamsMap = {}
@@ -37,18 +34,15 @@ module.exports = async ({ llm, yorha, dialog, logistics }) => {
37
34
  if (fs.existsSync(indexPath) && fs.existsSync(resourcePath)) {
38
35
  let indexCode = fs.readFileSync(indexPath, 'utf-8')
39
36
 
40
- // 💡 锁定待通电锚点
41
37
  if (indexCode.includes('BUNKER_API_ANCHOR')) {
42
38
  const resourceCode = fs.readFileSync(resourcePath, 'utf-8')
43
39
  const bunkerAnchors = indexCode.match(/BUNKER_API_ANCHOR_\w+/g)?.join('\n') ?? ''
44
40
 
45
- // 💡 执行语义提取流程
46
41
  const returnMatch = indexCode.match(/return\s*\(([\s\S]*?)\)\s*}/)
47
42
  const returnStatement = returnMatch ? returnMatch[0] : ''
48
43
 
49
44
  const moduleSemantics = `${returnStatement}${resourceCode}`.match(/[\u4e00-\u9fa5][\u4e00-\u9fa5A-Za-z0-9?。,、:;!() ]*/g) || []
50
45
  const pageKeywords = getSemanticKeywords(moduleSemantics)
51
- // 💡 物理扫描:执行“语义雷达”过滤
52
46
  const candidates = refinedApis
53
47
  .map(api => ({
54
48
  ...api,
@@ -56,40 +50,32 @@ module.exports = async ({ llm, yorha, dialog, logistics }) => {
56
50
  }))
57
51
  .filter(item => item.score > 0)
58
52
  .sort((a, b) => b.score - a.score)
59
- .slice(0, 20) // 💡 拓宽至前 20 名,给 AI 更多的判别空间
53
+ .slice(0, 20)
60
54
 
61
55
  const finalCandidates = candidates.map(item => `${item.method} ${item.path} ${item.desc}`).join('\n')
62
56
  const spinner = pod153.start(dialog.pod153.inactiveModule(fileName), 'yellow')
63
- // 3. 驱动 9S 最终裁决
64
57
  const result = await apiParser({ bunkerAnchors, realApis: finalCandidates })
65
58
 
66
59
  if (result) {
67
- // 💡 执行物理更替
68
- Object.entries(result).forEach(([anchor, fullSignal]) => {
69
- // 💡 1. 信号解压
70
- const [realMethod, realPath] = fullSignal.split(' ')
71
-
72
- // 💡 2. 路径清洗:确保路径拼接时不产生双斜杠 //
73
- // 逻辑:如果 realPath 以 / 开头,咱们拼接时就得小心
74
- const cleanPath = realPath.startsWith('/') ? realPath : `/${realPath}`
75
-
76
- /**
77
- * 💡 3. 物理定位正则表达式
78
- * 我们把 (request\(\s*['"])\/api\/ 这一段作为第一个捕获组 $1
79
- * 这样在替换时,只需在 $1 后面补上新的路径即可,/api/ 会被原样保留!
80
- */
81
- const requestRegex = new RegExp(`(request\\(\\s*['"]\\/api)${anchor}(['"]\\s*,\\s*\\{[^}]*method\\s*:\\s*['"])(.*?)(['"])`, 'g')
82
-
83
- // 💡 4. 执行“全频道通电”
84
- // $1 现在包含了 request('/api 这一串
85
- indexCode = indexCode.replace(requestRegex, `$1${cleanPath}$2${realMethod.toUpperCase()}$4`)
86
- // 💡 5. 兜底替换(针对没有 method 配置的简易请求)
87
- // 同样保留 /api/ 前缀
88
- indexCode = indexCode.replaceAll(`/api/${anchor}`, `/api${cleanPath}`)
60
+ let hasQsImport = indexCode.includes(`import qs from 'qs'`)
61
+ Object.entries(result).forEach(([anchor, { uri, method }]) => {
62
+ const cleanPath = uri.startsWith('/') ? uri : `/${uri}`
63
+ let finalUri = cleanPath.startsWith('/api') ? cleanPath : `/api${cleanPath}`
64
+ finalUri = finalUri.replace(/\{(\w+)\}/g, (_, key) => '${params.' + key + '}')
65
+ const quotationMark = finalUri.includes('${params.') ? '\`' : "'"
66
+ if (method?.toUpperCase() === 'GET') {
67
+ indexCode = indexCode.replaceAll(anchor, `request(\`${finalUri}?\${qs.stringify(params)}\`)`)
68
+ if (!hasQsImport) {
69
+ indexCode = `import qs from 'qs'\n${indexCode}`
70
+ hasQsImport = true
71
+ }
72
+ } else {
73
+ indexCode = indexCode.replaceAll(anchor, `request(${quotationMark}${finalUri}${quotationMark}, { method: '${method?.toUpperCase()}', body: params })`)
74
+ }
89
75
  })
90
76
 
91
- const [pagesMethod, pagesApi] = result?.['BUNKER_API_ANCHOR_pages']?.split(' ')
92
- const definitions = (apiData.paths[pagesApi]?.[pagesMethod] ?? apiData.paths[pagesApi]?.[pagesMethod.toLowerCase()])?.parameters?.flatMap(item => Object.values(item.schema)?.flatMap(def => def.split('/').at(-1))) || []
77
+ const { uri: pagesApi, method: pagesMethod } = result?.['BUNKER_API_ANCHOR_pages']
78
+ const definitions = (apiData.paths[pagesApi]?.[pagesMethod] ?? apiData.paths[pagesApi]?.[pagesMethod?.toLowerCase()])?.parameters?.flatMap(item => Object.values(item.schema ?? {})?.flatMap(def => def?.split('/')?.at(-1))) || []
93
79
  const parameters = apiData.definitions?.[definitions[0]]?.properties ?? {}
94
80
  const enumParams = Object.entries(parameters)?.filter(([_, value]) => value.hasOwnProperty('enum')) || []
95
81
  enumParamsMap[fileName] = contextStringify({
@@ -37,7 +37,12 @@ module.exports = {
37
37
 
38
38
  const pageConfig = await recognizePage({ prompt: raider, filePath: mission.input })
39
39
 
40
- const { formItems, dictBlocks, processedColumns } = formatFormItemAndColumns({ pageConfig })
40
+ let { formItems, dictBlocks, processedColumns } = formatFormItemAndColumns({ pageConfig })
41
+
42
+ processedColumns = processedColumns.map(col => {
43
+ delete col.type
44
+ return col
45
+ })
41
46
 
42
47
  const result = Object.fromEntries(Object.entries({ formItems, processedColumns }).filter(([key, value]) => value?.length > 0))
43
48
 
@@ -1,7 +1,7 @@
1
1
 
2
2
  {{#each operations}}
3
- const {{this.action}} = async record => {
4
- const response = await request('/api/{{this.uri}}',{ method: 'POST', body:{} })
3
+ const {{this.action}} = async params => {
4
+ const response = await {{{this.request}}}
5
5
  if({{{../responseSuccess}}}){
6
6
  refresh()
7
7
  }
@@ -11,12 +11,13 @@ const {{this.action}} = async record => {
11
11
 
12
12
  {{#each functionButtons}}
13
13
  const {{this.action}} = () => {
14
+ const params = {}
14
15
  Modal.confirm({
15
16
  title: '确认',
16
17
  centered: true,
17
18
  content: '确认{{this.btn}}吗?',
18
19
  onOk: async () => {
19
- const response = await request('/api/{{this.uri}}',{ method: 'POST', body:{} })
20
+ const response = await {{{this.request}}}
20
21
  if({{{../responseSuccess}}}){
21
22
  refresh()
22
23
  }
@@ -62,12 +63,13 @@ const showImg = (record, dataIndex) => {
62
63
 
63
64
  {{/if}}
64
65
 
65
- const modalSubmit = async values => {
66
- let response
67
- let body = values
68
- if(modal.type==='add'){
69
- response = await request('/api',{ method: 'POST', body })
66
+ const modalSubmit = async params => {
67
+ const requestMap={
68
+ {{#each operations}}
69
+ {{this.action}}:()=> {{this.request}},
70
+ {{/each}}
70
71
  }
72
+ const response = await requestMap[modal.type]
71
73
  if({{{responseSuccess}}}){
72
74
  setModal({ visible: false })
73
75
  refresh()
@@ -6,7 +6,7 @@ const query = formatQuery(Object.fromEntries(new URLSearchParams(location.search
6
6
  const { total, columns, loading, dataSource, search, setSearch, setDataSource, refresh } = useTableQuery({
7
7
  cols:tableColumns,
8
8
  initialParams: { ...initialParams, ...query },
9
- api: async params => await request('/api/{{uri}}', { method: 'POST', body: params }),
9
+ api: async params => await {{{this.uri}}},
10
10
  formatResponse: response => {
11
11
  return {
12
12
  data: response?.data?.datas ?? [],
@@ -23,6 +23,7 @@ module.exports = `
23
23
  - date: 时间/日期列
24
24
  - text: 普通文本(默认)
25
25
  - tag: 标签列(有背景色的文本)
26
+ - link: 链接列(蓝色下划线的文本)
26
27
  - badge: 徽标列(文本前有颜色小圆点)
27
28
  - **行操作 (Operation)**: [{label:'操作名', action:'动词ByRecord'}]
28
29
 
@@ -36,7 +36,7 @@ module.exports = `
36
36
  ## 表格主体 (table)
37
37
  - **基础格式(columns)**: [{ title: '文本', dataIndex: 'englishName' }]。
38
38
  - **配置项**: [pagination, expandable, rowSelection] 均为布尔值。
39
- - **行操作 (operation)**: [{ label: '操作名', action: '操作动词+[模块名单数]', uri: 'BUNKER_API_ANCHOR_动词+[模块名单数]'}]
39
+ - **行操作 (operation)**: [{ label: '操作名', action: '操作动词+[模块名单数]', request: 'BUNKER_API_ANCHOR_动词+[模块名单数]'}]
40
40
  - **类型定义 (type)**:
41
41
  - enum: 枚举列
42
42
  - money: 金额列
@@ -45,10 +45,11 @@ module.exports = `
45
45
  - date: 时间/日期列
46
46
  - text: 普通文本(默认)
47
47
  - tag: 标签列(有背景色的文本)
48
+ - link: 链接列(蓝色下划线的文本)
48
49
  - badge: 徽标列(文本前有颜色小圆点)
49
50
 
50
51
  ## 全局功能按钮 (functionButton)
51
- - **基础格式**: [{ btn: '按钮', action: '操作动词+[模块名复数]', uri: 'BUNKER_API_ANCHOR_动词+[模块名复数]'}]。
52
+ - **基础格式**: [{ btn: '按钮', action: '操作动词+[模块名复数]', request: 'BUNKER_API_ANCHOR_动词+[模块名复数]'}]。
52
53
  - **导出数据**: action 设为 'export[模块名]'。
53
54
  - **逻辑锁定**: 只要页面中出现了非查询、非重置的独立操作按钮,必须识别并存入 [functionButton] 数组。
54
55
  - **构筑联动**: 若 [functionButton] 数组不为空,则在下文的 [RenderTree] 中必须包含 "FunctionButtons"。
@@ -33,9 +33,6 @@ module.exports = ({ template, logistics, headquarters }) => {
33
33
  })
34
34
 
35
35
  const processedColumns = columns?.map(col => {
36
- if (col.type === 'text') {
37
- delete col.type
38
- }
39
36
  if (['image'].includes(col.type)) {
40
37
  return { ...col, renderAction: true }
41
38
  }
@@ -147,10 +144,10 @@ module.exports = ({ template, logistics, headquarters }) => {
147
144
  hasExpandable: pageConfig.table.expandable,
148
145
  staticInfoText: pageConfig.staticInfo?.text,
149
146
  hasRowSelection: pageConfig.table.rowSelection,
150
- uri: needMock ? fileName : 'BUNKER_API_ANCHOR_pages',
151
147
  formItems: hasFormItems ? (hasTabs ? 'formItems[activeKey]' : 'formItems') : '[]',
152
148
  operations: pageConfig.table.operation?.sort((a, b) => a.action.length - b.action.length) || [],
153
149
  initParams: `{ ${hasTabs ? 'type: tabs[0].key ,' : ''}${hasPagination ? 'pageNo: 1 , pageSize: 10' : ''}}`,
150
+ uri: needMock ? `request('/api/${fileName}', { method: 'POST', body: params })` : 'BUNKER_API_ANCHOR_pages',
154
151
  }
155
152
 
156
153
  const bodyCode = indexTpl(viewData)
@@ -83,13 +83,13 @@
83
83
  "items": {
84
84
  "type": "object",
85
85
  "required": [
86
- "uri",
87
86
  "label",
88
- "action"
87
+ "action",
88
+ "request"
89
89
  ],
90
90
  "additionalProperties": false,
91
91
  "properties": {
92
- "uri": {
92
+ "request": {
93
93
  "type": "string"
94
94
  },
95
95
  "label": {
@@ -230,15 +230,15 @@
230
230
  "type": "object",
231
231
  "required": [
232
232
  "btn",
233
- "uri",
234
- "action"
233
+ "action",
234
+ "request"
235
235
  ],
236
236
  "additionalProperties": false,
237
237
  "properties": {
238
238
  "btn": {
239
239
  "type": "string"
240
240
  },
241
- "uri": {
241
+ "request": {
242
242
  "type": "string"
243
243
  },
244
244
  "action": {