@powernukkitx/cli 1.0.0 → 1.0.2

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.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +186 -203
  3. package/dist/cli.js +34 -46
  4. package/dist/commands/backup.d.ts +2 -2
  5. package/dist/commands/backup.js +23 -24
  6. package/dist/commands/config.d.ts +1 -1
  7. package/dist/commands/config.js +60 -61
  8. package/dist/commands/console.d.ts +2 -2
  9. package/dist/commands/console.js +98 -99
  10. package/dist/commands/doctor.d.ts +2 -2
  11. package/dist/commands/doctor.js +97 -98
  12. package/dist/commands/info.d.ts +2 -2
  13. package/dist/commands/info.js +41 -42
  14. package/dist/commands/init.d.ts +2 -2
  15. package/dist/commands/init.js +87 -66
  16. package/dist/commands/install.js +45 -0
  17. package/dist/commands/logs.d.ts +2 -2
  18. package/dist/commands/logs.js +60 -61
  19. package/dist/commands/menu.js +94 -86
  20. package/dist/commands/plugin/index.d.ts +2 -2
  21. package/dist/commands/plugin/index.js +13 -13
  22. package/dist/commands/plugin/info.d.ts +2 -2
  23. package/dist/commands/plugin/info.js +39 -40
  24. package/dist/commands/plugin/install.d.ts +2 -2
  25. package/dist/commands/plugin/install.js +56 -57
  26. package/dist/commands/plugin/installed.d.ts +2 -2
  27. package/dist/commands/plugin/installed.js +41 -42
  28. package/dist/commands/plugin/list.d.ts +2 -2
  29. package/dist/commands/plugin/list.js +47 -49
  30. package/dist/commands/plugin/remove.d.ts +2 -2
  31. package/dist/commands/plugin/remove.js +44 -45
  32. package/dist/commands/plugin/search.d.ts +2 -2
  33. package/dist/commands/plugin/search.js +11 -12
  34. package/dist/commands/plugin/update.d.ts +2 -2
  35. package/dist/commands/plugin/update.js +52 -53
  36. package/dist/commands/start.d.ts +2 -2
  37. package/dist/commands/start.js +74 -75
  38. package/dist/commands/stop.d.ts +2 -2
  39. package/dist/commands/stop.js +26 -27
  40. package/dist/commands/update.d.ts +2 -2
  41. package/dist/commands/update.js +34 -35
  42. package/dist/commands/use.d.ts +2 -2
  43. package/dist/commands/use.js +39 -40
  44. package/dist/infra/http.js +119 -121
  45. package/dist/infra/paths.js +15 -20
  46. package/dist/infra/store.js +24 -27
  47. package/dist/services/backup.js +34 -40
  48. package/dist/services/plugins.js +139 -111
  49. package/dist/services/process.js +41 -43
  50. package/dist/services/release.js +51 -38
  51. package/dist/services/server.js +81 -89
  52. package/dist/ui/output.js +67 -71
  53. package/dist/ui/prompts.js +26 -30
  54. package/dist/ui/theme.js +16 -18
  55. package/package.json +44 -46
  56. package/dist/bundle.js +0 -18
  57. package/dist/commands/cfg.d.ts +0 -2
  58. package/dist/commands/cfg.js +0 -61
  59. package/dist/commands/lang.d.ts +0 -2
  60. package/dist/commands/lang.js +0 -28
  61. package/dist/commands/plugin.d.ts +0 -1
  62. package/dist/commands/plugin.js +0 -1
  63. package/dist/core/config.d.ts +0 -13
  64. package/dist/core/config.js +0 -31
  65. package/dist/core/network.d.ts +0 -13
  66. package/dist/core/network.js +0 -139
  67. package/dist/core/output.d.ts +0 -33
  68. package/dist/core/output.js +0 -75
  69. package/dist/core/process.d.ts +0 -10
  70. package/dist/core/process.js +0 -53
  71. package/dist/i18n/en.json +0 -174
  72. package/dist/i18n/fr.json +0 -174
  73. package/dist/i18n/index.js +0 -58
  74. package/dist/index.d.ts +0 -1
  75. package/dist/index.js +0 -1
  76. package/dist/lang/en.json +0 -146
  77. package/dist/lang/fr.json +0 -146
  78. package/dist/lang/index.d.ts +0 -7
  79. package/dist/lang/index.js +0 -83
  80. package/dist/main.d.ts +0 -2
  81. package/dist/main.js +0 -28
  82. package/dist/plugins.json +0 -66
  83. package/dist/types.d.ts +0 -61
  84. package/dist/types.js +0 -1
  85. package/dist/utils/api.d.ts +0 -9
  86. package/dist/utils/api.js +0 -24
  87. package/dist/utils/github.d.ts +0 -20
  88. package/dist/utils/github.js +0 -60
  89. package/dist/utils/logger.d.ts +0 -15
  90. package/dist/utils/logger.js +0 -72
  91. package/dist/utils/pause.d.ts +0 -1
  92. package/dist/utils/pause.js +0 -6
  93. package/dist/utils/plugins.d.ts +0 -4
  94. package/dist/utils/plugins.js +0 -34
  95. package/dist/utils/server.d.ts +0 -3
  96. package/dist/utils/server.js +0 -32
  97. package/dist/utils/updater.d.ts +0 -1
  98. package/dist/utils/updater.js +0 -86
@@ -1,61 +1,60 @@
1
- import { existsSync, readFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { defineCommand } from 'citty';
4
- import { intro, outro, log } from '../ui/output.js';
5
- import { c } from '../ui/theme.js';
6
- import { resolveServerDir } from '../services/server.js';
7
- import { t } from '../i18n/index.js';
8
- function colorVal(val) {
9
- if (val === 'true' || val === 'on')
10
- return c.green(val);
11
- if (val === 'false' || val === 'off')
12
- return c.red(val);
13
- if (/^\d+(\.\d+)?$/.test(val))
14
- return c.cyan(val);
15
- return val;
16
- }
17
- export const configCmd = defineCommand({
18
- meta: { description: 'View server configuration' },
19
- args: {},
20
- async run() {
21
- intro(`⚙️ ${t('config.title')}`);
22
- const serverPath = await resolveServerDir();
23
- if (!serverPath) {
24
- log.error(t('config.noServer'));
25
- process.exit(1);
26
- }
27
- let cfgPath = join(serverPath, 'pnx.yml');
28
- if (!existsSync(cfgPath))
29
- cfgPath = join(serverPath, 'server.properties');
30
- if (!existsSync(cfgPath)) {
31
- log.error(t('config.notFound'));
32
- process.exit(1);
33
- }
34
- let content = readFileSync(cfgPath, 'utf-8');
35
- if (content.charCodeAt(0) === 0xfeff)
36
- content = content.slice(1);
37
- const sep = cfgPath.endsWith('.yml') ? ':' : '=';
38
- for (const line of content.split('\n')) {
39
- const trimmed = line.trim();
40
- if (!trimmed) {
41
- log.raw('');
42
- continue;
43
- }
44
- if (trimmed.startsWith('#')) {
45
- log.raw(` ${c.dim(line)}`);
46
- continue;
47
- }
48
- const idx = trimmed.indexOf(sep);
49
- if (idx > 0) {
50
- const key = trimmed.slice(0, idx);
51
- const val = trimmed.slice(idx + 1).trim();
52
- const indent = line.match(/^(\s*)/)?.[1] ?? '';
53
- log.raw(` ${indent}${c.blue(key)}${sep}${val ? ' ' + colorVal(val) : ''}`);
54
- }
55
- else
56
- log.raw(` ${c.dim(trimmed)}`);
57
- }
58
- log.info(t('config.editHint', cfgPath));
59
- outro('');
60
- },
61
- });
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { defineCommand } from 'citty';
4
+ import { intro, outro, log } from '../ui/output.js';
5
+ import { c } from '../ui/theme.js';
6
+ import { resolveServerDir } from '../services/server.js';
7
+ function colorVal(val) {
8
+ if (val === 'true' || val === 'on')
9
+ return c.green(val);
10
+ if (val === 'false' || val === 'off')
11
+ return c.red(val);
12
+ if (/^\d+(\.\d+)?$/.test(val))
13
+ return c.cyan(val);
14
+ return val;
15
+ }
16
+ export const configCmd = defineCommand({
17
+ meta: { description: 'View server configuration' },
18
+ args: {},
19
+ async run() {
20
+ intro(`⚙️ ${"Configuration"}`);
21
+ const serverPath = await resolveServerDir();
22
+ if (!serverPath) {
23
+ log.error("No server detected");
24
+ process.exit(1);
25
+ }
26
+ let cfgPath = join(serverPath, 'pnx.yml');
27
+ if (!existsSync(cfgPath))
28
+ cfgPath = join(serverPath, 'server.properties');
29
+ if (!existsSync(cfgPath)) {
30
+ log.error("No config file found");
31
+ process.exit(1);
32
+ }
33
+ let content = readFileSync(cfgPath, 'utf-8');
34
+ if (content.charCodeAt(0) === 0xfeff)
35
+ content = content.slice(1);
36
+ const sep = cfgPath.endsWith('.yml') ? ':' : '=';
37
+ for (const line of content.split('\n')) {
38
+ const trimmed = line.trim();
39
+ if (!trimmed) {
40
+ log.raw('');
41
+ continue;
42
+ }
43
+ if (trimmed.startsWith('#')) {
44
+ log.raw(` ${c.dim(line)}`);
45
+ continue;
46
+ }
47
+ const idx = trimmed.indexOf(sep);
48
+ if (idx > 0) {
49
+ const key = trimmed.slice(0, idx);
50
+ const val = trimmed.slice(idx + 1).trim();
51
+ const indent = line.match(/^(\s*)/)?.[1] ?? '';
52
+ log.raw(` ${indent}${c.blue(key)}${sep}${val ? ' ' + colorVal(val) : ''}`);
53
+ }
54
+ else
55
+ log.raw(` ${c.dim(trimmed)}`);
56
+ }
57
+ log.info(`Edit ${cfgPath} to change these values`);
58
+ outro('');
59
+ },
60
+ });
@@ -1,2 +1,2 @@
1
- import type { CommandDef } from 'citty';
2
- export declare const consoleCmd: CommandDef;
1
+ import type { CommandDef } from 'citty';
2
+ export declare const consoleCmd: CommandDef;
@@ -1,99 +1,98 @@
1
- import { existsSync, readFileSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { createInterface } from 'node:readline';
4
- import { createConnection } from 'node:net';
5
- import { defineCommand } from 'citty';
6
- import { intro, outro, log } from '../ui/output.js';
7
- import { resolveServerDir } from '../services/server.js';
8
- import { t } from '../i18n/index.js';
9
- function parseServerProperties(serverPath) {
10
- const propsPath = join(serverPath, 'server.properties');
11
- if (!existsSync(propsPath))
12
- return {};
13
- const props = {};
14
- for (const line of readFileSync(propsPath, 'utf-8').split('\n')) {
15
- if (line.startsWith('#') || !line.includes('='))
16
- continue;
17
- const [k, ...v] = line.split('=');
18
- props[k.trim()] = v.join('=').trim();
19
- }
20
- return props;
21
- }
22
- function rconPacket(id, type, body) {
23
- const bodyBuf = Buffer.from(body + '\0\0', 'utf-8');
24
- const packet = Buffer.alloc(12 + bodyBuf.length);
25
- packet.writeInt32LE(8 + bodyBuf.length, 0);
26
- packet.writeInt32LE(id, 4);
27
- packet.writeInt32LE(type, 8);
28
- bodyBuf.copy(packet, 12);
29
- return packet;
30
- }
31
- function connectRcon(host, port, password) {
32
- return new Promise(resolve => {
33
- const socket = createConnection({ host, port }, () => socket.write(rconPacket(1, 3, password)));
34
- socket.once('data', data => {
35
- if (data.readInt32LE(4) === -1) {
36
- socket.destroy();
37
- resolve(null);
38
- return;
39
- }
40
- resolve({
41
- send: (cmd) => {
42
- socket.write(rconPacket(2, 2, cmd));
43
- socket.once('data', d => {
44
- const resp = d.slice(12, d.length - 2).toString('utf-8');
45
- if (resp)
46
- process.stdout.write(resp + '\n');
47
- });
48
- },
49
- close: () => socket.destroy(),
50
- });
51
- });
52
- socket.on('error', () => resolve(null));
53
- setTimeout(() => resolve(null), 3000);
54
- });
55
- }
56
- export const consoleCmd = defineCommand({
57
- meta: { description: 'Attach to server console via RCON' },
58
- args: {},
59
- async run() {
60
- intro(`💻 ${t('console.title')}`);
61
- const serverPath = await resolveServerDir();
62
- if (!serverPath) {
63
- log.error(t('console.noServer'));
64
- process.exit(1);
65
- }
66
- const props = parseServerProperties(serverPath);
67
- const enabled = props['enable-rcon'] === 'true';
68
- const port = parseInt(props['rcon.port'] ?? '25575');
69
- const password = props['rcon.password'] ?? '';
70
- if (enabled && password) {
71
- log.step(t('console.connecting'));
72
- const rcon = await connectRcon('127.0.0.1', port, password);
73
- if (rcon) {
74
- log.success(t('console.connected'));
75
- const rl = createInterface({ input: process.stdin, output: process.stdout, prompt: t('console.prompt') });
76
- rl.prompt();
77
- rl.on('line', line => {
78
- const cmd = line.trim();
79
- if (cmd === 'exit') {
80
- rcon.close();
81
- rl.close();
82
- outro(t('console.disconnected'));
83
- process.exit(0);
84
- }
85
- rcon.send(cmd);
86
- rl.prompt();
87
- });
88
- rl.on('close', () => { rcon.close(); process.exit(0); });
89
- return;
90
- }
91
- }
92
- log.warn(t('console.rconDisabled'));
93
- log.info(t('console.rconEnable'));
94
- log.dim('enable-rcon=true');
95
- log.dim('rcon.port=25575');
96
- log.dim('rcon.password=your_password');
97
- process.exit(1);
98
- },
99
- });
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { createInterface } from 'node:readline';
4
+ import { createConnection } from 'node:net';
5
+ import { defineCommand } from 'citty';
6
+ import { intro, outro, log } from '../ui/output.js';
7
+ import { resolveServerDir } from '../services/server.js';
8
+ function parseServerProperties(serverPath) {
9
+ const propsPath = join(serverPath, 'server.properties');
10
+ if (!existsSync(propsPath))
11
+ return {};
12
+ const props = {};
13
+ for (const line of readFileSync(propsPath, 'utf-8').split('\n')) {
14
+ if (line.startsWith('#') || !line.includes('='))
15
+ continue;
16
+ const [k, ...v] = line.split('=');
17
+ props[k.trim()] = v.join('=').trim();
18
+ }
19
+ return props;
20
+ }
21
+ function rconPacket(id, type, body) {
22
+ const bodyBuf = Buffer.from(body + '\0\0', 'utf-8');
23
+ const packet = Buffer.alloc(12 + bodyBuf.length);
24
+ packet.writeInt32LE(8 + bodyBuf.length, 0);
25
+ packet.writeInt32LE(id, 4);
26
+ packet.writeInt32LE(type, 8);
27
+ bodyBuf.copy(packet, 12);
28
+ return packet;
29
+ }
30
+ function connectRcon(host, port, password) {
31
+ return new Promise(resolve => {
32
+ const socket = createConnection({ host, port }, () => socket.write(rconPacket(1, 3, password)));
33
+ socket.once('data', data => {
34
+ if (data.readInt32LE(4) === -1) {
35
+ socket.destroy();
36
+ resolve(null);
37
+ return;
38
+ }
39
+ resolve({
40
+ send: (cmd) => {
41
+ socket.write(rconPacket(2, 2, cmd));
42
+ socket.once('data', d => {
43
+ const resp = d.slice(12, d.length - 2).toString('utf-8');
44
+ if (resp)
45
+ process.stdout.write(resp + '\n');
46
+ });
47
+ },
48
+ close: () => socket.destroy(),
49
+ });
50
+ });
51
+ socket.on('error', () => resolve(null));
52
+ setTimeout(() => resolve(null), 3000);
53
+ });
54
+ }
55
+ export const consoleCmd = defineCommand({
56
+ meta: { description: 'Attach to server console via RCON' },
57
+ args: {},
58
+ async run() {
59
+ intro(`💻 ${"Server console"}`);
60
+ const serverPath = await resolveServerDir();
61
+ if (!serverPath) {
62
+ log.error("No server detected");
63
+ process.exit(1);
64
+ }
65
+ const props = parseServerProperties(serverPath);
66
+ const enabled = props['enable-rcon'] === 'true';
67
+ const port = parseInt(props['rcon.port'] ?? '25575');
68
+ const password = props['rcon.password'] ?? '';
69
+ if (enabled && password) {
70
+ log.step("Connecting via RCON...");
71
+ const rcon = await connectRcon('127.0.0.1', port, password);
72
+ if (rcon) {
73
+ log.success("Connected — type commands (exit to quit)");
74
+ const rl = createInterface({ input: process.stdin, output: process.stdout, prompt: "> " });
75
+ rl.prompt();
76
+ rl.on('line', line => {
77
+ const cmd = line.trim();
78
+ if (cmd === 'exit') {
79
+ rcon.close();
80
+ rl.close();
81
+ outro("Disconnected");
82
+ process.exit(0);
83
+ }
84
+ rcon.send(cmd);
85
+ rl.prompt();
86
+ });
87
+ rl.on('close', () => { rcon.close(); process.exit(0); });
88
+ return;
89
+ }
90
+ }
91
+ log.warn("RCON disabled in server.properties");
92
+ log.info("Enable enable-rcon=true in server.properties");
93
+ log.dim('enable-rcon=true');
94
+ log.dim('rcon.port=25575');
95
+ log.dim('rcon.password=your_password');
96
+ process.exit(1);
97
+ },
98
+ });
@@ -1,2 +1,2 @@
1
- import type { CommandDef } from 'citty';
2
- export declare const doctorCmd: CommandDef;
1
+ import type { CommandDef } from 'citty';
2
+ export declare const doctorCmd: CommandDef;
@@ -1,98 +1,97 @@
1
- import { existsSync, statSync, readdirSync } from 'node:fs';
2
- import { statfs } from 'node:fs/promises';
3
- import { join } from 'node:path';
4
- import { spawnSync } from 'node:child_process';
5
- import { createSocket } from 'node:dgram';
6
- import { freemem, totalmem } from 'node:os';
7
- import { defineCommand } from 'citty';
8
- import { intro, outro, log, section, spin, isJson, json } from '../ui/output.js';
9
- import { resolveServerDir, checkJava } from '../services/server.js';
10
- import { JAR_NAME } from '../infra/paths.js';
11
- import { t } from '../i18n/index.js';
12
- function checkPort(port) {
13
- return new Promise(res => {
14
- const s = createSocket('udp4');
15
- s.once('error', () => { s.close(); res(false); });
16
- s.bind(port, '0.0.0.0', () => { s.close(); res(true); });
17
- });
18
- }
19
- function pnxVersion(jarPath) {
20
- try {
21
- const r = spawnSync('java', ['-jar', jarPath, '--version'], { timeout: 3000, encoding: 'utf8' });
22
- return ((r.stdout || '') + (r.stderr || '')).match(/(\d+\.\d+\.\d+[\w.-]*)/)?.[1] ?? null;
23
- }
24
- catch {
25
- return null;
26
- }
27
- }
28
- async function diskFreeMb(dir) {
29
- try {
30
- const s = await statfs(dir);
31
- return Math.round((s.bfree * s.bsize) / 1024 / 1024);
32
- }
33
- catch {
34
- return null;
35
- }
36
- }
37
- export const doctorCmd = defineCommand({
38
- meta: { description: 'Diagnose server health' },
39
- args: {},
40
- async run() {
41
- const sp = spin(t('doctor.checking'));
42
- const java = await checkJava();
43
- const serverPath = await resolveServerDir();
44
- const jarPath = serverPath ? join(serverPath, JAR_NAME) : null;
45
- const jarExists = jarPath ? existsSync(jarPath) : false;
46
- const version = jarPath && jarExists ? pnxVersion(jarPath) : null;
47
- const pluginsDir = serverPath ? join(serverPath, 'plugins') : null;
48
- const worldsDir = serverPath ? join(serverPath, 'worlds') : null;
49
- const pluginCount = pluginsDir && existsSync(pluginsDir)
50
- ? readdirSync(pluginsDir).filter(f => f.endsWith('.jar')).length : 0;
51
- const worldCount = worldsDir && existsSync(worldsDir)
52
- ? readdirSync(worldsDir).filter(f => statSync(join(worldsDir, f)).isDirectory()).length : 0;
53
- const freeMb = Math.round(freemem() / 1024 / 1024);
54
- const totalMb = Math.round(totalmem() / 1024 / 1024);
55
- const disk = serverPath ? await diskFreeMb(serverPath) : null;
56
- const portFree = await checkPort(19132);
57
- sp.stop(t('doctor.done'));
58
- if (isJson())
59
- json({
60
- java: { ok: java.ok, version: java.raw, vendor: java.vendor },
61
- server: { found: !!serverPath, path: serverPath, pnxVersion: version },
62
- ram: { freeMb, totalMb },
63
- disk: disk !== null ? { freeMb: disk } : null,
64
- port: { free: portFree, port: 19132 },
65
- plugins: { count: pluginCount },
66
- worlds: { count: worldCount },
67
- });
68
- intro(`🩺 ${t('doctor.title')}`);
69
- section(t('doctor.java'));
70
- if (java.ok)
71
- log.success(`${java.raw} (${java.vendor})`);
72
- else {
73
- log.error(t('doctor.javaMissing'));
74
- log.info('https://adoptium.net/');
75
- }
76
- section(t('doctor.server'));
77
- if (serverPath) {
78
- log.success(serverPath);
79
- if (jarExists)
80
- log.success(`${JAR_NAME}${version ? ` v${version}` : ''}`);
81
- else
82
- log.error(t('doctor.jarMissing'));
83
- }
84
- else
85
- log.error(t('doctor.noServer'));
86
- section(t('doctor.plugins'));
87
- log.info(`Plugins: ${pluginCount} | Worlds: ${worldCount}`);
88
- section(t('doctor.system'));
89
- log.info(`RAM: ${freeMb} Mo / ${totalMb} Mo`);
90
- if (disk !== null)
91
- log.info(`Disk: ${disk} Mo`);
92
- if (portFree)
93
- log.success(t('doctor.portFree', '19132'));
94
- else
95
- log.warn(t('doctor.portBusy', '19132'));
96
- outro(t('doctor.done'));
97
- },
98
- });
1
+ import { existsSync, statSync, readdirSync } from 'node:fs';
2
+ import { statfs } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { createSocket } from 'node:dgram';
6
+ import { freemem, totalmem } from 'node:os';
7
+ import { defineCommand } from 'citty';
8
+ import { intro, outro, log, section, spin, isJson, json } from '../ui/output.js';
9
+ import { resolveServerDir, checkJava } from '../services/server.js';
10
+ import { JAR_NAME } from '../infra/paths.js';
11
+ function checkPort(port) {
12
+ return new Promise(res => {
13
+ const s = createSocket('udp4');
14
+ s.once('error', () => { s.close(); res(false); });
15
+ s.bind(port, '0.0.0.0', () => { s.close(); res(true); });
16
+ });
17
+ }
18
+ function pnxVersion(jarPath) {
19
+ try {
20
+ const r = spawnSync('java', ['-jar', jarPath, '--version'], { timeout: 3000, encoding: 'utf8' });
21
+ return ((r.stdout || '') + (r.stderr || '')).match(/(\d+\.\d+\.\d+[\w.-]*)/)?.[1] ?? null;
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ async function diskFreeMb(dir) {
28
+ try {
29
+ const s = await statfs(dir);
30
+ return Math.round((s.bfree * s.bsize) / 1024 / 1024);
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ export const doctorCmd = defineCommand({
37
+ meta: { description: 'Diagnose server health' },
38
+ args: {},
39
+ async run() {
40
+ const sp = spin("Running diagnostics...");
41
+ const java = await checkJava();
42
+ const serverPath = await resolveServerDir();
43
+ const jarPath = serverPath ? join(serverPath, JAR_NAME) : null;
44
+ const jarExists = jarPath ? existsSync(jarPath) : false;
45
+ const version = jarPath && jarExists ? pnxVersion(jarPath) : null;
46
+ const pluginsDir = serverPath ? join(serverPath, 'plugins') : null;
47
+ const worldsDir = serverPath ? join(serverPath, 'worlds') : null;
48
+ const pluginCount = pluginsDir && existsSync(pluginsDir)
49
+ ? readdirSync(pluginsDir).filter(f => f.endsWith('.jar')).length : 0;
50
+ const worldCount = worldsDir && existsSync(worldsDir)
51
+ ? readdirSync(worldsDir).filter(f => statSync(join(worldsDir, f)).isDirectory()).length : 0;
52
+ const freeMb = Math.round(freemem() / 1024 / 1024);
53
+ const totalMb = Math.round(totalmem() / 1024 / 1024);
54
+ const disk = serverPath ? await diskFreeMb(serverPath) : null;
55
+ const portFree = await checkPort(19132);
56
+ sp.stop("Diagnostics complete");
57
+ if (isJson())
58
+ json({
59
+ java: { ok: java.ok, version: java.raw, vendor: java.vendor },
60
+ server: { found: !!serverPath, path: serverPath, pnxVersion: version },
61
+ ram: { freeMb, totalMb },
62
+ disk: disk !== null ? { freeMb: disk } : null,
63
+ port: { free: portFree, port: 19132 },
64
+ plugins: { count: pluginCount },
65
+ worlds: { count: worldCount },
66
+ });
67
+ intro(`🩺 ${"Server diagnostics"}`);
68
+ section("Java");
69
+ if (java.ok)
70
+ log.success(`${java.raw} (${java.vendor})`);
71
+ else {
72
+ log.error("Java 21+ required");
73
+ log.info('https://adoptium.net/');
74
+ }
75
+ section("Server");
76
+ if (serverPath) {
77
+ log.success(serverPath);
78
+ if (jarExists)
79
+ log.success(`${JAR_NAME}${version ? ` v${version}` : ''}`);
80
+ else
81
+ log.error("powernukkitx.jar missing");
82
+ }
83
+ else
84
+ log.error("No server — run 'pnx init'");
85
+ section("Plugins & Worlds");
86
+ log.info(`Plugins: ${pluginCount} | Worlds: ${worldCount}`);
87
+ section("System");
88
+ log.info(`RAM: ${freeMb} MB / ${totalMb} MB`);
89
+ if (disk !== null)
90
+ log.info(`Disk: ${disk} MB`);
91
+ if (portFree)
92
+ log.success(`Port ${'19132'} available`);
93
+ else
94
+ log.warn(`Port ${'19132'} busy`);
95
+ outro("Diagnostics complete");
96
+ },
97
+ });
@@ -1,2 +1,2 @@
1
- import type { CommandDef } from 'citty';
2
- export declare const infoCmd: CommandDef;
1
+ import type { CommandDef } from 'citty';
2
+ export declare const infoCmd: CommandDef;