skills-market-server/scripts/import-resources.js
hjjjj e9e0cf03c5 feat(chat): 添加聊天会话和消息管理功能
新增聊天会话和消息的API接口,支持会话的创建、更新、删除及消息的分页获取和追加。更新README文档以包含新的API信息,并在server.js中注册聊天路由和索引。引入新的脚本用于导入技能和代理资源到MongoDB。
2026-03-24 10:29:04 +08:00

213 lines
7.0 KiB
JavaScript
Raw 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.

/**
* 将 aiscri-xiong/resources/skills 和 resources/agents 导入 MongoDB
* 用法node scripts/import-resources.js [--owner <name>] [--dry-run]
*/
require('dotenv').config({ path: require('path').join(__dirname, '../.env') })
const fs = require('fs')
const path = require('path')
const { MongoClient } = require('mongodb')
const MONGO_URL = process.env.MONGO_URL || 'mongodb://localhost:27017'
const DB_NAME = process.env.DB_NAME || 'skills_market'
const args = process.argv.slice(2)
const ownerIdx = args.indexOf('--owner')
const OWNER = ownerIdx !== -1 ? args[ownerIdx + 1] : 'system'
const DRY_RUN = args.includes('--dry-run')
// 路径指向 aiscri-xiong/resources
const RESOURCES_DIR = path.join(__dirname, '../../aiscri-xiong/resources')
const SKILLS_DIR = path.join(RESOURCES_DIR, 'skills')
const AGENTS_DIR = path.join(RESOURCES_DIR, 'agents')
// ── 工具函数 ────────────────────────────────────────────────────────────────
function readFilesFromDir(dir) {
const result = []
const walk = (curDir, base) => {
for (const entry of fs.readdirSync(curDir, { withFileTypes: true })) {
const rel = base ? `${base}/${entry.name}` : entry.name
const full = path.join(curDir, entry.name)
if (entry.isDirectory()) {
walk(full, rel)
} else {
result.push({ path: rel, content: fs.readFileSync(full, 'utf-8') })
}
}
}
walk(dir, '')
return result
}
function extractFrontmatter(content) {
const m = content.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/)
if (!m) return {}
const obj = {}
for (const line of m[1].split('\n')) {
const kv = line.match(/^(\w+):\s*(.+)$/)
if (kv) obj[kv[1].trim()] = kv[2].trim().replace(/^["']|["']$/g, '')
}
return obj
}
// ── 技能导入 ────────────────────────────────────────────────────────────────
async function importSkills(skillsCollection) {
if (!fs.existsSync(SKILLS_DIR)) {
console.log(`[skills] 目录不存在:${SKILLS_DIR}`)
return
}
const skillDirs = fs.readdirSync(SKILLS_DIR, { withFileTypes: true })
.filter(e => e.isDirectory())
.map(e => e.name)
console.log(`\n[skills] 发现 ${skillDirs.length} 个技能目录`)
for (const dirName of skillDirs) {
const skillDir = path.join(SKILLS_DIR, dirName)
const files = readFilesFromDir(skillDir)
if (files.length === 0) {
console.log(` [跳过] ${dirName}:无文件`)
continue
}
const skillMd = files.find(f => f.path === 'SKILL.md')
const fm = skillMd ? extractFrontmatter(skillMd.content) : {}
const name = (fm.name || dirName).toLowerCase().replace(/[^a-z0-9-]/g, '-')
const description = fm.description || ''
const now = new Date()
const existing = await skillsCollection.findOne({ name })
if (DRY_RUN) {
console.log(` [dry-run] ${existing ? '更新' : '新建'} skill: ${name} (${files.length} 个文件)`)
continue
}
if (existing) {
const versionEntry = {
version: (existing.versions?.length || 0) + 1,
description: `Imported from resources by ${OWNER}`,
files: existing.files,
created_at: now,
created_by: OWNER
}
await skillsCollection.updateOne(
{ _id: existing._id },
{
$set: { files, description: description || existing.description, updated_at: now, updated_by: OWNER },
$push: { versions: versionEntry }
}
)
console.log(` [更新] skill: ${name} → v${versionEntry.version}`)
} else {
await skillsCollection.insertOne({
name,
description,
owner: OWNER,
files,
downloads: 0,
is_public: true,
tags: [],
versions: [{ version: 1, description: 'Initial import', files, created_at: now, created_by: OWNER }],
created_at: now,
updated_at: now,
created_by: OWNER,
updated_by: OWNER
})
console.log(` [新建] skill: ${name} (${files.length} 个文件)`)
}
}
}
// ── Agent 导入 ────────────────────────────────────────────────────────────
async function importAgents(agentsCollection) {
if (!fs.existsSync(AGENTS_DIR)) {
console.log(`[agents] 目录不存在:${AGENTS_DIR}`)
return
}
const agentFiles = fs.readdirSync(AGENTS_DIR, { withFileTypes: true })
.filter(e => e.isFile() && e.name.endsWith('.md'))
.map(e => e.name)
console.log(`\n[agents] 发现 ${agentFiles.length} 个 agent 文件`)
for (const fileName of agentFiles) {
const content = fs.readFileSync(path.join(AGENTS_DIR, fileName), 'utf-8')
const fm = extractFrontmatter(content)
const name = (fm.name || path.basename(fileName, '.md')).toLowerCase().replace(/[^a-z0-9-]/g, '-')
const description = fm.description || ''
const now = new Date()
const existing = await agentsCollection.findOne({ name })
if (DRY_RUN) {
console.log(` [dry-run] ${existing ? '更新' : '新建'} agent: ${name}`)
continue
}
if (existing) {
const versionEntry = {
version: (existing.versions?.length || 0) + 1,
content: existing.content,
created_at: now,
created_by: OWNER
}
await agentsCollection.updateOne(
{ _id: existing._id },
{
$set: { content, description: description || existing.description, updated_at: now, updated_by: OWNER },
$push: { versions: versionEntry }
}
)
console.log(` [更新] agent: ${name} → v${versionEntry.version}`)
} else {
await agentsCollection.insertOne({
name,
description,
owner: OWNER,
content,
is_public: true,
versions: [{ version: 1, content, created_at: now, created_by: OWNER }],
created_at: now,
updated_at: now,
created_by: OWNER,
updated_by: OWNER
})
console.log(` [新建] agent: ${name}`)
}
}
}
// ── 主流程 ────────────────────────────────────────────────────────────────
async function main() {
console.log(`MONGO_URL: ${MONGO_URL}`)
console.log(`DB: ${DB_NAME}`)
console.log(`OWNER: ${OWNER}`)
console.log(`DRY_RUN: ${DRY_RUN}`)
const client = new MongoClient(MONGO_URL)
await client.connect()
const db = client.db(DB_NAME)
const skillsCollection = db.collection('skills')
const agentsCollection = db.collection('agents')
await importSkills(skillsCollection)
await importAgents(agentsCollection)
await client.close()
console.log('\n✅ 导入完成')
}
main().catch(err => {
console.error('❌ 导入失败:', err)
process.exit(1)
})