@xubill/xx-cli 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +181 -0
- package/bin/cli.js +89 -0
- package/lib/core/base-plugin.js +292 -0
- package/lib/core/index.js +550 -0
- package/lib/plugins/ai.js +295 -0
- package/lib/plugins/config-manager.js +165 -0
- package/lib/plugins/history.js +108 -0
- package/lib/plugins/plugin-manager.js +700 -0
- package/lib/utils/config-manager.js +185 -0
- package/lib/utils/history-manager.js +119 -0
- package/lib/utils/logger.js +28 -0
- package/lib/utils/plugin-config.js +158 -0
- package/lib/utils/plugins-helper.js +276 -0
- package/lib/utils/transformers/getInput.js +76 -0
- package/lib/utils/transformers/index.js +43 -0
- package/lib/utils/transformers/renderLists.js +94 -0
- package/lib/utils/transformers/sfcMaterial.js +113 -0
- package/lib/utils/transformers/transformFilter.js +95 -0
- package/lib/utils/transformers/transformList.js +120 -0
- package/lib/utils/transformers/transformMock.js +43 -0
- package/lib/utils/transformers/transformRoutes.js +36 -0
- package/lib/utils/transformers/transformTable.js +81 -0
- package/lib/utils/transformers/transformTemplate.js +129 -0
- package/lib/utils/validator.js +96 -0
- package/package.json +68 -0
- package/plugin-development-new.md +565 -0
- package/plugin-development-old.md +447 -0
|
@@ -0,0 +1,700 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 插件管理插件
|
|
3
|
+
* 用于管理所有插件
|
|
4
|
+
*
|
|
5
|
+
* 命令说明:
|
|
6
|
+
* - plugin [options]:管理所有插件
|
|
7
|
+
* - 示例:plugin list
|
|
8
|
+
* - 示例:plugin enable <plugin>
|
|
9
|
+
* - 示例:plugin disable <plugin>
|
|
10
|
+
* - 示例:plugin add <url>
|
|
11
|
+
* - 示例:plugin remove <plugin>
|
|
12
|
+
* - 示例:plugin create <name>
|
|
13
|
+
* - 示例:plugin config <plugin> [key] [value]
|
|
14
|
+
*
|
|
15
|
+
* 功能说明:
|
|
16
|
+
* - 列出所有插件
|
|
17
|
+
* - 启用指定插件
|
|
18
|
+
* - 禁用指定插件
|
|
19
|
+
* - 添加外部插件
|
|
20
|
+
* - 删除指定插件
|
|
21
|
+
* - 创建指定名称的插件模板
|
|
22
|
+
* - 查看和设置插件配置
|
|
23
|
+
*
|
|
24
|
+
* 用户体验:
|
|
25
|
+
* - 使用标准化的用户提示
|
|
26
|
+
* - 提供清晰的错误提示
|
|
27
|
+
* - 支持命令行参数
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const fs = require("fs-extra");
|
|
31
|
+
const path = require("path");
|
|
32
|
+
const https = require("https");
|
|
33
|
+
const http = require("http");
|
|
34
|
+
const { createPluginsHelper } = require("../utils/plugins-helper");
|
|
35
|
+
const validator = require("../utils/validator");
|
|
36
|
+
const pluginConfig = require("../utils/plugin-config");
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 插件管理类
|
|
40
|
+
* 封装所有插件管理相关的方法
|
|
41
|
+
*/
|
|
42
|
+
class PluginManager {
|
|
43
|
+
constructor() {
|
|
44
|
+
// 获取用户主目录,添加 process.cwd() 作为 fallback
|
|
45
|
+
this.homeDir = process.env.HOME || process.env.USERPROFILE || process.cwd();
|
|
46
|
+
this.xxDir = path.join(this.homeDir, ".xx-cli");
|
|
47
|
+
this.builtinPluginsDir = path.join(__dirname, "../plugins");
|
|
48
|
+
this.userPluginsDir = path.join(this.xxDir, "plugins");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 列出所有插件
|
|
53
|
+
* @param {Object} helper - 插件帮助器
|
|
54
|
+
*/
|
|
55
|
+
listPlugins(helper) {
|
|
56
|
+
const core = require("../core");
|
|
57
|
+
const pluginsInfo = core.getPluginsInfo();
|
|
58
|
+
helper.showInfo("\n可用插件列表:");
|
|
59
|
+
helper.showInfo("=====================================");
|
|
60
|
+
pluginsInfo.forEach((plugin) => {
|
|
61
|
+
const isDisabled = pluginConfig.isPluginDisabled(plugin.name);
|
|
62
|
+
const status = isDisabled ? "[已禁用]" : "[已启用]";
|
|
63
|
+
helper.showInfo(`- ${plugin.name} ${status}`);
|
|
64
|
+
});
|
|
65
|
+
helper.showInfo("=====================================");
|
|
66
|
+
helper.showInfo(`总计: ${pluginsInfo.length} 个插件\n`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* 启用插件
|
|
71
|
+
* @param {string} pluginName - 插件名称
|
|
72
|
+
* @param {Object} helper - 插件帮助器
|
|
73
|
+
*/
|
|
74
|
+
enablePlugin(pluginName, helper) {
|
|
75
|
+
// 验证插件名称
|
|
76
|
+
if (!validator.validateCommandParam(pluginName)) {
|
|
77
|
+
helper.showError("错误: 插件名称包含无效字符");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 启用插件
|
|
82
|
+
pluginConfig.enablePlugin(pluginName);
|
|
83
|
+
helper.showSuccess(`已启用插件: ${pluginName}`);
|
|
84
|
+
helper.showInfo("提示: 请重新运行命令以应用更改");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 禁用插件
|
|
89
|
+
* @param {string} pluginName - 插件名称
|
|
90
|
+
* @param {Object} helper - 插件帮助器
|
|
91
|
+
*/
|
|
92
|
+
disablePlugin(pluginName, helper) {
|
|
93
|
+
// 验证插件名称
|
|
94
|
+
if (!validator.validateCommandParam(pluginName)) {
|
|
95
|
+
helper.showError("错误: 插件名称包含无效字符");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 禁用插件
|
|
100
|
+
pluginConfig.disablePlugin(pluginName);
|
|
101
|
+
helper.showSuccess(`已禁用插件: ${pluginName}`);
|
|
102
|
+
helper.showInfo("提示: 请重新运行命令以应用更改");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 删除插件
|
|
107
|
+
* @param {string} pluginName - 插件名称
|
|
108
|
+
* @param {Object} helper - 插件帮助器
|
|
109
|
+
*/
|
|
110
|
+
removePlugin(pluginName, helper) {
|
|
111
|
+
// 验证插件名称
|
|
112
|
+
if (!validator.validateCommandParam(pluginName)) {
|
|
113
|
+
helper.showError("错误: 插件名称包含无效字符");
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 构建插件文件路径
|
|
118
|
+
const pluginFileName = `${pluginName}.js`;
|
|
119
|
+
let pluginPath = path.join(this.builtinPluginsDir, pluginFileName);
|
|
120
|
+
|
|
121
|
+
// 如果内置插件目录中不存在,则从用户插件目录查找
|
|
122
|
+
if (!fs.existsSync(pluginPath)) {
|
|
123
|
+
pluginPath = path.join(this.userPluginsDir, pluginFileName);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 检查插件文件是否存在
|
|
127
|
+
if (!fs.existsSync(pluginPath)) {
|
|
128
|
+
helper.showError(`错误: 插件文件不存在`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
// 删除插件文件
|
|
134
|
+
fs.unlinkSync(pluginPath);
|
|
135
|
+
|
|
136
|
+
// 从配置中移除插件的禁用状态
|
|
137
|
+
pluginConfig.removePluginFromConfig(pluginName);
|
|
138
|
+
|
|
139
|
+
helper.showSuccess(`已成功删除插件: ${pluginName}`);
|
|
140
|
+
helper.showInfo("提示: 请重新运行命令以应用更改");
|
|
141
|
+
} catch (error) {
|
|
142
|
+
helper.showError(`删除插件失败: ${error.message}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* 转换为驼峰命名
|
|
148
|
+
* @param {string} str - 字符串
|
|
149
|
+
* @returns {string} 驼峰命名的字符串
|
|
150
|
+
*/
|
|
151
|
+
toCamelCase(str) {
|
|
152
|
+
if (!str) {
|
|
153
|
+
return "Plugin";
|
|
154
|
+
}
|
|
155
|
+
return str
|
|
156
|
+
.replace(/-([a-zA-Z])/g, (g) => g[1].toUpperCase())
|
|
157
|
+
.replace(/^[a-zA-Z]/, (g) => g.toUpperCase());
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* 生成插件模板内容
|
|
162
|
+
* @param {string} name - 插件名称
|
|
163
|
+
* @param {string} format - 插件格式,new 或 old
|
|
164
|
+
* @returns {string} 插件模板内容
|
|
165
|
+
*/
|
|
166
|
+
generatePluginTemplate(name, format) {
|
|
167
|
+
// 参数检查
|
|
168
|
+
if (!name) {
|
|
169
|
+
throw new Error("插件名称不能为空");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// 生成插件命令名称(去除后缀,转小写,连字符转驼峰)
|
|
173
|
+
const commandName = name
|
|
174
|
+
.replace(".js", "")
|
|
175
|
+
.toLowerCase()
|
|
176
|
+
.replace(/-([a-zA-Z])/g, (g) => g[1].toUpperCase());
|
|
177
|
+
|
|
178
|
+
// 生成驼峰命名的类名
|
|
179
|
+
const className = this.toCamelCase(name);
|
|
180
|
+
|
|
181
|
+
// 生成配置对象格式模板,包含类结构实现
|
|
182
|
+
return `/**
|
|
183
|
+
* ${name} 插件
|
|
184
|
+
* 用于实现 ${name} 相关功能
|
|
185
|
+
*
|
|
186
|
+
* 命令说明:
|
|
187
|
+
* - ${commandName} [options]:${name} 相关功能
|
|
188
|
+
* - 示例:${commandName}
|
|
189
|
+
*
|
|
190
|
+
* 功能说明:
|
|
191
|
+
* - 实现 ${name} 相关功能
|
|
192
|
+
*
|
|
193
|
+
* 用户体验:
|
|
194
|
+
* - 使用标准化的用户提示
|
|
195
|
+
* - 提供清晰的错误提示
|
|
196
|
+
* - 支持命令行参数
|
|
197
|
+
*/
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* ${name} 插件类
|
|
201
|
+
* 实现 ${name} 相关核心功能
|
|
202
|
+
*/
|
|
203
|
+
class ${className}Plugin {
|
|
204
|
+
constructor() {
|
|
205
|
+
this.pluginName = '${name}';
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* 执行 ${name} 命令
|
|
210
|
+
* @param {Array} args - 命令参数
|
|
211
|
+
* @param {Object} options - 命令选项
|
|
212
|
+
* @param {Object} helper - 插件帮助器
|
|
213
|
+
*/
|
|
214
|
+
async execute(args, options, helper) {
|
|
215
|
+
helper.showInfo('${name} 命令执行成功!');
|
|
216
|
+
if (options && options.verbose) {
|
|
217
|
+
helper.showInfo('详细信息:');
|
|
218
|
+
helper.showInfo('- 命令名称: ${commandName}');
|
|
219
|
+
helper.showInfo('- 执行时间: ' + new Date().toLocaleString());
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// 创建插件实例
|
|
225
|
+
const pluginInstance = new ${className}Plugin();
|
|
226
|
+
|
|
227
|
+
module.exports = {
|
|
228
|
+
name: "${commandName}",
|
|
229
|
+
alias: "${commandName.charAt(0)}",
|
|
230
|
+
description: "${name} 相关功能",
|
|
231
|
+
options: [
|
|
232
|
+
{
|
|
233
|
+
name: "-v, --verbose",
|
|
234
|
+
description: "显示详细信息",
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
name: "-h, --help",
|
|
238
|
+
description: "显示帮助信息",
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
action: async (args, options, helper) => {
|
|
242
|
+
await pluginInstance.execute(args, options, helper);
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* 创建插件
|
|
250
|
+
* @param {string} name - 插件名称
|
|
251
|
+
* @param {Object} options - 选项
|
|
252
|
+
* @param {Object} helper - 插件帮助器
|
|
253
|
+
*/
|
|
254
|
+
async createPlugin(name, options, helper) {
|
|
255
|
+
try {
|
|
256
|
+
helper.showInfo("创建插件模板...");
|
|
257
|
+
|
|
258
|
+
// 插件目录路径(用户插件目录,不会被重新安装影响)
|
|
259
|
+
const pluginsDir = this.userPluginsDir;
|
|
260
|
+
const pluginFileName = `${name}.js`;
|
|
261
|
+
const pluginPath = path.join(pluginsDir, pluginFileName);
|
|
262
|
+
|
|
263
|
+
// 检查插件是否已存在
|
|
264
|
+
if (fs.existsSync(pluginPath)) {
|
|
265
|
+
helper.showInfo(`插件 ${name} 已存在,请使用其他名称`);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 生成插件模板内容
|
|
270
|
+
const format = options.format || "new";
|
|
271
|
+
const pluginContent = this.generatePluginTemplate(name, format);
|
|
272
|
+
|
|
273
|
+
// 写入插件文件
|
|
274
|
+
await fs.writeFile(pluginPath, pluginContent);
|
|
275
|
+
|
|
276
|
+
helper.showSuccess(`插件模板已成功创建到:${pluginPath}`);
|
|
277
|
+
helper.showInfo("提示:请编辑插件文件,实现具体功能");
|
|
278
|
+
helper.showInfo('测试方法:运行 "node bin/cli.js <插件命令>"');
|
|
279
|
+
|
|
280
|
+
// 使用 vscode 打开创建的插件文件
|
|
281
|
+
try {
|
|
282
|
+
const { exec } = require("child_process");
|
|
283
|
+
const os = require("os");
|
|
284
|
+
let command;
|
|
285
|
+
|
|
286
|
+
// 根据操作系统类型构建不同的命令
|
|
287
|
+
if (os.platform() === "win32") {
|
|
288
|
+
// Windows 系统
|
|
289
|
+
command = `start code "${pluginPath}"`;
|
|
290
|
+
} else {
|
|
291
|
+
// macOS 和 Linux 系统
|
|
292
|
+
command = `code "${pluginPath}"`;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
exec(command, (error) => {
|
|
296
|
+
if (error) {
|
|
297
|
+
helper.showInfo(
|
|
298
|
+
"提示:无法自动打开 vscode,请手动打开插件文件进行编辑",
|
|
299
|
+
);
|
|
300
|
+
} else {
|
|
301
|
+
helper.showInfo("已自动打开 vscode 编辑插件文件");
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
} catch (error) {
|
|
305
|
+
helper.showInfo(
|
|
306
|
+
"提示:无法自动打开 vscode,请手动打开插件文件进行编辑",
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
} catch (error) {
|
|
310
|
+
helper.showError(`创建插件模板失败: ${error.message || error}`);
|
|
311
|
+
helper.showInfo(`错误详情: ${JSON.stringify(error)}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* 管理插件配置
|
|
317
|
+
* @param {string} pluginName - 插件名称
|
|
318
|
+
* @param {string} key - 配置键
|
|
319
|
+
* @param {string} value - 配置值
|
|
320
|
+
* @param {Object} helper - 插件帮助器
|
|
321
|
+
*/
|
|
322
|
+
managePluginConfig(pluginName, key, value, helper) {
|
|
323
|
+
// 验证插件名称
|
|
324
|
+
if (!validator.validateCommandParam(pluginName)) {
|
|
325
|
+
helper.showError("错误: 插件名称包含无效字符");
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const pluginConfigDir = path.join(this.xxDir, pluginName);
|
|
330
|
+
const configFile = path.join(pluginConfigDir, "config.json");
|
|
331
|
+
|
|
332
|
+
// 确保插件配置目录存在
|
|
333
|
+
fs.ensureDirSync(pluginConfigDir);
|
|
334
|
+
|
|
335
|
+
// 读取现有配置
|
|
336
|
+
let config = {};
|
|
337
|
+
if (fs.existsSync(configFile)) {
|
|
338
|
+
config = JSON.parse(fs.readFileSync(configFile, "utf8"));
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (!key) {
|
|
342
|
+
// 显示所有配置
|
|
343
|
+
helper.showInfo(`插件 ${pluginName} 的配置:`);
|
|
344
|
+
helper.showInfo("=====================================");
|
|
345
|
+
if (Object.keys(config).length > 0) {
|
|
346
|
+
Object.entries(config).forEach(([k, v]) => {
|
|
347
|
+
helper.showInfo(`${k}: ${v}`);
|
|
348
|
+
});
|
|
349
|
+
} else {
|
|
350
|
+
helper.showInfo("无配置项");
|
|
351
|
+
}
|
|
352
|
+
helper.showInfo("=====================================");
|
|
353
|
+
} else if (!value) {
|
|
354
|
+
// 显示指定配置
|
|
355
|
+
helper.showInfo(`插件 ${pluginName} 的配置 ${key}:`);
|
|
356
|
+
helper.showInfo("=====================================");
|
|
357
|
+
if (config.hasOwnProperty(key)) {
|
|
358
|
+
helper.showInfo(`${key}: ${config[key]}`);
|
|
359
|
+
} else {
|
|
360
|
+
helper.showInfo(`配置项 ${key} 不存在`);
|
|
361
|
+
}
|
|
362
|
+
helper.showInfo("=====================================");
|
|
363
|
+
} else {
|
|
364
|
+
// 设置配置
|
|
365
|
+
config[key] = value;
|
|
366
|
+
fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
|
|
367
|
+
helper.showSuccess(`已设置插件 ${pluginName} 的配置 ${key} = ${value}`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* 添加外部插件
|
|
373
|
+
* @param {string} url - 插件 URL
|
|
374
|
+
* @param {Object} helper - 插件帮助器
|
|
375
|
+
*/
|
|
376
|
+
async addExternalPlugin(url, helper) {
|
|
377
|
+
try {
|
|
378
|
+
// 确保用户插件目录存在
|
|
379
|
+
fs.ensureDirSync(this.userPluginsDir);
|
|
380
|
+
|
|
381
|
+
// 提取插件名称
|
|
382
|
+
const pluginFileName = path.basename(url);
|
|
383
|
+
const pluginPath = path.join(this.userPluginsDir, pluginFileName);
|
|
384
|
+
|
|
385
|
+
// 检查插件是否已存在
|
|
386
|
+
if (fs.existsSync(pluginPath)) {
|
|
387
|
+
helper.showInfo(`插件 ${pluginFileName} 已存在,是否覆盖?`);
|
|
388
|
+
// 这里可以添加交互式确认
|
|
389
|
+
helper.showInfo("已覆盖现有插件");
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// 下载或复制插件
|
|
393
|
+
if (url.startsWith("http://") || url.startsWith("https://")) {
|
|
394
|
+
// 是 URL,下载插件
|
|
395
|
+
helper.showInfo(`正在从 URL 下载插件: ${url}`);
|
|
396
|
+
|
|
397
|
+
// 选择 HTTP 或 HTTPS
|
|
398
|
+
const protocol = url.startsWith("https://") ? https : http;
|
|
399
|
+
|
|
400
|
+
await new Promise((resolve, reject) => {
|
|
401
|
+
protocol
|
|
402
|
+
.get(url, (response) => {
|
|
403
|
+
if (response.statusCode === 200) {
|
|
404
|
+
// 获取文件大小
|
|
405
|
+
const fileSize = parseInt(
|
|
406
|
+
response.headers["content-length"],
|
|
407
|
+
10,
|
|
408
|
+
);
|
|
409
|
+
let downloadedSize = 0;
|
|
410
|
+
|
|
411
|
+
// 创建写入流
|
|
412
|
+
const writer = fs.createWriteStream(pluginPath);
|
|
413
|
+
|
|
414
|
+
// 监听数据事件,显示进度
|
|
415
|
+
response.on("data", (chunk) => {
|
|
416
|
+
downloadedSize += chunk.length;
|
|
417
|
+
const percentage = Math.round(
|
|
418
|
+
(downloadedSize / fileSize) * 100,
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
// 清除当前行并显示进度
|
|
422
|
+
process.stdout.clearLine();
|
|
423
|
+
process.stdout.cursorTo(0);
|
|
424
|
+
process.stdout.write(`下载进度: ${percentage}%`);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// 管道数据到文件
|
|
428
|
+
response.pipe(writer);
|
|
429
|
+
|
|
430
|
+
// 监听完成事件
|
|
431
|
+
writer.on("finish", () => {
|
|
432
|
+
console.log(""); // 换行
|
|
433
|
+
helper.showSuccess(`插件已成功下载到: ${pluginPath}`);
|
|
434
|
+
resolve();
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
// 监听错误事件
|
|
438
|
+
writer.on("error", (error) => {
|
|
439
|
+
reject(error);
|
|
440
|
+
});
|
|
441
|
+
} else {
|
|
442
|
+
reject(new Error(`下载失败,状态码: ${response.statusCode}`));
|
|
443
|
+
}
|
|
444
|
+
})
|
|
445
|
+
.on("error", (error) => {
|
|
446
|
+
reject(error);
|
|
447
|
+
});
|
|
448
|
+
});
|
|
449
|
+
} else if (fs.existsSync(url) && url.endsWith(".js")) {
|
|
450
|
+
// 是本地文件,拷贝插件
|
|
451
|
+
helper.showInfo(`正在从本地文件拷贝插件: ${url}`);
|
|
452
|
+
|
|
453
|
+
// 提取文件名
|
|
454
|
+
const pluginFileName = path.basename(url);
|
|
455
|
+
const pluginPath = path.join(this.userPluginsDir, pluginFileName);
|
|
456
|
+
|
|
457
|
+
// 拷贝文件
|
|
458
|
+
await fs.copy(url, pluginPath);
|
|
459
|
+
|
|
460
|
+
helper.showSuccess(`插件已成功拷贝到: ${pluginPath}`);
|
|
461
|
+
} else {
|
|
462
|
+
helper.showError("错误: 无效的插件URL或本地文件路径");
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// 提示用户
|
|
467
|
+
helper.showInfo("\n提示:");
|
|
468
|
+
helper.showInfo("- 插件已自动注册到命令系统");
|
|
469
|
+
helper.showInfo('- 可以使用 "xx plugin list" 命令查看已安装的插件');
|
|
470
|
+
helper.showInfo('- 可以使用 "xx <plugin-command>" 命令使用插件功能');
|
|
471
|
+
helper.showInfo("- 此插件保存在用户目录中,不会被重新安装影响");
|
|
472
|
+
} catch (error) {
|
|
473
|
+
helper.showError("添加插件失败:", error.message);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* 同步开发插件
|
|
479
|
+
* 将当前目录下的 lib/myplugins 同步到 CLI 配置目录的 plugins
|
|
480
|
+
* @param {Object} helper - 插件帮助器
|
|
481
|
+
*/
|
|
482
|
+
async syncDevPlugins(helper) {
|
|
483
|
+
try {
|
|
484
|
+
// 确保用户插件目录存在
|
|
485
|
+
fs.ensureDirSync(this.userPluginsDir);
|
|
486
|
+
|
|
487
|
+
// 开发插件目录路径
|
|
488
|
+
const devPluginsDir = path.join(process.cwd(), "lib/myplugins");
|
|
489
|
+
|
|
490
|
+
// 检查开发插件目录是否存在
|
|
491
|
+
if (!fs.existsSync(devPluginsDir)) {
|
|
492
|
+
helper.showError(`开发插件目录不存在: ${devPluginsDir}`);
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// 读取开发插件目录中的文件
|
|
497
|
+
const devPluginFiles = fs
|
|
498
|
+
.readdirSync(devPluginsDir)
|
|
499
|
+
.filter((file) => file.endsWith(".js"));
|
|
500
|
+
|
|
501
|
+
if (devPluginFiles.length === 0) {
|
|
502
|
+
helper.showInfo("开发插件目录中没有 JavaScript 文件");
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
helper.showInfo(
|
|
507
|
+
`找到 ${devPluginFiles.length} 个开发插件文件,开始同步...`,
|
|
508
|
+
);
|
|
509
|
+
|
|
510
|
+
// 同步每个插件文件
|
|
511
|
+
let syncedCount = 0;
|
|
512
|
+
for (const file of devPluginFiles) {
|
|
513
|
+
const sourcePath = path.join(devPluginsDir, file);
|
|
514
|
+
const targetPath = path.join(this.userPluginsDir, file);
|
|
515
|
+
|
|
516
|
+
// 检查源文件是否存在
|
|
517
|
+
if (fs.existsSync(sourcePath)) {
|
|
518
|
+
// 复制文件
|
|
519
|
+
await fs.copy(sourcePath, targetPath, { overwrite: true });
|
|
520
|
+
helper.showSuccess(`已同步: ${file}`);
|
|
521
|
+
syncedCount++;
|
|
522
|
+
} else {
|
|
523
|
+
helper.showWarning(`跳过不存在的文件: ${file}`);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
helper.showSuccess(`同步完成,共同步 ${syncedCount} 个插件文件`);
|
|
528
|
+
helper.showInfo(`插件已同步到: ${this.userPluginsDir}`);
|
|
529
|
+
helper.showInfo("\n提示:");
|
|
530
|
+
helper.showInfo("- 插件已自动注册到命令系统");
|
|
531
|
+
helper.showInfo('- 可以使用 "xx plugin list" 命令查看已安装的插件');
|
|
532
|
+
helper.showInfo('- 可以使用 "xx <plugin-command>" 命令使用插件功能');
|
|
533
|
+
} catch (error) {
|
|
534
|
+
helper.showError(`同步开发插件失败: ${error.message}`);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// 创建插件管理实例
|
|
540
|
+
const pluginManager = new PluginManager();
|
|
541
|
+
|
|
542
|
+
module.exports = {
|
|
543
|
+
name: "plugin-manager",
|
|
544
|
+
alias: "p",
|
|
545
|
+
description: "插件管理",
|
|
546
|
+
subcommands: [
|
|
547
|
+
{
|
|
548
|
+
name: "list",
|
|
549
|
+
alias: "ls",
|
|
550
|
+
description: "列出所有插件",
|
|
551
|
+
action: (args, options, helper) => {
|
|
552
|
+
pluginManager.listPlugins(helper);
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
name: "enable",
|
|
557
|
+
alias: "en",
|
|
558
|
+
description: "启用插件",
|
|
559
|
+
args: [
|
|
560
|
+
{
|
|
561
|
+
name: "plugin",
|
|
562
|
+
description: "插件名称",
|
|
563
|
+
required: true,
|
|
564
|
+
},
|
|
565
|
+
],
|
|
566
|
+
action: (args, options, helper) => {
|
|
567
|
+
pluginManager.enablePlugin(args.plugin, helper);
|
|
568
|
+
},
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
name: "disable",
|
|
572
|
+
alias: "dis",
|
|
573
|
+
description: "禁用插件",
|
|
574
|
+
args: [
|
|
575
|
+
{
|
|
576
|
+
name: "plugin",
|
|
577
|
+
description: "插件名称",
|
|
578
|
+
required: true,
|
|
579
|
+
},
|
|
580
|
+
],
|
|
581
|
+
action: (args, options, helper) => {
|
|
582
|
+
pluginManager.disablePlugin(args.plugin, helper);
|
|
583
|
+
},
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
name: "add",
|
|
587
|
+
description: "添加外部自定义插件",
|
|
588
|
+
args: [
|
|
589
|
+
{
|
|
590
|
+
name: "url",
|
|
591
|
+
description: "插件 URL",
|
|
592
|
+
required: true,
|
|
593
|
+
},
|
|
594
|
+
],
|
|
595
|
+
action: async (args, options, helper) => {
|
|
596
|
+
await pluginManager.addExternalPlugin(args.url, helper);
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
name: "remove",
|
|
601
|
+
alias: "rm",
|
|
602
|
+
description: "删除指定插件",
|
|
603
|
+
args: [
|
|
604
|
+
{
|
|
605
|
+
name: "plugin",
|
|
606
|
+
description: "插件名称",
|
|
607
|
+
required: true,
|
|
608
|
+
},
|
|
609
|
+
],
|
|
610
|
+
action: (args, options, helper) => {
|
|
611
|
+
pluginManager.removePlugin(args.plugin, helper);
|
|
612
|
+
},
|
|
613
|
+
},
|
|
614
|
+
{
|
|
615
|
+
name: "create",
|
|
616
|
+
alias: "cr",
|
|
617
|
+
description: "创建指定名称的插件模板",
|
|
618
|
+
args: [
|
|
619
|
+
{
|
|
620
|
+
name: "name",
|
|
621
|
+
description: "插件名称",
|
|
622
|
+
required: true,
|
|
623
|
+
},
|
|
624
|
+
],
|
|
625
|
+
options: [
|
|
626
|
+
{
|
|
627
|
+
name: "-f, --format <format>",
|
|
628
|
+
description:
|
|
629
|
+
"插件模板格式,支持 new(配置对象格式)或 old(类结构格式)",
|
|
630
|
+
default: "new",
|
|
631
|
+
},
|
|
632
|
+
],
|
|
633
|
+
action: async (args, options, helper) => {
|
|
634
|
+
helper.showInfo(`args 对象: ${JSON.stringify(args)}`);
|
|
635
|
+
helper.showInfo(`options 对象: ${JSON.stringify(options)}`);
|
|
636
|
+
|
|
637
|
+
// 提取插件名称
|
|
638
|
+
let pluginName = "unnamed";
|
|
639
|
+
if (Array.isArray(args)) {
|
|
640
|
+
// 遍历数组,找到非对象的字符串参数
|
|
641
|
+
for (const arg of args) {
|
|
642
|
+
if (typeof arg === "string") {
|
|
643
|
+
pluginName = arg;
|
|
644
|
+
break;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
} else if (typeof args === "string") {
|
|
648
|
+
pluginName = args;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
helper.showInfo(`提取的插件名称: ${pluginName}`);
|
|
652
|
+
await pluginManager.createPlugin(pluginName, options, helper);
|
|
653
|
+
},
|
|
654
|
+
},
|
|
655
|
+
{
|
|
656
|
+
name: "config",
|
|
657
|
+
alias: "c",
|
|
658
|
+
description: "查看和设置插件配置",
|
|
659
|
+
args: [
|
|
660
|
+
{
|
|
661
|
+
name: "plugin",
|
|
662
|
+
description: "插件名称",
|
|
663
|
+
required: true,
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
name: "key",
|
|
667
|
+
description: "配置键",
|
|
668
|
+
optional: true,
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
name: "value",
|
|
672
|
+
description: "配置值",
|
|
673
|
+
optional: true,
|
|
674
|
+
},
|
|
675
|
+
],
|
|
676
|
+
action: (args, options, helper) => {
|
|
677
|
+
pluginManager.managePluginConfig(
|
|
678
|
+
args.plugin,
|
|
679
|
+
args.key,
|
|
680
|
+
args.value,
|
|
681
|
+
helper,
|
|
682
|
+
);
|
|
683
|
+
},
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
name: "sync",
|
|
687
|
+
alias: "s",
|
|
688
|
+
description: "将当前目录下的 lib/myplugins 同步到 CLI 配置目录的 plugins",
|
|
689
|
+
action: async (args, options, helper) => {
|
|
690
|
+
await pluginManager.syncDevPlugins(helper);
|
|
691
|
+
},
|
|
692
|
+
},
|
|
693
|
+
],
|
|
694
|
+
options: [
|
|
695
|
+
{
|
|
696
|
+
name: "-h, --help",
|
|
697
|
+
description: "显示帮助信息",
|
|
698
|
+
},
|
|
699
|
+
],
|
|
700
|
+
};
|