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/monitor.js
CHANGED
|
@@ -1,69 +1,60 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
async function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const res = await ctx.client.request('monitor.
|
|
30
|
-
console.log(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
async function top(ctx) {
|
|
34
|
-
await runTop(ctx.client, ctx.cfg.topRefreshMs);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
async function log(ctx, args) {
|
|
38
|
-
let grep = null;
|
|
39
|
-
for (let i = 0; i < args.length; i++) {
|
|
40
|
-
if (args[i] === '--grep') grep = args[++i];
|
|
41
|
-
else if (args[i].startsWith('--grep=')) grep = args[i].slice(7);
|
|
42
|
-
}
|
|
43
|
-
const res = await ctx.client.request('monitor.log.start', { grep });
|
|
44
|
-
console.log(`Streaming console log (Ctrl+C to stop)${res.grep ? ' filtered by "' + res.grep + '"' : ''}`);
|
|
45
|
-
if (res.buffer && res.buffer.length) console.log(res.buffer.join('\n'));
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const { formatStats, formatTps } = require('../formatters');
|
|
6
|
+
const { runTop } = require('../top');
|
|
7
|
+
const { EVENT_LOG_LINE } = require('../protocol');
|
|
8
|
+
|
|
9
|
+
async function stats(ctx) {
|
|
10
|
+
const res = await ctx.client.request('monitor.stats');
|
|
11
|
+
console.log(formatStats(res));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function tps(ctx) {
|
|
15
|
+
const res = await ctx.client.request('monitor.tps');
|
|
16
|
+
console.log(formatTps(res));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function top(ctx) {
|
|
20
|
+
await runTop(ctx.client, ctx.cfg.topRefreshMs);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function log(ctx, args) {
|
|
24
|
+
let grep = null;
|
|
25
|
+
for (let i = 0; i < args.length; i++) {
|
|
26
|
+
if (args[i] === '--grep') grep = args[++i];
|
|
27
|
+
else if (args[i].startsWith('--grep=')) grep = args[i].slice(7);
|
|
28
|
+
}
|
|
29
|
+
const res = await ctx.client.request('monitor.log.start', { grep });
|
|
30
|
+
console.log(`Streaming console log (Ctrl+C to stop)${res.grep ? ' filtered by "' + res.grep + '"' : ''}`);
|
|
31
|
+
if (res.buffer && res.buffer.length) console.log(res.buffer.join('\n'));
|
|
46
32
|
let stop = null;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
33
|
+
let stopped = false;
|
|
34
|
+
try {
|
|
35
|
+
await new Promise((resolve) => {
|
|
36
|
+
const onLine = (event, data) => {
|
|
37
|
+
if (event === EVENT_LOG_LINE) console.log(data.message);
|
|
38
|
+
};
|
|
52
39
|
ctx.client.on('event', onLine);
|
|
53
40
|
stop = () => {
|
|
41
|
+
if (stopped) return;
|
|
42
|
+
stopped = true;
|
|
54
43
|
ctx.client.removeListener('event', onLine);
|
|
55
|
-
ctx.client.request('monitor.log.stop').catch(() => {});
|
|
56
|
-
resolve();
|
|
57
|
-
};
|
|
58
|
-
process.once('SIGINT', stop);
|
|
59
|
-
ctx.interruptHandler = stop;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
44
|
+
ctx.client.request('monitor.log.stop').catch(() => {});
|
|
45
|
+
resolve();
|
|
46
|
+
};
|
|
47
|
+
process.once('SIGINT', stop);
|
|
48
|
+
ctx.interruptHandler = stop;
|
|
49
|
+
ctx.logTeardown = stop;
|
|
50
|
+
});
|
|
51
|
+
} finally {
|
|
52
|
+
if (stop) {
|
|
53
|
+
process.removeListener('SIGINT', stop);
|
|
54
|
+
if (ctx.interruptHandler === stop) ctx.interruptHandler = null;
|
|
55
|
+
if (ctx.logTeardown === stop) ctx.logTeardown = null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = { stats, tps, top, log };
|
package/src/commands/search.js
CHANGED
|
@@ -1,70 +1,56 @@
|
|
|
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
|
-
};
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (res.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (a === '--glob' || a === '-g') glob = args[++i] || '*';
|
|
58
|
-
else if (a === '--max-depth') maxDepth = parseInt(args[++i], 10) || 12;
|
|
59
|
-
else path = a;
|
|
60
|
-
}
|
|
61
|
-
const res = await ctx.client.request('fs.find', { path, glob, maxDepth });
|
|
62
|
-
if (!res.results.length) {
|
|
63
|
-
console.log('No matches');
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
console.log(res.results.join('\n'));
|
|
67
|
-
if (res.truncated) console.error('(truncated)');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
module.exports = { grep, find };
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const { formatMatches } = require('../formatters');
|
|
6
|
+
|
|
7
|
+
async function grep(ctx, args) {
|
|
8
|
+
const opts = {
|
|
9
|
+
caseInsensitive: false,
|
|
10
|
+
recursive: true,
|
|
11
|
+
lineNumbers: true,
|
|
12
|
+
maxDepth: 6,
|
|
13
|
+
maxResults: 1000,
|
|
14
|
+
};
|
|
15
|
+
const rest = [];
|
|
16
|
+
for (let i = 0; i < args.length; i++) {
|
|
17
|
+
const a = args[i];
|
|
18
|
+
if (a === '-i' || a === '--ignore-case') opts.caseInsensitive = true;
|
|
19
|
+
else if (a === '-r' || a === '-R' || a === '--recursive') opts.recursive = true;
|
|
20
|
+
else if (a === '-n' || a === '--line-number') opts.lineNumbers = true;
|
|
21
|
+
else if (a === '--max-depth') opts.maxDepth = parseInt(args[++i], 10) || 6;
|
|
22
|
+
else if (a === '--max-results') opts.maxResults = parseInt(args[++i], 10) || 1000;
|
|
23
|
+
else rest.push(a);
|
|
24
|
+
}
|
|
25
|
+
const pattern = rest[0];
|
|
26
|
+
const path = rest[1] || '.';
|
|
27
|
+
if (!pattern) throw new Error('Usage: grep [options] <pattern> [path]');
|
|
28
|
+
const res = await ctx.client.request('fs.grep', { path, pattern, ...opts });
|
|
29
|
+
if (!res.matches.length) {
|
|
30
|
+
console.log('No matches');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
console.log(formatMatches(res.matches));
|
|
34
|
+
if (res.truncated) console.error(`(truncated at ${res.matches.length} matches)`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function find(ctx, args) {
|
|
38
|
+
let glob = '*';
|
|
39
|
+
let path = '.';
|
|
40
|
+
let maxDepth = 12;
|
|
41
|
+
for (let i = 0; i < args.length; i++) {
|
|
42
|
+
const a = args[i];
|
|
43
|
+
if (a === '--glob' || a === '-g') glob = args[++i] || '*';
|
|
44
|
+
else if (a === '--max-depth') maxDepth = parseInt(args[++i], 10) || 12;
|
|
45
|
+
else path = a;
|
|
46
|
+
}
|
|
47
|
+
const res = await ctx.client.request('fs.find', { path, glob, maxDepth });
|
|
48
|
+
if (!res.results.length) {
|
|
49
|
+
console.log('No matches');
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
console.log(res.results.join('\n'));
|
|
53
|
+
if (res.truncated) console.error('(truncated)');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { grep, find };
|
package/src/config.js
CHANGED
|
@@ -1,61 +1,47 @@
|
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
fs.writeFileSync(CONFIG_FILE, JSON.stringify(updated, null, 2), { mode: 0o600 });
|
|
49
|
-
} catch {
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function applyFlags(cfg, flags) {
|
|
54
|
-
if (flags.host) cfg.host = flags.host;
|
|
55
|
-
if (flags.port) cfg.port = flags.port;
|
|
56
|
-
if (flags.editor) cfg.editor = flags.editor;
|
|
57
|
-
if (flags.refresh) cfg.topRefreshMs = flags.refresh;
|
|
58
|
-
return cfg;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
module.exports = { load, save, applyFlags, CONFIG_FILE, DEFAULTS };
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { DIR, HISTORY_FILE } = require('./session');
|
|
8
|
+
|
|
9
|
+
const CONFIG_FILE = path.join(DIR, 'config.json');
|
|
10
|
+
|
|
11
|
+
const DEFAULTS = {
|
|
12
|
+
host: '127.0.0.1',
|
|
13
|
+
port: 45678,
|
|
14
|
+
editor: process.env.EDITOR || 'vi',
|
|
15
|
+
topRefreshMs: 2000,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
function load() {
|
|
19
|
+
let file = {};
|
|
20
|
+
try {
|
|
21
|
+
file = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
const cfg = { ...DEFAULTS, ...file };
|
|
25
|
+
cfg.historyFile = HISTORY_FILE;
|
|
26
|
+
return cfg;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function save(newConfig) {
|
|
30
|
+
try {
|
|
31
|
+
fs.mkdirSync(DIR, { recursive: true, mode: 0o700 });
|
|
32
|
+
const existing = load();
|
|
33
|
+
const updated = { ...existing, ...newConfig };
|
|
34
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(updated, null, 2), { mode: 0o600 });
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function applyFlags(cfg, flags) {
|
|
40
|
+
if (flags.host) cfg.host = flags.host;
|
|
41
|
+
if (flags.port) cfg.port = flags.port;
|
|
42
|
+
if (flags.editor) cfg.editor = flags.editor;
|
|
43
|
+
if (flags.refresh) cfg.topRefreshMs = flags.refresh;
|
|
44
|
+
return cfg;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = { load, save, applyFlags, CONFIG_FILE, DEFAULTS };
|
package/src/editor.js
CHANGED
|
@@ -1,98 +1,84 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
function summarizeDiff(oldLines, newLines) {
|
|
89
|
-
const a = new Set(oldLines);
|
|
90
|
-
const b = new Set(newLines);
|
|
91
|
-
let added = 0;
|
|
92
|
-
let removed = 0;
|
|
93
|
-
for (const l of newLines) if (!a.has(l)) added++;
|
|
94
|
-
for (const l of oldLines) if (!b.has(l)) removed++;
|
|
95
|
-
return { added, removed };
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
module.exports = { editFile };
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const crypto = require('crypto');
|
|
8
|
+
const { spawn } = require('child_process');
|
|
9
|
+
const { DIR } = require('./session');
|
|
10
|
+
|
|
11
|
+
function sha256(content) {
|
|
12
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function editFile(ctx, remotePath) {
|
|
16
|
+
if (!remotePath) throw new Error('Usage: edit <path>');
|
|
17
|
+
const tmpRoot = path.join(DIR, 'tmp');
|
|
18
|
+
fs.mkdirSync(tmpRoot, { recursive: true, mode: 0o700 });
|
|
19
|
+
const tmpDir = fs.mkdtempSync(path.join(tmpRoot, 'edit-'));
|
|
20
|
+
const localName = remotePath.split('/').pop() || 'file';
|
|
21
|
+
const tmpFile = path.join(tmpDir, localName);
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
await ctx.client.request('fs.edit.lock', { path: remotePath });
|
|
25
|
+
} catch (err) {
|
|
26
|
+
if (err.code === 'E_LOCKED') {
|
|
27
|
+
console.error(`Locked: ${err.message}`);
|
|
28
|
+
return 1;
|
|
29
|
+
}
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const cat = await ctx.client.request('fs.cat', { path: remotePath });
|
|
35
|
+
fs.writeFileSync(tmpFile, cat.content, { mode: 0o600 });
|
|
36
|
+
|
|
37
|
+
const editor = ctx.cfg.editor;
|
|
38
|
+
const parts = (editor.match(/"[^"]+"|'[^']+'|\S+/g) || []).map((p) => {
|
|
39
|
+
if ((p.startsWith('"') && p.endsWith('"')) || (p.startsWith("'") && p.endsWith("'"))) {
|
|
40
|
+
return p.slice(1, -1);
|
|
41
|
+
}
|
|
42
|
+
return p;
|
|
43
|
+
});
|
|
44
|
+
const execName = parts[0] || editor;
|
|
45
|
+
const args = parts.slice(1);
|
|
46
|
+
await new Promise((resolve, reject) => {
|
|
47
|
+
const child = spawn(execName, [...args, tmpFile], { stdio: 'inherit' });
|
|
48
|
+
child.on('error', (err) => reject(new Error(`Could not start editor "${editor}": ${err.message}`)));
|
|
49
|
+
child.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`Editor exited with code ${code}`))));
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const newContent = fs.readFileSync(tmpFile, 'utf8');
|
|
53
|
+
if (newContent === cat.content) {
|
|
54
|
+
console.log('No changes.');
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
const res = await ctx.client.request('fs.edit.push', {
|
|
58
|
+
path: remotePath,
|
|
59
|
+
content: newContent,
|
|
60
|
+
sha256: sha256(newContent),
|
|
61
|
+
});
|
|
62
|
+
const diff = summarizeDiff(cat.content.split('\n'), newContent.split('\n'));
|
|
63
|
+
console.log(`Pushed ${res.bytes} bytes to ${res.path} (+${diff.added}/-${diff.removed} lines)`);
|
|
64
|
+
return 0;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
console.error(`Edit failed: ${err.message}`);
|
|
67
|
+
return 1;
|
|
68
|
+
} finally {
|
|
69
|
+
await ctx.client.request('fs.edit.unlock', { path: remotePath }).catch(() => {});
|
|
70
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function summarizeDiff(oldLines, newLines) {
|
|
75
|
+
const a = new Set(oldLines);
|
|
76
|
+
const b = new Set(newLines);
|
|
77
|
+
let added = 0;
|
|
78
|
+
let removed = 0;
|
|
79
|
+
for (const l of newLines) if (!a.has(l)) added++;
|
|
80
|
+
for (const l of oldLines) if (!b.has(l)) removed++;
|
|
81
|
+
return { added, removed };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = { editFile };
|