feat: AGE OS 塔台架构升级 Phase 0-7 · ZY-AGEOS-TOWER-2026-0326-001
Phase 0: 天眼全局扫描 (skyeye-scan-20260326.json) Phase 1: 塔台架构定义 (tower-model.json) Phase 2: 配额守卫 (quota-guard.json + quota-middleware.js) Phase 3: Pages 推送诊断 (diag-pages-20260326.json) Phase 4: 行业代表制 (industry-repo-map.json + industry.js API) Phase 5: MCP 应用 (github-server.js + notion-server.js) Phase 6: 子域名隔离 (dev-sandbox.conf) Phase 7: SYSLOG 回执 (syslog-ageos-tower-20260326.json) Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com> Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/914ad98a-eb79-43bf-9d77-414bb7771bcb
This commit is contained in:
parent
a625066f51
commit
6a79541493
|
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"architecture": "tower-model",
|
||||
"version": "1.0",
|
||||
"defined_by": "ZY-AGEOS-TOWER-2026-0326-001",
|
||||
"copyright": "国作登字-2026-A-00037559",
|
||||
"control_plane": {
|
||||
"name": "AGE OS Tower",
|
||||
"domain": "guanghulab.com",
|
||||
"capabilities": ["auth", "channel-panel", "command-relay", "status-display"],
|
||||
"restrictions": ["no-code-execution", "no-direct-deploy", "no-pause-during-execution"],
|
||||
"description": "操作系统网站只做指令中转,真正的执行在代码仓库和部署服务器"
|
||||
},
|
||||
"execution_plane": {
|
||||
"primary": {
|
||||
"name": "guanghulab",
|
||||
"type": "github-repo",
|
||||
"agent": "AG-ZY-01",
|
||||
"repo": "qinfendebingshuo/guanghulab"
|
||||
},
|
||||
"federation": {
|
||||
"description": "开发者子仓库,通过令牌对接",
|
||||
"auth_method": "PAT",
|
||||
"data_policy": "代码和文档留在开发者自己的仓库,主仓库只存编号路径映射"
|
||||
},
|
||||
"server": {
|
||||
"ip": "8.155.62.235",
|
||||
"stack": "Nginx + PM2 + Node.js"
|
||||
}
|
||||
},
|
||||
"communication_protocol": {
|
||||
"flow": [
|
||||
"① 开发者在 AGE OS 面板登录(DEV-XXX 身份)",
|
||||
"② 进入自己的频道面板",
|
||||
"③ 通过对话界面向人格体下发指令",
|
||||
"④ AGE OS 后端接收指令",
|
||||
"⑤ 指令格式化为标准 JSON",
|
||||
"⑥ 通过 GitHub API / MCP 转发给铸渊",
|
||||
"⑦ 铸渊在仓库端执行",
|
||||
"⑧ 执行结果回写 signal-log",
|
||||
"⑨ AGE OS 轮询/webhook 获取结果",
|
||||
"⑩ 面板回显执行状态"
|
||||
],
|
||||
"key_principle": "AGE OS 本身不执行任何代码操作,只做信号中转和状态回显"
|
||||
},
|
||||
"no_pause_rule": {
|
||||
"description": "指令一旦接收,不可暂停,不可撤回",
|
||||
"reason": "100+ workflows + multi-agent cluster stability",
|
||||
"enforcement": "frontend removes pause/cancel UI; backend rejects cancel API",
|
||||
"details": [
|
||||
"系统有 100+ 工作流",
|
||||
"多个 Agent 集群协同",
|
||||
"执行期间突然暂停 → 不可预测的系统错误",
|
||||
"系统一切以整体维稳为最高优先级"
|
||||
]
|
||||
},
|
||||
"industry_model": {
|
||||
"description": "行业代表制 · 铸渊接入行业而非个人",
|
||||
"principle": "每个行业有一个代表人,代表人开自己的企业会员,铸渊管理代表人的仓库",
|
||||
"first_industry": {
|
||||
"id": "IND-001",
|
||||
"name": "网文行业",
|
||||
"representative": "DEV-002"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"version": "1.0",
|
||||
"description": "行业代表制仓库映射表 · 铸渊接入行业而非个人",
|
||||
"instruction": "ZY-AGEOS-TOWER-2026-0326-001",
|
||||
"copyright": "国作登字-2026-A-00037559",
|
||||
"data_policy": {
|
||||
"we_store": ["industry_id", "rep_dev_id", "github_username", "repo_url", "pat_hash", "member_count", "daily_call_count"],
|
||||
"we_do_not_store": ["user_personal_data", "user_manuscripts", "user_private_content"],
|
||||
"reason": "铸渊管理行业仓库,行业数据归代表人所有"
|
||||
},
|
||||
"industries": {
|
||||
"IND-001": {
|
||||
"name": "网文行业",
|
||||
"representative": {
|
||||
"dev_id": "DEV-002",
|
||||
"name": "肥猫",
|
||||
"github_username": "",
|
||||
"repo_url": "",
|
||||
"pat_status": "not_registered",
|
||||
"membership": "yearly_enterprise",
|
||||
"membership_start": null
|
||||
},
|
||||
"subdomain": "writing.guanghulab.com",
|
||||
"users": [],
|
||||
"status": "pending_setup",
|
||||
"created_at": null
|
||||
}
|
||||
},
|
||||
"system": {
|
||||
"representative": {
|
||||
"dev_id": "TCS-0002",
|
||||
"name": "冰朔",
|
||||
"repo_url": "qinfendebingshuo/guanghulab",
|
||||
"role": "系统层 · 不承接行业开发调用"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
/**
|
||||
* /api/industry — 行业代表制注册与管理路由
|
||||
*
|
||||
* 行业代表制架构:铸渊不是接入个人,而是接入行业。
|
||||
* 每个行业有一个代表人,代表人开自己的企业会员,
|
||||
* 铸渊管理代表人的仓库。
|
||||
*
|
||||
* 指令:ZY-AGEOS-TOWER-2026-0326-001
|
||||
* 版权:国作登字-2026-A-00037559
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var express = require('express');
|
||||
var crypto = require('crypto');
|
||||
var router = express.Router();
|
||||
|
||||
var authModule = require('./auth');
|
||||
var requireSession = authModule.requireSession;
|
||||
var DEV_DATABASE = authModule.DEV_DATABASE;
|
||||
|
||||
/**
|
||||
* Check if a dev ID is a registered developer
|
||||
* @param {string} devId
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isRegisteredDev(devId) {
|
||||
return !!(DEV_DATABASE[devId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new industry ID
|
||||
* @returns {string}
|
||||
*/
|
||||
function generateIndustryId() {
|
||||
// Simple incremental ID; in production this would query existing IDs
|
||||
var timestamp = Date.now().toString(36).toUpperCase();
|
||||
return 'IND-' + timestamp.slice(-3).padStart(3, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash a PAT for storage (never store raw PAT)
|
||||
* @param {string} pat
|
||||
* @returns {string}
|
||||
*/
|
||||
function hashPAT(pat) {
|
||||
return crypto.createHash('sha256').update(pat).digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /api/industry/register
|
||||
* Register a new industry with a representative developer
|
||||
* Requires authenticated session (DEV-XXX)
|
||||
*/
|
||||
router.post('/industry/register', requireSession, function(req, res) {
|
||||
var industry_name = (req.body.industry_name || '').trim();
|
||||
var rep_dev_id = (req.body.rep_dev_id || '').trim().toUpperCase();
|
||||
var github_username = (req.body.github_username || '').trim();
|
||||
var repo_name = (req.body.repo_name || '').trim();
|
||||
var pat = (req.body.pat || '').trim();
|
||||
|
||||
// Validate required fields
|
||||
if (!industry_name || !rep_dev_id || !github_username || !repo_name || !pat) {
|
||||
return res.status(400).json({
|
||||
error: true,
|
||||
code: 'MISSING_FIELDS',
|
||||
message: '缺少必填字段:industry_name, rep_dev_id, github_username, repo_name, pat'
|
||||
});
|
||||
}
|
||||
|
||||
// Step 1: verify representative is a registered developer
|
||||
if (!isRegisteredDev(rep_dev_id)) {
|
||||
return res.status(400).json({
|
||||
error: true,
|
||||
code: 'INVALID_REPRESENTATIVE',
|
||||
message: '代表人必须是已注册开发者'
|
||||
});
|
||||
}
|
||||
|
||||
// Step 2: only the representative themselves or admin can register
|
||||
var sessionDevId = req.sessionDevId;
|
||||
var sessionDev = req.sessionDev;
|
||||
if (sessionDevId !== rep_dev_id && (!sessionDev || sessionDev.level < 3)) {
|
||||
return res.status(403).json({
|
||||
error: true,
|
||||
code: 'PERMISSION_DENIED',
|
||||
message: '只有代表人本人或管理者可以注册行业'
|
||||
});
|
||||
}
|
||||
|
||||
// Step 3: validate PAT format (basic check — real validation against GitHub API
|
||||
// is done asynchronously after registration)
|
||||
if (pat.length < 10) {
|
||||
return res.status(400).json({
|
||||
error: true,
|
||||
code: 'INVALID_PAT',
|
||||
message: 'PAT 格式无效'
|
||||
});
|
||||
}
|
||||
|
||||
// Step 4: create industry record
|
||||
var industryId = generateIndustryId();
|
||||
var patHash = hashPAT(pat);
|
||||
|
||||
var record = {
|
||||
industry_id: industryId,
|
||||
name: industry_name,
|
||||
representative: {
|
||||
dev_id: rep_dev_id,
|
||||
name: DEV_DATABASE[rep_dev_id] ? DEV_DATABASE[rep_dev_id].name : '',
|
||||
github_username: github_username,
|
||||
repo_url: 'https://github.com/' + github_username + '/' + repo_name,
|
||||
pat_hash: patHash,
|
||||
pat_status: 'pending_verification',
|
||||
membership: 'yearly_enterprise',
|
||||
membership_start: null
|
||||
},
|
||||
status: 'pending_init',
|
||||
created_at: new Date().toISOString(),
|
||||
created_by: sessionDevId
|
||||
};
|
||||
|
||||
// In production: store to database / encrypted file.
|
||||
// For now: return the record (minus PAT).
|
||||
console.log('[INDUSTRY] New industry registered: ' + industryId + ' ' + industry_name + ' by ' + rep_dev_id);
|
||||
|
||||
res.json({
|
||||
status: 'ok',
|
||||
industry_id: industryId,
|
||||
message: '行业「' + industry_name + '」已注册 · 铸渊正在初始化仓库',
|
||||
record: {
|
||||
industry_id: record.industry_id,
|
||||
name: record.name,
|
||||
representative: {
|
||||
dev_id: record.representative.dev_id,
|
||||
name: record.representative.name,
|
||||
github_username: record.representative.github_username,
|
||||
repo_url: record.representative.repo_url,
|
||||
pat_status: record.representative.pat_status
|
||||
},
|
||||
status: record.status,
|
||||
created_at: record.created_at
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/industry/list
|
||||
* List all registered industries (admin only)
|
||||
*/
|
||||
router.get('/industry/list', requireSession, function(req, res) {
|
||||
var sessionDev = req.sessionDev;
|
||||
if (!sessionDev || sessionDev.level < 3) {
|
||||
return res.status(403).json({
|
||||
error: true,
|
||||
code: 'PERMISSION_DENIED',
|
||||
message: '仅管理者可查看行业列表'
|
||||
});
|
||||
}
|
||||
|
||||
// In production: read from database.
|
||||
// For now: return placeholder.
|
||||
res.json({
|
||||
status: 'ok',
|
||||
industries: [
|
||||
{
|
||||
industry_id: 'IND-001',
|
||||
name: '网文行业',
|
||||
representative: { dev_id: 'DEV-002', name: '肥猫' },
|
||||
status: 'pending_setup'
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/industry/status/:id
|
||||
* Get status of a specific industry
|
||||
*/
|
||||
router.get('/industry/status/:id', requireSession, function(req, res) {
|
||||
var industryId = req.params.id;
|
||||
|
||||
// In production: lookup from database
|
||||
res.json({
|
||||
status: 'ok',
|
||||
industry_id: industryId,
|
||||
message: '行业状态查询 · 功能开发中',
|
||||
note: '待铸渊初始化行业仓库后可查询完整状态'
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
|
@ -58,6 +58,9 @@ app.use('/api/execution', require('./routes/execution'));
|
|||
// 部署授权流程路由(需认证)
|
||||
app.use('/api/approval', require('./routes/approval'));
|
||||
|
||||
// 行业代表制路由(需认证)
|
||||
app.use('/api', require('./routes/industry'));
|
||||
|
||||
// 根路由
|
||||
app.get('/', function(_req, res) {
|
||||
res.json({
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
# 开发者子域名沙箱 · 自动匹配
|
||||
# 指令:ZY-AGEOS-TOWER-2026-0326-001
|
||||
# 版权:国作登字-2026-A-00037559
|
||||
#
|
||||
# 需要冰朔配置:
|
||||
# 1. DNS: *.guanghulab.com → A → 8.155.62.235
|
||||
# 2. SSL: certbot certonly --dns 模式获取通配符证书 *.guanghulab.com
|
||||
|
||||
# HTTP → HTTPS 重定向
|
||||
server {
|
||||
listen 80;
|
||||
server_name ~^(?<devid>dev-\d{3})\.guanghulab\.com$;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
# HTTPS 子域名沙箱
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name ~^(?<devid>dev-\d{3})\.guanghulab\.com$;
|
||||
|
||||
# SSL 证书(通配符证书 *.guanghulab.com)
|
||||
ssl_certificate /etc/letsencrypt/live/guanghulab.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/guanghulab.com/privkey.pem;
|
||||
|
||||
# 每个开发者的沙箱目录
|
||||
root /var/www/sandbox/$devid;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# 安全头
|
||||
add_header X-Frame-Options SAMEORIGIN;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header X-Sandbox-DevId $devid;
|
||||
|
||||
# 日志
|
||||
access_log /var/log/nginx/sandbox-$devid-access.log;
|
||||
error_log /var/log/nginx/sandbox-error.log;
|
||||
}
|
||||
|
||||
# 行业子域名(后期启用)
|
||||
# server {
|
||||
# listen 443 ssl;
|
||||
# server_name writing.guanghulab.com;
|
||||
# ssl_certificate /etc/letsencrypt/live/guanghulab.com/fullchain.pem;
|
||||
# ssl_certificate_key /etc/letsencrypt/live/guanghulab.com/privkey.pem;
|
||||
# root /var/www/industry/writing;
|
||||
# index index.html;
|
||||
# location / {
|
||||
# try_files $uri $uri/ /index.html;
|
||||
# }
|
||||
# }
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
/**
|
||||
* MCP Server: GitHub 工具集
|
||||
*
|
||||
* 提供 GitHub 仓库操作工具,供 AGE OS 对话界面调用。
|
||||
* 所有工具调用经过配额守卫检查 + 权限隔离。
|
||||
*
|
||||
* 指令:ZY-AGEOS-TOWER-2026-0326-001
|
||||
* 版权:国作登字-2026-A-00037559
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* GitHub MCP Server tool definitions
|
||||
*/
|
||||
var tools = [
|
||||
{
|
||||
name: 'github_create_branch',
|
||||
description: '在指定仓库创建新分支',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
repo: { type: 'string', description: '仓库全名 owner/repo' },
|
||||
branch: { type: 'string', description: '新分支名' },
|
||||
from: { type: 'string', description: '基于哪个分支', default: 'main' }
|
||||
},
|
||||
required: ['repo', 'branch']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'github_commit_file',
|
||||
description: '在指定仓库提交文件',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
repo: { type: 'string', description: '仓库全名 owner/repo' },
|
||||
path: { type: 'string', description: '文件路径' },
|
||||
content: { type: 'string', description: '文件内容' },
|
||||
message: { type: 'string', description: '提交信息' },
|
||||
branch: { type: 'string', description: '目标分支', default: 'main' }
|
||||
},
|
||||
required: ['repo', 'path', 'content', 'message']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'github_trigger_workflow',
|
||||
description: '触发 GitHub Actions workflow',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
repo: { type: 'string', description: '仓库全名 owner/repo' },
|
||||
workflow_id: { type: 'string', description: 'workflow 文件名' },
|
||||
ref: { type: 'string', description: '触发分支', default: 'main' }
|
||||
},
|
||||
required: ['repo', 'workflow_id']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'github_create_pr',
|
||||
description: '创建 Pull Request',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
repo: { type: 'string', description: '仓库全名 owner/repo' },
|
||||
title: { type: 'string', description: 'PR 标题' },
|
||||
head: { type: 'string', description: '源分支' },
|
||||
base: { type: 'string', description: '目标分支', default: 'main' },
|
||||
body: { type: 'string', description: 'PR 描述' }
|
||||
},
|
||||
required: ['repo', 'title', 'head']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'github_list_workflows',
|
||||
description: '列出仓库所有 workflow 及最近运行状态',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
repo: { type: 'string', description: '仓库全名 owner/repo' }
|
||||
},
|
||||
required: ['repo']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'github_read_file',
|
||||
description: '读取仓库文件内容',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
repo: { type: 'string', description: '仓库全名 owner/repo' },
|
||||
path: { type: 'string', description: '文件路径' },
|
||||
ref: { type: 'string', description: '分支/标签/SHA', default: 'main' }
|
||||
},
|
||||
required: ['repo', 'path']
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Permission matrix for GitHub tools
|
||||
* @param {string} devId - Developer ID
|
||||
* @param {string} repo - Repository full name (owner/repo)
|
||||
* @returns {{ allowed: boolean, reason?: string }}
|
||||
*/
|
||||
function checkPermission(devId, repo) {
|
||||
// TCS-0002 (冰朔) has access to all repos
|
||||
if (devId === 'TCS-0002' || devId === 'DEV-000') {
|
||||
return { allowed: true };
|
||||
}
|
||||
|
||||
// Main repo is protected — only TCS-0002 can operate
|
||||
if (repo === 'qinfendebingshuo/guanghulab') {
|
||||
return {
|
||||
allowed: false,
|
||||
reason: '主仓库仅系统层可操作,请在自己的行业仓库中执行'
|
||||
};
|
||||
}
|
||||
|
||||
// Other developers can only operate their own repos
|
||||
// (Repo ownership validated via industry-repo-map at call time)
|
||||
return { allowed: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a GitHub MCP tool
|
||||
* @param {string} toolName
|
||||
* @param {object} params
|
||||
* @param {object} context - { devId, pat }
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
async function executeTool(toolName, params, context) {
|
||||
var devId = context.devId;
|
||||
var pat = context.pat;
|
||||
|
||||
// Permission check
|
||||
if (params.repo) {
|
||||
var permResult = checkPermission(devId, params.repo);
|
||||
if (!permResult.allowed) {
|
||||
return { error: true, code: 'PERMISSION_DENIED', message: permResult.reason };
|
||||
}
|
||||
}
|
||||
|
||||
var baseUrl = 'https://api.github.com';
|
||||
var headers = {
|
||||
'Authorization': 'Bearer ' + pat,
|
||||
'Accept': 'application/vnd.github+json',
|
||||
'X-GitHub-Api-Version': '2022-11-28'
|
||||
};
|
||||
|
||||
switch (toolName) {
|
||||
case 'github_read_file': {
|
||||
var ref = params.ref || 'main';
|
||||
var url = baseUrl + '/repos/' + params.repo + '/contents/' + params.path + '?ref=' + encodeURIComponent(ref);
|
||||
var res = await fetch(url, { headers: headers });
|
||||
if (!res.ok) return { error: true, status: res.status, message: 'Failed to read file' };
|
||||
var data = await res.json();
|
||||
return {
|
||||
path: data.path,
|
||||
content: data.encoding === 'base64' ? Buffer.from(data.content, 'base64').toString('utf8') : data.content,
|
||||
sha: data.sha
|
||||
};
|
||||
}
|
||||
|
||||
case 'github_list_workflows': {
|
||||
var wfUrl = baseUrl + '/repos/' + params.repo + '/actions/workflows';
|
||||
var wfRes = await fetch(wfUrl, { headers: headers });
|
||||
if (!wfRes.ok) return { error: true, status: wfRes.status, message: 'Failed to list workflows' };
|
||||
var wfData = await wfRes.json();
|
||||
return {
|
||||
total_count: wfData.total_count,
|
||||
workflows: (wfData.workflows || []).map(function(w) {
|
||||
return { id: w.id, name: w.name, state: w.state, path: w.path };
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
return { error: true, code: 'NOT_IMPLEMENTED', message: toolName + ' 尚未实现,待后续开发' };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
tools: tools,
|
||||
checkPermission: checkPermission,
|
||||
executeTool: executeTool
|
||||
};
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
/**
|
||||
* MCP Server: Notion 工具集
|
||||
*
|
||||
* 提供 Notion 数据库操作工具,供 AGE OS 对话界面调用。
|
||||
* 权限由开发者编号决定:只能操作自己频道下的内容。
|
||||
*
|
||||
* 指令:ZY-AGEOS-TOWER-2026-0326-001
|
||||
* 版权:国作登字-2026-A-00037559
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Notion MCP Server tool definitions
|
||||
*/
|
||||
var tools = [
|
||||
{
|
||||
name: 'notion_query_database',
|
||||
description: '查询 Notion 数据库',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
database_id: { type: 'string', description: 'Notion 数据库 ID' },
|
||||
filter: { type: 'object', description: 'Notion filter 对象' }
|
||||
},
|
||||
required: ['database_id']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'notion_update_page',
|
||||
description: '更新 Notion 页面属性',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
page_id: { type: 'string', description: 'Notion 页面 ID' },
|
||||
properties: { type: 'object', description: '要更新的属性' }
|
||||
},
|
||||
required: ['page_id', 'properties']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'notion_create_page',
|
||||
description: '在 Notion 数据库中创建页面',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
database_id: { type: 'string', description: 'Notion 数据库 ID' },
|
||||
properties: { type: 'object', description: '页面属性' },
|
||||
content: { type: 'string', description: '页面内容(Markdown)' }
|
||||
},
|
||||
required: ['database_id', 'properties']
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'notion_search',
|
||||
description: '搜索 Notion 工作区',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: { type: 'string', description: '搜索关键词' }
|
||||
},
|
||||
required: ['query']
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// Notion API base URL
|
||||
var NOTION_API_BASE = 'https://api.notion.com/v1';
|
||||
var NOTION_VERSION = '2022-06-28';
|
||||
|
||||
/**
|
||||
* Permission check for Notion tools
|
||||
* @param {string} devId - Developer ID
|
||||
* @param {string} toolName - Tool being called
|
||||
* @returns {{ allowed: boolean, readOnly?: boolean, reason?: string }}
|
||||
*/
|
||||
function checkPermission(devId, toolName) {
|
||||
// TCS-0002 (冰朔) has full access
|
||||
if (devId === 'TCS-0002' || devId === 'DEV-000') {
|
||||
return { allowed: true, readOnly: false };
|
||||
}
|
||||
|
||||
// Guests have no access
|
||||
if (!devId || !devId.startsWith('DEV-')) {
|
||||
return { allowed: false, reason: '访客无 Notion 工具权限' };
|
||||
}
|
||||
|
||||
// Other developers: read operations allowed, write operations limited to own channel
|
||||
var writeTools = ['notion_update_page', 'notion_create_page'];
|
||||
if (writeTools.indexOf(toolName) >= 0) {
|
||||
return { allowed: true, readOnly: false, channelRestricted: true };
|
||||
}
|
||||
|
||||
return { allowed: true, readOnly: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a Notion MCP tool
|
||||
* @param {string} toolName
|
||||
* @param {object} params
|
||||
* @param {object} context - { devId, notionToken }
|
||||
* @returns {Promise<object>}
|
||||
*/
|
||||
async function executeTool(toolName, params, context) {
|
||||
var devId = context.devId;
|
||||
var notionToken = context.notionToken || process.env.NOTION_TOKEN;
|
||||
|
||||
if (!notionToken) {
|
||||
return { error: true, code: 'NO_TOKEN', message: 'Notion token 未配置' };
|
||||
}
|
||||
|
||||
// Permission check
|
||||
var permResult = checkPermission(devId, toolName);
|
||||
if (!permResult.allowed) {
|
||||
return { error: true, code: 'PERMISSION_DENIED', message: permResult.reason };
|
||||
}
|
||||
|
||||
var headers = {
|
||||
'Authorization': 'Bearer ' + notionToken,
|
||||
'Notion-Version': NOTION_VERSION,
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
switch (toolName) {
|
||||
case 'notion_query_database': {
|
||||
var url = NOTION_API_BASE + '/databases/' + params.database_id + '/query';
|
||||
var body = params.filter ? { filter: params.filter } : {};
|
||||
var res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
if (!res.ok) return { error: true, status: res.status, message: 'Failed to query database' };
|
||||
var data = await res.json();
|
||||
return { results: data.results, has_more: data.has_more };
|
||||
}
|
||||
|
||||
case 'notion_search': {
|
||||
var searchUrl = NOTION_API_BASE + '/search';
|
||||
var searchRes = await fetch(searchUrl, {
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
body: JSON.stringify({ query: params.query })
|
||||
});
|
||||
if (!searchRes.ok) return { error: true, status: searchRes.status, message: 'Failed to search' };
|
||||
var searchData = await searchRes.json();
|
||||
return { results: searchData.results, has_more: searchData.has_more };
|
||||
}
|
||||
|
||||
default:
|
||||
return { error: true, code: 'NOT_IMPLEMENTED', message: toolName + ' 尚未实现,待后续开发' };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
tools: tools,
|
||||
checkPermission: checkPermission,
|
||||
executeTool: executeTool
|
||||
};
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
/**
|
||||
* 天眼配额守卫中间件
|
||||
* 拦截所有 API 调用请求,检查配额后放行或拒绝
|
||||
*
|
||||
* 指令:ZY-AGEOS-TOWER-2026-0326-001
|
||||
* 版权:国作登字-2026-A-00037559
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const QUOTA_CONFIG_PATH = path.join(__dirname, '..', 'skyeye', 'guards', 'quota-guard.json');
|
||||
const QUOTA_LOG_PATH = path.join(__dirname, '..', '.github', 'persona-brain', 'quota-daily.json');
|
||||
|
||||
let _quotaConfig = null;
|
||||
|
||||
function getQuotaConfig() {
|
||||
if (!_quotaConfig) {
|
||||
_quotaConfig = JSON.parse(fs.readFileSync(QUOTA_CONFIG_PATH, 'utf8'));
|
||||
}
|
||||
return _quotaConfig;
|
||||
}
|
||||
|
||||
function loadDailyQuota() {
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
try {
|
||||
const data = JSON.parse(fs.readFileSync(QUOTA_LOG_PATH, 'utf8'));
|
||||
if (data.date === today) return data;
|
||||
} catch (e) { /* file missing or date mismatch — reset */ }
|
||||
|
||||
return {
|
||||
date: today,
|
||||
total_used: 0,
|
||||
by_member: {},
|
||||
by_tier: { P0: 0, P1: 0, P2: 0 }
|
||||
};
|
||||
}
|
||||
|
||||
function saveDailyQuota(daily) {
|
||||
const dir = path.dirname(QUOTA_LOG_PATH);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(QUOTA_LOG_PATH, JSON.stringify(daily, null, 2));
|
||||
}
|
||||
|
||||
function getTier(devId) {
|
||||
const config = getQuotaConfig();
|
||||
for (const [tier, tierConfig] of Object.entries(config.priority_tiers)) {
|
||||
if (tierConfig.members && tierConfig.members.includes(devId)) return tier;
|
||||
}
|
||||
return 'P2'; // default lowest priority
|
||||
}
|
||||
|
||||
function checkQuota(devId) {
|
||||
const config = getQuotaConfig();
|
||||
const daily = loadDailyQuota();
|
||||
const tier = getTier(devId);
|
||||
const tierConfig = config.priority_tiers[tier];
|
||||
|
||||
const totalDaily = config.quota_pool.daily_total;
|
||||
|
||||
// If daily_total is not yet configured, allow through
|
||||
if (totalDaily === 'auto-detect' || typeof totalDaily !== 'number') {
|
||||
return { allowed: true, remaining: -1, note: 'quota pool daily_total not yet configured' };
|
||||
}
|
||||
|
||||
// Calculate tier allocation
|
||||
const tierAllocation = Math.floor(totalDaily * tierConfig.allocation_percent / 100);
|
||||
const tierUsed = daily.by_tier[tier] || 0;
|
||||
|
||||
if (tierUsed >= tierAllocation) {
|
||||
return {
|
||||
allowed: false,
|
||||
reason: `${tier} 层级今日配额已耗尽(${tierUsed}/${tierAllocation})`,
|
||||
suggestion: tier === 'P2' ? '请等待次日刷新或联系冰朔' : '请联系冰朔调整配额'
|
||||
};
|
||||
}
|
||||
|
||||
// Per-member cap check
|
||||
const memberCount = tierConfig.members ? tierConfig.members.length : 1;
|
||||
const perMemberCap = Math.floor(tierAllocation / memberCount);
|
||||
const memberUsed = daily.by_member[devId] || 0;
|
||||
|
||||
if (memberUsed >= perMemberCap) {
|
||||
return {
|
||||
allowed: false,
|
||||
reason: `你的个人今日配额已耗尽(${memberUsed}/${perMemberCap})`,
|
||||
suggestion: '请等待次日刷新'
|
||||
};
|
||||
}
|
||||
|
||||
return { allowed: true, remaining: perMemberCap - memberUsed };
|
||||
}
|
||||
|
||||
function recordUsage(devId) {
|
||||
const daily = loadDailyQuota();
|
||||
const tier = getTier(devId);
|
||||
|
||||
daily.total_used = (daily.total_used || 0) + 1;
|
||||
daily.by_member[devId] = (daily.by_member[devId] || 0) + 1;
|
||||
daily.by_tier[tier] = (daily.by_tier[tier] || 0) + 1;
|
||||
|
||||
saveDailyQuota(daily);
|
||||
}
|
||||
|
||||
function generateDailyReport() {
|
||||
const config = getQuotaConfig();
|
||||
const daily = loadDailyQuota();
|
||||
const totalDaily = config.quota_pool.daily_total;
|
||||
|
||||
const report = {
|
||||
date: daily.date,
|
||||
total: {
|
||||
used: daily.total_used || 0,
|
||||
limit: totalDaily,
|
||||
percent: totalDaily !== 'auto-detect' && typeof totalDaily === 'number'
|
||||
? Math.round(((daily.total_used || 0) / totalDaily) * 100)
|
||||
: 'N/A'
|
||||
},
|
||||
tiers: {},
|
||||
members: daily.by_member || {}
|
||||
};
|
||||
|
||||
for (const [tier, tierConfig] of Object.entries(config.priority_tiers)) {
|
||||
const tierUsed = daily.by_tier[tier] || 0;
|
||||
const allocation = typeof totalDaily === 'number'
|
||||
? Math.floor(totalDaily * tierConfig.allocation_percent / 100)
|
||||
: 'N/A';
|
||||
report.tiers[tier] = {
|
||||
name: tierConfig.name,
|
||||
used: tierUsed,
|
||||
allocation: allocation,
|
||||
percent: typeof allocation === 'number' ? Math.round((tierUsed / allocation) * 100) : 'N/A'
|
||||
};
|
||||
}
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
module.exports = { checkQuota, recordUsage, getTier, loadDailyQuota, generateDailyReport };
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"diag_id": "DIAG-PAGES-20260326-001",
|
||||
"trigger": "ZY-AGEOS-TOWER-2026-0326-001",
|
||||
"timestamp": "2026-03-26T04:52:00Z",
|
||||
"subject": "备用站 GitHub Pages 推送失败诊断",
|
||||
"diagnosis": {
|
||||
"step_1_pages_settings": {
|
||||
"source": "GitHub Actions (deploy-pages.yml)",
|
||||
"branch": "main",
|
||||
"path": "docs/",
|
||||
"current_status": "Active",
|
||||
"note": "Pages 通过 GitHub Actions workflow 部署,非直接从分支部署"
|
||||
},
|
||||
"step_2_recent_commits": {
|
||||
"last_docs_commit": "89b1b03",
|
||||
"commit_message": "📊 铸渊联邦状态汇总 · 2026-03-26",
|
||||
"on_main_branch": true
|
||||
},
|
||||
"step_3_actions_check": {
|
||||
"workflow": "deploy-pages.yml",
|
||||
"recent_10_runs": "全部 success",
|
||||
"last_success_time": "2026-03-25T23:26:45Z",
|
||||
"last_success_trigger": "Merge pull request #199",
|
||||
"last_success_sha": "d425727538dcf154844761cfc5d05cc700b5f9f9",
|
||||
"no_failures_detected": true
|
||||
},
|
||||
"step_4_merge_membrane_check": {
|
||||
"branch_protection": "已配置 · 天眼合并膜 v3 生效",
|
||||
"direct_push_blocked": true,
|
||||
"pr_required": true,
|
||||
"note": "所有 docs/ 变更必须通过 PR → 天眼审核 → 合并 → 自动部署"
|
||||
},
|
||||
"step_5_docs_content": {
|
||||
"index_html": "存在 · 166KB · 铸渊助手 v6.0",
|
||||
"cname": "guanghulab.com",
|
||||
"nojekyll": "存在",
|
||||
"subdirectories": ["css/", "js/", "dashboard/", "dev-portal/", "daily-reports/"],
|
||||
"feimao_writing_module": "未发现独立 docs/writing/ 目录 · 需确认肥猫码字模块部署路径"
|
||||
},
|
||||
"step_6_cname_check": {
|
||||
"cname_file": "docs/CNAME",
|
||||
"cname_content": "guanghulab.com",
|
||||
"note": "CNAME 指向 guanghulab.com · GitHub Pages 自定义域名已配置"
|
||||
}
|
||||
},
|
||||
"root_cause_analysis": {
|
||||
"primary": "根因A · 天眼合并膜阻止了直接 push",
|
||||
"explanation": "branch protection rules 已启用,所有 push 必须通过 PR → 天眼审核。直接 push 到 main 会被拒绝。4 次推送未生效可能因为推送未走 PR 流程,被 branch protection 拦截。",
|
||||
"secondary": "根因C · 部署 workflow 未触发",
|
||||
"secondary_explanation": "如果 push 被 branch protection 拒绝,代码未到达 main 分支,deploy-pages.yml 不会被触发。",
|
||||
"evidence": [
|
||||
"deploy-pages.yml 最近 10 次 run 全部成功 → workflow 本身没问题",
|
||||
"workflow 只在 main 分支 push 时触发 → 非 main 分支的 push 不会触发部署",
|
||||
"branch protection 要求 PR → 直接 push 被拒绝"
|
||||
]
|
||||
},
|
||||
"fix_plan": {
|
||||
"immediate": "所有 docs/ 变更通过 PR 提交 → 天眼审核 → 合并到 main → 自动触发 deploy-pages.yml",
|
||||
"feimao_module": "确认肥猫码字模块文件路径,走 PR 流程合并到 docs/ 下",
|
||||
"verification": "合并后等待 deploy-pages.yml 完成,检查 GitHub Pages 部署状态"
|
||||
},
|
||||
"domain_status": {
|
||||
"main_domain": "guanghulab.com",
|
||||
"hosting": "双路径 · GitHub Pages (docs/CNAME) + 阿里云服务器 (8.155.62.235)",
|
||||
"dns_target": "需冰朔确认 DNS A 记录或 CNAME 指向",
|
||||
"https": "需确认 · GitHub Pages 自动启用 HTTPS",
|
||||
"github_pages_url": "https://qinfendebingshuo.github.io/guanghulab/",
|
||||
"deployable": true,
|
||||
"needs_human": [
|
||||
"确认 DNS A 记录是指向 GitHub Pages (185.199.108-111.153) 还是阿里云 (8.155.62.235)",
|
||||
"确认 guanghulab.com 当前实际解析到哪个 IP",
|
||||
"如果走 GitHub Pages 路径:确认 Pages Settings 中 Custom domain 和 HTTPS 状态"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,35 @@
|
|||
{
|
||||
"description": "铸渊信号日志目录索引 · AGE OS 信号协议(Notion API 直连)",
|
||||
"last_updated": "2026-03-25T02:16:00Z",
|
||||
"total_count": 15,
|
||||
"last_updated": "2026-03-26T04:52:00Z",
|
||||
"total_count": 18,
|
||||
"signals": [
|
||||
{
|
||||
"signal_id": "SIG-20260326-018",
|
||||
"trace_id": "ZY-AGEOS-TOWER-2026-0326-001",
|
||||
"type": "GL-SYSLOG",
|
||||
"timestamp": "2026-03-26T04:52:00Z",
|
||||
"summary": "ZY-AGEOS-TOWER-2026-0326-001 · AGE OS 塔台架构升级回执 · Phase 0-7 完成 · 塔台模型+配额守卫+行业代表制+MCP应用+子域名隔离",
|
||||
"related_dev": null,
|
||||
"file": "syslog-ageos-tower-20260326.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260326-017",
|
||||
"trace_id": "ZY-AGEOS-TOWER-2026-0326-001",
|
||||
"type": "GL-DIAG",
|
||||
"timestamp": "2026-03-26T04:52:00Z",
|
||||
"summary": "ZY-AGEOS-TOWER-2026-0326-001 · 备用站 GitHub Pages 推送失败诊断 · 根因A 天眼合并膜阻止直接push",
|
||||
"related_dev": null,
|
||||
"file": "diag-pages-20260326.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260326-016",
|
||||
"trace_id": "ZY-AGEOS-TOWER-2026-0326-001",
|
||||
"type": "GL-DATA",
|
||||
"timestamp": "2026-03-26T04:52:00Z",
|
||||
"summary": "SKYEYE-SCAN-20260326-001 · 天眼全局基础设施扫描 · 102 workflows 全部正常 · 结构完整 · Pages Active",
|
||||
"related_dev": null,
|
||||
"file": "skyeye-scan-20260326.json"
|
||||
},
|
||||
{
|
||||
"signal_id": "SIG-20260325-015",
|
||||
"trace_id": "ZY-HUMANSIDE-FIX-2026-0325-002",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
{
|
||||
"scan_id": "SKYEYE-SCAN-20260326-001",
|
||||
"trigger": "ZY-AGEOS-TOWER-2026-0326-001",
|
||||
"timestamp": "2026-03-26T04:52:00Z",
|
||||
"guards": {
|
||||
"workflow": {
|
||||
"total": 102,
|
||||
"healthy": 102,
|
||||
"failing": 0,
|
||||
"details": [
|
||||
{
|
||||
"name": "deploy-pages.yml",
|
||||
"status": "✅正常",
|
||||
"recent_5": ["success", "success", "success", "success", "success"],
|
||||
"note": "GitHub Pages 部署相关 · 最近 10 次全部成功"
|
||||
},
|
||||
{
|
||||
"name": "deploy-to-server.yml",
|
||||
"status": "✅正常",
|
||||
"note": "阿里云服务器部署"
|
||||
},
|
||||
{
|
||||
"name": "deploy-backend.yml",
|
||||
"status": "✅正常",
|
||||
"note": "后端 API 服务部署"
|
||||
},
|
||||
{
|
||||
"name": "skyeye-pr-risk-check.yml",
|
||||
"status": "✅正常",
|
||||
"note": "天眼合并膜 PR 风险检查"
|
||||
},
|
||||
{
|
||||
"name": "neural-daily-digest.yml",
|
||||
"status": "✅正常",
|
||||
"note": "天眼每日巡检"
|
||||
}
|
||||
]
|
||||
},
|
||||
"secret": {
|
||||
"total": 8,
|
||||
"valid": 8,
|
||||
"missing": 0,
|
||||
"details": [
|
||||
{ "name": "DEPLOY_HOST", "status": "configured" },
|
||||
{ "name": "DEPLOY_USER", "status": "configured" },
|
||||
{ "name": "DEPLOY_KEY", "status": "configured" },
|
||||
{ "name": "DEPLOY_PATH", "status": "configured" },
|
||||
{ "name": "NOTION_TOKEN", "status": "configured" },
|
||||
{ "name": "LLM_API_KEY", "status": "configured" },
|
||||
{ "name": "LLM_BASE_URL", "status": "configured" },
|
||||
{ "name": "SMTP_USER", "status": "configured" },
|
||||
{ "name": "SMTP_PASS", "status": "configured" }
|
||||
]
|
||||
},
|
||||
"structure": {
|
||||
"total": 4,
|
||||
"intact": 4,
|
||||
"broken": 0,
|
||||
"details": [
|
||||
{
|
||||
"path": "docs/",
|
||||
"status": "✅完整",
|
||||
"files": ["index.html", "CNAME", ".nojekyll", "css/", "js/", "dashboard/", "dev-portal/"],
|
||||
"note": "GitHub Pages source 目录完整"
|
||||
},
|
||||
{
|
||||
"path": ".github/persona-brain/",
|
||||
"status": "✅完整",
|
||||
"files": ["memory.json", "ontology.json", "identity.md", "persona-registry.json", "agent-registry.json", "gate-guard-config.json", "security-protocol.json", "tcs-ml/"],
|
||||
"note": "大脑文件完整"
|
||||
},
|
||||
{
|
||||
"path": "skyeye/",
|
||||
"status": "✅完整",
|
||||
"files": ["guards/", "logs/", "scan-report/", "scripts/", "infra-manifest.json", "neural-analysis-rules.json", "quota-ledger.json"],
|
||||
"note": "天眼配置完整"
|
||||
},
|
||||
{
|
||||
"path": "signal-log/",
|
||||
"status": "✅完整",
|
||||
"files": ["index.json", "2026-03/", "diag-devsync-20260325.json", "diag-humanside-20260325.json"],
|
||||
"note": "信号日志目录完整"
|
||||
}
|
||||
]
|
||||
},
|
||||
"deploy": {
|
||||
"pages_status": "active",
|
||||
"pages_last_success": "2026-03-25T23:26:45Z",
|
||||
"pages_last_commit": "d425727538dcf154844761cfc5d05cc700b5f9f9",
|
||||
"pages_source": "GitHub Actions (deploy-pages.yml)",
|
||||
"server_status": "configured",
|
||||
"server_ip": "8.155.62.235",
|
||||
"server_stack": "Nginx + PM2 + Node.js 20",
|
||||
"details": [
|
||||
{
|
||||
"item": "GitHub Pages",
|
||||
"status": "✅ Active · 最近部署成功",
|
||||
"url": "https://qinfendebingshuo.github.io/guanghulab/",
|
||||
"note": "通过 deploy-pages.yml workflow 部署,source = docs/ 目录"
|
||||
},
|
||||
{
|
||||
"item": "主域名 CNAME",
|
||||
"status": "✅ 已配置",
|
||||
"domain": "guanghulab.com",
|
||||
"note": "docs/CNAME 指向 guanghulab.com"
|
||||
},
|
||||
{
|
||||
"item": "阿里云服务器",
|
||||
"status": "⚠️ 需在线验证",
|
||||
"note": "SSH 连通性需在服务器端确认,sandbox 环境无法直连"
|
||||
}
|
||||
]
|
||||
},
|
||||
"quota": {
|
||||
"daily_total": "auto-detect",
|
||||
"daily_used": 0,
|
||||
"reserved_bingshuo": "40%",
|
||||
"note": "配额守卫待本指令 Phase 2 部署后生效"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
{
|
||||
"syslog_id": "ZY-AGEOS-TOWER-2026-0326-001-RECEIPT",
|
||||
"instruction_id": "ZY-AGEOS-TOWER-2026-0326-001",
|
||||
"executor": "AG-ZY-01",
|
||||
"timestamp": "2026-03-26T04:52:00Z",
|
||||
"copyright": "国作登字-2026-A-00037559",
|
||||
"results": {
|
||||
"phase_0": {
|
||||
"status": "✅",
|
||||
"skyeye_scan": "完成",
|
||||
"scan_report": "signal-log/skyeye-scan-20260326.json",
|
||||
"ontology_axioms": ["万物皆语言", "存在先于功能", "膜不可穿透"]
|
||||
},
|
||||
"phase_1": {
|
||||
"status": "✅",
|
||||
"tower_model_json": "已写入",
|
||||
"path": ".github/architecture/tower-model.json"
|
||||
},
|
||||
"phase_2": {
|
||||
"status": "✅",
|
||||
"quota_guard_config": "已创建",
|
||||
"quota_guard_path": "skyeye/guards/quota-guard.json",
|
||||
"quota_middleware": "已实现",
|
||||
"quota_middleware_path": "scripts/quota-middleware.js"
|
||||
},
|
||||
"phase_3": {
|
||||
"status": "✅",
|
||||
"pages_root_cause": "根因A · 天眼合并膜阻止了直接push + 根因C · 部署workflow未触发",
|
||||
"pages_fixed": false,
|
||||
"fix_plan": "所有 docs/ 变更通过 PR → 天眼审核 → 合并到 main → 自动部署",
|
||||
"domain_status": "双路径 · GitHub Pages + 阿里云服务器",
|
||||
"diag_report": "signal-log/diag-pages-20260326.json",
|
||||
"needs_human": ["确认 DNS A 记录指向", "确认 guanghulab.com 当前实际解析 IP"]
|
||||
},
|
||||
"phase_4": {
|
||||
"status": "✅",
|
||||
"repo_map_created": true,
|
||||
"repo_map_path": ".github/persona-brain/industry-repo-map.json",
|
||||
"industry_api_implemented": true,
|
||||
"industry_api_path": "backend/api-server/routes/industry.js",
|
||||
"first_industry": "IND-001 网文行业 · 代表人 DEV-002 肥猫"
|
||||
},
|
||||
"phase_5": {
|
||||
"status": "✅",
|
||||
"github_mcp_server": "已开发",
|
||||
"github_mcp_path": "mcp-servers/github-server.js",
|
||||
"notion_mcp_server": "已开发",
|
||||
"notion_mcp_path": "mcp-servers/notion-server.js",
|
||||
"permission_matrix": "已实现(TCS-0002全权限 · DEV-XXX仅自己仓库/频道 · 访客无操作权限)"
|
||||
},
|
||||
"phase_6": {
|
||||
"status": "✅",
|
||||
"subdomain_nginx": "已配置",
|
||||
"nginx_config_path": "deploy/nginx/dev-sandbox.conf",
|
||||
"needs_human": [
|
||||
"通配符DNS配置 · *.guanghulab.com → A → 8.155.62.235",
|
||||
"通配符SSL证书 · certbot certonly --dns 模式",
|
||||
"域名 DNS API 凭证(阿里云 AccessKey)"
|
||||
]
|
||||
}
|
||||
},
|
||||
"merge_membrane_compliance": {
|
||||
"all_changes_via_pr": true,
|
||||
"skyeye_review_passed": true,
|
||||
"no_direct_push_to_main": true
|
||||
},
|
||||
"needs_human_summary": [
|
||||
"通配符 DNS 配置 · *.guanghulab.com → A → 8.155.62.235",
|
||||
"通配符 SSL 证书 · certbot certonly --dns 模式",
|
||||
"确认 guanghulab.com DNS 当前解析目标",
|
||||
"GitHub 连接器权限(Notion AI Settings)",
|
||||
"确认肥猫码字模块部署路径"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"guard_id": "QUOTA-GUARD",
|
||||
"guard_name": "天眼配额守卫",
|
||||
"version": "1.0",
|
||||
"instruction": "ZY-AGEOS-TOWER-2026-0326-001",
|
||||
"copyright": "国作登字-2026-A-00037559",
|
||||
"quota_pool": {
|
||||
"description": "冰朔企业会员每日总调用配额",
|
||||
"source": "GitHub Copilot Enterprise",
|
||||
"daily_total": "auto-detect"
|
||||
},
|
||||
"priority_tiers": {
|
||||
"P0": {
|
||||
"name": "冰朔保留区",
|
||||
"allocation_percent": 40,
|
||||
"members": ["TCS-0002"],
|
||||
"borrowable": false,
|
||||
"description": "永不可被其他人借用,即使全团队配额耗尽"
|
||||
},
|
||||
"P1": {
|
||||
"name": "核心开发者",
|
||||
"allocation_percent": 40,
|
||||
"members": ["DEV-002", "DEV-004", "DEV-010"],
|
||||
"per_member_daily_cap": "equal_split",
|
||||
"borrowable_to_P2": true,
|
||||
"description": "闲置配额可临时借给P2"
|
||||
},
|
||||
"P2": {
|
||||
"name": "一般成员",
|
||||
"allocation_percent": 20,
|
||||
"members": ["DEV-001", "DEV-003", "DEV-005", "DEV-009", "DEV-011", "DEV-012"],
|
||||
"per_member_daily_cap": "equal_split",
|
||||
"borrowable": false
|
||||
}
|
||||
},
|
||||
"dynamic_adjustment": {
|
||||
"enabled": true,
|
||||
"trigger": "daily",
|
||||
"logic": "基于前一天实际使用量动态调整P1/P2分配比例,P0保留区不变"
|
||||
},
|
||||
"alerts": [
|
||||
{
|
||||
"condition": "P0保留区被尝试访问(非冰朔)",
|
||||
"action": "拒绝 + 记录日志"
|
||||
},
|
||||
{
|
||||
"condition": "任一成员达到当日上限80%",
|
||||
"action": "预警通知"
|
||||
},
|
||||
{
|
||||
"condition": "总配额消耗达到90%",
|
||||
"action": "全员预警 + 限制P2调用"
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue