refactor: 重构整个资源引用协议
This commit is contained in:
@ -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}" 未在注册表中找到`)
|
||||
}
|
||||
|
||||
121
src/lib/core/resource/protocols/KnowledgeProtocol.js
Normal file
121
src/lib/core/resource/protocols/KnowledgeProtocol.js
Normal 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
|
||||
@ -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) {
|
||||
|
||||
@ -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('/')
|
||||
|
||||
@ -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(', ')}`)
|
||||
}
|
||||
|
||||
@ -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}" 未在注册表中找到`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user