@yorha2b-lab/autodev 5.7.0 → 5.7.2

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.
@@ -0,0 +1,28 @@
1
+ name: "CLA Assistant"
2
+
3
+ on:
4
+ issue_comment:
5
+ types: [created]
6
+ pull_request_target:
7
+ types: [opened, synchronize, reopened]
8
+
9
+ permissions:
10
+ actions: write
11
+ contents: write
12
+ pull-requests: write
13
+ statuses: write
14
+
15
+ jobs:
16
+ CLA:
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - name: "CLA Assistant Check"
20
+ if: (github.event.comment.issue.pull_request != null || github.event_name == 'pull_request_target')
21
+ uses: contributor-assistant/github-action@v2.4.0
22
+ env:
23
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
24
+ with:
25
+ path-to-signatures: 'bunker/signatures/cla.json'
26
+ path-to-document: 'https://github.com/${{ github.repository }}/blob/main/CLA.md'
27
+ branch: 'main'
28
+ allowlist: 'bot*'
package/CLA.md ADDED
@@ -0,0 +1,41 @@
1
+ # Contributor License Agreement (CLA) for autodev
2
+
3
+ Thank you for your interest in contributing to **autodev** ("the Project").
4
+
5
+ In order to protect the Project, its users, and its maintainers, the Project requires a signed Contributor License Agreement ("CLA") before accepting any Pull Requests or code Contributions.
6
+
7
+ By signing this CLA, You agree to the following legally binding terms:
8
+
9
+ ---
10
+
11
+ ### 1. Definitions
12
+ - **"You" (or "Your")** means the individual or legal entity submitting the Contribution.
13
+ - **"Contribution"** means any code, documentation, bug fixes, features, or other materials submitted by You to the Project via Pull Requests, Commits, Issues, or other communication channels.
14
+
15
+ ### 2. Perpetual & Irrevocable License Grant
16
+ You hereby grant to the Project maintainers and all recipients of software distributed by the Project a **perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable** copyright and patent license to reproduce, prepare derivative works of, publicly display, sublicense, and distribute Your Contributions and any derivative works thereof.
17
+
18
+ ### 3. Irrevocability
19
+ Once Your Contribution is submitted and accepted into the Project, **this Agreement and the licenses granted herein cannot be revoked, withdrawn, or canceled by You for any reason**.
20
+
21
+ ### 4. Representation of Originality & Employer Clearance
22
+ You represent and warrant that:
23
+ - Your Contribution is **Your 100% original creation** and does not infringe upon any third-party copyrights, patents, trademarks, trade secrets, or proprietary rights.
24
+ - If Your employer or company holds rights to intellectual property created by You, You represent that You have received **explicit written permission or a legal waiver from Your employer** to submit this Contribution to the Project.
25
+
26
+ ### 5. Future Re-Licensing Authority
27
+ You explicitly agree that the Project maintainers reserve the sole and exclusive right to re-license, dual-license, or update the license terms of the Project (including under AGPL-3.0, MIT, Apache 2.0, or commercial enterprise licenses) in the future without requiring additional consent or notice to You.
28
+
29
+ ### 6. Disclaimer of Warranty
30
+ Except as expressly set forth herein, Your Contribution is provided on an **"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND**, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
31
+
32
+ ---
33
+
34
+ ### 🤖 How to Sign
35
+
36
+ By replying in Your Pull Request with the exact phrase:
37
+ > **I have read the CLA Document and I hereby sign the CLA**
38
+
39
+ You acknowledge that You have read, understood, and agree to be bound by all terms in this Contributor License Agreement.
40
+
41
+ *Glory to Mankind.* 🤖⚔️
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yorha2b-lab/autodev",
3
- "version": "5.7.0",
3
+ "version": "5.7.2",
4
4
  "description": "基于视觉大模型的前端(react+Antd)全自动 CRUD 代码生成器",
5
5
  "bin": {
6
6
  "autodev": "bin/autodev.js"
package/src/ai/index.js CHANGED
@@ -1,51 +1,48 @@
1
- const askAI = async ({ model, yorha, dialog, openAI, messages, response_format = { type: 'json_object' }, retryCount = 0 }) => {
1
+ module.exports = ({ utils, yorha, openAI, dialog, prompts }) => {
2
2
 
3
- if (retryCount >= 3) {
4
- yorha.commander.report(dialog.bunker.linkSevered, 'red')
5
- throw new Error(dialog.bunker.linkSevered)
6
- }
3
+ const sharp = require('sharp')
7
4
 
8
- try {
9
- const response = await openAI.chat.completions.create({
10
- model,
11
- messages,
12
- top_p: 0.1,
13
- response_format,
14
- temperature: 0.01,
15
- })
16
- let raw = response.choices[0].message.content.trim()
17
- raw = raw.replace(/^```json\n?/, '').replace(/\n?```$/, '')
18
- const match = raw.match(/[\{\[][\s\S]*[\}\]]/)
19
- const JSON5 = require('json5')
20
- return JSON5.parse(match ? match[0] : raw)
21
- } catch (err) {
22
- const statusCode = err.status || err.response?.status
23
- const isAuthError = [401, 402].includes(statusCode)
24
- if (isAuthError) {
25
- yorha.commander.report(dialog.bunker.accessDenied, 'red')
26
- throw new Error(dialog.bunker.accessDenied)
27
- }
5
+ const { mock, system, council, response, similarity } = prompts
6
+ const { UI_DESIGNER, API_DESIGNER, MOCK_DESIGNER } = system
7
+ const { textModel, visionModel } = utils.foundation.getConfig()
28
8
 
29
- yorha.commander.report(dialog.bunker.networkInstability(retryCount + 1))
9
+ const askAI = async ({ model, messages, response_format = { type: 'json_object' }, retryCount = 0 }) => {
30
10
 
31
- return askAI({ model, yorha, dialog, openAI, messages, response_format, retryCount: retryCount + 1 })
32
- }
33
- }
11
+ if (retryCount >= 3) {
12
+ yorha.commander.report(dialog.bunker.linkSevered, 'red')
13
+ throw new Error(dialog.bunker.linkSevered)
14
+ }
34
15
 
35
- module.exports = ({ utils, yorha, openAI, dialog, prompts }) => {
16
+ try {
17
+ const response = await openAI.chat.completions.create({
18
+ model,
19
+ messages,
20
+ top_p: 0.1,
21
+ response_format,
22
+ temperature: 0.01,
23
+ })
24
+ let raw = response.choices[0].message.content.trim()
25
+ raw = raw.replace(/^```json\n?/, '').replace(/\n?```$/, '')
26
+ const match = raw.match(/[\{\[][\s\S]*[\}\]]/)
27
+ const JSON5 = require('json5')
28
+ return JSON5.parse(match ? match[0] : raw)
29
+ } catch (err) {
30
+ const statusCode = err.status || err.response?.status
31
+ const isAuthError = [401, 402].includes(statusCode)
32
+ if (isAuthError) {
33
+ yorha.commander.report(dialog.bunker.accessDenied, 'red')
34
+ throw new Error(dialog.bunker.accessDenied)
35
+ }
36
36
 
37
- const sharp = require('sharp')
37
+ yorha.commander.report(dialog.bunker.networkInstability(retryCount + 1))
38
38
 
39
- const { mock, system, council, response } = prompts
40
- const { UI_DESIGNER, API_DESIGNER, MOCK_DESIGNER } = system
41
- const { textModel, visionModel } = utils.foundation.getConfig()
39
+ return askAI({ model, yorha, dialog, openAI, messages, response_format, retryCount: retryCount + 1 })
40
+ }
41
+ }
42
42
 
43
43
  return {
44
44
  generateMock: async ({ columns, fileName }) => {
45
45
  return askAI({
46
- yorha,
47
- openAI,
48
- dialog,
49
46
  model: textModel,
50
47
  messages: [
51
48
  { role: 'system', content: MOCK_DESIGNER },
@@ -53,11 +50,17 @@ module.exports = ({ utils, yorha, openAI, dialog, prompts }) => {
53
50
  ]
54
51
  })
55
52
  },
53
+ nameSimilarity: async ({ fileName, english }) => {
54
+ return askAI({
55
+ model: textModel,
56
+ messages: [
57
+ { role: 'system', content: UI_DESIGNER },
58
+ { role: 'user', content: similarity({ fileName, english }) }
59
+ ]
60
+ })
61
+ },
56
62
  apiParser: async ({ bunkerAnchors, realApis }) => {
57
63
  return askAI({
58
- yorha,
59
- openAI,
60
- dialog,
61
64
  model: textModel,
62
65
  messages: [
63
66
  { role: 'system', content: API_DESIGNER },
@@ -73,9 +76,6 @@ module.exports = ({ utils, yorha, openAI, dialog, prompts }) => {
73
76
  const base64Image = compressedBuffer.toString('base64')
74
77
 
75
78
  return askAI({
76
- yorha,
77
- openAI,
78
- dialog,
79
79
  model: visionModel,
80
80
  messages: [
81
81
  { role: 'system', content: UI_DESIGNER },
@@ -92,9 +92,6 @@ module.exports = ({ utils, yorha, openAI, dialog, prompts }) => {
92
92
  },
93
93
  alignResponseFields: async ({ responseStr, resourceStr }) => {
94
94
  return askAI({
95
- yorha,
96
- openAI,
97
- dialog,
98
95
  model: textModel,
99
96
  messages: [
100
97
  { role: 'system', content: API_DESIGNER },
@@ -4,9 +4,9 @@ const initialParams = {{initParams}}
4
4
  const query = formatQuery(Object.fromEntries(new URLSearchParams(location.search).entries()), {{formItems}})
5
5
 
6
6
  const { total, columns, loading, dataSource, search, setSearch, setDataSource, refresh } = useTableQuery({
7
- api: async params => await request('/api/{{uri}}', { method: 'POST', body: params }),
8
- initialParams: { ...initialParams, ...query },
9
7
  cols:tableColumns,
8
+ initialParams: { ...initialParams, ...query },
9
+ api: async params => await request('/api/{{uri}}', { method: 'POST', body: params }),
10
10
  formatResponse: response => {
11
11
  return {
12
12
  data: response?.data?.datas ?? [],
@@ -1,8 +1,8 @@
1
- module.exports = fileName => `
1
+ module.exports = `
2
2
  # Task: Full-Page UI Analysis & Structural Assembly
3
3
  执行全页面视觉特征提取,并按照物理布局顺序输出标准的构筑协议。
4
4
 
5
- **模块名: ${fileName}**
5
+ **模块名: 页面主体名称[英文]**
6
6
 
7
7
  ## 核心命名协议 (Naming Convention)
8
8
  - **驼峰命名**: 所有变量及函数名必须遵循 camelCase。
@@ -30,7 +30,7 @@ module.exports = fileName => `
30
30
  ## 表格主体 (table)
31
31
  - **基础格式(columns)**: [{ title: '文本', dataIndex: 'englishName' }]。
32
32
  - **配置项**: [pagination, expandable, rowSelection] 均为布尔值。
33
- - **行操作 (operation)**: [{ label: '操作名', action: '动词Record', uri: 'BUNKER_API_ANCHOR_动词[模块名]'}]
33
+ - **行操作 (operation)**: [{ label: '操作名', action: '操作动词[模块名单数形式]', uri: 'BUNKER_API_ANCHOR_操作动词[模块名单数形式]'}]
34
34
  - **类型定义 (type)**:
35
35
  - enum: 枚举列
36
36
  - money: 金额列
@@ -42,7 +42,7 @@ module.exports = fileName => `
42
42
  - badge: 徽标列(文本前有颜色小圆点)
43
43
 
44
44
  ## 全局功能按钮 (functionButton)
45
- - **基础格式**: [{ btn: '按钮', action: '操作动词[模块名]BySelected', uri: 'BUNKER_API_ANCHOR_动词[模块名]BySelected'}]。
45
+ - **基础格式**: [{ btn: '按钮', action: '操作动词[模块名复数形式]', uri: 'BUNKER_API_ANCHOR_动词[模块名复数形式]'}]。
46
46
  - **导出数据**: action 设为 'export[模块名]'。
47
47
  - **逻辑锁定**: 只要页面中出现了非查询、非重置的独立操作按钮,必须识别并存入 [functionButton] 数组。
48
48
  - **构筑联动**: 若 [functionButton] 数组不为空,则在下文的 [RenderTree] 中必须包含 "FunctionButtons"。
@@ -0,0 +1 @@
1
+ module.exports = ({ fileName, english }) => `${fileName}是现行的文件夹名称,${english}推荐的文件夹名称,判断两个文件夹名称是否相似,返回一个json:{isSimilar:true/false,similarity:0-1之间的数字}`
@@ -7,6 +7,7 @@
7
7
  "required": [
8
8
  "tabs",
9
9
  "table",
10
+ "title",
10
11
  "formItems",
11
12
  "staticInfo",
12
13
  "optionList",
@@ -111,6 +112,22 @@
111
112
  }
112
113
  }
113
114
  },
115
+ "title": {
116
+ "type": "object",
117
+ "additionalProperties": false,
118
+ "required": [
119
+ "chinese",
120
+ "english"
121
+ ],
122
+ "properties": {
123
+ "chinese": {
124
+ "type": "string"
125
+ },
126
+ "english": {
127
+ "type": "string"
128
+ }
129
+ }
130
+ },
114
131
  "formItems": {
115
132
  "type": "array",
116
133
  "items": {
@@ -10,15 +10,14 @@ module.exports = {
10
10
  const { page } = prompts
11
11
  const { nineS, pod042 } = yorha
12
12
  const { index, resource } = generator
13
- const { generateMock, recognizePage } = llm
14
13
  const { contextStringify } = utils.generator
14
+ const { generateMock, recognizePage, nameSimilarity } = llm
15
15
  const { useDemo, pagesDir, utilsDir, needMock } = utils.foundation.getConfig()
16
16
 
17
17
 
18
18
  const startTime = Date.now()
19
- const fileName = path.basename(filePath, path.extname(filePath))
20
- const mockDir = path.join(process.cwd(), 'mock')
21
- const targetDir = path.join(process.cwd(), pagesDir, fileName)
19
+ let fileName = path.basename(filePath, path.extname(filePath))
20
+ let targetDir = path.join(process.cwd(), pagesDir, fileName)
22
21
 
23
22
  const spinner = pod042.start(dialog.pod042.visualCaptured(fileName))
24
23
 
@@ -28,23 +27,29 @@ module.exports = {
28
27
  return
29
28
  }
30
29
 
31
- if (!fs.existsSync(mockDir)) fs.mkdirSync(mockDir, { recursive: true })
32
- if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true })
33
-
34
30
  let pageConfig
35
31
  if (useDemo) {
36
32
  pod042.report(dialog.pod042.simulate)
37
33
  pageConfig = require('../../example/example.json')
38
34
  } else {
39
35
  pod042.update(spinner, dialog.pod042.uploadVisualMetadata)
40
- pageConfig = await recognizePage({ prompt: page(fileName), filePath, schema: require(`../framework/${template}/schema/page.json`) })
36
+ pageConfig = await recognizePage({ prompt: page, filePath, schema: require(`../framework/${template}/schema/page.json`) })
41
37
  }
42
38
 
39
+ const { isSimilar } = await nameSimilarity({ fileName, english: pageConfig.title.english })
40
+ if (!isSimilar) {
41
+ fileName = pageConfig.title.english
42
+ targetDir = path.join(process.cwd(), pagesDir, pageConfig.title.english)
43
+ }
44
+
45
+ if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true })
43
46
  fs.writeFileSync(path.join(targetDir, 'resource.js'), resource({ pageConfig }))
44
47
  fs.writeFileSync(path.join(targetDir, 'index.js'), index({ fileName, pageConfig }))
45
48
 
46
49
  if (needMock) {
47
50
  nineS.update(spinner, dialog.nineS.dataCamouflage(fileName))
51
+ const mockDir = path.join(process.cwd(), 'mock')
52
+ if (!fs.existsSync(mockDir)) fs.mkdirSync(mockDir, { recursive: true })
48
53
  const rawContent = await generateMock({ columns: pageConfig.table.columns, fileName })
49
54
  fs.writeFileSync(path.join(mockDir, `${fileName}.js`), `export default ${contextStringify({ context: rawContent })}`)
50
55
  nineS.success(spinner, dialog.nineS.dataCamouflageComplete)