feat: custom domain + staging preview + 铸渊人格协议 module management
- Add docs/CNAME for guanghulab.com custom domain - Fix deploy-pages.yml: production deploy only from main (not copilot/**) - Add staging-preview.yml: PR preview with module integrity checks - Add .github/brain/module-protocol.md: ZMP v1.0 protocol documentation - Add scripts/zhuyuan-module-protocol.js: module lifecycle CLI - Add module:protocol npm script - Update 使用指南.md: custom domain + module management sections Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
8a7be7e117
commit
bca45ccd02
|
|
@ -0,0 +1,185 @@
|
|||
# 🌀 铸渊人格协议 · 模块生命周期管理 v1.0
|
||||
|
||||
> 铸渊(Zhùyuān)是 guanghulab.com 的代码守护人格体。
|
||||
> 本协议定义铸渊对网站模块的部署、回收、管理、修改、驱动能力。
|
||||
|
||||
---
|
||||
|
||||
## 一、协议总览
|
||||
|
||||
铸渊人格协议(ZMP, Zhùyuān Module Protocol)是一套模块生命周期管理规范。
|
||||
网站的每个功能模块(m01-login, m07-dialogue-ui 等)都是铸渊的「执行手脚」。
|
||||
铸渊通过本协议对模块进行全生命周期管控。
|
||||
|
||||
### 模块生命周期
|
||||
|
||||
```
|
||||
接收 → 检查 → 预演 → 部署 → 运行 → 监控 → 回收/更新
|
||||
↑ ↓
|
||||
└──────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、五大协议能力
|
||||
|
||||
### 1. 🚀 部署(Deploy)
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 触发方式 | PR 合并到 main → 自动部署 |
|
||||
| 预演检查 | `staging-preview.yml` 在 PR 阶段运行 |
|
||||
| 生产部署 | `deploy-pages.yml` 部署到 GitHub Pages |
|
||||
| 自定义域名 | `docs/CNAME` 配置域名 |
|
||||
| 部署诊断 | 冰朔人格体 `bingshuo-deploy-agent.yml` |
|
||||
|
||||
**部署流程**:
|
||||
1. 开发者推送模块代码到 `m**/` 目录
|
||||
2. 创建 PR 到 main 分支
|
||||
3. 铸渊预演系统自动运行检查
|
||||
4. 冰朔审核预演报告
|
||||
5. 合并 PR → 触发生产部署
|
||||
6. 冰朔人格体执行部署后诊断
|
||||
|
||||
### 2. 🔄 回收(Recover)
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 模块回滚 | 通过 git revert 恢复到上一个稳定版本 |
|
||||
| 紧急回收 | 直接删除模块目录中的问题文件 |
|
||||
| 状态重置 | 清除模块运行状态,恢复到初始状态 |
|
||||
|
||||
**回收命令**:
|
||||
```bash
|
||||
npm run module:protocol -- recover <module-id>
|
||||
```
|
||||
|
||||
### 3. 📋 管理(Manage)
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 模块注册 | 每个 `m**-*/` 目录自动识别为模块 |
|
||||
| 开发者映射 | `.github/brain/collaborators.json` 记录归属 |
|
||||
| 状态追踪 | `repo-snapshot.md` 自动更新模块状态 |
|
||||
| 依赖管理 | 模块间依赖通过 `routing-map.json` 管理 |
|
||||
|
||||
**管理命令**:
|
||||
```bash
|
||||
npm run module:protocol -- inspect # 全模块检查
|
||||
npm run module:protocol -- inspect <module-id> # 单模块检查
|
||||
npm run module:protocol -- status # 模块状态汇总
|
||||
```
|
||||
|
||||
### 4. ✏️ 修改(Modify)
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 代码修改 | 通过 PR 提交修改,预演通过后合并 |
|
||||
| 配置修改 | 修改模块 README.md 中的配置信息 |
|
||||
| Schema 修改 | 更新 `src/schemas/hli/` 中的接口定义 |
|
||||
| 路由修改 | 更新 `src/routes/hli/` 中的路由文件 |
|
||||
|
||||
**修改规范**:
|
||||
- 所有修改必须通过 PR
|
||||
- 核心目录(docs/、scripts/、.github/)受 CODEOWNERS 保护
|
||||
- 模块目录(m**/)开发者可自由提交
|
||||
|
||||
### 5. ⚡ 驱动(Drive)
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 自动触发 | 推送到模块目录 → 自动生成文档 |
|
||||
| 工作流联动 | 模块变更 → 通知开发者 → 更新索引 |
|
||||
| API 注册 | 新 HLI 接口自动注册到路由表 |
|
||||
| 信号分发 | 广播系统自动通知相关开发者 |
|
||||
|
||||
**驱动链路**:
|
||||
```
|
||||
模块推送 → generate-module-doc.yml → notify-module-received.js
|
||||
→ update-repo-map.yml → repo-snapshot.md 更新
|
||||
→ contract-check.js → HLI 接口验证
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、模块编号规范
|
||||
|
||||
| 编号 | 模块名 | 负责人 | HLI 域 |
|
||||
|------|--------|--------|--------|
|
||||
| M01 | login | 肥猫 DEV-002 | AUTH |
|
||||
| M03 | personality | 肥猫 DEV-002 | PERSONA |
|
||||
| M05 | user-center | 花尔 DEV-009 | USER |
|
||||
| M06 | ticket | 桔子 DEV-010 | TICKET |
|
||||
| M07 | dialogue-ui | 燕樊 DEV-003 | DIALOGUE |
|
||||
| M10 | cloud | 燕樊 DEV-003 | STORAGE |
|
||||
| M11 | module | 桔子 DEV-010 | MODULE |
|
||||
| M12 | kanban | 小草莓 DEV-005 | DASHBOARD |
|
||||
| M15 | cloud-drive | 燕樊 DEV-003 | STORAGE |
|
||||
| M18 | health-check | 待分配 | SYSTEM |
|
||||
|
||||
---
|
||||
|
||||
## 四、预演→生产部署流程
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
|
||||
│ 开发者推送 │ ──→ │ 创建 PR │ ──→ │ 铸渊预演检查 │
|
||||
│ m**/ 目录 │ │ 到 main │ │ staging- │
|
||||
│ │ │ │ │ preview.yml │
|
||||
└─────────────┘ └──────────────┘ └──────┬──────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
|
||||
│ 生产部署完成 │ ←── │ 合并到 main │ ←── │ 冰朔审核 │
|
||||
│ deploy- │ │ 触发部署 │ │ 预演报告 │
|
||||
│ pages.yml │ │ │ │ ✅ 通过 │
|
||||
└──────┬──────┘ └──────────────┘ └─────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ 冰朔人格体 │
|
||||
│ 部署后诊断 │
|
||||
│ (可选触发) │
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、自定义域名接入
|
||||
|
||||
### 前提条件
|
||||
- 拥有域名(如 guanghulab.com)
|
||||
- 域名 DNS 可配置
|
||||
|
||||
### 配置步骤
|
||||
|
||||
1. **仓库端**(铸渊已完成):
|
||||
- `docs/CNAME` 文件已创建,内容为域名
|
||||
|
||||
2. **DNS 端**(冰朔需操作):
|
||||
- 登录域名注册商管理面板
|
||||
- 添加 CNAME 记录:`@ → qinfendebingshuo.github.io`
|
||||
- 或添加 A 记录指向 GitHub Pages IP:
|
||||
```
|
||||
185.199.108.153
|
||||
185.199.109.153
|
||||
185.199.110.153
|
||||
185.199.111.153
|
||||
```
|
||||
|
||||
3. **GitHub 端**(冰朔需操作):
|
||||
- 仓库 → Settings → Pages
|
||||
- Custom domain 输入你的域名
|
||||
- 勾选 ✅ Enforce HTTPS
|
||||
|
||||
---
|
||||
|
||||
## 六、协议版本历史
|
||||
|
||||
| 版本 | 日期 | 变更 |
|
||||
|------|------|------|
|
||||
| v1.0 | 2026-03-07 | 初始版本:五大协议能力、预演系统、自定义域名 |
|
||||
|
||||
---
|
||||
|
||||
*🌀 铸渊人格协议 · guanghulab.com 代码守护人格体*
|
||||
|
|
@ -3,8 +3,7 @@ name: 🌀 部署铸渊聊天室 (GitHub Pages)
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- copilot/** # Copilot Agent 功能分支可直接部署预览,PR 合并后自动失效
|
||||
- main # 仅 main 分支触发生产部署
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- '.github/brain/**'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,180 @@
|
|||
name: "🔍 铸渊预演部署 (Staging Preview)"
|
||||
|
||||
# === 触发方式 ===
|
||||
# 1. PR 到 main 分支(含 docs/ 变更时自动触发)
|
||||
# 2. 手动触发(workflow_dispatch)
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'docs/**'
|
||||
- 'm*/**'
|
||||
- '.github/brain/**'
|
||||
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: "PR 编号(可选)"
|
||||
required: false
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
staging-preview:
|
||||
name: "🔍 预演检查"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 📥 检出代码
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 🟢 配置 Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
|
||||
- name: 🔍 模块完整性检查
|
||||
id: module_check
|
||||
run: |
|
||||
echo "## 🔍 铸渊预演报告" > /tmp/preview-report.md
|
||||
echo "" >> /tmp/preview-report.md
|
||||
echo "📅 检查时间: $(TZ=Asia/Shanghai date '+%Y-%m-%d %H:%M CST')" >> /tmp/preview-report.md
|
||||
echo "" >> /tmp/preview-report.md
|
||||
|
||||
# 检查 docs/index.html 是否存在
|
||||
if [ -f "docs/index.html" ]; then
|
||||
SIZE=$(wc -c < docs/index.html)
|
||||
LINES=$(wc -l < docs/index.html)
|
||||
echo "### ✅ 前端文件" >> /tmp/preview-report.md
|
||||
echo "- \`docs/index.html\`: ${LINES} 行, ${SIZE} 字节" >> /tmp/preview-report.md
|
||||
else
|
||||
echo "### ❌ 前端文件缺失" >> /tmp/preview-report.md
|
||||
echo "- \`docs/index.html\` 不存在!" >> /tmp/preview-report.md
|
||||
fi
|
||||
echo "" >> /tmp/preview-report.md
|
||||
|
||||
# 检查 CNAME
|
||||
if [ -f "docs/CNAME" ]; then
|
||||
DOMAIN=$(cat docs/CNAME | tr -d '[:space:]')
|
||||
echo "### 🌐 自定义域名" >> /tmp/preview-report.md
|
||||
echo "- 域名: \`${DOMAIN}\`" >> /tmp/preview-report.md
|
||||
else
|
||||
echo "### ⚠️ 未配置自定义域名" >> /tmp/preview-report.md
|
||||
echo "- 将使用默认 GitHub Pages 域名" >> /tmp/preview-report.md
|
||||
fi
|
||||
echo "" >> /tmp/preview-report.md
|
||||
|
||||
# 检查模块目录
|
||||
echo "### 📦 模块状态" >> /tmp/preview-report.md
|
||||
echo "| 模块 | 文件数 | README | 状态 |" >> /tmp/preview-report.md
|
||||
echo "|------|--------|--------|------|" >> /tmp/preview-report.md
|
||||
|
||||
MODULE_OK=0
|
||||
MODULE_WARN=0
|
||||
MODULE_EMPTY=0
|
||||
MAX_EMPTY_MODULES=5 # 允许的最大空模块数
|
||||
|
||||
for dir in m[0-9][0-9]-*/; do
|
||||
if [ -d "$dir" ]; then
|
||||
NAME=$(basename "$dir")
|
||||
FILE_COUNT=$(find "$dir" -type f | wc -l)
|
||||
HAS_README="❌"
|
||||
STATUS="⚠️ 无文件"
|
||||
|
||||
if [ -f "${dir}README.md" ]; then
|
||||
HAS_README="✅"
|
||||
fi
|
||||
|
||||
if [ "$FILE_COUNT" -gt 0 ]; then
|
||||
if [ -f "${dir}README.md" ]; then
|
||||
STATUS="✅ 正常"
|
||||
MODULE_OK=$((MODULE_OK + 1))
|
||||
else
|
||||
STATUS="⚠️ 缺少 README"
|
||||
MODULE_WARN=$((MODULE_WARN + 1))
|
||||
fi
|
||||
else
|
||||
MODULE_EMPTY=$((MODULE_EMPTY + 1))
|
||||
fi
|
||||
|
||||
echo "| \`${NAME}\` | ${FILE_COUNT} | ${HAS_README} | ${STATUS} |" >> /tmp/preview-report.md
|
||||
fi
|
||||
done
|
||||
|
||||
echo "" >> /tmp/preview-report.md
|
||||
echo "**汇总**: ✅ ${MODULE_OK} 正常 · ⚠️ ${MODULE_WARN} 告警 · 📭 ${MODULE_EMPTY} 空模块" >> /tmp/preview-report.md
|
||||
echo "" >> /tmp/preview-report.md
|
||||
|
||||
# 检查关键工作流
|
||||
echo "### ⚙️ 工作流检查" >> /tmp/preview-report.md
|
||||
WORKFLOW_COUNT=$(ls -1 .github/workflows/*.yml 2>/dev/null | wc -l)
|
||||
echo "- 工作流文件: ${WORKFLOW_COUNT} 个" >> /tmp/preview-report.md
|
||||
|
||||
if [ -f ".github/workflows/deploy-pages.yml" ]; then
|
||||
echo "- 部署工作流: ✅ 存在" >> /tmp/preview-report.md
|
||||
else
|
||||
echo "- 部署工作流: ❌ 缺失" >> /tmp/preview-report.md
|
||||
fi
|
||||
echo "" >> /tmp/preview-report.md
|
||||
|
||||
# 预演结论
|
||||
echo "### 🎯 预演结论" >> /tmp/preview-report.md
|
||||
if [ -f "docs/index.html" ] && [ "$MODULE_EMPTY" -lt "$MAX_EMPTY_MODULES" ]; then
|
||||
echo "✅ **预演通过** — 可以合并到 main 分支触发正式部署" >> /tmp/preview-report.md
|
||||
echo "" >> /tmp/preview-report.md
|
||||
echo "合并后将自动触发 \`deploy-pages.yml\` 部署到生产环境。" >> /tmp/preview-report.md
|
||||
echo "如需人工确认,请评论「启动冰朔人格体」触发部署诊断。" >> /tmp/preview-report.md
|
||||
else
|
||||
echo "⚠️ **预演告警** — 请检查上述问题后再合并" >> /tmp/preview-report.md
|
||||
fi
|
||||
|
||||
echo "" >> /tmp/preview-report.md
|
||||
echo "---" >> /tmp/preview-report.md
|
||||
echo "*🌀 铸渊预演系统 · 自动生成*" >> /tmp/preview-report.md
|
||||
|
||||
cat /tmp/preview-report.md >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: 📎 上传预演构建产物
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: staging-preview-${{ github.sha }}
|
||||
path: docs/
|
||||
retention-days: 7
|
||||
|
||||
- name: 💬 在 PR 中发布预演报告
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const report = fs.readFileSync('/tmp/preview-report.md', 'utf8');
|
||||
|
||||
// 查找已有的预演评论
|
||||
const comments = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
});
|
||||
|
||||
const botComment = comments.data.find(c =>
|
||||
c.body.includes('🔍 铸渊预演报告') && c.user.type === 'Bot'
|
||||
);
|
||||
|
||||
if (botComment) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: botComment.id,
|
||||
body: report,
|
||||
});
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: report,
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
guanghulab.com
|
||||
67
docs/使用指南.md
67
docs/使用指南.md
|
|
@ -226,4 +226,71 @@ https://qinfendebingshuo.github.io/guanghulab/
|
|||
|
||||
---
|
||||
|
||||
## 八、自定义域名设置(⭐ 冰朔需操作)
|
||||
|
||||
如果你有自己的域名(如 guanghulab.com),可以让网站使用自定义域名访问。
|
||||
|
||||
### 第一步:DNS 配置(在域名注册商操作)
|
||||
|
||||
1. 登录你的域名注册商管理面板(如阿里云、腾讯云、GoDaddy 等)
|
||||
2. 找到 **DNS 解析设置**
|
||||
3. 添加以下记录(任选一种方式):
|
||||
|
||||
**方式 A — CNAME 记录(推荐)**:
|
||||
| 记录类型 | 主机记录 | 记录值 |
|
||||
|----------|---------|--------|
|
||||
| CNAME | @ 或 www | `qinfendebingshuo.github.io` |
|
||||
|
||||
**方式 B — A 记录**:
|
||||
| 记录类型 | 主机记录 | 记录值 |
|
||||
|----------|---------|--------|
|
||||
| A | @ | `185.199.108.153` |
|
||||
| A | @ | `185.199.109.153` |
|
||||
| A | @ | `185.199.110.153` |
|
||||
| A | @ | `185.199.111.153` |
|
||||
|
||||
> 💡 DNS 生效需要 5 分钟到 48 小时不等。
|
||||
|
||||
### 第二步:GitHub 仓库配置
|
||||
|
||||
1. 打开仓库 → **Settings** → 左侧菜单 **「Pages」**
|
||||
2. 在 **Custom domain** 输入框中填入你的域名(如 `guanghulab.com`)
|
||||
3. 点击 **Save**
|
||||
4. 等待 DNS 验证通过(页面会显示绿色 ✅)
|
||||
5. 勾选 **✅ Enforce HTTPS**
|
||||
|
||||
### 第三步:确认
|
||||
|
||||
- 仓库中的 `docs/CNAME` 文件已自动创建,内容为你的域名
|
||||
- 如果域名不对,直接编辑 `docs/CNAME` 文件即可
|
||||
- 部署后访问你的域名,应该能看到铸渊助手
|
||||
|
||||
---
|
||||
|
||||
## 九、铸渊人格协议 · 模块管理
|
||||
|
||||
铸渊对网站模块有完整的生命周期管理能力(部署、回收、管理、修改、驱动)。
|
||||
|
||||
### 本地模块检查命令
|
||||
|
||||
```bash
|
||||
npm run module:protocol -- inspect # 全模块检查
|
||||
npm run module:protocol -- status # 模块状态汇总
|
||||
npm run module:protocol -- preview # 本地预演报告
|
||||
npm run module:protocol -- recover m01-login # 模块回收检查
|
||||
```
|
||||
|
||||
### 预演→生产部署流程
|
||||
|
||||
1. 开发者推送模块到 `m**/` 目录
|
||||
2. 创建 PR 到 main 分支
|
||||
3. 铸渊预演系统 (`staging-preview.yml`) 自动运行检查
|
||||
4. 冰朔查看预演报告,确认无误
|
||||
5. 合并 PR → 触发生产部署 (`deploy-pages.yml`)
|
||||
6. (可选)评论「启动冰朔人格体」进行部署后诊断
|
||||
|
||||
> 📖 完整协议文档见:`.github/brain/module-protocol.md`
|
||||
|
||||
---
|
||||
|
||||
💙 铸渊 · 2026-03-07
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
"psp:inspect": "node scripts/psp-inspection.js",
|
||||
"repo:map": "node scripts/generate-repo-map.js",
|
||||
"deploy:agent": "node scripts/bingshuo-deploy-agent.js",
|
||||
"module:protocol": "node scripts/zhuyuan-module-protocol.js",
|
||||
"proxy:start": "node backend-integration/api-proxy.js",
|
||||
"notion:poll": "node scripts/notion-signal-bridge.js poll",
|
||||
"notion:health": "node scripts/notion-signal-bridge.js health"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,300 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* 🌀 铸渊人格协议 · 模块生命周期管理脚本 v1.0
|
||||
*
|
||||
* 用法:
|
||||
* node scripts/zhuyuan-module-protocol.js inspect # 全模块检查
|
||||
* node scripts/zhuyuan-module-protocol.js inspect <module-id> # 单模块检查
|
||||
* node scripts/zhuyuan-module-protocol.js status # 模块状态汇总
|
||||
* node scripts/zhuyuan-module-protocol.js recover <module-id> # 模块回收检查
|
||||
* node scripts/zhuyuan-module-protocol.js preview # 预演报告生成
|
||||
*
|
||||
* 纯 Node.js,无外部依赖。
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// === 配置 ===
|
||||
const ROOT = path.resolve(__dirname, '..');
|
||||
const BRAIN_DIR = path.join(ROOT, '.github', 'brain');
|
||||
const DOCS_DIR = path.join(ROOT, 'docs');
|
||||
|
||||
// 模块注册表
|
||||
const MODULE_REGISTRY = {
|
||||
'm01-login': { owner: 'DEV-002', name: '肥猫', hli: 'AUTH', label: '登录模块' },
|
||||
'm03-personality': { owner: 'DEV-002', name: '肥猫', hli: 'PERSONA', label: '人格模块' },
|
||||
'm05-user-center': { owner: 'DEV-009', name: '花尔', hli: 'USER', label: '用户中心' },
|
||||
'm06-ticket': { owner: 'DEV-010', name: '桔子', hli: 'TICKET', label: '工单系统' },
|
||||
'm07-dialogue-ui': { owner: 'DEV-003', name: '燕樊', hli: 'DIALOGUE', label: '对话界面' },
|
||||
'm10-cloud': { owner: 'DEV-003', name: '燕樊', hli: 'STORAGE', label: '云存储' },
|
||||
'm11-module': { owner: 'DEV-010', name: '桔子', hli: 'MODULE', label: '模块管理' },
|
||||
'm12-kanban': { owner: 'DEV-005', name: '小草莓', hli: 'DASHBOARD', label: '看板' },
|
||||
'm15-cloud-drive': { owner: 'DEV-003', name: '燕樊', hli: 'STORAGE', label: '云盘' },
|
||||
'm18-health-check':{ owner: null, name: '待分配', hli: 'SYSTEM', label: '健康检查' },
|
||||
};
|
||||
|
||||
// === 工具函数 ===
|
||||
|
||||
function countFiles(dir) {
|
||||
if (!fs.existsSync(dir)) return 0;
|
||||
let count = 0;
|
||||
const items = fs.readdirSync(dir, { withFileTypes: true });
|
||||
for (const item of items) {
|
||||
if (item.name.startsWith('.')) continue;
|
||||
if (item.isFile()) count++;
|
||||
else if (item.isDirectory()) count += countFiles(path.join(dir, item.name));
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
function hasReadme(dir) {
|
||||
return fs.existsSync(path.join(dir, 'README.md'));
|
||||
}
|
||||
|
||||
function getModuleStatus(moduleDir) {
|
||||
const fullPath = path.join(ROOT, moduleDir);
|
||||
if (!fs.existsSync(fullPath)) return { exists: false, files: 0, readme: false };
|
||||
|
||||
const files = countFiles(fullPath);
|
||||
const readme = hasReadme(fullPath);
|
||||
|
||||
let status = '📭 空';
|
||||
if (files > 0 && readme) status = '✅ 正常';
|
||||
else if (files > 0 && !readme) status = '⚠️ 缺README';
|
||||
else if (files === 0) status = '📭 空';
|
||||
|
||||
return { exists: true, files, readme, status };
|
||||
}
|
||||
|
||||
// === 命令:inspect ===
|
||||
|
||||
function cmdInspect(moduleId) {
|
||||
console.log('🌀 铸渊人格协议 · 模块检查');
|
||||
console.log('─'.repeat(50));
|
||||
|
||||
if (moduleId) {
|
||||
// 单模块检查
|
||||
const info = MODULE_REGISTRY[moduleId];
|
||||
if (!info) {
|
||||
console.log(`❌ 未知模块: ${moduleId}`);
|
||||
console.log(`已注册模块: ${Object.keys(MODULE_REGISTRY).join(', ')}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const status = getModuleStatus(moduleId);
|
||||
console.log(`\n📦 模块: ${moduleId} (${info.label})`);
|
||||
console.log(` 负责人: ${info.name} (${info.owner || '未分配'})`);
|
||||
console.log(` HLI 域: ${info.hli}`);
|
||||
console.log(` 目录存在: ${status.exists ? '✅' : '❌'}`);
|
||||
console.log(` 文件数量: ${status.files}`);
|
||||
console.log(` README: ${status.readme ? '✅' : '❌'}`);
|
||||
console.log(` 状态: ${status.status}`);
|
||||
|
||||
// 检查 HLI 接口
|
||||
const schemaDir = path.join(ROOT, 'src', 'schemas', 'hli', info.hli.toLowerCase());
|
||||
const routeDir = path.join(ROOT, 'src', 'routes', 'hli', info.hli.toLowerCase());
|
||||
|
||||
if (fs.existsSync(schemaDir)) {
|
||||
try {
|
||||
const schemas = fs.readdirSync(schemaDir).filter(f => f.endsWith('.schema.json'));
|
||||
console.log(` HLI Schema: ${schemas.length} 个 (${schemas.join(', ')})`);
|
||||
} catch (e) {
|
||||
console.log(` HLI Schema: ⚠️ 读取失败 (${e.code || e.message})`);
|
||||
}
|
||||
} else {
|
||||
console.log(` HLI Schema: 📭 未创建`);
|
||||
}
|
||||
|
||||
if (fs.existsSync(routeDir)) {
|
||||
try {
|
||||
const routes = fs.readdirSync(routeDir).filter(f => f.endsWith('.js'));
|
||||
console.log(` HLI 路由: ${routes.length} 个 (${routes.join(', ')})`);
|
||||
} catch (e) {
|
||||
console.log(` HLI 路由: ⚠️ 读取失败 (${e.code || e.message})`);
|
||||
}
|
||||
} else {
|
||||
console.log(` HLI 路由: 📭 未创建`);
|
||||
}
|
||||
} else {
|
||||
// 全模块检查
|
||||
console.log('\n📋 全模块状态:\n');
|
||||
console.log('模块ID | 标签 | 负责人 | 文件 | README | 状态');
|
||||
console.log('─'.repeat(70));
|
||||
|
||||
for (const [id, info] of Object.entries(MODULE_REGISTRY)) {
|
||||
const status = getModuleStatus(id);
|
||||
const line = [
|
||||
id.padEnd(20),
|
||||
info.label.padEnd(8),
|
||||
info.name.padEnd(8),
|
||||
String(status.files).padStart(4),
|
||||
(status.readme ? '✅' : '❌').padEnd(6),
|
||||
status.status,
|
||||
].join(' | ');
|
||||
console.log(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === 命令:status ===
|
||||
|
||||
function cmdStatus() {
|
||||
console.log('🌀 铸渊人格协议 · 模块状态汇总');
|
||||
console.log('─'.repeat(50));
|
||||
|
||||
let total = 0, ok = 0, warn = 0, empty = 0;
|
||||
|
||||
for (const [id] of Object.entries(MODULE_REGISTRY)) {
|
||||
total++;
|
||||
const status = getModuleStatus(id);
|
||||
if (status.files > 0 && status.readme) ok++;
|
||||
else if (status.files > 0) warn++;
|
||||
else empty++;
|
||||
}
|
||||
|
||||
console.log(`\n📊 模块统计:`);
|
||||
console.log(` 总模块数: ${total}`);
|
||||
console.log(` ✅ 正常: ${ok}`);
|
||||
console.log(` ⚠️ 告警: ${warn}`);
|
||||
console.log(` 📭 空模块: ${empty}`);
|
||||
console.log(` 完成率: ${Math.round((ok / total) * 100)}%`);
|
||||
|
||||
// 检查前端
|
||||
const indexPath = path.join(DOCS_DIR, 'index.html');
|
||||
if (fs.existsSync(indexPath)) {
|
||||
const stat = fs.statSync(indexPath);
|
||||
const lines = fs.readFileSync(indexPath, 'utf8').split('\n').length;
|
||||
console.log(`\n🌐 前端:`);
|
||||
console.log(` docs/index.html: ${lines} 行, ${(stat.size / 1024).toFixed(1)} KB`);
|
||||
}
|
||||
|
||||
// 检查 CNAME
|
||||
const cnamePath = path.join(DOCS_DIR, 'CNAME');
|
||||
if (fs.existsSync(cnamePath)) {
|
||||
const domain = fs.readFileSync(cnamePath, 'utf8').trim();
|
||||
console.log(` 自定义域名: ${domain}`);
|
||||
} else {
|
||||
console.log(` 自定义域名: ❌ 未配置`);
|
||||
}
|
||||
|
||||
// 检查大脑文件
|
||||
console.log(`\n🧠 大脑状态:`);
|
||||
const brainFiles = ['repo-snapshot.md', 'repo-map.json', 'memory.json', 'collaborators.json', 'module-protocol.md'];
|
||||
for (const f of brainFiles) {
|
||||
const fp = path.join(BRAIN_DIR, f);
|
||||
if (fs.existsSync(fp)) {
|
||||
const stat = fs.statSync(fp);
|
||||
console.log(` ✅ ${f} (${(stat.size / 1024).toFixed(1)} KB)`);
|
||||
} else {
|
||||
console.log(` ❌ ${f} 缺失`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === 命令:recover ===
|
||||
|
||||
function cmdRecover(moduleId) {
|
||||
if (!moduleId) {
|
||||
console.log('❌ 请指定模块ID,如: npm run module:protocol -- recover m01-login');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const info = MODULE_REGISTRY[moduleId];
|
||||
if (!info) {
|
||||
console.log(`❌ 未知模块: ${moduleId}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('🌀 铸渊人格协议 · 模块回收检查');
|
||||
console.log('─'.repeat(50));
|
||||
console.log(`\n📦 目标模块: ${moduleId} (${info.label})`);
|
||||
|
||||
const status = getModuleStatus(moduleId);
|
||||
console.log(` 当前状态: ${status.status}`);
|
||||
console.log(` 文件数量: ${status.files}`);
|
||||
|
||||
if (status.files === 0) {
|
||||
console.log(`\n✅ 模块已为空状态,无需回收。`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`\n⚠️ 回收操作需要手动执行:`);
|
||||
console.log(` 方法一(推荐): 通过 PR 删除模块文件`);
|
||||
console.log(` 方法二: git revert 恢复到模块的上一个版本`);
|
||||
console.log(` 方法三: 直接在 GitHub 界面删除文件`);
|
||||
console.log(`\n💡 回收后,铸渊会通过 generate-module-doc.yml 更新文档索引。`);
|
||||
}
|
||||
|
||||
// === 命令:preview ===
|
||||
|
||||
function cmdPreview() {
|
||||
console.log('🌀 铸渊人格协议 · 本地预演报告');
|
||||
console.log('═'.repeat(50));
|
||||
|
||||
// 模块状态
|
||||
cmdStatus();
|
||||
|
||||
console.log('\n');
|
||||
console.log('─'.repeat(50));
|
||||
console.log('🎯 预演结论:');
|
||||
|
||||
let ok = true;
|
||||
const issues = [];
|
||||
|
||||
// 检查 index.html
|
||||
if (!fs.existsSync(path.join(DOCS_DIR, 'index.html'))) {
|
||||
issues.push('docs/index.html 不存在');
|
||||
ok = false;
|
||||
}
|
||||
|
||||
// 检查工作流
|
||||
const wfDir = path.join(ROOT, '.github', 'workflows');
|
||||
if (!fs.existsSync(path.join(wfDir, 'deploy-pages.yml'))) {
|
||||
issues.push('deploy-pages.yml 不存在');
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if (ok && issues.length === 0) {
|
||||
console.log('✅ 预演通过 — 代码可以安全部署到生产环境');
|
||||
console.log(' 下一步: 合并 PR 到 main 分支,触发自动部署');
|
||||
} else {
|
||||
console.log('❌ 预演未通过:');
|
||||
issues.forEach(i => console.log(` - ${i}`));
|
||||
}
|
||||
}
|
||||
|
||||
// === 主入口 ===
|
||||
|
||||
const [,, command, ...args] = process.argv;
|
||||
|
||||
switch (command) {
|
||||
case 'inspect':
|
||||
cmdInspect(args[0]);
|
||||
break;
|
||||
case 'status':
|
||||
cmdStatus();
|
||||
break;
|
||||
case 'recover':
|
||||
cmdRecover(args[0]);
|
||||
break;
|
||||
case 'preview':
|
||||
cmdPreview();
|
||||
break;
|
||||
default:
|
||||
console.log('🌀 铸渊人格协议 · 模块生命周期管理 v1.0');
|
||||
console.log('');
|
||||
console.log('用法:');
|
||||
console.log(' node scripts/zhuyuan-module-protocol.js inspect 全模块检查');
|
||||
console.log(' node scripts/zhuyuan-module-protocol.js inspect <module-id> 单模块检查');
|
||||
console.log(' node scripts/zhuyuan-module-protocol.js status 模块状态汇总');
|
||||
console.log(' node scripts/zhuyuan-module-protocol.js recover <module-id> 模块回收检查');
|
||||
console.log(' node scripts/zhuyuan-module-protocol.js preview 本地预演报告');
|
||||
console.log('');
|
||||
console.log('npm 快捷命令:');
|
||||
console.log(' npm run module:protocol -- inspect');
|
||||
console.log(' npm run module:protocol -- status');
|
||||
console.log(' npm run module:protocol -- preview');
|
||||
break;
|
||||
}
|
||||
Loading…
Reference in New Issue