@woopsy/mcpanel 1.0.3 → 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/dist/commands/commandRouter.js +18 -0
- package/dist/index.js +30 -2
- package/dist/managers/playitManager.js +40 -5
- package/dist/services/updateChecker.js +140 -0
- package/dist/utils/helpers.js +65 -0
- package/package.json +1 -1
|
@@ -42,6 +42,7 @@ const path = __importStar(require("path"));
|
|
|
42
42
|
const colors = __importStar(require("../utils/colors"));
|
|
43
43
|
const helpers_1 = require("../utils/helpers");
|
|
44
44
|
const downloadService_1 = require("../services/downloadService");
|
|
45
|
+
const updateChecker_1 = require("../services/updateChecker");
|
|
45
46
|
const pidusage_1 = __importDefault(require("pidusage"));
|
|
46
47
|
class CommandRouter {
|
|
47
48
|
configManager;
|
|
@@ -96,6 +97,7 @@ class CommandRouter {
|
|
|
96
97
|
' /java [path] - Show/list Java runtimes, or set the one used to launch',
|
|
97
98
|
' /folder - Open the server folder in the file explorer',
|
|
98
99
|
' /clear - Clear the screen, scrollback and command history',
|
|
100
|
+
' /update - Check npm for a newer version of MCPANEL',
|
|
99
101
|
' /config - View active application config.json',
|
|
100
102
|
' /exit - Close MCPANEL server manager',
|
|
101
103
|
colors.gray('──────────────────────────────────────────────\n')
|
|
@@ -465,6 +467,22 @@ class CommandRouter {
|
|
|
465
467
|
this.configManager.updateSettings({ defaultJavaPath: cleanPath });
|
|
466
468
|
return colors.success(`Java set to "${cleanPath}" (version ${info.version}). It will be used on the next /start.`);
|
|
467
469
|
}
|
|
470
|
+
/**
|
|
471
|
+
* Executes /update — checks npm for a newer version and prints how to update.
|
|
472
|
+
*/
|
|
473
|
+
async executeUpdate() {
|
|
474
|
+
const info = await (0, updateChecker_1.checkForUpdate)(true);
|
|
475
|
+
if (!info) {
|
|
476
|
+
return colors.warning('Could not check for updates (no network connection?).');
|
|
477
|
+
}
|
|
478
|
+
if (info.updateAvailable) {
|
|
479
|
+
return [
|
|
480
|
+
colors.warning(`Update available: ${colors.bold(info.current)} → ${colors.bold(colors.green(info.latest))}`),
|
|
481
|
+
colors.gray('Update with: ') + colors.cyan(`npm i -g ${info.name}@latest`),
|
|
482
|
+
].join('\n');
|
|
483
|
+
}
|
|
484
|
+
return colors.success(`You're on the latest version (${info.current}).`);
|
|
485
|
+
}
|
|
468
486
|
/**
|
|
469
487
|
* Executes /config
|
|
470
488
|
*/
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,7 @@ const playitManager_1 = require("./managers/playitManager");
|
|
|
50
50
|
const commandRouter_1 = require("./commands/commandRouter");
|
|
51
51
|
const colors = __importStar(require("./utils/colors"));
|
|
52
52
|
const helpers_1 = require("./utils/helpers");
|
|
53
|
+
const updateChecker_1 = require("./services/updateChecker");
|
|
53
54
|
const logger_1 = require("./utils/logger");
|
|
54
55
|
// Initialize managers
|
|
55
56
|
const configManager = new configManager_1.ConfigManager();
|
|
@@ -70,6 +71,12 @@ let consoleActiveServer = '';
|
|
|
70
71
|
let logViewServer = '';
|
|
71
72
|
// Readline interface
|
|
72
73
|
let rl;
|
|
74
|
+
let CLI_VERSION = '1.0.3';
|
|
75
|
+
try {
|
|
76
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(configManager_1.APP_ROOT, 'package.json'), 'utf-8'));
|
|
77
|
+
CLI_VERSION = pkg.version || '1.0.3';
|
|
78
|
+
}
|
|
79
|
+
catch { /* ignore */ }
|
|
73
80
|
/**
|
|
74
81
|
* Renders the figlet "MCPANEL" ASCII banner with a chalk gradient.
|
|
75
82
|
*/
|
|
@@ -79,7 +86,7 @@ function renderBanner() {
|
|
|
79
86
|
const tints = [chalk_1.default.cyanBright, chalk_1.default.cyan, chalk_1.default.greenBright, chalk_1.default.green, chalk_1.default.green];
|
|
80
87
|
console.log();
|
|
81
88
|
lines.forEach((line, i) => console.log((tints[i] || chalk_1.default.green)(line)));
|
|
82
|
-
console.log(chalk_1.default.greenBright.bold(' Minecraft Server Manager') + chalk_1.default.gray(
|
|
89
|
+
console.log(chalk_1.default.greenBright.bold(' Minecraft Server Manager') + chalk_1.default.gray(` v${CLI_VERSION}`));
|
|
83
90
|
}
|
|
84
91
|
/**
|
|
85
92
|
* Renders the neofetch / Arch-Linux-style info block for the synced server.
|
|
@@ -161,7 +168,7 @@ const COMMAND_LIST = [
|
|
|
161
168
|
'/plugins list', '/plugins install', '/plugins remove',
|
|
162
169
|
'/setup',
|
|
163
170
|
'/tunnel java', '/tunnel bedrock', '/tunnel status', '/tunnel stop', '/tunnel reset',
|
|
164
|
-
'/config', '/clear', '/exit'
|
|
171
|
+
'/config', '/clear', '/update', '/exit'
|
|
165
172
|
];
|
|
166
173
|
// Subcommands offered once "<command> " has been typed.
|
|
167
174
|
const SUBCOMMANDS = {
|
|
@@ -609,6 +616,9 @@ async function handleCommandState(line) {
|
|
|
609
616
|
case '/java':
|
|
610
617
|
console.log(router.executeJava(args.length ? args.join(' ') : undefined));
|
|
611
618
|
break;
|
|
619
|
+
case '/update':
|
|
620
|
+
console.log(await router.executeUpdate());
|
|
621
|
+
break;
|
|
612
622
|
case '/config':
|
|
613
623
|
console.log(router.executeConfig());
|
|
614
624
|
break;
|
|
@@ -720,11 +730,29 @@ async function finishStartup() {
|
|
|
720
730
|
currentState = 'COMMAND';
|
|
721
731
|
promptUser();
|
|
722
732
|
}
|
|
733
|
+
/**
|
|
734
|
+
* Prints a one-time-per-launch notice if a newer version is on npm. Fail-silent
|
|
735
|
+
* and cached, so it never slows down or blocks startup.
|
|
736
|
+
*/
|
|
737
|
+
async function showUpdateNotice() {
|
|
738
|
+
try {
|
|
739
|
+
const info = await (0, updateChecker_1.checkForUpdate)();
|
|
740
|
+
if (info && info.updateAvailable) {
|
|
741
|
+
console.log();
|
|
742
|
+
console.log(chalk_1.default.yellow(' ⚡ Update available: ') + chalk_1.default.gray(info.current) + chalk_1.default.gray(' → ') + chalk_1.default.greenBright.bold(info.latest));
|
|
743
|
+
console.log(chalk_1.default.gray(' Update with: ') + chalk_1.default.cyan(`npm i -g ${info.name}@latest`));
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
catch {
|
|
747
|
+
// Never let an update check break startup.
|
|
748
|
+
}
|
|
749
|
+
}
|
|
723
750
|
/**
|
|
724
751
|
* Main application setup
|
|
725
752
|
*/
|
|
726
753
|
async function main() {
|
|
727
754
|
renderBanner();
|
|
755
|
+
await showUpdateNotice();
|
|
728
756
|
rl = readline.createInterface({
|
|
729
757
|
input: process.stdin,
|
|
730
758
|
output: process.stdout,
|
|
@@ -362,20 +362,28 @@ class PlayitManager {
|
|
|
362
362
|
// Full automated setup
|
|
363
363
|
// ---------------------------------------------------------------------------
|
|
364
364
|
/**
|
|
365
|
-
* One-call entry point: ensures binary + secret,
|
|
366
|
-
*
|
|
365
|
+
* One-call entry point: ensures binary + secret, starts the relay daemon (so
|
|
366
|
+
* the agent registers its version with playit), creates the tunnel via the
|
|
367
|
+
* API, and returns the live tunnel status.
|
|
368
|
+
*
|
|
369
|
+
* The relay MUST be started before creating a tunnel — playit rejects
|
|
370
|
+
* /tunnels/create with "AgentVersionTooOld" until a current agent has
|
|
371
|
+
* connected and reported its version.
|
|
367
372
|
*/
|
|
368
373
|
async setupAndStart(type, callbacks = {}) {
|
|
369
374
|
await this.ensureBinary();
|
|
370
375
|
const secret = await this.ensureSecret(callbacks);
|
|
371
376
|
this.tunnelStatus.status = 'Connecting';
|
|
372
377
|
this.tunnelStatus.type = type;
|
|
378
|
+
// Start the relay first so the agent connects and registers its version.
|
|
379
|
+
callbacks.onStatus?.('Starting tunnel agent...');
|
|
380
|
+
await this.startAgent(secret);
|
|
373
381
|
callbacks.onStatus?.('Checking your playit account for an existing tunnel...');
|
|
374
382
|
let rd = await this.getRunData(secret);
|
|
375
383
|
let tunnel = this.findTunnel(rd, type);
|
|
376
384
|
if (!tunnel) {
|
|
377
385
|
callbacks.onStatus?.(`Creating ${type} tunnel...`);
|
|
378
|
-
await this.
|
|
386
|
+
await this.createTunnelWithRetry(type, rd.agent_id, secret, callbacks);
|
|
379
387
|
// Poll until the tunnel leaves "pending" and gets a public address.
|
|
380
388
|
for (let i = 0; i < 15 && !tunnel; i++) {
|
|
381
389
|
await this.sleep(3000);
|
|
@@ -390,11 +398,38 @@ class PlayitManager {
|
|
|
390
398
|
this.tunnelStatus.address = address;
|
|
391
399
|
this.tunnelStatus.port = port;
|
|
392
400
|
this.configManager.updatePlayitTunnel({ tunnelAddress: address, tunnelPort: Number(port) });
|
|
393
|
-
callbacks.onStatus?.('Starting tunnel relay...');
|
|
394
|
-
await this.startAgent(secret);
|
|
395
401
|
this.tunnelStatus.status = 'Online';
|
|
396
402
|
return this.tunnelStatus;
|
|
397
403
|
}
|
|
404
|
+
/**
|
|
405
|
+
* Creates a tunnel, retrying on "AgentVersionTooOld" — that error means the
|
|
406
|
+
* freshly-started agent hasn't finished registering its version yet, so we
|
|
407
|
+
* wait and retry (refreshing the agent_id) a few times.
|
|
408
|
+
*/
|
|
409
|
+
async createTunnelWithRetry(type, agentId, secret, callbacks) {
|
|
410
|
+
let lastErr;
|
|
411
|
+
for (let attempt = 0; attempt < 8; attempt++) {
|
|
412
|
+
try {
|
|
413
|
+
await this.createApiTunnel(type, agentId, secret);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
catch (err) {
|
|
417
|
+
lastErr = err;
|
|
418
|
+
if (!/AgentVersionTooOld/i.test(err.message || ''))
|
|
419
|
+
throw err;
|
|
420
|
+
callbacks.onStatus?.('Waiting for the agent to finish registering with playit...');
|
|
421
|
+
await this.sleep(4000);
|
|
422
|
+
try {
|
|
423
|
+
const rd = await this.getRunData(secret);
|
|
424
|
+
if (rd?.agent_id)
|
|
425
|
+
agentId = rd.agent_id;
|
|
426
|
+
}
|
|
427
|
+
catch { /* keep previous agentId */ }
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
throw new Error(`${lastErr?.message || 'AgentVersionTooOld'} — the playit agent did not register in time. ` +
|
|
431
|
+
`Make sure the server can reach playit.gg, then try /tunnel again.`);
|
|
432
|
+
}
|
|
398
433
|
/** Spawns the long-running daemon that relays tunnel traffic. */
|
|
399
434
|
startAgent(secret) {
|
|
400
435
|
return new Promise((resolve) => {
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.isNewer = isNewer;
|
|
37
|
+
exports.checkForUpdate = checkForUpdate;
|
|
38
|
+
const https = __importStar(require("https"));
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const configManager_1 = require("../config/configManager");
|
|
42
|
+
/**
|
|
43
|
+
* Lightweight "is there a newer version on npm?" checker.
|
|
44
|
+
*
|
|
45
|
+
* - Reads the installed name/version from the package's own package.json.
|
|
46
|
+
* - Asks the npm registry's dist-tags endpoint for the latest version.
|
|
47
|
+
* - Caches the result (logs/.update-check.json) so we only hit the network
|
|
48
|
+
* every few hours, keeping startup fast.
|
|
49
|
+
* - Fully fail-silent: no network / offline / parse error => returns null.
|
|
50
|
+
*/
|
|
51
|
+
const CACHE_FILE = path.join(configManager_1.APP_ROOT, 'logs', '.update-check.json');
|
|
52
|
+
const CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000; // re-check at most every 6h
|
|
53
|
+
const FETCH_TIMEOUT_MS = 2500;
|
|
54
|
+
function readPkg() {
|
|
55
|
+
try {
|
|
56
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(configManager_1.APP_ROOT, 'package.json'), 'utf-8'));
|
|
57
|
+
if (pkg && pkg.name && pkg.version)
|
|
58
|
+
return { name: pkg.name, version: pkg.version };
|
|
59
|
+
}
|
|
60
|
+
catch { /* ignore */ }
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function parseVer(v) {
|
|
64
|
+
// Strip any pre-release/build suffix, then split into numeric parts.
|
|
65
|
+
return v.split('-')[0].split('.').map((n) => parseInt(n, 10) || 0);
|
|
66
|
+
}
|
|
67
|
+
/** True if `latest` is a higher semver than `current`. */
|
|
68
|
+
function isNewer(latest, current) {
|
|
69
|
+
const a = parseVer(latest);
|
|
70
|
+
const b = parseVer(current);
|
|
71
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
72
|
+
const x = a[i] || 0;
|
|
73
|
+
const y = b[i] || 0;
|
|
74
|
+
if (x > y)
|
|
75
|
+
return true;
|
|
76
|
+
if (x < y)
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
function fetchLatest(name) {
|
|
82
|
+
// dist-tags is a tiny payload: {"latest":"1.2.3", ...}
|
|
83
|
+
const url = `https://registry.npmjs.org/-/package/${name.replace('/', '%2F')}/dist-tags`;
|
|
84
|
+
return new Promise((resolve) => {
|
|
85
|
+
const req = https.get(url, { timeout: FETCH_TIMEOUT_MS }, (res) => {
|
|
86
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
87
|
+
res.resume();
|
|
88
|
+
resolve(null);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
let data = '';
|
|
92
|
+
res.on('data', (c) => { data += c; });
|
|
93
|
+
res.on('end', () => {
|
|
94
|
+
try {
|
|
95
|
+
resolve(JSON.parse(data).latest || null);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
resolve(null);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
req.on('error', () => resolve(null));
|
|
103
|
+
req.on('timeout', () => { req.destroy(); resolve(null); });
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
function readCache() {
|
|
107
|
+
try {
|
|
108
|
+
const c = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf-8'));
|
|
109
|
+
if (c && typeof c.latest === 'string' && typeof c.checkedAt === 'number')
|
|
110
|
+
return c;
|
|
111
|
+
}
|
|
112
|
+
catch { /* ignore */ }
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
function writeCache(latest) {
|
|
116
|
+
try {
|
|
117
|
+
fs.writeFileSync(CACHE_FILE, JSON.stringify({ latest, checkedAt: Date.now() }), 'utf-8');
|
|
118
|
+
}
|
|
119
|
+
catch { /* ignore */ }
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Returns update info, or null if it couldn't be determined.
|
|
123
|
+
* Pass force=true (e.g. for an explicit /update command) to bypass the cache.
|
|
124
|
+
*/
|
|
125
|
+
async function checkForUpdate(force = false) {
|
|
126
|
+
const pkg = readPkg();
|
|
127
|
+
if (!pkg)
|
|
128
|
+
return null;
|
|
129
|
+
if (!force) {
|
|
130
|
+
const cached = readCache();
|
|
131
|
+
if (cached && Date.now() - cached.checkedAt < CHECK_INTERVAL_MS) {
|
|
132
|
+
return { name: pkg.name, current: pkg.version, latest: cached.latest, updateAvailable: isNewer(cached.latest, pkg.version) };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const latest = await fetchLatest(pkg.name);
|
|
136
|
+
if (!latest)
|
|
137
|
+
return null;
|
|
138
|
+
writeCache(latest);
|
|
139
|
+
return { name: pkg.name, current: pkg.version, latest, updateAvailable: isNewer(latest, pkg.version) };
|
|
140
|
+
}
|
package/dist/utils/helpers.js
CHANGED
|
@@ -42,9 +42,11 @@ exports.getDirSize = getDirSize;
|
|
|
42
42
|
exports.checkJava = checkJava;
|
|
43
43
|
exports.findInstalledJavas = findInstalledJavas;
|
|
44
44
|
exports.getSystemStats = getSystemStats;
|
|
45
|
+
exports.checkForUpdates = checkForUpdates;
|
|
45
46
|
const fs = __importStar(require("fs"));
|
|
46
47
|
const os = __importStar(require("os"));
|
|
47
48
|
const child_process_1 = require("child_process");
|
|
49
|
+
const https = __importStar(require("https"));
|
|
48
50
|
/**
|
|
49
51
|
* Detects the runtime OS environment: Windows, WSL, or Linux
|
|
50
52
|
*/
|
|
@@ -363,3 +365,66 @@ function getSystemStats() {
|
|
|
363
365
|
uptimeSeconds: Math.floor(os.uptime()),
|
|
364
366
|
};
|
|
365
367
|
}
|
|
368
|
+
/**
|
|
369
|
+
* Checks npm registry for a newer version of the CLI package.
|
|
370
|
+
* Returns the latest version string if a newer version is available, or null otherwise.
|
|
371
|
+
*/
|
|
372
|
+
function checkForUpdates(currentVersion) {
|
|
373
|
+
return new Promise((resolve) => {
|
|
374
|
+
const options = {
|
|
375
|
+
hostname: 'registry.npmjs.org',
|
|
376
|
+
path: '/@woopsy/mcpanel/latest',
|
|
377
|
+
method: 'GET',
|
|
378
|
+
timeout: 2000,
|
|
379
|
+
headers: {
|
|
380
|
+
'User-Agent': 'mcpanel-cli',
|
|
381
|
+
},
|
|
382
|
+
};
|
|
383
|
+
const req = https.get(options, (res) => {
|
|
384
|
+
if (res.statusCode !== 200) {
|
|
385
|
+
resolve(null);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
let data = '';
|
|
389
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
390
|
+
res.on('end', () => {
|
|
391
|
+
try {
|
|
392
|
+
const parsed = JSON.parse(data);
|
|
393
|
+
const latest = parsed.version;
|
|
394
|
+
if (latest && isNewerVersion(currentVersion, latest)) {
|
|
395
|
+
resolve(latest);
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
resolve(null);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
catch {
|
|
402
|
+
resolve(null);
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
req.on('timeout', () => {
|
|
407
|
+
req.destroy();
|
|
408
|
+
resolve(null);
|
|
409
|
+
});
|
|
410
|
+
req.on('error', () => {
|
|
411
|
+
resolve(null);
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Basic semver comparison (a < b)
|
|
417
|
+
*/
|
|
418
|
+
function isNewerVersion(current, latest) {
|
|
419
|
+
const cParts = current.split('.').map(Number);
|
|
420
|
+
const lParts = latest.split('.').map(Number);
|
|
421
|
+
for (let i = 0; i < 3; i++) {
|
|
422
|
+
const c = cParts[i] || 0;
|
|
423
|
+
const l = lParts[i] || 0;
|
|
424
|
+
if (l > c)
|
|
425
|
+
return true;
|
|
426
|
+
if (c > l)
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
return false;
|
|
430
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@woopsy/mcpanel",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "MCPANEL — a terminal-based, single-server Minecraft server manager with an Arch/neofetch-style UI, live logs, backups, plugins and Playit.gg tunnels.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|