🎯 PromptX v0.0.1 完整实现 - 五大锦囊命令、AI记忆系统、角色系统、PATEOAS状态机、DPML协议全部完成
This commit is contained in:
78
src/lib/core/resource/protocols/ExecutionProtocol.js
Normal file
78
src/lib/core/resource/protocols/ExecutionProtocol.js
Normal file
@ -0,0 +1,78 @@
|
||||
const ResourceProtocol = require('./ResourceProtocol');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* 执行模式协议处理器
|
||||
* 处理 execution:// 协议的资源解析
|
||||
*/
|
||||
class ExecutionProtocol extends ResourceProtocol {
|
||||
constructor() {
|
||||
super('execution');
|
||||
this.registry = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置注册表
|
||||
*/
|
||||
setRegistry(registry) {
|
||||
this.registry = registry || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协议信息
|
||||
*/
|
||||
getProtocolInfo() {
|
||||
return {
|
||||
name: 'execution',
|
||||
description: '执行模式资源协议',
|
||||
location: 'execution://{execution_id}',
|
||||
examples: [
|
||||
'execution://deal-at-reference',
|
||||
'execution://prompt-developer',
|
||||
'execution://memory-trigger'
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析资源路径
|
||||
*/
|
||||
async resolvePath(resourcePath, queryParams) {
|
||||
const executionId = resourcePath.trim();
|
||||
|
||||
if (!this.registry[executionId]) {
|
||||
throw new Error(`执行模式 "${executionId}" 未在注册表中找到`);
|
||||
}
|
||||
|
||||
let resolvedPath = this.registry[executionId];
|
||||
|
||||
// 处理 @package:// 前缀
|
||||
if (resolvedPath.startsWith('@package://')) {
|
||||
resolvedPath = resolvedPath.replace('@package://', '');
|
||||
}
|
||||
|
||||
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 = ExecutionProtocol;
|
||||
@ -17,6 +17,14 @@ class PackageProtocol extends ResourceProtocol {
|
||||
this.installModeCache = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置注册表(保持与其他协议的一致性)
|
||||
*/
|
||||
setRegistry(registry) {
|
||||
// Package协议不使用注册表,但为了一致性提供此方法
|
||||
this.registry = registry || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协议信息
|
||||
*/
|
||||
|
||||
@ -35,6 +35,14 @@ class ProjectProtocol extends ResourceProtocol {
|
||||
this.projectRootCache = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置注册表(保持与其他协议的一致性)
|
||||
*/
|
||||
setRegistry(registry) {
|
||||
// Project协议不使用注册表,但为了一致性提供此方法
|
||||
this.registry = registry || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协议信息
|
||||
* @returns {object} 协议信息
|
||||
|
||||
255
src/lib/core/resource/protocols/PromptProtocol.js
Normal file
255
src/lib/core/resource/protocols/PromptProtocol.js
Normal file
@ -0,0 +1,255 @@
|
||||
const ResourceProtocol = require('./ResourceProtocol');
|
||||
const path = require('path');
|
||||
const fs = require('fs').promises;
|
||||
const { glob } = require('glob');
|
||||
const { promisify } = require('util');
|
||||
|
||||
/**
|
||||
* PromptX内置提示词资源协议实现
|
||||
* 实现@prompt://协议,用于访问PromptX内置的提示词资源
|
||||
*/
|
||||
class PromptProtocol extends ResourceProtocol {
|
||||
constructor(options = {}) {
|
||||
super('prompt', options);
|
||||
|
||||
// PromptX 内置资源注册表
|
||||
this.registry = new Map([
|
||||
['protocols', '@package://prompt/protocol/**/*.md'],
|
||||
['core', '@package://prompt/core/**/*.md'],
|
||||
['domain', '@package://prompt/domain/**/*.md'],
|
||||
['resource', '@package://prompt/resource/**/*.md'],
|
||||
['bootstrap', '@package://bootstrap.md']
|
||||
]);
|
||||
|
||||
// 依赖的其他协议
|
||||
this.packageProtocol = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置依赖的协议
|
||||
* @param {PackageProtocol} packageProtocol - 包协议实例
|
||||
*/
|
||||
setPackageProtocol(packageProtocol) {
|
||||
this.packageProtocol = packageProtocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置注册表
|
||||
*/
|
||||
setRegistry(registry) {
|
||||
this.registry = registry || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协议信息
|
||||
*/
|
||||
getProtocolInfo() {
|
||||
return {
|
||||
name: 'prompt',
|
||||
description: 'PromptX内置提示词资源协议',
|
||||
location: 'prompt://{resource_id}',
|
||||
examples: [
|
||||
'prompt://protocols',
|
||||
'prompt://core',
|
||||
'prompt://domain',
|
||||
'prompt://bootstrap'
|
||||
],
|
||||
availableResources: Array.from(this.registry.keys()),
|
||||
params: this.getSupportedParams()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 支持的查询参数
|
||||
*/
|
||||
getSupportedParams() {
|
||||
return {
|
||||
...super.getSupportedParams(),
|
||||
merge: 'boolean - 是否合并多个文件内容',
|
||||
separator: 'string - 文件间分隔符',
|
||||
include_filename: 'boolean - 是否包含文件名标题'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证资源路径
|
||||
*/
|
||||
validatePath(resourcePath) {
|
||||
if (!super.validatePath(resourcePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否在注册表中
|
||||
return this.registry.has(resourcePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析资源路径
|
||||
*/
|
||||
async resolvePath(resourcePath, queryParams) {
|
||||
// 验证资源是否存在
|
||||
if (!this.registry.has(resourcePath)) {
|
||||
throw new Error(`未找到 prompt 资源: ${resourcePath}。可用资源: ${Array.from(this.registry.keys()).join(', ')}`);
|
||||
}
|
||||
|
||||
// 获取对应的包路径
|
||||
const packagePath = this.registry.get(resourcePath);
|
||||
return packagePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载资源内容
|
||||
*/
|
||||
async loadContent(packagePath, queryParams) {
|
||||
// 确保有包协议依赖
|
||||
if (!this.packageProtocol) {
|
||||
throw new Error('PromptProtocol 需要 PackageProtocol 依赖');
|
||||
}
|
||||
|
||||
// 检查是否是通配符路径
|
||||
if (packagePath.includes('**') || packagePath.includes('*')) {
|
||||
return await this.loadMultipleFiles(packagePath, queryParams);
|
||||
} else {
|
||||
return await this.loadSingleFile(packagePath, queryParams);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载单个文件
|
||||
*/
|
||||
async loadSingleFile(packagePath, queryParams) {
|
||||
try {
|
||||
// 移除协议前缀
|
||||
const cleanPath = packagePath.replace('@package://', '');
|
||||
const result = await this.packageProtocol.loadContent(cleanPath, queryParams);
|
||||
return result.content || result;
|
||||
} catch (error) {
|
||||
throw new Error(`加载单个文件失败 ${packagePath}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载多个文件(通配符支持)
|
||||
*/
|
||||
async loadMultipleFiles(packagePath, queryParams) {
|
||||
try {
|
||||
// 获取包根目录
|
||||
const packageRoot = await this.packageProtocol.getPackageRoot();
|
||||
|
||||
// 移除协议前缀并构建搜索路径
|
||||
const cleanPath = packagePath.replace('@package://', '');
|
||||
const searchPattern = path.join(packageRoot, cleanPath);
|
||||
|
||||
// 使用 glob 查找匹配的文件
|
||||
const files = await glob(searchPattern, {
|
||||
ignore: ['**/node_modules/**', '**/.git/**']
|
||||
});
|
||||
|
||||
if (files.length === 0) {
|
||||
throw new Error(`没有找到匹配的文件: ${packagePath}`);
|
||||
}
|
||||
|
||||
// 读取所有文件内容
|
||||
const contents = [];
|
||||
for (const filePath of files.sort()) {
|
||||
try {
|
||||
const content = await fs.readFile(filePath, 'utf8');
|
||||
const relativePath = path.relative(packageRoot, filePath);
|
||||
|
||||
contents.push({
|
||||
path: relativePath,
|
||||
content: content
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn(`警告: 无法读取文件 ${filePath}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 合并内容
|
||||
return this.mergeContents(contents, queryParams);
|
||||
} catch (error) {
|
||||
throw new Error(`加载多个文件失败 ${packagePath}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并多个文件内容
|
||||
*/
|
||||
mergeContents(contents, queryParams) {
|
||||
const merge = queryParams?.get('merge') !== 'false'; // 默认合并
|
||||
const separator = queryParams?.get('separator') || '\n\n---\n\n';
|
||||
const includeFilename = queryParams?.get('include_filename') !== 'false'; // 默认包含文件名
|
||||
|
||||
if (!merge) {
|
||||
// 不合并,返回 JSON 格式
|
||||
return JSON.stringify(contents, null, 2);
|
||||
}
|
||||
|
||||
// 合并所有内容
|
||||
const mergedParts = contents.map(({ path, content }) => {
|
||||
let part = '';
|
||||
|
||||
if (includeFilename) {
|
||||
part += `# ${path}\n\n`;
|
||||
}
|
||||
|
||||
part += content;
|
||||
|
||||
return part;
|
||||
});
|
||||
|
||||
return mergedParts.join(separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查资源是否存在
|
||||
*/
|
||||
async exists(resourcePath, queryParams) {
|
||||
try {
|
||||
const packagePath = await this.resolvePath(resourcePath, queryParams);
|
||||
|
||||
if (packagePath.includes('**') || packagePath.includes('*')) {
|
||||
// 通配符路径:检查是否有匹配的文件
|
||||
const packageRoot = await this.packageProtocol.getPackageRoot();
|
||||
const cleanPath = packagePath.replace('@package://', '');
|
||||
const searchPattern = path.join(packageRoot, cleanPath);
|
||||
const files = await glob(searchPattern);
|
||||
return files.length > 0;
|
||||
} else {
|
||||
// 单个文件:检查文件是否存在
|
||||
const cleanPath = packagePath.replace('@package://', '');
|
||||
return await this.packageProtocol.exists(cleanPath, queryParams);
|
||||
}
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出所有可用资源
|
||||
*/
|
||||
listResources() {
|
||||
return Array.from(this.registry.entries()).map(([key, value]) => ({
|
||||
id: key,
|
||||
path: value,
|
||||
description: this.getResourceDescription(key)
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资源描述
|
||||
*/
|
||||
getResourceDescription(resourceId) {
|
||||
const descriptions = {
|
||||
'protocols': 'DPML协议规范文档',
|
||||
'core': '核心思维和执行模式',
|
||||
'domain': '领域专家角色定义',
|
||||
'resource': '资源管理和路径解析',
|
||||
'bootstrap': 'PromptX启动引导文件'
|
||||
};
|
||||
|
||||
return descriptions[resourceId] || '未知资源';
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PromptProtocol;
|
||||
79
src/lib/core/resource/protocols/RoleProtocol.js
Normal file
79
src/lib/core/resource/protocols/RoleProtocol.js
Normal file
@ -0,0 +1,79 @@
|
||||
const ResourceProtocol = require('./ResourceProtocol');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* AI角色协议处理器
|
||||
* 处理 role:// 协议的资源解析,直接加载完整role文件
|
||||
*/
|
||||
class RoleProtocol extends ResourceProtocol {
|
||||
constructor() {
|
||||
super('role');
|
||||
this.registry = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置注册表
|
||||
*/
|
||||
setRegistry(registry) {
|
||||
this.registry = registry || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协议信息
|
||||
*/
|
||||
getProtocolInfo() {
|
||||
return {
|
||||
name: 'role',
|
||||
description: 'AI角色资源协议',
|
||||
location: 'role://{role_id}',
|
||||
examples: [
|
||||
'role://video-copywriter',
|
||||
'role://product-owner',
|
||||
'role://assistant',
|
||||
'role://prompt-developer'
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析资源路径
|
||||
*/
|
||||
async resolvePath(resourcePath, queryParams) {
|
||||
const roleId = resourcePath.trim();
|
||||
|
||||
if (!this.registry[roleId]) {
|
||||
throw new Error(`角色 "${roleId}" 未在注册表中找到。可用角色:${Object.keys(this.registry).join(', ')}`);
|
||||
}
|
||||
|
||||
let resolvedPath = this.registry[roleId];
|
||||
|
||||
// 处理 @package:// 前缀
|
||||
if (resolvedPath.startsWith('@package://')) {
|
||||
resolvedPath = resolvedPath.replace('@package://', '');
|
||||
}
|
||||
|
||||
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 = RoleProtocol;
|
||||
77
src/lib/core/resource/protocols/ThoughtProtocol.js
Normal file
77
src/lib/core/resource/protocols/ThoughtProtocol.js
Normal file
@ -0,0 +1,77 @@
|
||||
const ResourceProtocol = require('./ResourceProtocol');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* 思维模式协议处理器
|
||||
* 处理 thought:// 协议的资源解析
|
||||
*/
|
||||
class ThoughtProtocol extends ResourceProtocol {
|
||||
constructor() {
|
||||
super('thought');
|
||||
this.registry = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置注册表
|
||||
*/
|
||||
setRegistry(registry) {
|
||||
this.registry = registry || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协议信息
|
||||
*/
|
||||
getProtocolInfo() {
|
||||
return {
|
||||
name: 'thought',
|
||||
description: '思维模式资源协议',
|
||||
location: 'thought://{thought_id}',
|
||||
examples: [
|
||||
'thought://prompt-developer',
|
||||
'thought://product-owner'
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析资源路径
|
||||
*/
|
||||
async resolvePath(resourcePath, queryParams) {
|
||||
const thoughtId = resourcePath.trim();
|
||||
|
||||
if (!this.registry[thoughtId]) {
|
||||
throw new Error(`思维模式 "${thoughtId}" 未在注册表中找到`);
|
||||
}
|
||||
|
||||
let resolvedPath = this.registry[thoughtId];
|
||||
|
||||
// 处理 @package:// 前缀
|
||||
if (resolvedPath.startsWith('@package://')) {
|
||||
resolvedPath = resolvedPath.replace('@package://', '');
|
||||
}
|
||||
|
||||
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 = ThoughtProtocol;
|
||||
@ -49,6 +49,14 @@ class UserProtocol extends ResourceProtocol {
|
||||
this.dirCache = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置注册表(保持与其他协议的一致性)
|
||||
*/
|
||||
setRegistry(registry) {
|
||||
// User协议不使用注册表,但为了一致性提供此方法
|
||||
this.registry = registry || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协议信息
|
||||
* @returns {object} 协议信息
|
||||
|
||||
@ -2,319 +2,140 @@ const ResourceProtocolParser = require('./resourceProtocolParser');
|
||||
const ResourceRegistry = require('./resourceRegistry');
|
||||
const { ResourceResult } = require('./types');
|
||||
const logger = require('../../utils/logger');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
// 导入协议实现
|
||||
const PackageProtocol = require('./protocols/PackageProtocol');
|
||||
const ProjectProtocol = require('./protocols/ProjectProtocol');
|
||||
const UserProtocol = require('./protocols/UserProtocol');
|
||||
const PromptProtocol = require('./protocols/PromptProtocol');
|
||||
|
||||
/**
|
||||
* 资源管理器
|
||||
* 基于DPML资源协议的统一资源管理入口
|
||||
* 资源管理器 - 统一管理各种协议的资源加载
|
||||
*/
|
||||
class ResourceManager {
|
||||
constructor(options = {}) {
|
||||
this.parser = new ResourceProtocolParser();
|
||||
this.registry = new ResourceRegistry();
|
||||
this.workingDirectory = options.workingDirectory || process.cwd();
|
||||
|
||||
// 暂时直接实现简单的加载功能,后续可扩展为独立组件
|
||||
this.cache = new Map();
|
||||
this.enableCache = options.enableCache !== false;
|
||||
constructor() {
|
||||
this.protocolHandlers = new Map();
|
||||
this.registry = null;
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析并获取资源
|
||||
* @param {string} resourceRef - DPML资源引用
|
||||
* @param {object} options - 选项
|
||||
* @returns {Promise<ResourceResult>} 资源结果
|
||||
* 初始化资源管理器
|
||||
*/
|
||||
async resolve(resourceRef, options = {}) {
|
||||
async initialize() {
|
||||
if (this.initialized) return;
|
||||
|
||||
try {
|
||||
logger.debug(`Resolving resource: ${resourceRef}`);
|
||||
// 从统一注册表加载所有协议信息
|
||||
await this.loadUnifiedRegistry();
|
||||
|
||||
// 1. 解析资源引用
|
||||
const parsed = this.parser.parse(resourceRef);
|
||||
logger.debug(`Parsed reference:`, parsed);
|
||||
|
||||
// 2. 通过注册表解析路径
|
||||
const resolvedPath = this.registry.resolve(parsed.protocol, parsed.path);
|
||||
logger.debug(`Resolved path: ${resolvedPath}`);
|
||||
|
||||
// 3. 处理可能的嵌套引用
|
||||
if (resolvedPath.startsWith('@')) {
|
||||
logger.debug(`Detected nested reference: ${resolvedPath}`);
|
||||
return await this.resolve(resolvedPath, options);
|
||||
}
|
||||
|
||||
// 4. 加载资源内容
|
||||
let content = await this.loadResource(resolvedPath, parsed, options);
|
||||
// 注册协议处理器
|
||||
await this.registerProtocolHandlers();
|
||||
|
||||
// 5. 检查内容是否是另一个资源引用(用于嵌套引用)
|
||||
if (content.trim().startsWith('@')) {
|
||||
logger.debug(`Content is a nested reference: ${content.trim()}`);
|
||||
return await this.resolve(content.trim(), options);
|
||||
}
|
||||
|
||||
// 6. 创建结果
|
||||
const result = ResourceResult.success(content, {
|
||||
originalRef: resourceRef,
|
||||
resolvedPath: resolvedPath,
|
||||
protocol: parsed.protocol,
|
||||
loadingSemantics: parsed.loadingSemantics,
|
||||
queryParams: parsed.queryParams.getAll()
|
||||
});
|
||||
|
||||
result.sources = [resolvedPath];
|
||||
result.format = options.format || 'text';
|
||||
|
||||
logger.debug(`Resource resolved successfully`);
|
||||
return result;
|
||||
|
||||
this.initialized = true;
|
||||
} catch (error) {
|
||||
logger.error(`Failed to resolve resource ${resourceRef}:`, error.message);
|
||||
return ResourceResult.error(error, { originalRef: resourceRef });
|
||||
throw new Error(`ResourceManager初始化失败: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量解析多个资源
|
||||
* @param {string[]} resourceRefs - 资源引用列表
|
||||
* @param {object} options - 选项
|
||||
* @returns {Promise<ResourceResult[]>} 资源结果列表
|
||||
* 加载统一资源注册表
|
||||
*/
|
||||
async resolveMultiple(resourceRefs, options = {}) {
|
||||
const results = [];
|
||||
async loadUnifiedRegistry() {
|
||||
const registryPath = path.resolve(__dirname, '../../../resource.registry.json');
|
||||
|
||||
for (const ref of resourceRefs) {
|
||||
const result = await this.resolve(ref, options);
|
||||
results.push(result);
|
||||
if (!await fs.pathExists(registryPath)) {
|
||||
throw new Error(`统一资源注册表文件不存在: ${registryPath}`);
|
||||
}
|
||||
|
||||
const registryContent = await fs.readJSON(registryPath);
|
||||
this.registry = registryContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册协议处理器
|
||||
*/
|
||||
async registerProtocolHandlers() {
|
||||
// 动态导入协议处理器
|
||||
const protocolsDir = path.join(__dirname, 'protocols');
|
||||
const protocolFiles = await fs.readdir(protocolsDir);
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载单个资源
|
||||
* @param {string} resourcePath - 资源路径
|
||||
* @param {ParsedReference} parsed - 解析后的引用
|
||||
* @param {object} options - 选项
|
||||
* @returns {Promise<string>} 资源内容
|
||||
*/
|
||||
async loadResource(resourcePath, parsed, options = {}) {
|
||||
// 检查缓存
|
||||
const cacheKey = `${resourcePath}:${JSON.stringify(parsed.queryParams.getAll())}`;
|
||||
if (this.enableCache && this.cache.has(cacheKey)) {
|
||||
logger.debug(`Cache hit for: ${cacheKey}`);
|
||||
return this.cache.get(cacheKey);
|
||||
}
|
||||
|
||||
let content = '';
|
||||
|
||||
// 根据协议类型加载资源
|
||||
if (parsed.protocol === 'file' || resourcePath.startsWith('/') || resourcePath.includes('./')) {
|
||||
content = await this.loadFileResource(resourcePath, parsed.queryParams);
|
||||
} else if (parsed.protocol === 'http' || parsed.protocol === 'https') {
|
||||
content = await this.loadHttpResource(resourcePath, parsed.queryParams);
|
||||
} else if (parsed.protocol === 'prompt') {
|
||||
// prompt协议通过注册表已经解析为文件路径
|
||||
content = await this.loadFileResource(resourcePath, parsed.queryParams);
|
||||
} else {
|
||||
throw new Error(`Unsupported protocol: ${parsed.protocol}`);
|
||||
}
|
||||
|
||||
// 应用查询参数过滤
|
||||
content = this.applyQueryParams(content, parsed.queryParams);
|
||||
|
||||
// 缓存结果
|
||||
if (this.enableCache) {
|
||||
this.cache.set(cacheKey, content);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载文件资源
|
||||
* @param {string} filePath - 文件路径
|
||||
* @param {QueryParams} queryParams - 查询参数
|
||||
* @returns {Promise<string>} 文件内容
|
||||
*/
|
||||
async loadFileResource(filePath, queryParams) {
|
||||
const fs = require('fs').promises;
|
||||
const path = require('path');
|
||||
|
||||
// 处理相对路径
|
||||
let fullPath = filePath;
|
||||
if (!path.isAbsolute(filePath)) {
|
||||
fullPath = path.resolve(this.workingDirectory, filePath);
|
||||
}
|
||||
|
||||
// 处理通配符
|
||||
if (fullPath.includes('*')) {
|
||||
return await this.loadGlobPattern(fullPath, queryParams);
|
||||
}
|
||||
|
||||
// 读取单个文件
|
||||
try {
|
||||
const content = await fs.readFile(fullPath, 'utf8');
|
||||
return content;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to read file ${fullPath}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载通配符模式的文件
|
||||
* @param {string} pattern - 通配符模式
|
||||
* @param {QueryParams} queryParams - 查询参数
|
||||
* @returns {Promise<string>} 合并后的内容
|
||||
*/
|
||||
async loadGlobPattern(pattern, queryParams) {
|
||||
const fs = require('fs').promises;
|
||||
const path = require('path');
|
||||
const { glob } = require('glob');
|
||||
|
||||
try {
|
||||
const files = await glob(pattern, { nodir: true });
|
||||
|
||||
if (files.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// 排序文件
|
||||
files.sort();
|
||||
|
||||
const contents = [];
|
||||
for (const file of files) {
|
||||
try {
|
||||
const content = await fs.readFile(file, 'utf8');
|
||||
const relativePath = path.relative(this.workingDirectory, file);
|
||||
|
||||
// 添加文件分隔符
|
||||
const separator = '='.repeat(80);
|
||||
const header = `### 文件: ${relativePath}`;
|
||||
contents.push(`${separator}\n${header}\n${separator}\n\n${content}`);
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to read file ${file}: ${error.message}`);
|
||||
for (const file of protocolFiles) {
|
||||
if (file.endsWith('.js') && file !== 'ResourceProtocol.js') {
|
||||
// 将文件名映射到协议名:ExecutionProtocol.js -> execution
|
||||
const protocolName = file.replace('Protocol.js', '').toLowerCase();
|
||||
const ProtocolClass = require(path.join(protocolsDir, file));
|
||||
const protocolHandler = new ProtocolClass();
|
||||
|
||||
// 从统一注册表获取协议配置
|
||||
const protocolConfig = this.registry.protocols[protocolName];
|
||||
if (protocolConfig && protocolConfig.registry) {
|
||||
protocolHandler.setRegistry(protocolConfig.registry);
|
||||
}
|
||||
}
|
||||
|
||||
return contents.join('\n\n');
|
||||
} catch (error) {
|
||||
throw new Error(`Glob pattern error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载HTTP资源
|
||||
* @param {string} url - URL地址
|
||||
* @param {QueryParams} queryParams - 查询参数
|
||||
* @returns {Promise<string>} 响应内容
|
||||
*/
|
||||
async loadHttpResource(url, queryParams) {
|
||||
// 简单实现,实际项目中可以使用axios等库
|
||||
const https = require('https');
|
||||
const http = require('http');
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const client = url.startsWith('https://') ? https : http;
|
||||
|
||||
client.get(url, (res) => {
|
||||
let data = '';
|
||||
|
||||
res.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
|
||||
res.on('end', () => {
|
||||
resolve(data);
|
||||
});
|
||||
}).on('error', (err) => {
|
||||
reject(new Error(`HTTP request failed: ${err.message}`));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用查询参数过滤
|
||||
* @param {string} content - 原始内容
|
||||
* @param {QueryParams} queryParams - 查询参数
|
||||
* @returns {string} 处理后的内容
|
||||
*/
|
||||
applyQueryParams(content, queryParams) {
|
||||
let result = content;
|
||||
|
||||
// 处理行范围过滤
|
||||
if (queryParams.line) {
|
||||
result = this.applyLineFilter(result, queryParams.line);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用行范围过滤
|
||||
* @param {string} content - 内容
|
||||
* @param {string} lineRange - 行范围 "5-10"
|
||||
* @returns {string} 过滤后的内容
|
||||
*/
|
||||
applyLineFilter(content, lineRange) {
|
||||
const lines = content.split('\n');
|
||||
|
||||
if (lineRange.includes('-')) {
|
||||
const [start, end] = lineRange.split('-').map(n => parseInt(n.trim()));
|
||||
if (!isNaN(start) && !isNaN(end)) {
|
||||
// 转换为0基索引,并确保范围有效
|
||||
const startIdx = Math.max(0, start - 1);
|
||||
const endIdx = Math.min(lines.length, end);
|
||||
return lines.slice(startIdx, endIdx).join('\n');
|
||||
}
|
||||
} else {
|
||||
const lineNum = parseInt(lineRange);
|
||||
if (!isNaN(lineNum) && lineNum > 0 && lineNum <= lines.length) {
|
||||
return lines[lineNum - 1];
|
||||
this.protocolHandlers.set(protocolName, protocolHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析资源路径并获取内容
|
||||
*/
|
||||
async resolveResource(resourceUrl) {
|
||||
await this.initialize();
|
||||
|
||||
const urlMatch = resourceUrl.match(/^([a-zA-Z]+):\/\/(.+)$/);
|
||||
if (!urlMatch) {
|
||||
throw new Error(`无效的资源URL格式: ${resourceUrl}`);
|
||||
}
|
||||
|
||||
const [, protocol, path] = urlMatch;
|
||||
const handler = this.protocolHandlers.get(protocol);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证资源引用
|
||||
* @param {string} resourceRef - 资源引用
|
||||
* @returns {boolean} 是否有效
|
||||
*/
|
||||
isValidReference(resourceRef) {
|
||||
try {
|
||||
const parsed = this.parser.parse(resourceRef);
|
||||
return this.registry.validateReference(parsed.protocol, parsed.path);
|
||||
} catch (error) {
|
||||
return false;
|
||||
if (!handler) {
|
||||
throw new Error(`未注册的协议: ${protocol}`);
|
||||
}
|
||||
|
||||
return await handler.resolve(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注册表信息
|
||||
* @param {string} protocol - 协议名(可选)
|
||||
* @returns {object} 注册表信息
|
||||
* resolve方法的别名,保持向后兼容
|
||||
*/
|
||||
getRegistryInfo(protocol) {
|
||||
if (protocol) {
|
||||
return this.registry.getProtocolInfo(protocol);
|
||||
async resolve(resourceUrl) {
|
||||
return await this.resolveResource(resourceUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协议的注册表信息
|
||||
*/
|
||||
getProtocolRegistry(protocol) {
|
||||
if (!this.registry) {
|
||||
throw new Error('ResourceManager未初始化');
|
||||
}
|
||||
return this.registry.getRegistryInfo();
|
||||
|
||||
const protocolConfig = this.registry.protocols[protocol];
|
||||
return protocolConfig ? protocolConfig.registry : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出可用协议
|
||||
* @returns {string[]} 协议列表
|
||||
* 获取所有已注册的协议
|
||||
*/
|
||||
listProtocols() {
|
||||
return this.registry.listProtocols();
|
||||
getAvailableProtocols() {
|
||||
return this.registry ? Object.keys(this.registry.protocols) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
* 获取协议的描述信息
|
||||
*/
|
||||
clearCache() {
|
||||
this.cache.clear();
|
||||
logger.debug('Resource cache cleared');
|
||||
getProtocolInfo(protocol) {
|
||||
if (!this.registry) {
|
||||
throw new Error('ResourceManager未初始化');
|
||||
}
|
||||
|
||||
return this.registry.protocols[protocol];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user