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.
@@ -1,69 +1,60 @@
1
- /*
2
- * Copyright 2026 DemonZDevelopment
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
- 'use strict';
18
-
19
- const { formatStats, formatTps } = require('../formatters');
20
- const { runTop } = require('../top');
21
- const { EVENT_LOG_LINE } = require('../protocol');
22
-
23
- async function stats(ctx) {
24
- const res = await ctx.client.request('monitor.stats');
25
- console.log(formatStats(res));
26
- }
27
-
28
- async function tps(ctx) {
29
- const res = await ctx.client.request('monitor.tps');
30
- console.log(formatTps(res));
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
- try {
48
- await new Promise((resolve) => {
49
- const onLine = (event, data) => {
50
- if (event === EVENT_LOG_LINE) console.log(data.message);
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
- } finally {
62
- if (stop) {
63
- process.removeListener('SIGINT', stop);
64
- if (ctx.interruptHandler === stop) ctx.interruptHandler = null;
65
- }
66
- }
67
- }
68
-
69
- module.exports = { stats, tps, top, log };
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 };
@@ -1,70 +1,56 @@
1
- /*
2
- * Copyright 2026 DemonZDevelopment
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
- 'use strict';
18
-
19
- const { formatMatches } = require('../formatters');
20
-
21
- async function grep(ctx, args) {
22
- const opts = {
23
- caseInsensitive: false,
24
- recursive: true,
25
- lineNumbers: true,
26
- maxDepth: 6,
27
- maxResults: 1000,
28
- };
29
- const rest = [];
30
- for (let i = 0; i < args.length; i++) {
31
- const a = args[i];
32
- if (a === '-i' || a === '--ignore-case') opts.caseInsensitive = true;
33
- else if (a === '-r' || a === '-R' || a === '--recursive') opts.recursive = true;
34
- else if (a === '-n' || a === '--line-number') opts.lineNumbers = true;
35
- else if (a === '--max-depth') opts.maxDepth = parseInt(args[++i], 10) || 6;
36
- else if (a === '--max-results') opts.maxResults = parseInt(args[++i], 10) || 1000;
37
- else rest.push(a);
38
- }
39
- const pattern = rest[0];
40
- const path = rest[1] || '.';
41
- if (!pattern) throw new Error('Usage: grep [options] <pattern> [path]');
42
- const res = await ctx.client.request('fs.grep', { path, pattern, ...opts });
43
- if (!res.matches.length) {
44
- console.log('No matches');
45
- return;
46
- }
47
- console.log(formatMatches(res.matches));
48
- if (res.truncated) console.error(`(truncated at ${res.matches.length} matches)`);
49
- }
50
-
51
- async function find(ctx, args) {
52
- let glob = '*';
53
- let path = '.';
54
- let maxDepth = 12;
55
- for (let i = 0; i < args.length; i++) {
56
- const a = args[i];
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
- * Copyright 2026 DemonZDevelopment
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
- 'use strict';
18
-
19
- const fs = require('fs');
20
- const path = require('path');
21
- const { DIR, HISTORY_FILE } = require('./session');
22
-
23
- const CONFIG_FILE = path.join(DIR, 'config.json');
24
-
25
- const DEFAULTS = {
26
- host: '127.0.0.1',
27
- port: 45678,
28
- editor: process.env.EDITOR || 'vi',
29
- topRefreshMs: 2000,
30
- };
31
-
32
- function load() {
33
- let file = {};
34
- try {
35
- file = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
36
- } catch {
37
- }
38
- const cfg = { ...DEFAULTS, ...file };
39
- cfg.historyFile = HISTORY_FILE;
40
- return cfg;
41
- }
42
-
43
- function save(newConfig) {
44
- try {
45
- fs.mkdirSync(DIR, { recursive: true, mode: 0o700 });
46
- const existing = load();
47
- const updated = { ...existing, ...newConfig };
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
- * Copyright 2026 DemonZDevelopment
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
- 'use strict';
18
-
19
- const fs = require('fs');
20
- const path = require('path');
21
- const crypto = require('crypto');
22
- const { spawn } = require('child_process');
23
- const { DIR } = require('./session');
24
-
25
- function sha256(content) {
26
- return crypto.createHash('sha256').update(content).digest('hex');
27
- }
28
-
29
- async function editFile(ctx, remotePath) {
30
- if (!remotePath) throw new Error('Usage: edit <path>');
31
- const tmpRoot = path.join(DIR, 'tmp');
32
- fs.mkdirSync(tmpRoot, { recursive: true, mode: 0o700 });
33
- const tmpDir = fs.mkdtempSync(path.join(tmpRoot, 'edit-'));
34
- const localName = remotePath.split('/').pop() || 'file';
35
- const tmpFile = path.join(tmpDir, localName);
36
-
37
- try {
38
- await ctx.client.request('fs.edit.lock', { path: remotePath });
39
- } catch (err) {
40
- if (err.code === 'E_LOCKED') {
41
- console.error(`Locked: ${err.message}`);
42
- return 1;
43
- }
44
- throw err;
45
- }
46
-
47
- try {
48
- const cat = await ctx.client.request('fs.cat', { path: remotePath });
49
- fs.writeFileSync(tmpFile, cat.content, { mode: 0o600 });
50
-
51
- const editor = ctx.cfg.editor;
52
- const parts = (editor.match(/"[^"]+"|'[^']+'|\S+/g) || []).map((p) => {
53
- if ((p.startsWith('"') && p.endsWith('"')) || (p.startsWith("'") && p.endsWith("'"))) {
54
- return p.slice(1, -1);
55
- }
56
- return p;
57
- });
58
- const execName = parts[0] || editor;
59
- const args = parts.slice(1);
60
- await new Promise((resolve, reject) => {
61
- const child = spawn(execName, [...args, tmpFile], { stdio: 'inherit' });
62
- child.on('error', (err) => reject(new Error(`Could not start editor "${editor}": ${err.message}`)));
63
- child.on('close', (code) => (code === 0 ? resolve() : reject(new Error(`Editor exited with code ${code}`))));
64
- });
65
-
66
- const newContent = fs.readFileSync(tmpFile, 'utf8');
67
- if (newContent === cat.content) {
68
- console.log('No changes.');
69
- return 0;
70
- }
71
- const res = await ctx.client.request('fs.edit.push', {
72
- path: remotePath,
73
- content: newContent,
74
- sha256: sha256(newContent),
75
- });
76
- const diff = summarizeDiff(cat.content.split('\n'), newContent.split('\n'));
77
- console.log(`Pushed ${res.bytes} bytes to ${res.path} (+${diff.added}/-${diff.removed} lines)`);
78
- return 0;
79
- } catch (err) {
80
- console.error(`Edit failed: ${err.message}`);
81
- return 1;
82
- } finally {
83
- await ctx.client.request('fs.edit.unlock', { path: remotePath }).catch(() => {});
84
- fs.rmSync(tmpDir, { recursive: true, force: true });
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 };