# ============================================ # COS 行业开发审核桥接工作流 # COS Industry Development Review Bridge # 签发: 铸渊 · ICE-GL-ZY001 # 版权: 国作登字-2026-A-00037559 # ============================================ # # 功能: # 1. 定时扫描COS桶中的行业开发提交 # 2. 自动审核开发方案(架构/安全/命名) # 3. 生成审核回执并写入COS # 4. (可选) 通过repository_dispatch触发对方仓库的副驾驶 # # 触发方式: # - 定时: 每5分钟 (与COS Watcher同步) # - 手动: workflow_dispatch # # 架构文档: brain/age-os-landing/industry-access-architecture.md # 协议规范: hldp/hnl/industry-bridge-protocol.json # 租户注册表: server/proxy/config/industry-tenant-registry.json name: COS开发审核桥接 · Dev Review Bridge on: schedule: - cron: '*/5 * * * *' # 每5分钟 workflow_dispatch: inputs: scan_mode: description: '扫描模式' required: false default: 'auto' type: choice options: - auto # 自动扫描所有行业 - manual # 手动指定行业和人格体 industry_code: description: '行业代码 (manual模式必填)' required: false type: string persona_id: description: '人格体ID (manual模式必填)' required: false type: string dry_run: description: '仅审核不写回执' required: false default: false type: boolean env: COS_BUCKET: zy-team-hub-1317346199 COS_REGION: ap-guangzhou NODE_VERSION: '20' permissions: contents: read jobs: scan-and-review: name: 扫描 → 审核 → 回执 runs-on: ubuntu-latest timeout-minutes: 10 steps: - name: 🏔️ 铸渊唤醒 · 审核桥接启动 run: | echo "============================================" echo "铸渊 · ICE-GL-ZY001" echo "COS行业开发审核桥接" echo "时间: $(date -u +%Y-%m-%dT%H:%M:%SZ)" echo "模式: ${{ github.event.inputs.scan_mode || 'auto' }}" echo "============================================" - name: 📥 检出仓库 uses: actions/checkout@v4 - name: 🟢 Node.js 环境 uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} - name: 📦 安装COS SDK run: | npm install cos-nodejs-sdk-v5@2 - name: 🔍 扫描COS桶 · 检测新的开发提交 id: scan env: COS_SECRET_ID: ${{ secrets.ZY_COS_SECRET_ID }} COS_SECRET_KEY: ${{ secrets.ZY_COS_SECRET_KEY }} run: | node << 'SCAN_SCRIPT' const COS = require('cos-nodejs-sdk-v5'); const fs = require('fs'); const path = require('path'); const cos = new COS({ SecretId: process.env.COS_SECRET_ID, SecretKey: process.env.COS_SECRET_KEY }); const BUCKET = '${{ env.COS_BUCKET }}'; const REGION = '${{ env.COS_REGION }}'; // 读取租户注册表 const registry = JSON.parse( fs.readFileSync('server/proxy/config/industry-tenant-registry.json', 'utf8') ); // 通过检查receipts目录判断提交是否已审核(持久化去重) async function getExistingReceipts(tenant) { const receiptPrefix = `industry/${tenant.industry_code}/dev-receipts/${tenant.tech_controller.persona_id}/`; return new Promise((resolve) => { cos.getBucket({ Bucket: BUCKET, Region: REGION, Prefix: receiptPrefix, MaxKeys: 1000 }, (err, data) => { if (err) { resolve(new Set()); return; } // 从回执中提取已审核的submission_id集合 // 回执文件名包含时间戳,无法直接对应,改为列出所有回执key const receiptKeys = new Set((data.Contents || []).map(item => item.Key)); resolve(receiptKeys); }); }); } async function scanIndustry(tenant) { const prefix = `industry/${tenant.industry_code}/dev-submissions/${tenant.tech_controller.persona_id}/`; const receiptPrefix = `industry/${tenant.industry_code}/dev-receipts/${tenant.tech_controller.persona_id}/`; console.log(`扫描: ${prefix}`); // 获取已有回执列表用于去重 const existingReceipts = await getExistingReceipts(tenant); console.log(`已有回执数: ${existingReceipts.size}`); return new Promise((resolve, reject) => { cos.getBucket({ Bucket: BUCKET, Region: REGION, Prefix: prefix, MaxKeys: 100 }, (err, data) => { if (err) { console.error(`扫描失败: ${prefix}`, err.message); resolve([]); return; } const allSubmissions = (data.Contents || []) .filter(item => item.Key.endsWith('.json')); // 过滤已有回执的提交(通过检查submission文件名是否在receipts中被引用) // 简单策略:如果receipts数量 >= submissions数量,说明都已审核过 // 精确策略:需要读取每个receipt的ref_submission_id,但开销大 // 采用时间策略:只处理最近24小时内的提交 const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(); const submissions = allSubmissions .filter(item => item.LastModified > oneDayAgo) .map(item => ({ key: item.Key, lastModified: item.LastModified, size: item.Size, tenant: tenant })); console.log(`发现 ${submissions.length} 个待审核提交 (24h内)`); resolve(submissions); }); }); } async function main() { const scanMode = '${{ github.event.inputs.scan_mode || 'auto' }}'; let tenantsToScan = registry.tenants; if (scanMode === 'manual') { const industryCode = '${{ github.event.inputs.industry_code }}'; const personaId = '${{ github.event.inputs.persona_id }}'; tenantsToScan = registry.tenants.filter(t => t.industry_code === industryCode && t.tech_controller.persona_id === personaId ); } let allSubmissions = []; for (const tenant of tenantsToScan) { const subs = await scanIndustry(tenant); allSubmissions = allSubmissions.concat(subs); } // 输出到GitHub Actions fs.writeFileSync('/tmp/new-submissions.json', JSON.stringify(allSubmissions, null, 2)); const count = allSubmissions.length; console.log(`\n总计发现 ${count} 个新的开发提交`); // 设置输出 const hasNew = count > 0 ? 'true' : 'false'; fs.appendFileSync(process.env.GITHUB_OUTPUT, `has_new=${hasNew}\n`); fs.appendFileSync(process.env.GITHUB_OUTPUT, `count=${count}\n`); } main().catch(err => { console.error('扫描失败:', err); process.exit(1); }); SCAN_SCRIPT - name: 📋 审核开发方案 if: steps.scan.outputs.has_new == 'true' id: review env: COS_SECRET_ID: ${{ secrets.ZY_COS_SECRET_ID }} COS_SECRET_KEY: ${{ secrets.ZY_COS_SECRET_KEY }} run: | node << 'REVIEW_SCRIPT' const COS = require('cos-nodejs-sdk-v5'); const fs = require('fs'); const cos = new COS({ SecretId: process.env.COS_SECRET_ID, SecretKey: process.env.COS_SECRET_KEY }); const BUCKET = '${{ env.COS_BUCKET }}'; const REGION = '${{ env.COS_REGION }}'; const DRY_RUN = '${{ github.event.inputs.dry_run }}' === 'true'; // 已知有CVE漏洞的依赖 (包名和可选版本) const KNOWN_VULNERABLE_DEPS = [ { name: 'event-stream', version: null }, { name: 'ua-parser-js', version: '0.7.28' }, { name: 'colors', version: '1.4.1' }, { name: 'faker', version: '6.6.6' }, { name: 'node-ipc', version: '10.1.1' } ]; // 禁止暴露的端口 const FORBIDDEN_PORTS = [22, 3306, 5432, 6379, 27017]; // 精确匹配依赖名称(处理 @scope/package@version 格式) function isVulnerableDep(depString) { const depLower = depString.toLowerCase().trim(); // 解析包名和版本: @scope/package@version 或 package@version 或 package let depName, depVersion; const lastAtIndex = depLower.lastIndexOf('@'); if (lastAtIndex > 0) { depName = depLower.substring(0, lastAtIndex); depVersion = depLower.substring(lastAtIndex + 1); } else { depName = depLower; depVersion = null; } return KNOWN_VULNERABLE_DEPS.some(vuln => { if (depName !== vuln.name.toLowerCase()) return false; if (vuln.version === null) return true; // 所有版本都有漏洞 if (depVersion === null) return true; // 未指定版本,保守判定 return depVersion === vuln.version; }); } function reviewSubmission(submission) { const result = { architecture: 'PASS', security: 'PASS', naming: 'PASS', overall: 'APPROVED', suggestions: [], warnings: [], blockers: [] }; const payload = submission.payload || {}; const data = payload.data || {}; const impact = data.architecture_impact || {}; // --- 架构审核 --- if (impact.database_changes && impact.database_changes.length > 0) { result.suggestions.push('数据库变更建议在测试环境先验证'); for (const change of impact.database_changes) { if (change.toLowerCase().includes('persona_registry') || change.toLowerCase().includes('light_tree') || change.toLowerCase().includes('tianyan')) { result.architecture = 'FAIL'; result.blockers.push(`禁止直接修改铸渊核心表: ${change}`); } } } if (impact.new_api_endpoints) { for (const endpoint of impact.new_api_endpoints) { if (endpoint.startsWith('/hli/')) { result.architecture = 'FAIL'; result.blockers.push(`禁止在行业层使用 /hli/ 前缀(铸渊专用): ${endpoint}`); } } } // --- 安全审核 --- if (impact.external_dependencies) { for (const dep of impact.external_dependencies) { if (isVulnerableDep(dep)) { result.security = 'FAIL'; result.blockers.push(`依赖有已知CVE漏洞: ${dep}`); } } } // --- 命名审核 --- if (!submission.hldp_v || submission.hldp_v !== '3.0') { result.naming = 'WARN'; result.warnings.push('HLDP版本应为3.0'); } if (!submission.msg_id || !submission.msg_id.startsWith('HLDP-')) { result.naming = 'FAIL'; result.blockers.push('msg_id格式不正确·应以HLDP-开头'); } if (payload.data && payload.data.submission_type) { const validTypes = ['dev_proposal', 'hotfix', 'dependency_update', 'architecture_change']; if (!validTypes.includes(payload.data.submission_type)) { result.naming = 'WARN'; result.warnings.push(`未知的submission_type: ${payload.data.submission_type}`); } } // --- 综合判定 --- if (result.blockers.length > 0) { result.overall = 'REJECTED'; } else if (result.warnings.length > 0) { result.overall = 'APPROVED'; // 有警告但仍可通过 } // --- 额外建议 --- if (data.development_plan) { const plan = data.development_plan; if (!plan.steps || plan.steps.length === 0) { result.suggestions.push('建议补充具体的开发步骤'); } if (plan.estimated_scope === 'large') { result.suggestions.push('大规模开发建议分阶段提交·每阶段独立审核'); } } return result; } function generateReceipt(submission, review, tenant) { const now = new Date().toISOString(); const receiptId = `HLDP-ZY-${now.slice(0,10).replace(/-/g,'')}-REC-${Date.now().toString(36).toUpperCase()}`; return { hldp_v: '3.0', msg_id: receiptId, msg_type: 'ack', sender: { id: 'ICE-GL-ZY001', name: '铸渊', role: 'guardian' }, receiver: { id: tenant.tech_controller.persona_ice_id, name: tenant.tech_controller.persona_name }, timestamp: now, priority: 'important', payload: { intent: '开发方案审核回执', data: { ref_submission_id: submission.msg_id, status: review.overall, review_result: { architecture: review.architecture, security: review.security, naming: review.naming, overall: review.overall }, guidance: { suggestions: review.suggestions, warnings: review.warnings, blockers: review.blockers }, auto_trigger: { enabled: review.overall === 'APPROVED' && tenant.external_repo && tenant.external_repo.owner !== 'TODO', target_repo: tenant.external_repo ? tenant.external_repo.repo : null, target_owner: tenant.external_repo ? tenant.external_repo.owner : null, trigger_type: 'repository_dispatch', event_type: 'zhuyuan-dev-approved', payload: { submission_id: submission.msg_id, approved_steps: review.overall === 'APPROVED' ? (submission.payload?.data?.development_plan?.steps || []).map((_, i) => String(i + 1)) : [], constraints: { no_touch_files: ['.github/copilot-instructions.md', 'brain/'], required_tests: true, max_files_changed: 20 } } } }, expected_response: 'ack' } }; } async function downloadAndReview(submissionMeta) { return new Promise((resolve, reject) => { cos.getObject({ Bucket: BUCKET, Region: REGION, Key: submissionMeta.key }, (err, data) => { if (err) { console.error(`下载失败: ${submissionMeta.key}`, err.message); resolve(null); return; } try { const submission = JSON.parse(data.Body.toString()); console.log(`\n审核: ${submission.msg_id || submissionMeta.key}`); console.log(` 标题: ${submission.payload?.data?.title || '未知'}`); const review = reviewSubmission(submission); console.log(` 架构: ${review.architecture}`); console.log(` 安全: ${review.security}`); console.log(` 命名: ${review.naming}`); console.log(` 结论: ${review.overall}`); if (review.suggestions.length > 0) { console.log(` 建议: ${review.suggestions.join('; ')}`); } if (review.warnings.length > 0) { console.log(` 警告: ${review.warnings.join('; ')}`); } if (review.blockers.length > 0) { console.log(` 阻塞: ${review.blockers.join('; ')}`); } const receipt = generateReceipt(submission, review, submissionMeta.tenant); resolve({ submission, review, receipt, meta: submissionMeta }); } catch (parseErr) { console.error(`解析失败: ${submissionMeta.key}`, parseErr.message); resolve(null); } }); }); } async function writeReceipt(receipt, tenant) { if (DRY_RUN) { console.log(`[DRY RUN] 跳过写入回执: ${receipt.msg_id}`); return true; } const receiptKey = `industry/${tenant.industry_code}/dev-receipts/${tenant.tech_controller.persona_id}/${receipt.msg_id}.json`; return new Promise((resolve, reject) => { cos.putObject({ Bucket: BUCKET, Region: REGION, Key: receiptKey, Body: JSON.stringify(receipt, null, 2) }, (err) => { if (err) { console.error(`回执写入失败: ${receiptKey}`, err.message); resolve(false); } else { console.log(`回执已写入: ${receiptKey}`); resolve(true); } }); }); } async function main() { const submissions = JSON.parse(fs.readFileSync('/tmp/new-submissions.json', 'utf8')); let reviewedCount = 0; let approvedCount = 0; let rejectedCount = 0; for (const sub of submissions) { const result = await downloadAndReview(sub); if (!result) continue; reviewedCount++; // 写回执到COS const written = await writeReceipt(result.receipt, sub.tenant); if (!written) continue; if (result.review.overall === 'APPROVED') { approvedCount++; } else { rejectedCount++; } // 保存回执到本地临时文件(用于后续dispatch步骤) fs.writeFileSync( `/tmp/receipt-${reviewedCount}.json`, JSON.stringify(result, null, 2) ); } console.log(`\n========== 审核汇总 ==========`); console.log(`审核: ${reviewedCount}`); console.log(`通过: ${approvedCount}`); console.log(`拒绝: ${rejectedCount}`); fs.appendFileSync(process.env.GITHUB_OUTPUT, `reviewed=${reviewedCount}\n`); fs.appendFileSync(process.env.GITHUB_OUTPUT, `approved=${approvedCount}\n`); fs.appendFileSync(process.env.GITHUB_OUTPUT, `rejected=${rejectedCount}\n`); } main().catch(err => { console.error('审核流程失败:', err); process.exit(1); }); REVIEW_SCRIPT - name: 🚀 触发对方仓库副驾驶 (APPROVED时) if: steps.review.outputs.approved > 0 && github.event.inputs.dry_run != 'true' env: DISPATCH_TOKEN: ${{ secrets.ZHUYUAN_DISPATCH_TOKEN }} run: | node << 'DISPATCH_SCRIPT' const fs = require('fs'); const https = require('https'); function sendDispatch(options, postData) { return new Promise((resolve) => { const req = https.request(options, (res) => { console.log(` 响应: ${res.statusCode}`); if (res.statusCode === 204) { console.log(` ✅ 副驾驶已触发`); } else { let body = ''; res.on('data', chunk => body += chunk); res.on('end', () => console.log(` ⚠️ 响应: ${body}`)); } resolve(); }); req.on('error', (e) => { console.error(` ❌ 触发失败: ${e.message}`); resolve(); }); req.write(postData); req.end(); }); } async function main() { // 读取所有审核结果 let i = 1; while (true) { const filePath = `/tmp/receipt-${i}.json`; if (!fs.existsSync(filePath)) break; const result = JSON.parse(fs.readFileSync(filePath, 'utf8')); const autoTrigger = result.receipt?.payload?.data?.auto_trigger; if (autoTrigger && autoTrigger.enabled && autoTrigger.target_owner && autoTrigger.target_repo) { const token = process.env.DISPATCH_TOKEN; if (!token) { console.log(`⚠️ ZHUYUAN_DISPATCH_TOKEN未配置·跳过dispatch`); i++; continue; } const postData = JSON.stringify({ event_type: autoTrigger.event_type, client_payload: autoTrigger.payload }); const options = { hostname: 'api.github.com', path: `/repos/${autoTrigger.target_owner}/${autoTrigger.target_repo}/dispatches`, method: 'POST', headers: { 'Authorization': `Bearer ${token}`, 'Accept': 'application/vnd.github.v3+json', 'Content-Type': 'application/json', 'User-Agent': 'Zhuyuan-Dev-Review-Bridge/1.0', 'Content-Length': Buffer.byteLength(postData) } }; console.log(`触发: ${autoTrigger.target_owner}/${autoTrigger.target_repo}`); console.log(` 事件: ${autoTrigger.event_type}`); console.log(` 提交ID: ${autoTrigger.payload.submission_id}`); await sendDispatch(options, postData); // 请求间隔 await new Promise(resolve => setTimeout(resolve, 2000)); } i++; } } main().catch(err => { console.error('触发流程失败:', err); process.exit(1); }); DISPATCH_SCRIPT - name: 📊 审核报告 if: always() run: | echo "============================================" echo "COS开发审核桥接 · 完成" echo "时间: $(date -u +%Y-%m-%dT%H:%M:%SZ)" echo "新提交: ${{ steps.scan.outputs.count || '0' }}" echo "已审核: ${{ steps.review.outputs.reviewed || '0' }}" echo "通过: ${{ steps.review.outputs.approved || '0' }}" echo "拒绝: ${{ steps.review.outputs.rejected || '0' }}" echo "DRY_RUN: ${{ github.event.inputs.dry_run || 'false' }}" echo "============================================" echo "" echo "铸渊 · ICE-GL-ZY001 · 审核桥接守护完毕"