@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
@@ -1,187 +1,60 @@
1
- import https from 'node:https';
2
- import http from 'node:http';
3
- import { randomBytes } from 'node:crypto';
4
- import { createWriteStream, existsSync } from 'node:fs';
1
+ import { fetchJSON } from '../core/network.js';
2
+ import { platform } from 'node:os';
3
+ import { existsSync } from 'node:fs';
5
4
  import { readFile, writeFile, mkdir } from 'node:fs/promises';
6
- import { homedir, platform } from 'node:os';
5
+ import { homedir } from 'node:os';
7
6
  import { join } from 'node:path';
8
- import chalk from 'chalk';
9
- import { step } from './logger.js';
10
- import { t } from '../lang/index.js';
11
7
  const CACHE_DIR = join(homedir(), '.pnx-cli');
12
8
  const CACHE_FILE = join(CACHE_DIR, 'latest-release.json');
13
9
  const CACHE_TTL = 3_600_000;
14
- export async function fetchJSON(url, redirects = 5) {
15
- if (redirects <= 0)
16
- return Promise.reject(new Error('Too many redirects'));
17
- return new Promise((resolve, reject) => {
18
- const mod = url.startsWith('https') ? https : http;
19
- mod.get(url, { timeout: 15_000, headers: { 'User-Agent': 'pnx-cli' } }, (res) => {
20
- const code = res.statusCode ?? 0;
21
- if ([301, 302, 303, 307, 308].includes(code)) {
22
- if (res.headers.location) {
23
- resolve(fetchJSON(res.headers.location, redirects - 1));
24
- return;
25
- }
26
- }
27
- if (code !== 200) {
28
- reject(new Error(`HTTP ${code}`));
29
- return;
30
- }
31
- let data = '';
32
- res.on('data', (chunk) => (data += chunk));
33
- res.on('end', () => {
34
- try {
35
- resolve(JSON.parse(data));
36
- }
37
- catch {
38
- reject(new Error('Invalid JSON response'));
39
- }
40
- });
41
- })
42
- .on('error', reject)
43
- .on('timeout', function () {
44
- this?.destroy();
45
- reject(new Error(t('errors.timeout')));
46
- });
47
- });
48
- }
49
- async function getCachedRelease() {
10
+ async function getCached() {
50
11
  try {
51
12
  if (existsSync(CACHE_FILE)) {
52
- const raw = await readFile(CACHE_FILE, 'utf-8');
53
- const cached = JSON.parse(raw);
54
- if (Date.now() - cached.fetchedAt < CACHE_TTL) {
55
- return cached.data;
56
- }
13
+ const c = JSON.parse(await readFile(CACHE_FILE, 'utf-8'));
14
+ if (Date.now() - c.fetchedAt < CACHE_TTL)
15
+ return c.data;
57
16
  }
58
17
  }
59
- catch { /* ignore */ }
18
+ catch { }
60
19
  return null;
61
20
  }
62
- async function setCachedRelease(data) {
21
+ async function setCache(data) {
63
22
  try {
64
23
  await mkdir(CACHE_DIR, { recursive: true });
65
24
  await writeFile(CACHE_FILE, JSON.stringify({ fetchedAt: Date.now(), data }), 'utf-8');
66
25
  }
67
- catch { /* ignore */ }
26
+ catch { }
68
27
  }
69
28
  export async function getLatestRelease() {
70
- const cached = await getCachedRelease();
29
+ const cached = await getCached();
71
30
  if (cached)
72
31
  return cached;
73
32
  const data = await fetchJSON('https://api.github.com/repos/PowerNukkitX/PowerNukkitX/releases/latest');
74
- await setCachedRelease(data);
33
+ await setCache(data);
75
34
  return data;
76
35
  }
77
- export async function findAsset(release, name) {
78
- return release.assets.find((a) => a.name === name);
79
- }
80
- export async function findPluginAsset(repo, assetName) {
81
- try {
82
- const data = await fetchJSON(`https://api.github.com/repos/${repo}/releases/latest`);
83
- const asset = data.assets.find((a) => a.name === assetName);
84
- if (asset)
85
- return { url: asset.browser_download_url, size: asset.size };
86
- }
87
- catch { /* not found */ }
88
- return null;
36
+ export function findAsset(release, name) {
37
+ return release.assets.find(a => a.name === name);
89
38
  }
90
- export function downloadFile(url, dest, label) {
91
- const fileName = label || dest.split(/[/\\]/).pop() || 'file';
92
- step(chalk.cyan(fileName));
93
- function doGet(targetUrl, redirects = 5) {
94
- if (redirects <= 0)
95
- return Promise.reject(new Error('Too many redirects'));
96
- return new Promise((resolve, reject) => {
97
- const file = createWriteStream(dest);
98
- const mod = targetUrl.startsWith('https') ? https : http;
99
- mod.get(targetUrl, { timeout: 120_000, headers: { 'User-Agent': 'pnx-cli' } }, (res) => {
100
- const code = res.statusCode ?? 0;
101
- if ([301, 302, 303, 307, 308].includes(code)) {
102
- if (res.headers.location) {
103
- file.close();
104
- resolve(doGet(res.headers.location, redirects - 1));
105
- return;
106
- }
107
- }
108
- if (code !== 200) {
109
- file.close();
110
- reject(new Error(`HTTP ${code}`));
111
- return;
112
- }
113
- const total = parseInt(res.headers['content-length'] || '0', 10);
114
- let downloaded = 0;
115
- let lastPercent = -1;
116
- res.on('data', (chunk) => {
117
- downloaded += chunk.length;
118
- if (total > 0) {
119
- const percent = Math.round((downloaded / total) * 100);
120
- if (percent !== lastPercent && percent % 5 === 0) {
121
- const done = '━'.repeat(Math.floor(percent / 5));
122
- const left = '─'.repeat(20 - Math.floor(percent / 5));
123
- process.stdout.write(`\r ${chalk.hex('#4ECDC4')(done)}${chalk.dim(left)} ${chalk.bold(String(percent))}%`);
124
- lastPercent = percent;
125
- }
126
- }
127
- });
128
- res.pipe(file);
129
- file.on('finish', () => {
130
- if (total > 0)
131
- process.stdout.write('\n');
132
- file.close();
133
- resolve();
134
- });
135
- })
136
- .on('error', (err) => {
137
- file.close();
138
- reject(err);
139
- })
140
- .on('timeout', function () {
141
- this?.destroy();
142
- file.close();
143
- reject(new Error(t('errors.timeout')));
144
- });
145
- });
146
- }
147
- return doGet(url);
148
- }
149
- export function generateServerId() {
150
- return randomBytes(4).toString('hex');
39
+ export function getStartScripts() {
40
+ const base = 'https://raw.githubusercontent.com/PowerNukkitX/scripts/master';
41
+ if (platform() === 'win32')
42
+ return [
43
+ { url: `${base}/start.bat`, name: 'start.bat' },
44
+ { url: `${base}/start.ps1`, name: 'start.ps1' },
45
+ ];
46
+ return [{ url: `${base}/start.sh`, name: 'start.sh' }];
151
47
  }
152
48
  export function getDefaultJavaArgs() {
153
49
  return [
154
- '-Dfile.encoding=UTF-8',
155
- '-Dstdout.encoding=UTF-8',
156
- '-Dstderr.encoding=UTF-8',
157
- '-Djansi.passthrough=true',
158
- '-Dterminal.ansi=true',
159
- '-XX:+UseZGC',
160
- '-XX:+ZGenerational',
161
- '-XX:+UseStringDeduplication',
50
+ '-Dfile.encoding=UTF-8', '-Dstdout.encoding=UTF-8', '-Dstderr.encoding=UTF-8',
51
+ '-Djansi.passthrough=true', '-Dterminal.ansi=true',
52
+ '-XX:+UseZGC', '-XX:+ZGenerational', '-XX:+UseStringDeduplication',
162
53
  '--add-opens', 'java.base/java.lang=ALL-UNNAMED',
163
54
  '--add-opens', 'java.base/java.io=ALL-UNNAMED',
164
55
  '--add-opens', 'java.base/java.net=ALL-UNNAMED',
165
56
  ];
166
57
  }
167
58
  export function getJavaEnv() {
168
- return {
169
- ...process.env,
170
- JAVA_TOOL_OPTIONS: '-Dfile.encoding=UTF-8',
171
- LANG: process.env.LANG || 'en_US.UTF-8',
172
- };
173
- }
174
- const RAW_BASE = 'https://raw.githubusercontent.com/PowerNukkitX/scripts/master';
175
- export function getStartScripts() {
176
- const os = platform();
177
- if (os === 'win32') {
178
- return [
179
- { url: `${RAW_BASE}/start.bat`, name: 'start.bat' },
180
- { url: `${RAW_BASE}/start.ps1`, name: 'start.ps1' },
181
- ];
182
- }
183
- // linux, darwin, etc.
184
- return [
185
- { url: `${RAW_BASE}/start.sh`, name: 'start.sh' },
186
- ];
59
+ return { ...process.env, JAVA_TOOL_OPTIONS: '-Dfile.encoding=UTF-8', LANG: process.env.LANG ?? 'en_US.UTF-8' };
187
60
  }
@@ -1,3 +1,3 @@
1
- export declare function detectServer(startPath?: string): string | null;
2
1
  export declare function detectAllServers(startPath?: string): string[];
3
- export declare function resolveServerDir(label?: string): Promise<string | null>;
2
+ export declare function detectServer(startPath?: string): string | null;
3
+ export declare function resolveServerDir(): Promise<string | null>;
@@ -1,39 +1,32 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { join, dirname, resolve } from 'node:path';
3
3
  import { cwd } from 'node:process';
4
- import { select } from '@inquirer/prompts';
5
- import { highlight } from './logger.js';
6
- export function detectServer(startPath) {
7
- const all = detectAllServers(startPath);
8
- return all[0] || null;
9
- }
4
+ import { getDefaultServer } from '../core/config.js';
10
5
  export function detectAllServers(startPath) {
11
6
  const found = [];
12
- const checkPaths = [
7
+ const paths = [
13
8
  startPath || cwd(),
14
9
  join(startPath || cwd(), 'pnx-server'),
15
10
  startPath ? dirname(resolve(startPath)) : dirname(cwd()),
16
11
  ];
17
- for (const dir of checkPaths) {
18
- if (existsSync(join(dir, 'powernukkitx.jar')) && !found.includes(dir)) {
12
+ for (const dir of paths) {
13
+ if (existsSync(join(dir, 'powernukkitx.jar')) && !found.includes(dir))
19
14
  found.push(dir);
20
- }
21
15
  }
22
16
  return found;
23
17
  }
24
- export async function resolveServerDir(label) {
25
- const servers = detectAllServers();
26
- if (servers.length === 0)
18
+ export function detectServer(startPath) {
19
+ return detectAllServers(startPath)[0] ?? null;
20
+ }
21
+ export async function resolveServerDir() {
22
+ const def = await getDefaultServer();
23
+ if (def && existsSync(join(def, 'powernukkitx.jar')))
24
+ return def;
25
+ const all = detectAllServers();
26
+ if (all.length === 0)
27
27
  return null;
28
- if (servers.length === 1)
29
- return servers[0];
30
- const choice = await select({
31
- message: label || 'Multiple servers found. Which one to manage?',
32
- pageSize: 10,
33
- choices: servers.map((s) => ({
34
- name: highlight(s),
35
- value: s,
36
- })),
37
- });
38
- return choice;
28
+ if (all.length === 1)
29
+ return all[0];
30
+ const { select } = await import('@inquirer/prompts');
31
+ return select({ message: 'Plusieurs serveurs détectés. Lequel ?', choices: all.map(s => ({ name: s, value: s })) });
39
32
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@powernukkitx/cli",
3
- "version": "0.0.3",
3
+ "version": "1.0.0",
4
4
  "description": "PowerNukkitX CLI — Manage your Minecraft Bedrock servers with style",
5
5
  "keywords": [
6
6
  "powernukkitx",
@@ -13,51 +13,34 @@
13
13
  "license": "MIT",
14
14
  "author": "AzaleeX",
15
15
  "type": "module",
16
- "main": "dist/index.js",
16
+ "main": "dist/cli.js",
17
17
  "bin": {
18
- "pnx": "dist/index.js"
18
+ "pnx": "dist/cli.js"
19
19
  },
20
20
  "files": [
21
21
  "dist"
22
22
  ],
23
+ "engines": {
24
+ "node": ">=20.12.0"
25
+ },
23
26
  "scripts": {
24
- "build": "tsc",
25
- "start": "node dist/index.js",
26
- "prepublishOnly": "npm run build",
27
+ "build": "tsc && node scripts/copy-assets.mjs",
27
28
  "dev": "tsc --watch",
28
- "generate-icon": "node scripts/generate-icon.js",
29
- "set-icon": "node scripts/apply-icon.js",
30
- "bundle": "esbuild src/index.ts --bundle --platform=node --format=cjs --outfile=dist/bundle.js --tsconfig=tsconfig.json --external:@yao-pkg/pkg",
31
- "build:exe": "npm run bundle && pkg dist/bundle.js --targets node18-win-x64,node18-linux-x64,node18-macos-x64 --output pnx-cli",
32
- "build:exe:win": "npm run generate-icon && npm run bundle && pkg dist/bundle.js --targets node18-win-x64 --output pnx-cli.exe && npm run set-icon",
33
- "build:exe:linux": "npm run bundle && pkg dist/bundle.js --targets node18-linux-x64 --output pnx-cli-linux",
34
- "build:exe:mac": "npm run bundle && pkg dist/bundle.js --targets node18-macos-x64 --output pnx-cli-macos"
29
+ "start": "node dist/cli.js",
30
+ "test": "node --import tsx --test src/**/*.test.ts",
31
+ "prepublishOnly": "npm run build",
32
+ "build:sea": "npm run build && node scripts/build-sea.mjs"
35
33
  },
36
34
  "dependencies": {
37
- "@inquirer/prompts": "^7.10.1",
35
+ "@clack/prompts": "^1.7.0",
38
36
  "chalk": "^5.6.2",
39
- "commander": "^12.1.0"
37
+ "citty": "^0.2.2"
40
38
  },
41
39
  "devDependencies": {
42
40
  "@types/node": "^22.19.19",
43
- "@yao-pkg/pkg": "^6.19.0",
44
41
  "esbuild": "^0.28.0",
45
- "rcedit": "^5.0.2",
42
+ "postject": "^1.0.0-alpha.6",
43
+ "tsx": "^4.19.2",
46
44
  "typescript": "^5.9.3"
47
- },
48
- "engines": {
49
- "node": ">=18.0.0"
50
- },
51
- "pkg": {
52
- "assets": [
53
- "dist/lang/*.json",
54
- "dist/plugins.json"
55
- ],
56
- "targets": [
57
- "node18-win-x64",
58
- "node18-linux-x64",
59
- "node18-macos-x64"
60
- ],
61
- "public": true
62
45
  }
63
46
  }