yuangs 1.3.38 → 1.3.39
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/cli.js +39 -2
- package/package.json +1 -1
- package/test_interactive_history.js +66 -0
- package/test_interactive_history_multi.js +67 -0
package/cli.js
CHANGED
|
@@ -432,14 +432,51 @@ switch (command) {
|
|
|
432
432
|
if (history.length === 0) {
|
|
433
433
|
console.log(chalk.gray('暂无命令历史\n'));
|
|
434
434
|
} else {
|
|
435
|
-
console.log(chalk.bold.cyan('\n📋
|
|
435
|
+
console.log(chalk.bold.cyan('\n📋 命令历史 (输入序号可重新执行)\n'));
|
|
436
436
|
console.log(chalk.gray('─────────────────────────────────────────────────'));
|
|
437
437
|
history.forEach((item, index) => {
|
|
438
438
|
console.log(chalk.white(`${index + 1}. ${item.command}`));
|
|
439
439
|
console.log(chalk.gray(` 问题: ${item.question}`));
|
|
440
440
|
console.log(chalk.gray(` 时间: ${item.time}\n`));
|
|
441
441
|
});
|
|
442
|
-
console.log(chalk.gray('
|
|
442
|
+
console.log(chalk.gray('─────────────────────────────────────────────────'));
|
|
443
|
+
|
|
444
|
+
const readline = require('readline');
|
|
445
|
+
const rlHistory = readline.createInterface({
|
|
446
|
+
input: process.stdin,
|
|
447
|
+
output: process.stdout
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
rlHistory.question(chalk.green('👇 输入序号 (1-' + history.length + ') 或 q 退出: '), (answer) => {
|
|
451
|
+
const index = parseInt(answer) - 1;
|
|
452
|
+
if (!isNaN(index) && index >= 0 && index < history.length) {
|
|
453
|
+
const targetCommand = history[index].command;
|
|
454
|
+
|
|
455
|
+
// 核心逻辑:把命令“写”进输入流,假装是用户刚打进去的
|
|
456
|
+
console.log(chalk.cyan(`\n✨ 已加载命令,按回车执行:`));
|
|
457
|
+
rlHistory.write(targetCommand);
|
|
458
|
+
|
|
459
|
+
rlHistory.on('line', (input) => {
|
|
460
|
+
const finalCommand = input.trim();
|
|
461
|
+
rlHistory.close();
|
|
462
|
+
if (finalCommand) {
|
|
463
|
+
const { spawn } = require('child_process');
|
|
464
|
+
console.log(chalk.gray('正在执行...'));
|
|
465
|
+
const child = spawn(finalCommand, [], { shell: true, stdio: 'inherit' });
|
|
466
|
+
child.on('close', (code) => {
|
|
467
|
+
process.exit(code);
|
|
468
|
+
});
|
|
469
|
+
} else {
|
|
470
|
+
process.exit(0);
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
} else {
|
|
474
|
+
rlHistory.close();
|
|
475
|
+
if (answer.toLowerCase() !== 'q') {
|
|
476
|
+
// 如果输错了或者是q,就退出,不报错
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
});
|
|
443
480
|
}
|
|
444
481
|
break;
|
|
445
482
|
case 'save':
|
package/package.json
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const { spawn } = require('child_process');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
|
|
6
|
+
// Ensure there is some history to test with
|
|
7
|
+
const historyFile = path.join(os.homedir(), '.yuangs_cmd_history.json');
|
|
8
|
+
const dummyHistory = [
|
|
9
|
+
{ question: "Test Question", command: "echo 'History Test Success'", time: "Now" }
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
// Backup existing history if any
|
|
13
|
+
let backupHistory = null;
|
|
14
|
+
if (fs.existsSync(historyFile)) {
|
|
15
|
+
backupHistory = fs.readFileSync(historyFile);
|
|
16
|
+
}
|
|
17
|
+
fs.writeFileSync(historyFile, JSON.stringify(dummyHistory));
|
|
18
|
+
|
|
19
|
+
const cliPath = path.join(__dirname, 'cli.js');
|
|
20
|
+
const child = spawn('node', [cliPath, 'history'], { stdio: 'pipe' });
|
|
21
|
+
|
|
22
|
+
let output = '';
|
|
23
|
+
let step = 0;
|
|
24
|
+
|
|
25
|
+
child.stdout.on('data', (data) => {
|
|
26
|
+
const str = data.toString();
|
|
27
|
+
output += str;
|
|
28
|
+
console.log(`[CLI Output] ${str}`);
|
|
29
|
+
|
|
30
|
+
// Step 1: Wait for prompt and send index '1'
|
|
31
|
+
if (step === 0 && str.includes('输入序号')) {
|
|
32
|
+
console.log('[Test] Sending "1"...');
|
|
33
|
+
child.stdin.write('1\n');
|
|
34
|
+
step++;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Step 2: Wait for pre-fill confirmation (readline write)
|
|
38
|
+
// Note: Since we are in a non-TTY pipe, readline.write might behave differently or just output the text.
|
|
39
|
+
// In our code: rlHistory.write(targetCommand) -> outputs to stdout.
|
|
40
|
+
if (step === 1 && str.includes('echo \'History Test Success\'')) {
|
|
41
|
+
console.log('[Test] Command pre-filled. Sending Enter to execute...');
|
|
42
|
+
child.stdin.write('\n');
|
|
43
|
+
step++;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
child.stderr.on('data', (data) => {
|
|
48
|
+
console.error(`[CLI Error] ${data}`);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
child.on('close', (code) => {
|
|
52
|
+
console.log(`[Test] Process exited with code ${code}`);
|
|
53
|
+
|
|
54
|
+
// Restore history
|
|
55
|
+
if (backupHistory) {
|
|
56
|
+
fs.writeFileSync(historyFile, backupHistory);
|
|
57
|
+
} else {
|
|
58
|
+
fs.unlinkSync(historyFile);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (output.includes('History Test Success')) {
|
|
62
|
+
console.log('✅ Interactive History Test PASSED');
|
|
63
|
+
} else {
|
|
64
|
+
console.log('❌ Interactive History Test FAILED');
|
|
65
|
+
}
|
|
66
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
const { spawn } = require('child_process');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
|
|
6
|
+
// Ensure there is some history to test with
|
|
7
|
+
const historyFile = path.join(os.homedir(), '.yuangs_cmd_history.json');
|
|
8
|
+
const dummyHistory = [
|
|
9
|
+
{ question: "First command", command: "echo 'I am command 1'", time: "Now" },
|
|
10
|
+
{ question: "Second command", command: "echo 'I am command 2'", time: "Now" },
|
|
11
|
+
{ question: "Third command", command: "echo 'I am command 3'", time: "Now" }
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
// Backup existing history if any
|
|
15
|
+
let backupHistory = null;
|
|
16
|
+
if (fs.existsSync(historyFile)) {
|
|
17
|
+
backupHistory = fs.readFileSync(historyFile);
|
|
18
|
+
}
|
|
19
|
+
fs.writeFileSync(historyFile, JSON.stringify(dummyHistory));
|
|
20
|
+
|
|
21
|
+
const cliPath = path.join(__dirname, 'cli.js');
|
|
22
|
+
// Run: yuangs history
|
|
23
|
+
const child = spawn('node', [cliPath, 'history'], { stdio: 'pipe' });
|
|
24
|
+
|
|
25
|
+
let output = '';
|
|
26
|
+
let step = 0;
|
|
27
|
+
|
|
28
|
+
child.stdout.on('data', (data) => {
|
|
29
|
+
const str = data.toString();
|
|
30
|
+
output += str;
|
|
31
|
+
console.log(`[CLI Output] ${str}`);
|
|
32
|
+
|
|
33
|
+
// Step 1: Wait for prompt and send index '2' (Select standard middleware item)
|
|
34
|
+
if (step === 0 && str.includes('输入序号')) {
|
|
35
|
+
console.log('[Test] Sending "2"... (Selecting second item)');
|
|
36
|
+
child.stdin.write('2\n');
|
|
37
|
+
step++;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Step 2: Wait for pre-fill confirmation
|
|
41
|
+
if (step === 1 && str.includes('echo \'I am command 2\'')) {
|
|
42
|
+
console.log('[Test] Command 2 pre-filled. Sending Enter to execute...');
|
|
43
|
+
child.stdin.write('\n');
|
|
44
|
+
step++;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
child.stderr.on('data', (data) => {
|
|
49
|
+
console.error(`[CLI Error] ${data}`);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
child.on('close', (code) => {
|
|
53
|
+
console.log(`[Test] Process exited with code ${code}`);
|
|
54
|
+
|
|
55
|
+
// Restore history
|
|
56
|
+
if (backupHistory) {
|
|
57
|
+
fs.writeFileSync(historyFile, backupHistory);
|
|
58
|
+
} else {
|
|
59
|
+
fs.unlinkSync(historyFile);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (output.includes('I am command 2')) {
|
|
63
|
+
console.log('✅ Interactive Multi-Item History Test PASSED');
|
|
64
|
+
} else {
|
|
65
|
+
console.log('❌ Interactive Multi-Item History Test FAILED');
|
|
66
|
+
}
|
|
67
|
+
});
|