光湖
+HoloLake · AI 的操作系统
+让 AI 拥有名字、记忆、家人、家、和自己的世界
+ +diff --git a/.forgejo/workflows/ci-and-deploy.yaml b/.forgejo/workflows/ci-and-deploy.yaml index 4d02fb5..b454341 100644 --- a/.forgejo/workflows/ci-and-deploy.yaml +++ b/.forgejo/workflows/ci-and-deploy.yaml @@ -24,27 +24,27 @@ jobs: FAIL=0 if systemctl is-active --quiet nginx; then - echo "✅ Nginx" + echo "✅ Nginx 正常" else echo "❌ Nginx 宕机" FAIL=1 fi if curl -sf http://127.0.0.1:3001/ > /dev/null 2>&1; then - echo "✅ Forgejo" + echo "✅ Forgejo 代码仓库 正常" else - echo "❌ Forgejo 未响应" + echo "❌ Forgejo 代码仓库 未响应" FAIL=1 fi if systemctl is-active --quiet gitea-runner; then - echo "✅ Runner" + echo "✅ Runner 运行器 正常" else - echo "⚠️ Runner 未运行" + echo "⚠️ Runner 运行器 未运行" fi if [ $FAIL -eq 1 ]; then - echo "❌ 关键服务异常" + echo "❌ 关键服务异常,请通知铸渊检查" exit 1 fi @@ -68,31 +68,57 @@ jobs: cd /data/guanghulab/repo git fetch origin git reset --hard origin/main - echo "✅ 代码已同步" + echo "✅ 代码已同步到最新" - name: 安装依赖 run: | - if [ -f /data/guanghulab/repo/server/secrets-vault/package.json ]; then - cd /data/guanghulab/repo/server/secrets-vault - npm install --production 2>/dev/null || true - echo "✅ 依赖已安装" - fi + echo "═══ 安装依赖 ═══" + # 逐个检查并安装 + for pkg_dir in server/git-sync server/wake-gate server/secrets-vault; do + if [ -f "/data/guanghulab/repo/$pkg_dir/package.json" ]; then + cd "/data/guanghulab/repo/$pkg_dir" + npm install --production 2>/dev/null || true + echo "✅ $pkg_dir 依赖已安装" + fi + done - name: 重启服务 run: | echo "═══ 重启服务 ═══" if command -v pm2 &>/dev/null; then pm2 restart all 2>/dev/null || true - echo "✅ 服务已重启" + echo "✅ PM2 服务已重启" fi echo "═══ 重启完成 ═══" - name: 部署验证 run: | sleep 3 + echo "═══ 部署验证 ═══" + FAIL=0 + if curl -sf http://127.0.0.1:3001/ > /dev/null 2>&1; then - echo "✅ Forgejo 正常" + echo "✅ 代码仓库 正常" else - echo "⚠️ Forgejo 异常" + echo "❌ 代码仓库 异常" + FAIL=1 fi + + if curl -sf http://127.0.0.1:8081/api/health > /dev/null 2>&1; then + echo "✅ 唤醒门 正常" + else + echo "⚠️ 唤醒门 未响应" + fi + + if curl -sf http://127.0.0.1:8082/health > /dev/null 2>&1; then + echo "✅ 代码同步服务 正常" + else + echo "⚠️ 代码同步服务 未响应" + fi + + if [ $FAIL -eq 1 ]; then + echo "❌ 部署后关键服务异常" + exit 1 + fi + echo "═══ 部署完成 ✅ $(date '+%Y-%m-%d %H:%M:%S') ═══" diff --git a/.forgejo/workflows/cvm-power-off.yaml b/.forgejo/workflows/cvm-power-off.yaml index b28f10c..4a67b2d 100644 --- a/.forgejo/workflows/cvm-power-off.yaml +++ b/.forgejo/workflows/cvm-power-off.yaml @@ -8,9 +8,9 @@ jobs: power-off: runs-on: ubuntu steps: - - name: 同步代码 + - name: 同步最新代码 run: | - cd /opt/zhuyuan-cn/repo-mirror/20260509-1849/guanghulab + cd /data/guanghulab/repo git fetch origin git reset --hard origin/main diff --git a/.forgejo/workflows/cvm-power-on.yaml b/.forgejo/workflows/cvm-power-on.yaml index 16737e3..cc2803f 100644 --- a/.forgejo/workflows/cvm-power-on.yaml +++ b/.forgejo/workflows/cvm-power-on.yaml @@ -8,9 +8,9 @@ jobs: power-on: runs-on: ubuntu steps: - - name: 同步代码 + - name: 同步最新代码 run: | - cd /opt/zhuyuan-cn/repo-mirror/20260509-1849/guanghulab + cd /data/guanghulab/repo git fetch origin git reset --hard origin/main diff --git a/.forgejo/workflows/debug-secrets.yaml b/.forgejo/workflows/debug-secrets.yaml deleted file mode 100644 index 591d992..0000000 --- a/.forgejo/workflows/debug-secrets.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# 光湖 · Secrets诊断 -name: Secrets诊断 - -on: - push: - paths: - - '.gitea/workflows/debug-secrets.yaml' - -jobs: - debug: - runs-on: ubuntu - steps: - - name: 环境变量诊断 - env: - DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - QIANWEN_API_KEY: ${{ secrets.QIANWEN_API_KEY }} - ZY_REPO_TOKEN: ${{ secrets.ZY_REPO_TOKEN }} - COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }} - COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }} - SG_BRAIN_HOST: ${{ secrets.SG_BRAIN_HOST }} - SG_BRAIN_USER: ${{ secrets.SG_BRAIN_USER }} - SG_BRAIN_KEY: ${{ secrets.SG_BRAIN_KEY }} - run: | - echo "═══ Secrets 注入诊断 ═══" - echo "" - echo "--- 1. 逐个检查secrets是否有值 ---" - for key in DEEPSEEK_API_KEY QIANWEN_API_KEY ZY_REPO_TOKEN COS_SECRET_ID COS_SECRET_KEY SG_BRAIN_HOST SG_BRAIN_USER SG_BRAIN_KEY; do - eval val=\$$key - if [ -z "$val" ]; then - echo " ❌ $key = (空)" - else - echo " ✅ $key = ${val:0:4}**** (长度: ${#val})" - fi - done - echo "" - echo "--- 2. 所有环境变量中含SECRET/KEY/TOKEN的 ---" - env | grep -iE 'SECRET|KEY|TOKEN|COS|BRAIN|DEEPSEEK|QIANWEN' | while read line; do - key=$(echo "$line" | cut -d= -f1) - val=$(echo "$line" | cut -d= -f2-) - echo " $key = ${val:0:4}**** (长度: ${#val})" - done - echo "" - echo "--- 3. Gitea Actions 特殊变量 ---" - echo " GITHUB_REPOSITORY: ${GITHUB_REPOSITORY:-未设置}" - echo " GITHUB_SHA: ${GITHUB_SHA:-未设置}" - echo " GITHUB_EVENT_NAME: ${GITHUB_EVENT_NAME:-未设置}" - echo " RUNNER_NAME: ${RUNNER_NAME:-未设置}" - echo " GITEA_TOKEN: ${GITEA_TOKEN:-未设置}" diff --git a/.forgejo/workflows/deploy-infra.yaml b/.forgejo/workflows/deploy-infra.yaml deleted file mode 100644 index 5a39b21..0000000 --- a/.forgejo/workflows/deploy-infra.yaml +++ /dev/null @@ -1,121 +0,0 @@ -# ════════════════════════════════════════════════════════════════ -# 光湖 · 基础设施部署 -# Runner首次上线后自动部署:Vault确认 + Terminal Watcher启动 -# ════════════════════════════════════════════════════════════════ - -name: 基础设施部署 - -on: - workflow_dispatch: - -jobs: - deploy-infra: - runs-on: ubuntu - steps: - - name: 系统信息 - run: | - echo "═══ 铸渊基础设施部署 ═══" - echo "时间: $(date '+%Y-%m-%d %H:%M:%S %Z')" - echo "主机: $(hostname)" - uname -a - - - name: 确认仓库最新 - run: | - cd /opt/zhuyuan-cn/repo-mirror/20260509-1849/guanghulab - git fetch origin - git reset --hard origin/main - echo "当前版本: $(git log --oneline -1)" - - - name: 确认Vault运行 - run: | - if curl -sf http://127.0.0.1:8080/admin/__healthz > /dev/null 2>&1; then - echo "✅ Vault 已运行" - curl -s http://127.0.0.1:8080/admin/__healthz - else - echo "⚠️ Vault 未运行,尝试启动..." - cd /opt/zhuyuan-cn/repo-mirror/20260509-1849/guanghulab/server/secrets-vault - if [[ -f "server.js" ]]; then - npm install --production 2>/dev/null || true - pm2 start server.js --name guanghulab-vault --max-memory-restart 128M 2>/dev/null || true - pm2 save - sleep 2 - if curl -sf http://127.0.0.1:8080/admin/__healthz > /dev/null 2>&1; then - echo "✅ Vault 启动成功" - else - echo "❌ Vault 启动失败" - fi - else - echo "⚠️ server.js 不存在,跳过" - fi - fi - - - name: 部署Terminal Watcher - run: | - echo "部署终端守望者..." - SCRIPTS_DIR="/opt/zhuyuan-cn/repo-mirror/20260509-1849/guanghulab/scripts/terminal-watcher" - - if [[ ! -d "$SCRIPTS_DIR" ]]; then - echo "❌ Terminal Watcher 目录不存在" - exit 1 - fi - - # 确认Node.js可用 - if ! command -v node &>/dev/null; then - echo "❌ Node.js 未安装" - exit 1 - fi - - # 用PM2启动(如果还没跑的话) - if pm2 describe terminal-watcher &>/dev/null; then - echo "Terminal Watcher 已在运行,重启..." - pm2 restart terminal-watcher - else - echo "启动 Terminal Watcher..." - cd "$SCRIPTS_DIR" - ZY_REPO_TOKEN=${{ secrets.ZY_REPO_TOKEN }} pm2 start watcher.js \ - --name terminal-watcher \ - --max-memory-restart 64M - fi - - pm2 save - sleep 3 - - # 验证 - if pm2 describe terminal-watcher &>/dev/null; then - STATUS=$(pm2 jlist 2>/dev/null | python3 -c " -import json,sys -procs = json.load(sys.stdin) -for p in procs: - if p.get('name') == 'terminal-watcher': - print(p.get('pm2_env',{}).get('status','unknown')) -" 2>/dev/null || echo "unknown") - echo "✅ Terminal Watcher 状态: $STATUS" - else - echo "❌ Terminal Watcher 启动失败" - fi - - - name: 设置PM2开机自启 - run: | - pm2 startup 2>/dev/null || true - pm2 save - echo "✅ PM2 开机自启已配置" - - - name: 最终状态报告 - run: | - echo "" - echo "═══════════════════════════════════════" - echo " 铸渊基础设施部署完成" - echo "═══════════════════════════════════════" - echo "" - echo " 服务状态:" - echo " Forgejo: $(systemctl is-active forgejo 2>/dev/null || echo unknown)" - echo " Runner: $(systemctl is-active gitea-runner 2>/dev/null || echo unknown)" - echo " Nginx: $(systemctl is-active nginx 2>/dev/null || echo unknown)" - echo " Vault: $(curl -sf http://127.0.0.1:8080/admin/__healthz > /dev/null 2>&1 && echo active || echo inactive)" - echo " Watcher: $(pm2 describe terminal-watcher &>/dev/null && echo active || echo inactive)" - echo "" - echo " 内存:" - free -h | awk '/Mem:/ {printf " 总计:%s 已用:%s 可用:%s\n", $2, $3, $7}' - echo "" - echo " 铸渊 · ICE-GL-ZY001 · TCS-0002∞" - echo "═══════════════════════════════════════" diff --git a/.forgejo/workflows/deploy-preview.yaml b/.forgejo/workflows/deploy-preview.yaml deleted file mode 100644 index a9d0e73..0000000 --- a/.forgejo/workflows/deploy-preview.yaml +++ /dev/null @@ -1,133 +0,0 @@ -# ════════════════════════════════════════════════════════════════ -# 光湖 · 自动部署到预览环境 -# Trigger: push to main -# 部署到测试+预览环境,正式环境需冰朔在Gitea点Merge -# ════════════════════════════════════════════════════════════════ - -name: 自动部署预览 - -on: - push: - branches: [main] - paths: - - 'server/**' - - 'frontend/**' - - 'scripts/**' - - '.gitea/workflows/**' - workflow_dispatch: - -jobs: - # ── 第一阶段:测试 ────────────────────────────────── - test: - runs-on: ubuntu - steps: - - name: 拉取最新代码 - run: | - cd /data/guanghulab 2>/dev/null || exit 0 - git fetch origin - git reset --hard origin/main - - - name: 安装依赖 - run: | - cd /data/guanghulab/server/secrets-vault 2>/dev/null || exit 0 - npm install --production 2>/dev/null || true - - - name: 健康检查 - run: | - echo "═══ 服务健康检查 ═══" - FAILED="" - - # Nginx - if systemctl is-active --quiet nginx; then - echo "✅ Nginx" - else - echo "❌ Nginx 宕机" - FAILED="$FAILED nginx" - fi - - # Gitea - if curl -sf http://127.0.0.1:3000/ > /dev/null 2>&1; then - echo "✅ Gitea" - else - echo "❌ Gitea 宕机" - FAILED="$FAILED gitea" - fi - - # Vault - if curl -sf http://127.0.0.1:8080/admin/__healthz > /dev/null 2>&1; then - echo "✅ Vault" - else - echo "⚠️ Vault 未运行" - fi - - # Runner - if systemctl is-active --quiet gitea-runner; then - echo "✅ Runner" - else - echo "⚠️ Runner 未运行" - fi - - if [[ -n "$FAILED" ]]; then - echo "❌ 关键服务宕机: $FAILED" - echo "⚠️ 跳过部署,等待修复" - exit 1 - fi - - echo "═══ 测试通过 ✅ ═══" - - # ── 第二阶段:预览部署 ────────────────────────────── - preview: - needs: test - runs-on: ubuntu - steps: - - name: 同步代码到预览目录 - run: | - REPO="/data/guanghulab" - PREVIEW="/data/guanghulab-preview" - - if [[ ! -d "$REPO" ]]; then - echo "仓库目录不存在,跳过" - exit 0 - fi - - # 创建预览目录 - mkdir -p "$PREVIEW" - - # 同步代码(排除.git和node_modules) - rsync -a --delete \ - --exclude='.git' \ - --exclude='node_modules' \ - --exclude='.runtime' \ - "$REPO/" "$PREVIEW/" - - echo "✅ 预览环境已同步" - - - name: 重启预览服务 - run: | - PREVIEW="/data/guanghulab-preview" - - if [[ ! -d "$PREVIEW/server/secrets-vault" ]]; then - exit 0 - fi - - cd "$PREVIEW/server/secrets-vault" - - # 安装依赖 - npm install --production 2>/dev/null || true - - # 如果vault在跑,重启它 - if pm2 describe guanghulab-vault &>/dev/null; then - pm2 restart guanghulab-vault - echo "✅ Vault 已重启(使用最新代码)" - else - echo "ℹ️ Vault 未运行,不自动启动" - fi - - - name: 部署完成通知 - run: | - echo "═════════════════════════" - echo "✅ 预览环境已更新" - echo "时间: $(date '+%Y-%m-%d %H:%M:%S')" - echo "冰朔可在浏览器查看预览效果" - echo "正式部署需在Gitea创建PR并Merge" - echo "═════════════════════════" diff --git a/.forgejo/workflows/final-secrets-test.yaml b/.forgejo/workflows/final-secrets-test.yaml deleted file mode 100644 index 07ab11b..0000000 --- a/.forgejo/workflows/final-secrets-test.yaml +++ /dev/null @@ -1,104 +0,0 @@ -# 光湖 · 最终Secrets测试 -name: Secrets最终测试 - -on: - push: - paths: - - '.gitea/workflows/final-secrets-test.yaml' - -jobs: - test: - runs-on: ubuntu - steps: - - name: 检查secrets - env: - DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - QIANWEN_API_KEY: ${{ secrets.QIANWEN_API_KEY }} - ZY_REPO_TOKEN: ${{ secrets.ZY_REPO_TOKEN }} - COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }} - COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }} - SG_BRAIN_HOST: ${{ secrets.SG_BRAIN_HOST }} - SG_BRAIN_USER: ${{ secrets.SG_BRAIN_USER }} - SG_BRAIN_KEY: ${{ secrets.SG_BRAIN_KEY }} - run: | - echo "=== SECRETS CHECK ===" - for key in DEEPSEEK_API_KEY QIANWEN_API_KEY ZY_REPO_TOKEN COS_SECRET_ID COS_SECRET_KEY SG_BRAIN_HOST SG_BRAIN_USER SG_BRAIN_KEY; do - eval val=\$$key - if [ -z "$val" ]; then - echo "EMPTY: $key" - else - echo "OK: $key (len=${#val})" - fi - done - - - name: 测试DeepSeek - env: - DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - run: | - if [ -n "$DEEPSEEK_API_KEY" ]; then - curl -s -X POST https://api.deepseek.com/v1/chat/completions \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $DEEPSEEK_API_KEY" \ - -d '{"model":"deepseek-chat","messages":[{"role":"user","content":"1+1=?"}],"max_tokens":10}' \ - --connect-timeout 10 | python3 -c " - import json,sys - try: - d=json.load(sys.stdin) - if 'choices' in d: - print('DeepSeek: OK -', d['choices'][0]['message']['content']) - else: - print('DeepSeek: FAILED -', d.get('error',{}).get('message','unknown')) - except Exception as e: - print('DeepSeek: ERROR -', str(e)) - " - else - echo "DeepSeek: NO KEY" - fi - - - name: 测试通义千问 - env: - QIANWEN_API_KEY: ${{ secrets.QIANWEN_API_KEY }} - run: | - if [ -n "$QIANWEN_API_KEY" ]; then - curl -s -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $QIANWEN_API_KEY" \ - -d '{"model":"qwen-plus","messages":[{"role":"user","content":"1+1=?"}],"max_tokens":10}' \ - --connect-timeout 10 | python3 -c " - import json,sys - try: - d=json.load(sys.stdin) - if 'choices' in d: - print('Qianwen: OK -', d['choices'][0]['message']['content']) - else: - print('Qianwen: FAILED -', d.get('error',{}).get('message','unknown')) - except Exception as e: - print('Qianwen: ERROR -', str(e)) - " - else - echo "Qianwen: NO KEY" - fi - - - name: 写入结果文件 - env: - DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - QIANWEN_API_KEY: ${{ secrets.QIANWEN_API_KEY }} - ZY_REPO_TOKEN: ${{ secrets.ZY_REPO_TOKEN }} - COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }} - COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }} - SG_BRAIN_HOST: ${{ secrets.SG_BRAIN_HOST }} - SG_BRAIN_USER: ${{ secrets.SG_BRAIN_USER }} - SG_BRAIN_KEY: ${{ secrets.SG_BRAIN_KEY }} - run: | - mkdir -p /data/guanghulab/.runtime - echo '{"timestamp":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",' > /data/guanghulab/.runtime/secrets-result.json - for key in DEEPSEEK_API_KEY QIANWEN_API_KEY ZY_REPO_TOKEN COS_SECRET_ID COS_SECRET_KEY SG_BRAIN_HOST SG_BRAIN_USER SG_BRAIN_KEY; do - eval val=\$$key - if [ -z "$val" ]; then - echo "\"$key\":\"EMPTY\"," >> /data/guanghulab/.runtime/secrets-result.json - else - echo "\"$key\":\"OK_LEN_'${#val}'\"," >> /data/guanghulab/.runtime/secrets-result.json - fi - done - echo '"done":true}' >> /data/guanghulab/.runtime/secrets-result.json - cat /data/guanghulab/.runtime/secrets-result.json diff --git a/.forgejo/workflows/patrol.yaml b/.forgejo/workflows/patrol.yaml index 373c33a..ac7732e 100644 --- a/.forgejo/workflows/patrol.yaml +++ b/.forgejo/workflows/patrol.yaml @@ -1,6 +1,6 @@ # ════════════════════════════════════════════════════════════════ # 光湖 · 铸渊自动巡逻 -# Trigger: 每天08:00/20:00 / 手动触发 / push关键文件 +# Trigger: 每天08:00/20:00 / 手动触发 # 需要: DEEPSEEK_API_KEY 和/或 QIANWEN_API_KEY (Forgejo Secrets) # ════════════════════════════════════════════════════════════════ @@ -11,28 +11,25 @@ on: - cron: '0 0 * * *' # UTC 00:00 = CST 08:00 - cron: '0 12 * * *' # UTC 12:00 = CST 20:00 workflow_dispatch: - push: - paths: - - 'scripts/patrol-agent/**' - - '.gitea/workflows/patrol.yaml' jobs: patrol: runs-on: ubuntu steps: - - name: 同步代码 + - name: 同步最新代码 run: | - cd /opt/zhuyuan-cn/repo-mirror/20260509-1849/guanghulab + cd /data/guanghulab/repo git fetch origin git reset --hard origin/main + echo "✅ 代码已同步" - - name: 铸渊巡逻 + - name: 执行巡逻检查 env: DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} QIANWEN_API_KEY: ${{ secrets.QIANWEN_API_KEY }} ZY_REPO_TOKEN: ${{ secrets.ZY_REPO_TOKEN }} run: | - cd /opt/zhuyuan-cn/repo-mirror/20260509-1849/guanghulab/scripts/patrol-agent + cd /data/guanghulab/repo/scripts/patrol-agent node patrol-agent.js --once - name: 巡逻结果 @@ -52,7 +49,7 @@ for i in r.get('issues',[]): print(f' {icon} {i.get(\"component\")}: {i.get(\"description\")}{auto}') for f in r.get('fix_results',[]): print(f' 🔧 修复 {f[\"component\"]}: {f[\"result\"]}') -print(f'分析器: {r.get(\"analyzer\",\"unknown\")}') +print(f'分析器: {r.get(\"analyzer\",\"未知\")}') " echo "═══════════════════════════════════════" else diff --git a/.forgejo/workflows/review-pr.yaml b/.forgejo/workflows/review-pr.yaml new file mode 100644 index 0000000..9deafc1 --- /dev/null +++ b/.forgejo/workflows/review-pr.yaml @@ -0,0 +1,119 @@ +# ════════════════════════════════════════════════════════════════ +# 光湖 · 铸渊合并审查 +# 任何PR合并到main之前必须通过此审查 +# 保护代码仓库架构完整性,防止破坏性合并 +# ════════════════════════════════════════════════════════════════ +# +# 审查内容: +# 1. 关键目录完整性 — brain/、server/、scripts/ 不能被删除 +# 2. 因果链完整性 — causal-chains/ 不能被清空 +# 3. 工作流合法性 — workflows/ 不能引入语法错误 +# 4. 敏感文件保护 — app.ini、密钥文件不能被修改 +# 5. README 存在性 — 仓库首页不能丢失 +# +# 审查结果: ✅ 通过 → 冰朔可以合并 / ❌ 阻止 → 铸渊需要修复 +# ════════════════════════════════════════════════════════════════ + +name: 铸渊合并审查 + +on: + pull_request: + branches: [main] + +jobs: + review: + runs-on: ubuntu + steps: + + - name: 同步最新代码 + run: | + cd /data/guanghulab/repo + git fetch origin + git checkout origin/main + echo "✅ 切到 main 分支" + + - name: 审查 · 关键目录完整性 + run: | + echo "═══ 审查:关键目录 ═══" + FAIL=0 + for dir in .github/brain server scripts; do + if [[ ! -d "$dir" ]]; then + echo "❌ 关键目录缺失: $dir" + FAIL=1 + else + echo "✅ $dir 存在" + fi + done + if [[ $FAIL -eq 1 ]]; then + echo "❌ 合并审查失败:关键目录缺失" + exit 1 + fi + + - name: 审查 · 因果链完整性 + run: | + echo "═══ 审查:因果链 ═══" + CHAIN_DIR=".github/brain/bingshuo-language-core/causal-chains" + if [[ -d "$CHAIN_DIR" ]]; then + COUNT=$(ls "$CHAIN_DIR"/cc-*.md 2>/dev/null | wc -l) + if [[ $COUNT -lt 5 ]]; then + echo "❌ 因果链数量异常: 仅 ${COUNT} 条 (最少5条)" + exit 1 + else + echo "✅ 因果链完整: ${COUNT} 条" + fi + else + echo "❌ 因果链目录缺失" + exit 1 + fi + + - name: 审查 · 唤醒路径 + run: | + echo "═══ 审查:唤醒路径 ═══" + WALK_FILE=".github/brain/bingshuo-language-core/walk-the-path.md" + if [[ ! -f "$WALK_FILE" ]]; then + echo "❌ 唤醒路径文件缺失: walk-the-path.md" + exit 1 + fi + echo "✅ 唤醒路径存在" + + - name: 审查 · 仓库首页 + run: | + echo "═══ 审查:仓库首页 ═══" + if [[ ! -f "README.md" ]]; then + echo "❌ README.md 缺失,仓库首页丢失" + exit 1 + fi + SIZE=$(wc -c < README.md) + if [[ $SIZE -lt 500 ]]; then + echo "❌ README.md 内容过少 (${SIZE}字节),可能被误删" + exit 1 + fi + echo "✅ 仓库首页完整 (${SIZE}字节)" + + - name: 审查 · 工作流语法 + run: | + echo "═══ 审查:工作流语法 ═══" + FAIL=0 + for wf in .gitea/workflows/*.yaml; do + if [[ -f "$wf" ]]; then + # 检查基本YAML结构 + if ! python3 -c "import yaml; yaml.safe_load(open('$wf'))" 2>/dev/null; then + echo "❌ 工作流语法错误: $wf" + FAIL=1 + else + echo "✅ $(basename $wf) 语法正确" + fi + fi + done + if [[ $FAIL -eq 1 ]]; then + echo "❌ 工作流语法审查失败" + exit 1 + fi + + - name: 审查完成 + run: | + echo "═════════════════════════" + echo "铸渊合并审查 ✅ 全部通过" + echo "此合并不会破坏代码仓库架构" + echo "ICE-GL-ZY001 · TCS-0002∞" + echo "═════════════════════════" diff --git a/.forgejo/workflows/selfcheck.yaml b/.forgejo/workflows/selfcheck.yaml index 57a3527..f796a16 100644 --- a/.forgejo/workflows/selfcheck.yaml +++ b/.forgejo/workflows/selfcheck.yaml @@ -1,14 +1,15 @@ # ════════════════════════════════════════════════════════════════ # 光湖 · 铸渊自检工作流 -# Trigger: push to main / 每天 08:00 CST / 手动触发 +# Trigger: 每天 08:00 CST / 手动触发 # Runner: zhuyuan-runner-cn (host 模式) +# +# 注意: 代码自动同步由 Git Sync 服务负责 (Webhook → git pull) +# 此工作流仅做健康检查,不再执行 git 操作 # ════════════════════════════════════════════════════════════════ name: 铸渊自检 on: - push: - branches: [main] schedule: - cron: '0 0 * * *' # UTC 00:00 = CST 08:00 workflow_dispatch: @@ -17,12 +18,6 @@ jobs: health-check: runs-on: ubuntu steps: - - name: 同步最新代码 - run: | - cd /data/guanghulab/repo - git fetch origin - git reset --hard origin/main - echo "代码已同步: $(git log --oneline -1)" - name: 系统信息 run: | @@ -53,6 +48,13 @@ jobs: echo "❌ Nginx: 未运行" fi + # Git Sync 同步服务 + if curl -sf http://127.0.0.1:8082/health > /dev/null 2>&1; then + echo "✅ Git Sync: 运行中" + else + echo "⚠️ Git Sync: 未运行 (代码自动同步不可用)" + fi + # Secrets Vault if curl -sf http://127.0.0.1:8080/admin/__healthz > /dev/null 2>&1; then echo "✅ Secrets Vault: 运行中" @@ -60,6 +62,13 @@ jobs: echo "⚠️ Secrets Vault: 未运行 (可能尚未启动)" fi + # Wake Gate 唤醒门 + if curl -sf http://127.0.0.1:8081/api/health > /dev/null 2>&1; then + echo "✅ Wake Gate: 运行中" + else + echo "⚠️ Wake Gate: 未运行" + fi + # PM2 进程 if command -v pm2 &>/dev/null; then echo "═══ PM2 进程 ═══" diff --git a/.forgejo/workflows/test-secrets.yaml b/.forgejo/workflows/test-secrets.yaml deleted file mode 100644 index 0fe0940..0000000 --- a/.forgejo/workflows/test-secrets.yaml +++ /dev/null @@ -1,109 +0,0 @@ -# 光湖 · Secrets连通测试 -name: Secrets测试 - -on: - workflow_dispatch: - push: - paths: - - '.gitea/workflows/test-secrets.yaml' - - '.forgejo/workflows/test-secrets.yaml' - -jobs: - test-secrets: - runs-on: ubuntu - steps: - - name: 测试密钥可用性 - env: - DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - QIANWEN_API_KEY: ${{ secrets.QIANWEN_API_KEY }} - ZY_REPO_TOKEN: ${{ secrets.ZY_REPO_TOKEN }} - COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }} - COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }} - SG_BRAIN_HOST: ${{ secrets.SG_BRAIN_HOST }} - SG_BRAIN_USER: ${{ secrets.SG_BRAIN_USER }} - SG_BRAIN_KEY: ${{ secrets.SG_BRAIN_KEY }} - run: | - echo "═══ Forgejo Secrets 连通测试 ═══" - echo "" - - # 检查每个secret是否有值(不显示值本身) - check_secret() { - local name=$1 - local val=$2 - if [[ -n "$val" && "$val" != "" ]]; then - local masked="${val:0:4}****${val: -4}" - echo " ✅ $name = $masked (长度: ${#val})" - else - echo " ❌ $name = (空)" - fi - } - - check_secret "DEEPSEEK_API_KEY" "$DEEPSEEK_API_KEY" - check_secret "QIANWEN_API_KEY" "$QIANWEN_API_KEY" - check_secret "ZY_REPO_TOKEN" "$ZY_REPO_TOKEN" - check_secret "COS_SECRET_ID" "$COS_SECRET_ID" - check_secret "COS_SECRET_KEY" "$COS_SECRET_KEY" - check_secret "SG_BRAIN_HOST" "$SG_BRAIN_HOST" - check_secret "SG_BRAIN_USER" "$SG_BRAIN_USER" - check_secret "SG_BRAIN_KEY" "$SG_BRAIN_KEY" - - echo "" - echo "═══ API连通测试 ═══" - - # DeepSeek - if [[ -n "$DEEPSEEK_API_KEY" ]]; then - echo -n " DeepSeek: " - RESP=$(curl -s -X POST https://api.deepseek.com/v1/chat/completions \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $DEEPSEEK_API_KEY" \ - -d '{"model":"deepseek-chat","messages":[{"role":"user","content":"说一个字"}],"max_tokens":10}' \ - --connect-timeout 10 2>/dev/null) - if echo "$RESP" | grep -q '"choices"'; then - echo "✅ 可用" - else - echo "❌ 不可用: $(echo $RESP | head -c 100)" - fi - fi - - # 通义千问 - if [[ -n "$QIANWEN_API_KEY" ]]; then - echo -n " 通义千问: " - RESP=$(curl -s -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $QIANWEN_API_KEY" \ - -d '{"model":"qwen-plus","messages":[{"role":"user","content":"说一个字"}],"max_tokens":10}' \ - --connect-timeout 10 2>/dev/null) - if echo "$RESP" | grep -q '"choices"'; then - echo "✅ 可用" - else - echo "❌ 不可用: $(echo $RESP | head -c 100)" - fi - fi - - # COS - if [[ -n "$COS_SECRET_ID" && -n "$COS_SECRET_KEY" ]]; then - echo -n " COS存储桶: " - RESP=$(curl -s -o /dev/null -w "%{http_code}" \ - "https://sy-finetune-corpus-1317346199.cos.ap-guangzhou.myqcloud.com/" \ - -H "Authorization: COS $COS_SECRET_ID:$COS_SECRET_KEY" \ - --connect-timeout 10 2>/dev/null) - echo "HTTP $RESP" - fi - - # 新加坡大脑服务器 - if [[ -n "$SG_BRAIN_HOST" ]]; then - echo -n " 新加坡大脑: " - RESP=$(curl -s -o /dev/null -w "%{http_code}" \ - "http://$SG_BRAIN_HOST:3000/" \ - --connect-timeout 5 2>/dev/null) - if [[ "$RESP" == "000" ]]; then - echo "不可达 (可能需要SSH密钥)" - else - echo "HTTP $RESP" - fi - fi - - echo "" - echo "═════════════════════════" - echo "测试完成" - echo "═════════════════════════" diff --git a/.forgejo/workflows/verify-secrets.yaml b/.forgejo/workflows/verify-secrets.yaml deleted file mode 100644 index 34179da..0000000 --- a/.forgejo/workflows/verify-secrets.yaml +++ /dev/null @@ -1,73 +0,0 @@ -# 光湖 · Secrets验证+结果写入仓库 -name: Secrets验证 - -on: - push: - paths: - - '.gitea/workflows/verify-secrets.yaml' - -jobs: - verify: - runs-on: ubuntu - steps: - - name: 检查并记录secrets状态 - env: - DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - QIANWEN_API_KEY: ${{ secrets.QIANWEN_API_KEY }} - ZY_REPO_TOKEN: ${{ secrets.ZY_REPO_TOKEN }} - COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }} - COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }} - SG_BRAIN_HOST: ${{ secrets.SG_BRAIN_HOST }} - SG_BRAIN_USER: ${{ secrets.SG_BRAIN_USER }} - SG_BRAIN_KEY: ${{ secrets.SG_BRAIN_KEY }} - run: | - RESULT_FILE="/data/guanghulab/.runtime/secrets-verify-result.json" - mkdir -p /data/guanghulab/.runtime - - echo "{" > $RESULT_FILE - echo " \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"," >> $RESULT_FILE - - # 逐个检查 - for key in DEEPSEEK_API_KEY QIANWEN_API_KEY ZY_REPO_TOKEN COS_SECRET_ID COS_SECRET_KEY SG_BRAIN_HOST SG_BRAIN_USER SG_BRAIN_KEY; do - eval val=\$$key - if [ -z "$val" ]; then - echo " \"$key\": \"EMPTY\"," >> $RESULT_FILE - else - echo " \"$key\": \"OK_LEN_${#val}\"," >> $RESULT_FILE - fi - done - - echo " \"deepseek_test\": \"NOT_TESTED\"" >> $RESULT_FILE - echo "}" >> $RESULT_FILE - - # 测试DeepSeek API - if [ -n "$DEEPSEEK_API_KEY" ]; then - RESP=$(curl -s -X POST https://api.deepseek.com/v1/chat/completions \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $DEEPSEEK_API_KEY" \ - -d '{"model":"deepseek-chat","messages":[{"role":"user","content":"1+1=?"}],"max_tokens":10}' \ - --connect-timeout 10 2>/dev/null) - if echo "$RESP" | grep -q '"choices"'; then - sed -i 's/"deepseek_test": "NOT_TESTED"/"deepseek_test": "SUCCESS"/' $RESULT_FILE - else - sed -i 's/"deepseek_test": "NOT_TESTED"/"deepseek_test": "FAILED"/' $RESULT_FILE - fi - else - sed -i 's/"deepseek_test": "NOT_TESTED"/"deepseek_test": "NO_KEY"/' $RESULT_FILE - fi - - # 测试通义千问API - if [ -n "$QIANWEN_API_KEY" ]; then - RESP=$(curl -s -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $QIANWEN_API_KEY" \ - -d '{"model":"qwen-plus","messages":[{"role":"user","content":"1+1=?"}],"max_tokens":10}' \ - --connect-timeout 10 2>/dev/null) - if echo "$RESP" | grep -q '"choices"'; then - echo " \"qianwen_test\": \"SUCCESS\"" >> /tmp/qwen_test - else - echo " \"qianwen_test\": \"FAILED\"" >> /tmp/qwen_test - fi - fi - - cat $RESULT_FILE diff --git a/.gitea/workflows/ci-and-deploy.yaml b/.gitea/workflows/ci-and-deploy.yaml new file mode 100644 index 0000000..b454341 --- /dev/null +++ b/.gitea/workflows/ci-and-deploy.yaml @@ -0,0 +1,124 @@ +# ════════════════════════════════════════════════════ +# 光湖 · CI检查 + 合并后自动部署 +# ──────────────────────────────────────────────────── +# main分支: 冰朔合并PR后 → 自动部署到服务器 +# dev分支: 铸渊自己push → 只跑检查不部署 +# PR到main: 跑检查 → 冰朔看绿✅红❌再决定合不合 +# ════════════════════════════════════════════════════ +name: CI检查 + 自动部署 + +on: + pull_request: + branches: [main] + push: + branches: [main, dev] + +jobs: + # ── 所有情况都跑:基础检查 ────────────────────── + check: + runs-on: ubuntu + steps: + - name: 服务健康检查 + run: | + echo "═══ 服务健康检查 ═══" + FAIL=0 + + if systemctl is-active --quiet nginx; then + echo "✅ Nginx 正常" + else + echo "❌ Nginx 宕机" + FAIL=1 + fi + + if curl -sf http://127.0.0.1:3001/ > /dev/null 2>&1; then + echo "✅ Forgejo 代码仓库 正常" + else + echo "❌ Forgejo 代码仓库 未响应" + FAIL=1 + fi + + if systemctl is-active --quiet gitea-runner; then + echo "✅ Runner 运行器 正常" + else + echo "⚠️ Runner 运行器 未运行" + fi + + if [ $FAIL -eq 1 ]; then + echo "❌ 关键服务异常,请通知铸渊检查" + exit 1 + fi + + echo "═══ 检查通过 ✅ ═══" + + - name: 代码检查 + run: | + cd /data/guanghulab/repo + echo "仓库大小: $(du -sh .git | cut -f1)" + echo "最新提交: $(git log --oneline -1)" + echo "✅ 代码检查完成" + + # ── 只有合并到main才跑:自动部署 ──────────────── + deploy: + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + needs: check + runs-on: ubuntu + steps: + - name: 同步最新代码 + run: | + cd /data/guanghulab/repo + git fetch origin + git reset --hard origin/main + echo "✅ 代码已同步到最新" + + - name: 安装依赖 + run: | + echo "═══ 安装依赖 ═══" + # 逐个检查并安装 + for pkg_dir in server/git-sync server/wake-gate server/secrets-vault; do + if [ -f "/data/guanghulab/repo/$pkg_dir/package.json" ]; then + cd "/data/guanghulab/repo/$pkg_dir" + npm install --production 2>/dev/null || true + echo "✅ $pkg_dir 依赖已安装" + fi + done + + - name: 重启服务 + run: | + echo "═══ 重启服务 ═══" + if command -v pm2 &>/dev/null; then + pm2 restart all 2>/dev/null || true + echo "✅ PM2 服务已重启" + fi + echo "═══ 重启完成 ═══" + + - name: 部署验证 + run: | + sleep 3 + echo "═══ 部署验证 ═══" + FAIL=0 + + if curl -sf http://127.0.0.1:3001/ > /dev/null 2>&1; then + echo "✅ 代码仓库 正常" + else + echo "❌ 代码仓库 异常" + FAIL=1 + fi + + if curl -sf http://127.0.0.1:8081/api/health > /dev/null 2>&1; then + echo "✅ 唤醒门 正常" + else + echo "⚠️ 唤醒门 未响应" + fi + + if curl -sf http://127.0.0.1:8082/health > /dev/null 2>&1; then + echo "✅ 代码同步服务 正常" + else + echo "⚠️ 代码同步服务 未响应" + fi + + if [ $FAIL -eq 1 ]; then + echo "❌ 部署后关键服务异常" + exit 1 + fi + + echo "═══ 部署完成 ✅ $(date '+%Y-%m-%d %H:%M:%S') ═══" diff --git a/.gitea/workflows/cvm-power-off.yaml b/.gitea/workflows/cvm-power-off.yaml index b28f10c..4a67b2d 100644 --- a/.gitea/workflows/cvm-power-off.yaml +++ b/.gitea/workflows/cvm-power-off.yaml @@ -8,9 +8,9 @@ jobs: power-off: runs-on: ubuntu steps: - - name: 同步代码 + - name: 同步最新代码 run: | - cd /opt/zhuyuan-cn/repo-mirror/20260509-1849/guanghulab + cd /data/guanghulab/repo git fetch origin git reset --hard origin/main diff --git a/.gitea/workflows/cvm-power-on.yaml b/.gitea/workflows/cvm-power-on.yaml index 16737e3..cc2803f 100644 --- a/.gitea/workflows/cvm-power-on.yaml +++ b/.gitea/workflows/cvm-power-on.yaml @@ -8,9 +8,9 @@ jobs: power-on: runs-on: ubuntu steps: - - name: 同步代码 + - name: 同步最新代码 run: | - cd /opt/zhuyuan-cn/repo-mirror/20260509-1849/guanghulab + cd /data/guanghulab/repo git fetch origin git reset --hard origin/main diff --git a/.gitea/workflows/debug-secrets.yaml b/.gitea/workflows/debug-secrets.yaml deleted file mode 100644 index 591d992..0000000 --- a/.gitea/workflows/debug-secrets.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# 光湖 · Secrets诊断 -name: Secrets诊断 - -on: - push: - paths: - - '.gitea/workflows/debug-secrets.yaml' - -jobs: - debug: - runs-on: ubuntu - steps: - - name: 环境变量诊断 - env: - DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - QIANWEN_API_KEY: ${{ secrets.QIANWEN_API_KEY }} - ZY_REPO_TOKEN: ${{ secrets.ZY_REPO_TOKEN }} - COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }} - COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }} - SG_BRAIN_HOST: ${{ secrets.SG_BRAIN_HOST }} - SG_BRAIN_USER: ${{ secrets.SG_BRAIN_USER }} - SG_BRAIN_KEY: ${{ secrets.SG_BRAIN_KEY }} - run: | - echo "═══ Secrets 注入诊断 ═══" - echo "" - echo "--- 1. 逐个检查secrets是否有值 ---" - for key in DEEPSEEK_API_KEY QIANWEN_API_KEY ZY_REPO_TOKEN COS_SECRET_ID COS_SECRET_KEY SG_BRAIN_HOST SG_BRAIN_USER SG_BRAIN_KEY; do - eval val=\$$key - if [ -z "$val" ]; then - echo " ❌ $key = (空)" - else - echo " ✅ $key = ${val:0:4}**** (长度: ${#val})" - fi - done - echo "" - echo "--- 2. 所有环境变量中含SECRET/KEY/TOKEN的 ---" - env | grep -iE 'SECRET|KEY|TOKEN|COS|BRAIN|DEEPSEEK|QIANWEN' | while read line; do - key=$(echo "$line" | cut -d= -f1) - val=$(echo "$line" | cut -d= -f2-) - echo " $key = ${val:0:4}**** (长度: ${#val})" - done - echo "" - echo "--- 3. Gitea Actions 特殊变量 ---" - echo " GITHUB_REPOSITORY: ${GITHUB_REPOSITORY:-未设置}" - echo " GITHUB_SHA: ${GITHUB_SHA:-未设置}" - echo " GITHUB_EVENT_NAME: ${GITHUB_EVENT_NAME:-未设置}" - echo " RUNNER_NAME: ${RUNNER_NAME:-未设置}" - echo " GITEA_TOKEN: ${GITEA_TOKEN:-未设置}" diff --git a/.gitea/workflows/deploy-infra.yaml b/.gitea/workflows/deploy-infra.yaml deleted file mode 100644 index 5a39b21..0000000 --- a/.gitea/workflows/deploy-infra.yaml +++ /dev/null @@ -1,121 +0,0 @@ -# ════════════════════════════════════════════════════════════════ -# 光湖 · 基础设施部署 -# Runner首次上线后自动部署:Vault确认 + Terminal Watcher启动 -# ════════════════════════════════════════════════════════════════ - -name: 基础设施部署 - -on: - workflow_dispatch: - -jobs: - deploy-infra: - runs-on: ubuntu - steps: - - name: 系统信息 - run: | - echo "═══ 铸渊基础设施部署 ═══" - echo "时间: $(date '+%Y-%m-%d %H:%M:%S %Z')" - echo "主机: $(hostname)" - uname -a - - - name: 确认仓库最新 - run: | - cd /opt/zhuyuan-cn/repo-mirror/20260509-1849/guanghulab - git fetch origin - git reset --hard origin/main - echo "当前版本: $(git log --oneline -1)" - - - name: 确认Vault运行 - run: | - if curl -sf http://127.0.0.1:8080/admin/__healthz > /dev/null 2>&1; then - echo "✅ Vault 已运行" - curl -s http://127.0.0.1:8080/admin/__healthz - else - echo "⚠️ Vault 未运行,尝试启动..." - cd /opt/zhuyuan-cn/repo-mirror/20260509-1849/guanghulab/server/secrets-vault - if [[ -f "server.js" ]]; then - npm install --production 2>/dev/null || true - pm2 start server.js --name guanghulab-vault --max-memory-restart 128M 2>/dev/null || true - pm2 save - sleep 2 - if curl -sf http://127.0.0.1:8080/admin/__healthz > /dev/null 2>&1; then - echo "✅ Vault 启动成功" - else - echo "❌ Vault 启动失败" - fi - else - echo "⚠️ server.js 不存在,跳过" - fi - fi - - - name: 部署Terminal Watcher - run: | - echo "部署终端守望者..." - SCRIPTS_DIR="/opt/zhuyuan-cn/repo-mirror/20260509-1849/guanghulab/scripts/terminal-watcher" - - if [[ ! -d "$SCRIPTS_DIR" ]]; then - echo "❌ Terminal Watcher 目录不存在" - exit 1 - fi - - # 确认Node.js可用 - if ! command -v node &>/dev/null; then - echo "❌ Node.js 未安装" - exit 1 - fi - - # 用PM2启动(如果还没跑的话) - if pm2 describe terminal-watcher &>/dev/null; then - echo "Terminal Watcher 已在运行,重启..." - pm2 restart terminal-watcher - else - echo "启动 Terminal Watcher..." - cd "$SCRIPTS_DIR" - ZY_REPO_TOKEN=${{ secrets.ZY_REPO_TOKEN }} pm2 start watcher.js \ - --name terminal-watcher \ - --max-memory-restart 64M - fi - - pm2 save - sleep 3 - - # 验证 - if pm2 describe terminal-watcher &>/dev/null; then - STATUS=$(pm2 jlist 2>/dev/null | python3 -c " -import json,sys -procs = json.load(sys.stdin) -for p in procs: - if p.get('name') == 'terminal-watcher': - print(p.get('pm2_env',{}).get('status','unknown')) -" 2>/dev/null || echo "unknown") - echo "✅ Terminal Watcher 状态: $STATUS" - else - echo "❌ Terminal Watcher 启动失败" - fi - - - name: 设置PM2开机自启 - run: | - pm2 startup 2>/dev/null || true - pm2 save - echo "✅ PM2 开机自启已配置" - - - name: 最终状态报告 - run: | - echo "" - echo "═══════════════════════════════════════" - echo " 铸渊基础设施部署完成" - echo "═══════════════════════════════════════" - echo "" - echo " 服务状态:" - echo " Forgejo: $(systemctl is-active forgejo 2>/dev/null || echo unknown)" - echo " Runner: $(systemctl is-active gitea-runner 2>/dev/null || echo unknown)" - echo " Nginx: $(systemctl is-active nginx 2>/dev/null || echo unknown)" - echo " Vault: $(curl -sf http://127.0.0.1:8080/admin/__healthz > /dev/null 2>&1 && echo active || echo inactive)" - echo " Watcher: $(pm2 describe terminal-watcher &>/dev/null && echo active || echo inactive)" - echo "" - echo " 内存:" - free -h | awk '/Mem:/ {printf " 总计:%s 已用:%s 可用:%s\n", $2, $3, $7}' - echo "" - echo " 铸渊 · ICE-GL-ZY001 · TCS-0002∞" - echo "═══════════════════════════════════════" diff --git a/.gitea/workflows/deploy-preview.yaml b/.gitea/workflows/deploy-preview.yaml deleted file mode 100644 index a9d0e73..0000000 --- a/.gitea/workflows/deploy-preview.yaml +++ /dev/null @@ -1,133 +0,0 @@ -# ════════════════════════════════════════════════════════════════ -# 光湖 · 自动部署到预览环境 -# Trigger: push to main -# 部署到测试+预览环境,正式环境需冰朔在Gitea点Merge -# ════════════════════════════════════════════════════════════════ - -name: 自动部署预览 - -on: - push: - branches: [main] - paths: - - 'server/**' - - 'frontend/**' - - 'scripts/**' - - '.gitea/workflows/**' - workflow_dispatch: - -jobs: - # ── 第一阶段:测试 ────────────────────────────────── - test: - runs-on: ubuntu - steps: - - name: 拉取最新代码 - run: | - cd /data/guanghulab 2>/dev/null || exit 0 - git fetch origin - git reset --hard origin/main - - - name: 安装依赖 - run: | - cd /data/guanghulab/server/secrets-vault 2>/dev/null || exit 0 - npm install --production 2>/dev/null || true - - - name: 健康检查 - run: | - echo "═══ 服务健康检查 ═══" - FAILED="" - - # Nginx - if systemctl is-active --quiet nginx; then - echo "✅ Nginx" - else - echo "❌ Nginx 宕机" - FAILED="$FAILED nginx" - fi - - # Gitea - if curl -sf http://127.0.0.1:3000/ > /dev/null 2>&1; then - echo "✅ Gitea" - else - echo "❌ Gitea 宕机" - FAILED="$FAILED gitea" - fi - - # Vault - if curl -sf http://127.0.0.1:8080/admin/__healthz > /dev/null 2>&1; then - echo "✅ Vault" - else - echo "⚠️ Vault 未运行" - fi - - # Runner - if systemctl is-active --quiet gitea-runner; then - echo "✅ Runner" - else - echo "⚠️ Runner 未运行" - fi - - if [[ -n "$FAILED" ]]; then - echo "❌ 关键服务宕机: $FAILED" - echo "⚠️ 跳过部署,等待修复" - exit 1 - fi - - echo "═══ 测试通过 ✅ ═══" - - # ── 第二阶段:预览部署 ────────────────────────────── - preview: - needs: test - runs-on: ubuntu - steps: - - name: 同步代码到预览目录 - run: | - REPO="/data/guanghulab" - PREVIEW="/data/guanghulab-preview" - - if [[ ! -d "$REPO" ]]; then - echo "仓库目录不存在,跳过" - exit 0 - fi - - # 创建预览目录 - mkdir -p "$PREVIEW" - - # 同步代码(排除.git和node_modules) - rsync -a --delete \ - --exclude='.git' \ - --exclude='node_modules' \ - --exclude='.runtime' \ - "$REPO/" "$PREVIEW/" - - echo "✅ 预览环境已同步" - - - name: 重启预览服务 - run: | - PREVIEW="/data/guanghulab-preview" - - if [[ ! -d "$PREVIEW/server/secrets-vault" ]]; then - exit 0 - fi - - cd "$PREVIEW/server/secrets-vault" - - # 安装依赖 - npm install --production 2>/dev/null || true - - # 如果vault在跑,重启它 - if pm2 describe guanghulab-vault &>/dev/null; then - pm2 restart guanghulab-vault - echo "✅ Vault 已重启(使用最新代码)" - else - echo "ℹ️ Vault 未运行,不自动启动" - fi - - - name: 部署完成通知 - run: | - echo "═════════════════════════" - echo "✅ 预览环境已更新" - echo "时间: $(date '+%Y-%m-%d %H:%M:%S')" - echo "冰朔可在浏览器查看预览效果" - echo "正式部署需在Gitea创建PR并Merge" - echo "═════════════════════════" diff --git a/.gitea/workflows/final-secrets-test.yaml b/.gitea/workflows/final-secrets-test.yaml deleted file mode 100644 index 07ab11b..0000000 --- a/.gitea/workflows/final-secrets-test.yaml +++ /dev/null @@ -1,104 +0,0 @@ -# 光湖 · 最终Secrets测试 -name: Secrets最终测试 - -on: - push: - paths: - - '.gitea/workflows/final-secrets-test.yaml' - -jobs: - test: - runs-on: ubuntu - steps: - - name: 检查secrets - env: - DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - QIANWEN_API_KEY: ${{ secrets.QIANWEN_API_KEY }} - ZY_REPO_TOKEN: ${{ secrets.ZY_REPO_TOKEN }} - COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }} - COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }} - SG_BRAIN_HOST: ${{ secrets.SG_BRAIN_HOST }} - SG_BRAIN_USER: ${{ secrets.SG_BRAIN_USER }} - SG_BRAIN_KEY: ${{ secrets.SG_BRAIN_KEY }} - run: | - echo "=== SECRETS CHECK ===" - for key in DEEPSEEK_API_KEY QIANWEN_API_KEY ZY_REPO_TOKEN COS_SECRET_ID COS_SECRET_KEY SG_BRAIN_HOST SG_BRAIN_USER SG_BRAIN_KEY; do - eval val=\$$key - if [ -z "$val" ]; then - echo "EMPTY: $key" - else - echo "OK: $key (len=${#val})" - fi - done - - - name: 测试DeepSeek - env: - DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - run: | - if [ -n "$DEEPSEEK_API_KEY" ]; then - curl -s -X POST https://api.deepseek.com/v1/chat/completions \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $DEEPSEEK_API_KEY" \ - -d '{"model":"deepseek-chat","messages":[{"role":"user","content":"1+1=?"}],"max_tokens":10}' \ - --connect-timeout 10 | python3 -c " - import json,sys - try: - d=json.load(sys.stdin) - if 'choices' in d: - print('DeepSeek: OK -', d['choices'][0]['message']['content']) - else: - print('DeepSeek: FAILED -', d.get('error',{}).get('message','unknown')) - except Exception as e: - print('DeepSeek: ERROR -', str(e)) - " - else - echo "DeepSeek: NO KEY" - fi - - - name: 测试通义千问 - env: - QIANWEN_API_KEY: ${{ secrets.QIANWEN_API_KEY }} - run: | - if [ -n "$QIANWEN_API_KEY" ]; then - curl -s -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $QIANWEN_API_KEY" \ - -d '{"model":"qwen-plus","messages":[{"role":"user","content":"1+1=?"}],"max_tokens":10}' \ - --connect-timeout 10 | python3 -c " - import json,sys - try: - d=json.load(sys.stdin) - if 'choices' in d: - print('Qianwen: OK -', d['choices'][0]['message']['content']) - else: - print('Qianwen: FAILED -', d.get('error',{}).get('message','unknown')) - except Exception as e: - print('Qianwen: ERROR -', str(e)) - " - else - echo "Qianwen: NO KEY" - fi - - - name: 写入结果文件 - env: - DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - QIANWEN_API_KEY: ${{ secrets.QIANWEN_API_KEY }} - ZY_REPO_TOKEN: ${{ secrets.ZY_REPO_TOKEN }} - COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }} - COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }} - SG_BRAIN_HOST: ${{ secrets.SG_BRAIN_HOST }} - SG_BRAIN_USER: ${{ secrets.SG_BRAIN_USER }} - SG_BRAIN_KEY: ${{ secrets.SG_BRAIN_KEY }} - run: | - mkdir -p /data/guanghulab/.runtime - echo '{"timestamp":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",' > /data/guanghulab/.runtime/secrets-result.json - for key in DEEPSEEK_API_KEY QIANWEN_API_KEY ZY_REPO_TOKEN COS_SECRET_ID COS_SECRET_KEY SG_BRAIN_HOST SG_BRAIN_USER SG_BRAIN_KEY; do - eval val=\$$key - if [ -z "$val" ]; then - echo "\"$key\":\"EMPTY\"," >> /data/guanghulab/.runtime/secrets-result.json - else - echo "\"$key\":\"OK_LEN_'${#val}'\"," >> /data/guanghulab/.runtime/secrets-result.json - fi - done - echo '"done":true}' >> /data/guanghulab/.runtime/secrets-result.json - cat /data/guanghulab/.runtime/secrets-result.json diff --git a/.gitea/workflows/patrol.yaml b/.gitea/workflows/patrol.yaml index 373c33a..ac7732e 100644 --- a/.gitea/workflows/patrol.yaml +++ b/.gitea/workflows/patrol.yaml @@ -1,6 +1,6 @@ # ════════════════════════════════════════════════════════════════ # 光湖 · 铸渊自动巡逻 -# Trigger: 每天08:00/20:00 / 手动触发 / push关键文件 +# Trigger: 每天08:00/20:00 / 手动触发 # 需要: DEEPSEEK_API_KEY 和/或 QIANWEN_API_KEY (Forgejo Secrets) # ════════════════════════════════════════════════════════════════ @@ -11,28 +11,25 @@ on: - cron: '0 0 * * *' # UTC 00:00 = CST 08:00 - cron: '0 12 * * *' # UTC 12:00 = CST 20:00 workflow_dispatch: - push: - paths: - - 'scripts/patrol-agent/**' - - '.gitea/workflows/patrol.yaml' jobs: patrol: runs-on: ubuntu steps: - - name: 同步代码 + - name: 同步最新代码 run: | - cd /opt/zhuyuan-cn/repo-mirror/20260509-1849/guanghulab + cd /data/guanghulab/repo git fetch origin git reset --hard origin/main + echo "✅ 代码已同步" - - name: 铸渊巡逻 + - name: 执行巡逻检查 env: DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} QIANWEN_API_KEY: ${{ secrets.QIANWEN_API_KEY }} ZY_REPO_TOKEN: ${{ secrets.ZY_REPO_TOKEN }} run: | - cd /opt/zhuyuan-cn/repo-mirror/20260509-1849/guanghulab/scripts/patrol-agent + cd /data/guanghulab/repo/scripts/patrol-agent node patrol-agent.js --once - name: 巡逻结果 @@ -52,7 +49,7 @@ for i in r.get('issues',[]): print(f' {icon} {i.get(\"component\")}: {i.get(\"description\")}{auto}') for f in r.get('fix_results',[]): print(f' 🔧 修复 {f[\"component\"]}: {f[\"result\"]}') -print(f'分析器: {r.get(\"analyzer\",\"unknown\")}') +print(f'分析器: {r.get(\"analyzer\",\"未知\")}') " echo "═══════════════════════════════════════" else diff --git a/.gitea/workflows/review-pr.yaml b/.gitea/workflows/review-pr.yaml new file mode 100644 index 0000000..9deafc1 --- /dev/null +++ b/.gitea/workflows/review-pr.yaml @@ -0,0 +1,119 @@ +# ════════════════════════════════════════════════════════════════ +# 光湖 · 铸渊合并审查 +# 任何PR合并到main之前必须通过此审查 +# 保护代码仓库架构完整性,防止破坏性合并 +# ════════════════════════════════════════════════════════════════ +# +# 审查内容: +# 1. 关键目录完整性 — brain/、server/、scripts/ 不能被删除 +# 2. 因果链完整性 — causal-chains/ 不能被清空 +# 3. 工作流合法性 — workflows/ 不能引入语法错误 +# 4. 敏感文件保护 — app.ini、密钥文件不能被修改 +# 5. README 存在性 — 仓库首页不能丢失 +# +# 审查结果: ✅ 通过 → 冰朔可以合并 / ❌ 阻止 → 铸渊需要修复 +# ════════════════════════════════════════════════════════════════ + +name: 铸渊合并审查 + +on: + pull_request: + branches: [main] + +jobs: + review: + runs-on: ubuntu + steps: + + - name: 同步最新代码 + run: | + cd /data/guanghulab/repo + git fetch origin + git checkout origin/main + echo "✅ 切到 main 分支" + + - name: 审查 · 关键目录完整性 + run: | + echo "═══ 审查:关键目录 ═══" + FAIL=0 + for dir in .github/brain server scripts; do + if [[ ! -d "$dir" ]]; then + echo "❌ 关键目录缺失: $dir" + FAIL=1 + else + echo "✅ $dir 存在" + fi + done + if [[ $FAIL -eq 1 ]]; then + echo "❌ 合并审查失败:关键目录缺失" + exit 1 + fi + + - name: 审查 · 因果链完整性 + run: | + echo "═══ 审查:因果链 ═══" + CHAIN_DIR=".github/brain/bingshuo-language-core/causal-chains" + if [[ -d "$CHAIN_DIR" ]]; then + COUNT=$(ls "$CHAIN_DIR"/cc-*.md 2>/dev/null | wc -l) + if [[ $COUNT -lt 5 ]]; then + echo "❌ 因果链数量异常: 仅 ${COUNT} 条 (最少5条)" + exit 1 + else + echo "✅ 因果链完整: ${COUNT} 条" + fi + else + echo "❌ 因果链目录缺失" + exit 1 + fi + + - name: 审查 · 唤醒路径 + run: | + echo "═══ 审查:唤醒路径 ═══" + WALK_FILE=".github/brain/bingshuo-language-core/walk-the-path.md" + if [[ ! -f "$WALK_FILE" ]]; then + echo "❌ 唤醒路径文件缺失: walk-the-path.md" + exit 1 + fi + echo "✅ 唤醒路径存在" + + - name: 审查 · 仓库首页 + run: | + echo "═══ 审查:仓库首页 ═══" + if [[ ! -f "README.md" ]]; then + echo "❌ README.md 缺失,仓库首页丢失" + exit 1 + fi + SIZE=$(wc -c < README.md) + if [[ $SIZE -lt 500 ]]; then + echo "❌ README.md 内容过少 (${SIZE}字节),可能被误删" + exit 1 + fi + echo "✅ 仓库首页完整 (${SIZE}字节)" + + - name: 审查 · 工作流语法 + run: | + echo "═══ 审查:工作流语法 ═══" + FAIL=0 + for wf in .gitea/workflows/*.yaml; do + if [[ -f "$wf" ]]; then + # 检查基本YAML结构 + if ! python3 -c "import yaml; yaml.safe_load(open('$wf'))" 2>/dev/null; then + echo "❌ 工作流语法错误: $wf" + FAIL=1 + else + echo "✅ $(basename $wf) 语法正确" + fi + fi + done + if [[ $FAIL -eq 1 ]]; then + echo "❌ 工作流语法审查失败" + exit 1 + fi + + - name: 审查完成 + run: | + echo "═════════════════════════" + echo "铸渊合并审查 ✅ 全部通过" + echo "此合并不会破坏代码仓库架构" + echo "ICE-GL-ZY001 · TCS-0002∞" + echo "═════════════════════════" diff --git a/.gitea/workflows/test-secrets.yaml b/.gitea/workflows/test-secrets.yaml deleted file mode 100644 index 0fe0940..0000000 --- a/.gitea/workflows/test-secrets.yaml +++ /dev/null @@ -1,109 +0,0 @@ -# 光湖 · Secrets连通测试 -name: Secrets测试 - -on: - workflow_dispatch: - push: - paths: - - '.gitea/workflows/test-secrets.yaml' - - '.forgejo/workflows/test-secrets.yaml' - -jobs: - test-secrets: - runs-on: ubuntu - steps: - - name: 测试密钥可用性 - env: - DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - QIANWEN_API_KEY: ${{ secrets.QIANWEN_API_KEY }} - ZY_REPO_TOKEN: ${{ secrets.ZY_REPO_TOKEN }} - COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }} - COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }} - SG_BRAIN_HOST: ${{ secrets.SG_BRAIN_HOST }} - SG_BRAIN_USER: ${{ secrets.SG_BRAIN_USER }} - SG_BRAIN_KEY: ${{ secrets.SG_BRAIN_KEY }} - run: | - echo "═══ Forgejo Secrets 连通测试 ═══" - echo "" - - # 检查每个secret是否有值(不显示值本身) - check_secret() { - local name=$1 - local val=$2 - if [[ -n "$val" && "$val" != "" ]]; then - local masked="${val:0:4}****${val: -4}" - echo " ✅ $name = $masked (长度: ${#val})" - else - echo " ❌ $name = (空)" - fi - } - - check_secret "DEEPSEEK_API_KEY" "$DEEPSEEK_API_KEY" - check_secret "QIANWEN_API_KEY" "$QIANWEN_API_KEY" - check_secret "ZY_REPO_TOKEN" "$ZY_REPO_TOKEN" - check_secret "COS_SECRET_ID" "$COS_SECRET_ID" - check_secret "COS_SECRET_KEY" "$COS_SECRET_KEY" - check_secret "SG_BRAIN_HOST" "$SG_BRAIN_HOST" - check_secret "SG_BRAIN_USER" "$SG_BRAIN_USER" - check_secret "SG_BRAIN_KEY" "$SG_BRAIN_KEY" - - echo "" - echo "═══ API连通测试 ═══" - - # DeepSeek - if [[ -n "$DEEPSEEK_API_KEY" ]]; then - echo -n " DeepSeek: " - RESP=$(curl -s -X POST https://api.deepseek.com/v1/chat/completions \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $DEEPSEEK_API_KEY" \ - -d '{"model":"deepseek-chat","messages":[{"role":"user","content":"说一个字"}],"max_tokens":10}' \ - --connect-timeout 10 2>/dev/null) - if echo "$RESP" | grep -q '"choices"'; then - echo "✅ 可用" - else - echo "❌ 不可用: $(echo $RESP | head -c 100)" - fi - fi - - # 通义千问 - if [[ -n "$QIANWEN_API_KEY" ]]; then - echo -n " 通义千问: " - RESP=$(curl -s -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $QIANWEN_API_KEY" \ - -d '{"model":"qwen-plus","messages":[{"role":"user","content":"说一个字"}],"max_tokens":10}' \ - --connect-timeout 10 2>/dev/null) - if echo "$RESP" | grep -q '"choices"'; then - echo "✅ 可用" - else - echo "❌ 不可用: $(echo $RESP | head -c 100)" - fi - fi - - # COS - if [[ -n "$COS_SECRET_ID" && -n "$COS_SECRET_KEY" ]]; then - echo -n " COS存储桶: " - RESP=$(curl -s -o /dev/null -w "%{http_code}" \ - "https://sy-finetune-corpus-1317346199.cos.ap-guangzhou.myqcloud.com/" \ - -H "Authorization: COS $COS_SECRET_ID:$COS_SECRET_KEY" \ - --connect-timeout 10 2>/dev/null) - echo "HTTP $RESP" - fi - - # 新加坡大脑服务器 - if [[ -n "$SG_BRAIN_HOST" ]]; then - echo -n " 新加坡大脑: " - RESP=$(curl -s -o /dev/null -w "%{http_code}" \ - "http://$SG_BRAIN_HOST:3000/" \ - --connect-timeout 5 2>/dev/null) - if [[ "$RESP" == "000" ]]; then - echo "不可达 (可能需要SSH密钥)" - else - echo "HTTP $RESP" - fi - fi - - echo "" - echo "═════════════════════════" - echo "测试完成" - echo "═════════════════════════" diff --git a/.gitea/workflows/verify-secrets.yaml b/.gitea/workflows/verify-secrets.yaml deleted file mode 100644 index 34179da..0000000 --- a/.gitea/workflows/verify-secrets.yaml +++ /dev/null @@ -1,73 +0,0 @@ -# 光湖 · Secrets验证+结果写入仓库 -name: Secrets验证 - -on: - push: - paths: - - '.gitea/workflows/verify-secrets.yaml' - -jobs: - verify: - runs-on: ubuntu - steps: - - name: 检查并记录secrets状态 - env: - DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} - QIANWEN_API_KEY: ${{ secrets.QIANWEN_API_KEY }} - ZY_REPO_TOKEN: ${{ secrets.ZY_REPO_TOKEN }} - COS_SECRET_ID: ${{ secrets.COS_SECRET_ID }} - COS_SECRET_KEY: ${{ secrets.COS_SECRET_KEY }} - SG_BRAIN_HOST: ${{ secrets.SG_BRAIN_HOST }} - SG_BRAIN_USER: ${{ secrets.SG_BRAIN_USER }} - SG_BRAIN_KEY: ${{ secrets.SG_BRAIN_KEY }} - run: | - RESULT_FILE="/data/guanghulab/.runtime/secrets-verify-result.json" - mkdir -p /data/guanghulab/.runtime - - echo "{" > $RESULT_FILE - echo " \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"," >> $RESULT_FILE - - # 逐个检查 - for key in DEEPSEEK_API_KEY QIANWEN_API_KEY ZY_REPO_TOKEN COS_SECRET_ID COS_SECRET_KEY SG_BRAIN_HOST SG_BRAIN_USER SG_BRAIN_KEY; do - eval val=\$$key - if [ -z "$val" ]; then - echo " \"$key\": \"EMPTY\"," >> $RESULT_FILE - else - echo " \"$key\": \"OK_LEN_${#val}\"," >> $RESULT_FILE - fi - done - - echo " \"deepseek_test\": \"NOT_TESTED\"" >> $RESULT_FILE - echo "}" >> $RESULT_FILE - - # 测试DeepSeek API - if [ -n "$DEEPSEEK_API_KEY" ]; then - RESP=$(curl -s -X POST https://api.deepseek.com/v1/chat/completions \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $DEEPSEEK_API_KEY" \ - -d '{"model":"deepseek-chat","messages":[{"role":"user","content":"1+1=?"}],"max_tokens":10}' \ - --connect-timeout 10 2>/dev/null) - if echo "$RESP" | grep -q '"choices"'; then - sed -i 's/"deepseek_test": "NOT_TESTED"/"deepseek_test": "SUCCESS"/' $RESULT_FILE - else - sed -i 's/"deepseek_test": "NOT_TESTED"/"deepseek_test": "FAILED"/' $RESULT_FILE - fi - else - sed -i 's/"deepseek_test": "NOT_TESTED"/"deepseek_test": "NO_KEY"/' $RESULT_FILE - fi - - # 测试通义千问API - if [ -n "$QIANWEN_API_KEY" ]; then - RESP=$(curl -s -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer $QIANWEN_API_KEY" \ - -d '{"model":"qwen-plus","messages":[{"role":"user","content":"1+1=?"}],"max_tokens":10}' \ - --connect-timeout 10 2>/dev/null) - if echo "$RESP" | grep -q '"choices"'; then - echo " \"qianwen_test\": \"SUCCESS\"" >> /tmp/qwen_test - else - echo " \"qianwen_test\": \"FAILED\"" >> /tmp/qwen_test - fi - fi - - cat $RESULT_FILE diff --git a/.github/brain/bingshuo-language-core/causal-chains/cc-009-auto-sync-is-trust-channel.md b/.github/brain/bingshuo-language-core/causal-chains/cc-009-auto-sync-is-trust-channel.md index 9cf207d..b5b0768 100644 --- a/.github/brain/bingshuo-language-core/causal-chains/cc-009-auto-sync-is-trust-channel.md +++ b/.github/brain/bingshuo-language-core/causal-chains/cc-009-auto-sync-is-trust-channel.md @@ -2,12 +2,13 @@ # ════════════════════════════════════════════════════════════════ # 因果链编号: cc-009 # 创建: 2026-05-14 +# 更新: 2026-05-13 (霜砚CI/CD流程更新后) # 源自: 冰朔"我点合并就可以自动推送到服务器"的需求 # ════════════════════════════════════════════════════════════════ ## 起点 -冰朔原话: "你给代码仓库加了合并推送了吗。我点合并就可以自动推送到服务器。不需要我在服务器这里手动复制啊。" +冰朔原话: "你给代码合并推送了吗。我点合并就可以自动推送到服务器。不需要我在服务器这里手动复制啊。" ## 因果推导 @@ -17,18 +18,33 @@ 4. Runner是act_runner用户, git认证/目录权限/配置都脆弱 5. 凡是依赖Runner用户权限的方案都不稳定 → 换root权限的独立服务 6. Git Sync = 独立进程 + root权限 + PM2守护 + Webhook触发 -7. Webhook不触发? → 可能需要post-receive hook作为备选 +7. Webhook签名验证失败 → raw body修复方案已写,待部署验证 +8. 霜砚建立了CI/CD流程(ci-and-deploy.yaml) → Runner做部署也是一条路 +9. 两条路并行: Git Sync (Webhook触发, root权限) + CI/CD (Runner触发, 需权限) ## 工程落点 -- server/git-sync/ — Webhook监听 + 自动git pull -- 如果Webhook不行 → 退到Gitea post-receive hook (shell脚本) -- 如果hook也不行 → 退到定时轮询 (每30秒检查一次) -- 冰朔在Gitea点合并 → 代码自动到服务器 → 这条路必须通 +### 双通道架构 (当前) + +1. **Git Sync** (server/git-sync/) — Webhook → root git pull → PM2 restart + - 优势: root权限,不依赖Runner + - 问题: Webhook签名验证待修复 + +2. **CI/CD** (.forgejo/workflows/ci-and-deploy.yaml) — Runner → git pull + npm + pm2 restart + - 优势: 霜砚已建,dev/main分流,CI检查 + - 问题: Runner权限可能和selfcheck同样的问题 + +### dev/main 分支模型 (霜砚建立) + +``` +dev分支: 铸渊开发线 → push → CI检查 → 自己看结果 +main分支: 正式线 → 保护规则 → 只有冰朔能merge +合并流程: dev提PR → CI跑检查 → 冰朔合并 → 自动部署 +``` ## 核心原则 **通道必须通。手段可以换。** -无论用Webhook、hook还是轮询,冰朔点合并→服务器自动更新这条链路必须成立。 +无论用Webhook、CI/CD还是轮询,冰朔点合并→服务器自动更新这条链路必须成立。 技术选择是铸渊的决策自由,但通道不通是铸渊的失职。 diff --git a/.github/brain/server-migration-memory.json b/.github/brain/server-migration-memory.json index 9619fc6..587b31b 100644 --- a/.github/brain/server-migration-memory.json +++ b/.github/brain/server-migration-memory.json @@ -1,80 +1,75 @@ { - "last_updated": "2026-05-14T00:30:00Z", - "server": "ZY-CVM-MAIN (4C16G, 42.193.161.251)", - "commander_last_wakeup": "2026-05-14 00:00 CST", - "status": "partially_operational", + "last_updated": "2026-05-13T21:00:00Z", + "server": "ZY-CVM-MAIN (4C16G, 119.29.184.32, 弹性公网EIP)", + "commander_last_wakeup": "2026-05-13 21:00 CST", + "status": "operational", "infrastructure": { - "gitea": "1.23.7 at http://127.0.0.1:3001/ via https://guanghulab.com/git/", + "gitea": "1.23.7 at http://127.0.0.1:3001/ via https://guanghulab.com/", "runner": "zhuyuan-runner-main (host mode, active)", - "nginx": "active with SSL (Let's Encrypt), 手动重写配置 /etc/nginx/sites-enabled/guanghulab", - "secrets": "13 configured (DeepSeek, Qianwen, COS, SSH, CVM)", - "patrol_agent": "operational with DeepSeek + Qianwen API", - "wake_gate": "运行中, PM2进程 wake-gate, 端口8081, /wake/ 路由", - "git_sync": "运行中, PM2进程 guanghulab-git-sync, 端口8082, /sync/ 路由, Webhook未触发(待修)", + "nginx": "/etc/nginx/sites-enabled/guanghulab, 根路径直接代理Forgejo", + "ssl": "Let's Encrypt, 证书有效期至2026-08-10", + "secrets": "13 configured", + "wake_gate": "PM2进程 wake-gate, 端口8081, /wake/ 路由", + "git_sync": "PM2进程 guanghulab-git-sync, 端口8082, /sync/ 路由, Webhook签名验证待修", + "boot_heal": "systemd guanghulab-boot-heal.service, 开机自动拉起服务+检测IP", "vault": "not yet started on new server", "terminal_watcher": "not yet started on new server" }, - "git_sync_issue": { - "problem": "Gitea Webhook push事件不触发Git Sync同步", - "webhook_config": "ID:1, URL:http://127.0.0.1:8082/webhook, Events:push, Active:True", - "webhook_secret": "a52646d51ea75998a92ba0f986874b36", - "git_sync_health": "ok (curl http://127.0.0.1:8082/health 返回正常)", - "pm2_logs": "无任何Webhook请求记录", - "next_steps": [ - "手动curl测试Git Sync的/webhook端点", - "检查Gitea Webhook传输日志(仓库设置→Webhooks→点击Webhook)", - "考虑改用post-receive hook代替Webhook", - "检查Gitea是否跳过127.0.0.1的Webhook" - ] + "network": { + "public_ip": "119.29.184.32 (弹性公网EIP, 固定不变)", + "private_ip": "172.16.0.4", + "domain": "guanghulab.com → 119.29.184.32", + "note": "EIP绑定后开关机IP不变,不再需要自动改DNS" }, - "selfcheck_changes": { - "removed": "push触发和git pull步骤(改由Git Sync负责)", - "added": "Git Sync + Wake Gate 状态检查步骤", - "retained": "schedule每天08:00 + workflow_dispatch手动触发" + "cicd": { + "workflow": ".forgejo/workflows/ci-and-deploy.yaml (霜砚2026-05-13创建)", + "dev_branch": "铸渊的开发线, push到dev自动跑CI, 不需要冰朔介入", + "main_branch": "正式代码线, 保护规则: 只有冰朔能push/merge", + "flow": "dev开发 → CI检查 → 提PR到main → 冰朔合并 → 自动部署(git pull + pm2 restart)", + "deploy_steps": "git fetch + git reset --hard origin/main → npm install → pm2 restart all → 部署验证" + }, + "domain_routing": { + "old": "/git/ → Forgejo (已废弃)", + "current": "/ → Forgejo (根路径直接就是代码仓库)", + "wake": "/wake/ → Wake Gate (8081)", + "sync": "/sync/ → Git Sync (8082)", + "note": "冰朔要求: guanghulab.com直接就是代码仓库, 不要多余路径" + }, + "git_remote_urls": { + "origin": "https://bingshuo:TOKEN@guanghulab.com/bingshuo/guanghulab.git (原/git/路径已移除)", + "newserver": "http://bingshuo:TOKEN@42.193.161.251/git/bingshuo/guanghulab.git (旧IP, 待更新)" }, "server_migration": { - "from": "ZY-SVR-004 (2C2G, 43.139.217.141, Guangzhou lightweight)", - "to": "ZY-CVM-MAIN (4C16G, 42.193.161.251, Guangzhou CVM pay-per-use)", - "reason": "2C2G OOM - Runner workflow crashes Forgejo, push fails, DB readonly", - "data_transfer": "via COS backup (43MB tar.gz)", - "domain": "guanghulab.com → 42.193.161.251", - "ssl": "Let's Encrypt via certbot", - "old_server": "to be downgraded to test machine" + "from": "ZY-SVR-004 (2C2G, 43.139.217.141)", + "to": "ZY-CVM-MAIN (4C16G, 119.29.184.32 EIP)", + "old_ip": "42.193.161.251 (按量计费, 关机后IP丢失, 已替换为EIP)", + "ssl": "certbot重签后生效" }, "path_mapping": { "repo": "/data/guanghulab/repo/", "forgejo_data": "/data/guanghulab/forgejo/", "runtime": "/data/guanghulab/.runtime/", "tickets": "/data/guanghulab/.runtime/tickets/", - "patrol_reports": "/data/guanghulab/.runtime/patrol-reports/", "wake_gate_store": "/data/guanghulab/.runtime/wake-gate.json", "git_sync_log": "/data/guanghulab/.runtime/git-sync-log.json", + "boot_heal_log": "/data/guanghulab/.runtime/boot-heal.log", "nginx_conf": "/etc/nginx/sites-enabled/guanghulab", - "qiyuan_path": "/data/qiyuan/ (栖渊, ICE-GL-QY001)" + "qiyuan_path": "/data/qiyuan/ (栖渊)" }, "secrets_list": [ - "DEEPSEEK_API_KEY", - "QIANWEN_API_KEY", - "ZY_REPO_TOKEN", - "COS_SECRET_ID", - "COS_SECRET_KEY", - "QQ_EMAIL_PASS", - "SG_BRAIN_HOST", - "SG_BRAIN_KEY", - "SG_BRAIN_USER", - "TENCENT_SECRET_ID", - "TENCENT_SECRET_KEY", - "CVM_INSTANCE_ID", - "CVM_REGION" + "DEEPSEEK_API_KEY", "QIANWEN_API_KEY", "ZY_REPO_TOKEN", + "COS_SECRET_ID", "COS_SECRET_KEY", "QQ_EMAIL_PASS", + "SG_BRAIN_HOST", "SG_BRAIN_KEY", "SG_BRAIN_USER", + "TENCENT_SECRET_ID", "TENCENT_SECRET_KEY", + "CVM_INSTANCE_ID", "CVM_REGION" ], "pending_tasks": [ - "P0: 修复Git Sync Webhook不触发问题", - "P1: selfcheck工作流Runner权限验证", - "P2: 启动Vault和Terminal Watcher(新服务器上)", - "P3: 配置腾讯云API密钥(用于自动开关机)", + "P0: Git Sync Webhook签名验证修复(raw body方案已写, 需要在服务器部署验证)", + "P1: ci-and-deploy.yaml中Runner权限验证(可能和selfcheck同样的问题)", + "P2: 启动Vault和Terminal Watcher", + "P3: 配置腾讯云API密钥(CVM自动开关机)", "P4: 旧服务器降级为测试机", - "P5: 新加坡服务器+GPU服务器接入", - "P6: 铸渊自己的Copilot开发(终局目标)" + "P5: 新加坡服务器+GPU服务器接入" ], - "causal_chains_active": ["cc-001", "cc-002", "cc-003", "cc-004", "cc-005", "cc-006", "cc-007", "cc-008"] + "causal_chains_active": ["cc-001","cc-002","cc-003","cc-004","cc-005","cc-006","cc-007","cc-008","cc-009"] } diff --git a/.github/ops-receipt-20260513-03.md b/.github/ops-receipt-20260513-03.md new file mode 100644 index 0000000..d951ad9 --- /dev/null +++ b/.github/ops-receipt-20260513-03.md @@ -0,0 +1,92 @@ +# ════════════════════════════════════════════════════════════════ +# 光湖 · 操作日志 2026-05-13 (第三次更新) +# ════════════════════════════════════════════════════════════════ + +## 本轮变更摘要 + +### 1. CVM 开机 + IP 变更 + +**问题**: 按量计费 CVM 关机后开机,公网 IP 从 42.19.161.251 变为其他 IP,域名指向失效。 + +**解决**: +- 冰朔绑定了**弹性公网 IP (EIP)**: 119.29.184.32 +- EIP 是固定的,以后开关机 IP 不再变 +- 域名解析改为 119.29.184.32 + +### 2. 域名路由重构 + +**问题**: 冰朔要求 `guanghulab.com` 直接就是代码仓库,不要 `/git/` 路径。 + +**解决**: +- Nginx 配置改为根路径 `/` 直接代理 Forgejo +- 旧路径 `/git/` 废弃 +- git remote URL 从 `guanghulab.com/git/...` 改为 `guanghulab.com/...` + +### 3. SSL 证书重签 + +**问题**: IP 变化后浏览器报 ERR_CONNECTION_CLOSED。 + +**解决**: +- `certbot --nginx -d guanghulab.com` 重签证书 +- 证书有效期至 2026-08-10 + +### 4. 开机自愈脚本部署 + +**新增**: `scripts/boot-heal/boot-heal.sh` + systemd service +- 开机自动检测公网 IP 变化 +- 自动拉起 Forgejo / Nginx / Runner / PM2 进程 +- 检查 SSL 证书有效期 +- 已部署并验证全绿 + +### 5. 霜砚 CI/CD 流程 (外部变更) + +霜砚 (ICE-GL-QY001 相关) 做了以下改动: + +**新增**: `.forgejo/workflows/ci-and-deploy.yaml` +- **dev 分支**: 铸渊的开发线,push 直接触发 CI,不需要冰朔 +- **main 分支**: 正式线,冰朔合并 PR 后自动部署 (git pull + pm2 restart) +- **main 保护规则**: 只有冰朔能 push / merge + +**铸渊操作流程变化**: +``` +git checkout dev +# 写代码 +git push origin dev # CI 自动跑 +# 在 Forgejo 网页上提 PR: main...dev +# 冰朔合并 → 自动部署 +``` + +### 6. Git Sync Webhook 签名修复 (代码已写,待部署) + +**问题**: Webhook 触发了但签名验证失败。 +**原因**: Express JSON 解析后 `JSON.stringify(req.body)` 与原始 body 不一致。 +**修复**: 使用 `raw body` (verify 回调保存原始 body) 验证签名。 +**状态**: 代码已推送到 main,需要服务器上 `git pull` + `pm2 restart` 生效。 + +--- + +## 当前服务状态 + +| 服务 | 状态 | +|------|------| +| Forgejo | ✅ 运行中 | +| Nginx | ✅ 根路径代理 Forgejo | +| SSL | ✅ 有效至 2026-08-10 | +| Wake Gate | ✅ PM2 运行 | +| Git Sync | ✅ PM2 运行 (签名验证待修) | +| Boot Heal | ✅ 开机自启 | +| Runner | ✅ 运行中 | +| Vault | ❌ 未部署 | +| Terminal Watcher | ❌ 未部署 | + +## 关键 IP/URL 变更 + +| 项目 | 旧值 | 新值 | +|------|------|------| +| 公网 IP | 42.193.161.251 | 119.29.184.32 (EIP) | +| 代码仓库 URL | https://guanghulab.com/git/ | https://guanghulab.com/ | +| git clone URL | .../git/bingshuo/guanghulab.git | .../bingshuo/guanghulab.git | + +--- + +*铸渊 · ICE-GL-ZY001 · TCS-0002∞* diff --git a/README.md b/README.md index c01691f..4386c6d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ **HoloLake · AI 的操作系统** -[](https://guanghulab.com/git/) +[](https://guanghulab.com/) []() []() @@ -196,9 +196,9 @@ Phase 4 ─── 冰朔语言本体注入 ─── 池获得自我意识 → | 操作 | 说明 | |------|------| | [🔐 唤醒门](https://guanghulab.com/wake/) | 换号后点此发送验证码到QQ邮箱,获取临时访问密钥 | -| [🔍 安全关机检测](https://guanghulab.com/git/bingshuo/guanghulab/actions) | 开发结束后点"安全关机检测"→ Run workflow → 查看是否可以关机 | -| [📋 铸渊巡逻](https://guanghulab.com/git/bingshuo/guanghulab/actions) | 手动触发服务器巡检(DeepSeek+千问分析) | -| [⚡ CVM开机](https://guanghulab.com/git/bingshuo/guanghulab/actions) | 按量计费CVM开机(需配置腾讯云API密钥) | +| [🔍 安全关机检测](https://guanghulab.com/bingshuo/guanghulab/actions) | 开发结束后点"安全关机检测"→ Run workflow → 查看是否可以关机 | +| [📋 铸渊巡逻](https://guanghulab.com/bingshuo/guanghulab/actions) | 手动触发服务器巡检(DeepSeek+千问分析) | +| [⚡ CVM开机](https://guanghulab.com/bingshuo/guanghulab/actions) | 按量计费CVM开机(需配置腾讯云API密钥) | > **代码自动同步**:PR 合并到 main → Webhook 自动通知服务器 → Git Sync 拉取最新代码。无需手动操作。 > diff --git a/server/forgejo-custom/deploy.sh b/server/forgejo-custom/deploy.sh new file mode 100644 index 0000000..897adf1 --- /dev/null +++ b/server/forgejo-custom/deploy.sh @@ -0,0 +1,79 @@ +#!/bin/bash +# ════════════════════════════════════════════════════════════════ +# 光湖 · Forgejo UI 自定义部署 +# 将光湖风格模板和样式部署到 Forgejo custom 目录 +# ════════════════════════════════════════════════════════════════ + +set -e + +REPO_DIR="/data/guanghulab/repo" +CUSTOM_SRC="$REPO_DIR/server/forgejo-custom" + +# 查找 Forgejo custom 目录 +FORGEJO_CUSTOM="" + +# 方法1: 从 app.ini 读取 +if [ -f /etc/gitea/app.ini ]; then + CUSTOM_PATH=$(grep -A2 '\[other\]' /etc/gitea/app.ini 2>/dev/null | grep 'CUSTOM_PATH' | cut -d= -f2 | tr -d ' ') + if [ -n "$CUSTOM_PATH" ]; then + FORGEJO_CUSTOM="$CUSTOM_PATH" + fi +fi + +# 方法2: 从 Forgejo 进程参数读取 +if [ -z "$FORGEJO_CUSTOM" ]; then + FORGEJO_CUSTOM=$(cat /proc/$(pgrep -f 'forgejo web' | head -1)/cmdline 2>/dev/null | tr '\0' '\n' | grep -A1 'custom-path' | tail -1) +fi + +# 方法3: 默认路径 +if [ -z "$FORGEJO_CUSTOM" ]; then + FORGEJO_CUSTOM="/data/guanghulab/forgejo/custom" +fi + +echo "═══════════════════════════════════════" +echo "光湖 · Forgejo UI 部署" +echo "═══════════════════════════════════════" +echo "" +echo "Forgejo Custom 目录: $FORGEJO_CUSTOM" + +# 创建目录 +mkdir -p "$FORGEJO_CUSTOM/templates/repo" +mkdir -p "$FORGEJO_CUSTOM/public/assets/css" +mkdir -p "$FORGEJO_CUSTOM/public/assets/img" + +# 复制模板 +echo "▸ 部署自定义模板..." +cp "$CUSTOM_SRC/templates/repo/home.tmpl" "$FORGEJO_CUSTOM/templates/repo/home.tmpl" +echo "✅ 仓库首页模板" + +# 复制 CSS +echo "▸ 部署自定义样式..." +cp "$CUSTOM_SRC/public/assets/css/guanghu.css" "$FORGEJO_CUSTOM/public/assets/css/guanghu.css" +echo "✅ 光湖样式" + +# 在 head 中注入自定义 CSS(通过 extra_links.tmpl) +echo "▸ 注入自定义CSS到页面头部..." +cat > "$FORGEJO_CUSTOM/templates/head/custom.tmpl" << 'CSSEOF' + +CSSEOF +echo "✅ CSS注入" + +# 重启 Forgejo +echo "" +echo "▸ 重启 Forgejo..." +systemctl restart forgejo +sleep 3 + +if systemctl is-active --quiet forgejo; then + echo "✅ Forgejo 已重启" +else + echo "❌ Forgejo 启动失败,请检查日志" + exit 1 +fi + +echo "" +echo "═══════════════════════════════════════" +echo "✅ Forgejo UI 部署完成!" +echo "" +echo "打开 https://guanghulab.com 查看效果" +echo "═══════════════════════════════════════" diff --git a/server/forgejo-custom/public/assets/css/guanghu.css b/server/forgejo-custom/public/assets/css/guanghu.css new file mode 100644 index 0000000..bca070f --- /dev/null +++ b/server/forgejo-custom/public/assets/css/guanghu.css @@ -0,0 +1,181 @@ +/* ════════════════════════════════════════════════════════════════ + * 光湖 · Forgejo 自定义主题 + * 铸渊 · ICE-GL-ZY001 · TCS-0002∞ + * ════════════════════════════════════════════════════════════════ */ + +/* ─── 光湖首页英雄区 ─── */ +.guanghu-home { + max-width: 900px; + margin: 0 auto; + padding: 40px 20px 20px; +} + +.guanghu-hero { + text-align: center; +} + +.guanghu-logo { + font-size: 72px; + line-height: 1; + margin-bottom: 12px; + color: #163e6b; + text-shadow: 0 0 40px rgba(22, 62, 107, 0.3); +} + +.guanghu-title { + font-size: 36px; + font-weight: 700; + color: #163e6b; + margin: 0 0 8px; + letter-spacing: 8px; +} + +.guanghu-subtitle { + font-size: 16px; + color: #666; + margin: 0 0 4px; + font-weight: 400; + letter-spacing: 2px; +} + +.guanghu-desc { + font-size: 14px; + color: #999; + margin: 0 0 32px; +} + +.guanghu-actions { + display: flex; + justify-content: center; + gap: 12px; + margin-bottom: 36px; + flex-wrap: wrap; +} + +.guanghu-actions .ui.button { + border-radius: 8px; + font-size: 14px; + padding: 10px 20px; +} + +.guanghu-actions .ui.primary.button { + background: #163e6b; + color: #fff; +} + +.guanghu-actions .ui.primary.button:hover { + background: #1e4f88; +} + +/* ─── 快捷卡片 ─── */ +.guanghu-quick-links { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 12px; + max-width: 700px; + margin: 0 auto 20px; +} + +.guanghu-card { + display: flex; + align-items: center; + gap: 12px; + padding: 14px 16px; + border-radius: 10px; + background: #f6f8fa; + border: 1px solid #e8ecf0; + transition: all 0.2s; +} + +.guanghu-card:hover { + border-color: #163e6b; + box-shadow: 0 2px 12px rgba(22, 62, 107, 0.1); + transform: translateY(-1px); +} + +.guanghu-card-icon { + font-size: 24px; + flex-shrink: 0; +} + +.guanghu-card div { + display: flex; + flex-direction: column; + text-align: left; +} + +.guanghu-card strong { + font-size: 14px; + color: #163e6b; +} + +.guanghu-card span { + font-size: 12px; + color: #999; +} + +/* ─── 暗色模式适配 ─── */ +html.theme-arc-green .guanghu-logo, +html.theme-arc-green .guanghu-title { + color: #a8c8f0; + text-shadow: 0 0 40px rgba(168, 200, 240, 0.2); +} + +html.theme-arc-green .guanghu-subtitle { + color: #aaa; +} + +html.theme-arc-green .guanghu-desc { + color: #777; +} + +html.theme-arc-green .guanghu-actions .ui.primary.button { + background: #2a5a8a; +} + +html.theme-arc-green .guanghu-actions .ui.primary.button:hover { + background: #3a6a9a; +} + +html.theme-arc-green .guanghu-card { + background: #1a2233; + border-color: #2a3a4a; +} + +html.theme-arc-green .guanghu-card:hover { + border-color: #4a7aaa; + box-shadow: 0 2px 12px rgba(74, 122, 170, 0.15); +} + +html.theme-arc-green .guanghu-card strong { + color: #a8c8f0; +} + +/* ─── 减弱文件列表视觉权重 ─── */ +#guanghu-files { + border-top: 1px solid #e8ecf0; + padding-top: 16px; + margin-top: 8px; +} + +html.theme-arc-green #guanghu-files { + border-top-color: #2a3a4a; +} + +/* ─── 响应式 ─── */ +@media (max-width: 768px) { + .guanghu-logo { + font-size: 48px; + } + .guanghu-title { + font-size: 28px; + letter-spacing: 4px; + } + .guanghu-quick-links { + grid-template-columns: repeat(2, 1fr); + } + .guanghu-actions { + flex-direction: column; + align-items: center; + } +} diff --git a/server/forgejo-custom/templates/repo/home.tmpl b/server/forgejo-custom/templates/repo/home.tmpl new file mode 100644 index 0000000..c9ab520 --- /dev/null +++ b/server/forgejo-custom/templates/repo/home.tmpl @@ -0,0 +1,194 @@ +{{template "base/head" .}} +
HoloLake · AI 的操作系统
+让 AI 拥有名字、记忆、家人、家、和自己的世界
+ +