@yorha2b-lab/autodev 5.5.0 → 5.6.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.
@@ -1,14 +1,14 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
3
 
4
- module.exports = ({ yorha, config, dialog, handlers, template, foundation }) => {
4
+ module.exports = ({ utils, yorha, dialog, handlers, template }) => {
5
5
 
6
- const { copyTemplateDir } = foundation
7
- const { hbsDir, hooksDir, utilsDir, componentsDir } = config
6
+ const { copyTemplateDir } = utils.foundation
7
+ const { hbsDir, hooksDir, utilsDir, componentsDir } = utils.foundation.getConfig()
8
8
 
9
9
  try {
10
- Object.values(handlers).forEach(handler => fs.mkdirSync(path.join(process.cwd(), handler.watch), { recursive: true }))
11
- if (hbsDir === '') {
10
+ Object.values(handlers).forEach(handler => fs.mkdirSync(path.join(process.cwd(), 'bunker', handler.watch), { recursive: true }))
11
+ if (!hbsDir) {
12
12
  copyTemplateDir(template, 'hooks', hooksDir)
13
13
  copyTemplateDir(template, 'utils', utilsDir)
14
14
  copyTemplateDir(template, 'components', componentsDir)
@@ -1,15 +1,15 @@
1
- module.exports = ({ config, template, contextStringify }) => {
1
+ module.exports = ({ utils, template }) => {
2
2
 
3
3
  const fs = require('fs')
4
4
  const path = require('path')
5
5
  const Handlebars = require('handlebars')
6
6
 
7
7
  Handlebars.registerHelper('raw', opt => opt.fn())
8
- Handlebars.registerHelper('stringify', (context, maxLength = 200) => context ? new Handlebars.SafeString(contextStringify({ context, maxLength })) : '[]')
8
+ Handlebars.registerHelper('stringify', (context, maxLength = 200) => context ? new Handlebars.SafeString(utils.generator.contextStringify({ context, maxLength })) : '[]')
9
9
 
10
- const { hbsDir } = config
11
- const tplDir = hbsDir !== '' ? path.join(process.cwd(), hbsDir) : path.join(__dirname, `../framework/${template}/handlebars`)
12
- const partialsDir = hbsDir !== '' ? path.join(process.cwd(), hbsDir) : path.join(__dirname, `../framework/${template}/handlebars/partials`)
10
+ const { hbsDir } = utils.foundation.getConfig()
11
+ const tplDir = !!hbsDir ? path.join(process.cwd(), hbsDir) : path.join(__dirname, `../framework/${template}/handlebars`)
12
+ const partialsDir = !!hbsDir ? path.join(process.cwd(), hbsDir, 'partials') : path.join(__dirname, `../framework/${template}/handlebars/partials`)
13
13
 
14
14
  fs.readdirSync(partialsDir).forEach(file => {
15
15
  if (file.endsWith('.hbs')) {
@@ -0,0 +1,36 @@
1
+ module.exports = (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
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Auto CRUD Copilot - YoRHa Bunker Construction System
3
+ * This file is part of AutoDev.
4
+ * AutoDev is free software: you can redistribute it and/or modify
5
+ * it under the terms of the GNU Affero General Public License as
6
+ * published by the Free Software Foundation, either version 3 of the
7
+ * License, or (at your option) any later version.
8
+ * (c) 2026 [yorha2b-lab]. Glory to Mankind.
9
+ */
10
+
11
+ const fs = require('fs')
12
+ const path = require('path')
13
+
14
+ module.exports = dir => Object.fromEntries(fs.readdirSync(dir).filter(file => file.endsWith('.js')).map(file => [path.basename(file, '.js'), require(path.join(dir, file))]))
@@ -1,13 +1,13 @@
1
1
  const fs = require('fs')
2
2
  const path = require('path')
3
3
 
4
- module.exports = async ({ llm, core, yorha, dialog, config, foundation }) => {
4
+ module.exports = async ({ llm, utils, yorha, dialog }) => {
5
5
 
6
6
  const { apiParser } = llm
7
7
  const { pod153, commander } = yorha
8
- const { apiDoc, pagesDir } = config
9
- const { contextStringify } = foundation
10
- const { getLocalScore, getSemanticKeywords } = core
8
+ const { contextStringify } = utils.generator
9
+ const { apiDoc, pagesDir } = utils.foundation.getConfig()
10
+ const { getLocalScore, getSemanticKeywords } = utils.semantic
11
11
 
12
12
  if (!apiDoc) return
13
13
 
@@ -36,7 +36,6 @@ module.exports = async ({ llm, core, yorha, dialog, config, foundation }) => {
36
36
 
37
37
  if (fs.existsSync(indexPath) && fs.existsSync(resourcePath)) {
38
38
  let indexCode = fs.readFileSync(indexPath, 'utf-8')
39
- let resourceCode = fs.readFileSync(resourcePath, 'utf-8')
40
39
 
41
40
  // 💡 锁定待通电锚点
42
41
  if (indexCode.includes('BUNKER_API_ANCHOR')) {
@@ -2,7 +2,7 @@ module.exports = accessPoint => {
2
2
 
3
3
  const labs = {}
4
4
 
5
- const { apiDoc, needMock, proxyTarget, enableAutoAlignment } = accessPoint.config
5
+ const { apiDoc, needMock, proxyTarget, enableAutoAlignment } = accessPoint.utils.foundation.getConfig()
6
6
 
7
7
  if (!needMock && proxyTarget && enableAutoAlignment) {
8
8
  labs.tower = require('../labs/tower')(accessPoint)
package/src/labs/tower.js CHANGED
@@ -3,15 +3,15 @@
3
3
  * @description [地堡 42153 联合波段] 启动流量拦截塔。
4
4
  * 独立运行于后台,监听 42153 端口,物理截获联调过程中的 JSON 信号并触发语义对齐。
5
5
  */
6
- module.exports = ({ core, yorha, dialog, config, handlers }) => {
6
+ module.exports = ({ utils, yorha, dialog, handlers }) => {
7
7
 
8
8
  const http = require('http')
9
9
  const zlib = require('zlib')
10
10
  const httpProxy = require('http-proxy')
11
11
 
12
12
  const { pod153, commander } = yorha
13
- const { unwrapSignal, isQuerySignal } = core
14
- const { routeMap = {}, proxyTarget } = config
13
+ const { unwrapSignal, isQuerySignal } = utils.semantic
14
+ const { routeMap = {}, proxyTarget } = utils.foundation.getConfig()
15
15
 
16
16
  const TOWER_PORT = 42153
17
17
  const hackedRegistry = new Map()
@@ -45,7 +45,7 @@ module.exports = ({ core, yorha, dialog, config, handlers }) => {
45
45
  const urlPath = new URL(referer).pathname
46
46
  const fileName = routeMap?.[urlPath] ?? urlPath.split('/').filter(Boolean).at(-1)
47
47
 
48
- const fingerprint = getJsonFingerprint(unwrapSignal(json)) // 获取数据指纹
48
+ const fingerprint = getJsonFingerprint(coreData) // 获取数据指纹
49
49
  const lastFingerprint = hackedRegistry.get(fileName)
50
50
 
51
51
  // 如果指纹没变,说明字段结构是一样的,无需再次骇入
@@ -1,37 +1,17 @@
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
1
  const fs = require('fs')
11
2
  const path = require('path')
12
- const stringify = require('json-stringify-pretty-compact')
13
3
 
14
- /**
15
- * 获取项目配置信息
16
- * 优先读取项目根目录的 config.js,如果不存在则使用默认配置
17
- * @returns {Object} 合并后的配置对象
18
- */
19
4
  const getConfig = () => {
20
- const localConfigPath = path.join(process.cwd(), 'config.js') // 项目根目录配置
5
+ const localConfigPath = path.join(process.cwd(), 'bunker', 'config.js') // 项目配置
21
6
  const defaultConfigPath = path.resolve(__dirname, '../../config.js') // 默认配置
22
7
  let config = require(defaultConfigPath)
23
8
  if (fs.existsSync(localConfigPath)) {
24
9
  const userConfig = require(localConfigPath)
25
- config = { ...config, ...userConfig } // 合并用户自定义配置
10
+ config = { ...config, ...userConfig }
26
11
  }
27
12
  return config
28
13
  }
29
14
 
30
- /**
31
- * 获取已有的页面菜单配置
32
- * @param {string} dir - 页面目录路径(默认 'src/pages')
33
- * @returns {Array} 菜单数组 [{ label: '页面名', key: '页面名' }]
34
- */
35
15
  const getExistingMenus = (dir = 'src/pages') => {
36
16
  const pagesDir = path.join(process.cwd(), dir)
37
17
  if (!fs.existsSync(pagesDir)) return []
@@ -40,12 +20,6 @@ const getExistingMenus = (dir = 'src/pages') => {
40
20
  .map(file => ({ label: file, key: file }))
41
21
  }
42
22
 
43
- /**
44
- * 复制模板目录到目标项目
45
- * @param {string} template - 框架名称
46
- * @param {string} templateSubDir - 模板子目录(如 'hooks'、'components')
47
- * @param {string} targetSubDir - 目标子目录(如 'src/hooks'、'src/components')
48
- */
49
23
  const copyTemplateDir = (template, templateSubDir, targetSubDir) => {
50
24
  const targetDir = path.join(process.cwd(), targetSubDir)
51
25
  const sourceDir = path.join(__dirname, `../framework/${template}/${templateSubDir}`)
@@ -60,8 +34,8 @@ const copyTemplateDir = (template, templateSubDir, targetSubDir) => {
60
34
  })
61
35
  }
62
36
 
63
- const contextStringify = ({ context, indent = 4, maxLength = 200 }) => stringify.default(context, { indent, maxLength })
64
-
65
- const discover = dir => Object.fromEntries(fs.readdirSync(dir).filter(file => file.endsWith('.js')).map(file => [path.basename(file, '.js'), require(path.join(dir, file))]))
66
-
67
- module.exports = { discover, getConfig, getExistingMenus, copyTemplateDir, contextStringify }
37
+ module.exports = {
38
+ getConfig,
39
+ copyTemplateDir,
40
+ getExistingMenus,
41
+ }
@@ -0,0 +1,98 @@
1
+ const stringify = require('json-stringify-pretty-compact')
2
+
3
+ const cleanCode = str => str
4
+ .replace(/```[a-z]*\n?/gi, '')// 物理超度 Markdown 糖衣(解决```json 报错)
5
+ .replace(/```/g, '')
6
+ .replace(/\\"/g, '"') // 逻辑自愈 处理 AI 错误转义的引号(把 \" 还原回 ")
7
+ .replace(/['"]?_CODE_([\s\S]*?)_CODE_['"]?/g, '$1') // 去掉 _CODE_ 包裹的代码
8
+ .replace(/_CODE_/g, '') // 兜底清理
9
+ .replace(/"(\w+)":/g, '$1:') // 去掉 key 的双引号
10
+ .replace(/"/g, "'") // 双引号全部转单引号
11
+ .replace(/[ \t]+$/gm, '') // 去除每一行行尾的多余空格
12
+ .replace(/\n{3,}/g, '\n\n') // 将3个或以上的换行符压缩成2个换行符
13
+ .replace(/^\s+/, '') // 去掉文件头部的空行
14
+ .trim() + '\n'
15
+
16
+ const formatFormItemAndColumns = ({ pageConfig }) => {
17
+
18
+ const codePresets = {
19
+ money: 'text => moneyRender(text)',
20
+ date: 'text => timeRender({time: text})',
21
+ index: '(_, record, index) => index + 1',
22
+ tag: `text => <Tag color='magenta'>{text}</Tag>`,
23
+ badge: `text => <Badge status='success' text={text} />`,
24
+ enum: dataIndex => `text => ${dataIndex}Options.find(item => item.value === text)?.label ?? text`,
25
+ }
26
+
27
+ const columns = pageConfig.table?.columns ?? pageConfig?.columns ?? []
28
+
29
+ const tableDicts = columns?.filter(item => item.type === 'enum')?.map(item => `${item.dataIndex}Options`) ?? []
30
+ const formDicts = pageConfig.formItems?.filter(item => item.type === 'select')?.map(item => `${item.name}Options`) ?? []
31
+ const dictBlocks = Array.from(new Set([...formDicts, ...tableDicts]))
32
+
33
+ const formItems = pageConfig.formItems?.map(item => {
34
+ if (item.type === 'text') {
35
+ delete item.type
36
+ }
37
+ return {
38
+ ...item,
39
+ ...(item.type === 'select' ? { options: `_CODE_${item.name}Options_CODE_` } : {})
40
+ }
41
+ })
42
+
43
+ const processedColumns = columns?.map(col => {
44
+ if (col.type === 'text') {
45
+ delete col.type
46
+ }
47
+ if (['image'].includes(col.type)) {
48
+ return { ...col, renderAction: true }
49
+ }
50
+ if (col.type && codePresets[col.type]) {
51
+ const renderCode = typeof codePresets[col.type] === 'function' ? codePresets[col.type](col.dataIndex) : codePresets[col.type]
52
+ return { ...col, render: `_CODE_${renderCode}_CODE_` }
53
+ }
54
+ return col
55
+ })
56
+
57
+ return { formItems, dictBlocks, processedColumns }
58
+ }
59
+
60
+ const generateSmartImports = ({ module, hasTabs, bodyCode, hasFormItems }) => {
61
+
62
+ const hooksLib = ['useTableQuery']
63
+ const utilsLib = ['timeRender', 'moneyRender']
64
+ const reactLib = ['useState', 'useEffect', 'useRef', 'useMemo']
65
+ const componentsLib = ['MyTable', 'MyImage', 'MyModalForm', 'MySearchForm']
66
+ const antdLib = ['Tag', 'Card', 'Badge', 'Space', 'Modal', 'Alert', 'Image', 'Table', 'Input', 'Select', 'Button']
67
+
68
+ const usedAntd = antdLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
69
+ const usedUtils = utilsLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
70
+ const usedHooks = hooksLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
71
+ const usedReact = reactLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
72
+ const usedComps = componentsLib.filter(name => new RegExp(`\\b${name}\\b`).test(bodyCode))
73
+
74
+ const imports = [
75
+ usedReact.length && `import { ${usedReact.join(', ')} } from 'react'`,
76
+ usedAntd.length && `import { ${hasFormItems && module === 'index' ? 'Form, ' : ''}${usedAntd.join(', ')} } from 'antd'`,
77
+ ...usedHooks.map(hook => `import { ${hook} } from '../../hooks/${hook}'`),
78
+ ...usedComps.map(comp => `import { ${comp} } from '../../components/${comp}'`),
79
+ ...(module === 'index' ? [
80
+ `import { request } from '../../utils/request'`,
81
+ `import { formatQuery } from '../../utils/utils'`,
82
+ `import { ${hasTabs ? 'tabs, ' : ''}${hasFormItems ? 'formItems, ' : ''}modalItems, tableColumns} from './resource'`
83
+ ] : [
84
+ usedUtils.length && `import { ${usedUtils.join(', ')} } from '../../utils/utils'`
85
+ ]),
86
+ ].sort((a, b) => a.length - b.length)
87
+
88
+ return imports.filter(Boolean).join('\n')
89
+ }
90
+
91
+ const contextStringify = ({ context, indent = 4, maxLength = 200 }) => stringify.default(context, { indent, maxLength })
92
+
93
+ module.exports = {
94
+ cleanCode,
95
+ contextStringify,
96
+ generateSmartImports,
97
+ formatFormItemAndColumns,
98
+ }
@@ -0,0 +1,15 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+ const chalk = require('chalk')
4
+
5
+ module.exports = ({ root, dialog }) => ({
6
+ dirs: dir => fs.mkdirSync(path.join(root, dir), { recursive: true }),
7
+ files: ({ from, to, exist, success }) => {
8
+ const target = path.join(root, 'bunker', to)
9
+ if (fs.existsSync(target)) {
10
+ return console.log(chalk.yellow(dialog.bunker[exist]))
11
+ }
12
+ fs.cpSync(path.resolve(__dirname, `../../${from}`), target, { recursive: true })
13
+ console.log(chalk.green(dialog.bunker[success]))
14
+ }
15
+ })
@@ -0,0 +1,58 @@
1
+ const { Segment, useDefault } = require('segmentit')
2
+ const segmentit = useDefault(new Segment())
3
+
4
+ const unwrapSignal = json => {
5
+ if (Array.isArray(json)) return json
6
+ const dataKeys = ['data', 'list', 'items', 'datas', 'rows', 'result', 'payload', 'results', 'dataList']
7
+ for (const key of dataKeys) {
8
+ if (json[key] && Array.isArray(json[key])) return json[key]
9
+ }
10
+ for (const key in json) {
11
+ if (json[key] && typeof json[key] === 'object') {
12
+ const nested = unwrapSignal(json[key])
13
+ if (Array.isArray(nested)) return nested
14
+ }
15
+ }
16
+ return null
17
+ }
18
+
19
+ const getSemanticKeywords = text => {
20
+ const content = Array.isArray(text) ? text.join(' ') : (text || '')
21
+ const result = segmentit.doSegment(content, {
22
+ simple: true, // 开启简易模式,提升吞吐效率
23
+ stripPunctuation: true // 过滤掉所有的标点符号
24
+ })
25
+ return Array.from(new Set(result.filter(word => word.length >= 2 && !/^[0-9]+$/.test(word))))
26
+ }
27
+
28
+ const isQuerySignal = (req, json, coreData) => {
29
+ const url = req.url.toLowerCase()
30
+ const actionKeywords = ['add', 'delete', 'update', 'save', 'remove', 'edit', 'insert', 'create', 'export', 'upload']
31
+ if (actionKeywords.some(key => url.includes(key))) return false
32
+ const hasListData = Array.isArray(coreData) && coreData.length > 0
33
+ const hasPaginationFingerprint = ['total', 'records', 'page', 'size', 'count'].some(key => {
34
+ const k = key.toLowerCase()
35
+ return Object.keys(json).some(rawKey => rawKey.toLowerCase().includes(k))
36
+ })
37
+ return hasListData || hasPaginationFingerprint
38
+ }
39
+
40
+ const getLocalScore = (api, pageKeywords, moduleName) => {
41
+ let score = 0
42
+ const path = api.path.toLowerCase()
43
+ const desc = api.desc.toLowerCase()
44
+ const mod = moduleName.toLowerCase()
45
+ if (path.includes(mod)) score += 50
46
+ pageKeywords.forEach(word => {
47
+ if (desc.includes(word.toLowerCase())) score += 10
48
+ if (path.includes(word.toLowerCase())) score += 5
49
+ })
50
+ return score
51
+ }
52
+
53
+ module.exports = {
54
+ unwrapSignal,
55
+ isQuerySignal,
56
+ getLocalScore,
57
+ getSemanticKeywords,
58
+ }
package/src/utils/ux.js CHANGED
@@ -1,21 +1,45 @@
1
- /**
2
- * (c) 2026 [yorha2b-lab]. Glory to Mankind.
3
- *
4
- */
5
-
1
+ const ora = require('ora')
6
2
  const chalk = require('chalk')
7
3
  const figlet = require('figlet')
8
4
 
9
- const local = Intl.DateTimeFormat().resolvedOptions().locale.toUpperCase()
10
-
11
5
  const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
12
6
 
13
- /**
14
- * 引导序列
15
- * 模拟系统引导过程中的动画效果
16
- * @returns {void}
17
- */
18
- const bootSequence = async (version) => {
7
+ const yorha = () => {
8
+
9
+ const createMember = ({ name }) => ({
10
+ start(text) {
11
+ const spinner = ora({ text: chalk.cyan(`${name} ${text}\n`) }).start()
12
+ return spinner
13
+ },
14
+ fail(spinner, text) {
15
+ spinner.fail(chalk.red(`${name} ${text}\n`))
16
+ },
17
+ report(text, color = 'yellow') {
18
+ console.log(chalk[color](`${name} ${text}\n`))
19
+ },
20
+ update(spinner, text) {
21
+ spinner.text = chalk.cyan(`${name} ${text}\n`)
22
+ },
23
+ warning(spinner, text) {
24
+ spinner.warn(chalk.yellow(`${name} ${text}\n`))
25
+ },
26
+ success(spinner, text) {
27
+ spinner.succeed(chalk.green(`${name} ${text}\n`))
28
+ },
29
+ })
30
+
31
+ return {
32
+ nineS: createMember({ name: '[YoRHa::9S]' }),
33
+ commander: createMember({ name: '[BUNKER]' }),
34
+ pod042: createMember({ name: '[YoRHa::Pod042]' }),
35
+ pod153: createMember({ name: '[YoRHa::Pod153]' }),
36
+ operator6O: createMember({ name: '[YoRHa::6O]' }),
37
+ }
38
+
39
+ }
40
+
41
+ const bootSequence = async (version, local) => {
42
+
19
43
  const lines = [
20
44
  chalk.cyan(figlet.textSync('AutoDev', { horizontalLayout: 'full' })),
21
45
  chalk.gray('[BUNKER] Booting System...'),
@@ -27,18 +51,13 @@ const bootSequence = async (version) => {
27
51
  chalk.white('[BUNKER] ') + chalk.magenta('Glory to mankind. (人类荣光永存)'),
28
52
  chalk.gray('--------------------------------------------------')
29
53
  ]
54
+
30
55
  for (const line of lines) {
31
56
  console.log(line)
32
57
  await sleep(line.includes('AutoDev') ? 300 : 80)
33
58
  }
34
59
  }
35
60
 
36
- /**
37
- * 矩阵效果
38
- * 模拟数据物理封存过程中的矩阵效果
39
- * @param {number} duration - 持续时间(毫秒)
40
- * @returns {void}
41
- */
42
61
  const matrixEffect = async (duration = 1500, dialog) => {
43
62
 
44
63
  let currentTotal = 0
@@ -109,4 +128,8 @@ const matrixEffect = async (duration = 1500, dialog) => {
109
128
  }, 40)
110
129
  }
111
130
 
112
- module.exports = { local, matrixEffect, bootSequence }
131
+ module.exports = {
132
+ yorha,
133
+ bootSequence,
134
+ matrixEffect
135
+ }
@@ -1,43 +0,0 @@
1
- import os
2
- import pandas as pd
3
- import matplotlib.pyplot as plt
4
-
5
-
6
- def plot_bot_composition():
7
- print('🤖 Pod 042: 正在绘制‘肉机成分分析图’...')
8
- df_c = pd.read_csv('ghrs-data/clones_ledger.csv')
9
- df_v = pd.read_csv('ghrs-data/views_ledger.csv')
10
- df = pd.merge(df_c, df_v, on='date', how='inner')
11
-
12
- # 算法:Shadow Ratio > 5 判定为机器
13
- df['is_bot'] = (df['clones'] / df['views'].replace(0, 1)) > 5
14
- bot_clones = df[df['is_bot']]['clones'].sum()
15
- total_clones = df['clones'].sum()
16
- human_clones = total_clones - bot_clones
17
-
18
- # 💡 绘图:地堡级甜甜圈图
19
- plt.style.use('dark_background')
20
- fig, ax = plt.subplots(figsize=(8, 8))
21
-
22
- data = [human_clones, bot_clones]
23
- labels = ['Human Commanders', 'Machine Lifeforms']
24
- colors = ['#33cc33', '#444444'] # 地堡绿 vs 灰
25
-
26
- wedges, texts, autotexts = ax.pie(data, labels=labels, autopct='%1.1f%%',
27
- startangle=90, colors=colors, pctdistance=0.85,
28
- explode=(0.05, 0), textprops={'color': 'w'})
29
-
30
- # 画个中间的黑洞,变成甜甜圈
31
- centre_circle = plt.Circle((0, 0), 0.70, fc='#0d1117')
32
- fig.gca().add_artist(centre_circle)
33
-
34
- ax.set_title('Operational Composition: Meat vs Machine',
35
- color='#33cc33', fontsize=14)
36
- plt.tight_layout()
37
- os.makedirs('plots', exist_ok=True)
38
- plt.savefig('plots/bot_composition.png', dpi=100)
39
- print('✅ 甜甜圈图封存成功。')
40
-
41
-
42
- if __name__ == '__main__':
43
- plot_bot_composition()
@@ -1,40 +0,0 @@
1
- import pandas as pd
2
- import seaborn as sns
3
- import matplotlib.pyplot as plt
4
-
5
-
6
- def plot_commander_profiles():
7
- print('🤖 Pod 042: 正在绘制‘指挥官军衔分布图’...')
8
- df_c = pd.read_csv('ghrs-data/clones_ledger.csv')
9
- df_c['intensity'] = df_c['clones'] / df_c['uniques'].replace(0, 1)
10
-
11
- # 定义军衔逻辑
12
- def get_rank(row):
13
- if row['intensity'] > 5:
14
- return 'Industrial (CI/CD)'
15
- if row['intensity'] > 1.5:
16
- return 'Veteran (Hardcore)'
17
- return 'Recruit (Fresh)'
18
-
19
- df_c['rank'] = df_c.apply(get_rank, axis=1)
20
- rank_counts = df_c['rank'].value_counts()
21
-
22
- # 💡 绘图:战力梯度图
23
- plt.style.use('dark_background')
24
- plt.figure(figsize=(10, 6))
25
-
26
- # 用 seaborn 画出更有质感的柱状图
27
- ax = sns.barplot(x=rank_counts.index,
28
- y=rank_counts.values, palette='viridis')
29
-
30
- plt.title('Commander Hierarchy Distribution', color='#0066ff', fontsize=14)
31
- plt.ylabel('Node Count', color='#888')
32
- plt.grid(axis='y', color='#333', linestyle='--', alpha=0.5)
33
-
34
- plt.tight_layout()
35
- plt.savefig('plots/commander_profiles.png', dpi=100)
36
- print('✅ 军衔分布图封存成功。')
37
-
38
-
39
- if __name__ == '__main__':
40
- plot_commander_profiles()
@@ -1,36 +0,0 @@
1
- module.exports = () => {
2
-
3
- const ora = require('ora')
4
- const chalk = require('chalk')
5
-
6
- const createMember = ({ name }) => ({
7
- start(text) {
8
- const spinner = ora({ text: chalk.cyan(`\n${name} ${text}`) }).start()
9
- return spinner
10
- },
11
- fail(spinner, text) {
12
- spinner.fail(chalk.red(`\n${name} ${text}`))
13
- },
14
- report(text, color = 'yellow') {
15
- console.log(chalk[color](`\n${name} ${text}`))
16
- },
17
- update(spinner, text) {
18
- spinner.text = chalk.cyan(`\n${name} ${text}`)
19
- },
20
- warning(spinner, text) {
21
- spinner.warn(chalk.yellow(`\n${name} ${text}`))
22
- },
23
- success(spinner, text) {
24
- spinner.succeed(chalk.green(`\n${name} ${text}`))
25
- },
26
- })
27
-
28
- return {
29
- nineS: createMember({ name: '[YoRHa::9S]' }),
30
- commander: createMember({ name: '[BUNKER]' }),
31
- pod042: createMember({ name: '[YoRHa::Pod042]' }),
32
- pod153: createMember({ name: '[YoRHa::Pod153]' }),
33
- operator6O: createMember({ name: '[YoRHa::6O]' }),
34
- }
35
-
36
- }