@powernukkitx/cli 0.0.3 → 1.0.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.
Files changed (82) hide show
  1. package/README.md +203 -177
  2. package/dist/bundle.js +3 -18856
  3. package/dist/cli.js +46 -0
  4. package/dist/commands/backup.d.ts +2 -1
  5. package/dist/commands/backup.js +21 -71
  6. package/dist/commands/cfg.d.ts +2 -0
  7. package/dist/commands/cfg.js +61 -0
  8. package/dist/commands/config.js +52 -105
  9. package/dist/commands/console.d.ts +2 -0
  10. package/dist/commands/console.js +99 -0
  11. package/dist/commands/doctor.d.ts +2 -1
  12. package/dist/commands/doctor.js +87 -108
  13. package/dist/commands/info.d.ts +2 -1
  14. package/dist/commands/info.js +39 -33
  15. package/dist/commands/init.d.ts +2 -4
  16. package/dist/commands/init.js +58 -65
  17. package/dist/commands/lang.d.ts +2 -0
  18. package/dist/commands/lang.js +28 -0
  19. package/dist/commands/logs.d.ts +2 -0
  20. package/dist/commands/logs.js +61 -0
  21. package/dist/commands/menu.js +86 -0
  22. package/dist/commands/plugin/index.d.ts +2 -0
  23. package/dist/commands/plugin/index.js +13 -0
  24. package/dist/commands/plugin/info.d.ts +2 -0
  25. package/dist/commands/plugin/info.js +40 -0
  26. package/dist/commands/plugin/install.d.ts +2 -0
  27. package/dist/commands/plugin/install.js +57 -0
  28. package/dist/commands/plugin/installed.d.ts +2 -0
  29. package/dist/commands/plugin/installed.js +42 -0
  30. package/dist/commands/plugin/list.d.ts +2 -0
  31. package/dist/commands/plugin/list.js +49 -0
  32. package/dist/commands/plugin/remove.d.ts +2 -0
  33. package/dist/commands/plugin/remove.js +45 -0
  34. package/dist/commands/plugin/search.d.ts +2 -0
  35. package/dist/commands/plugin/search.js +12 -0
  36. package/dist/commands/plugin/update.d.ts +2 -0
  37. package/dist/commands/plugin/update.js +53 -0
  38. package/dist/commands/plugin.d.ts +1 -12
  39. package/dist/commands/plugin.js +1 -385
  40. package/dist/commands/start.d.ts +2 -7
  41. package/dist/commands/start.js +61 -94
  42. package/dist/commands/stop.d.ts +2 -0
  43. package/dist/commands/stop.js +27 -0
  44. package/dist/commands/update.d.ts +2 -5
  45. package/dist/commands/update.js +30 -50
  46. package/dist/commands/use.d.ts +2 -0
  47. package/dist/commands/use.js +40 -0
  48. package/dist/core/config.d.ts +13 -0
  49. package/dist/core/config.js +31 -0
  50. package/dist/core/network.d.ts +13 -0
  51. package/dist/core/network.js +139 -0
  52. package/dist/core/output.d.ts +33 -0
  53. package/dist/core/output.js +75 -0
  54. package/dist/core/process.d.ts +10 -0
  55. package/dist/core/process.js +53 -0
  56. package/dist/i18n/en.json +174 -0
  57. package/dist/i18n/fr.json +174 -0
  58. package/dist/i18n/index.js +58 -0
  59. package/dist/index.d.ts +0 -1
  60. package/dist/index.js +1 -260
  61. package/dist/infra/http.js +121 -0
  62. package/dist/infra/paths.js +20 -0
  63. package/dist/infra/store.js +27 -0
  64. package/dist/lang/en.json +116 -148
  65. package/dist/lang/fr.json +115 -147
  66. package/dist/main.d.ts +2 -0
  67. package/dist/main.js +28 -0
  68. package/dist/services/backup.js +40 -0
  69. package/dist/services/plugins.js +111 -0
  70. package/dist/services/process.js +43 -0
  71. package/dist/services/release.js +38 -0
  72. package/dist/services/server.js +89 -0
  73. package/dist/ui/output.js +71 -0
  74. package/dist/ui/prompts.js +30 -0
  75. package/dist/ui/theme.js +18 -0
  76. package/dist/utils/api.d.ts +3 -12
  77. package/dist/utils/api.js +7 -57
  78. package/dist/utils/github.d.ts +3 -10
  79. package/dist/utils/github.js +27 -154
  80. package/dist/utils/server.d.ts +2 -2
  81. package/dist/utils/server.js +17 -24
  82. package/package.json +15 -32
package/dist/cli.js ADDED
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ import { defineCommand, runMain } from 'citty';
3
+ import { initLang } from './i18n/index.js';
4
+ import { setColorEnabled } from './ui/theme.js';
5
+ // Flags globaux traités avant le routing citty.
6
+ const argv = process.argv.slice(2);
7
+ const langFlag = (() => {
8
+ const i = argv.findIndex(a => a === '--lang');
9
+ if (i >= 0 && argv[i + 1])
10
+ return argv[i + 1];
11
+ const eq = argv.find(a => a.startsWith('--lang='));
12
+ return eq ? eq.split('=')[1] : undefined;
13
+ })();
14
+ if (argv.includes('--no-color'))
15
+ setColorEnabled(false);
16
+ initLang(langFlag);
17
+ const main = defineCommand({
18
+ meta: {
19
+ name: 'pnx',
20
+ version: '1.0.0',
21
+ description: 'PowerNukkitX CLI — Manage your Minecraft Bedrock servers',
22
+ },
23
+ subCommands: {
24
+ init: () => import('./commands/init.js').then(m => m.initCmd),
25
+ start: () => import('./commands/start.js').then(m => m.startCmd),
26
+ stop: () => import('./commands/stop.js').then(m => m.stopCmd),
27
+ update: () => import('./commands/update.js').then(m => m.updateCmd),
28
+ info: () => import('./commands/info.js').then(m => m.infoCmd),
29
+ doctor: () => import('./commands/doctor.js').then(m => m.doctorCmd),
30
+ backup: () => import('./commands/backup.js').then(m => m.backupCmd),
31
+ config: () => import('./commands/config.js').then(m => m.configCmd),
32
+ use: () => import('./commands/use.js').then(m => m.useCmd),
33
+ lang: () => import('./commands/lang.js').then(m => m.langCmd),
34
+ logs: () => import('./commands/logs.js').then(m => m.logsCmd),
35
+ console: () => import('./commands/console.js').then(m => m.consoleCmd),
36
+ plugin: () => import('./commands/plugin/index.js').then(m => m.pluginCmd),
37
+ },
38
+ async run({ args }) {
39
+ // Aucune sous-commande → menu interactif.
40
+ if (args._.length === 0) {
41
+ const { runMenu } = await import('./commands/menu.js');
42
+ await runMenu();
43
+ }
44
+ },
45
+ });
46
+ runMain(main);
@@ -1 +1,2 @@
1
- export declare function backupCmd(): Promise<void>;
1
+ import type { CommandDef } from 'citty';
2
+ export declare const backupCmd: CommandDef;
@@ -1,74 +1,24 @@
1
- import { readdirSync, statSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { mkdir, cp, readdir } from 'node:fs/promises';
4
- import { cwd } from 'node:process';
5
- import { confirm } from '@inquirer/prompts';
6
- import { header, success, error, info, highlight, step } from '../utils/logger.js';
7
- import { t } from '../lang/index.js';
8
- import { resolveServerDir } from '../utils/server.js';
9
- import { pauseForExit } from '../utils/pause.js';
10
- const IGNORE = new Set(['logs', 'cache', 'crash-reports', 'backups', 'powernukkitx.jar.old']);
11
- function getDirSize(dir) {
12
- let total = 0;
13
- try {
14
- const items = readdirSync(dir);
15
- for (const item of items) {
16
- const full = join(dir, item);
17
- const s = statSync(full);
18
- if (s.isDirectory())
19
- total += getDirSize(full);
20
- else
21
- total += s.size;
22
- }
23
- }
24
- catch { /* ignore */ }
25
- return total;
26
- }
27
- function formatSize(bytes) {
28
- if (bytes < 1024)
29
- return `${bytes} B`;
30
- if (bytes < 1024 * 1024)
31
- return `${(bytes / 1024).toFixed(1)} KB`;
32
- return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
33
- }
34
- export async function backupCmd() {
35
- const serverPath = await resolveServerDir(t('backup.selectServer'));
1
+ import { defineCommand } from 'citty';
2
+ import { intro, outro, log, spin } from '../ui/output.js';
3
+ import { resolveServerDir } from '../services/server.js';
4
+ import { createBackup, dirSize, fmtSize } from '../services/backup.js';
5
+ import { t } from '../i18n/index.js';
6
+ export async function runBackup() {
7
+ intro(`💾 ${t('backup.title')}`);
8
+ const serverPath = await resolveServerDir();
36
9
  if (!serverPath) {
37
- error(t('backup.noServer'));
38
- await pauseForExit();
39
- return;
40
- }
41
- if (serverPath === cwd()) {
42
- const ok = await confirm({
43
- message: t('backup.backupCurrent', highlight(serverPath)),
44
- default: true,
45
- });
46
- if (!ok) {
47
- info(t('backup.cancelled'));
48
- await pauseForExit();
49
- return;
50
- }
51
- }
52
- header(`💾 ${t('backup.title')}`);
53
- step(t('backup.collecting'));
54
- const size = getDirSize(serverPath);
55
- info(t('backup.estimatedSize', formatSize(size)));
56
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
57
- const backupDir = join(serverPath, 'backups');
58
- const destDir = join(backupDir, `pnx-backup-${timestamp}`);
59
- await mkdir(destDir, { recursive: true });
60
- step(t('backup.copying'));
61
- const items = await readdir(serverPath);
62
- let count = 0;
63
- for (const item of items) {
64
- if (IGNORE.has(item) || item.startsWith('.'))
65
- continue;
66
- const src = join(serverPath, item);
67
- const dst = join(destDir, item);
68
- await cp(src, dst, { recursive: true, force: true });
69
- count++;
10
+ log.error(t('backup.noServer'));
11
+ process.exit(1);
70
12
  }
71
- const finalSize = getDirSize(destDir);
72
- success(t('backup.done', destDir, formatSize(finalSize)));
73
- await pauseForExit();
13
+ const sp = spin(t('backup.collecting'));
14
+ sp.stop(t('backup.size', fmtSize(dirSize(serverPath))));
15
+ const sp2 = spin(t('backup.copying'));
16
+ const { path, size } = await createBackup(serverPath);
17
+ sp2.stop(t('backup.done', path, fmtSize(size)));
18
+ outro(t('backup.done', path, fmtSize(size)));
74
19
  }
20
+ export const backupCmd = defineCommand({
21
+ meta: { description: 'Create a server backup' },
22
+ args: {},
23
+ run: () => runBackup(),
24
+ });
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from 'citty';
2
+ export declare const configCmd: CommandDef;
@@ -0,0 +1,61 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import chalk from 'chalk';
4
+ import { intro, outro, log } from '../core/output.js';
5
+ import { resolveServerDir } from '../utils/server.js';
6
+ import { t } from '../lang/index.js';
7
+ function colorVal(val) {
8
+ if (val === 'true' || val === 'on')
9
+ return chalk.green(val);
10
+ if (val === 'false' || val === 'off')
11
+ return chalk.red(val);
12
+ if (/^\d+(\.\d+)?$/.test(val))
13
+ return chalk.cyan(val);
14
+ return chalk.white(val);
15
+ }
16
+ export const configCmd = {
17
+ meta: { description: 'View server configuration' },
18
+ args: {},
19
+ async run() {
20
+ intro(`⚙️ ${t('config.title')}`);
21
+ const serverPath = await resolveServerDir();
22
+ if (!serverPath) {
23
+ log.error(t('config.noServer'));
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(t('config.notFound'));
31
+ process.exit(1);
32
+ }
33
+ let content = readFileSync(cfgPath, 'utf-8');
34
+ if (content.charCodeAt(0) === 0xFEFF)
35
+ content = content.slice(1);
36
+ for (const line of content.split('\n')) {
37
+ const trimmed = line.trim();
38
+ if (!trimmed) {
39
+ console.log();
40
+ continue;
41
+ }
42
+ if (trimmed.startsWith('#')) {
43
+ console.log(` ${chalk.dim(line)}`);
44
+ continue;
45
+ }
46
+ const sep = cfgPath.endsWith('.yml') ? ':' : '=';
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 = chalk.dim(line.match(/^(\s*)/)?.[1] ?? '');
52
+ console.log(` ${indent}${chalk.hex('#6C8EBF')(key)}${sep}${val ? ' ' + colorVal(val) : ''}`);
53
+ }
54
+ else {
55
+ console.log(` ${chalk.dim(trimmed)}`);
56
+ }
57
+ }
58
+ log.info(t('config.editHint', cfgPath));
59
+ outro('');
60
+ },
61
+ };
@@ -1,114 +1,61 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- import chalk from 'chalk';
4
- import { header, error, info, highlight, dim } from '../utils/logger.js';
5
- import { t } from '../lang/index.js';
6
- import { resolveServerDir } from '../utils/server.js';
7
- import { pauseForExit } from '../utils/pause.js';
8
- export async function configCmd() {
9
- const serverPath = await resolveServerDir();
10
- if (!serverPath) {
11
- error(t('config.noServer'));
12
- info(t('config.hintInit'));
13
- await pauseForExit();
14
- return;
15
- }
16
- // Prefer pnx.yml, fallback to server.properties
17
- let configPath = join(serverPath, 'pnx.yml');
18
- if (!existsSync(configPath)) {
19
- configPath = join(serverPath, 'server.properties');
20
- if (!existsSync(configPath)) {
21
- error(t('config.notFound'));
22
- info(t('config.hintGenerate'));
23
- await pauseForExit();
24
- return;
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);
25
26
  }
26
- }
27
- const isYml = configPath.endsWith('.yml');
28
- header(`⚙️ ${t('config.title')} ${dim(isYml ? '(pnx.yml)' : '(server.properties)')}`);
29
- let content = readFileSync(configPath, 'utf-8');
30
- // Strip BOM
31
- if (content.charCodeAt(0) === 0xFEFF)
32
- content = content.slice(1);
33
- const lines = content.split('\n');
34
- if (isYml) {
35
- // Display YAML with syntax highlighting
36
- for (let i = 0; i < lines.length; i++) {
37
- const line = lines[i];
38
- if (line.trim() === '' || line.trim().startsWith('#')) {
39
- if (line.trim().startsWith('#')) {
40
- console.log(` ${dim(line)}`);
41
- }
42
- else {
43
- console.log();
44
- }
45
- continue;
46
- }
47
- // Key: value or nested key
48
- const indent = line.match(/^(\s*)/)?.[1] || '';
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')) {
49
39
  const trimmed = line.trim();
50
- const colonIdx = trimmed.indexOf(':');
51
- if (colonIdx > 0) {
52
- const key = trimmed.slice(0, colonIdx);
53
- const rest = trimmed.slice(colonIdx + 1).trim();
54
- const suffix = rest
55
- ? ` ${colorYamlValue(rest)}`
56
- : '';
57
- const prefix = indent ? chalk.dim(indent) : '';
58
- console.log(` ${prefix}${chalk.hex('#6C8EBF')(key)}:${suffix}`);
59
- }
60
- else {
61
- console.log(` ${chalk.dim(indent)}${chalk.white(trimmed)}`);
40
+ if (!trimmed) {
41
+ log.raw('');
42
+ continue;
62
43
  }
63
- }
64
- }
65
- else {
66
- // Display server.properties with highlighting
67
- let inSection = false;
68
- for (let i = 0; i < lines.length; i++) {
69
- const line = lines[i];
70
- if (line.startsWith('#') || line.trim() === '') {
71
- const trimmed = line.replace(/^#\s*/, '').trim();
72
- if (trimmed && !inSection) {
73
- console.log(`\n ${dim(trimmed)}`);
74
- inSection = true;
75
- }
44
+ if (trimmed.startsWith('#')) {
45
+ log.raw(` ${c.dim(line)}`);
76
46
  continue;
77
47
  }
78
- inSection = false;
79
- const eqIdx = line.indexOf('=');
80
- if (eqIdx > 0) {
81
- const key = line.slice(0, eqIdx).trim();
82
- const value = line.slice(eqIdx + 1).trim();
83
- console.log(` ${highlight(key)} ${dim('=')} ${colorPropsValue(value)}`);
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) : ''}`);
84
54
  }
55
+ else
56
+ log.raw(` ${c.dim(trimmed)}`);
85
57
  }
86
- }
87
- console.log(`\n ${dim(''.repeat(40))}`);
88
- info(t('config.editHint', highlight(configPath)));
89
- await pauseForExit();
90
- }
91
- function colorYamlValue(val) {
92
- if (val === 'true')
93
- return chalk.green(val);
94
- if (val === 'false')
95
- return chalk.red(val);
96
- if (/^\d+(\.\d+)?$/.test(val))
97
- return chalk.cyan(val);
98
- if (val.startsWith('[') || val.startsWith('-'))
99
- return chalk.hex('#E040FB')(val);
100
- if (val.startsWith('"') && val.endsWith('"'))
101
- return chalk.green(val);
102
- if (val.startsWith("'") && val.endsWith("'"))
103
- return chalk.green(val);
104
- return chalk.white(val);
105
- }
106
- function colorPropsValue(val) {
107
- if (val === 'true' || val === 'on')
108
- return chalk.green(val);
109
- if (val === 'false' || val === 'off')
110
- return chalk.red(val);
111
- if (/^\d+$/.test(val))
112
- return chalk.cyan(val);
113
- return chalk.white(val);
114
- }
58
+ log.info(t('config.editHint', cfgPath));
59
+ outro('');
60
+ },
61
+ });
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from 'citty';
2
+ export declare const consoleCmd: CommandDef;
@@ -0,0 +1,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
+ 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 +1,2 @@
1
- export declare function doctorCmd(): Promise<void>;
1
+ import type { CommandDef } from 'citty';
2
+ export declare const doctorCmd: CommandDef;