@icyfenix-dmla/cli 2026.5.24-1015 → 2026.5.24-2045

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icyfenix-dmla/cli",
3
- "version": "2026.5.24-1015",
3
+ "version": "2026.5.24-2045",
4
4
  "description": "DMLA 沙箱服务命令行工具",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -8,8 +8,7 @@ const { prompt } = pkg
8
8
  import fs from 'fs'
9
9
  import path from 'path'
10
10
  import os from 'os'
11
- import { spawn } from 'child_process'
12
- import { execSync } from 'child_process'
11
+ import { spawn, execSync } from '../verbose.js'
13
12
  import AdmZip from 'adm-zip'
14
13
 
15
14
  // 配置文件路径
@@ -310,10 +309,11 @@ async function showMainMenu(dataPath) {
310
309
  const choices = [
311
310
  { name: '1', message: '挂载路径设置 ' + chalk.gray(`[当前: ${dataPath}]`) },
312
311
  { name: '2', message: '下载数据集' },
313
- { name: '3', message: '查看数据集列表' },
314
- { name: '4', message: '清空数据内容' },
315
- { name: '5', message: '删除数据卷' },
316
- { name: '6', message: '退出' }
312
+ { name: '3', message: '删除数据集' },
313
+ { name: '4', message: '查看数据集列表' },
314
+ { name: '5', message: '清空数据内容' },
315
+ { name: '6', message: '删除数据卷' },
316
+ { name: '7', message: '退出' }
317
317
  ]
318
318
 
319
319
  const { action } = await prompt({
@@ -461,6 +461,117 @@ async function removeData() {
461
461
  console.log(chalk.green('数据卷已删除'))
462
462
  }
463
463
 
464
+ /**
465
+ * 删除数据集子菜单
466
+ */
467
+ async function deleteDatasets() {
468
+ const dataPath = getDataVolumePath()
469
+
470
+ console.log()
471
+ console.log(chalk.bold('删除数据集'))
472
+ console.log()
473
+
474
+ // 收集已下载(含不完整)的数据集
475
+ const existingDatasets = DATASETS.filter(d => isDatasetExists(dataPath, d.id))
476
+
477
+ if (existingDatasets.length === 0) {
478
+ console.log(chalk.yellow('没有已下载的数据集'))
479
+ return
480
+ }
481
+
482
+ // 构建选项列表
483
+ const choices = existingDatasets.map((dataset) => {
484
+ const downloaded = isDatasetDownloaded(dataPath, dataset.id)
485
+ const incomplete = isDatasetIncomplete(dataPath, dataset.id)
486
+
487
+ let message = `${dataset.name} (${dataset.size})`
488
+ if (downloaded) {
489
+ message += ' [可用]'
490
+ } else if (incomplete) {
491
+ message += ' [不完整]'
492
+ } else {
493
+ message += ' [存在]'
494
+ }
495
+
496
+ return {
497
+ name: dataset.id,
498
+ message
499
+ }
500
+ })
501
+
502
+ console.log(chalk.gray('操作: 上下键移动,空格勾选/取消,回车确认,ESC 返回'))
503
+ console.log()
504
+
505
+ try {
506
+ const { selected } = await prompt({
507
+ type: 'multiselect',
508
+ name: 'selected',
509
+ message: '选择要删除的数据集',
510
+ choices,
511
+ hint: '空格选择,回车确认删除',
512
+ styles: {
513
+ primary: chalk.cyan.bold
514
+ }
515
+ })
516
+
517
+ if (!selected || selected.length === 0) {
518
+ console.log(chalk.yellow('未选择任何数据集'))
519
+ return
520
+ }
521
+
522
+ // 确认删除
523
+ const selectedNames = selected.map(id => {
524
+ const ds = existingDatasets.find(d => d.id === id)
525
+ return ds.name
526
+ })
527
+
528
+ console.log()
529
+ console.log(chalk.red(`将删除以下数据集: ${selectedNames.join(', ')}`))
530
+
531
+ const { confirm } = await prompt({
532
+ type: 'confirm',
533
+ name: 'confirm',
534
+ message: '确认删除?',
535
+ initial: false
536
+ })
537
+
538
+ if (!confirm) {
539
+ console.log(chalk.yellow('操作已取消'))
540
+ return
541
+ }
542
+
543
+ // 执行删除
544
+ for (const datasetId of selected) {
545
+ const dataset = DATASETS.find(d => d.id === datasetId)
546
+ const targetDir = path.join(dataPath, dataset.targetDir)
547
+
548
+ if (fs.existsSync(targetDir)) {
549
+ fs.rmSync(targetDir, { recursive: true, force: true })
550
+ console.log(chalk.green(`已删除: ${dataset.name}`))
551
+ }
552
+
553
+ // 更新配置
554
+ const config = readConfig()
555
+ if (config.installedDatasets) {
556
+ config.installedDatasets = config.installedDatasets.filter(id => id !== datasetId)
557
+ }
558
+ if (config.incompleteDatasets) {
559
+ config.incompleteDatasets = config.incompleteDatasets.filter(id => id !== datasetId)
560
+ }
561
+ writeConfig(config)
562
+ }
563
+
564
+ console.log()
565
+ console.log(chalk.green('删除完成'))
566
+ } catch (error) {
567
+ if (isUserCancel(error)) {
568
+ console.log(chalk.gray('返回上一级'))
569
+ return
570
+ }
571
+ throw error
572
+ }
573
+ }
574
+
464
575
  /**
465
576
  * 查看数据集列表
466
577
  */
@@ -1002,9 +1113,20 @@ export async function runDataTUI() {
1002
1113
  }
1003
1114
  break
1004
1115
  case 3:
1005
- listDatasets()
1116
+ try {
1117
+ await deleteDatasets()
1118
+ } catch (error) {
1119
+ if (isUserCancel(error)) {
1120
+ console.log(chalk.gray('返回主菜单'))
1121
+ } else {
1122
+ throw error
1123
+ }
1124
+ }
1006
1125
  break
1007
1126
  case 4:
1127
+ listDatasets()
1128
+ break
1129
+ case 5:
1008
1130
  try {
1009
1131
  await clearData()
1010
1132
  } catch (error) {
@@ -1015,7 +1137,7 @@ export async function runDataTUI() {
1015
1137
  }
1016
1138
  }
1017
1139
  break
1018
- case 5:
1140
+ case 6:
1019
1141
  try {
1020
1142
  await removeData()
1021
1143
  } catch (error) {
@@ -1026,7 +1148,7 @@ export async function runDataTUI() {
1026
1148
  }
1027
1149
  }
1028
1150
  break
1029
- case 6:
1151
+ case 7:
1030
1152
  console.log()
1031
1153
  console.log(chalk.gray('已退出数据管理'))
1032
1154
  console.log()
@@ -1077,6 +1199,9 @@ export async function runDataCommand(subCommand, options) {
1077
1199
  case 'download':
1078
1200
  await downloadDatasets()
1079
1201
  break
1202
+ case 'delete':
1203
+ await deleteDatasets()
1204
+ break
1080
1205
  default:
1081
1206
  // 无子命令时进入 TUI
1082
1207
  await runDataTUI()
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import chalk from 'chalk'
5
5
  import Docker from 'dockerode'
6
- import { spawn, execSync } from 'child_process'
6
+ import { spawn, execSync } from '../verbose.js'
7
7
  import http from 'http'
8
8
  import path from 'path'
9
9
  import { fileURLToPath } from 'url'
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import chalk from 'chalk'
5
5
  import Docker from 'dockerode'
6
- import { spawn } from 'child_process'
6
+ import { spawn, execSync } from '../verbose.js'
7
7
  import http from 'http'
8
8
  import path from 'path'
9
9
  import { fileURLToPath, pathToFileURL } from 'url'
@@ -3,7 +3,7 @@
3
3
  * 通过 npm 更新程序
4
4
  */
5
5
  import chalk from 'chalk'
6
- import { execSync } from 'child_process'
6
+ import { execSync } from '../verbose.js'
7
7
 
8
8
  /**
9
9
  * 运行 update 命令
package/src/index.js CHANGED
@@ -12,6 +12,7 @@ import { runDoctor } from './commands/manage.js'
12
12
  import { runDataTUI, runDataCommand } from './commands/data.js'
13
13
  import { runImagesTUI } from './commands/images.js'
14
14
  import { runUpdate } from './commands/update.js'
15
+ import { setVerbose } from './verbose.js'
15
16
 
16
17
  // 从 package.json 读取版本号
17
18
  const __filename = fileURLToPath(import.meta.url)
@@ -85,6 +86,13 @@ program
85
86
  .version(VERSION, '-v, --version', '显示版本号')
86
87
  .helpOption('-h, --help', '显示帮助信息')
87
88
  .addHelpCommand('help [command]', '显示命令帮助信息')
89
+ .option('--verbose', '显示所有执行的外部命令,便于调试')
90
+
91
+ // 解析全局选项(需要在子命令 action 之前解析,以便设置 verbose 开关)
92
+ program.parseOptions(process.argv)
93
+ if (program.opts().verbose) {
94
+ setVerbose(true)
95
+ }
88
96
 
89
97
  // ─────────────────────────────────────────────────────────────
90
98
  // start 命令
package/src/verbose.js ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * 命令执行封装
3
+ * --verbose 模式下打印所有执行的外部命令,方便调试
4
+ */
5
+ import { execSync as nodeExecSync, spawn as nodeSpawn } from 'child_process'
6
+
7
+ // 全局 verbose 开关
8
+ let verboseEnabled = false
9
+
10
+ /**
11
+ * 启用/禁用 verbose 模式
12
+ */
13
+ export function setVerbose(enabled) {
14
+ verboseEnabled = !!enabled
15
+ }
16
+
17
+ /**
18
+ * 查询 verbose 模式状态
19
+ */
20
+ export function isVerbose() {
21
+ return verboseEnabled
22
+ }
23
+
24
+ /**
25
+ * 打印 verbose 日志(仅在 verbose 模式下输出)
26
+ */
27
+ function verboseLog(cmd, args) {
28
+ if (!verboseEnabled) return
29
+ const fullCmd = args && args.length > 0
30
+ ? `${cmd} ${args.map(a => `'${a}'`).join(' ')}`
31
+ : cmd
32
+ console.log(`[verbose] $ ${fullCmd}`)
33
+ }
34
+
35
+ /**
36
+ * 封装 execSync,verbose 模式下打印命令
37
+ * 参数与原生 execSync 完全一致
38
+ */
39
+ export function execSync(command, options) {
40
+ verboseLog(command)
41
+ return nodeExecSync(command, options)
42
+ }
43
+
44
+ /**
45
+ * 封装 spawn,verbose 模式下打印命令
46
+ * 参数与原生 spawn 完全一致
47
+ */
48
+ export function spawn(command, args, options) {
49
+ verboseLog(command, args)
50
+ return nodeSpawn(command, args, options)
51
+ }
package/version.json CHANGED
@@ -1,4 +1,4 @@
1
1
  {
2
- "buildTime": "2026-05-24T02:17:04.071Z",
3
- "cliVersion": "2026.5.24-1015"
2
+ "buildTime": "2026-05-24T12:46:48.154Z",
3
+ "cliVersion": "2026.5.24-2045"
4
4
  }