quick-sh 1.0.0
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/LICENSE +21 -0
- package/README.md +226 -0
- package/bin/cli.js +49 -0
- package/lib/config.js +71 -0
- package/lib/executor.js +98 -0
- package/lib/help.js +62 -0
- package/lib/script-manager.js +153 -0
- package/lib/utils.js +18 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 YoungChou
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# Quick Shell
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/@youngchou%2Fquick-sh)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
A local script management tool for quick execution of JavaScript and Shell scripts with advanced alias support.
|
|
7
|
+
|
|
8
|
+
[中文文档](./README-zh.md) | English
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- 🚀 **Quick Execution**: Run local scripts with simple `q script-name` command
|
|
13
|
+
- 📁 **Multiple Formats**: Support JavaScript (.js), Shell scripts (.sh), and directories
|
|
14
|
+
- 🔗 **Advanced Aliases**: Configure custom aliases with relative paths, absolute paths, and system commands
|
|
15
|
+
- 📝 **Parameter Forwarding**: Pass arguments transparently to your scripts
|
|
16
|
+
- 🎯 **Smart Priority**: Alias config > Script files > System commands
|
|
17
|
+
- ⚡ **Global Access**: Install once, use anywhere
|
|
18
|
+
- 🧪 **Comprehensive Testing**: 16 automated test cases ensure reliability
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g @youngchou/quick-sh
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
After installation, you can use the `q` command globally.
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### 1. Set Script Directory
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Set your script directory
|
|
34
|
+
q -path /path/to/your/scripts
|
|
35
|
+
|
|
36
|
+
# Check current status
|
|
37
|
+
q -list
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. Run Scripts
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Run a JavaScript file
|
|
44
|
+
q myscript
|
|
45
|
+
|
|
46
|
+
# Run a shell script
|
|
47
|
+
q deploy.sh
|
|
48
|
+
|
|
49
|
+
# Run with parameters
|
|
50
|
+
q backup --target /data --compress
|
|
51
|
+
|
|
52
|
+
# Execute directory (looks for index.js or index.sh)
|
|
53
|
+
q myproject
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 3. Configure Aliases
|
|
57
|
+
|
|
58
|
+
Create or edit `config.json` in your script directory:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"aliases": {
|
|
63
|
+
"deploy": {
|
|
64
|
+
"bin": "./deploy/index.js"
|
|
65
|
+
},
|
|
66
|
+
"backup": {
|
|
67
|
+
"bin": "/usr/local/bin/backup.sh"
|
|
68
|
+
},
|
|
69
|
+
"start": {
|
|
70
|
+
"bin": "npm start"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Commands
|
|
77
|
+
|
|
78
|
+
| Command | Description | Example |
|
|
79
|
+
|---------|-------------|---------|
|
|
80
|
+
| `q -path <dir>` | Set script directory | `q -path ~/scripts` |
|
|
81
|
+
| `q -list` | Show current status and available scripts | `q -list` |
|
|
82
|
+
| `q -help` | Display help information | `q -help` |
|
|
83
|
+
| `q <script>` | Execute script or alias | `q deploy` |
|
|
84
|
+
|
|
85
|
+
## Alias Configuration
|
|
86
|
+
|
|
87
|
+
### Supported Alias Types
|
|
88
|
+
|
|
89
|
+
1. **Relative Path**: Relative to script directory
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"aliases": {
|
|
93
|
+
"deploy": {"bin": "./deploy/index.js"}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
2. **Absolute Path**: Full system path
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"aliases": {
|
|
102
|
+
"backup": {"bin": "/usr/local/bin/backup.sh"}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
3. **System Command**: Execute system commands
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"aliases": {
|
|
111
|
+
"list": {"bin": "ls -la"},
|
|
112
|
+
"findnode": {"bin": "which node"}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Execution Priority
|
|
118
|
+
|
|
119
|
+
1. **Alias Configuration** (highest priority)
|
|
120
|
+
2. **Script Files** (medium priority)
|
|
121
|
+
3. **System Commands** (lowest priority)
|
|
122
|
+
|
|
123
|
+
## Parameter Forwarding
|
|
124
|
+
|
|
125
|
+
All parameters are transparently forwarded to target scripts:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# These parameters will be passed to the script
|
|
129
|
+
q deploy --env production --verbose
|
|
130
|
+
q backup /data/important --compress
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
In your script, access parameters normally:
|
|
134
|
+
- **JavaScript**: `process.argv`
|
|
135
|
+
- **Shell**: `$1`, `$2`, etc.
|
|
136
|
+
|
|
137
|
+
## Examples
|
|
138
|
+
|
|
139
|
+
### Directory Structure
|
|
140
|
+
```
|
|
141
|
+
~/scripts/
|
|
142
|
+
├── config.json
|
|
143
|
+
├── deploy.js
|
|
144
|
+
├── backup.sh
|
|
145
|
+
└── utils/
|
|
146
|
+
└── index.js
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### config.json
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"aliases": {
|
|
153
|
+
"deploy": {"bin": "./deploy.js"},
|
|
154
|
+
"util": {"bin": "./utils"},
|
|
155
|
+
"backup": {"bin": "./backup.sh"},
|
|
156
|
+
"start": {"bin": "npm start"}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Usage
|
|
162
|
+
```bash
|
|
163
|
+
# Execute via alias
|
|
164
|
+
q deploy --env prod
|
|
165
|
+
|
|
166
|
+
# Execute shell script
|
|
167
|
+
q backup.sh /data
|
|
168
|
+
|
|
169
|
+
# Execute directory (looks for utils/index.js)
|
|
170
|
+
q utils --help
|
|
171
|
+
|
|
172
|
+
# System command via alias
|
|
173
|
+
q start
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Development
|
|
177
|
+
|
|
178
|
+
### Testing
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# Run all tests
|
|
182
|
+
npm test
|
|
183
|
+
|
|
184
|
+
# Manual testing with example scripts
|
|
185
|
+
npm run test:manual
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Project Structure
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
quick-sh/
|
|
192
|
+
├── bin/ # CLI entry point
|
|
193
|
+
├── lib/ # Core modules
|
|
194
|
+
│ ├── config.js # Configuration management
|
|
195
|
+
│ ├── executor.js # Script execution
|
|
196
|
+
│ ├── help.js # Help text
|
|
197
|
+
│ ├── script-manager.js # Main logic
|
|
198
|
+
│ └── utils.js # Utilities
|
|
199
|
+
├── test/ # Test suite
|
|
200
|
+
└── examples/ # Example scripts
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Requirements
|
|
204
|
+
|
|
205
|
+
- Node.js >= 14.0.0
|
|
206
|
+
- npm or yarn
|
|
207
|
+
|
|
208
|
+
## Contributing
|
|
209
|
+
|
|
210
|
+
1. Fork the repository
|
|
211
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
212
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
213
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
214
|
+
5. Open a Pull Request
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
219
|
+
|
|
220
|
+
## Author
|
|
221
|
+
|
|
222
|
+
- **YoungChou** - *Initial work*
|
|
223
|
+
|
|
224
|
+
## Support
|
|
225
|
+
|
|
226
|
+
If you encounter any issues or have questions, please [open an issue](https://github.com/YoungChou/quick-sh/issues) on GitHub.
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { program } = require('commander');
|
|
4
|
+
const { setPath } = require('../lib/config');
|
|
5
|
+
const { executeScript, showStatus } = require('../lib/script-manager');
|
|
6
|
+
const { showHelp } = require('../lib/help');
|
|
7
|
+
|
|
8
|
+
// 自定义命令处理 - 在 commander.js 解析之前拦截
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
|
|
11
|
+
if (args.length > 0) {
|
|
12
|
+
const firstArg = args[0];
|
|
13
|
+
|
|
14
|
+
// 处理内置命令
|
|
15
|
+
if (firstArg === '-path' && args.length >= 2) {
|
|
16
|
+
setPath(args[1]);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (firstArg === '-list' || firstArg === '-l') {
|
|
21
|
+
showStatus();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (firstArg === '-help' || firstArg === '-h') {
|
|
26
|
+
showHelp();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 设置命令行程序
|
|
32
|
+
program
|
|
33
|
+
.name('q')
|
|
34
|
+
.description('Quick Shell - Local script management tool')
|
|
35
|
+
.version('1.0.0');
|
|
36
|
+
|
|
37
|
+
// 默认命令:执行脚本
|
|
38
|
+
program
|
|
39
|
+
.argument('[script]', 'Script name to execute')
|
|
40
|
+
.argument('[args...]', 'Arguments to pass to the script')
|
|
41
|
+
.action(async (script, args) => {
|
|
42
|
+
if (!script) {
|
|
43
|
+
await showStatus();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
await executeScript(script, args || []);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
program.parse();
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
|
|
5
|
+
// 配置文件路径
|
|
6
|
+
const CONFIG_DIR = path.join(os.homedir(), '.quick-sh');
|
|
7
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
8
|
+
|
|
9
|
+
// 确保配置目录存在
|
|
10
|
+
async function ensureConfigDir() {
|
|
11
|
+
await fs.ensureDir(CONFIG_DIR);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// 读取全局配置
|
|
15
|
+
async function readConfig() {
|
|
16
|
+
try {
|
|
17
|
+
await ensureConfigDir();
|
|
18
|
+
if (await fs.pathExists(CONFIG_FILE)) {
|
|
19
|
+
return await fs.readJson(CONFIG_FILE);
|
|
20
|
+
}
|
|
21
|
+
return {};
|
|
22
|
+
} catch (error) {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 写入全局配置
|
|
28
|
+
async function writeConfig(config) {
|
|
29
|
+
try {
|
|
30
|
+
await ensureConfigDir();
|
|
31
|
+
await fs.writeJson(CONFIG_FILE, config, { spaces: 2 });
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error('Error writing config:', error.message);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 读取脚本目录中的alias配置
|
|
38
|
+
async function readAliasConfig(scriptPath) {
|
|
39
|
+
try {
|
|
40
|
+
const aliasConfigPath = path.join(scriptPath, 'config.json');
|
|
41
|
+
if (await fs.pathExists(aliasConfigPath)) {
|
|
42
|
+
return await fs.readJson(aliasConfigPath);
|
|
43
|
+
}
|
|
44
|
+
return {};
|
|
45
|
+
} catch (error) {
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 设置脚本路径
|
|
51
|
+
async function setPath(scriptPath) {
|
|
52
|
+
const absolutePath = path.resolve(scriptPath);
|
|
53
|
+
|
|
54
|
+
if (!await fs.pathExists(absolutePath)) {
|
|
55
|
+
console.error(`Path does not exist: ${absolutePath}`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const config = await readConfig();
|
|
60
|
+
config.scriptPath = absolutePath;
|
|
61
|
+
await writeConfig(config);
|
|
62
|
+
|
|
63
|
+
console.log(`Script path set to: ${absolutePath}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = {
|
|
67
|
+
readConfig,
|
|
68
|
+
writeConfig,
|
|
69
|
+
readAliasConfig,
|
|
70
|
+
setPath
|
|
71
|
+
};
|
package/lib/executor.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
|
|
5
|
+
// 执行文件
|
|
6
|
+
async function executeFile(filePath, args = []) {
|
|
7
|
+
const ext = path.extname(filePath);
|
|
8
|
+
const fileName = path.basename(filePath);
|
|
9
|
+
|
|
10
|
+
console.log(`Executing: ${fileName}${args.length > 0 ? ' with args: ' + args.join(' ') : ''}`);
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
if (ext === '.js' || ext === '.mjs') {
|
|
14
|
+
// 使用spawn来保持交互性
|
|
15
|
+
const child = spawn('node', [filePath, ...args], {
|
|
16
|
+
stdio: 'inherit',
|
|
17
|
+
cwd: path.dirname(filePath)
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
child.on('close', (code) => {
|
|
21
|
+
process.exit(code);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
} else if (ext === '.sh') {
|
|
25
|
+
// 确保shell脚本有执行权限
|
|
26
|
+
try {
|
|
27
|
+
await fs.chmod(filePath, 0o755);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
// 忽略权限设置错误
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const child = spawn('sh', [filePath, ...args], {
|
|
33
|
+
stdio: 'inherit',
|
|
34
|
+
cwd: path.dirname(filePath)
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
child.on('close', (code) => {
|
|
38
|
+
process.exit(code);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
} else {
|
|
42
|
+
console.error(`Unsupported file type: ${ext}`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error(`Error executing script: ${error.message}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 从目录执行脚本
|
|
52
|
+
async function executeFromDirectory(dirPath, args = []) {
|
|
53
|
+
const possibleFiles = [
|
|
54
|
+
path.join(dirPath, 'index.js'),
|
|
55
|
+
path.join(dirPath, 'index.sh'),
|
|
56
|
+
path.join(dirPath, 'index.mjs')
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
let targetFile = null;
|
|
60
|
+
for (const file of possibleFiles) {
|
|
61
|
+
if (await fs.pathExists(file)) {
|
|
62
|
+
targetFile = file;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!targetFile) {
|
|
68
|
+
console.error(`No index.js, index.sh, or index.mjs found in directory: ${dirPath}`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
await executeFile(targetFile, args);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 执行系统命令
|
|
76
|
+
async function executeSystemCommand(command, args = []) {
|
|
77
|
+
console.log(`Executing system command: ${command}${args.length > 0 ? ' with args: ' + args.join(' ') : ''}`);
|
|
78
|
+
|
|
79
|
+
const child = spawn(command, args, {
|
|
80
|
+
stdio: 'inherit',
|
|
81
|
+
cwd: process.cwd()
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
child.on('close', (code) => {
|
|
85
|
+
process.exit(code);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
child.on('error', (error) => {
|
|
89
|
+
console.error(`Error executing system command: ${error.message}`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = {
|
|
95
|
+
executeFile,
|
|
96
|
+
executeFromDirectory,
|
|
97
|
+
executeSystemCommand
|
|
98
|
+
};
|
package/lib/help.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// 显示帮助信息
|
|
2
|
+
function showHelp() {
|
|
3
|
+
console.log(`
|
|
4
|
+
🚀 Quick Shell - Local Script Management Tool
|
|
5
|
+
|
|
6
|
+
DESCRIPTION:
|
|
7
|
+
Quick Shell (q) is a command-line tool that helps you manage and execute
|
|
8
|
+
local scripts quickly. It supports JavaScript (.js, .mjs), Shell (.sh)
|
|
9
|
+
scripts, and directory-based scripts.
|
|
10
|
+
|
|
11
|
+
USAGE:
|
|
12
|
+
q [command] [options]
|
|
13
|
+
q <script> [args...]
|
|
14
|
+
|
|
15
|
+
COMMANDS:
|
|
16
|
+
-path <directory> Set the script directory path
|
|
17
|
+
-list, -l Show current configuration and available scripts
|
|
18
|
+
-help, -h Show this help message
|
|
19
|
+
--version, -V Show version number
|
|
20
|
+
|
|
21
|
+
SCRIPT EXECUTION:
|
|
22
|
+
q <script> Execute a script by name
|
|
23
|
+
q <script> [args] Execute a script with arguments
|
|
24
|
+
|
|
25
|
+
EXAMPLES:
|
|
26
|
+
# Setup
|
|
27
|
+
q -path /path/to/scripts Set script directory
|
|
28
|
+
q -list View current configuration
|
|
29
|
+
|
|
30
|
+
# Execute scripts
|
|
31
|
+
q hello Run hello.js or hello.sh
|
|
32
|
+
q backup /src /dest Run backup script with arguments
|
|
33
|
+
q deploy production --verbose Run deploy script with multiple args
|
|
34
|
+
q my-folder Run index.js/index.sh from my-folder/
|
|
35
|
+
|
|
36
|
+
SUPPORTED FILE TYPES:
|
|
37
|
+
.js, .mjs JavaScript files (executed with node)
|
|
38
|
+
.sh Shell scripts (executed with sh)
|
|
39
|
+
directories Looks for index.js, index.sh, or index.mjs
|
|
40
|
+
|
|
41
|
+
ALIAS CONFIGURATION:
|
|
42
|
+
Create a config.json file in your script directory to define aliases:
|
|
43
|
+
|
|
44
|
+
{
|
|
45
|
+
"chat": {"bin": "./chat/src/main.js"}, // Relative path
|
|
46
|
+
"python3": "/usr/bin/python3", // Absolute path
|
|
47
|
+
"w": "which" // System command
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
Execution priority: Alias config > Script files > System commands
|
|
51
|
+
|
|
52
|
+
CONFIGURATION:
|
|
53
|
+
Global config: ~/.quick-sh/config.json
|
|
54
|
+
Alias config: <script-path>/config.json
|
|
55
|
+
|
|
56
|
+
For more information, visit: https://github.com/your-repo/quick-sh
|
|
57
|
+
`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = {
|
|
61
|
+
showHelp
|
|
62
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { readConfig, readAliasConfig } = require('./config');
|
|
4
|
+
const { executeFile, executeFromDirectory, executeSystemCommand } = require('./executor');
|
|
5
|
+
const { checkSystemCommand } = require('./utils');
|
|
6
|
+
|
|
7
|
+
// 执行脚本(核心逻辑,包含alias处理和优先级)
|
|
8
|
+
async function executeScript(scriptName, args = []) {
|
|
9
|
+
const config = await readConfig();
|
|
10
|
+
|
|
11
|
+
if (!config.scriptPath) {
|
|
12
|
+
console.error('No script path configured. Use "q -path <directory>" to set one.');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// 优先级1: 检查alias配置
|
|
17
|
+
const aliasConfig = await readAliasConfig(config.scriptPath);
|
|
18
|
+
if (aliasConfig[scriptName]) {
|
|
19
|
+
const aliasValue = aliasConfig[scriptName];
|
|
20
|
+
|
|
21
|
+
if (typeof aliasValue === 'string') {
|
|
22
|
+
// 字符串值:可能是绝对路径或系统命令
|
|
23
|
+
if (path.isAbsolute(aliasValue)) {
|
|
24
|
+
// 绝对路径
|
|
25
|
+
if (await fs.pathExists(aliasValue)) {
|
|
26
|
+
console.log(`Executing alias (absolute path): ${aliasValue}`);
|
|
27
|
+
await executeFile(aliasValue, args);
|
|
28
|
+
return;
|
|
29
|
+
} else {
|
|
30
|
+
console.error(`Alias target not found: ${aliasValue}`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
// 系统命令
|
|
35
|
+
if (await checkSystemCommand(aliasValue)) {
|
|
36
|
+
await executeSystemCommand(aliasValue, args);
|
|
37
|
+
return;
|
|
38
|
+
} else {
|
|
39
|
+
console.error(`System command not found: ${aliasValue}`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} else if (aliasValue && typeof aliasValue === 'object' && aliasValue.bin) {
|
|
44
|
+
// 对象值:相对路径配置
|
|
45
|
+
const relativePath = aliasValue.bin;
|
|
46
|
+
const fullPath = path.resolve(config.scriptPath, relativePath);
|
|
47
|
+
|
|
48
|
+
if (await fs.pathExists(fullPath)) {
|
|
49
|
+
console.log(`Executing alias (relative path): ${relativePath}`);
|
|
50
|
+
await executeFile(fullPath, args);
|
|
51
|
+
return;
|
|
52
|
+
} else {
|
|
53
|
+
console.error(`Alias target not found: ${fullPath}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
console.error(`Invalid alias configuration for: ${scriptName}`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 优先级2: 检查path中的文件(原有逻辑)
|
|
63
|
+
const scriptPath = path.join(config.scriptPath, scriptName);
|
|
64
|
+
|
|
65
|
+
// 检查是否是文件夹
|
|
66
|
+
if (await fs.pathExists(scriptPath) && (await fs.stat(scriptPath)).isDirectory()) {
|
|
67
|
+
await executeFromDirectory(scriptPath, args);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 检查不同的文件扩展名
|
|
72
|
+
const possibleFiles = [
|
|
73
|
+
scriptPath,
|
|
74
|
+
`${scriptPath}.js`,
|
|
75
|
+
`${scriptPath}.sh`,
|
|
76
|
+
`${scriptPath}.mjs`
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
let targetFile = null;
|
|
80
|
+
for (const file of possibleFiles) {
|
|
81
|
+
if (await fs.pathExists(file)) {
|
|
82
|
+
targetFile = file;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (targetFile) {
|
|
88
|
+
await executeFile(targetFile, args);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 优先级3: 检查系统可执行命令
|
|
93
|
+
if (await checkSystemCommand(scriptName)) {
|
|
94
|
+
await executeSystemCommand(scriptName, args);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.error(`Command not found: ${scriptName}`);
|
|
99
|
+
console.error(`Looked in:`);
|
|
100
|
+
console.error(` - Alias config: ${path.join(config.scriptPath, 'config.json')}`);
|
|
101
|
+
console.error(` - Script directory: ${config.scriptPath}`);
|
|
102
|
+
console.error(` - System PATH`);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 显示当前配置和可用脚本
|
|
107
|
+
async function showStatus() {
|
|
108
|
+
const config = await readConfig();
|
|
109
|
+
|
|
110
|
+
if (config.scriptPath) {
|
|
111
|
+
console.log(`Current script path: ${config.scriptPath}`);
|
|
112
|
+
|
|
113
|
+
// 列出可用的脚本
|
|
114
|
+
try {
|
|
115
|
+
const files = await fs.readdir(config.scriptPath);
|
|
116
|
+
const scripts = [];
|
|
117
|
+
|
|
118
|
+
for (const file of files) {
|
|
119
|
+
const filePath = path.join(config.scriptPath, file);
|
|
120
|
+
const stat = await fs.stat(filePath);
|
|
121
|
+
|
|
122
|
+
if (stat.isDirectory()) {
|
|
123
|
+
// 检查目录是否包含index文件
|
|
124
|
+
const indexFiles = ['index.js', 'index.sh', 'index.mjs'];
|
|
125
|
+
for (const indexFile of indexFiles) {
|
|
126
|
+
if (await fs.pathExists(path.join(filePath, indexFile))) {
|
|
127
|
+
scripts.push(`${file}/ (${indexFile})`);
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
} else if (file.endsWith('.js') || file.endsWith('.sh') || file.endsWith('.mjs')) {
|
|
132
|
+
scripts.push(file);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (scripts.length > 0) {
|
|
137
|
+
console.log('\nAvailable scripts:');
|
|
138
|
+
scripts.forEach(script => console.log(` - ${script}`));
|
|
139
|
+
} else {
|
|
140
|
+
console.log('\nNo executable scripts found.');
|
|
141
|
+
}
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error('Error reading script directory:', error.message);
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
console.log('No script path configured. Use "q -path <directory>" to set one.');
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module.exports = {
|
|
151
|
+
executeScript,
|
|
152
|
+
showStatus
|
|
153
|
+
};
|
package/lib/utils.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const { execSync } = require('child_process');
|
|
2
|
+
|
|
3
|
+
// 检查系统命令是否存在
|
|
4
|
+
async function checkSystemCommand(command) {
|
|
5
|
+
try {
|
|
6
|
+
const isWindows = process.platform === 'win32';
|
|
7
|
+
const whichCommand = isWindows ? 'where' : 'which';
|
|
8
|
+
|
|
9
|
+
execSync(`${whichCommand} ${command}`, { stdio: 'ignore' });
|
|
10
|
+
return true;
|
|
11
|
+
} catch (error) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = {
|
|
17
|
+
checkSystemCommand
|
|
18
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "quick-sh",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A local script management tool for quick execution of JavaScript and Shell scripts with alias support",
|
|
5
|
+
"main": "lib/script-manager.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"q": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"lib/",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "node test/test.js",
|
|
17
|
+
"test:manual": "echo \"Run manual tests with: q test-args, q test-args.sh, q test-dir\"",
|
|
18
|
+
"prepublishOnly": "npm test"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"shell",
|
|
22
|
+
"script",
|
|
23
|
+
"cli",
|
|
24
|
+
"automation",
|
|
25
|
+
"local-scripts",
|
|
26
|
+
"alias",
|
|
27
|
+
"command-line",
|
|
28
|
+
"script-manager",
|
|
29
|
+
"javascript",
|
|
30
|
+
"bash",
|
|
31
|
+
"quick-execution"
|
|
32
|
+
],
|
|
33
|
+
"author": "YoungChou <your-email@example.com>",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=14.0.0"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/YoungChou/quick-sh.git"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/YoungChou/quick-sh#readme",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/YoungChou/quick-sh/issues"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"commander": "^9.0.0",
|
|
48
|
+
"fs-extra": "^11.0.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {},
|
|
51
|
+
"preferGlobal": true,
|
|
52
|
+
"directories": {
|
|
53
|
+
"example": "examples",
|
|
54
|
+
"lib": "lib",
|
|
55
|
+
"test": "test"
|
|
56
|
+
}
|
|
57
|
+
}
|