feat: 更新命令名称为dpml-prompt,保持PromptX品牌名称

This commit is contained in:
sean
2025-05-31 18:59:25 +08:00
parent 0bdb5e865b
commit 0466cd3289
44 changed files with 3138 additions and 3127 deletions

View File

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