refactor: 重构整个资源引用协议

This commit is contained in:
sean
2025-06-12 16:33:50 +08:00
parent d0a6b0b304
commit f9bbc55069
31 changed files with 1985 additions and 3604 deletions

View File

@ -10,6 +10,14 @@ class ExecutionProtocol extends ResourceProtocol {
constructor () {
super('execution')
this.registry = {}
this.registryManager = null // 统一注册表管理器
}
/**
* 设置注册表管理器
*/
setRegistryManager(manager) {
this.registryManager = manager
}
/**
@ -40,7 +48,36 @@ class ExecutionProtocol extends ResourceProtocol {
*/
async resolvePath (resourcePath, queryParams) {
const executionId = resourcePath.trim()
const fullResourceId = `execution:${executionId}`
// 优先使用统一注册表管理器
if (this.registryManager) {
const reference = this.registryManager.registry.get(fullResourceId)
if (!reference) {
const availableExecutions = this.registryManager.registry.keys()
.filter(id => id.startsWith('execution:'))
.map(id => id.replace('execution:', ''))
throw new Error(`执行模式 "${executionId}" 未在注册表中找到。可用执行模式:${availableExecutions.join(', ')}`)
}
let resolvedPath = reference
// 处理 @package:// 前缀
if (resolvedPath.startsWith('@package://')) {
const PackageProtocol = require('./PackageProtocol')
const packageProtocol = new PackageProtocol()
const relativePath = resolvedPath.replace('@package://', '')
resolvedPath = await packageProtocol.resolvePath(relativePath)
} else if (resolvedPath.startsWith('@project://')) {
// 处理 @project:// 前缀,转换为绝对路径
const relativePath = resolvedPath.replace('@project://', '')
resolvedPath = path.join(process.cwd(), relativePath)
}
return resolvedPath
}
// 向后兼容使用旧的registry
if (!this.registry[executionId]) {
throw new Error(`执行模式 "${executionId}" 未在注册表中找到`)
}

View File

@ -0,0 +1,121 @@
const ResourceProtocol = require('./ResourceProtocol')
const fs = require('fs-extra')
const path = require('path')
/**
* 知识资源协议处理器
* 处理 knowledge:// 协议的资源解析
*/
class KnowledgeProtocol extends ResourceProtocol {
constructor () {
super('knowledge')
this.registry = {}
this.registryManager = null // 统一注册表管理器
}
/**
* 设置注册表管理器
*/
setRegistryManager(manager) {
this.registryManager = manager
}
/**
* 设置注册表
*/
setRegistry (registry) {
this.registry = registry || {}
}
/**
* 获取协议信息
*/
getProtocolInfo () {
return {
name: 'knowledge',
description: '知识资源协议',
location: 'knowledge://{knowledge_id}',
examples: [
'knowledge://xiaohongshu-marketing',
'knowledge://ai-tools-guide'
]
}
}
/**
* 解析资源路径
*/
async resolvePath (resourcePath, queryParams) {
const knowledgeId = resourcePath.trim()
const fullResourceId = `knowledge:${knowledgeId}`
// 优先使用统一注册表管理器
if (this.registryManager) {
const reference = this.registryManager.registry.get(fullResourceId)
if (!reference) {
const availableKnowledge = this.registryManager.registry.keys()
.filter(id => id.startsWith('knowledge:'))
.map(id => id.replace('knowledge:', ''))
throw new Error(`知识资源 "${knowledgeId}" 未在注册表中找到。可用知识资源:${availableKnowledge.join(', ')}`)
}
let resolvedPath = reference
// 处理 @package:// 前缀
if (resolvedPath.startsWith('@package://')) {
const PackageProtocol = require('./PackageProtocol')
const packageProtocol = new PackageProtocol()
const relativePath = resolvedPath.replace('@package://', '')
resolvedPath = await packageProtocol.resolvePath(relativePath)
} else if (resolvedPath.startsWith('@project://')) {
// 处理 @project:// 前缀,转换为绝对路径
const relativePath = resolvedPath.replace('@project://', '')
resolvedPath = path.join(process.cwd(), relativePath)
}
return resolvedPath
}
// 向后兼容使用旧的registry
if (!this.registry[knowledgeId]) {
throw new Error(`知识资源 "${knowledgeId}" 未在注册表中找到`)
}
let resolvedPath = this.registry[knowledgeId]
// 处理 @package:// 前缀
if (resolvedPath.startsWith('@package://')) {
const PackageProtocol = require('./PackageProtocol')
const packageProtocol = new PackageProtocol()
const relativePath = resolvedPath.replace('@package://', '')
resolvedPath = await packageProtocol.resolvePath(relativePath)
} else if (resolvedPath.startsWith('@project://')) {
// 处理 @project:// 前缀,转换为绝对路径
const relativePath = resolvedPath.replace('@project://', '')
resolvedPath = path.join(process.cwd(), relativePath)
}
return resolvedPath
}
/**
* 加载资源内容
*/
async loadContent (resolvedPath, queryParams) {
try {
const content = await fs.readFile(resolvedPath, 'utf-8')
return content
} catch (error) {
throw new Error(`无法加载知识资源文件 ${resolvedPath}: ${error.message}`)
}
}
/**
* 验证资源路径
*/
validatePath (resourcePath) {
return /^[a-zA-Z0-9_-]+$/.test(resourcePath)
}
}
module.exports = KnowledgeProtocol

View File

@ -258,12 +258,17 @@ class PackageProtocol extends ResourceProtocol {
findPackageJson (startPath = __dirname) {
let currentPath = path.resolve(startPath)
while (currentPath !== path.parse(currentPath).root) {
let maxIterations = 50 // Prevent infinite loops
while (currentPath !== path.parse(currentPath).root && maxIterations-- > 0) {
const packageJsonPath = path.join(currentPath, 'package.json')
if (require('fs').existsSync(packageJsonPath)) {
return packageJsonPath
}
currentPath = path.dirname(currentPath)
const parentPath = path.dirname(currentPath)
if (parentPath === currentPath) {
break // Additional protection
}
currentPath = parentPath
}
return null
@ -275,13 +280,18 @@ class PackageProtocol extends ResourceProtocol {
findRootPackageJson () {
let currentPath = process.cwd()
let lastValidPackageJson = null
let maxIterations = 50 // Prevent infinite loops
while (currentPath !== path.parse(currentPath).root) {
while (currentPath !== path.parse(currentPath).root && maxIterations-- > 0) {
const packageJsonPath = path.join(currentPath, 'package.json')
if (require('fs').existsSync(packageJsonPath)) {
lastValidPackageJson = packageJsonPath
}
currentPath = path.dirname(currentPath)
const parentPath = path.dirname(currentPath)
if (parentPath === currentPath) {
break // Additional protection
}
currentPath = parentPath
}
return lastValidPackageJson
@ -520,23 +530,27 @@ class PackageProtocol extends ResourceProtocol {
/**
* 加载资源内容
* @param {string} resolvedPath - 已解析的路径
* @param {QueryParams} [queryParams] - 查询参数
* @returns {Object} 包含内容和元数据的对象
*/
async loadContent (resolvedPath, queryParams) {
try {
await fsPromises.access(resolvedPath)
const content = await fsPromises.readFile(resolvedPath, 'utf8')
const stats = await fsPromises.stat(resolvedPath)
const packageRoot = await this.getPackageRoot()
return {
content,
path: resolvedPath,
protocol: this.name,
protocol: 'package',
installMode: this.detectInstallMode(),
metadata: {
size: content.length,
lastModified: stats.mtime,
absolutePath: resolvedPath,
relativePath: path.relative(await this.getPackageRoot(), resolvedPath)
relativePath: path.relative(packageRoot, resolvedPath)
}
}
} catch (error) {

View File

@ -89,6 +89,11 @@ class ProjectProtocol extends ResourceProtocol {
return false
}
// 特殊处理:允许.promptx开头的路径项目配置目录
if (resourcePath.startsWith('.promptx/')) {
return true
}
// 解析路径的第一部分(目录类型)
const parts = resourcePath.split('/')
const dirType = parts[0]
@ -162,11 +167,37 @@ class ProjectProtocol extends ResourceProtocol {
/**
* 解析项目路径
* @param {string} resourcePath - 原始资源路径,如 "src/index.js"
* @param {string} resourcePath - 原始资源路径,如 "src/index.js" 或 ".promptx/resource/..."
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 解析后的绝对路径
*/
async resolvePath (resourcePath, queryParams) {
// 特殊处理:.promptx开头的路径直接相对于项目根目录
if (resourcePath.startsWith('.promptx/')) {
// 确定搜索起始点
const startDir = queryParams?.get('from') || process.cwd()
// 查找项目根目录
const projectRoot = await this.findProjectRoot(startDir)
if (!projectRoot) {
throw new Error('未找到项目根目录(.promptx标识。请确保在项目目录内或使用 \'from\' 参数指定项目路径')
}
// 直接拼接完整路径
const fullPath = path.join(projectRoot, resourcePath)
// 安全检查:确保路径在项目目录内
const resolvedPath = path.resolve(fullPath)
const resolvedProjectRoot = path.resolve(projectRoot)
if (!resolvedPath.startsWith(resolvedProjectRoot)) {
throw new Error(`安全错误:路径超出项目目录范围: ${resolvedPath}`)
}
return resolvedPath
}
// 标准路径处理逻辑
const parts = resourcePath.split('/')
const dirType = parts[0]
const relativePath = parts.slice(1).join('/')

View File

@ -10,6 +10,14 @@ class RoleProtocol extends ResourceProtocol {
constructor () {
super('role')
this.registry = {}
this.registryManager = null // 统一注册表管理器
}
/**
* 设置注册表管理器
*/
setRegistryManager(manager) {
this.registryManager = manager
}
/**
@ -41,7 +49,36 @@ class RoleProtocol extends ResourceProtocol {
*/
async resolvePath (resourcePath, queryParams) {
const roleId = resourcePath.trim()
const fullResourceId = `role:${roleId}`
// 优先使用统一注册表管理器
if (this.registryManager) {
const reference = this.registryManager.registry.get(fullResourceId)
if (!reference) {
const availableRoles = this.registryManager.registry.keys()
.filter(id => id.startsWith('role:'))
.map(id => id.replace('role:', ''))
throw new Error(`角色 "${roleId}" 未在注册表中找到。可用角色:${availableRoles.join(', ')}`)
}
let resolvedPath = reference
// 处理 @package:// 前缀
if (resolvedPath.startsWith('@package://')) {
const PackageProtocol = require('./PackageProtocol')
const packageProtocol = new PackageProtocol()
const relativePath = resolvedPath.replace('@package://', '')
resolvedPath = await packageProtocol.resolvePath(relativePath)
} else if (resolvedPath.startsWith('@project://')) {
// 处理 @project:// 前缀,转换为绝对路径
const relativePath = resolvedPath.replace('@project://', '')
resolvedPath = path.join(process.cwd(), relativePath)
}
return resolvedPath
}
// 向后兼容使用旧的registry
if (!this.registry[roleId]) {
throw new Error(`角色 "${roleId}" 未在注册表中找到。可用角色:${Object.keys(this.registry).join(', ')}`)
}

View File

@ -10,6 +10,14 @@ class ThoughtProtocol extends ResourceProtocol {
constructor () {
super('thought')
this.registry = {}
this.registryManager = null // 统一注册表管理器
}
/**
* 设置注册表管理器
*/
setRegistryManager(manager) {
this.registryManager = manager
}
/**
@ -39,7 +47,36 @@ class ThoughtProtocol extends ResourceProtocol {
*/
async resolvePath (resourcePath, queryParams) {
const thoughtId = resourcePath.trim()
const fullResourceId = `thought:${thoughtId}`
// 优先使用统一注册表管理器
if (this.registryManager) {
const reference = this.registryManager.registry.get(fullResourceId)
if (!reference) {
const availableThoughts = this.registryManager.registry.keys()
.filter(id => id.startsWith('thought:'))
.map(id => id.replace('thought:', ''))
throw new Error(`思维模式 "${thoughtId}" 未在注册表中找到。可用思维模式:${availableThoughts.join(', ')}`)
}
let resolvedPath = reference
// 处理 @package:// 前缀
if (resolvedPath.startsWith('@package://')) {
const PackageProtocol = require('./PackageProtocol')
const packageProtocol = new PackageProtocol()
const relativePath = resolvedPath.replace('@package://', '')
resolvedPath = await packageProtocol.resolvePath(relativePath)
} else if (resolvedPath.startsWith('@project://')) {
// 处理 @project:// 前缀,转换为绝对路径
const relativePath = resolvedPath.replace('@project://', '')
resolvedPath = path.join(process.cwd(), relativePath)
}
return resolvedPath
}
// 向后兼容使用旧的registry
if (!this.registry[thoughtId]) {
throw new Error(`思维模式 "${thoughtId}" 未在注册表中找到`)
}