@powernukkitx/cli 0.0.2 → 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
@@ -0,0 +1,40 @@
1
+ import { readdirSync, statSync } from 'node:fs';
2
+ import { mkdir, cp, readdir } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ const IGNORE = new Set(['logs', 'cache', 'crash-reports', 'backups', '.git']);
5
+ /** Taille récursive d'un dossier en octets. */
6
+ export function dirSize(dir) {
7
+ let total = 0;
8
+ try {
9
+ for (const f of readdirSync(dir)) {
10
+ const full = join(dir, f);
11
+ const s = statSync(full);
12
+ total += s.isDirectory() ? dirSize(full) : s.size;
13
+ }
14
+ }
15
+ catch { /* ignore */ }
16
+ return total;
17
+ }
18
+ /** Formatte une taille en octets. */
19
+ export function fmtSize(b) {
20
+ if (b < 1024)
21
+ return `${b} B`;
22
+ if (b < 1024 ** 2)
23
+ return `${(b / 1024).toFixed(1)} KB`;
24
+ return `${(b / 1024 / 1024).toFixed(1)} MB`;
25
+ }
26
+ /**
27
+ * Crée une sauvegarde horodatée du serveur dans `<serverDir>/backups/`.
28
+ * Copie récursive (zéro dépendance), en ignorant logs/cache/backups.
29
+ */
30
+ export async function createBackup(serverDir) {
31
+ const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
32
+ const destDir = join(serverDir, 'backups', `pnx-backup-${ts}`);
33
+ await mkdir(destDir, { recursive: true });
34
+ for (const item of await readdir(serverDir)) {
35
+ if (IGNORE.has(item) || item.startsWith('.'))
36
+ continue;
37
+ await cp(join(serverDir, item), join(destDir, item), { recursive: true, force: true });
38
+ }
39
+ return { path: destDir, size: dirSize(destDir) };
40
+ }
@@ -0,0 +1,111 @@
1
+ import { join } from 'node:path';
2
+ import { mkdir, rename, rm } from 'node:fs/promises';
3
+ import { fetchJSON, downloadFile } from '../infra/http.js';
4
+ import { readJson, writeJson } from '../infra/store.js';
5
+ import { apiBase } from '../infra/paths.js';
6
+ const REGISTRY = 'installed-plugins.json';
7
+ /** Liste/recherche de plugins depuis la marketplace. */
8
+ export async function searchPlugins(params) {
9
+ const qs = new URLSearchParams();
10
+ if (params?.sort)
11
+ qs.set('sort', params.sort);
12
+ if (params?.limit)
13
+ qs.set('limit', String(params.limit));
14
+ if (params?.page)
15
+ qs.set('page', String(params.page));
16
+ if (params?.q)
17
+ qs.set('q', params.q);
18
+ const q = qs.toString();
19
+ return fetchJSON(`${apiBase()}/api/plugins/cli${q ? `?${q}` : ''}`);
20
+ }
21
+ /** Fiche détaillée d'un plugin. */
22
+ export async function getPluginDetail(slug) {
23
+ return fetchJSON(`${apiBase()}/api/plugins/cli/${encodeURIComponent(slug)}`);
24
+ }
25
+ /** URL de download proxifiée pour un plugin. */
26
+ export function proxyDownloadUrl(slug, version) {
27
+ const qs = new URLSearchParams({ proxy: '1' });
28
+ if (version)
29
+ qs.set('version', version);
30
+ return `${apiBase()}/api/plugins/${encodeURIComponent(slug)}/download?${qs}`;
31
+ }
32
+ /** Sélectionne la release stable la plus récente, sinon la première. */
33
+ export function pickRelease(detail) {
34
+ return detail.releases.find(r => r.is_stable) ?? detail.releases[0] ?? null;
35
+ }
36
+ // --- Registre local ---
37
+ export async function getInstalled() {
38
+ return readJson(REGISTRY, []);
39
+ }
40
+ /** Upsert par slug dans le registre local. */
41
+ export async function recordInstalled(plugin) {
42
+ const list = await getInstalled();
43
+ const idx = list.findIndex(p => p.slug === plugin.slug);
44
+ if (idx >= 0)
45
+ list[idx] = plugin;
46
+ else
47
+ list.push(plugin);
48
+ await writeJson(REGISTRY, list);
49
+ }
50
+ export async function removeInstalled(slug) {
51
+ const list = (await getInstalled()).filter(p => p.slug !== slug);
52
+ await writeJson(REGISTRY, list);
53
+ }
54
+ // --- Installation ---
55
+ /** Installe un plugin depuis la marketplace. Renvoie l'entrée enregistrée. */
56
+ export async function installPlugin(slug, pluginsDir, onProgress) {
57
+ const detail = await getPluginDetail(slug);
58
+ const release = pickRelease(detail);
59
+ if (!release)
60
+ throw new Error(`no-release:${slug}`);
61
+ const file = release.files[0];
62
+ if (!file)
63
+ throw new Error(`no-file:${slug}`);
64
+ await mkdir(pluginsDir, { recursive: true });
65
+ const dest = join(pluginsDir, file.name);
66
+ await downloadFile(proxyDownloadUrl(slug, release.version), dest, {
67
+ onProgress: onProgress ? pct => onProgress(pct, file.name) : undefined,
68
+ });
69
+ const entry = {
70
+ slug, name: detail.name, version: release.version,
71
+ installedAt: new Date().toISOString(), fileName: file.name,
72
+ };
73
+ await recordInstalled(entry);
74
+ return entry;
75
+ }
76
+ /** Installe un plugin depuis une URL .jar directe. */
77
+ export async function installFromUrl(url, pluginsDir, onProgress) {
78
+ if (!url.endsWith('.jar'))
79
+ throw new Error('url-invalid');
80
+ await mkdir(pluginsDir, { recursive: true });
81
+ const fileName = url.split('/').pop() ?? 'plugin.jar';
82
+ const dest = join(pluginsDir, fileName);
83
+ await downloadFile(url, dest, { onProgress: onProgress ? pct => onProgress(pct, fileName) : undefined });
84
+ return fileName;
85
+ }
86
+ /** Met à jour un plugin installé; null si déjà à jour. */
87
+ export async function updatePlugin(slug, pluginsDir, currentVersion, existingJar, onProgress) {
88
+ const detail = await getPluginDetail(slug);
89
+ const release = pickRelease(detail);
90
+ if (!release)
91
+ throw new Error(`no-release:${slug}`);
92
+ if (currentVersion && currentVersion === release.version)
93
+ return null;
94
+ const file = release.files[0];
95
+ if (!file)
96
+ throw new Error(`no-file:${slug}`);
97
+ if (existingJar) {
98
+ await rename(join(pluginsDir, existingJar), join(pluginsDir, `${existingJar}.old`)).catch(() => { });
99
+ }
100
+ await downloadFile(proxyDownloadUrl(slug, release.version), join(pluginsDir, file.name), {
101
+ onProgress: onProgress ? pct => onProgress(pct, file.name) : undefined,
102
+ });
103
+ if (existingJar)
104
+ await rm(join(pluginsDir, `${existingJar}.old`)).catch(() => { });
105
+ const entry = {
106
+ slug, name: detail.name, version: release.version,
107
+ installedAt: new Date().toISOString(), fileName: file.name,
108
+ };
109
+ await recordInstalled(entry);
110
+ return entry;
111
+ }
@@ -0,0 +1,43 @@
1
+ import { platform } from 'node:os';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { readJson, writeJson, removeFile } from '../infra/store.js';
4
+ const PID_FILE = 'pid.json';
5
+ export async function savePid(pid, serverPath) {
6
+ await writeJson(PID_FILE, { pid, serverPath, startedAt: new Date().toISOString() });
7
+ }
8
+ export async function loadPid() {
9
+ return readJson(PID_FILE, null);
10
+ }
11
+ export async function clearPid() {
12
+ await removeFile(PID_FILE);
13
+ }
14
+ /** True si le processus `pid` est vivant. */
15
+ export function isAlive(pid) {
16
+ try {
17
+ process.kill(pid, 0);
18
+ return true;
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ }
24
+ /** Arrête un processus (taskkill sous Windows, SIGTERM→SIGKILL sinon). */
25
+ export async function killProcess(pid) {
26
+ if (!isAlive(pid))
27
+ return false;
28
+ try {
29
+ if (platform() === 'win32') {
30
+ spawnSync('taskkill', ['/PID', String(pid), '/F']);
31
+ }
32
+ else {
33
+ process.kill(pid, 'SIGTERM');
34
+ await new Promise(r => setTimeout(r, 5000));
35
+ if (isAlive(pid))
36
+ process.kill(pid, 'SIGKILL');
37
+ }
38
+ return true;
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ }
@@ -0,0 +1,38 @@
1
+ import { fetchJSON, downloadFile } from '../infra/http.js';
2
+ import { readJson, writeJson } from '../infra/store.js';
3
+ import { GITHUB_LATEST_RELEASE, JAR_NAME } from '../infra/paths.js';
4
+ const CACHE_FILE = 'latest-release.json';
5
+ const CACHE_TTL = 3_600_000; // 1h
6
+ function normalize(gh) {
7
+ return {
8
+ tag: gh.tag_name,
9
+ version: gh.tag_name.replace(/^v/, ''),
10
+ assets: gh.assets.map(a => ({ name: a.name, url: a.browser_download_url, size: a.size })),
11
+ publishedAt: gh.published_at,
12
+ body: gh.body,
13
+ };
14
+ }
15
+ /** Dernière release du core, mise en cache 1h (sauf `force`). */
16
+ export async function getLatestRelease(force = false) {
17
+ if (!force) {
18
+ const cached = await readJson(CACHE_FILE, null);
19
+ if (cached && Date.now() - cached.fetchedAt < CACHE_TTL)
20
+ return cached.data;
21
+ }
22
+ const gh = await fetchJSON(GITHUB_LATEST_RELEASE);
23
+ const release = normalize(gh);
24
+ await writeJson(CACHE_FILE, { fetchedAt: Date.now(), data: release });
25
+ return release;
26
+ }
27
+ /** Retrouve un asset par nom exact. */
28
+ export function findAsset(release, name) {
29
+ return release.assets.find(a => a.name === name);
30
+ }
31
+ /** Télécharge le jar du core vers `dest`. Renvoie sa taille. */
32
+ export async function downloadCore(release, dest, onProgress) {
33
+ const asset = findAsset(release, JAR_NAME);
34
+ if (!asset)
35
+ throw new Error(`${JAR_NAME} not found in release ${release.tag}`);
36
+ await downloadFile(asset.url, dest, { onProgress: onProgress ? pct => onProgress(pct) : undefined });
37
+ return { size: asset.size };
38
+ }
@@ -0,0 +1,89 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join, dirname, resolve } from 'node:path';
3
+ import { cwd } from 'node:process';
4
+ import { platform } from 'node:os';
5
+ import { spawn } from 'node:child_process';
6
+ import { readJson, writeJson } from '../infra/store.js';
7
+ import { JAR_NAME, SCRIPTS_BASE } from '../infra/paths.js';
8
+ const CONFIG_FILE = 'config.json';
9
+ export async function getDefaultServer() {
10
+ return (await readJson(CONFIG_FILE, {})).defaultServer ?? null;
11
+ }
12
+ export async function setDefaultServer(path) {
13
+ const cfg = await readJson(CONFIG_FILE, {});
14
+ if (path === null)
15
+ delete cfg.defaultServer;
16
+ else
17
+ cfg.defaultServer = path;
18
+ await writeJson(CONFIG_FILE, cfg);
19
+ }
20
+ /** Détecte les dossiers serveur (jar présent) autour d'un point de départ. */
21
+ export function detectServers(startPath) {
22
+ const base = startPath || cwd();
23
+ const found = [];
24
+ for (const dir of [base, join(base, 'pnx-server'), dirname(resolve(base))]) {
25
+ if (existsSync(join(dir, JAR_NAME)) && !found.includes(dir))
26
+ found.push(dir);
27
+ }
28
+ return found;
29
+ }
30
+ /** Résout le dossier serveur: défaut mémorisé, sinon détection (`pick` si plusieurs). */
31
+ export async function resolveServerDir(pick) {
32
+ const def = await getDefaultServer();
33
+ if (def && existsSync(join(def, JAR_NAME)))
34
+ return def;
35
+ const all = detectServers();
36
+ if (all.length === 0)
37
+ return null;
38
+ if (all.length === 1)
39
+ return all[0];
40
+ return pick ? pick(all) : all[0];
41
+ }
42
+ /** Chemin de l'exécutable Java (JAVA_HOME sinon PATH). */
43
+ export function findJava() {
44
+ return process.env.JAVA_HOME ? join(process.env.JAVA_HOME, 'bin', 'java') : 'java';
45
+ }
46
+ /** Vérifie Java (ok si >= 21) et son vendor. */
47
+ export function checkJava() {
48
+ return new Promise(res => {
49
+ const child = spawn(findJava(), ['-version']);
50
+ let out = '';
51
+ child.on('error', () => res({ ok: false, version: 0, raw: '?', vendor: 'Unknown' }));
52
+ child.stderr.on('data', (d) => (out += d.toString()));
53
+ child.on('close', () => {
54
+ const m = out.match(/(?:openjdk|java) version "?(?:1\.)?(\d+)/i);
55
+ const version = m ? parseInt(m[1]) : 0;
56
+ const vendors = ['Temurin', 'GraalVM', 'Zulu', 'Corretto', 'Oracle', 'Microsoft', 'OpenJDK'];
57
+ const vendor = vendors.find(v => out.toLowerCase().includes(v.toLowerCase())) ?? 'Unknown';
58
+ res({ ok: version >= 21, version, raw: out.split('\n')[0]?.trim() || '?', vendor });
59
+ });
60
+ });
61
+ }
62
+ /** Arguments JVM par défaut (encodage, ZGC generational, add-opens). */
63
+ export function defaultJavaArgs() {
64
+ return [
65
+ '-Dfile.encoding=UTF-8', '-Dstdout.encoding=UTF-8', '-Dstderr.encoding=UTF-8',
66
+ '-Djansi.passthrough=true', '-Dterminal.ansi=true',
67
+ '-XX:+UseZGC', '-XX:+ZGenerational', '-XX:+UseStringDeduplication',
68
+ '--add-opens', 'java.base/java.lang=ALL-UNNAMED',
69
+ '--add-opens', 'java.base/java.io=ALL-UNNAMED',
70
+ '--add-opens', 'java.base/java.net=ALL-UNNAMED',
71
+ ];
72
+ }
73
+ /** Ligne de commande complète pour lancer le serveur. */
74
+ export function buildLaunchArgs(memory) {
75
+ return [`-Xms${memory}`, '-XX:MaxRAMPercentage=75.0', ...defaultJavaArgs(), '-jar', JAR_NAME];
76
+ }
77
+ /** Environnement d'exécution du serveur (encodage UTF-8). */
78
+ export function javaEnv() {
79
+ return { ...process.env, JAVA_TOOL_OPTIONS: '-Dfile.encoding=UTF-8', LANG: process.env.LANG ?? 'en_US.UTF-8' };
80
+ }
81
+ /** Scripts de démarrage à télécharger selon la plateforme. */
82
+ export function startScripts() {
83
+ if (platform() === 'win32')
84
+ return [
85
+ { url: `${SCRIPTS_BASE}/start.bat`, name: 'start.bat' },
86
+ { url: `${SCRIPTS_BASE}/start.ps1`, name: 'start.ps1' },
87
+ ];
88
+ return [{ url: `${SCRIPTS_BASE}/start.sh`, name: 'start.sh' }];
89
+ }
@@ -0,0 +1,71 @@
1
+ import * as clack from '@clack/prompts';
2
+ import { c, chalk } from './theme.js';
3
+ const _isJson = process.argv.includes('--json');
4
+ /** True si le CLI tourne en mode machine (--json). */
5
+ export function isJson() { return _isJson; }
6
+ /** Émet un objet JSON sur stdout et quitte (mode --json). */
7
+ export function json(data) {
8
+ process.stdout.write(JSON.stringify(data, null, 2) + '\n');
9
+ process.exit(0);
10
+ }
11
+ export function intro(title) {
12
+ if (_isJson)
13
+ return;
14
+ clack.intro(c.primary.bold(` ${title} `));
15
+ }
16
+ export function outro(msg) {
17
+ if (_isJson)
18
+ return;
19
+ clack.outro(c.accent(msg));
20
+ }
21
+ export function section(title) {
22
+ if (_isJson)
23
+ return;
24
+ clack.log.step(c.accent.bold(title));
25
+ }
26
+ /** Annule proprement (prompt annulé) et quitte avec succès. */
27
+ export function cancel(msg = 'Annulé') {
28
+ if (!_isJson)
29
+ clack.cancel(c.yellow(msg));
30
+ process.exit(0);
31
+ }
32
+ export function spin(msg) {
33
+ if (_isJson)
34
+ return { update: () => { }, stop: () => { }, fail: () => { } };
35
+ const s = clack.spinner();
36
+ s.start(msg);
37
+ return {
38
+ update: m => s.message(m),
39
+ stop: m => s.stop(m),
40
+ fail: m => s.stop(m, 1),
41
+ };
42
+ }
43
+ export const log = {
44
+ success: (msg) => { if (!_isJson)
45
+ clack.log.success(c.green(msg)); },
46
+ error: (msg) => { if (!_isJson)
47
+ clack.log.error(c.red(msg)); },
48
+ warn: (msg) => { if (!_isJson)
49
+ clack.log.warn(c.yellow(msg)); },
50
+ info: (msg) => { if (!_isJson)
51
+ clack.log.info(c.blue(msg)); },
52
+ step: (msg) => { if (!_isJson)
53
+ clack.log.step(chalk.dim(msg)); },
54
+ dim: (msg) => { if (!_isJson)
55
+ clack.log.message(chalk.dim(msg)); },
56
+ raw: (msg) => { if (!_isJson)
57
+ process.stdout.write(msg + '\n'); },
58
+ };
59
+ /** Barre de progression inline (0-100). */
60
+ export function progress(pct, label) {
61
+ if (_isJson)
62
+ return;
63
+ const clamped = Math.max(0, Math.min(100, pct));
64
+ const filled = Math.floor(clamped / 5);
65
+ const bar = c.cyan('━'.repeat(filled)) + chalk.dim('─'.repeat(20 - filled));
66
+ const suffix = label ? ` ${chalk.dim(label)}` : '';
67
+ process.stdout.write(`\r ${bar} ${chalk.bold(String(clamped))}%${suffix}`);
68
+ if (clamped >= 100)
69
+ process.stdout.write('\n');
70
+ }
71
+ export { clack };
@@ -0,0 +1,30 @@
1
+ import { text, confirm, select, multiselect, isCancel } from '@clack/prompts';
2
+ import { cancel } from './output.js';
3
+ /** Prompt texte. Annulation → cancel() propre. */
4
+ export async function promptText(opts) {
5
+ const res = await text(opts);
6
+ if (isCancel(res))
7
+ cancel();
8
+ return res;
9
+ }
10
+ /** Prompt oui/non. */
11
+ export async function promptConfirm(message, initialValue = false) {
12
+ const res = await confirm({ message, initialValue });
13
+ if (isCancel(res))
14
+ cancel();
15
+ return res;
16
+ }
17
+ /** Prompt sélection simple. */
18
+ export async function promptSelect(message, options) {
19
+ const res = await select({ message, options: options });
20
+ if (isCancel(res))
21
+ cancel();
22
+ return res;
23
+ }
24
+ /** Prompt sélection multiple. */
25
+ export async function promptMulti(message, options, required = false) {
26
+ const res = await multiselect({ message, options: options, required });
27
+ if (isCancel(res))
28
+ cancel();
29
+ return res;
30
+ }
@@ -0,0 +1,18 @@
1
+ import chalk from 'chalk';
2
+ /** Palette de couleurs unique du CLI. */
3
+ export const c = {
4
+ primary: chalk.hex('#FF6B35'),
5
+ accent: chalk.hex('#FFB347'),
6
+ green: chalk.hex('#4CAF50'),
7
+ red: chalk.hex('#E53935'),
8
+ yellow: chalk.hex('#FFC107'),
9
+ blue: chalk.hex('#6C8EBF'),
10
+ cyan: chalk.hex('#4ECDC4'),
11
+ dim: chalk.dim,
12
+ bold: chalk.bold,
13
+ };
14
+ /** Désactive/active la couleur globalement (--no-color). */
15
+ export function setColorEnabled(on) {
16
+ chalk.level = on ? (chalk.level || 1) : 0;
17
+ }
18
+ export { chalk };
@@ -1,18 +1,9 @@
1
1
  import type { PluginListResponse, PluginDetail } from '../types.js';
2
- export declare function fetchJSON(url: string): Promise<any>;
3
- export interface ListPluginsParams {
4
- sort?: 'newest' | 'updated' | 'stars';
2
+ export declare function fetchPluginsList(params?: {
3
+ sort?: string;
5
4
  limit?: number;
6
5
  page?: number;
7
6
  q?: string;
8
- }
9
- export declare function fetchPluginsList(params?: ListPluginsParams): Promise<PluginListResponse>;
7
+ }): Promise<PluginListResponse>;
10
8
  export declare function fetchPluginDetail(slug: string): Promise<PluginDetail>;
11
- export interface ResolvedDownload {
12
- url: string;
13
- name: string;
14
- size: number;
15
- version: string;
16
- }
17
- export declare function resolveDownload(slug: string, version?: string): Promise<ResolvedDownload>;
18
9
  export declare function proxyDownloadUrl(slug: string, version?: string): string;
package/dist/utils/api.js CHANGED
@@ -1,38 +1,5 @@
1
- import https from 'node:https';
2
- import http from 'node:http';
3
- import { t } from '../lang/index.js';
4
- let API_BASE = process.env.PNX_API_URL || 'http://localhost:3000';
5
- if (API_BASE.endsWith('/'))
6
- API_BASE = API_BASE.slice(0, -1);
7
- function agentGet(url, redirects = 5) {
8
- if (redirects <= 0)
9
- return Promise.reject(new Error('Too many redirects'));
10
- return new Promise((resolve, reject) => {
11
- const mod = url.startsWith('https') ? https : http;
12
- mod.get(url, { timeout: 15_000, headers: { 'User-Agent': 'pnx-cli' } }, (res) => {
13
- const code = res.statusCode ?? 0;
14
- if ([301, 302, 303, 307, 308].includes(code)) {
15
- if (res.headers.location) {
16
- resolve(agentGet(res.headers.location, redirects - 1));
17
- return;
18
- }
19
- }
20
- if (code !== 200) {
21
- reject(new Error(`HTTP ${code}`));
22
- return;
23
- }
24
- let data = '';
25
- res.on('data', (chunk) => (data += chunk));
26
- res.on('end', () => resolve(data));
27
- })
28
- .on('error', reject)
29
- .on('timeout', function () { this?.destroy(); reject(new Error(t('errors.timeout'))); });
30
- });
31
- }
32
- export async function fetchJSON(url) {
33
- const raw = await agentGet(url);
34
- return JSON.parse(raw);
35
- }
1
+ import { fetchJSON } from '../core/network.js';
2
+ let API_BASE = (process.env.PNX_API_URL ?? 'https://api.powernukkitx.org').replace(/\/$/, '');
36
3
  export async function fetchPluginsList(params) {
37
4
  const qs = new URLSearchParams();
38
5
  if (params?.sort)
@@ -43,32 +10,15 @@ export async function fetchPluginsList(params) {
43
10
  qs.set('page', String(params.page));
44
11
  if (params?.q)
45
12
  qs.set('q', params.q);
46
- const query = qs.toString();
47
- const url = `${API_BASE}/api/plugins/cli${query ? `?${query}` : ''}`;
48
- return fetchJSON(url);
13
+ const q = qs.toString();
14
+ return fetchJSON(`${API_BASE}/api/plugins/cli${q ? `?${q}` : ''}`);
49
15
  }
50
16
  export async function fetchPluginDetail(slug) {
51
- const url = `${API_BASE}/api/plugins/cli/${encodeURIComponent(slug)}`;
52
- return fetchJSON(url);
53
- }
54
- export async function resolveDownload(slug, version) {
55
- const qs = new URLSearchParams();
56
- qs.set('resolve', '1');
57
- if (version)
58
- qs.set('version', version);
59
- const url = `${API_BASE}/api/plugins/${encodeURIComponent(slug)}/download?${qs.toString()}`;
60
- const data = await fetchJSON(url);
61
- return {
62
- url: data.url || data.download_url,
63
- name: data.name || data.file_name || `${slug}.jar`,
64
- size: data.size || 0,
65
- version: data.version || version || 'latest',
66
- };
17
+ return fetchJSON(`${API_BASE}/api/plugins/cli/${encodeURIComponent(slug)}`);
67
18
  }
68
19
  export function proxyDownloadUrl(slug, version) {
69
- const qs = new URLSearchParams();
70
- qs.set('proxy', '1');
20
+ const qs = new URLSearchParams({ proxy: '1' });
71
21
  if (version)
72
22
  qs.set('version', version);
73
- return `${API_BASE}/api/plugins/${encodeURIComponent(slug)}/download?${qs.toString()}`;
23
+ return `${API_BASE}/api/plugins/${encodeURIComponent(slug)}/download?${qs}`;
74
24
  }
@@ -9,19 +9,12 @@ interface GitHubRelease {
9
9
  published_at: string;
10
10
  body: string;
11
11
  }
12
- export declare function fetchJSON(url: string, redirects?: number): Promise<any>;
13
12
  export declare function getLatestRelease(): Promise<GitHubRelease>;
14
- export declare function findAsset(release: GitHubRelease, name: string): Promise<GitHubAsset | undefined>;
15
- export declare function findPluginAsset(repo: string, assetName: string): Promise<{
16
- url: string;
17
- size: number;
18
- } | null>;
19
- export declare function downloadFile(url: string, dest: string, label?: string): Promise<void>;
20
- export declare function generateServerId(): string;
21
- export declare function getDefaultJavaArgs(): string[];
22
- export declare function getJavaEnv(): Record<string, string>;
13
+ export declare function findAsset(release: GitHubRelease, name: string): GitHubAsset | undefined;
23
14
  export declare function getStartScripts(): {
24
15
  url: string;
25
16
  name: string;
26
17
  }[];
18
+ export declare function getDefaultJavaArgs(): string[];
19
+ export declare function getJavaEnv(): NodeJS.ProcessEnv;
27
20
  export {};