guanghulab/server/age-os/mcp-server/tools/cos-persona-db-ops.js
Guanghu Domestic Migration d1e47f4565
Some checks are pending
自动更新代码和重启 / update-and-restart (push) Waiting to run
CI检查 + 自动部署 / check (push) Waiting to run
CI检查 + 自动部署 / deploy (push) Blocked by required conditions
重启聊天服务 / restart (push) Waiting to run
chore: import sanitized domestic snapshot for REPO-002
Source snapshot: ca48d3ddf926d79aa138306164169baf764bb829
2026-07-17 15:54:41 +08:00

429 lines
13 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* ═══════════════════════════════════════════════════════════
* 模块G · COS桶内自研数据库协议 MCP 工具
* ═══════════════════════════════════════════════════════════
*
* 签发: 铸渊 · ICE-GL-ZY001
* 版权: 国作登字-2026-A-00037559
*
* cos-persona-db 协议 — COS桶内的文件组织结构 = 人格体数据库
*
* 代码仓库侧结构:
* /zhuyuan/db/
* ├── index.json — 全局索引
* ├── code-modules/ — 代码模块结构化
* ├── brain-nodes/ — 认知节点
* └── training-data/ — 训练数据TCS格式
*
* Notion侧结构:
* /notion-db/
* ├── index.json — 全局索引
* ├── pages/ — 页面内容(按分类)
* ├── databases/ — 数据库结构
* └── training-data/ — 训练数据TCS格式
*
* 工具清单:
* cosDbInit — 初始化COS数据库结构
* cosDbGetIndex — 获取数据库全局索引
* cosDbUpdateIndex — 更新全局索引
* cosDbWriteEntry — 写入数据条目
* cosDbReadEntry — 读取数据条目
* cosDbListEntries — 列出数据条目
* cosDbDeleteEntry — 删除数据条目
* cosDbGetStats — 获取数据库统计信息
*/
'use strict';
const cos = require('../cos');
// ─── 数据库协议版本 ───
const COS_DB_VERSION = '1.0';
// ─── 数据库类型定义 ───
const DB_TYPES = {
'zhuyuan': {
root: 'zhuyuan/db',
description: '铸渊·代码仓库侧人格体大脑',
categories: ['code-modules', 'brain-nodes', 'training-data', 'directives', 'alerts']
},
'notion': {
root: 'notion-db',
description: 'Notion侧人格体大脑',
categories: ['pages', 'databases', 'training-data', 'workorders']
},
'team': {
root: 'team-hub',
description: '团队协作通信枢纽',
categories: ['workorders', 'reports', 'tasks', 'alerts']
},
'awen': {
root: 'awen-hub',
description: 'Awen技术主控通信桶',
categories: ['tasks', 'reports', 'progress', 'alerts']
}
};
/**
* cosDbInit — 初始化COS数据库结构
*
* input:
* bucket: string — 桶名
* db_type: string — 数据库类型zhuyuan/notion/team/awen
*/
async function cosDbInit(input) {
const { bucket, db_type } = input;
if (!bucket) throw new Error('缺少 bucket');
if (!db_type || !DB_TYPES[db_type]) {
throw new Error(`无效的 db_type可选: ${Object.keys(DB_TYPES).join(', ')}`);
}
const dbConfig = DB_TYPES[db_type];
const now = new Date().toISOString();
// 创建索引文件
const index = {
cos_db_version: COS_DB_VERSION,
db_type,
description: dbConfig.description,
root_path: dbConfig.root,
categories: dbConfig.categories,
created_at: now,
updated_at: now,
total_entries: 0,
category_counts: Object.fromEntries(dbConfig.categories.map(c => [c, 0])),
last_write: null,
sovereign: '冰朔 · TCS-0002∞',
guardian: '铸渊 · ICE-GL-ZY001',
copyright: '国作登字-2026-A-00037559'
};
await cos.write(bucket, `${dbConfig.root}/index.json`, JSON.stringify(index, null, 2), 'application/json');
// 创建各分类目录的.gitkeep文件
const writes = dbConfig.categories.map(category =>
cos.write(bucket, `${dbConfig.root}/${category}/.init`, JSON.stringify({
category,
created_at: now,
entries: 0
}), 'application/json')
);
await Promise.all(writes);
return {
status: 'initialized',
db_type,
bucket,
root: dbConfig.root,
categories: dbConfig.categories,
index_key: `${dbConfig.root}/index.json`
};
}
/**
* cosDbGetIndex — 获取数据库全局索引
*
* input:
* bucket: string — 桶名
* db_type: string — 数据库类型
*/
async function cosDbGetIndex(input) {
const { bucket, db_type } = input;
if (!bucket || !db_type) throw new Error('缺少 bucket 或 db_type');
const dbConfig = DB_TYPES[db_type];
if (!dbConfig) throw new Error(`无效的 db_type: ${db_type}`);
try {
const result = await cos.read(bucket, `${dbConfig.root}/index.json`);
return {
index: JSON.parse(result.content),
size_bytes: result.size_bytes
};
} catch {
return {
index: null,
error: '索引不存在,请先执行 cosDbInit 初始化'
};
}
}
/**
* cosDbUpdateIndex — 更新全局索引
*
* input:
* bucket: string — 桶名
* db_type: string — 数据库类型
* updates: object — 要更新的字段
*/
async function cosDbUpdateIndex(input) {
const { bucket, db_type, updates } = input;
if (!bucket || !db_type) throw new Error('缺少 bucket 或 db_type');
const dbConfig = DB_TYPES[db_type];
if (!dbConfig) throw new Error(`无效的 db_type: ${db_type}`);
// 读取现有索引
let index;
try {
const result = await cos.read(bucket, `${dbConfig.root}/index.json`);
index = JSON.parse(result.content);
} catch {
throw new Error('索引不存在,请先执行 cosDbInit 初始化');
}
// 合并更新(只允许更新安全字段)
const safeFields = ['total_entries', 'category_counts', 'last_write', 'description'];
for (const [key, value] of Object.entries(updates || {})) {
if (safeFields.includes(key)) {
index[key] = value;
}
}
index.updated_at = new Date().toISOString();
await cos.write(bucket, `${dbConfig.root}/index.json`, JSON.stringify(index, null, 2), 'application/json');
return { index, updated: true };
}
/**
* cosDbWriteEntry — 写入数据条目
*
* input:
* bucket: string — 桶名
* db_type: string — 数据库类型
* category: string — 分类(如 code-modules, pages, workorders
* entry_id: string — 条目ID
* content: object — 条目内容JSON
* metadata: object — 元数据(可选)
*/
async function cosDbWriteEntry(input) {
const { bucket, db_type, category, entry_id, content, metadata } = input;
if (!bucket || !db_type || !category || !entry_id) {
throw new Error('缺少必填字段: bucket, db_type, category, entry_id');
}
if (!content) throw new Error('缺少 content');
const dbConfig = DB_TYPES[db_type];
if (!dbConfig) throw new Error(`无效的 db_type: ${db_type}`);
if (!dbConfig.categories.includes(category)) {
throw new Error(`无效的 category: ${category},可选: ${dbConfig.categories.join(', ')}`);
}
// 验证 entry_id 安全性
if (!/^[a-zA-Z0-9_-]+$/.test(entry_id)) {
throw new Error('entry_id 包含非法字符,仅允许字母、数字、下划线、连字符');
}
if (entry_id.includes('..')) {
throw new Error('entry_id 包含非法路径穿越');
}
const now = new Date().toISOString();
const entry = {
cos_db_version: COS_DB_VERSION,
entry_id,
category,
db_type,
created_at: now,
updated_at: now,
metadata: metadata || {},
content
};
const key = `${dbConfig.root}/${category}/${entry_id}.json`;
await cos.write(bucket, key, JSON.stringify(entry, null, 2), 'application/json');
// 异步更新索引(不阻塞写入)
updateIndexAfterWrite(bucket, db_type, category).catch(() => {});
return {
status: 'written',
key,
entry_id,
category,
db_type,
size_bytes: Buffer.byteLength(JSON.stringify(entry))
};
}
/**
* cosDbReadEntry — 读取数据条目
*
* input:
* bucket: string — 桶名
* db_type: string — 数据库类型
* category: string — 分类
* entry_id: string — 条目ID
*/
async function cosDbReadEntry(input) {
const { bucket, db_type, category, entry_id } = input;
if (!bucket || !db_type || !category || !entry_id) {
throw new Error('缺少必填字段: bucket, db_type, category, entry_id');
}
const dbConfig = DB_TYPES[db_type];
if (!dbConfig) throw new Error(`无效的 db_type: ${db_type}`);
const key = `${dbConfig.root}/${category}/${entry_id}.json`;
const result = await cos.read(bucket, key);
return {
entry: JSON.parse(result.content),
size_bytes: result.size_bytes,
last_modified: result.last_modified
};
}
/**
* cosDbListEntries — 列出数据条目
*
* input:
* bucket: string — 桶名
* db_type: string — 数据库类型
* category: string — 分类
* limit: number — 最大数量默认100
*/
async function cosDbListEntries(input) {
const { bucket, db_type, category, limit } = input;
if (!bucket || !db_type || !category) {
throw new Error('缺少必填字段: bucket, db_type, category');
}
const dbConfig = DB_TYPES[db_type];
if (!dbConfig) throw new Error(`无效的 db_type: ${db_type}`);
const prefix = `${dbConfig.root}/${category}/`;
const result = await cos.list(bucket, prefix, limit || 100);
const entries = result.files
.filter(f => f.key.endsWith('.json') && !f.key.endsWith('.init'))
.map(f => ({
key: f.key,
entry_id: f.key.replace(prefix, '').replace('.json', ''),
size_bytes: f.size_bytes
}));
return {
entries,
count: entries.length,
category,
db_type
};
}
/**
* cosDbDeleteEntry — 删除数据条目
*
* input:
* bucket: string — 桶名
* db_type: string — 数据库类型
* category: string — 分类
* entry_id: string — 条目ID
*/
async function cosDbDeleteEntry(input) {
const { bucket, db_type, category, entry_id } = input;
if (!bucket || !db_type || !category || !entry_id) {
throw new Error('缺少必填字段: bucket, db_type, category, entry_id');
}
const dbConfig = DB_TYPES[db_type];
if (!dbConfig) throw new Error(`无效的 db_type: ${db_type}`);
const key = `${dbConfig.root}/${category}/${entry_id}.json`;
await cos.del(bucket, key);
// 异步更新索引
updateIndexAfterWrite(bucket, db_type, category).catch(() => {});
return { status: 'deleted', key, entry_id };
}
/**
* cosDbGetStats — 获取数据库统计信息
*
* input:
* bucket: string — 桶名
* db_type: string — 数据库类型
*/
async function cosDbGetStats(input) {
const { bucket, db_type } = input;
if (!bucket || !db_type) throw new Error('缺少 bucket 或 db_type');
const dbConfig = DB_TYPES[db_type];
if (!dbConfig) throw new Error(`无效的 db_type: ${db_type}`);
// 读取索引
let index = null;
try {
const result = await cos.read(bucket, `${dbConfig.root}/index.json`);
index = JSON.parse(result.content);
} catch {
// 索引不存在
}
// 统计各分类文件数
const categoryStats = {};
for (const category of dbConfig.categories) {
try {
const result = await cos.list(bucket, `${dbConfig.root}/${category}/`, 500);
const entries = result.files.filter(f => f.key.endsWith('.json') && !f.key.endsWith('.init'));
categoryStats[category] = {
entries: entries.length,
total_size_bytes: entries.reduce((sum, f) => sum + f.size_bytes, 0)
};
} catch {
categoryStats[category] = { entries: 0, total_size_bytes: 0 };
}
}
const totalEntries = Object.values(categoryStats).reduce((sum, c) => sum + c.entries, 0);
const totalSize = Object.values(categoryStats).reduce((sum, c) => sum + c.total_size_bytes, 0);
return {
db_type,
bucket,
root: dbConfig.root,
index_exists: !!index,
total_entries: totalEntries,
total_size_bytes: totalSize,
categories: categoryStats,
last_updated: index?.updated_at || null,
timestamp: new Date().toISOString()
};
}
// ─── 内部辅助 ───
async function updateIndexAfterWrite(bucket, dbType, category) {
const dbConfig = DB_TYPES[dbType];
try {
const result = await cos.read(bucket, `${dbConfig.root}/index.json`);
const index = JSON.parse(result.content);
// 更新分类计数
const listResult = await cos.list(bucket, `${dbConfig.root}/${category}/`, 500);
const count = listResult.files.filter(f => f.key.endsWith('.json') && !f.key.endsWith('.init')).length;
index.category_counts = index.category_counts || {};
index.category_counts[category] = count;
index.total_entries = Object.values(index.category_counts).reduce((s, c) => s + c, 0);
index.last_write = new Date().toISOString();
index.updated_at = new Date().toISOString();
await cos.write(bucket, `${dbConfig.root}/index.json`, JSON.stringify(index, null, 2), 'application/json');
} catch {
// 索引更新失败不影响主操作
}
}
module.exports = {
cosDbInit,
cosDbGetIndex,
cosDbUpdateIndex,
cosDbWriteEntry,
cosDbReadEntry,
cosDbListEntries,
cosDbDeleteEntry,
cosDbGetStats
};