creepercli 1.0.0 → 1.1.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 +190 -0
- package/README.md +85 -117
- package/bin/creepercli.js +12 -24
- package/package.json +48 -48
- package/src/client.js +120 -110
- package/src/commands/auth.js +94 -108
- package/src/commands/exec.js +23 -37
- package/src/commands/fs.js +138 -152
- package/src/commands/index.js +126 -136
- package/src/commands/monitor.js +56 -65
- package/src/commands/search.js +56 -70
- package/src/config.js +47 -61
- package/src/editor.js +84 -98
- package/src/formatters.js +120 -134
- package/src/index.js +160 -171
- package/src/prompts.js +120 -134
- package/src/protocol.js +68 -82
- package/src/repl.js +86 -96
- package/src/session.js +39 -57
- package/src/top.js +39 -53
- package/src/transfer.js +243 -168
- package/src/update.js +71 -85
package/src/commands/fs.js
CHANGED
|
@@ -1,152 +1,138 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
ctx.
|
|
45
|
-
console.log(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (!
|
|
101
|
-
const res = await ctx.client.request('fs.
|
|
102
|
-
console.log(
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async function
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (
|
|
123
|
-
const res = await ctx.client.request('fs.
|
|
124
|
-
console.log(`
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
async function
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
async function info(ctx, args) {
|
|
142
|
-
if (!args[0]) throw new Error('Usage: info <path>');
|
|
143
|
-
const res = await ctx.client.request('fs.info', { path: args[0] });
|
|
144
|
-
console.log(formatInfo(res));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
async function edit(ctx, args) {
|
|
148
|
-
if (!args[0]) throw new Error('Usage: edit <path>');
|
|
149
|
-
return editFile(ctx, args[0]);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
module.exports = { pwd, ls, cd, tree, cat, head, tail, wc, touch, mkdir, rm, cp, mv, info, edit };
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const { formatLs, formatTree, humanSize, formatInfo } = require('../formatters');
|
|
6
|
+
const { editFile } = require('../editor');
|
|
7
|
+
|
|
8
|
+
async function pwd(ctx) {
|
|
9
|
+
const res = await ctx.client.request('fs.pwd');
|
|
10
|
+
ctx.cwd = res.cwd;
|
|
11
|
+
console.log(res.cwd);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function ls(ctx, args) {
|
|
15
|
+
const flags = args.filter((a) => a.startsWith('-') && a !== '-');
|
|
16
|
+
const longFormat = flags.some((f) => f.includes('l'));
|
|
17
|
+
const all = flags.some((f) => f.includes('a'));
|
|
18
|
+
const path = args.find((a) => !a.startsWith('-')) || '.';
|
|
19
|
+
const res = await ctx.client.request('fs.ls', { path, long: longFormat, all });
|
|
20
|
+
ctx.cwd = res.cwd;
|
|
21
|
+
console.log(formatLs(res.entries, { longFormat }));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function cd(ctx, args) {
|
|
25
|
+
let path = args[0] || '/';
|
|
26
|
+
if (path === '~' || path.startsWith('~/')) {
|
|
27
|
+
path = '/' + path.slice(1).replace(/^\/+/, '');
|
|
28
|
+
}
|
|
29
|
+
const res = await ctx.client.request('fs.cd', { path });
|
|
30
|
+
ctx.cwd = res.cwd;
|
|
31
|
+
console.log(`Changed directory to ${res.cwd}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function tree(ctx, args) {
|
|
35
|
+
let depth = 3;
|
|
36
|
+
const pathArgs = [];
|
|
37
|
+
for (let i = 0; i < args.length; i++) {
|
|
38
|
+
if (args[i] === '-d' || args[i] === '--depth') {
|
|
39
|
+
depth = parseInt(args[++i], 10) || 3;
|
|
40
|
+
} else {
|
|
41
|
+
pathArgs.push(args[i]);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const res = await ctx.client.request('fs.tree', { path: pathArgs[0] || '.', depth });
|
|
45
|
+
console.log(formatTree(res.children || []));
|
|
46
|
+
if (res.truncated) console.log('(tree truncated)');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function cat(ctx, args) {
|
|
50
|
+
if (!args[0]) throw new Error('Usage: cat <path>');
|
|
51
|
+
const res = await ctx.client.request('fs.cat', { path: args[0] });
|
|
52
|
+
process.stdout.write(res.content);
|
|
53
|
+
if (!res.content.endsWith('\n')) process.stdout.write('\n');
|
|
54
|
+
if (res.truncated) console.error(`(truncated: file is ${humanSize(res.size)})`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function head(ctx, args) {
|
|
58
|
+
const { lines, path } = parseTailArgs(args, 10);
|
|
59
|
+
const res = await ctx.client.request('fs.head', { path, lines });
|
|
60
|
+
console.log(res.lines.join('\n'));
|
|
61
|
+
if (res.truncated) console.error('(truncated)');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function tail(ctx, args) {
|
|
65
|
+
if (args.includes('-f') || args.includes('--follow')) {
|
|
66
|
+
throw new Error('Live follow is available via the "log" command: creepercli log [--grep <regex>]');
|
|
67
|
+
}
|
|
68
|
+
const { lines, path } = parseTailArgs(args, 10);
|
|
69
|
+
const res = await ctx.client.request('fs.tail', { path, lines });
|
|
70
|
+
console.log(res.lines.join('\n'));
|
|
71
|
+
if (res.truncated) console.error('(truncated)');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function parseTailArgs(args, def) {
|
|
75
|
+
let n = def;
|
|
76
|
+
let path = null;
|
|
77
|
+
for (let i = 0; i < args.length; i++) {
|
|
78
|
+
if (args[i] === '-n') n = parseInt(args[++i], 10) || def;
|
|
79
|
+
else path = args[i];
|
|
80
|
+
}
|
|
81
|
+
if (!path) throw new Error('Usage: <command> [-n N] <path>');
|
|
82
|
+
return { lines: n, path };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function wc(ctx, args) {
|
|
86
|
+
if (!args[0]) throw new Error('Usage: wc <path>');
|
|
87
|
+
const res = await ctx.client.request('fs.wc', { path: args[0] });
|
|
88
|
+
console.log(`${res.lines} ${res.words} ${res.chars} ${humanSize(res.bytes)} ${args[0]}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function touch(ctx, args) {
|
|
92
|
+
if (!args[0]) throw new Error('Usage: touch <path>');
|
|
93
|
+
const res = await ctx.client.request('fs.touch', { path: args[0] });
|
|
94
|
+
console.log(`touched ${res.path}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function mkdir(ctx, args) {
|
|
98
|
+
const parents = args.includes('-p') || args.includes('--parents');
|
|
99
|
+
const path = args.find((a) => !a.startsWith('-'));
|
|
100
|
+
if (!path) throw new Error('Usage: mkdir [-p] <path>');
|
|
101
|
+
const res = await ctx.client.request('fs.mkdir', { path, parents });
|
|
102
|
+
console.log(`created ${res.path}`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function rm(ctx, args) {
|
|
106
|
+
const recursive = args.includes('-r') || args.includes('-R') || args.includes('--recursive');
|
|
107
|
+
const path = args.find((a) => !a.startsWith('-'));
|
|
108
|
+
if (!path) throw new Error('Usage: rm [-r] <path>');
|
|
109
|
+
const res = await ctx.client.request('fs.rm', { path, recursive });
|
|
110
|
+
console.log(`removed ${res.path}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function cp(ctx, args) {
|
|
114
|
+
const recursive = args.includes('-r') || args.includes('-R') || args.includes('--recursive');
|
|
115
|
+
const rest = args.filter((a) => !a.startsWith('-'));
|
|
116
|
+
if (rest.length < 2) throw new Error('Usage: cp [-r] <src> <dst>');
|
|
117
|
+
const res = await ctx.client.request('fs.cp', { src: rest[0], dst: rest[1], recursive });
|
|
118
|
+
console.log(`copied ${res.src} -> ${res.dst}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function mv(ctx, args) {
|
|
122
|
+
if (args.length < 2) throw new Error('Usage: mv <src> <dst>');
|
|
123
|
+
const res = await ctx.client.request('fs.mv', { src: args[0], dst: args[1] });
|
|
124
|
+
console.log(`moved ${res.src} -> ${res.dst}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function info(ctx, args) {
|
|
128
|
+
if (!args[0]) throw new Error('Usage: info <path>');
|
|
129
|
+
const res = await ctx.client.request('fs.info', { path: args[0] });
|
|
130
|
+
console.log(formatInfo(res));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function edit(ctx, args) {
|
|
134
|
+
if (!args[0]) throw new Error('Usage: edit <path>');
|
|
135
|
+
return editFile(ctx, args[0]);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
module.exports = { pwd, ls, cd, tree, cat, head, tail, wc, touch, mkdir, rm, cp, mv, info, edit };
|
package/src/commands/index.js
CHANGED
|
@@ -1,139 +1,129 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
'
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
{ name: '
|
|
29
|
-
{ name: '
|
|
30
|
-
{ name: '
|
|
31
|
-
{ name: '
|
|
32
|
-
{ name: '
|
|
33
|
-
{ name: '
|
|
34
|
-
{ name: '
|
|
35
|
-
{ name: '
|
|
36
|
-
{ name: '
|
|
37
|
-
{ name: '
|
|
38
|
-
{ name: '
|
|
39
|
-
{ name: '
|
|
40
|
-
{ name: '
|
|
41
|
-
{ name: '
|
|
42
|
-
{ name: '
|
|
43
|
-
{ name: '
|
|
44
|
-
{ name: '
|
|
45
|
-
{ name: '
|
|
46
|
-
{ name: '
|
|
47
|
-
{ name: '
|
|
48
|
-
{ name: '
|
|
49
|
-
{ name: '
|
|
50
|
-
{ name: '
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
{ name: 'passwd', run: authCmds.passwd, desc: 'Change your password' },
|
|
58
|
-
{ name: 'totp', run: authCmds.totp, desc: 'Manage 2FA: totp setup | totp disable' },
|
|
59
|
-
{ name: 'whoami', run: authCmds.whoami, desc: 'Show current session info' },
|
|
60
|
-
{ name: 'ping', run: authCmds.ping, desc: 'Check server connectivity' },
|
|
61
|
-
{ name: 'help', run: help, desc: 'Show this help' },
|
|
62
|
-
{ name: 'exit', run: exitCmd, desc: 'Leave the REPL' },
|
|
63
|
-
{ name: 'quit', run: exitCmd, desc: 'Leave the REPL' },
|
|
64
|
-
{ name: 'q', run: exitCmd, desc: 'Leave the REPL' },
|
|
65
|
-
];
|
|
66
|
-
|
|
67
|
-
function find(name) {
|
|
68
|
-
return COMMANDS.find((c) => c.name === name);
|
|
69
|
-
}
|
|
70
|
-
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const fsCmds = require('./fs');
|
|
6
|
+
const searchCmds = require('./search');
|
|
7
|
+
const execCmds = require('./exec');
|
|
8
|
+
const monitorCmds = require('./monitor');
|
|
9
|
+
const authCmds = require('./auth');
|
|
10
|
+
const { cpush, cpull, csync } = require('../transfer');
|
|
11
|
+
const { friendly } = require('../protocol');
|
|
12
|
+
|
|
13
|
+
const COMMANDS = [
|
|
14
|
+
{ name: 'pwd', run: fsCmds.pwd, desc: 'Print working directory' },
|
|
15
|
+
{ name: 'ls', run: fsCmds.ls, desc: 'List directory (flags: -l, -a)' },
|
|
16
|
+
{ name: 'cd', run: fsCmds.cd, desc: 'Change directory' },
|
|
17
|
+
{ name: 'tree', run: fsCmds.tree, desc: 'Show directory tree (flag: --depth N)' },
|
|
18
|
+
{ name: 'cat', run: fsCmds.cat, desc: 'Print file contents' },
|
|
19
|
+
{ name: 'head', run: fsCmds.head, desc: 'Print first lines of a file (-n N)' },
|
|
20
|
+
{ name: 'tail', run: fsCmds.tail, desc: 'Print last lines of a file (-n N)' },
|
|
21
|
+
{ name: 'wc', run: fsCmds.wc, desc: 'Count lines/words/chars of a file' },
|
|
22
|
+
{ name: 'touch', run: fsCmds.touch, desc: 'Create or update a file' },
|
|
23
|
+
{ name: 'mkdir', run: fsCmds.mkdir, desc: 'Create directory (flag: -p)' },
|
|
24
|
+
{ name: 'rm', run: fsCmds.rm, desc: 'Remove file/directory (flag: -r)' },
|
|
25
|
+
{ name: 'cp', run: fsCmds.cp, desc: 'Copy file/directory (flag: -r)' },
|
|
26
|
+
{ name: 'mv', run: fsCmds.mv, desc: 'Move/rename a file' },
|
|
27
|
+
{ name: 'info', run: fsCmds.info, desc: 'Show file info (perms, size, mtime)' },
|
|
28
|
+
{ name: 'edit', run: fsCmds.edit, desc: 'Edit a remote file in your local $EDITOR' },
|
|
29
|
+
{ name: 'grep', run: searchCmds.grep, desc: 'Search file contents (flags: -i, --max-depth N)' },
|
|
30
|
+
{ name: 'find', run: searchCmds.find, desc: 'Find files by glob (flag: --glob "**/*.jar")' },
|
|
31
|
+
{ name: 'exec', run: execCmds.exec, desc: 'Run a Minecraft console command (allowlisted)' },
|
|
32
|
+
{ name: 'say', run: execCmds.say, desc: 'Shortcut for "exec say ..."' },
|
|
33
|
+
{ name: 'restart', run: execCmds.restart, desc: 'Shortcut for "exec restart"' },
|
|
34
|
+
{ name: 'stats', run: monitorCmds.stats, desc: 'Server CPU/RAM/disk stats' },
|
|
35
|
+
{ name: 'tps', run: monitorCmds.tps, desc: 'Server TPS (1m/5m/15m)' },
|
|
36
|
+
{ name: 'log', run: monitorCmds.log, desc: 'Follow the server console (flag: --grep <regex>)' },
|
|
37
|
+
{ name: 'top', run: monitorCmds.top, desc: 'Live dashboard (q to quit)' },
|
|
38
|
+
{ name: 'cpush', run: cpush, desc: 'Upload a local file to the server' },
|
|
39
|
+
{ name: 'cpull', run: cpull, desc: 'Download a server file locally' },
|
|
40
|
+
{ name: 'csync', run: csync, desc: 'Synchronize local/remote directories (flag: --yes)' },
|
|
41
|
+
{ name: 'login', run: authCmds.login, desc: 'Authenticate and store a session token' },
|
|
42
|
+
{ name: 'logout', run: authCmds.logout, desc: 'Invalidate session and clear local credentials' },
|
|
43
|
+
{ name: 'passwd', run: authCmds.passwd, desc: 'Change your password' },
|
|
44
|
+
{ name: 'totp', run: authCmds.totp, desc: 'Manage 2FA: totp setup | totp disable' },
|
|
45
|
+
{ name: 'whoami', run: authCmds.whoami, desc: 'Show current session info' },
|
|
46
|
+
{ name: 'ping', run: authCmds.ping, desc: 'Check server connectivity' },
|
|
47
|
+
{ name: 'help', run: help, desc: 'Show this help' },
|
|
48
|
+
{ name: 'exit', run: exitCmd, desc: 'Leave the REPL' },
|
|
49
|
+
{ name: 'quit', run: exitCmd, desc: 'Leave the REPL' },
|
|
50
|
+
{ name: 'q', run: exitCmd, desc: 'Leave the REPL' },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
function find(name) {
|
|
54
|
+
return COMMANDS.find((c) => c.name === name);
|
|
55
|
+
}
|
|
56
|
+
|
|
71
57
|
function splitArgs(input) {
|
|
72
|
-
const args = [];
|
|
73
|
-
let cur = '';
|
|
74
|
-
let quote = null;
|
|
75
|
-
let escaped = false;
|
|
76
|
-
for (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
58
|
+
const args = [];
|
|
59
|
+
let cur = '';
|
|
60
|
+
let quote = null;
|
|
61
|
+
let escaped = false;
|
|
62
|
+
for (let i = 0; i < input.length; i++) {
|
|
63
|
+
const ch = input[i];
|
|
64
|
+
if (escaped) {
|
|
65
|
+
cur += ch;
|
|
66
|
+
escaped = false;
|
|
80
67
|
} else if (ch === '\\') {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (
|
|
68
|
+
const next = input[i + 1];
|
|
69
|
+
const escapable = next != null && (next === '\\' || next === '"' || next === "'" || /\s/.test(next));
|
|
70
|
+
if (escapable) escaped = true;
|
|
84
71
|
else cur += ch;
|
|
85
|
-
} else if (
|
|
86
|
-
quote =
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (trimmed.startsWith('
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
72
|
+
} else if (quote) {
|
|
73
|
+
if (ch === quote) quote = null;
|
|
74
|
+
else cur += ch;
|
|
75
|
+
} else if (ch === '"' || ch === "'") {
|
|
76
|
+
quote = ch;
|
|
77
|
+
} else if (/\s/.test(ch)) {
|
|
78
|
+
if (cur) {
|
|
79
|
+
args.push(cur);
|
|
80
|
+
cur = '';
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
cur += ch;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (escaped) cur += '\\';
|
|
87
|
+
if (cur) args.push(cur);
|
|
88
|
+
return args;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function runCommand(ctx, input) {
|
|
92
|
+
let trimmed = input.trim();
|
|
93
|
+
if (!trimmed || trimmed.startsWith('#')) return 0;
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if (trimmed.startsWith('creepercli.exe ')) {
|
|
97
|
+
trimmed = trimmed.slice(15).trim();
|
|
98
|
+
} else if (trimmed.startsWith('creepercli ')) {
|
|
99
|
+
trimmed = trimmed.slice(11).trim();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const args = splitArgs(trimmed);
|
|
103
|
+
const cmd = find(args[0]);
|
|
104
|
+
if (!cmd) {
|
|
105
|
+
console.error(`Unknown command: ${args[0]} (try "help")`);
|
|
106
|
+
return 1;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const rc = await cmd.run(ctx, args.slice(1));
|
|
110
|
+
return rc == null ? 0 : rc;
|
|
111
|
+
} catch (err) {
|
|
112
|
+
console.error(`Error: ${friendly(err)}`);
|
|
113
|
+
return 1;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function help() {
|
|
118
|
+
const width = Math.max(...COMMANDS.map((c) => c.name.length)) + 2;
|
|
119
|
+
for (const c of COMMANDS) {
|
|
120
|
+
console.log(c.name.padEnd(width) + c.desc);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function exitCmd(ctx) {
|
|
125
|
+
ctx.exit = true;
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = { COMMANDS, find, splitArgs, runCommand, help };
|