@powernukkitx/cli 0.0.3 → 1.0.1
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/LICENSE +21 -0
- package/README.md +110 -101
- package/dist/cli.js +34 -0
- package/dist/commands/backup.d.ts +2 -1
- package/dist/commands/backup.js +23 -74
- package/dist/commands/config.d.ts +1 -1
- package/dist/commands/config.js +60 -114
- package/dist/commands/console.d.ts +2 -0
- package/dist/commands/console.js +98 -0
- package/dist/commands/doctor.d.ts +2 -1
- package/dist/commands/doctor.js +97 -119
- package/dist/commands/info.d.ts +2 -1
- package/dist/commands/info.js +41 -36
- package/dist/commands/init.d.ts +2 -4
- package/dist/commands/init.js +87 -73
- package/dist/commands/install.js +45 -0
- package/dist/commands/logs.d.ts +2 -0
- package/dist/commands/logs.js +60 -0
- package/dist/commands/menu.js +94 -0
- package/dist/commands/plugin/index.d.ts +2 -0
- package/dist/commands/plugin/index.js +13 -0
- package/dist/commands/plugin/info.d.ts +2 -0
- package/dist/commands/plugin/info.js +39 -0
- package/dist/commands/plugin/install.d.ts +2 -0
- package/dist/commands/plugin/install.js +56 -0
- package/dist/commands/plugin/installed.d.ts +2 -0
- package/dist/commands/plugin/installed.js +41 -0
- package/dist/commands/plugin/list.d.ts +2 -0
- package/dist/commands/plugin/list.js +47 -0
- package/dist/commands/plugin/remove.d.ts +2 -0
- package/dist/commands/plugin/remove.js +44 -0
- package/dist/commands/plugin/search.d.ts +2 -0
- package/dist/commands/plugin/search.js +11 -0
- package/dist/commands/plugin/update.d.ts +2 -0
- package/dist/commands/plugin/update.js +52 -0
- package/dist/commands/start.d.ts +2 -7
- package/dist/commands/start.js +74 -108
- package/dist/commands/stop.d.ts +2 -0
- package/dist/commands/stop.js +26 -0
- package/dist/commands/update.d.ts +2 -5
- package/dist/commands/update.js +34 -55
- package/dist/commands/use.d.ts +2 -0
- package/dist/commands/use.js +39 -0
- package/dist/infra/http.js +119 -0
- package/dist/infra/paths.js +15 -0
- package/dist/infra/store.js +24 -0
- package/dist/services/backup.js +34 -0
- package/dist/services/plugins.js +139 -0
- package/dist/services/process.js +41 -0
- package/dist/services/release.js +51 -0
- package/dist/services/server.js +81 -0
- package/dist/ui/output.js +67 -0
- package/dist/ui/prompts.js +26 -0
- package/dist/ui/theme.js +16 -0
- package/package.json +44 -63
- package/dist/bundle.js +0 -18871
- package/dist/commands/plugin.d.ts +0 -12
- package/dist/commands/plugin.js +0 -385
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -260
- package/dist/lang/en.json +0 -178
- package/dist/lang/fr.json +0 -178
- package/dist/lang/index.d.ts +0 -7
- package/dist/lang/index.js +0 -83
- package/dist/plugins.json +0 -66
- package/dist/types.d.ts +0 -61
- package/dist/types.js +0 -1
- package/dist/utils/api.d.ts +0 -18
- package/dist/utils/api.js +0 -74
- package/dist/utils/github.d.ts +0 -27
- package/dist/utils/github.js +0 -187
- package/dist/utils/logger.d.ts +0 -15
- package/dist/utils/logger.js +0 -72
- package/dist/utils/pause.d.ts +0 -1
- package/dist/utils/pause.js +0 -6
- package/dist/utils/plugins.d.ts +0 -4
- package/dist/utils/plugins.js +0 -34
- package/dist/utils/server.d.ts +0 -3
- package/dist/utils/server.js +0 -39
- package/dist/utils/updater.d.ts +0 -1
- package/dist/utils/updater.js +0 -86
|
@@ -0,0 +1,139 @@
|
|
|
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
|
+
export async function searchPlugins(params) {
|
|
8
|
+
const qs = new URLSearchParams();
|
|
9
|
+
if (params?.sort)
|
|
10
|
+
qs.set('sort', params.sort);
|
|
11
|
+
if (params?.limit)
|
|
12
|
+
qs.set('limit', String(params.limit));
|
|
13
|
+
if (params?.page)
|
|
14
|
+
qs.set('page', String(params.page));
|
|
15
|
+
if (params?.q)
|
|
16
|
+
qs.set('q', params.q);
|
|
17
|
+
const q = qs.toString();
|
|
18
|
+
return fetchJSON(`${apiBase()}/api/plugins/cli${q ? `?${q}` : ''}`);
|
|
19
|
+
}
|
|
20
|
+
export async function getPluginDetail(slug) {
|
|
21
|
+
return fetchJSON(`${apiBase()}/api/plugins/cli/${encodeURIComponent(slug)}`);
|
|
22
|
+
}
|
|
23
|
+
export async function fetchApprovedPlugin(slug) {
|
|
24
|
+
try {
|
|
25
|
+
const res = await fetchJSON(`${apiBase()}/api/v1/plugins/${encodeURIComponent(slug)}`);
|
|
26
|
+
return res?.data ?? null;
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
if (e?.statusCode === 404)
|
|
30
|
+
return null;
|
|
31
|
+
throw e;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function pickJarAsset(detail) {
|
|
35
|
+
for (const rel of detail.releases ?? []) {
|
|
36
|
+
const jar = (rel.assets ?? []).find(a => a.name.endsWith('.jar'));
|
|
37
|
+
if (jar)
|
|
38
|
+
return { url: jar.downloadUrl, name: jar.name, version: rel.tag };
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
export async function installApprovedPlugin(slug, pluginsDir, onProgress) {
|
|
43
|
+
const detail = await fetchApprovedPlugin(slug);
|
|
44
|
+
if (!detail || detail.status !== 'published')
|
|
45
|
+
throw new Error(`not-approved:${slug}`);
|
|
46
|
+
const asset = pickJarAsset(detail);
|
|
47
|
+
if (!asset)
|
|
48
|
+
throw new Error(`no-file:${slug}`);
|
|
49
|
+
await mkdir(pluginsDir, { recursive: true });
|
|
50
|
+
const dest = join(pluginsDir, asset.name);
|
|
51
|
+
await downloadFile(asset.url, dest, {
|
|
52
|
+
onProgress: onProgress ? pct => onProgress(pct, asset.name) : undefined,
|
|
53
|
+
});
|
|
54
|
+
const entry = {
|
|
55
|
+
slug, name: detail.name, version: asset.version,
|
|
56
|
+
installedAt: new Date().toISOString(), fileName: asset.name,
|
|
57
|
+
};
|
|
58
|
+
await recordInstalled(entry);
|
|
59
|
+
return entry;
|
|
60
|
+
}
|
|
61
|
+
export function proxyDownloadUrl(slug, version) {
|
|
62
|
+
const qs = new URLSearchParams({ proxy: '1' });
|
|
63
|
+
if (version)
|
|
64
|
+
qs.set('version', version);
|
|
65
|
+
return `${apiBase()}/api/plugins/${encodeURIComponent(slug)}/download?${qs}`;
|
|
66
|
+
}
|
|
67
|
+
export function pickRelease(detail) {
|
|
68
|
+
return detail.releases.find(r => r.is_stable) ?? detail.releases[0] ?? null;
|
|
69
|
+
}
|
|
70
|
+
export async function getInstalled() {
|
|
71
|
+
return readJson(REGISTRY, []);
|
|
72
|
+
}
|
|
73
|
+
export async function recordInstalled(plugin) {
|
|
74
|
+
const list = await getInstalled();
|
|
75
|
+
const idx = list.findIndex(p => p.slug === plugin.slug);
|
|
76
|
+
if (idx >= 0)
|
|
77
|
+
list[idx] = plugin;
|
|
78
|
+
else
|
|
79
|
+
list.push(plugin);
|
|
80
|
+
await writeJson(REGISTRY, list);
|
|
81
|
+
}
|
|
82
|
+
export async function removeInstalled(slug) {
|
|
83
|
+
const list = (await getInstalled()).filter(p => p.slug !== slug);
|
|
84
|
+
await writeJson(REGISTRY, list);
|
|
85
|
+
}
|
|
86
|
+
export async function installPlugin(slug, pluginsDir, onProgress) {
|
|
87
|
+
const detail = await getPluginDetail(slug);
|
|
88
|
+
const release = pickRelease(detail);
|
|
89
|
+
if (!release)
|
|
90
|
+
throw new Error(`no-release:${slug}`);
|
|
91
|
+
const file = release.files[0];
|
|
92
|
+
if (!file)
|
|
93
|
+
throw new Error(`no-file:${slug}`);
|
|
94
|
+
await mkdir(pluginsDir, { recursive: true });
|
|
95
|
+
const dest = join(pluginsDir, file.name);
|
|
96
|
+
await downloadFile(proxyDownloadUrl(slug, release.version), dest, {
|
|
97
|
+
onProgress: onProgress ? pct => onProgress(pct, file.name) : undefined,
|
|
98
|
+
});
|
|
99
|
+
const entry = {
|
|
100
|
+
slug, name: detail.name, version: release.version,
|
|
101
|
+
installedAt: new Date().toISOString(), fileName: file.name,
|
|
102
|
+
};
|
|
103
|
+
await recordInstalled(entry);
|
|
104
|
+
return entry;
|
|
105
|
+
}
|
|
106
|
+
export async function installFromUrl(url, pluginsDir, onProgress) {
|
|
107
|
+
if (!url.endsWith('.jar'))
|
|
108
|
+
throw new Error('url-invalid');
|
|
109
|
+
await mkdir(pluginsDir, { recursive: true });
|
|
110
|
+
const fileName = url.split('/').pop() ?? 'plugin.jar';
|
|
111
|
+
const dest = join(pluginsDir, fileName);
|
|
112
|
+
await downloadFile(url, dest, { onProgress: onProgress ? pct => onProgress(pct, fileName) : undefined });
|
|
113
|
+
return fileName;
|
|
114
|
+
}
|
|
115
|
+
export async function updatePlugin(slug, pluginsDir, currentVersion, existingJar, onProgress) {
|
|
116
|
+
const detail = await getPluginDetail(slug);
|
|
117
|
+
const release = pickRelease(detail);
|
|
118
|
+
if (!release)
|
|
119
|
+
throw new Error(`no-release:${slug}`);
|
|
120
|
+
if (currentVersion && currentVersion === release.version)
|
|
121
|
+
return null;
|
|
122
|
+
const file = release.files[0];
|
|
123
|
+
if (!file)
|
|
124
|
+
throw new Error(`no-file:${slug}`);
|
|
125
|
+
if (existingJar) {
|
|
126
|
+
await rename(join(pluginsDir, existingJar), join(pluginsDir, `${existingJar}.old`)).catch(() => { });
|
|
127
|
+
}
|
|
128
|
+
await downloadFile(proxyDownloadUrl(slug, release.version), join(pluginsDir, file.name), {
|
|
129
|
+
onProgress: onProgress ? pct => onProgress(pct, file.name) : undefined,
|
|
130
|
+
});
|
|
131
|
+
if (existingJar)
|
|
132
|
+
await rm(join(pluginsDir, `${existingJar}.old`)).catch(() => { });
|
|
133
|
+
const entry = {
|
|
134
|
+
slug, name: detail.name, version: release.version,
|
|
135
|
+
installedAt: new Date().toISOString(), fileName: file.name,
|
|
136
|
+
};
|
|
137
|
+
await recordInstalled(entry);
|
|
138
|
+
return entry;
|
|
139
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
export function isAlive(pid) {
|
|
15
|
+
try {
|
|
16
|
+
process.kill(pid, 0);
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export async function killProcess(pid) {
|
|
24
|
+
if (!isAlive(pid))
|
|
25
|
+
return false;
|
|
26
|
+
try {
|
|
27
|
+
if (platform() === 'win32') {
|
|
28
|
+
spawnSync('taskkill', ['/PID', String(pid), '/F']);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
process.kill(pid, 'SIGTERM');
|
|
32
|
+
await new Promise(r => setTimeout(r, 5000));
|
|
33
|
+
if (isAlive(pid))
|
|
34
|
+
process.kill(pid, 'SIGKILL');
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { fetchJSON, downloadFile } from '../infra/http.js';
|
|
2
|
+
import { readJson, writeJson } from '../infra/store.js';
|
|
3
|
+
import { GITHUB_LATEST_RELEASE, GITHUB_RELEASES, JAR_NAME } from '../infra/paths.js';
|
|
4
|
+
const CACHE_FILE = 'latest-release.json';
|
|
5
|
+
const LIST_CACHE_FILE = 'releases.json';
|
|
6
|
+
const CACHE_TTL = 3_600_000;
|
|
7
|
+
function normalize(gh) {
|
|
8
|
+
return {
|
|
9
|
+
tag: gh.tag_name,
|
|
10
|
+
version: gh.tag_name.replace(/^v/, ''),
|
|
11
|
+
assets: gh.assets.map(a => ({ name: a.name, url: a.browser_download_url, size: a.size })),
|
|
12
|
+
publishedAt: gh.published_at,
|
|
13
|
+
prerelease: gh.prerelease ?? false,
|
|
14
|
+
body: gh.body,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export async function getLatestRelease(force = false) {
|
|
18
|
+
if (!force) {
|
|
19
|
+
const cached = await readJson(CACHE_FILE, null);
|
|
20
|
+
if (cached && Date.now() - cached.fetchedAt < CACHE_TTL)
|
|
21
|
+
return cached.data;
|
|
22
|
+
}
|
|
23
|
+
const gh = await fetchJSON(GITHUB_LATEST_RELEASE);
|
|
24
|
+
const release = normalize(gh);
|
|
25
|
+
await writeJson(CACHE_FILE, { fetchedAt: Date.now(), data: release });
|
|
26
|
+
return release;
|
|
27
|
+
}
|
|
28
|
+
export async function listReleases(force = false) {
|
|
29
|
+
if (!force) {
|
|
30
|
+
const cached = await readJson(LIST_CACHE_FILE, null);
|
|
31
|
+
if (cached && Date.now() - cached.fetchedAt < CACHE_TTL)
|
|
32
|
+
return cached.data;
|
|
33
|
+
}
|
|
34
|
+
const gh = await fetchJSON(`${GITHUB_RELEASES}?per_page=30`);
|
|
35
|
+
const releases = gh
|
|
36
|
+
.filter(r => !r.draft)
|
|
37
|
+
.map(normalize)
|
|
38
|
+
.filter(r => r.assets.some(a => a.name === JAR_NAME));
|
|
39
|
+
await writeJson(LIST_CACHE_FILE, { fetchedAt: Date.now(), data: releases });
|
|
40
|
+
return releases;
|
|
41
|
+
}
|
|
42
|
+
export function findAsset(release, name) {
|
|
43
|
+
return release.assets.find(a => a.name === name);
|
|
44
|
+
}
|
|
45
|
+
export async function downloadCore(release, dest, onProgress) {
|
|
46
|
+
const asset = findAsset(release, JAR_NAME);
|
|
47
|
+
if (!asset)
|
|
48
|
+
throw new Error(`${JAR_NAME} not found in release ${release.tag}`);
|
|
49
|
+
await downloadFile(asset.url, dest, { onProgress: onProgress ? pct => onProgress(pct) : undefined });
|
|
50
|
+
return { size: asset.size };
|
|
51
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
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
|
+
export function detectServers(startPath) {
|
|
21
|
+
const base = startPath || cwd();
|
|
22
|
+
const found = [];
|
|
23
|
+
for (const dir of [base, join(base, 'pnx-server'), dirname(resolve(base))]) {
|
|
24
|
+
if (existsSync(join(dir, JAR_NAME)) && !found.includes(dir))
|
|
25
|
+
found.push(dir);
|
|
26
|
+
}
|
|
27
|
+
return found;
|
|
28
|
+
}
|
|
29
|
+
export async function resolveServerDir(pick) {
|
|
30
|
+
const def = await getDefaultServer();
|
|
31
|
+
if (def && existsSync(join(def, JAR_NAME)))
|
|
32
|
+
return def;
|
|
33
|
+
const all = detectServers();
|
|
34
|
+
if (all.length === 0)
|
|
35
|
+
return null;
|
|
36
|
+
if (all.length === 1)
|
|
37
|
+
return all[0];
|
|
38
|
+
return pick ? pick(all) : all[0];
|
|
39
|
+
}
|
|
40
|
+
export function findJava() {
|
|
41
|
+
return process.env.JAVA_HOME ? join(process.env.JAVA_HOME, 'bin', 'java') : 'java';
|
|
42
|
+
}
|
|
43
|
+
export function checkJava() {
|
|
44
|
+
return new Promise(res => {
|
|
45
|
+
const child = spawn(findJava(), ['-version']);
|
|
46
|
+
let out = '';
|
|
47
|
+
child.on('error', () => res({ ok: false, version: 0, raw: '?', vendor: 'Unknown' }));
|
|
48
|
+
child.stderr.on('data', (d) => (out += d.toString()));
|
|
49
|
+
child.on('close', () => {
|
|
50
|
+
const m = out.match(/(?:openjdk|java) version "?(?:1\.)?(\d+)/i);
|
|
51
|
+
const version = m ? parseInt(m[1]) : 0;
|
|
52
|
+
const vendors = ['Temurin', 'GraalVM', 'Zulu', 'Corretto', 'Oracle', 'Microsoft', 'OpenJDK'];
|
|
53
|
+
const vendor = vendors.find(v => out.toLowerCase().includes(v.toLowerCase())) ?? 'Unknown';
|
|
54
|
+
res({ ok: version >= 21, version, raw: out.split('\n')[0]?.trim() || '?', vendor });
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
export function defaultJavaArgs() {
|
|
59
|
+
return [
|
|
60
|
+
'-Dfile.encoding=UTF-8', '-Dstdout.encoding=UTF-8', '-Dstderr.encoding=UTF-8',
|
|
61
|
+
'-Djansi.passthrough=true', '-Dterminal.ansi=true',
|
|
62
|
+
'-XX:+UseZGC', '-XX:+ZGenerational', '-XX:+UseStringDeduplication',
|
|
63
|
+
'--add-opens', 'java.base/java.lang=ALL-UNNAMED',
|
|
64
|
+
'--add-opens', 'java.base/java.io=ALL-UNNAMED',
|
|
65
|
+
'--add-opens', 'java.base/java.net=ALL-UNNAMED',
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
export function buildLaunchArgs(memory) {
|
|
69
|
+
return [`-Xms${memory}`, '-XX:MaxRAMPercentage=75.0', ...defaultJavaArgs(), '-jar', JAR_NAME];
|
|
70
|
+
}
|
|
71
|
+
export function javaEnv() {
|
|
72
|
+
return { ...process.env, JAVA_TOOL_OPTIONS: '-Dfile.encoding=UTF-8', LANG: process.env.LANG ?? 'en_US.UTF-8' };
|
|
73
|
+
}
|
|
74
|
+
export function startScripts() {
|
|
75
|
+
if (platform() === 'win32')
|
|
76
|
+
return [
|
|
77
|
+
{ url: `${SCRIPTS_BASE}/start.bat`, name: 'start.bat' },
|
|
78
|
+
{ url: `${SCRIPTS_BASE}/start.ps1`, name: 'start.ps1' },
|
|
79
|
+
];
|
|
80
|
+
return [{ url: `${SCRIPTS_BASE}/start.sh`, name: 'start.sh' }];
|
|
81
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as clack from '@clack/prompts';
|
|
2
|
+
import { c, chalk } from './theme.js';
|
|
3
|
+
const _isJson = process.argv.includes('--json');
|
|
4
|
+
export function isJson() { return _isJson; }
|
|
5
|
+
export function json(data) {
|
|
6
|
+
process.stdout.write(JSON.stringify(data, null, 2) + '\n');
|
|
7
|
+
process.exit(0);
|
|
8
|
+
}
|
|
9
|
+
export function intro(title) {
|
|
10
|
+
if (_isJson)
|
|
11
|
+
return;
|
|
12
|
+
clack.intro(c.primary.bold(` ${title} `));
|
|
13
|
+
}
|
|
14
|
+
export function outro(msg) {
|
|
15
|
+
if (_isJson)
|
|
16
|
+
return;
|
|
17
|
+
clack.outro(c.accent(msg));
|
|
18
|
+
}
|
|
19
|
+
export function section(title) {
|
|
20
|
+
if (_isJson)
|
|
21
|
+
return;
|
|
22
|
+
clack.log.step(c.accent.bold(title));
|
|
23
|
+
}
|
|
24
|
+
export function cancel(msg = 'Cancelled') {
|
|
25
|
+
if (!_isJson)
|
|
26
|
+
clack.cancel(c.yellow(msg));
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
export function spin(msg) {
|
|
30
|
+
if (_isJson)
|
|
31
|
+
return { update: () => { }, stop: () => { }, fail: () => { } };
|
|
32
|
+
const s = clack.spinner();
|
|
33
|
+
s.start(msg);
|
|
34
|
+
return {
|
|
35
|
+
update: m => s.message(m),
|
|
36
|
+
stop: m => s.stop(m),
|
|
37
|
+
fail: m => s.stop(m, 1),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export const log = {
|
|
41
|
+
success: (msg) => { if (!_isJson)
|
|
42
|
+
clack.log.success(c.green(msg)); },
|
|
43
|
+
error: (msg) => { if (!_isJson)
|
|
44
|
+
clack.log.error(c.red(msg)); },
|
|
45
|
+
warn: (msg) => { if (!_isJson)
|
|
46
|
+
clack.log.warn(c.yellow(msg)); },
|
|
47
|
+
info: (msg) => { if (!_isJson)
|
|
48
|
+
clack.log.info(c.blue(msg)); },
|
|
49
|
+
step: (msg) => { if (!_isJson)
|
|
50
|
+
clack.log.step(chalk.dim(msg)); },
|
|
51
|
+
dim: (msg) => { if (!_isJson)
|
|
52
|
+
clack.log.message(chalk.dim(msg)); },
|
|
53
|
+
raw: (msg) => { if (!_isJson)
|
|
54
|
+
process.stdout.write(msg + '\n'); },
|
|
55
|
+
};
|
|
56
|
+
export function progress(pct, label) {
|
|
57
|
+
if (_isJson)
|
|
58
|
+
return;
|
|
59
|
+
const clamped = Math.max(0, Math.min(100, pct));
|
|
60
|
+
const filled = Math.floor(clamped / 5);
|
|
61
|
+
const bar = c.cyan('━'.repeat(filled)) + chalk.dim('─'.repeat(20 - filled));
|
|
62
|
+
const suffix = label ? ` ${chalk.dim(label)}` : '';
|
|
63
|
+
process.stdout.write(`\r ${bar} ${chalk.bold(String(clamped))}%${suffix}`);
|
|
64
|
+
if (clamped >= 100)
|
|
65
|
+
process.stdout.write('\n');
|
|
66
|
+
}
|
|
67
|
+
export { clack };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { text, confirm, select, multiselect, isCancel } from '@clack/prompts';
|
|
2
|
+
import { cancel } from './output.js';
|
|
3
|
+
export async function promptText(opts) {
|
|
4
|
+
const res = await text(opts);
|
|
5
|
+
if (isCancel(res))
|
|
6
|
+
cancel();
|
|
7
|
+
return res;
|
|
8
|
+
}
|
|
9
|
+
export async function promptConfirm(message, initialValue = false) {
|
|
10
|
+
const res = await confirm({ message, initialValue });
|
|
11
|
+
if (isCancel(res))
|
|
12
|
+
cancel();
|
|
13
|
+
return res;
|
|
14
|
+
}
|
|
15
|
+
export async function promptSelect(message, options) {
|
|
16
|
+
const res = await select({ message, options: options });
|
|
17
|
+
if (isCancel(res))
|
|
18
|
+
cancel();
|
|
19
|
+
return res;
|
|
20
|
+
}
|
|
21
|
+
export async function promptMulti(message, options, required = false) {
|
|
22
|
+
const res = await multiselect({ message, options: options, required });
|
|
23
|
+
if (isCancel(res))
|
|
24
|
+
cancel();
|
|
25
|
+
return res;
|
|
26
|
+
}
|
package/dist/ui/theme.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
export const c = {
|
|
3
|
+
primary: chalk.hex('#FF6B35'),
|
|
4
|
+
accent: chalk.hex('#FFB347'),
|
|
5
|
+
green: chalk.hex('#4CAF50'),
|
|
6
|
+
red: chalk.hex('#E53935'),
|
|
7
|
+
yellow: chalk.hex('#FFC107'),
|
|
8
|
+
blue: chalk.hex('#6C8EBF'),
|
|
9
|
+
cyan: chalk.hex('#4ECDC4'),
|
|
10
|
+
dim: chalk.dim,
|
|
11
|
+
bold: chalk.bold,
|
|
12
|
+
};
|
|
13
|
+
export function setColorEnabled(on) {
|
|
14
|
+
chalk.level = on ? (chalk.level || 1) : 0;
|
|
15
|
+
}
|
|
16
|
+
export { chalk };
|
package/package.json
CHANGED
|
@@ -1,63 +1,44 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@powernukkitx/cli",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "PowerNukkitX CLI — Manage your Minecraft Bedrock servers with style",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"powernukkitx",
|
|
7
|
-
"pnx",
|
|
8
|
-
"minecraft",
|
|
9
|
-
"bedrock",
|
|
10
|
-
"server",
|
|
11
|
-
"cli"
|
|
12
|
-
],
|
|
13
|
-
"license": "MIT",
|
|
14
|
-
"author": "AzaleeX",
|
|
15
|
-
"type": "module",
|
|
16
|
-
"main": "dist/
|
|
17
|
-
"bin": {
|
|
18
|
-
"pnx": "dist/
|
|
19
|
-
},
|
|
20
|
-
"files": [
|
|
21
|
-
"dist"
|
|
22
|
-
],
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"rcedit": "^5.0.2",
|
|
46
|
-
"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
|
-
}
|
|
63
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@powernukkitx/cli",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "PowerNukkitX CLI — Manage your Minecraft Bedrock servers with style",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"powernukkitx",
|
|
7
|
+
"pnx",
|
|
8
|
+
"minecraft",
|
|
9
|
+
"bedrock",
|
|
10
|
+
"server",
|
|
11
|
+
"cli"
|
|
12
|
+
],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "AzaleeX",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"main": "dist/cli.js",
|
|
17
|
+
"bin": {
|
|
18
|
+
"pnx": "dist/cli.js"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/PowerNukkitX/cli.git"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/PowerNukkitX/cli/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/PowerNukkitX/cli#readme",
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20.12.0"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"start": "node dist/cli.js",
|
|
36
|
+
"check": "find dist -name '*.js' -print0 | xargs -0 -n1 node --check",
|
|
37
|
+
"smoke": "node dist/cli.js --help"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@clack/prompts": "^1.7.0",
|
|
41
|
+
"chalk": "^5.6.2",
|
|
42
|
+
"citty": "^0.2.2"
|
|
43
|
+
}
|
|
44
|
+
}
|