@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.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +110 -101
  3. package/dist/cli.js +34 -0
  4. package/dist/commands/backup.d.ts +2 -1
  5. package/dist/commands/backup.js +23 -74
  6. package/dist/commands/config.d.ts +1 -1
  7. package/dist/commands/config.js +60 -114
  8. package/dist/commands/console.d.ts +2 -0
  9. package/dist/commands/console.js +98 -0
  10. package/dist/commands/doctor.d.ts +2 -1
  11. package/dist/commands/doctor.js +97 -119
  12. package/dist/commands/info.d.ts +2 -1
  13. package/dist/commands/info.js +41 -36
  14. package/dist/commands/init.d.ts +2 -4
  15. package/dist/commands/init.js +87 -73
  16. package/dist/commands/install.js +45 -0
  17. package/dist/commands/logs.d.ts +2 -0
  18. package/dist/commands/logs.js +60 -0
  19. package/dist/commands/menu.js +94 -0
  20. package/dist/commands/plugin/index.d.ts +2 -0
  21. package/dist/commands/plugin/index.js +13 -0
  22. package/dist/commands/plugin/info.d.ts +2 -0
  23. package/dist/commands/plugin/info.js +39 -0
  24. package/dist/commands/plugin/install.d.ts +2 -0
  25. package/dist/commands/plugin/install.js +56 -0
  26. package/dist/commands/plugin/installed.d.ts +2 -0
  27. package/dist/commands/plugin/installed.js +41 -0
  28. package/dist/commands/plugin/list.d.ts +2 -0
  29. package/dist/commands/plugin/list.js +47 -0
  30. package/dist/commands/plugin/remove.d.ts +2 -0
  31. package/dist/commands/plugin/remove.js +44 -0
  32. package/dist/commands/plugin/search.d.ts +2 -0
  33. package/dist/commands/plugin/search.js +11 -0
  34. package/dist/commands/plugin/update.d.ts +2 -0
  35. package/dist/commands/plugin/update.js +52 -0
  36. package/dist/commands/start.d.ts +2 -7
  37. package/dist/commands/start.js +74 -108
  38. package/dist/commands/stop.d.ts +2 -0
  39. package/dist/commands/stop.js +26 -0
  40. package/dist/commands/update.d.ts +2 -5
  41. package/dist/commands/update.js +34 -55
  42. package/dist/commands/use.d.ts +2 -0
  43. package/dist/commands/use.js +39 -0
  44. package/dist/infra/http.js +119 -0
  45. package/dist/infra/paths.js +15 -0
  46. package/dist/infra/store.js +24 -0
  47. package/dist/services/backup.js +34 -0
  48. package/dist/services/plugins.js +139 -0
  49. package/dist/services/process.js +41 -0
  50. package/dist/services/release.js +51 -0
  51. package/dist/services/server.js +81 -0
  52. package/dist/ui/output.js +67 -0
  53. package/dist/ui/prompts.js +26 -0
  54. package/dist/ui/theme.js +16 -0
  55. package/package.json +44 -63
  56. package/dist/bundle.js +0 -18871
  57. package/dist/commands/plugin.d.ts +0 -12
  58. package/dist/commands/plugin.js +0 -385
  59. package/dist/index.d.ts +0 -2
  60. package/dist/index.js +0 -260
  61. package/dist/lang/en.json +0 -178
  62. package/dist/lang/fr.json +0 -178
  63. package/dist/lang/index.d.ts +0 -7
  64. package/dist/lang/index.js +0 -83
  65. package/dist/plugins.json +0 -66
  66. package/dist/types.d.ts +0 -61
  67. package/dist/types.js +0 -1
  68. package/dist/utils/api.d.ts +0 -18
  69. package/dist/utils/api.js +0 -74
  70. package/dist/utils/github.d.ts +0 -27
  71. package/dist/utils/github.js +0 -187
  72. package/dist/utils/logger.d.ts +0 -15
  73. package/dist/utils/logger.js +0 -72
  74. package/dist/utils/pause.d.ts +0 -1
  75. package/dist/utils/pause.js +0 -6
  76. package/dist/utils/plugins.d.ts +0 -4
  77. package/dist/utils/plugins.js +0 -34
  78. package/dist/utils/server.d.ts +0 -3
  79. package/dist/utils/server.js +0 -39
  80. package/dist/utils/updater.d.ts +0 -1
  81. package/dist/utils/updater.js +0 -86
@@ -0,0 +1,60 @@
1
+ import { existsSync, readdirSync, statSync, readFileSync, watch } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { defineCommand } from 'citty';
4
+ import { intro, log } from '../ui/output.js';
5
+ import { c } from '../ui/theme.js';
6
+ import { resolveServerDir } from '../services/server.js';
7
+ export const logsCmd = defineCommand({
8
+ meta: { description: 'View server logs' },
9
+ args: {
10
+ lines: { type: 'string', description: 'Number of lines to show', default: '50' },
11
+ follow: { type: 'boolean', description: 'Follow log output', default: true },
12
+ },
13
+ async run({ args }) {
14
+ intro(`📋 ${"Server logs"}`);
15
+ const serverPath = await resolveServerDir();
16
+ if (!serverPath) {
17
+ log.error("No server detected");
18
+ process.exit(1);
19
+ }
20
+ const logsDir = join(serverPath, 'logs');
21
+ if (!existsSync(logsDir)) {
22
+ log.error("No log files found");
23
+ process.exit(1);
24
+ }
25
+ const files = readdirSync(logsDir)
26
+ .filter(f => f.endsWith('.log') || f === 'latest.log')
27
+ .map(f => ({ name: f, mtime: statSync(join(logsDir, f)).mtime.getTime() }))
28
+ .sort((a, b) => b.mtime - a.mtime);
29
+ if (files.length === 0) {
30
+ log.error("No log files found");
31
+ process.exit(1);
32
+ }
33
+ const logPath = join(logsDir, files[0].name);
34
+ log.step(`Reading ${files[0].name}`);
35
+ const n = parseInt(args.lines) || 50;
36
+ const content = readFileSync(logPath, 'utf-8');
37
+ const colorize = (line) => line.replace(/\[ERROR\]/g, c.red('[ERROR]'))
38
+ .replace(/\[WARN\]/g, c.yellow('[WARN]'))
39
+ .replace(/\[INFO\]/g, c.blue('[INFO]'));
40
+ for (const line of content.split('\n').filter(Boolean).slice(-n)) {
41
+ process.stdout.write(colorize(line) + '\n');
42
+ }
43
+ if (args.follow) {
44
+ log.dim("Live mode (Ctrl+C to quit)");
45
+ process.on('SIGINT', () => { process.stdout.write('\n'); process.exit(0); });
46
+ let buf = content;
47
+ watch(logPath, () => {
48
+ try {
49
+ const updated = readFileSync(logPath, 'utf-8');
50
+ if (updated.length > buf.length) {
51
+ process.stdout.write(updated.slice(buf.length));
52
+ buf = updated;
53
+ }
54
+ }
55
+ catch { }
56
+ });
57
+ await new Promise(() => { });
58
+ }
59
+ },
60
+ });
@@ -0,0 +1,94 @@
1
+ import { intro, outro, log } from '../ui/output.js';
2
+ import { promptSelect, promptText } from '../ui/prompts.js';
3
+ import { resolveServerDir } from '../services/server.js';
4
+ import { loadPid, isAlive } from '../services/process.js';
5
+ import { c } from '../ui/theme.js';
6
+ export async function runMenu() {
7
+ while (true) {
8
+ const done = await menuStep();
9
+ if (done)
10
+ return;
11
+ await promptText({ message: "Press enter to return to the menu", placeholder: '' });
12
+ }
13
+ }
14
+ async function menuStep() {
15
+ const serverPath = await resolveServerDir();
16
+ const hasServer = serverPath !== null;
17
+ const pid = await loadPid();
18
+ const running = pid !== null && isAlive(pid.pid);
19
+ intro(c.green(`⚡ ${"PowerNukkitX Menu"}`));
20
+ if (running)
21
+ log.info(`Server running (PID ${String(pid.pid)})`);
22
+ else if (hasServer)
23
+ log.info("Server stopped");
24
+ else
25
+ log.warn("No server detected");
26
+ const options = [];
27
+ if (!hasServer)
28
+ options.push({ value: 'create', label: "Create a server" });
29
+ if (hasServer && !running)
30
+ options.push({ value: 'start', label: "Start the server" });
31
+ if (running)
32
+ options.push({ value: 'stop', label: "Stop the server" });
33
+ if (hasServer) {
34
+ options.push({ value: 'plugins', label: "Manage plugins" });
35
+ options.push({ value: 'install', label: "Install a plugin" });
36
+ options.push({ value: 'update', label: "Update the core" });
37
+ options.push({ value: 'backup', label: "Backup" });
38
+ }
39
+ options.push({ value: 'doctor', label: "Diagnostics" });
40
+ options.push({ value: 'info', label: "Information" });
41
+ options.push({ value: 'quit', label: "Quit" });
42
+ const choice = await promptSelect("What do you want to do?", options);
43
+ switch (choice) {
44
+ case 'create': {
45
+ const { initCmd } = await import('./init.js');
46
+ await initCmd.run({ args: {} });
47
+ break;
48
+ }
49
+ case 'start': {
50
+ const { runStart } = await import('./start.js');
51
+ await runStart({});
52
+ break;
53
+ }
54
+ case 'stop': {
55
+ const { runStop } = await import('./stop.js');
56
+ await runStop();
57
+ break;
58
+ }
59
+ case 'plugins': {
60
+ const { installCmd } = await import('./plugin/install.js');
61
+ await installCmd.run({ args: {} });
62
+ break;
63
+ }
64
+ case 'install': {
65
+ const { runInstall } = await import('./install.js');
66
+ await runInstall();
67
+ break;
68
+ }
69
+ case 'update': {
70
+ const { runUpdate } = await import('./update.js');
71
+ await runUpdate();
72
+ break;
73
+ }
74
+ case 'backup': {
75
+ const { runBackup } = await import('./backup.js');
76
+ await runBackup();
77
+ break;
78
+ }
79
+ case 'doctor': {
80
+ const { doctorCmd } = await import('./doctor.js');
81
+ await doctorCmd.run({ args: {} });
82
+ break;
83
+ }
84
+ case 'info': {
85
+ const { infoCmd } = await import('./info.js');
86
+ await infoCmd.run({ args: {} });
87
+ break;
88
+ }
89
+ case 'quit':
90
+ outro("See you soon!");
91
+ return true;
92
+ }
93
+ return false;
94
+ }
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from 'citty';
2
+ export declare const pluginCmd: CommandDef;
@@ -0,0 +1,13 @@
1
+ import { defineCommand } from 'citty';
2
+ export const pluginCmd = defineCommand({
3
+ meta: { description: 'Plugin management' },
4
+ subCommands: {
5
+ install: () => import('./install.js').then(m => m.installCmd),
6
+ list: () => import('./list.js').then(m => m.listCmd),
7
+ search: () => import('./search.js').then(m => m.searchCmd),
8
+ info: () => import('./info.js').then(m => m.infoCmd),
9
+ update: () => import('./update.js').then(m => m.updateCmd),
10
+ remove: () => import('./remove.js').then(m => m.removeCmd),
11
+ installed: () => import('./installed.js').then(m => m.installedCmd),
12
+ },
13
+ });
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from 'citty';
2
+ export declare const infoCmd: CommandDef;
@@ -0,0 +1,39 @@
1
+ import { defineCommand } from 'citty';
2
+ import { intro, outro, log, spin } from '../../ui/output.js';
3
+ import { c } from '../../ui/theme.js';
4
+ import { promptText } from '../../ui/prompts.js';
5
+ import { getPluginDetail } from '../../services/plugins.js';
6
+ export const infoCmd = defineCommand({
7
+ meta: { description: 'Show plugin details' },
8
+ args: { slug: { type: 'positional', description: 'Plugin slug', required: false } },
9
+ async run({ args }) {
10
+ const slug = args.slug ?? await promptText({ message: "Plugin slug:" });
11
+ const sp = spin(slug);
12
+ try {
13
+ const d = await getPluginDetail(slug);
14
+ sp.stop(d.name);
15
+ intro(`📖 ${d.name}`);
16
+ if (d.description)
17
+ log.dim(d.description);
18
+ log.info(`Author: ${c.accent(d.author)}`);
19
+ log.info(`Version: ${c.green(d.latest_version)}`);
20
+ log.info(`Downloads: ${c.cyan(d.downloads >= 1000 ? (d.downloads / 1000).toFixed(1) + 'k' : String(d.downloads))}`);
21
+ log.info(`Updated: ${new Date(d.updated_at).toLocaleDateString()}`);
22
+ if (d.tags.length)
23
+ log.info(`Tags: ${d.tags.map(tag => c.cyan(tag)).join(', ')}`);
24
+ if (d.repository_url)
25
+ log.info(`Repo: ${c.blue(d.repository_url)}`);
26
+ if (d.releases.length) {
27
+ log.step("Available versions:");
28
+ for (const r of d.releases.slice(0, 5)) {
29
+ const stable = r.is_stable ? c.green('●') : c.dim('○');
30
+ log.raw(` ${stable} v${r.version} ${c.dim(new Date(r.created_at).toLocaleDateString())}`);
31
+ }
32
+ }
33
+ outro('');
34
+ }
35
+ catch {
36
+ sp.fail(`Plugin not found: ${slug}`);
37
+ }
38
+ },
39
+ });
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from 'citty';
2
+ export declare const installCmd: CommandDef;
@@ -0,0 +1,56 @@
1
+ import { join } from 'node:path';
2
+ import { defineCommand } from 'citty';
3
+ import { intro, outro, log, spin, progress } from '../../ui/output.js';
4
+ import { promptMulti } from '../../ui/prompts.js';
5
+ import { resolveServerDir } from '../../services/server.js';
6
+ import { searchPlugins, installPlugin, installFromUrl } from '../../services/plugins.js';
7
+ export const installCmd = defineCommand({
8
+ meta: { description: 'Install plugins' },
9
+ args: { names: { type: 'positional', description: 'Plugin slugs or .jar URLs', required: false } },
10
+ async run({ args }) {
11
+ const serverPath = (await resolveServerDir()) ?? '.';
12
+ const pluginsDir = join(serverPath, 'plugins');
13
+ let names = args.names ? [args.names] : [];
14
+ if (names.length === 0) {
15
+ intro(`📦 ${"Install plugins"}`);
16
+ const sp = spin("Fetching from marketplace...");
17
+ const resp = await searchPlugins({ sort: 'stars', limit: 50 });
18
+ sp.stop(`${resp.plugins.length} plugins`);
19
+ names = await promptMulti("Select plugins to install:", resp.plugins.map(p => ({ value: p.slug, label: p.name, hint: p.description })));
20
+ if (names.length === 0) {
21
+ log.info("No plugin selected");
22
+ return;
23
+ }
24
+ }
25
+ intro(`📦 ${"Install plugins"}`);
26
+ for (const name of names) {
27
+ if (name.startsWith('http://') || name.startsWith('https://')) {
28
+ try {
29
+ const file = await installFromUrl(name, pluginsDir, (pct, f) => progress(pct, f));
30
+ log.success(`${file} installed`);
31
+ }
32
+ catch {
33
+ log.error("URL must point to a .jar file");
34
+ }
35
+ continue;
36
+ }
37
+ const sp = spin(name);
38
+ try {
39
+ const entry = await installPlugin(name, pluginsDir, (pct, f) => progress(pct, f));
40
+ sp.stop(`${entry.name} v${entry.version}`);
41
+ log.success(`${entry.name} installed`);
42
+ }
43
+ catch (e) {
44
+ const msg = e.message;
45
+ if (msg.startsWith('no-release'))
46
+ sp.fail(`No release available for ${name}`);
47
+ else if (msg.startsWith('no-file'))
48
+ sp.fail(`No file for ${name}`);
49
+ else
50
+ sp.fail(`Error installing ${name}`);
51
+ }
52
+ }
53
+ log.info("Restart the server to load plugins");
54
+ outro('');
55
+ },
56
+ });
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from 'citty';
2
+ export declare const installedCmd: CommandDef;
@@ -0,0 +1,41 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { readdir } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { defineCommand } from 'citty';
5
+ import { intro, outro, log, isJson, json } from '../../ui/output.js';
6
+ import { c } from '../../ui/theme.js';
7
+ import { resolveServerDir } from '../../services/server.js';
8
+ import { getInstalled } from '../../services/plugins.js';
9
+ export const installedCmd = defineCommand({
10
+ meta: { description: 'List installed plugins' },
11
+ args: {},
12
+ async run() {
13
+ const serverPath = await resolveServerDir();
14
+ const pluginsDir = serverPath ? join(serverPath, 'plugins') : 'plugins';
15
+ if (!existsSync(pluginsDir)) {
16
+ if (isJson())
17
+ json({ plugins: [] });
18
+ log.warn("No plugins/ directory");
19
+ return;
20
+ }
21
+ const jars = (await readdir(pluginsDir)).filter(f => f.endsWith('.jar'));
22
+ if (isJson())
23
+ json({ plugins: jars.map(j => ({ file: j })) });
24
+ intro(`📁 ${"Installed plugins"}`);
25
+ if (jars.length === 0) {
26
+ log.warn("No plugins installed");
27
+ return;
28
+ }
29
+ const tracked = await getInstalled();
30
+ for (const j of jars) {
31
+ const meta = tracked.find(p => p.fileName === j);
32
+ if (meta)
33
+ log.raw(` ${c.accent('●')} ${c.bold(meta.name)} ${c.dim('v' + meta.version)}`);
34
+ else
35
+ log.raw(` ${c.dim('○')} ${j.replace('.jar', '')}`);
36
+ }
37
+ log.info(`${String(jars.length)} plugin(s)`);
38
+ log.info("Restart the server to load plugins");
39
+ outro('');
40
+ },
41
+ });
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from 'citty';
2
+ export declare const listCmd: CommandDef;
@@ -0,0 +1,47 @@
1
+ import { defineCommand } from 'citty';
2
+ import { intro, outro, log, spin, isJson, json } from '../../ui/output.js';
3
+ import { c } from '../../ui/theme.js';
4
+ import { searchPlugins } from '../../services/plugins.js';
5
+ function stars(n) {
6
+ const full = '★'.repeat(Math.min(Math.floor(n / 10), 5));
7
+ const empty = '☆'.repeat(5 - full.length);
8
+ return (n >= 50 ? c.yellow : c.dim)(full + empty);
9
+ }
10
+ function dl(n) { return n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n); }
11
+ export async function renderList(params) {
12
+ const sp = spin("Fetching from marketplace...");
13
+ try {
14
+ const resp = await searchPlugins(params);
15
+ sp.stop(`${resp.plugins.length} plugins`);
16
+ if (isJson())
17
+ json({ plugins: resp.plugins, count: resp.count });
18
+ intro(`📋 ${"Plugin catalog"}`);
19
+ for (const p of resp.plugins) {
20
+ log.raw(` ${c.accent.bold(p.name)} ${c.dim('by ' + p.author)} ${stars(p.stars)} ${c.cyan(dl(p.downloads) + ' dl')} ${c.dim('v' + p.version)}`);
21
+ if (p.description)
22
+ log.raw(` ${c.dim(p.description)}`);
23
+ log.raw(` ${c.dim('→')} ${c.blue('pnx plugin install ' + p.slug)}\n`);
24
+ }
25
+ if (resp.count > resp.plugins.length)
26
+ log.info(`${resp.plugins.length}/${resp.count}`);
27
+ outro('');
28
+ }
29
+ catch (e) {
30
+ sp.fail(`Error: ${e.message}`);
31
+ }
32
+ }
33
+ export const listCmd = defineCommand({
34
+ meta: { description: 'List available plugins' },
35
+ args: {
36
+ sort: { type: 'string', description: 'Sort: newest|updated|stars' },
37
+ limit: { type: 'string', description: 'Results per page', default: '20' },
38
+ page: { type: 'string', description: 'Page number', default: '1' },
39
+ query: { type: 'string', description: 'Search query' },
40
+ },
41
+ run: ({ args }) => renderList({
42
+ sort: args.sort,
43
+ limit: parseInt(args.limit),
44
+ page: parseInt(args.page),
45
+ q: args.query,
46
+ }),
47
+ });
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from 'citty';
2
+ export declare const removeCmd: CommandDef;
@@ -0,0 +1,44 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { readdir, rm } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { defineCommand } from 'citty';
5
+ import { intro, outro, log } from '../../ui/output.js';
6
+ import { promptMulti, promptConfirm } from '../../ui/prompts.js';
7
+ import { resolveServerDir } from '../../services/server.js';
8
+ import { removeInstalled } from '../../services/plugins.js';
9
+ export const removeCmd = defineCommand({
10
+ meta: { description: 'Remove installed plugins' },
11
+ args: { names: { type: 'positional', description: 'Plugin names', required: false } },
12
+ async run({ args }) {
13
+ const serverPath = (await resolveServerDir()) ?? '.';
14
+ const pluginsDir = join(serverPath, 'plugins');
15
+ if (!existsSync(pluginsDir)) {
16
+ log.error("No plugins/ directory");
17
+ return;
18
+ }
19
+ const jars = (await readdir(pluginsDir)).filter(f => f.endsWith('.jar'));
20
+ if (jars.length === 0) {
21
+ log.warn("No plugins installed");
22
+ return;
23
+ }
24
+ let names = args.names ? [args.names] : [];
25
+ if (names.length === 0) {
26
+ names = await promptMulti("Select plugins to remove:", jars.map(j => ({ value: j, label: j.replace('.jar', '') })));
27
+ }
28
+ intro(`🗑️ ${"Remove plugins"}`);
29
+ for (const name of names) {
30
+ const target = name.endsWith('.jar') ? name : `${name}.jar`;
31
+ const found = jars.find(j => j.toLowerCase() === target.toLowerCase());
32
+ if (!found) {
33
+ log.warn(`Plugin not found: ${name}`);
34
+ continue;
35
+ }
36
+ if (await promptConfirm(`Remove ${found}?`, false)) {
37
+ await rm(join(pluginsDir, found));
38
+ await removeInstalled(found.replace('.jar', ''));
39
+ log.success(`${found} removed`);
40
+ }
41
+ }
42
+ outro('');
43
+ },
44
+ });
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from 'citty';
2
+ export declare const searchCmd: CommandDef;
@@ -0,0 +1,11 @@
1
+ import { defineCommand } from 'citty';
2
+ import { promptText } from '../../ui/prompts.js';
3
+ import { renderList } from './list.js';
4
+ export const searchCmd = defineCommand({
5
+ meta: { description: 'Search plugins' },
6
+ args: { term: { type: 'positional', description: 'Search term', required: false } },
7
+ async run({ args }) {
8
+ const term = args.term ?? await promptText({ message: "Search plugins:" });
9
+ await renderList({ q: term, limit: 20, page: 1 });
10
+ },
11
+ });
@@ -0,0 +1,2 @@
1
+ import type { CommandDef } from 'citty';
2
+ export declare const updateCmd: CommandDef;
@@ -0,0 +1,52 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { readdir } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { defineCommand } from 'citty';
5
+ import { intro, outro, log, spin, progress } from '../../ui/output.js';
6
+ import { promptMulti } from '../../ui/prompts.js';
7
+ import { resolveServerDir } from '../../services/server.js';
8
+ import { getInstalled, updatePlugin } from '../../services/plugins.js';
9
+ export const updateCmd = defineCommand({
10
+ meta: { description: 'Update installed plugins' },
11
+ args: { names: { type: 'positional', description: 'Plugin slugs', required: false } },
12
+ async run({ args }) {
13
+ const serverPath = (await resolveServerDir()) ?? '.';
14
+ const pluginsDir = join(serverPath, 'plugins');
15
+ if (!existsSync(pluginsDir)) {
16
+ log.error("No plugins/ directory");
17
+ return;
18
+ }
19
+ const jars = (await readdir(pluginsDir)).filter(f => f.endsWith('.jar'));
20
+ let names = args.names ? [args.names] : [];
21
+ if (names.length === 0) {
22
+ names = await promptMulti("Select plugins to update:", jars.map(j => ({ value: j.replace('.jar', ''), label: j.replace('.jar', '') })));
23
+ }
24
+ intro(`🔄 ${"Update plugins"}`);
25
+ const tracked = await getInstalled();
26
+ for (const slug of names) {
27
+ const sp = spin(slug);
28
+ try {
29
+ const meta = tracked.find(p => p.slug === slug);
30
+ const existingJar = jars.find(j => j.toLowerCase().startsWith(slug.toLowerCase()));
31
+ const entry = await updatePlugin(slug, pluginsDir, meta?.version, existingJar, (pct, f) => progress(pct, f));
32
+ if (!entry) {
33
+ sp.stop(`${slug} is already up to date (v${meta?.version ?? '?'})`);
34
+ continue;
35
+ }
36
+ sp.stop(`${entry.name} v${entry.version}`);
37
+ log.success(`${entry.name} updated to v${entry.version}`);
38
+ }
39
+ catch (e) {
40
+ const msg = e.message;
41
+ if (msg.startsWith('no-release'))
42
+ sp.fail(`No release available for ${slug}`);
43
+ else if (msg.startsWith('no-file'))
44
+ sp.fail(`No file for ${slug}`);
45
+ else
46
+ sp.fail(`Error installing ${slug}`);
47
+ }
48
+ }
49
+ log.info("Restart the server to load plugins");
50
+ outro('');
51
+ },
52
+ });
@@ -1,7 +1,2 @@
1
- export declare function startCmd(options: {
2
- dir?: string;
3
- generateOnly?: boolean;
4
- restart?: boolean;
5
- memory?: string;
6
- interactive?: boolean;
7
- }): Promise<void>;
1
+ import type { CommandDef } from 'citty';
2
+ export declare const startCmd: CommandDef;