@holic512/slothtool 1.0.2 → 1.0.3
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 +330 -31
- package/bin/slothtool.js +60 -31
- package/lib/commands/config.js +35 -0
- package/lib/commands/index.js +14 -4
- package/lib/commands/install.js +9 -9
- package/lib/commands/interactive.js +498 -0
- package/lib/commands/list.js +16 -14
- package/lib/commands/run.js +38 -35
- package/lib/commands/uninstall-all.js +78 -0
- package/lib/commands/uninstall.js +9 -9
- package/lib/commands/update-all.js +10 -0
- package/lib/commands/update.js +20 -0
- package/lib/i18n.js +329 -0
- package/lib/official-plugins.json +24 -0
- package/lib/plugin-manager.js +276 -78
- package/lib/registry.js +43 -43
- package/lib/settings.js +85 -0
- package/lib/utils.js +46 -18
- package/package.json +5 -2
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
const prompts = require('prompts');
|
|
2
|
+
const {spawn} = require('child_process');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const registry = require('../registry');
|
|
6
|
+
const settings = require('../settings');
|
|
7
|
+
const {installPlugin, uninstallPlugin, updatePlugin, updateAllPlugins} = require('../plugin-manager');
|
|
8
|
+
const uninstallAll = require('./uninstall-all');
|
|
9
|
+
const {t} = require('../i18n');
|
|
10
|
+
|
|
11
|
+
// 读取官方插件配置
|
|
12
|
+
const officialPlugins = require('../official-plugins.json').officialPlugins;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 交互式模式主函数
|
|
16
|
+
*/
|
|
17
|
+
async function interactive() {
|
|
18
|
+
console.log('\n' + t('pluginManager') + '\n');
|
|
19
|
+
|
|
20
|
+
while (true) {
|
|
21
|
+
const response = await prompts({
|
|
22
|
+
type: 'select',
|
|
23
|
+
name: 'action',
|
|
24
|
+
message: t('interactive.mainMenu'),
|
|
25
|
+
choices: [
|
|
26
|
+
{title: t('interactive.installPlugin'), value: 'install'},
|
|
27
|
+
{title: t('interactive.uninstallPlugin'), value: 'uninstall'},
|
|
28
|
+
{title: t('interactive.updatePlugin'), value: 'update'},
|
|
29
|
+
{title: t('interactive.updateAllPlugins'), value: 'updateAll'},
|
|
30
|
+
{title: t('interactive.listPlugins'), value: 'list'},
|
|
31
|
+
{title: t('interactive.runPlugin'), value: 'run'},
|
|
32
|
+
{title: t('interactive.configLanguage'), value: 'config'},
|
|
33
|
+
{title: t('interactive.uninstallAll'), value: 'uninstallAll'},
|
|
34
|
+
{title: t('interactive.exit'), value: 'exit'}
|
|
35
|
+
]
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (!response.action || response.action === 'exit') {
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.log('');
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
switch (response.action) {
|
|
46
|
+
case 'install':
|
|
47
|
+
await handleInstall();
|
|
48
|
+
break;
|
|
49
|
+
case 'uninstall':
|
|
50
|
+
await handleUninstall();
|
|
51
|
+
break;
|
|
52
|
+
case 'update':
|
|
53
|
+
await handleUpdate();
|
|
54
|
+
break;
|
|
55
|
+
case 'updateAll':
|
|
56
|
+
await handleUpdateAll();
|
|
57
|
+
break;
|
|
58
|
+
case 'list':
|
|
59
|
+
await handleList();
|
|
60
|
+
break;
|
|
61
|
+
case 'run':
|
|
62
|
+
await handleRun();
|
|
63
|
+
break;
|
|
64
|
+
case 'config':
|
|
65
|
+
await handleConfig();
|
|
66
|
+
break;
|
|
67
|
+
case 'uninstallAll':
|
|
68
|
+
await uninstallAll();
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
} catch (error) {
|
|
72
|
+
if (error.message !== 'cancelled') {
|
|
73
|
+
console.error('Error:', error.message);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
await waitForEnter();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 处理安装插件
|
|
83
|
+
*/
|
|
84
|
+
async function handleInstall() {
|
|
85
|
+
const installType = await prompts({
|
|
86
|
+
type: 'select',
|
|
87
|
+
name: 'type',
|
|
88
|
+
message: t('interactive.installPlugin'),
|
|
89
|
+
choices: [
|
|
90
|
+
{title: t('interactive.installOfficial'), value: 'official'},
|
|
91
|
+
{title: t('interactive.installCustom'), value: 'custom'}
|
|
92
|
+
]
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (!installType.type) {
|
|
96
|
+
throw new Error('cancelled');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (installType.type === 'official') {
|
|
100
|
+
await installOfficialPlugin();
|
|
101
|
+
} else {
|
|
102
|
+
await installCustomPlugin();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 安装官方插件
|
|
108
|
+
*/
|
|
109
|
+
async function installOfficialPlugin() {
|
|
110
|
+
const lang = settings.getLanguage();
|
|
111
|
+
|
|
112
|
+
const choices = officialPlugins.map(plugin => {
|
|
113
|
+
const description = lang === 'zh' ? plugin.description : plugin.descriptionEn;
|
|
114
|
+
const features = lang === 'zh' ? plugin.features : plugin.featuresEn;
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
title: `${plugin.alias} - ${description}`,
|
|
118
|
+
description: features.join(', '),
|
|
119
|
+
value: plugin
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const response = await prompts({
|
|
124
|
+
type: 'select',
|
|
125
|
+
name: 'plugin',
|
|
126
|
+
message: t('interactive.selectOfficialPlugin'),
|
|
127
|
+
choices: choices
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
if (!response.plugin) {
|
|
131
|
+
throw new Error('cancelled');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const plugin = response.plugin;
|
|
135
|
+
|
|
136
|
+
// 显示插件详细信息
|
|
137
|
+
console.log('\n' + t('interactive.pluginInfo') + ':');
|
|
138
|
+
console.log(` ${plugin.name}`);
|
|
139
|
+
console.log(` ${t('interactive.author')} ${plugin.author}`);
|
|
140
|
+
console.log(` ${t('interactive.version')} ${plugin.version}`);
|
|
141
|
+
console.log(` ${t('interactive.features')}`);
|
|
142
|
+
const features = lang === 'zh' ? plugin.features : plugin.featuresEn;
|
|
143
|
+
features.forEach(f => console.log(` - ${f}`));
|
|
144
|
+
|
|
145
|
+
// 确认安装
|
|
146
|
+
const confirm = await prompts({
|
|
147
|
+
type: 'confirm',
|
|
148
|
+
name: 'value',
|
|
149
|
+
message: t('interactive.confirmInstall', {name: plugin.name}),
|
|
150
|
+
initial: true
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (!confirm.value) {
|
|
154
|
+
console.log(t('interactive.operationCancelled'));
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
console.log('');
|
|
159
|
+
installPlugin(plugin.name);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* 安装自定义插件
|
|
164
|
+
*/
|
|
165
|
+
async function installCustomPlugin() {
|
|
166
|
+
const response = await prompts({
|
|
167
|
+
type: 'text',
|
|
168
|
+
name: 'packageName',
|
|
169
|
+
message: t('interactive.enterPackageName'),
|
|
170
|
+
validate: value => value.length > 0 ? true : 'Package name is required'
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
if (!response.packageName) {
|
|
174
|
+
throw new Error('cancelled');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
console.log('');
|
|
178
|
+
installPlugin(response.packageName);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* 处理卸载插件
|
|
183
|
+
*/
|
|
184
|
+
async function handleUninstall() {
|
|
185
|
+
const plugins = registry.getAllPlugins();
|
|
186
|
+
const pluginList = Object.keys(plugins);
|
|
187
|
+
|
|
188
|
+
if (pluginList.length === 0) {
|
|
189
|
+
console.log(t('interactive.noPluginsToUninstall'));
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const choices = pluginList.map(alias => ({
|
|
194
|
+
title: `${alias} (${plugins[alias].name})`,
|
|
195
|
+
value: alias
|
|
196
|
+
}));
|
|
197
|
+
|
|
198
|
+
const response = await prompts({
|
|
199
|
+
type: 'select',
|
|
200
|
+
name: 'alias',
|
|
201
|
+
message: t('interactive.selectPlugin'),
|
|
202
|
+
choices: choices
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
if (!response.alias) {
|
|
206
|
+
throw new Error('cancelled');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 确认卸载
|
|
210
|
+
const confirm = await prompts({
|
|
211
|
+
type: 'confirm',
|
|
212
|
+
name: 'value',
|
|
213
|
+
message: t('interactive.confirmUninstall', {alias: response.alias}),
|
|
214
|
+
initial: false
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (!confirm.value) {
|
|
218
|
+
console.log(t('interactive.operationCancelled'));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
console.log('');
|
|
223
|
+
uninstallPlugin(response.alias);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* 处理更新插件
|
|
228
|
+
*/
|
|
229
|
+
async function handleUpdate() {
|
|
230
|
+
const plugins = registry.getAllPlugins();
|
|
231
|
+
const pluginList = Object.keys(plugins);
|
|
232
|
+
|
|
233
|
+
if (pluginList.length === 0) {
|
|
234
|
+
console.log(t('interactive.noPluginsToUpdate'));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const choices = pluginList.map(alias => ({
|
|
239
|
+
title: `${alias} (${plugins[alias].name} v${plugins[alias].version})`,
|
|
240
|
+
value: alias
|
|
241
|
+
}));
|
|
242
|
+
|
|
243
|
+
const response = await prompts({
|
|
244
|
+
type: 'select',
|
|
245
|
+
name: 'alias',
|
|
246
|
+
message: t('interactive.selectPluginToUpdate'),
|
|
247
|
+
choices: choices
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
if (!response.alias) {
|
|
251
|
+
throw new Error('cancelled');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// 确认更新
|
|
255
|
+
const confirm = await prompts({
|
|
256
|
+
type: 'confirm',
|
|
257
|
+
name: 'value',
|
|
258
|
+
message: t('interactive.confirmUpdate', {alias: response.alias}),
|
|
259
|
+
initial: true
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
if (!confirm.value) {
|
|
263
|
+
console.log(t('interactive.operationCancelled'));
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
console.log('');
|
|
268
|
+
updatePlugin(response.alias);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* 处理更新所有插件
|
|
273
|
+
*/
|
|
274
|
+
async function handleUpdateAll() {
|
|
275
|
+
const plugins = registry.getAllPlugins();
|
|
276
|
+
const pluginList = Object.keys(plugins);
|
|
277
|
+
|
|
278
|
+
if (pluginList.length === 0) {
|
|
279
|
+
console.log(t('interactive.noPluginsToUpdate'));
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// 确认更新所有插件
|
|
284
|
+
const confirm = await prompts({
|
|
285
|
+
type: 'confirm',
|
|
286
|
+
name: 'value',
|
|
287
|
+
message: t('interactive.confirmUpdateAll', {count: pluginList.length}),
|
|
288
|
+
initial: true
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
if (!confirm.value) {
|
|
292
|
+
console.log(t('interactive.operationCancelled'));
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
console.log('');
|
|
297
|
+
updateAllPlugins();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* 处理列出插件
|
|
302
|
+
*/
|
|
303
|
+
async function handleList() {
|
|
304
|
+
const plugins = registry.getAllPlugins();
|
|
305
|
+
|
|
306
|
+
if (Object.keys(plugins).length === 0) {
|
|
307
|
+
console.log(t('noPlugins'));
|
|
308
|
+
console.log(t('installExample'));
|
|
309
|
+
console.log(' slothtool install <plugin-name>');
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
console.log(t('installedPlugins') + '\n');
|
|
314
|
+
for (const [alias, info] of Object.entries(plugins)) {
|
|
315
|
+
console.log(` ${alias}`);
|
|
316
|
+
console.log(` Package: ${info.name}`);
|
|
317
|
+
console.log(` Version: ${info.version}`);
|
|
318
|
+
console.log(` Installed: ${new Date(info.installedAt).toLocaleString()}`);
|
|
319
|
+
console.log('');
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* 处理运行插件
|
|
325
|
+
*/
|
|
326
|
+
async function handleRun() {
|
|
327
|
+
const plugins = registry.getAllPlugins();
|
|
328
|
+
const pluginList = Object.keys(plugins);
|
|
329
|
+
|
|
330
|
+
if (pluginList.length === 0) {
|
|
331
|
+
console.log(t('interactive.noPluginsToRun'));
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const choices = pluginList.map(alias => ({
|
|
336
|
+
title: `${alias} (${plugins[alias].name})`,
|
|
337
|
+
value: alias
|
|
338
|
+
}));
|
|
339
|
+
|
|
340
|
+
const response = await prompts({
|
|
341
|
+
type: 'select',
|
|
342
|
+
name: 'alias',
|
|
343
|
+
message: t('interactive.selectPlugin'),
|
|
344
|
+
choices: choices
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
if (!response.alias) {
|
|
348
|
+
throw new Error('cancelled');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const plugin = plugins[response.alias];
|
|
352
|
+
|
|
353
|
+
// 检查插件是否支持交互式模式
|
|
354
|
+
const pluginPackageJsonPath = path.join(
|
|
355
|
+
path.dirname(plugin.binPath),
|
|
356
|
+
'..',
|
|
357
|
+
'package.json'
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
let hasInteractiveMode = false;
|
|
361
|
+
let interactiveFlag = '-i';
|
|
362
|
+
|
|
363
|
+
if (fs.existsSync(pluginPackageJsonPath)) {
|
|
364
|
+
try {
|
|
365
|
+
const pluginPackage = JSON.parse(fs.readFileSync(pluginPackageJsonPath, 'utf8'));
|
|
366
|
+
if (pluginPackage.slothtool && pluginPackage.slothtool.interactive) {
|
|
367
|
+
hasInteractiveMode = true;
|
|
368
|
+
interactiveFlag = pluginPackage.slothtool.interactiveFlag || '-i';
|
|
369
|
+
}
|
|
370
|
+
} catch (error) {
|
|
371
|
+
// 忽略解析错误
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
console.log('');
|
|
376
|
+
|
|
377
|
+
// 如果插件支持交互式模式,直接启动交互式模式
|
|
378
|
+
if (hasInteractiveMode) {
|
|
379
|
+
console.log(`Running: ${response.alias} (interactive mode)...`);
|
|
380
|
+
console.log('─'.repeat(50));
|
|
381
|
+
|
|
382
|
+
const child = spawn('node', [plugin.binPath, interactiveFlag], {
|
|
383
|
+
stdio: 'inherit'
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
await new Promise((resolve, reject) => {
|
|
387
|
+
child.on('error', (error) => {
|
|
388
|
+
console.error(t('failedToRun', {pluginAlias: response.alias}), error.message);
|
|
389
|
+
reject(error);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
child.on('exit', (code) => {
|
|
393
|
+
resolve(code);
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
} else {
|
|
397
|
+
// 如果插件不支持交互式模式,先显示帮助,然后让用户输入参数
|
|
398
|
+
console.log(`Running: ${response.alias}...`);
|
|
399
|
+
console.log('─'.repeat(50));
|
|
400
|
+
console.log('');
|
|
401
|
+
|
|
402
|
+
// 先显示帮助信息
|
|
403
|
+
const helpChild = spawn('node', [plugin.binPath, '--help'], {
|
|
404
|
+
stdio: 'inherit'
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
await new Promise((resolve) => {
|
|
408
|
+
helpChild.on('exit', () => resolve());
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
console.log('');
|
|
412
|
+
console.log('─'.repeat(50));
|
|
413
|
+
|
|
414
|
+
// 询问用户是否要运行插件
|
|
415
|
+
const runResponse = await prompts({
|
|
416
|
+
type: 'confirm',
|
|
417
|
+
name: 'value',
|
|
418
|
+
message: t('interactive.runWithArgs'),
|
|
419
|
+
initial: true
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
if (!runResponse.value) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// 让用户输入参数
|
|
427
|
+
const argsResponse = await prompts({
|
|
428
|
+
type: 'text',
|
|
429
|
+
name: 'args',
|
|
430
|
+
message: t('interactive.enterArgs'),
|
|
431
|
+
initial: ''
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
if (argsResponse.args === undefined) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
console.log('');
|
|
439
|
+
console.log('─'.repeat(50));
|
|
440
|
+
|
|
441
|
+
// 解析参数并运行
|
|
442
|
+
const args = argsResponse.args.trim().split(/\s+/).filter(arg => arg.length > 0);
|
|
443
|
+
const child = spawn('node', [plugin.binPath, ...args], {
|
|
444
|
+
stdio: 'inherit'
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
await new Promise((resolve, reject) => {
|
|
448
|
+
child.on('error', (error) => {
|
|
449
|
+
console.error(t('failedToRun', {pluginAlias: response.alias}), error.message);
|
|
450
|
+
reject(error);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
child.on('exit', (code) => {
|
|
454
|
+
resolve(code);
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* 处理配置语言
|
|
462
|
+
*/
|
|
463
|
+
async function handleConfig() {
|
|
464
|
+
const currentLang = settings.getLanguage();
|
|
465
|
+
|
|
466
|
+
const response = await prompts({
|
|
467
|
+
type: 'select',
|
|
468
|
+
name: 'language',
|
|
469
|
+
message: t('interactive.selectLanguage'),
|
|
470
|
+
choices: [
|
|
471
|
+
{title: '中文 (Chinese)', value: 'zh', selected: currentLang === 'zh'},
|
|
472
|
+
{title: 'English', value: 'en', selected: currentLang === 'en'}
|
|
473
|
+
]
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
if (!response.language) {
|
|
477
|
+
throw new Error('cancelled');
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
settings.setLanguage(response.language);
|
|
481
|
+
console.log(t('languageSet'), response.language);
|
|
482
|
+
console.log('\n' + t('interactive.pressEnterToContinue').replace('\n', ''));
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* 等待用户按回车键
|
|
487
|
+
*/
|
|
488
|
+
async function waitForEnter() {
|
|
489
|
+
await prompts({
|
|
490
|
+
type: 'text',
|
|
491
|
+
name: 'continue',
|
|
492
|
+
message: t('interactive.pressEnterToContinue'),
|
|
493
|
+
initial: ''
|
|
494
|
+
});
|
|
495
|
+
console.log('');
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
module.exports = interactive;
|
package/lib/commands/list.js
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
const registry = require('../registry');
|
|
2
|
+
const {t} = require('../i18n');
|
|
2
3
|
|
|
3
4
|
function list() {
|
|
4
|
-
|
|
5
|
+
const plugins = registry.getAllPlugins();
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
if (Object.keys(plugins).length === 0) {
|
|
8
|
+
console.log(t('noPlugins'));
|
|
9
|
+
console.log(t('installExample'));
|
|
10
|
+
console.log(' slothtool install <plugin-name>');
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
console.log(t('installedPlugins') + '\n');
|
|
15
|
+
for (const [alias, info] of Object.entries(plugins)) {
|
|
16
|
+
console.log(` ${alias}`);
|
|
17
|
+
console.log(` Package: ${info.name}`);
|
|
18
|
+
console.log(` Version: ${info.version}`);
|
|
19
|
+
console.log(` Installed: ${new Date(info.installedAt).toLocaleString()}`);
|
|
20
|
+
console.log('');
|
|
21
|
+
}
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
module.exports = list;
|
package/lib/commands/run.js
CHANGED
|
@@ -1,41 +1,44 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {spawn} = require('child_process');
|
|
2
2
|
const registry = require('../registry');
|
|
3
|
+
const {t} = require('../i18n');
|
|
3
4
|
|
|
4
5
|
function run(args) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
6
|
+
if (args.length === 0) {
|
|
7
|
+
console.error(t('specifyPlugin'));
|
|
8
|
+
console.log(t('usage'));
|
|
9
|
+
console.log(' slothtool run <plugin-alias> [args...]');
|
|
10
|
+
console.log(' slothtool <plugin-alias> [args...]');
|
|
11
|
+
console.log('\n' + t('examples'));
|
|
12
|
+
console.log(' slothtool run loc ./src');
|
|
13
|
+
console.log(' slothtool loc ./src');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const pluginAlias = args[0];
|
|
18
|
+
const pluginArgs = args.slice(1);
|
|
19
|
+
|
|
20
|
+
const plugin = registry.getPlugin(pluginAlias);
|
|
21
|
+
|
|
22
|
+
if (!plugin) {
|
|
23
|
+
console.error(t('pluginNotFound', {pluginAlias}));
|
|
24
|
+
console.log(t('seeInstalled'));
|
|
25
|
+
console.log(t('orInstall'));
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 调用插件
|
|
30
|
+
const child = spawn('node', [plugin.binPath, ...pluginArgs], {
|
|
31
|
+
stdio: 'inherit'
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
child.on('error', (error) => {
|
|
35
|
+
console.error(t('failedToRun', {pluginAlias}), error.message);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
child.on('exit', (code) => {
|
|
40
|
+
process.exit(code || 0);
|
|
41
|
+
});
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
module.exports = run;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const readline = require('readline');
|
|
5
|
+
const registry = require('../registry');
|
|
6
|
+
const {t} = require('../i18n');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 完全卸载 SlothTool,删除所有数据
|
|
10
|
+
*/
|
|
11
|
+
async function uninstallAll() {
|
|
12
|
+
const slothtoolDir = path.join(os.homedir(), '.slothtool');
|
|
13
|
+
|
|
14
|
+
// 检查目录是否存在
|
|
15
|
+
if (!fs.existsSync(slothtoolDir)) {
|
|
16
|
+
console.log(t('uninstallAll.noData', {dir: slothtoolDir}));
|
|
17
|
+
console.log(t('uninstallAll.alreadyClean'));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 显示标题和警告
|
|
22
|
+
console.log(t('uninstallAll.title'));
|
|
23
|
+
console.log(t('uninstallAll.warning'));
|
|
24
|
+
|
|
25
|
+
// 获取已安装插件数量
|
|
26
|
+
const plugins = registry.getAllPlugins();
|
|
27
|
+
const pluginCount = Object.keys(plugins).length;
|
|
28
|
+
|
|
29
|
+
// 显示将要删除的内容
|
|
30
|
+
console.log(t('uninstallAll.willRemove'));
|
|
31
|
+
console.log(t('uninstallAll.slothtoolDir', {dir: slothtoolDir}));
|
|
32
|
+
console.log(t('uninstallAll.allPlugins', {count: pluginCount}));
|
|
33
|
+
console.log(t('uninstallAll.allConfigs'));
|
|
34
|
+
console.log(t('uninstallAll.registry'));
|
|
35
|
+
console.log(t('uninstallAll.settings'));
|
|
36
|
+
|
|
37
|
+
// 确认删除
|
|
38
|
+
console.log(t('uninstallAll.confirm'));
|
|
39
|
+
const confirmed = await promptConfirmation(t('uninstallAll.confirmPrompt'));
|
|
40
|
+
|
|
41
|
+
if (!confirmed) {
|
|
42
|
+
console.log(t('uninstallAll.cancelled'));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 执行删除
|
|
47
|
+
try {
|
|
48
|
+
console.log(t('uninstallAll.removing'));
|
|
49
|
+
fs.rmSync(slothtoolDir, {recursive: true, force: true});
|
|
50
|
+
console.log(t('uninstallAll.success'));
|
|
51
|
+
console.log(t('uninstallAll.nextStep'));
|
|
52
|
+
console.log(t('uninstallAll.npmUninstall'));
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error(t('uninstallAll.failed'), error.message);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 提示用户确认
|
|
61
|
+
* @param {string} prompt - 提示信息
|
|
62
|
+
* @returns {Promise<boolean>} 用户是否确认
|
|
63
|
+
*/
|
|
64
|
+
function promptConfirmation(prompt) {
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
const rl = readline.createInterface({
|
|
67
|
+
input: process.stdin,
|
|
68
|
+
output: process.stdout
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
rl.question(prompt, (answer) => {
|
|
72
|
+
rl.close();
|
|
73
|
+
resolve(answer.trim().toLowerCase() === 'yes');
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = uninstallAll;
|