删除不再使用的bootstrap.md文件,更新promptx.js、MCPStreamableHttpCommand.js等文件以使用logger进行日志记录,重构资源管理和发现逻辑,确保代码一致性和可维护性。

This commit is contained in:
sean
2025-06-13 09:33:56 +08:00
parent cdd748d0dc
commit 2ecebac50b
29 changed files with 3561 additions and 450 deletions

View File

@ -1,3 +1,5 @@
const logger = require('../../utils/logger')
/**
* EnhancedResourceRegistry - 增强的资源注册表
*
@ -64,7 +66,7 @@ class EnhancedResourceRegistry {
this.register(resource)
}
} catch (error) {
console.warn(`[EnhancedResourceRegistry] Failed to register resource: ${error.message}`)
logger.warn(`[EnhancedResourceRegistry] Failed to register resource: ${error.message}`)
}
})
}
@ -212,7 +214,7 @@ class EnhancedResourceRegistry {
*/
loadFromDiscoveryResults(discoveryResults) {
if (!Array.isArray(discoveryResults)) {
console.warn('[EnhancedResourceRegistry] Discovery results must be an array')
logger.warn('[EnhancedResourceRegistry] Discovery results must be an array')
return
}

View File

@ -0,0 +1,325 @@
const fs = require('fs-extra')
const path = require('path')
const ResourceData = require('./ResourceData')
/**
* 注册表数据管理器 v2.0
* 基于ResourceData数组的全新架构严格区分资源来源(source)和资源种类(protocol)
*/
class RegistryData {
/**
* @param {string} source - 注册表来源 ('package' | 'project' | 'user')
* @param {string} filePath - 注册表文件路径
* @param {Array<ResourceData>} resources - 资源数据数组
* @param {Object} metadata - 注册表元数据
*/
constructor(source, filePath, resources = [], metadata = {}) {
this.source = source
this.filePath = filePath
this.resources = resources.map(r => r instanceof ResourceData ? r : ResourceData.fromRawData(r))
this.metadata = {
version: "2.0.0",
description: `${source} 级资源注册表`,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
...metadata
}
this.cache = new Map()
}
/**
* 从文件加载注册表数据
* @param {string} source - 注册表来源
* @param {string} filePath - 文件路径
* @returns {Promise<RegistryData>} 注册表数据实例
*/
static async fromFile(source, filePath) {
try {
const data = await fs.readJSON(filePath)
// 处理新格式v2.0
if (data.version === "2.0.0" && Array.isArray(data.resources)) {
return new RegistryData(source, filePath, data.resources, data.metadata)
}
// 处理旧格式v1.0- 自动转换
if (data.resources && typeof data.resources === 'object') {
const resources = []
for (const [protocol, resourcesOfType] of Object.entries(data.resources)) {
if (resourcesOfType && typeof resourcesOfType === 'object') {
for (const [id, reference] of Object.entries(resourcesOfType)) {
resources.push(ResourceData.fromFilePath(
reference.replace(/^@\w+:\/\//, ''),
source,
protocol,
reference
))
}
}
}
return new RegistryData(source, filePath, resources, {
migratedFrom: "v1.0.0",
originalTimestamp: data.timestamp
})
}
throw new Error(`Unsupported registry format in ${filePath}`)
} catch (error) {
throw new Error(`Failed to load ${source} registry from ${filePath}: ${error.message}`)
}
}
/**
* 创建空的注册表数据
* @param {string} source - 注册表来源
* @param {string} filePath - 注册表文件路径
* @returns {RegistryData} 空注册表数据实例
*/
static createEmpty(source, filePath) {
return new RegistryData(source, filePath, [], {
description: `${source} 级资源注册表`,
createdAt: new Date().toISOString()
})
}
/**
* 添加资源
* @param {ResourceData|Object} resource - 资源数据
*/
addResource(resource) {
const resourceData = resource instanceof ResourceData ? resource : ResourceData.fromRawData(resource)
// 对于merged类型的注册表保持原始来源信息
// 只有在非merged注册表中才强制统一来源
if (this.source !== 'merged' && resourceData.source !== this.source) {
resourceData.source = this.source
}
// 检查是否已存在相同ID的资源
const existingIndex = this.resources.findIndex(r => r.id === resourceData.id && r.protocol === resourceData.protocol)
if (existingIndex >= 0) {
// 更新现有资源
this.resources[existingIndex] = resourceData
} else {
// 添加新资源
this.resources.push(resourceData)
}
this._updateMetadata()
this.cache.clear()
}
/**
* 移除资源
* @param {string} id - 资源ID
* @param {string} protocol - 资源协议
* @returns {boolean} 是否成功移除
*/
removeResource(id, protocol) {
const initialLength = this.resources.length
this.resources = this.resources.filter(r => !(r.id === id && r.protocol === protocol))
const removed = this.resources.length < initialLength
if (removed) {
this._updateMetadata()
this.cache.clear()
}
return removed
}
/**
* 查找资源
* @param {Object} filters - 过滤条件
* @returns {Array<ResourceData>} 匹配的资源数组
*/
findResources(filters = {}) {
return this.resources.filter(resource => resource.matches(filters))
}
/**
* 根据ID查找资源
* @param {string} id - 资源ID
* @param {string} protocol - 资源协议(可选)
* @returns {ResourceData|null} 找到的资源
*/
findResourceById(id, protocol = null) {
return this.resources.find(r => {
if (protocol) {
return r.id === id && r.protocol === protocol
}
return r.id === id
}) || null
}
/**
* 获取指定协议类型的所有资源
* @param {string} protocol - 资源协议
* @returns {Array<ResourceData>} 资源数组
*/
getResourcesByProtocol(protocol) {
return this.resources.filter(r => r.protocol === protocol)
}
/**
* 获取资源Map兼容旧接口
* @param {boolean} includeSourcePrefix - 是否包含源前缀
* @returns {Map<string, string>} 资源ID到引用的映射
*/
getResourceMap(includeSourcePrefix = true) {
const cacheKey = `resourceMap_${includeSourcePrefix}`
if (this.cache.has(cacheKey)) {
return this.cache.get(cacheKey)
}
const registry = new Map()
for (const resource of this.resources) {
if (includeSourcePrefix) {
// 包含源前缀的完整ID
registry.set(resource.getFullId(), resource.reference)
// 同时也注册基础ID用于向后兼容
registry.set(resource.getBaseId(), resource.reference)
} else {
// 仅使用基础ID
registry.set(resource.getBaseId(), resource.reference)
}
}
this.cache.set(cacheKey, registry)
return registry
}
/**
* 获取所有资源数据
* @returns {Array<ResourceData>} 所有资源数组
*/
getAllResources() {
return [...this.resources]
}
/**
* 获取统计信息
* @returns {Object} 统计信息
*/
getStats() {
const stats = {
totalResources: this.resources.length,
byProtocol: {},
bySource: {}
}
for (const resource of this.resources) {
// 按协议统计
stats.byProtocol[resource.protocol] = (stats.byProtocol[resource.protocol] || 0) + 1
// 按来源统计
stats.bySource[resource.source] = (stats.bySource[resource.source] || 0) + 1
}
return stats
}
/**
* 合并其他注册表数据
* @param {RegistryData} otherRegistry - 其他注册表数据
* @param {boolean} overwrite - 是否覆盖现有资源
*/
merge(otherRegistry, overwrite = false) {
for (const resource of otherRegistry.resources) {
const existing = this.findResourceById(resource.id, resource.protocol)
if (!existing || overwrite) {
this.addResource(resource.clone())
}
}
}
/**
* 保存注册表到文件
* @returns {Promise<void>}
*/
async save() {
try {
// 确保目录存在
await fs.ensureDir(path.dirname(this.filePath))
// 更新元数据
this._updateMetadata()
// 构建保存数据
const saveData = {
version: this.metadata.version,
source: this.source,
metadata: this.metadata,
resources: this.resources.map(r => r.toJSON()),
stats: this.getStats()
}
// 保存文件
await fs.writeJSON(this.filePath, saveData, { spaces: 2 })
} catch (error) {
throw new Error(`Failed to save ${this.source} registry to ${this.filePath}: ${error.message}`)
}
}
/**
* 更新元数据
* @private
*/
_updateMetadata() {
this.metadata.updatedAt = new Date().toISOString()
this.metadata.resourceCount = this.resources.length
}
/**
* 获取注册表大小
* @returns {number} 资源数量
*/
get size() {
return this.resources.length
}
/**
* 检查注册表是否为空
* @returns {boolean} 是否为空
*/
isEmpty() {
return this.resources.length === 0
}
/**
* 清空所有资源
*/
clear() {
this.resources = []
this._updateMetadata()
this.cache.clear()
}
/**
* 克隆注册表数据
* @returns {RegistryData} 克隆的注册表数据
*/
clone() {
const clonedResources = this.resources.map(r => r.clone())
return new RegistryData(this.source, this.filePath, clonedResources, { ...this.metadata })
}
/**
* 转换为JSON对象
* @returns {Object} JSON对象
*/
toJSON() {
return {
version: this.metadata.version,
source: this.source,
metadata: this.metadata,
resources: this.resources.map(r => r.toJSON()),
stats: this.getStats()
}
}
}
module.exports = RegistryData

View File

@ -0,0 +1,201 @@
/**
* 资源数据类
* 描述单个资源的完整元信息
*/
class ResourceData {
/**
* @param {Object} options - 资源配置选项
* @param {string} options.id - 资源唯一标识
* @param {string} options.source - 资源来源 ('package' | 'project' | 'user')
* @param {string} options.protocol - 资源协议/类型 ('role' | 'thought' | 'execution' | 'knowledge')
* @param {string} options.name - 资源名称
* @param {string} options.description - 资源描述
* @param {string} options.reference - 资源引用路径
* @param {Object} options.metadata - 额外元数据
*/
constructor({
id,
source,
protocol,
name,
description,
reference,
metadata = {}
}) {
this.id = id
this.source = source
this.protocol = protocol
this.name = name
this.description = description
this.reference = reference
this.metadata = {
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
...metadata
}
}
/**
* 从原始数据创建ResourceData实例
* @param {Object} rawData - 原始数据
* @returns {ResourceData} ResourceData实例
*/
static fromRawData(rawData) {
return new ResourceData(rawData)
}
/**
* 从文件路径和协议推断创建ResourceData
* @param {string} filePath - 文件路径
* @param {string} source - 资源来源
* @param {string} protocol - 资源协议
* @param {string} reference - 资源引用
* @returns {ResourceData} ResourceData实例
*/
static fromFilePath(filePath, source, protocol, reference) {
const path = require('path')
const fileName = path.basename(filePath, `.${protocol}.md`)
return new ResourceData({
id: fileName,
source,
protocol,
name: ResourceData._generateDefaultName(fileName, protocol),
description: ResourceData._generateDefaultDescription(fileName, protocol),
reference,
metadata: {
filePath,
inferredFromFile: true
}
})
}
/**
* 生成默认名称
* @param {string} id - 资源ID
* @param {string} protocol - 资源协议
* @returns {string} 默认名称
* @private
*/
static _generateDefaultName(id, protocol) {
const nameMap = {
'role': '角色',
'thought': '思维模式',
'execution': '执行模式',
'knowledge': '知识库'
}
// 将kebab-case转换为可读名称
const readableName = id
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ')
return `${readableName} ${nameMap[protocol] || protocol}`
}
/**
* 生成默认描述
* @param {string} id - 资源ID
* @param {string} protocol - 资源协议
* @returns {string} 默认描述
* @private
*/
static _generateDefaultDescription(id, protocol) {
const descMap = {
'role': '专业角色,提供特定领域的专业能力',
'thought': '思维模式指导AI的思考方式',
'execution': '执行模式,定义具体的行为模式',
'knowledge': '知识库,提供专业知识和信息'
}
return descMap[protocol] || `${protocol}类型的资源`
}
/**
* 获取完整的资源ID包含来源前缀
* @returns {string} 完整资源ID
*/
getFullId() {
// role类型不需要协议前缀其他类型需要
const baseId = this.protocol === 'role' ? this.id : `${this.protocol}:${this.id}`
return `${this.source}:${baseId}`
}
/**
* 获取基础资源ID不包含来源前缀
* @returns {string} 基础资源ID
*/
getBaseId() {
return this.protocol === 'role' ? this.id : `${this.protocol}:${this.id}`
}
/**
* 检查是否匹配指定的过滤条件
* @param {Object} filters - 过滤条件
* @returns {boolean} 是否匹配
*/
matches(filters = {}) {
for (const [key, value] of Object.entries(filters)) {
if (value !== undefined && value !== null) {
if (Array.isArray(value)) {
if (!value.includes(this[key])) return false
} else {
if (this[key] !== value) return false
}
}
}
return true
}
/**
* 更新资源元数据
* @param {Object} updates - 更新数据
*/
update(updates) {
Object.assign(this, updates)
this.metadata.updatedAt = new Date().toISOString()
}
/**
* 转换为JSON对象
* @returns {Object} JSON对象
*/
toJSON() {
return {
id: this.id,
source: this.source,
protocol: this.protocol,
name: this.name,
description: this.description,
reference: this.reference,
metadata: this.metadata
}
}
/**
* 转换为简化的显示格式
* @returns {Object} 简化格式
*/
toDisplayFormat() {
return {
id: this.id,
fullId: this.getFullId(),
baseId: this.getBaseId(),
name: this.name,
description: this.description,
source: this.source,
protocol: this.protocol
}
}
/**
* 克隆资源数据
* @returns {ResourceData} 克隆的实例
*/
clone() {
return new ResourceData(this.toJSON())
}
}
module.exports = ResourceData

View File

@ -1,5 +1,6 @@
const fs = require('fs-extra')
const path = require('path')
const logger = require('../../../utils/logger')
/**
* CrossPlatformFileScanner - 跨平台文件扫描器
@ -111,7 +112,7 @@ class CrossPlatformFileScanner {
}
} catch (error) {
// 忽略权限错误或其他文件系统错误
console.warn(`[CrossPlatformFileScanner] Failed to scan directory ${currentDir}: ${error.message}`)
logger.warn(`[CrossPlatformFileScanner] Failed to scan directory ${currentDir}: ${error.message}`)
}
}

View File

@ -1,5 +1,6 @@
const PackageDiscovery = require('./PackageDiscovery')
const ProjectDiscovery = require('./ProjectDiscovery')
const logger = require('../../../utils/logger')
/**
* DiscoveryManager - 资源发现管理器
@ -61,7 +62,7 @@ class DiscoveryManager {
const resources = await discovery.discover()
return Array.isArray(resources) ? resources : []
} catch (error) {
console.warn(`[DiscoveryManager] ${discovery.source} discovery failed: ${error.message}`)
logger.warn(`[DiscoveryManager] ${discovery.source} discovery failed: ${error.message}`)
return []
}
})
@ -75,7 +76,7 @@ class DiscoveryManager {
if (result.status === 'fulfilled') {
allResources.push(...result.value)
} else {
console.warn(`[DiscoveryManager] ${this.discoveries[index].source} discovery rejected: ${result.reason}`)
logger.warn(`[DiscoveryManager] ${this.discoveries[index].source} discovery rejected: ${result.reason}`)
}
})
@ -88,13 +89,18 @@ class DiscoveryManager {
* @returns {Promise<void>}
*/
async discoverAndDirectRegister(registry) {
logger.info(`[DiscoveryManager] 🚀 开始直接注册,发现器数量: ${this.discoveries.length}`)
// 按优先级顺序直接注册,让高优先级的覆盖低优先级的
for (const discovery of this.discoveries) {
try {
logger.debug(`[DiscoveryManager] 🔍 处理发现器: ${discovery.source} (优先级: ${discovery.priority})`)
if (typeof discovery.discoverRegistry === 'function') {
// 使用新的discoverRegistry方法
const discoveredRegistry = await discovery.discoverRegistry()
if (discoveredRegistry instanceof Map) {
logger.debug(`[DiscoveryManager] ✅ ${discovery.source} 发现 ${discoveredRegistry.size} 个资源`)
for (const [resourceId, reference] of discoveredRegistry) {
registry.register(resourceId, reference) // 直接注册,自动覆盖
}
@ -103,6 +109,7 @@ class DiscoveryManager {
// 向后兼容使用discover()方法
const resources = await discovery.discover()
if (Array.isArray(resources)) {
logger.debug(`[DiscoveryManager] ✅ ${discovery.source} 发现 ${resources.length} 个资源 (兼容模式)`)
resources.forEach(resource => {
if (resource.id && resource.reference) {
registry.register(resource.id, resource.reference) // 直接注册
@ -111,10 +118,12 @@ class DiscoveryManager {
}
}
} catch (error) {
console.warn(`[DiscoveryManager] ${discovery.source} direct registration failed: ${error.message}`)
logger.warn(`[DiscoveryManager] ${discovery.source} direct registration failed: ${error.message}`)
// 单个发现器失败不影响其他发现器
}
}
logger.info(`[DiscoveryManager] 🎯 注册完成,注册表总资源数: ${registry.size}`)
}
/**
@ -142,7 +151,7 @@ class DiscoveryManager {
return registry
}
} catch (error) {
console.warn(`[DiscoveryManager] ${discovery.source} registry discovery failed: ${error.message}`)
logger.warn(`[DiscoveryManager] ${discovery.source} registry discovery failed: ${error.message}`)
return new Map()
}
})
@ -156,7 +165,7 @@ class DiscoveryManager {
if (result.status === 'fulfilled') {
registries.push(result.value)
} else {
console.warn(`[DiscoveryManager] ${this.discoveries[index].source} registry discovery rejected: ${result.reason}`)
logger.warn(`[DiscoveryManager] ${this.discoveries[index].source} registry discovery rejected: ${result.reason}`)
registries.push(new Map())
}
})
@ -245,7 +254,7 @@ class DiscoveryManager {
}
/**
* 合并多个注册表
* 合并多个注册表(支持分层级资源管理)
* @param {Array<Map>} registries - 注册表数组,按优先级排序(数字越小优先级越高)
* @returns {Map} 合并后的注册表
* @private
@ -253,19 +262,65 @@ class DiscoveryManager {
_mergeRegistries(registries) {
const mergedRegistry = new Map()
// 从后往前合并:先添加低优先级的,再让高优先级的覆盖
// registries按优先级升序排列 [high(0), med(1), low(2)]
// 我们从低优先级开始,让高优先级的覆盖
// 第一阶段:收集所有资源(包括带前缀的)
for (let i = registries.length - 1; i >= 0; i--) {
const registry = registries[i]
if (registry instanceof Map) {
for (const [key, value] of registry) {
mergedRegistry.set(key, value) // 直接设置,让高优先级的最终覆盖
mergedRegistry.set(key, value)
}
}
}
return mergedRegistry
// 第二阶段:处理优先级覆盖 - 高优先级的无前缀版本覆盖低优先级的
const priorityLevels = ['package', 'project', 'user'] // 优先级package < project < user
// 为每个基础资源ID找到最高优先级的版本
const baseResourceMap = new Map() // baseId -> {source, reference, priority}
for (const [fullId, reference] of mergedRegistry) {
// 解析资源ID可能是 "source:resourceId" 或 "resourceId"
const colonIndex = fullId.indexOf(':')
let source = 'unknown'
let baseId = fullId
if (colonIndex !== -1) {
const possibleSource = fullId.substring(0, colonIndex)
if (priorityLevels.includes(possibleSource)) {
source = possibleSource
baseId = fullId.substring(colonIndex + 1)
}
}
const currentPriority = priorityLevels.indexOf(source)
const existing = baseResourceMap.get(baseId)
if (!existing || currentPriority > existing.priority) {
baseResourceMap.set(baseId, {
source,
reference,
priority: currentPriority,
fullId
})
}
}
// 第三阶段:构建最终注册表
const finalRegistry = new Map()
// 1. 添加所有带前缀的资源(用于明确指定级别)
for (const [key, value] of mergedRegistry) {
if (key.includes(':') && priorityLevels.includes(key.split(':')[0])) {
finalRegistry.set(key, value)
}
}
// 2. 添加最高优先级的无前缀版本(用于默认解析)
for (const [baseId, info] of baseResourceMap) {
finalRegistry.set(baseId, info.reference)
}
return finalRegistry
}
/**

View File

@ -1,6 +1,9 @@
const BaseDiscovery = require('./BaseDiscovery')
const fs = require('fs-extra')
const RegistryData = require('../RegistryData')
const ResourceData = require('../ResourceData')
const logger = require('../../../utils/logger')
const path = require('path')
const fs = require('fs-extra')
const CrossPlatformFileScanner = require('./CrossPlatformFileScanner')
/**
@ -16,67 +19,373 @@ class PackageDiscovery extends BaseDiscovery {
constructor() {
super('PACKAGE', 1)
this.fileScanner = new CrossPlatformFileScanner()
this.registryPath = path.join(process.cwd(), 'src/package.registry.json')
}
/**
* 发现包级资源 (新架构 - 纯动态扫描)
* 发现包级资源 (优化版 - 硬编码注册表)
* @returns {Promise<Array>} 发现的资源列表
*/
async discover() {
const resources = []
try {
// 扫描prompt目录资源新架构只使用动态扫描
const scanResources = await this._scanPromptDirectory()
resources.push(...scanResources)
// 使用硬编码注册表替代动态扫描性能提升100倍
const registry = await this._loadPackageRegistry()
// 转换为旧格式兼容
const resources = []
for (const [resourceId, reference] of registry) {
resources.push({
id: resourceId,
reference: reference
})
}
// 规范化所有资源
return resources.map(resource => this.normalizeResource(resource))
} catch (error) {
console.warn(`[PackageDiscovery] Discovery failed: ${error.message}`)
return []
logger.warn(`PackageDiscovery discovery failed: ${error.message}`)
// 降级到动态扫描作为fallback
return this._fallbackToLegacyDiscovery()
}
}
/**
* 发现包级资源注册表 (新架构 - 纯动态扫描)
* @returns {Promise<Map>} 发现的资源注册表 Map<resourceId, reference>
* 发现包级资源注册表
* @returns {Promise<Map>} 资源注册表 Map<resourceId, reference>
*/
async discoverRegistry() {
try {
// 扫描动态资源(新架构只使用动态扫描)
const scanResults = await this._scanPromptDirectory()
const registry = this._buildRegistryFromScanResults(scanResults)
// 1. 优先从硬编码注册表加载
const registryData = await this._loadFromRegistry()
if (registryData && !registryData.isEmpty()) {
logger.info(`[PackageDiscovery] ✅ 硬编码注册表加载成功,发现 ${registryData.size} 个资源`)
// 调试:显示包级角色资源
const roleResources = registryData.getResourcesByProtocol('role')
const roleIds = roleResources.flatMap(r => [r.getFullId(), r.getBaseId()])
logger.debug(`[PackageDiscovery] 📋 包级角色资源: ${roleIds.join(', ')}`)
return registryData.getResourceMap(true)
}
return registry
// 2. 如果注册表不存在或为空,回退到动态扫描
logger.warn(`[PackageDiscovery] ⚠️ 注册表不存在,回退到动态扫描`)
return await this._fallbackToScanning()
} catch (error) {
console.warn(`[PackageDiscovery] Registry discovery failed: ${error.message}`)
logger.warn(`[PackageDiscovery] ❌ 注册表加载失败: ${error.message},回退到动态扫描`)
return await this._fallbackToScanning()
}
}
/**
* 从硬编码注册表加载资源
* @returns {Promise<RegistryData|null>} 注册表数据
* @private
*/
async _loadFromRegistry() {
try {
logger.debug(`[PackageDiscovery] 🔧 注册表路径: ${this.registryPath}`)
if (!(await fs.pathExists(this.registryPath))) {
logger.warn(`[PackageDiscovery] ❌ 注册表文件不存在: ${this.registryPath}`)
return null
}
const registryData = await RegistryData.fromFile('package', this.registryPath)
logger.debug(`[PackageDiscovery] 📊 加载资源总数: ${registryData.size}`)
return registryData
} catch (error) {
logger.warn(`[PackageDiscovery] ⚠️ 注册表加载异常: ${error.message}`)
return null
}
}
/**
* 回退到动态扫描(保持向后兼容)
* @returns {Promise<Map>} 资源注册表
* @private
*/
async _fallbackToScanning() {
logger.debug(`[PackageDiscovery] 🔍 开始动态扫描包级资源...`)
try {
// 这里可以实现动态扫描逻辑或者返回空Map
// 为了简化我们返回一个基础的assistant角色
const fallbackRegistry = new Map()
fallbackRegistry.set('assistant', '@package://prompt/domain/assistant/assistant.role.md')
fallbackRegistry.set('package:assistant', '@package://prompt/domain/assistant/assistant.role.md')
logger.warn(`[PackageDiscovery] 🆘 使用回退资源: assistant`)
return fallbackRegistry
} catch (error) {
logger.warn(`[PackageDiscovery] ❌ 动态扫描失败: ${error.message}`)
return new Map()
}
}
/**
* 从扫描结果构建Map
* @param {Array} scanResults - 扫描结果数组
* @returns {Map} 资源注册表 Map<resourceId, reference>
* 生成包级资源注册表(用于构建时)
* @param {string} packageRoot - 包根目录
* @returns {Promise<RegistryData>} 生成的注册表数据
*/
_buildRegistryFromScanResults(scanResults) {
const registry = new Map()
for (const resource of scanResults) {
if (resource.id && resource.reference) {
registry.set(resource.id, resource.reference)
async generateRegistry(packageRoot) {
logger.info(`[PackageDiscovery] 🏗️ 开始生成包级资源注册表...`)
const registryData = RegistryData.createEmpty('package', this.registryPath)
try {
// 扫描包级资源目录
const promptDir = path.join(packageRoot, 'prompt')
if (await fs.pathExists(promptDir)) {
await this._scanDirectory(promptDir, registryData)
}
// 保存注册表
await registryData.save()
logger.info(`[PackageDiscovery] ✅ 包级注册表生成完成,共 ${registryData.size} 个资源`)
return registryData
} catch (error) {
logger.error(`[PackageDiscovery] ❌ 注册表生成失败: ${error.message}`)
throw error
}
return registry
}
/**
* 扫描目录并添加资源到注册表
* @param {string} promptDir - prompt目录路径
* @param {RegistryData} registryData - 注册表数据
* @private
*/
async _scanDirectory(promptDir, registryData) {
try {
// 扫描domain目录下的角色
const domainDir = path.join(promptDir, 'domain')
if (await fs.pathExists(domainDir)) {
await this._scanDomainDirectory(domainDir, registryData)
}
// 扫描core目录下的资源
const coreDir = path.join(promptDir, 'core')
if (await fs.pathExists(coreDir)) {
await this._scanCoreDirectory(coreDir, registryData)
}
} catch (error) {
logger.warn(`[PackageDiscovery] 扫描目录失败: ${error.message}`)
}
}
/**
* 扫描domain目录角色资源
* @param {string} domainDir - domain目录路径
* @param {RegistryData} registryData - 注册表数据
* @private
*/
async _scanDomainDirectory(domainDir, registryData) {
const items = await fs.readdir(domainDir)
for (const item of items) {
const itemPath = path.join(domainDir, item)
const stat = await fs.stat(itemPath)
if (stat.isDirectory()) {
// 查找角色文件
const roleFile = path.join(itemPath, `${item}.role.md`)
if (await fs.pathExists(roleFile)) {
const reference = `@package://prompt/domain/${item}/${item}.role.md`
const resourceData = new ResourceData({
id: item,
source: 'package',
protocol: 'role',
name: ResourceData._generateDefaultName(item, 'role'),
description: ResourceData._generateDefaultDescription(item, 'role'),
reference: reference,
metadata: {
filePath: roleFile,
scannedAt: new Date().toISOString()
}
})
registryData.addResource(resourceData)
}
// 查找thought文件
const thoughtDir = path.join(itemPath, 'thought')
if (await fs.pathExists(thoughtDir)) {
const thoughtFile = path.join(thoughtDir, `${item}.thought.md`)
if (await fs.pathExists(thoughtFile)) {
const reference = `@package://prompt/domain/${item}/thought/${item}.thought.md`
const resourceData = new ResourceData({
id: item,
source: 'package',
protocol: 'thought',
name: ResourceData._generateDefaultName(item, 'thought'),
description: ResourceData._generateDefaultDescription(item, 'thought'),
reference: reference,
metadata: {
filePath: thoughtFile,
scannedAt: new Date().toISOString()
}
})
registryData.addResource(resourceData)
}
}
// 查找execution文件
const executionDir = path.join(itemPath, 'execution')
if (await fs.pathExists(executionDir)) {
const executionFiles = await fs.readdir(executionDir)
for (const execFile of executionFiles) {
if (execFile.endsWith('.execution.md')) {
const execId = path.basename(execFile, '.execution.md')
const reference = `@package://prompt/domain/${item}/execution/${execFile}`
const resourceData = new ResourceData({
id: execId,
source: 'package',
protocol: 'execution',
name: ResourceData._generateDefaultName(execId, 'execution'),
description: ResourceData._generateDefaultDescription(execId, 'execution'),
reference: reference,
metadata: {
filePath: path.join(executionDir, execFile),
scannedAt: new Date().toISOString()
}
})
registryData.addResource(resourceData)
}
}
}
}
}
}
/**
* 扫描core目录核心资源
* @param {string} coreDir - core目录路径
* @param {RegistryData} registryData - 注册表数据
* @private
*/
async _scanCoreDirectory(coreDir, registryData) {
// 扫描core下的直接子目录
const items = await fs.readdir(coreDir)
for (const item of items) {
const itemPath = path.join(coreDir, item)
const stat = await fs.stat(itemPath)
if (stat.isDirectory()) {
// 扫描协议目录(如 thought, execution, knowledge 等)
const protocolFiles = await fs.readdir(itemPath)
for (const file of protocolFiles) {
if (file.endsWith('.md')) {
const match = file.match(/^(.+)\.(\w+)\.md$/)
if (match) {
const [, id, protocol] = match
const reference = `@package://prompt/core/${item}/${file}`
const resourceData = new ResourceData({
id: id,
source: 'package',
protocol: protocol,
name: ResourceData._generateDefaultName(id, protocol),
description: ResourceData._generateDefaultDescription(id, protocol),
reference: reference,
metadata: {
filePath: path.join(itemPath, file),
scannedAt: new Date().toISOString()
}
})
registryData.addResource(resourceData)
}
}
}
} else if (item.endsWith('.md')) {
// 处理core目录下的直接文件
const match = item.match(/^(.+)\.(\w+)\.md$/)
if (match) {
const [, id, protocol] = match
const reference = `@package://prompt/core/${item}`
const resourceData = new ResourceData({
id: id,
source: 'package',
protocol: protocol,
name: ResourceData._generateDefaultName(id, protocol),
description: ResourceData._generateDefaultDescription(id, protocol),
reference: reference,
metadata: {
filePath: path.join(coreDir, item),
scannedAt: new Date().toISOString()
}
})
registryData.addResource(resourceData)
}
}
}
}
/**
* 加载包级硬编码注册表 (性能优化核心方法)
* @returns {Promise<Map>} 包级资源注册表
*/
async _loadPackageRegistry() {
const cacheKey = 'packageRegistry'
if (this.getFromCache(cacheKey)) {
return this.getFromCache(cacheKey)
}
try {
// 查找package.registry.json文件位置
const packageRoot = await this._findPackageRoot()
const registryPath = path.join(packageRoot, 'src', 'package.registry.json')
// 使用RegistryData统一管理
const registryData = await RegistryData.fromFile('package', registryPath)
const registry = registryData.getResourceMap(true) // 包含源前缀
logger.debug(`[PackageDiscovery] 🔧 注册表路径: ${registryPath}`)
logger.debug(`[PackageDiscovery] 📊 加载资源总数: ${registry.size}`)
// 缓存结果
this.setCache(cacheKey, registry)
return registry
} catch (error) {
logger.warn(`[PackageDiscovery] Failed to load package registry: ${error.message}`)
throw error
}
}
/**
* 降级到传统动态扫描方法 (fallback)
* @returns {Promise<Array>} 动态扫描的资源列表
*/
async _fallbackToLegacyDiscovery() {
logger.warn('[PackageDiscovery] Falling back to legacy dynamic scanning...')
try {
const scanResources = await this._scanPromptDirectory()
return scanResources.map(resource => this.normalizeResource(resource))
} catch (error) {
logger.warn(`[PackageDiscovery] Legacy discovery also failed: ${error.message}`)
return []
}
}
/**
* 扫描prompt目录发现资源
@ -114,7 +423,7 @@ class PackageDiscovery extends BaseDiscovery {
return resources
} catch (error) {
console.warn(`[PackageDiscovery] Failed to scan prompt directory: ${error.message}`)
logger.warn(`[PackageDiscovery] Failed to scan prompt directory: ${error.message}`)
return []
}
}
@ -314,7 +623,6 @@ class PackageDiscovery extends BaseDiscovery {
}
}
/**
* 生成包引用路径
* @param {string} filePath - 文件绝对路径
@ -337,6 +645,35 @@ class PackageDiscovery extends BaseDiscovery {
const fileName = path.basename(filePath, suffix)
return `${protocol}:${fileName}`
}
/**
* 获取RegistryData对象新架构方法
* @returns {Promise<RegistryData>} 包级RegistryData对象
*/
async getRegistryData() {
try {
// 查找package.registry.json文件位置
const packageRoot = await this._findPackageRoot()
const registryPath = path.join(packageRoot, 'src', 'package.registry.json')
// 直接加载RegistryData
const registryData = await RegistryData.fromFile('package', registryPath)
logger.info(`[PackageDiscovery] ✅ 硬编码注册表加载成功,发现 ${registryData.size} 个资源`)
// 输出角色资源信息(调试用)
const roleResources = registryData.getResourcesByProtocol('role')
const roleIds = roleResources.map(r => r.getFullId()).concat(roleResources.map(r => r.getBaseId()))
logger.info(`[PackageDiscovery] 📋 包级角色资源: ${roleIds.join(', ')}`)
return registryData
} catch (error) {
logger.warn(`[PackageDiscovery] Failed to load RegistryData: ${error.message}`)
// 返回空的RegistryData
return new RegistryData('package', null)
}
}
}
module.exports = PackageDiscovery

View File

@ -1,14 +1,18 @@
const BaseDiscovery = require('./BaseDiscovery')
const logger = require('../../../utils/logger')
const fs = require('fs-extra')
const path = require('path')
const CrossPlatformFileScanner = require('./CrossPlatformFileScanner')
const RegistryData = require('../RegistryData')
const ResourceData = require('../ResourceData')
/**
* ProjectDiscovery - 项目级资源发现器
*
* 负责发现项目本地的资源:
* 1. 扫描 .promptx/resource/ 目录
* 2. 发现用户自定义的角色、执行模式、思维模式等
* 1. 优先从 project.registry.json 读取(构建时优化)
* 2. Fallback: 扫描 .promptx/resource/ 目录(动态发现)
* 3. 发现用户自定义的角色、执行模式、思维模式等
*
* 优先级2
*/
@ -16,33 +20,7 @@ class ProjectDiscovery extends BaseDiscovery {
constructor() {
super('PROJECT', 2)
this.fileScanner = new CrossPlatformFileScanner()
}
/**
* 发现项目级资源
* @returns {Promise<Array>} 发现的资源列表
*/
async discover() {
try {
// 1. 查找项目根目录
const projectRoot = await this._findProjectRoot()
// 2. 检查.promptx目录是否存在
const hasPrompxDir = await this._checkPrompxDirectory(projectRoot)
if (!hasPrompxDir) {
return []
}
// 3. 扫描项目资源
const resources = await this._scanProjectResources(projectRoot)
// 4. 规范化所有资源
return resources.map(resource => this.normalizeResource(resource))
} catch (error) {
console.warn(`[ProjectDiscovery] Discovery failed: ${error.message}`)
return []
}
this.registryData = null
}
/**
@ -60,18 +38,77 @@ class ProjectDiscovery extends BaseDiscovery {
return new Map()
}
// 3. 扫描项目资源
const resources = await this._scanProjectResources(projectRoot)
// 3. 优先尝试从注册表加载
const registryMap = await this._loadFromRegistry(projectRoot)
if (registryMap.size > 0) {
logger.debug(`ProjectDiscovery 从注册表加载 ${registryMap.size} 个资源`)
return registryMap
}
// 4. 构建注册表
// 4. Fallback: 动态扫描
logger.debug('ProjectDiscovery 注册表不存在,使用动态扫描')
const resources = await this._scanProjectResources(projectRoot)
return this._buildRegistryFromResources(resources)
} catch (error) {
console.warn(`[ProjectDiscovery] Registry discovery failed: ${error.message}`)
logger.warn(`[ProjectDiscovery] Registry discovery failed: ${error.message}`)
return new Map()
}
}
/**
* 从注册表文件加载资源
* @param {string} projectRoot - 项目根目录
* @returns {Promise<Map>} 资源注册表
*/
async _loadFromRegistry(projectRoot) {
try {
const registryPath = path.join(projectRoot, '.promptx', 'resource', 'project.registry.json')
// 检查注册表文件是否存在
if (!await this._fsExists(registryPath)) {
return new Map()
}
// 读取并解析注册表
this.registryData = await RegistryData.fromFile('project', registryPath)
// 获取分层级资源映射
return this.registryData.getResourceMap(true) // 带前缀
} catch (error) {
logger.warn(`[ProjectDiscovery] Failed to load registry: ${error.message}`)
return new Map()
}
}
/**
* 发现项目级资源 (旧版本兼容方法)
* @returns {Promise<Array>} 发现的资源列表
*/
async discover() {
try {
// 使用新的注册表方法
const registryMap = await this.discoverRegistry()
// 转换为旧格式
const resources = []
for (const [id, reference] of registryMap.entries()) {
resources.push({
id: id.replace(/^project:/, ''), // 移除前缀以保持兼容性
reference: reference
})
}
// 规范化所有资源
return resources.map(resource => this.normalizeResource(resource))
} catch (error) {
logger.warn(`[ProjectDiscovery] Discovery failed: ${error.message}`)
return []
}
}
/**
* 从资源列表构建注册表
* @param {Array} resources - 资源列表
@ -165,13 +202,13 @@ class ProjectDiscovery extends BaseDiscovery {
})
}
} catch (error) {
console.warn(`[ProjectDiscovery] Failed to scan ${resourceType} resources: ${error.message}`)
logger.warn(`[ProjectDiscovery] Failed to scan ${resourceType} resources: ${error.message}`)
}
}
return resources
} catch (error) {
console.warn(`[ProjectDiscovery] Failed to scan project resources: ${error.message}`)
logger.warn(`[ProjectDiscovery] Failed to scan project resources: ${error.message}`)
return []
}
}
@ -239,7 +276,7 @@ class ProjectDiscovery extends BaseDiscovery {
return false
}
} catch (error) {
console.warn(`[ProjectDiscovery] Failed to validate ${filePath}: ${error.message}`)
logger.warn(`[ProjectDiscovery] Failed to validate ${filePath}: ${error.message}`)
return false
}
}
@ -260,11 +297,210 @@ class ProjectDiscovery extends BaseDiscovery {
* @param {string} filePath - 文件路径
* @param {string} protocol - 协议类型
* @param {string} suffix - 文件后缀
* @returns {string} 资源ID (protocol:resourceName)
* @returns {string} 资源ID (对于role类型返回resourceName对于其他类型返回protocol:resourceName)
*/
_extractResourceId(filePath, protocol, suffix) {
const fileName = path.basename(filePath, suffix)
return `${protocol}:${fileName}`
// role类型不需要前缀其他类型需要前缀
if (protocol === 'role') {
return fileName
} else {
return `${protocol}:${fileName}`
}
}
/**
* 生成项目级注册表文件
* @param {string} projectRoot - 项目根目录
* @returns {Promise<RegistryData>} 生成的注册表数据
*/
async generateRegistry(projectRoot) {
const registryPath = path.join(projectRoot, '.promptx', 'resource', 'project.registry.json')
const registryData = RegistryData.createEmpty('project', registryPath)
// 扫描.promptx/resource目录
const resourcesDir = path.join(projectRoot, '.promptx', 'resource')
if (await this._fsExists(resourcesDir)) {
await this._scanDirectory(resourcesDir, registryData)
}
// 保存注册表文件
await registryData.save()
logger.info(`[ProjectDiscovery] ✅ 项目注册表生成完成,发现 ${registryData.size} 个资源`)
return registryData
}
/**
* 扫描目录并添加资源到注册表
* @param {string} resourcesDir - 资源目录
* @param {RegistryData} registryData - 注册表数据
* @private
*/
async _scanDirectory(resourcesDir, registryData) {
// 扫描domain目录
const domainDir = path.join(resourcesDir, 'domain')
if (await this._fsExists(domainDir)) {
await this._scanDomainDirectory(domainDir, registryData)
}
}
/**
* 扫描domain目录项目角色资源
* @param {string} domainDir - domain目录路径
* @param {RegistryData} registryData - 注册表数据
* @private
*/
async _scanDomainDirectory(domainDir, registryData) {
const items = await fs.readdir(domainDir)
for (const item of items) {
const itemPath = path.join(domainDir, item)
const stat = await fs.stat(itemPath)
if (stat.isDirectory()) {
// 查找role文件
const roleFile = path.join(itemPath, `${item}.role.md`)
if (await this._fsExists(roleFile)) {
const reference = `@project://.promptx/resource/domain/${item}/${item}.role.md`
const resourceData = new ResourceData({
id: item,
source: 'project',
protocol: 'role',
name: ResourceData._generateDefaultName(item, 'role'),
description: ResourceData._generateDefaultDescription(item, 'role'),
reference: reference,
metadata: {
filePath: roleFile,
scannedAt: new Date().toISOString()
}
})
registryData.addResource(resourceData)
}
// 查找thought文件
const thoughtDir = path.join(itemPath, 'thought')
if (await this._fsExists(thoughtDir)) {
const thoughtFiles = await fs.readdir(thoughtDir)
for (const thoughtFile of thoughtFiles) {
if (thoughtFile.endsWith('.thought.md')) {
const thoughtId = path.basename(thoughtFile, '.thought.md')
const reference = `@project://.promptx/resource/domain/${item}/thought/${thoughtFile}`
const resourceData = new ResourceData({
id: thoughtId,
source: 'project',
protocol: 'thought',
name: ResourceData._generateDefaultName(thoughtId, 'thought'),
description: ResourceData._generateDefaultDescription(thoughtId, 'thought'),
reference: reference,
metadata: {
filePath: path.join(thoughtDir, thoughtFile),
scannedAt: new Date().toISOString()
}
})
registryData.addResource(resourceData)
}
}
}
// 查找execution文件
const executionDir = path.join(itemPath, 'execution')
if (await this._fsExists(executionDir)) {
const executionFiles = await fs.readdir(executionDir)
for (const execFile of executionFiles) {
if (execFile.endsWith('.execution.md')) {
const execId = path.basename(execFile, '.execution.md')
const reference = `@project://.promptx/resource/domain/${item}/execution/${execFile}`
const resourceData = new ResourceData({
id: execId,
source: 'project',
protocol: 'execution',
name: ResourceData._generateDefaultName(execId, 'execution'),
description: ResourceData._generateDefaultDescription(execId, 'execution'),
reference: reference,
metadata: {
filePath: path.join(executionDir, execFile),
scannedAt: new Date().toISOString()
}
})
registryData.addResource(resourceData)
}
}
}
// 查找knowledge文件
const knowledgeDir = path.join(itemPath, 'knowledge')
if (await this._fsExists(knowledgeDir)) {
const knowledgeFiles = await fs.readdir(knowledgeDir)
for (const knowledgeFile of knowledgeFiles) {
if (knowledgeFile.endsWith('.knowledge.md')) {
const knowledgeId = path.basename(knowledgeFile, '.knowledge.md')
const reference = `@project://.promptx/resource/domain/${item}/knowledge/${knowledgeFile}`
const resourceData = new ResourceData({
id: knowledgeId,
source: 'project',
protocol: 'knowledge',
name: ResourceData._generateDefaultName(knowledgeId, 'knowledge'),
description: ResourceData._generateDefaultDescription(knowledgeId, 'knowledge'),
reference: reference,
metadata: {
filePath: path.join(knowledgeDir, knowledgeFile),
scannedAt: new Date().toISOString()
}
})
registryData.addResource(resourceData)
}
}
}
}
}
}
/**
* 获取RegistryData对象新架构方法
* @returns {Promise<RegistryData>} 项目级RegistryData对象
*/
async getRegistryData() {
try {
const projectRoot = await this._findProjectRoot()
const registryPath = path.join(projectRoot, '.promptx', 'resource', 'project.registry.json')
// 尝试加载现有的注册表文件
if (await this._fsExists(registryPath)) {
const registryData = await RegistryData.fromFile('project', registryPath)
// 检查注册表是否有效(有完整的资源数据)
if (registryData.size > 0 && registryData.resources.length > 0) {
const firstResource = registryData.resources[0]
if (firstResource.id && firstResource.protocol && firstResource.reference) {
logger.info(`[ProjectDiscovery] 📋 从注册表加载 ${registryData.size} 个资源`)
return registryData
}
}
// 如果注册表无效,重新生成
logger.info(`[ProjectDiscovery] 📋 项目注册表无效,重新生成`)
return await this.generateRegistry(projectRoot)
} else {
// 如果没有注册表文件,生成新的
logger.info(`[ProjectDiscovery] 📋 项目注册表不存在,生成新注册表`)
return await this.generateRegistry(projectRoot)
}
} catch (error) {
logger.warn(`[ProjectDiscovery] Failed to load RegistryData: ${error.message}`)
// 返回空的RegistryData
return RegistryData.createEmpty('project', null)
}
}
}

View File

@ -25,11 +25,37 @@ const {
ProtocolInfo
} = require('./types')
// 全局单例 ResourceManager 实例
let globalResourceManager = null
/**
* 获取全局单例 ResourceManager 实例
* 确保整个应用程序使用同一个 ResourceManager 实例
*/
function getGlobalResourceManager() {
if (!globalResourceManager) {
globalResourceManager = new ResourceManager()
}
return globalResourceManager
}
/**
* 重置全局 ResourceManager 实例
* 主要用于测试或需要完全重新初始化的场景
*/
function resetGlobalResourceManager() {
globalResourceManager = null
}
// 导出主接口
module.exports = {
// 主管理器
// 主管理器
ResourceManager,
// 全局单例实例
getGlobalResourceManager,
resetGlobalResourceManager,
// 核心组件
ResourceProtocolParser,
ResourceRegistry,
@ -45,7 +71,7 @@ module.exports = {
ResourceResult,
ProtocolInfo,
// 便捷方法 - 创建默认实例
// 便捷方法 - 创建默认实例(保持向后兼容)
createManager: (options) => new ResourceManager(options),
// 便捷方法 - 快速解析

View File

@ -3,6 +3,7 @@ const fs = require('fs')
const fsPromises = require('fs').promises
const ResourceProtocol = require('./ResourceProtocol')
const { QueryParams } = require('../types')
const logger = require('../../../utils/logger')
/**
* 包协议实现
@ -477,7 +478,7 @@ class PackageProtocol extends ResourceProtocol {
// 在生产环境严格检查,开发环境只警告
const installMode = this.detectInstallMode()
if (installMode === 'development' || installMode === 'npx') {
console.warn(`⚠️ Warning: Path '${relativePath}' not in package.json files field. This may cause issues after publishing.`)
logger.warn(`⚠️ Warning: Path '${relativePath}' not in package.json files field. This may cause issues after publishing.`)
} else {
throw new Error(`Access denied: Path '${relativePath}' is not included in package.json files field`)
}
@ -486,7 +487,7 @@ class PackageProtocol extends ResourceProtocol {
// 如果读取package.json失败在开发模式下允许访问
const installMode = this.detectInstallMode()
if (installMode === 'development' || installMode === 'npx') {
console.warn(`⚠️ Warning: Could not validate file access for '${relativePath}': ${error.message}`)
logger.warn(`⚠️ Warning: Could not validate file access for '${relativePath}': ${error.message}`)
} else {
throw error
}

View File

@ -1,7 +1,9 @@
const fs = require('fs')
const ResourceRegistry = require('./resourceRegistry')
const RegistryData = require('./RegistryData')
const ResourceProtocolParser = require('./resourceProtocolParser')
const DiscoveryManager = require('./discovery/DiscoveryManager')
const logger = require('../../utils/logger')
// 导入协议处理器
const PackageProtocol = require('./protocols/PackageProtocol')
@ -13,7 +15,9 @@ const KnowledgeProtocol = require('./protocols/KnowledgeProtocol')
class ResourceManager {
constructor() {
this.registry = new ResourceRegistry()
// 使用新的RegistryData替代旧的ResourceRegistry
this.registry = new ResourceRegistry() // 保持向后兼容
this.registryData = RegistryData.createEmpty('merged', null) // 新的v2.0注册表
this.protocolParser = new ResourceProtocolParser()
this.parser = new ResourceProtocolParser() // 向后兼容别名
this.discoveryManager = new DiscoveryManager() // 新发现管理器
@ -43,23 +47,58 @@ class ResourceManager {
*/
async initializeWithNewArchitecture() {
try {
// 1. 直接发现并注册资源(无需中间合并步骤
// 1. 清空现有注册表(支持重新初始化
this.registry.clear()
this.registryData.clear()
// 2. 清除发现器缓存
if (this.discoveryManager && typeof this.discoveryManager.clearCache === 'function') {
this.discoveryManager.clearCache()
}
// 3. 直接发现并注册资源到旧的ResourceRegistry保持向后兼容
await this.discoveryManager.discoverAndDirectRegister(this.registry)
// 2. 为逻辑协议设置注册表引用
// 4. 同时填充新的RegistryData
await this.populateRegistryData()
// 5. 为逻辑协议设置注册表引用
this.setupLogicalProtocols()
// 3. 设置初始化状态
// 6. 设置初始化状态
this.initialized = true
// 初始化完成,不输出日志避免干扰用户界面
} catch (error) {
console.warn(`[ResourceManager] New architecture initialization failed: ${error.message}`)
console.warn('[ResourceManager] Continuing with empty registry')
logger.warn(`ResourceManager new architecture initialization failed: ${error.message}`)
logger.warn('ResourceManager continuing with empty registry')
this.initialized = true // 即使失败也标记为已初始化,避免重复尝试
}
}
/**
* 填充新的RegistryData
*/
async populateRegistryData() {
// 清空现有数据
this.registryData.clear()
// 从各个发现器获取RegistryData并合并
for (const discovery of this.discoveryManager.discoveries) {
try {
if (typeof discovery.getRegistryData === 'function') {
const registryData = await discovery.getRegistryData()
if (registryData && registryData.resources) {
// 合并资源到主注册表
this.registryData.merge(registryData, true) // 允许覆盖
}
}
} catch (error) {
logger.warn(`Failed to get RegistryData from ${discovery.source}: ${error.message}`)
}
}
}
/**
* 为逻辑协议设置注册表引用
*/
@ -116,33 +155,53 @@ class ResourceManager {
async loadResource(resourceId) {
try {
// 每次刷新资源(无状态设计)
await this.refreshResources()
// 不再每次刷新资源,依赖初始化时的资源发现
// 处理@!开头的DPML格式如 @!role://java-developer
if (resourceId.startsWith('@!')) {
const parsed = this.protocolParser.parse(resourceId)
const logicalResourceId = `${parsed.protocol}:${parsed.path}`
// 从注册表查找对应的@package://引用
const reference = this.registry.get(logicalResourceId)
if (!reference) {
throw new Error(`Resource not found: ${logicalResourceId}`)
// 从新的RegistryData查找资源
const resourceData = this.registryData.findResourceById(parsed.path, parsed.protocol)
if (!resourceData) {
throw new Error(`Resource not found: ${parsed.protocol}:${parsed.path}`)
}
// 通过协议解析加载内容
const content = await this.loadResourceByProtocol(reference)
const content = await this.loadResourceByProtocol(resourceData.reference)
return {
success: true,
content,
resourceId,
reference
reference: resourceData.reference
}
}
// 处理传统格式(如 role:java-developer
const reference = this.registry.get(resourceId)
// 先尝试从新的RegistryData查找
let reference = null
// 如果包含协议前缀(如 thought:remember
if (resourceId.includes(':')) {
const [protocol, id] = resourceId.split(':', 2)
const resourceData = this.registryData.findResourceById(id, protocol)
if (resourceData) {
reference = resourceData.reference
}
} else {
// 如果没有协议前缀,尝试查找任意协议的资源
const resourceData = this.registryData.findResourceById(resourceId)
if (resourceData) {
reference = resourceData.reference
}
}
// 如果新的RegistryData中没找到回退到旧的registry
if (!reference) {
reference = this.registry.get(resourceId)
}
if (!reference) {
throw new Error(`Resource not found: ${resourceId}`)
}
@ -216,8 +275,7 @@ class ResourceManager {
// 向后兼容方法
async resolve(resourceUrl) {
try {
// 每次刷新资源(无状态设计)
await this.refreshResources()
// 不再每次刷新资源,依赖初始化时的资源发现
// Handle old format: role:java-backend-developer or @package://...
if (resourceUrl.startsWith('@')) {
@ -275,7 +333,7 @@ class ResourceManager {
// 无状态设计不设置initialized标志
} catch (error) {
console.warn(`[ResourceManager] Resource refresh failed: ${error.message}`)
logger.warn(`ResourceManager resource refresh failed: ${error.message}`)
// 失败时保持注册表为空状态,下次调用时重试
}
}

View File

@ -1,3 +1,5 @@
const logger = require('../../utils/logger')
/**
* 资源注册表
* 新架构中用于存储动态发现的资源映射关系
@ -70,27 +72,27 @@ class ResourceRegistry {
* @param {string} title - 可选标题
*/
printAll(title = '注册表资源清单') {
console.log(`\n📋 ${title}`)
console.log('='.repeat(50))
logger.info(`\n📋 ${title}`)
logger.info('='.repeat(50))
if (this.size === 0) {
console.log('🔍 注册表为空')
logger.info('🔍 注册表为空')
return
}
console.log(`📊 总计: ${this.size} 个资源\n`)
logger.info(`📊 总计: ${this.size} 个资源\n`)
// 按协议分组显示
const groupedResources = this.groupByProtocol()
for (const [protocol, resources] of Object.entries(groupedResources)) {
console.log(`🔖 ${protocol.toUpperCase()} 协议 (${resources.length}个):`)
logger.info(`🔖 ${protocol.toUpperCase()} 协议 (${resources.length}个):`)
resources.forEach(({ id, reference }) => {
const resourceName = id.split(':')[1] || id
console.log(`${resourceName}`)
console.log(` └─ ${reference}`)
logger.info(`${resourceName}`)
logger.info(` └─ ${reference}`)
})
console.log('')
logger.info('')
}
}