feat: 完成DPML协议体系0~1阶段开发 - 三层协议架构100%实现,智能路径检测系统,@package://与package.json完美集成,用户项目集成方案,CLI框架完整实现,132/137核心测试通过(96.3%通过率)

This commit is contained in:
sean
2025-05-31 13:03:26 +08:00
parent 3338b7c21f
commit be285f55b8
89 changed files with 6071 additions and 467 deletions

View File

@ -0,0 +1,67 @@
/**
* PromptX Resource Module
* 基于DPML资源协议的统一资源管理模块
*
* 提供完整的资源协议解析、注册表管理、资源加载功能
*/
// 核心管理器
const ResourceManager = require('./resourceManager');
// 核心组件
const ResourceProtocolParser = require('./resourceProtocolParser');
const ResourceRegistry = require('./resourceRegistry');
// 数据类型
const {
LoadingSemantics,
ParsedReference,
QueryParams,
NestedReference,
ResourceContent,
LazyResource,
ProcessedResult,
ResourceResult,
ProtocolInfo
} = require('./types');
// 导出主接口
module.exports = {
// 主管理器
ResourceManager,
// 核心组件
ResourceProtocolParser,
ResourceRegistry,
// 数据类型
LoadingSemantics,
ParsedReference,
QueryParams,
NestedReference,
ResourceContent,
LazyResource,
ProcessedResult,
ResourceResult,
ProtocolInfo,
// 便捷方法 - 创建默认实例
createManager: (options) => new ResourceManager(options),
// 便捷方法 - 快速解析
parse: (resourceRef) => {
const parser = new ResourceProtocolParser();
return parser.parse(resourceRef);
},
// 便捷方法 - 快速验证
validate: (resourceRef) => {
try {
const parser = new ResourceProtocolParser();
parser.parse(resourceRef);
return true;
} catch (error) {
return false;
}
}
};

View File

@ -0,0 +1,505 @@
const path = require('path');
const fs = require('fs');
const fsPromises = require('fs').promises;
const ResourceProtocol = require('./ResourceProtocol');
const { QueryParams } = require('../types');
/**
* 包协议实现
* 实现@package://协议智能检测并访问NPM包资源
* 支持本地开发、npm install、npm -g、npx、monorepo等场景
*/
class PackageProtocol extends ResourceProtocol {
constructor(options = {}) {
super('package', options);
// 包安装模式检测缓存
this.installModeCache = new Map();
}
/**
* 获取协议信息
*/
getProtocolInfo() {
return {
name: this.name,
description: '包协议 - 智能访问NPM包资源支持多种安装模式',
examples: [
'@package://package.json',
'@package://src/index.js',
'@package://docs/README.md',
'@package://prompt/core/thought.md',
'@package://templates/basic/template.md'
],
installModes: [
'development', // 开发模式
'local', // 本地npm install
'global', // 全局npm install -g
'npx', // npx执行
'monorepo', // monorepo workspace
'link' // npm link
]
};
}
/**
* 检测当前包安装模式
*/
detectInstallMode() {
const cacheKey = 'currentInstallMode';
if (this.installModeCache.has(cacheKey)) {
return this.installModeCache.get(cacheKey);
}
const mode = this._performInstallModeDetection();
this.installModeCache.set(cacheKey, mode);
return mode;
}
/**
* 执行安装模式检测
*/
_performInstallModeDetection() {
const cwd = process.cwd();
const execPath = process.argv[0];
const scriptPath = process.argv[1];
// 检测npx执行
if (this._isNpxExecution()) {
return 'npx';
}
// 检测全局安装
if (this._isGlobalInstall()) {
return 'global';
}
// 检测开发模式
if (this._isDevelopmentMode()) {
return 'development';
}
// 检测monorepo
if (this._isMonorepoWorkspace()) {
return 'monorepo';
}
// 检测npm link
if (this._isNpmLink()) {
return 'link';
}
// 默认为本地安装
return 'local';
}
/**
* 检测是否是npx执行
*/
_isNpxExecution() {
// 检查环境变量
if (process.env.npm_execpath && process.env.npm_execpath.includes('npx')) {
return true;
}
// 检查npm_config_cache路径
if (process.env.npm_config_cache && process.env.npm_config_cache.includes('_npx')) {
return true;
}
// 检查执行路径
const scriptPath = process.argv[1];
if (scriptPath && scriptPath.includes('_npx')) {
return true;
}
return false;
}
/**
* 检测是否是全局安装
*/
_isGlobalInstall() {
const currentPath = __dirname;
// 常见全局安装路径
const globalPaths = [
'/usr/lib/node_modules',
'/usr/local/lib/node_modules',
'/opt/homebrew/lib/node_modules',
path.join(process.env.HOME || '', '.npm-global'),
path.join(process.env.APPDATA || '', 'npm', 'node_modules'),
path.join(process.env.PREFIX || '', 'lib', 'node_modules')
];
return globalPaths.some(globalPath =>
currentPath.startsWith(globalPath)
);
}
/**
* 检测是否是开发模式
*/
_isDevelopmentMode() {
// 检查NODE_ENV
if (process.env.NODE_ENV === 'development') {
return true;
}
// 检查是否在node_modules外
const currentPath = __dirname;
if (!currentPath.includes('node_modules')) {
return true;
}
// 检查package.json中的main字段是否指向源文件
try {
const packageJsonPath = this.findPackageJson();
if (packageJsonPath) {
const packageJson = require(packageJsonPath);
const mainFile = packageJson.main || 'index.js';
return mainFile.startsWith('src/') || mainFile.startsWith('lib/');
}
} catch (error) {
// 忽略错误,继续其他检测
}
return false;
}
/**
* 检测是否是monorepo workspace
*/
_isMonorepoWorkspace() {
try {
const packageJsonPath = this.findPackageJson();
if (packageJsonPath) {
const packageJson = require(packageJsonPath);
// 检查workspaces字段
if (packageJson.workspaces) {
return true;
}
// 检查是否在workspace包内
const rootPackageJsonPath = this.findRootPackageJson();
if (rootPackageJsonPath && rootPackageJsonPath !== packageJsonPath) {
const rootPackageJson = require(rootPackageJsonPath);
return !!rootPackageJson.workspaces;
}
}
} catch (error) {
// 忽略错误
}
return false;
}
/**
* 检测是否是npm link
*/
_isNpmLink() {
try {
const currentPath = __dirname;
const stats = require('fs').lstatSync(currentPath);
return stats.isSymbolicLink();
} catch (error) {
return false;
}
}
/**
* 查找package.json文件
*/
findPackageJson(startPath = __dirname) {
let currentPath = path.resolve(startPath);
while (currentPath !== path.parse(currentPath).root) {
const packageJsonPath = path.join(currentPath, 'package.json');
if (require('fs').existsSync(packageJsonPath)) {
return packageJsonPath;
}
currentPath = path.dirname(currentPath);
}
return null;
}
/**
* 查找根package.json文件用于monorepo检测
*/
findRootPackageJson() {
let currentPath = process.cwd();
let lastValidPackageJson = null;
while (currentPath !== path.parse(currentPath).root) {
const packageJsonPath = path.join(currentPath, 'package.json');
if (require('fs').existsSync(packageJsonPath)) {
lastValidPackageJson = packageJsonPath;
}
currentPath = path.dirname(currentPath);
}
return lastValidPackageJson;
}
/**
* 获取包根目录
*/
async getPackageRoot() {
const mode = this.detectInstallMode();
switch (mode) {
case 'development':
// 开发模式:查找项目根目录
return this._findProjectRoot();
case 'global':
// 全局安装:查找全局包目录
return this._findGlobalPackageRoot();
case 'npx':
// npx查找临时包目录
return this._findNpxPackageRoot();
case 'monorepo':
// monorepo查找workspace包目录
return this._findWorkspacePackageRoot();
case 'link':
// npm link解析符号链接
return this._findLinkedPackageRoot();
case 'local':
default:
// 本地安装查找node_modules中的包目录
return this._findLocalPackageRoot();
}
}
/**
* 查找项目根目录
*/
_findProjectRoot() {
const packageJsonPath = this.findPackageJson();
return packageJsonPath ? path.dirname(packageJsonPath) : process.cwd();
}
/**
* 查找全局包根目录
*/
_findGlobalPackageRoot() {
// 从当前模块路径向上查找直到找到package.json
return this._findProjectRoot();
}
/**
* 查找npx包根目录
*/
_findNpxPackageRoot() {
// npx通常将包缓存在特定目录
const packageJsonPath = this.findPackageJson();
return packageJsonPath ? path.dirname(packageJsonPath) : process.cwd();
}
/**
* 查找workspace包根目录
*/
_findWorkspacePackageRoot() {
// 在monorepo中查找当前workspace的根目录
return this._findProjectRoot();
}
/**
* 查找链接包根目录
*/
_findLinkedPackageRoot() {
try {
// 解析符号链接
const realPath = require('fs').realpathSync(__dirname);
const packageJsonPath = this.findPackageJson(realPath);
return packageJsonPath ? path.dirname(packageJsonPath) : realPath;
} catch (error) {
return this._findProjectRoot();
}
}
/**
* 查找本地包根目录
*/
_findLocalPackageRoot() {
// 在node_modules中查找包根目录
return this._findProjectRoot();
}
/**
* 解析路径到具体的文件系统路径
* @param {string} relativePath - 相对于包根目录的路径
* @param {QueryParams} params - 查询参数
* @returns {Promise<string>} 解析后的绝对路径
*/
async resolvePath(relativePath, params = null) {
// 获取包根目录
const packageRoot = await this.getPackageRoot();
// 验证路径是否在package.json的files字段中
this.validateFileAccess(packageRoot, relativePath);
// 直接处理路径,不需要目录映射
const relativePathClean = relativePath.replace(/^\/+/, '');
const fullPath = path.resolve(packageRoot, relativePathClean);
// 安全检查:确保路径在包根目录内
if (!fullPath.startsWith(packageRoot)) {
throw new Error(`Path traversal detected: ${relativePath}`);
}
return fullPath;
}
/**
* 验证文件访问权限基于package.json的files字段
* @param {string} packageRoot - 包根目录
* @param {string} relativePath - 相对路径
*/
validateFileAccess(packageRoot, relativePath) {
try {
const packageJsonPath = path.join(packageRoot, 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
// 如果没有files字段允许访问所有文件开发模式
if (!packageJson.files || !Array.isArray(packageJson.files)) {
return;
}
// 标准化路径
const normalizedPath = relativePath.replace(/^\/+/, '').replace(/\\/g, '/');
// 检查是否匹配files字段中的任何模式
const isAllowed = packageJson.files.some(filePattern => {
// 标准化文件模式
const normalizedPattern = filePattern.replace(/^\/+/, '').replace(/\\/g, '/');
// 精确匹配
if (normalizedPattern === normalizedPath) {
return true;
}
// 目录匹配(以/结尾或包含/*
if (normalizedPattern.endsWith('/') || normalizedPattern.endsWith('/*')) {
const dirPattern = normalizedPattern.replace(/\/?\*?$/, '/');
return normalizedPath.startsWith(dirPattern);
}
// 通配符匹配
if (normalizedPattern.includes('*')) {
const regexPattern = normalizedPattern
.replace(/\./g, '\\.')
.replace(/\*/g, '.*');
const regex = new RegExp(`^${regexPattern}$`);
return regex.test(normalizedPath);
}
// 目录前缀匹配
if (normalizedPath.startsWith(normalizedPattern + '/')) {
return true;
}
return false;
});
if (!isAllowed) {
// 在生产环境严格检查,开发环境只警告
const installMode = this.detectInstallMode();
if (installMode === 'development') {
console.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`);
}
}
} catch (error) {
// 如果读取package.json失败在开发模式下允许访问
const installMode = this.detectInstallMode();
if (installMode === 'development') {
console.warn(`⚠️ Warning: Could not validate file access for '${relativePath}': ${error.message}`);
} else {
throw error;
}
}
}
/**
* 检查资源是否存在
*/
async exists(resourcePath, queryParams) {
try {
const resolvedPath = await this.resolvePath(resourcePath, queryParams);
await fsPromises.access(resolvedPath);
return true;
} catch (error) {
return false;
}
}
/**
* 加载资源内容
*/
async loadContent(resourcePath, queryParams) {
const resolvedPath = await this.resolvePath(resourcePath, queryParams);
try {
await fsPromises.access(resolvedPath);
const content = await fsPromises.readFile(resolvedPath, 'utf8');
const stats = await fsPromises.stat(resolvedPath);
return {
content,
path: resolvedPath,
protocol: this.name,
installMode: this.detectInstallMode(),
metadata: {
size: content.length,
lastModified: stats.mtime,
absolutePath: resolvedPath,
relativePath: resourcePath
}
};
} catch (error) {
if (error.code === 'ENOENT') {
throw new Error(`包资源不存在: ${resourcePath} (解析为: ${resolvedPath})`);
}
throw new Error(`加载包资源失败: ${error.message}`);
}
}
/**
* 获取调试信息
*/
getDebugInfo() {
const mode = this.detectInstallMode();
return {
protocol: this.name,
installMode: mode,
packageRoot: this.getPackageRoot(),
currentWorkingDirectory: process.cwd(),
moduleDirectory: __dirname,
environment: {
NODE_ENV: process.env.NODE_ENV,
npm_execpath: process.env.npm_execpath,
npm_config_cache: process.env.npm_config_cache
},
cacheSize: this.cache.size
};
}
/**
* 清理缓存
*/
clearCache() {
super.clearCache();
this.installModeCache.clear();
}
}
module.exports = PackageProtocol;

View File

@ -0,0 +1,351 @@
const ResourceProtocol = require('./ResourceProtocol');
const path = require('path');
const fs = require('fs').promises;
/**
* 项目协议实现
* 实现@project://协议,通过查找.promptx目录确定项目根目录
*/
class ProjectProtocol extends ResourceProtocol {
constructor(options = {}) {
super('project', options);
// 支持的项目结构目录映射
this.projectDirs = {
'root': '', // 项目根目录
'src': 'src', // 源代码目录
'lib': 'lib', // 库目录
'build': 'build', // 构建输出目录
'dist': 'dist', // 分发目录
'docs': 'docs', // 文档目录
'test': 'test', // 测试目录
'tests': 'tests', // 测试目录(复数)
'spec': 'spec', // 规范测试目录
'config': 'config', // 配置目录
'scripts': 'scripts', // 脚本目录
'assets': 'assets', // 资源目录
'public': 'public', // 公共资源目录
'static': 'static', // 静态资源目录
'templates': 'templates', // 模板目录
'examples': 'examples', // 示例目录
'tools': 'tools' // 工具目录
};
// 项目根目录缓存
this.projectRootCache = new Map();
}
/**
* 获取协议信息
* @returns {object} 协议信息
*/
getProtocolInfo() {
return {
name: 'project',
description: '项目协议,通过.promptx目录标识提供项目结构访问',
location: 'project://{directory}/{path}',
examples: [
'project://src/index.js',
'project://lib/utils.js',
'project://docs/README.md',
'project://root/package.json',
'project://test/unit/'
],
supportedDirectories: Object.keys(this.projectDirs),
projectMarker: '.promptx',
params: this.getSupportedParams()
};
}
/**
* 支持的查询参数
* @returns {object} 参数说明
*/
getSupportedParams() {
return {
...super.getSupportedParams(),
from: 'string - 指定搜索起始目录',
create: 'boolean - 如果目录不存在是否创建',
exists: 'boolean - 仅返回存在的文件/目录',
type: 'string - 过滤类型 (file|dir|both)'
};
}
/**
* 验证项目协议路径
* @param {string} resourcePath - 资源路径
* @returns {boolean} 是否有效
*/
validatePath(resourcePath) {
if (!super.validatePath(resourcePath)) {
return false;
}
// 解析路径的第一部分(目录类型)
const parts = resourcePath.split('/');
const dirType = parts[0];
return this.projectDirs.hasOwnProperty(dirType);
}
/**
* 向上查找指定目录的同步版本
* @param {string} targetDir - 要查找的目录名(如 '.promptx'
* @param {string} startDir - 开始搜索的目录
* @returns {string|null} 找到的目录路径或null
*/
findUpDirectorySync(targetDir, startDir = process.cwd()) {
let currentDir = path.resolve(startDir);
const rootDir = path.parse(currentDir).root;
while (currentDir !== rootDir) {
const targetPath = path.join(currentDir, targetDir);
try {
const stats = require('fs').statSync(targetPath);
if (stats.isDirectory()) {
return targetPath;
}
} catch (error) {
// 目录不存在,继续向上查找
}
const parentDir = path.dirname(currentDir);
if (parentDir === currentDir) {
// 已到达根目录
break;
}
currentDir = parentDir;
}
return null;
}
/**
* 查找项目根目录
* @param {string} startDir - 开始搜索的目录
* @returns {Promise<string|null>} 项目根目录路径
*/
async findProjectRoot(startDir = process.cwd()) {
// 检查缓存
const cacheKey = path.resolve(startDir);
if (this.projectRootCache.has(cacheKey)) {
return this.projectRootCache.get(cacheKey);
}
try {
// 使用自实现的向上查找
const promptxPath = this.findUpDirectorySync('.promptx', startDir);
let projectRoot = null;
if (promptxPath) {
// .promptx 目录的父目录就是项目根目录
projectRoot = path.dirname(promptxPath);
}
// 缓存结果
this.projectRootCache.set(cacheKey, projectRoot);
return projectRoot;
} catch (error) {
throw new Error(`查找项目根目录失败: ${error.message}`);
}
}
/**
* 解析项目路径
* @param {string} resourcePath - 原始资源路径,如 "src/index.js"
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 解析后的绝对路径
*/
async resolvePath(resourcePath, queryParams) {
const parts = resourcePath.split('/');
const dirType = parts[0];
const relativePath = parts.slice(1).join('/');
// 验证目录类型
if (!this.projectDirs.hasOwnProperty(dirType)) {
throw new Error(`不支持的项目目录类型: ${dirType}。支持的类型: ${Object.keys(this.projectDirs).join(', ')}`);
}
// 确定搜索起始点
const startDir = queryParams?.get('from') || process.cwd();
// 查找项目根目录
const projectRoot = await this.findProjectRoot(startDir);
if (!projectRoot) {
throw new Error(`未找到项目根目录(.promptx标识。请确保在项目目录内或使用 'from' 参数指定项目路径`);
}
// 构建目标目录路径
const projectDirPath = this.projectDirs[dirType];
const targetDir = projectDirPath ? path.join(projectRoot, projectDirPath) : projectRoot;
// 如果没有相对路径,返回目录本身
if (!relativePath) {
return targetDir;
}
// 拼接完整路径
const fullPath = path.join(targetDir, relativePath);
// 安全检查:确保路径在项目目录内
const resolvedPath = path.resolve(fullPath);
const resolvedProjectRoot = path.resolve(projectRoot);
if (!resolvedPath.startsWith(resolvedProjectRoot)) {
throw new Error(`安全错误:路径超出项目目录范围: ${resolvedPath}`);
}
return resolvedPath;
}
/**
* 加载资源内容
* @param {string} resolvedPath - 解析后的路径
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 资源内容
*/
async loadContent(resolvedPath, queryParams) {
try {
// 检查路径是否存在
const stats = await fs.stat(resolvedPath);
if (stats.isDirectory()) {
return await this.loadDirectoryContent(resolvedPath, queryParams);
} else if (stats.isFile()) {
return await this.loadFileContent(resolvedPath, queryParams);
} else {
throw new Error(`不支持的文件类型: ${resolvedPath}`);
}
} catch (error) {
if (error.code === 'ENOENT') {
// 检查是否需要创建目录
if (queryParams?.get('create') === 'true') {
await fs.mkdir(path.dirname(resolvedPath), { recursive: true });
return ''; // 返回空内容
}
// 如果设置了exists参数为false返回空内容而不是错误
if (queryParams?.get('exists') === 'false') {
return '';
}
throw new Error(`文件或目录不存在: ${resolvedPath}`);
}
throw error;
}
}
/**
* 加载文件内容
* @param {string} filePath - 文件路径
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 文件内容
*/
async loadFileContent(filePath, queryParams) {
const encoding = queryParams?.get('encoding') || 'utf8';
return await fs.readFile(filePath, encoding);
}
/**
* 加载目录内容
* @param {string} dirPath - 目录路径
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 目录内容列表
*/
async loadDirectoryContent(dirPath, queryParams) {
const entries = await fs.readdir(dirPath, { withFileTypes: true });
// 应用类型过滤
const typeFilter = queryParams?.get('type');
let filteredEntries = entries;
if (typeFilter) {
filteredEntries = entries.filter(entry => {
switch (typeFilter) {
case 'file': return entry.isFile();
case 'dir': return entry.isDirectory();
case 'both': return true;
default: return true;
}
});
}
// 格式化输出
const format = queryParams?.get('format') || 'list';
switch (format) {
case 'json':
return JSON.stringify(
filteredEntries.map(entry => ({
name: entry.name,
type: entry.isDirectory() ? 'directory' : 'file',
path: path.join(dirPath, entry.name)
})),
null,
2
);
case 'paths':
return filteredEntries
.map(entry => path.join(dirPath, entry.name))
.join('\n');
case 'list':
default:
return filteredEntries
.map(entry => {
const type = entry.isDirectory() ? '[DIR]' : '[FILE]';
return `${type} ${entry.name}`;
})
.join('\n');
}
}
/**
* 列出项目结构信息
* @param {string} startDir - 开始搜索的目录
* @returns {Promise<object>} 项目信息
*/
async getProjectInfo(startDir = process.cwd()) {
const projectRoot = await this.findProjectRoot(startDir);
if (!projectRoot) {
return { error: '未找到项目根目录' };
}
const result = {
projectRoot,
promptxPath: path.join(projectRoot, '.promptx'),
directories: {}
};
for (const [dirType, dirPath] of Object.entries(this.projectDirs)) {
const fullPath = dirPath ? path.join(projectRoot, dirPath) : projectRoot;
try {
const stats = await fs.stat(fullPath);
result.directories[dirType] = {
path: fullPath,
exists: true,
type: stats.isDirectory() ? 'directory' : 'file'
};
} catch (error) {
result.directories[dirType] = {
path: fullPath,
exists: false
};
}
}
return result;
}
/**
* 清除缓存
*/
clearCache() {
super.clearCache();
this.projectRootCache.clear();
}
}
module.exports = ProjectProtocol;

View File

@ -0,0 +1,208 @@
/**
* 资源协议接口基类
* 定义所有DPML资源协议的统一规范
*/
class ResourceProtocol {
/**
* 构造函数
* @param {string} name - 协议名称
* @param {object} options - 配置选项
*/
constructor(name, options = {}) {
if (new.target === ResourceProtocol) {
throw new Error('ResourceProtocol是抽象类不能直接实例化');
}
this.name = name;
this.options = options;
this.cache = new Map();
this.enableCache = options.enableCache !== false;
}
/**
* 协议信息 - 需要子类实现
* @returns {object} 协议信息
*/
getProtocolInfo() {
throw new Error('子类必须实现 getProtocolInfo() 方法');
}
/**
* 解析资源路径 - 需要子类实现
* @param {string} resourcePath - 原始资源路径
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 解析后的路径
*/
async resolvePath(resourcePath, queryParams) {
throw new Error('子类必须实现 resolvePath() 方法');
}
/**
* 加载资源内容 - 需要子类实现
* @param {string} resolvedPath - 解析后的路径
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 资源内容
*/
async loadContent(resolvedPath, queryParams) {
throw new Error('子类必须实现 loadContent() 方法');
}
/**
* 验证资源路径格式 - 可选实现
* @param {string} resourcePath - 资源路径
* @returns {boolean} 是否有效
*/
validatePath(resourcePath) {
return typeof resourcePath === 'string' && resourcePath.length > 0;
}
/**
* 支持的查询参数列表 - 可选实现
* @returns {object} 参数说明
*/
getSupportedParams() {
return {
line: 'string - 行范围,如 "1-10"',
format: 'string - 输出格式',
cache: 'boolean - 是否缓存'
};
}
/**
* 统一的资源解析入口点
* @param {string} resourcePath - 资源路径
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 资源内容
*/
async resolve(resourcePath, queryParams) {
// 1. 验证路径格式
if (!this.validatePath(resourcePath)) {
throw new Error(`无效的资源路径: ${resourcePath}`);
}
// 2. 生成缓存键
const cacheKey = this.generateCacheKey(resourcePath, queryParams);
// 3. 检查缓存
if (this.enableCache && this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
// 4. 解析路径
const resolvedPath = await this.resolvePath(resourcePath, queryParams);
// 5. 加载内容
const content = await this.loadContent(resolvedPath, queryParams);
// 6. 应用通用查询参数过滤
const filteredContent = this.applyCommonParams(content, queryParams);
// 7. 缓存结果
if (this.enableCache) {
this.cache.set(cacheKey, filteredContent);
}
return filteredContent;
}
/**
* 生成缓存键
* @param {string} resourcePath - 资源路径
* @param {QueryParams} queryParams - 查询参数
* @returns {string} 缓存键
*/
generateCacheKey(resourcePath, queryParams) {
const params = queryParams ? queryParams.getAll() : {};
return `${this.name}:${resourcePath}:${JSON.stringify(params)}`;
}
/**
* 应用通用查询参数
* @param {string} content - 原始内容
* @param {QueryParams} queryParams - 查询参数
* @returns {string} 过滤后的内容
*/
applyCommonParams(content, queryParams) {
if (!queryParams) {
return content;
}
let result = content;
// 应用行过滤
if (queryParams.line) {
result = this.applyLineFilter(result, queryParams.line);
}
// 应用格式化(基础实现,子类可以重写)
if (queryParams.format && queryParams.format !== 'text') {
result = this.applyFormat(result, queryParams.format);
}
return result;
}
/**
* 应用行过滤
* @param {string} content - 内容
* @param {string} lineRange - 行范围,如 "5-10" 或 "5"
* @returns {string} 过滤后的内容
*/
applyLineFilter(content, lineRange) {
const lines = content.split('\n');
if (lineRange.includes('-')) {
const [start, end] = lineRange.split('-').map(n => parseInt(n.trim(), 10));
const startIndex = Math.max(0, start - 1);
const endIndex = Math.min(lines.length, end);
return lines.slice(startIndex, endIndex).join('\n');
} else {
const lineNum = parseInt(lineRange, 10);
const lineIndex = lineNum - 1;
return lines[lineIndex] || '';
}
}
/**
* 应用格式化
* @param {string} content - 内容
* @param {string} format - 格式
* @returns {string} 格式化后的内容
*/
applyFormat(content, format) {
// 基础实现,子类可以重写
switch (format) {
case 'json':
try {
return JSON.stringify(JSON.parse(content), null, 2);
} catch {
return content;
}
case 'trim':
return content.trim();
default:
return content;
}
}
/**
* 清除缓存
*/
clearCache() {
this.cache.clear();
}
/**
* 获取缓存统计
* @returns {object} 缓存统计信息
*/
getCacheStats() {
return {
protocol: this.name,
size: this.cache.size,
enabled: this.enableCache
};
}
}
module.exports = ResourceProtocol;

View File

@ -0,0 +1,299 @@
const ResourceProtocol = require('./ResourceProtocol');
const path = require('path');
const fs = require('fs').promises;
// 延迟加载platform-folders以处理可能的原生模块依赖
let platformFolders = null;
const getPlatformFolders = () => {
if (!platformFolders) {
try {
platformFolders = require('platform-folders');
} catch (error) {
// 如果platform-folders不可用回退到os.homedir()
const os = require('os');
platformFolders = {
getHomeFolder: () => os.homedir(),
getDesktopFolder: () => path.join(os.homedir(), 'Desktop'),
getDocumentsFolder: () => path.join(os.homedir(), 'Documents'),
getDownloadsFolder: () => path.join(os.homedir(), 'Downloads'),
getMusicFolder: () => path.join(os.homedir(), 'Music'),
getPicturesFolder: () => path.join(os.homedir(), 'Pictures'),
getVideosFolder: () => path.join(os.homedir(), 'Videos')
};
console.warn('platform-folders不可用使用os.homedir()回退方案');
}
}
return platformFolders;
};
/**
* 用户目录协议实现
* 实现@user://协议用于访问用户的标准目录Documents、Desktop、Downloads等
*/
class UserProtocol extends ResourceProtocol {
constructor(options = {}) {
super('user', options);
// 支持的用户目录映射
this.userDirs = {
'home': 'getHomeFolder',
'desktop': 'getDesktopFolder',
'documents': 'getDocumentsFolder',
'downloads': 'getDownloadsFolder',
'music': 'getMusicFolder',
'pictures': 'getPicturesFolder',
'videos': 'getVideosFolder'
};
// 目录路径缓存
this.dirCache = new Map();
}
/**
* 获取协议信息
* @returns {object} 协议信息
*/
getProtocolInfo() {
return {
name: 'user',
description: '用户目录协议,提供跨平台的用户标准目录访问',
location: 'user://{directory}/{path}',
examples: [
'user://documents/notes.txt',
'user://desktop/readme.md',
'user://downloads/',
'user://home/.bashrc'
],
supportedDirectories: Object.keys(this.userDirs),
params: this.getSupportedParams()
};
}
/**
* 支持的查询参数
* @returns {object} 参数说明
*/
getSupportedParams() {
return {
...super.getSupportedParams(),
exists: 'boolean - 仅返回存在的文件/目录',
type: 'string - 过滤类型 (file|dir|both)'
};
}
/**
* 验证用户协议路径
* @param {string} resourcePath - 资源路径
* @returns {boolean} 是否有效
*/
validatePath(resourcePath) {
if (!super.validatePath(resourcePath)) {
return false;
}
// 解析路径的第一部分(目录类型)
const parts = resourcePath.split('/');
const dirType = parts[0];
return this.userDirs.hasOwnProperty(dirType);
}
/**
* 解析用户目录路径
* @param {string} resourcePath - 原始资源路径,如 "documents/notes.txt"
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 解析后的绝对路径
*/
async resolvePath(resourcePath, queryParams) {
const parts = resourcePath.split('/');
const dirType = parts[0];
const relativePath = parts.slice(1).join('/');
// 验证目录类型
if (!this.userDirs[dirType]) {
throw new Error(`不支持的用户目录类型: ${dirType}。支持的类型: ${Object.keys(this.userDirs).join(', ')}`);
}
// 获取用户目录路径
const userDirPath = await this.getUserDirectory(dirType);
// 如果没有相对路径,返回目录本身
if (!relativePath) {
return userDirPath;
}
// 拼接完整路径
const fullPath = path.join(userDirPath, relativePath);
// 安全检查:确保路径在用户目录内
const resolvedPath = path.resolve(fullPath);
const resolvedUserDir = path.resolve(userDirPath);
if (!resolvedPath.startsWith(resolvedUserDir)) {
throw new Error(`安全错误:路径超出用户目录范围: ${resolvedPath}`);
}
return resolvedPath;
}
/**
* 获取用户目录路径
* @param {string} dirType - 目录类型
* @returns {Promise<string>} 目录路径
*/
async getUserDirectory(dirType) {
// 检查缓存
if (this.dirCache.has(dirType)) {
return this.dirCache.get(dirType);
}
const folders = getPlatformFolders();
const methodName = this.userDirs[dirType];
if (!folders[methodName]) {
throw new Error(`未找到用户目录获取方法: ${methodName}`);
}
try {
let dirPath;
// 调用platform-folders方法
if (typeof folders[methodName] === 'function') {
dirPath = await folders[methodName]();
} else {
dirPath = folders[methodName];
}
// 缓存结果
this.dirCache.set(dirType, dirPath);
return dirPath;
} catch (error) {
throw new Error(`获取用户目录失败 (${dirType}): ${error.message}`);
}
}
/**
* 加载资源内容
* @param {string} resolvedPath - 解析后的路径
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 资源内容
*/
async loadContent(resolvedPath, queryParams) {
try {
// 检查路径是否存在
const stats = await fs.stat(resolvedPath);
if (stats.isDirectory()) {
return await this.loadDirectoryContent(resolvedPath, queryParams);
} else if (stats.isFile()) {
return await this.loadFileContent(resolvedPath, queryParams);
} else {
throw new Error(`不支持的文件类型: ${resolvedPath}`);
}
} catch (error) {
if (error.code === 'ENOENT') {
// 如果设置了exists参数为false返回空内容而不是错误
if (queryParams && queryParams.get('exists') === 'false') {
return '';
}
throw new Error(`文件或目录不存在: ${resolvedPath}`);
}
throw error;
}
}
/**
* 加载文件内容
* @param {string} filePath - 文件路径
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 文件内容
*/
async loadFileContent(filePath, queryParams) {
const encoding = queryParams?.get('encoding') || 'utf8';
return await fs.readFile(filePath, encoding);
}
/**
* 加载目录内容
* @param {string} dirPath - 目录路径
* @param {QueryParams} queryParams - 查询参数
* @returns {Promise<string>} 目录内容列表
*/
async loadDirectoryContent(dirPath, queryParams) {
const entries = await fs.readdir(dirPath, { withFileTypes: true });
// 应用类型过滤
const typeFilter = queryParams?.get('type');
let filteredEntries = entries;
if (typeFilter) {
filteredEntries = entries.filter(entry => {
switch (typeFilter) {
case 'file': return entry.isFile();
case 'dir': return entry.isDirectory();
case 'both': return true;
default: return true;
}
});
}
// 格式化输出
const format = queryParams?.get('format') || 'list';
switch (format) {
case 'json':
return JSON.stringify(
filteredEntries.map(entry => ({
name: entry.name,
type: entry.isDirectory() ? 'directory' : 'file',
path: path.join(dirPath, entry.name)
})),
null,
2
);
case 'paths':
return filteredEntries
.map(entry => path.join(dirPath, entry.name))
.join('\n');
case 'list':
default:
return filteredEntries
.map(entry => {
const type = entry.isDirectory() ? '[DIR]' : '[FILE]';
return `${type} ${entry.name}`;
})
.join('\n');
}
}
/**
* 列出所有支持的用户目录
* @returns {Promise<object>} 目录信息
*/
async listUserDirectories() {
const result = {};
for (const dirType of Object.keys(this.userDirs)) {
try {
result[dirType] = await this.getUserDirectory(dirType);
} catch (error) {
result[dirType] = { error: error.message };
}
}
return result;
}
/**
* 清除目录缓存
*/
clearCache() {
super.clearCache();
this.dirCache.clear();
}
}
module.exports = UserProtocol;

View File

@ -0,0 +1,321 @@
const ResourceProtocolParser = require('./resourceProtocolParser');
const ResourceRegistry = require('./resourceRegistry');
const { ResourceResult } = require('./types');
const logger = require('../../utils/logger');
/**
* 资源管理器
* 基于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;
}
/**
* 解析并获取资源
* @param {string} resourceRef - DPML资源引用
* @param {object} options - 选项
* @returns {Promise<ResourceResult>} 资源结果
*/
async resolve(resourceRef, options = {}) {
try {
logger.debug(`Resolving resource: ${resourceRef}`);
// 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);
// 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;
} catch (error) {
logger.error(`Failed to resolve resource ${resourceRef}:`, error.message);
return ResourceResult.error(error, { originalRef: resourceRef });
}
}
/**
* 批量解析多个资源
* @param {string[]} resourceRefs - 资源引用列表
* @param {object} options - 选项
* @returns {Promise<ResourceResult[]>} 资源结果列表
*/
async resolveMultiple(resourceRefs, options = {}) {
const results = [];
for (const ref of resourceRefs) {
const result = await this.resolve(ref, options);
results.push(result);
}
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}`);
}
}
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];
}
}
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;
}
}
/**
* 获取注册表信息
* @param {string} protocol - 协议名(可选)
* @returns {object} 注册表信息
*/
getRegistryInfo(protocol) {
if (protocol) {
return this.registry.getProtocolInfo(protocol);
}
return this.registry.getRegistryInfo();
}
/**
* 列出可用协议
* @returns {string[]} 协议列表
*/
listProtocols() {
return this.registry.listProtocols();
}
/**
* 清除缓存
*/
clearCache() {
this.cache.clear();
logger.debug('Resource cache cleared');
}
}
module.exports = ResourceManager;

View File

@ -0,0 +1,287 @@
const {
LoadingSemantics,
ParsedReference,
QueryParams,
NestedReference
} = require('./types');
/**
* 资源协议解析器
* 解析DPML资源引用语法@protocol://path?params
*/
class ResourceProtocolParser {
constructor() {
// 资源引用正则表达式
this.resourceRefRegex = /^(@[!?]?|@)([a-zA-Z][a-zA-Z0-9_-]*):(.+)$/;
this.nestedRefRegex = /^(@[!?]?|@)([a-zA-Z][a-zA-Z0-9_-]*):(@[!?]?|@)?(.+)$/;
this.queryParamsRegex = /^([^?]+)(?:\?(.+))?$/;
}
/**
* 解析资源引用
* @param {string} resourceRef - 资源引用字符串
* @returns {ParsedReference} 解析后的引用对象
*/
parse(resourceRef) {
if (!resourceRef || typeof resourceRef !== 'string') {
throw new Error('Invalid resource reference: must be a non-empty string');
}
const trimmedRef = resourceRef.trim();
if (!this.validateSyntax(trimmedRef)) {
throw new Error(`Invalid resource reference syntax: ${trimmedRef}`);
}
const parsed = new ParsedReference();
parsed.originalRef = trimmedRef;
// 检查是否为嵌套引用
if (this.isNestedReference(trimmedRef)) {
return this.parseNestedReference(trimmedRef);
}
// 解析基础引用
return this.parseBasicReference(trimmedRef);
}
/**
* 解析基础资源引用
* @param {string} ref - 基础引用
* @returns {ParsedReference}
*/
parseBasicReference(ref) {
const parsed = new ParsedReference();
parsed.originalRef = ref;
// 解析加载语义
parsed.loadingSemantics = this.parseLoadingSemantics(ref);
// 移除加载语义前缀
const withoutSemantics = this.removeLoadingSemantics(ref);
// 匹配协议和路径
const match = withoutSemantics.match(/^([a-zA-Z][a-zA-Z0-9_-]*):(.+)$/);
if (!match) {
throw new Error(`Invalid protocol format: ${ref}`);
}
parsed.protocol = match[1];
let pathAndParams = match[2];
// 移除 :// 前缀(如果存在)
if (pathAndParams.startsWith('//')) {
pathAndParams = pathAndParams.substring(2);
}
// 解析路径和查询参数
const pathMatch = pathAndParams.match(this.queryParamsRegex);
if (pathMatch) {
parsed.path = pathMatch[1];
if (pathMatch[2]) {
parsed.queryParams = this.parseQueryParams(pathMatch[2]);
}
} else {
parsed.path = pathAndParams;
}
return parsed;
}
/**
* 解析嵌套引用
* @param {string} ref - 嵌套引用
* @returns {ParsedReference}
*/
parseNestedReference(ref) {
const parsed = new ParsedReference();
parsed.originalRef = ref;
parsed.isNested = true;
// 解析外层加载语义
parsed.loadingSemantics = this.parseLoadingSemantics(ref);
const withoutOuterSemantics = this.removeLoadingSemantics(ref);
// 匹配嵌套结构: protocol:@inner_protocol://path 或 protocol:inner_protocol://path
const match = withoutOuterSemantics.match(/^([a-zA-Z][a-zA-Z0-9_-]*):(.+)$/);
if (!match) {
throw new Error(`Invalid nested reference format: ${ref}`);
}
parsed.protocol = match[1];
let innerRef = match[2];
// 处理内层引用:移除可能的 :// 前缀,但保留 @ 前缀
if (innerRef.startsWith('//')) {
innerRef = innerRef.substring(2);
}
// 确保内层引用有正确的格式
if (!innerRef.startsWith('@')) {
innerRef = '@' + innerRef;
}
// 递归解析内层引用
try {
const innerParsed = this.parse(innerRef);
// 创建嵌套引用结构
const nested = new NestedReference();
nested.outer = parsed;
nested.inner = innerParsed;
nested.depth = this.calculateNestingDepth(innerParsed);
parsed.nestedRef = nested;
} catch (error) {
throw new Error(`Invalid nested inner reference: ${error.message}`);
}
return parsed;
}
/**
* 解析加载语义
* @param {string} ref - 资源引用
* @returns {string} 加载语义
*/
parseLoadingSemantics(ref) {
if (ref.startsWith('@!')) {
return LoadingSemantics.HOT_LOAD;
} else if (ref.startsWith('@?')) {
return LoadingSemantics.LAZY_LOAD;
} else if (ref.startsWith('@')) {
return LoadingSemantics.DEFAULT;
}
throw new Error(`Invalid loading semantics: ${ref}`);
}
/**
* 移除加载语义前缀
* @param {string} ref - 资源引用
* @returns {string} 移除前缀后的引用
*/
removeLoadingSemantics(ref) {
if (ref.startsWith('@!') || ref.startsWith('@?')) {
return ref.substring(2);
} else if (ref.startsWith('@')) {
return ref.substring(1);
}
return ref;
}
/**
* 解析查询参数
* @param {string} queryString - 查询字符串
* @returns {QueryParams} 查询参数对象
*/
parseQueryParams(queryString) {
const params = new QueryParams();
if (!queryString) {
return params;
}
const pairs = queryString.split('&');
for (const pair of pairs) {
const [key, value] = pair.split('=').map(decodeURIComponent);
if (key) {
// 处理特殊参数
if (key === 'cache') {
params.set(key, value === 'true' || value === '1');
} else {
params.set(key, value || '');
}
}
}
return params;
}
/**
* 验证语法
* @param {string} ref - 资源引用
* @returns {boolean} 是否有效
*/
validateSyntax(ref) {
if (!ref) return false;
// 必须以@开头
if (!ref.startsWith('@')) return false;
// 基本格式检查
const withoutSemantics = this.removeLoadingSemantics(ref);
return /^[a-zA-Z][a-zA-Z0-9_-]*:.+$/.test(withoutSemantics);
}
/**
* 检查是否为嵌套引用
* @param {string} ref - 资源引用
* @returns {boolean} 是否为嵌套引用
*/
isNestedReference(ref) {
const withoutSemantics = this.removeLoadingSemantics(ref);
const colonIndex = withoutSemantics.indexOf(':');
if (colonIndex === -1) return false;
const afterColon = withoutSemantics.substring(colonIndex + 1);
// 检查是否包含内层引用 (@protocol: 或 protocol:)
return afterColon.includes('@') || afterColon.includes('://');
}
/**
* 计算嵌套深度
* @param {ParsedReference} ref - 解析后的引用
* @returns {number} 嵌套深度
*/
calculateNestingDepth(ref) {
if (!ref.isNested) return 1;
return 1 + this.calculateNestingDepth(ref.nestedRef.inner);
}
/**
* 提取协议名
* @param {string} ref - 资源引用
* @returns {string} 协议名
*/
extractProtocol(ref) {
const withoutSemantics = this.removeLoadingSemantics(ref);
const colonIndex = withoutSemantics.indexOf(':');
return colonIndex > 0 ? withoutSemantics.substring(0, colonIndex) : '';
}
/**
* 提取路径
* @param {string} ref - 资源引用
* @returns {string} 路径
*/
extractPath(ref) {
const withoutSemantics = this.removeLoadingSemantics(ref);
const colonIndex = withoutSemantics.indexOf(':');
if (colonIndex === -1) return '';
let pathAndParams = withoutSemantics.substring(colonIndex + 1);
// 移除 :// 前缀(如果存在)
if (pathAndParams.startsWith('//')) {
pathAndParams = pathAndParams.substring(2);
}
const queryIndex = pathAndParams.indexOf('?');
return queryIndex > 0 ? pathAndParams.substring(0, queryIndex) : pathAndParams;
}
/**
* 提取查询参数字符串
* @param {string} ref - 资源引用
* @returns {string} 查询参数字符串
*/
extractParams(ref) {
const queryIndex = ref.indexOf('?');
return queryIndex > 0 ? ref.substring(queryIndex + 1) : '';
}
}
module.exports = ResourceProtocolParser;

View File

@ -0,0 +1,248 @@
const path = require('path');
const { ProtocolInfo } = require('./types');
/**
* 资源注册表管理器
* 管理资源协议和ID到路径的映射
*/
class ResourceRegistry {
constructor() {
this.builtinRegistry = new Map();
this.customRegistry = new Map();
this.loadBuiltinRegistry();
}
/**
* 加载内置注册表
*/
loadBuiltinRegistry() {
// PromptX 内置资源协议
const promptProtocol = new ProtocolInfo();
promptProtocol.name = 'prompt';
promptProtocol.description = 'PromptX内置提示词资源协议';
promptProtocol.location = 'prompt://{resource_id}';
promptProtocol.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.builtinRegistry.set('prompt', promptProtocol);
// File 协议(标准协议,无需注册表)
const fileProtocol = new ProtocolInfo();
fileProtocol.name = 'file';
fileProtocol.description = '文件系统资源协议';
fileProtocol.location = 'file://{absolute_or_relative_path}';
fileProtocol.params = {
line: 'string - 行范围,如 "1-10"',
encoding: 'string - 文件编码,默认 utf8'
};
this.builtinRegistry.set('file', fileProtocol);
// Memory 协议(项目记忆系统)
const memoryProtocol = new ProtocolInfo();
memoryProtocol.name = 'memory';
memoryProtocol.description = '项目记忆系统协议';
memoryProtocol.location = 'memory://{resource_id}';
memoryProtocol.registry = new Map([
['declarative', '@project://.promptx/memory/declarative.md'],
['procedural', '@project://.promptx/memory/procedural.md'],
['episodic', '@project://.promptx/memory/episodic.md'],
['semantic', '@project://.promptx/memory/semantic.md']
]);
this.builtinRegistry.set('memory', memoryProtocol);
// HTTP/HTTPS 协议(标准协议)
const httpProtocol = new ProtocolInfo();
httpProtocol.name = 'http';
httpProtocol.description = 'HTTP网络资源协议';
httpProtocol.location = 'http://{url}';
httpProtocol.params = {
format: 'string - 响应格式,如 json, text',
timeout: 'number - 超时时间(毫秒)',
cache: 'boolean - 是否缓存响应'
};
this.builtinRegistry.set('http', httpProtocol);
this.builtinRegistry.set('https', httpProtocol);
}
/**
* 解析资源ID到具体路径
* @param {string} protocol - 协议名
* @param {string} resourceId - 资源ID
* @returns {string} 解析后的路径
*/
resolve(protocol, resourceId) {
const protocolInfo = this.getProtocolInfo(protocol);
if (!protocolInfo) {
throw new Error(`Unknown protocol: ${protocol}`);
}
// 如果协议有注册表尝试解析ID
if (protocolInfo.registry && protocolInfo.registry.size > 0) {
const resolvedPath = protocolInfo.registry.get(resourceId);
if (resolvedPath) {
return resolvedPath;
}
// 如果在注册表中找不到,但这是一个有注册表的协议,抛出错误
throw new Error(`Resource ID '${resourceId}' not found in ${protocol} protocol registry`);
}
// 对于没有注册表的协议如file, http直接返回资源ID作为路径
return resourceId;
}
/**
* 注册新的协议或更新现有协议
* @param {string} protocolName - 协议名
* @param {object} protocolDefinition - 协议定义
*/
register(protocolName, protocolDefinition) {
const protocolInfo = new ProtocolInfo();
protocolInfo.name = protocolName;
protocolInfo.description = protocolDefinition.description || '';
protocolInfo.location = protocolDefinition.location || '';
protocolInfo.params = protocolDefinition.params || {};
// 设置注册表映射
if (protocolDefinition.registry) {
protocolInfo.registry = new Map();
for (const [id, path] of Object.entries(protocolDefinition.registry)) {
protocolInfo.registry.set(id, path);
}
}
this.customRegistry.set(protocolName, protocolInfo);
}
/**
* 获取协议信息
* @param {string} protocolName - 协议名
* @returns {ProtocolInfo|null} 协议信息
*/
getProtocolInfo(protocolName) {
return this.customRegistry.get(protocolName) ||
this.builtinRegistry.get(protocolName) ||
null;
}
/**
* 列出所有可用协议
* @returns {string[]} 协议名列表
*/
listProtocols() {
const protocols = new Set();
for (const protocol of this.builtinRegistry.keys()) {
protocols.add(protocol);
}
for (const protocol of this.customRegistry.keys()) {
protocols.add(protocol);
}
return Array.from(protocols).sort();
}
/**
* 检查协议是否存在
* @param {string} protocolName - 协议名
* @returns {boolean} 是否存在
*/
hasProtocol(protocolName) {
return this.builtinRegistry.has(protocolName) ||
this.customRegistry.has(protocolName);
}
/**
* 获取协议的注册表内容
* @param {string} protocolName - 协议名
* @returns {Map|null} 注册表映射
*/
getProtocolRegistry(protocolName) {
const protocolInfo = this.getProtocolInfo(protocolName);
return protocolInfo ? protocolInfo.registry : null;
}
/**
* 列出协议的所有可用资源ID
* @param {string} protocolName - 协议名
* @returns {string[]} 资源ID列表
*/
listProtocolResources(protocolName) {
const registry = this.getProtocolRegistry(protocolName);
return registry ? Array.from(registry.keys()) : [];
}
/**
* 展开通配符模式
* @param {string} pattern - 通配符模式
* @returns {string[]} 展开后的路径列表
*/
expandWildcards(pattern) {
// 这里暂时返回原样,实际实现需要结合文件系统
// 在ResourceLocator中会有更详细的实现
return [pattern];
}
/**
* 验证资源引用
* @param {string} protocol - 协议名
* @param {string} resourceId - 资源ID
* @returns {boolean} 是否有效
*/
validateReference(protocol, resourceId) {
if (!this.hasProtocol(protocol)) {
return false;
}
const protocolInfo = this.getProtocolInfo(protocol);
// 如果有注册表检查ID是否存在
if (protocolInfo.registry && protocolInfo.registry.size > 0) {
return protocolInfo.registry.has(resourceId);
}
// 对于没有注册表的协议,只要协议存在就认为有效
return true;
}
/**
* 获取所有注册表信息(用于调试)
* @returns {object} 注册表信息
*/
getRegistryInfo() {
const info = {
builtin: {},
custom: {}
};
for (const [name, protocol] of this.builtinRegistry) {
info.builtin[name] = {
description: protocol.description,
location: protocol.location,
params: protocol.params,
registrySize: protocol.registry ? protocol.registry.size : 0,
resources: protocol.registry ? Array.from(protocol.registry.keys()) : []
};
}
for (const [name, protocol] of this.customRegistry) {
info.custom[name] = {
description: protocol.description,
location: protocol.location,
params: protocol.params,
registrySize: protocol.registry ? protocol.registry.size : 0,
resources: protocol.registry ? Array.from(protocol.registry.keys()) : []
};
}
return info;
}
}
module.exports = ResourceRegistry;

View File

@ -0,0 +1,236 @@
/**
* 资源模块基础数据类型定义
* 基于DPML资源协议标准
*/
/**
* 加载语义枚举
*/
const LoadingSemantics = {
DEFAULT: 'default', // @ - AI自行决定加载时机
HOT_LOAD: 'hot_load', // @! - 立即加载
LAZY_LOAD: 'lazy_load' // @? - 懒加载
};
/**
* 解析后的资源引用
*/
class ParsedReference {
constructor() {
this.loadingSemantics = LoadingSemantics.DEFAULT;
this.protocol = '';
this.path = '';
this.queryParams = new QueryParams();
this.isNested = false;
this.nestedRef = null;
this.originalRef = '';
}
}
/**
* 查询参数
*/
class QueryParams {
constructor() {
this.line = null; // 行范围 "5-10"
this.format = null; // 输出格式 "json"
this.cache = null; // 是否缓存默认为null表示未设置
this.params = new Map(); // 其他参数
}
/**
* 设置参数
*/
set(key, value) {
if (['line', 'format', 'cache'].includes(key)) {
this[key] = value;
} else {
this.params.set(key, value);
}
}
/**
* 获取参数
*/
get(key) {
if (['line', 'format', 'cache'].includes(key)) {
return this[key];
}
return this.params.get(key);
}
/**
* 获取所有参数
*/
getAll() {
const result = {};
// 只添加非null的内置参数
if (this.line !== null) {
result.line = this.line;
}
if (this.format !== null) {
result.format = this.format;
}
if (this.cache !== null) {
result.cache = this.cache;
}
// 添加其他参数
for (const [key, value] of this.params) {
result[key] = value;
}
return result;
}
/**
* 转换为字符串用于缓存键
*/
toString() {
const params = [];
// 添加内置参数
if (this.line !== null) {
params.push(`line=${this.line}`);
}
if (this.format !== null) {
params.push(`format=${this.format}`);
}
if (this.cache !== null) {
params.push(`cache=${this.cache}`);
}
// 添加其他参数(按键排序以确保一致性)
const sortedParams = Array.from(this.params.entries()).sort();
for (const [key, value] of sortedParams) {
params.push(`${key}=${value}`);
}
return params.join('&');
}
}
/**
* 嵌套引用
*/
class NestedReference {
constructor() {
this.outer = null; // 外层引用
this.inner = null; // 内层引用
this.depth = 0; // 嵌套深度
}
}
/**
* 资源内容
*/
class ResourceContent {
constructor(path, content, metadata = {}) {
this.path = path;
this.content = content;
this.metadata = metadata;
this.relativePath = '';
this.lastModified = null;
this.size = content ? content.length : 0;
}
}
/**
* 懒加载资源
*/
class LazyResource {
constructor(path, loader) {
this.path = path;
this.loader = loader;
this.loaded = false;
this._content = null;
}
/**
* 加载资源
*/
async load() {
if (!this.loaded) {
this._content = await this.loader(this.path);
this.loaded = true;
}
return this._content;
}
}
/**
* 处理后的结果
*/
class ProcessedResult {
constructor() {
this.content = '';
this.metadata = {};
this.format = 'text';
this.sources = [];
this.cached = false;
}
}
/**
* 最终资源结果
*/
class ResourceResult {
constructor() {
this.content = '';
this.metadata = {};
this.sources = [];
this.format = 'text';
this.cached = false;
this.loadTime = Date.now();
this.success = true;
this.error = null;
}
/**
* 创建成功结果
*/
static success(content, metadata = {}) {
const result = new ResourceResult();
result.content = content;
result.metadata = metadata;
result.success = true;
return result;
}
/**
* 创建错误结果
*/
static error(error, metadata = {}) {
const result = new ResourceResult();
result.success = false;
result.error = error;
result.metadata = metadata;
return result;
}
}
/**
* 资源协议信息
*/
class ProtocolInfo {
constructor() {
this.name = '';
this.description = '';
this.location = ''; // EBNF路径定义
this.params = {}; // 支持的参数
this.registry = new Map(); // ID到路径的映射
}
}
module.exports = {
LoadingSemantics,
ParsedReference,
QueryParams,
NestedReference,
ResourceContent,
LazyResource,
ProcessedResult,
ResourceResult,
ProtocolInfo
};