@minecraft-docker/mcctl 1.6.0 → 1.6.2

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 (193) hide show
  1. package/dist/application/index.d.ts +2 -0
  2. package/dist/application/index.d.ts.map +1 -0
  3. package/dist/application/index.js +5 -0
  4. package/dist/application/index.js.map +1 -0
  5. package/dist/commands/backup.d.ts +16 -0
  6. package/dist/commands/backup.d.ts.map +1 -0
  7. package/dist/commands/backup.js +182 -0
  8. package/dist/commands/backup.js.map +1 -0
  9. package/dist/commands/ban.d.ts +14 -0
  10. package/dist/commands/ban.d.ts.map +1 -0
  11. package/dist/commands/ban.js +418 -0
  12. package/dist/commands/ban.js.map +1 -0
  13. package/dist/commands/config.d.ts +18 -0
  14. package/dist/commands/config.d.ts.map +1 -0
  15. package/dist/commands/config.js +106 -0
  16. package/dist/commands/config.js.map +1 -0
  17. package/dist/commands/console/api.d.ts +19 -0
  18. package/dist/commands/console/api.d.ts.map +1 -0
  19. package/dist/commands/console/api.js +399 -0
  20. package/dist/commands/console/api.js.map +1 -0
  21. package/dist/commands/console/index.d.ts +10 -0
  22. package/dist/commands/console/index.d.ts.map +1 -0
  23. package/dist/commands/console/index.js +12 -0
  24. package/dist/commands/console/index.js.map +1 -0
  25. package/dist/commands/console/init.d.ts +47 -0
  26. package/dist/commands/console/init.d.ts.map +1 -0
  27. package/dist/commands/console/init.js +491 -0
  28. package/dist/commands/console/init.js.map +1 -0
  29. package/dist/commands/console/remove.d.ts +25 -0
  30. package/dist/commands/console/remove.d.ts.map +1 -0
  31. package/dist/commands/console/remove.js +189 -0
  32. package/dist/commands/console/remove.js.map +1 -0
  33. package/dist/commands/console/service.d.ts +40 -0
  34. package/dist/commands/console/service.d.ts.map +1 -0
  35. package/dist/commands/console/service.js +321 -0
  36. package/dist/commands/console/service.js.map +1 -0
  37. package/dist/commands/console/user.d.ts +17 -0
  38. package/dist/commands/console/user.d.ts.map +1 -0
  39. package/dist/commands/console/user.js +431 -0
  40. package/dist/commands/console/user.js.map +1 -0
  41. package/dist/commands/create.d.ts +22 -0
  42. package/dist/commands/create.d.ts.map +1 -0
  43. package/dist/commands/create.js +86 -0
  44. package/dist/commands/create.js.map +1 -0
  45. package/dist/commands/delete.d.ts +17 -0
  46. package/dist/commands/delete.d.ts.map +1 -0
  47. package/dist/commands/delete.js +74 -0
  48. package/dist/commands/delete.js.map +1 -0
  49. package/dist/commands/exec.d.ts +10 -0
  50. package/dist/commands/exec.d.ts.map +1 -0
  51. package/dist/commands/exec.js +29 -0
  52. package/dist/commands/exec.js.map +1 -0
  53. package/dist/commands/index.d.ts +21 -0
  54. package/dist/commands/index.d.ts.map +1 -0
  55. package/dist/commands/index.js +23 -0
  56. package/dist/commands/index.js.map +1 -0
  57. package/dist/commands/init.d.ts +9 -0
  58. package/dist/commands/init.d.ts.map +1 -0
  59. package/dist/commands/init.js +218 -0
  60. package/dist/commands/init.js.map +1 -0
  61. package/dist/commands/kick.d.ts +12 -0
  62. package/dist/commands/kick.d.ts.map +1 -0
  63. package/dist/commands/kick.js +71 -0
  64. package/dist/commands/kick.js.map +1 -0
  65. package/dist/commands/migrate.d.ts +18 -0
  66. package/dist/commands/migrate.d.ts.map +1 -0
  67. package/dist/commands/migrate.js +470 -0
  68. package/dist/commands/migrate.js.map +1 -0
  69. package/dist/commands/mod.d.ts +19 -0
  70. package/dist/commands/mod.d.ts.map +1 -0
  71. package/dist/commands/mod.js +441 -0
  72. package/dist/commands/mod.js.map +1 -0
  73. package/dist/commands/msg.d.ts +12 -0
  74. package/dist/commands/msg.d.ts.map +1 -0
  75. package/dist/commands/msg.js +273 -0
  76. package/dist/commands/msg.js.map +1 -0
  77. package/dist/commands/op.d.ts +12 -0
  78. package/dist/commands/op.d.ts.map +1 -0
  79. package/dist/commands/op.js +220 -0
  80. package/dist/commands/op.js.map +1 -0
  81. package/dist/commands/player-online.d.ts +11 -0
  82. package/dist/commands/player-online.d.ts.map +1 -0
  83. package/dist/commands/player-online.js +153 -0
  84. package/dist/commands/player-online.js.map +1 -0
  85. package/dist/commands/player.d.ts +18 -0
  86. package/dist/commands/player.d.ts.map +1 -0
  87. package/dist/commands/player.js +352 -0
  88. package/dist/commands/player.js.map +1 -0
  89. package/dist/commands/server-backup.d.ts +36 -0
  90. package/dist/commands/server-backup.d.ts.map +1 -0
  91. package/dist/commands/server-backup.js +320 -0
  92. package/dist/commands/server-backup.js.map +1 -0
  93. package/dist/commands/server-restore.d.ts +13 -0
  94. package/dist/commands/server-restore.d.ts.map +1 -0
  95. package/dist/commands/server-restore.js +294 -0
  96. package/dist/commands/server-restore.js.map +1 -0
  97. package/dist/commands/status.d.ts +13 -0
  98. package/dist/commands/status.d.ts.map +1 -0
  99. package/dist/commands/status.js +325 -0
  100. package/dist/commands/status.js.map +1 -0
  101. package/dist/commands/whitelist.d.ts +12 -0
  102. package/dist/commands/whitelist.d.ts.map +1 -0
  103. package/dist/commands/whitelist.js +316 -0
  104. package/dist/commands/whitelist.js.map +1 -0
  105. package/dist/commands/world.d.ts +18 -0
  106. package/dist/commands/world.d.ts.map +1 -0
  107. package/dist/commands/world.js +238 -0
  108. package/dist/commands/world.js.map +1 -0
  109. package/dist/domain/index.d.ts +2 -0
  110. package/dist/domain/index.d.ts.map +1 -0
  111. package/dist/domain/index.js +7 -0
  112. package/dist/domain/index.js.map +1 -0
  113. package/dist/index.d.ts +3 -0
  114. package/dist/index.d.ts.map +1 -0
  115. package/dist/index.js +766 -0
  116. package/dist/index.js.map +1 -0
  117. package/dist/infrastructure/adapters/ClackPromptAdapter.d.ts +30 -0
  118. package/dist/infrastructure/adapters/ClackPromptAdapter.d.ts.map +1 -0
  119. package/dist/infrastructure/adapters/ClackPromptAdapter.js +430 -0
  120. package/dist/infrastructure/adapters/ClackPromptAdapter.js.map +1 -0
  121. package/dist/infrastructure/adapters/Pm2ServiceManagerAdapter.d.ts +78 -0
  122. package/dist/infrastructure/adapters/Pm2ServiceManagerAdapter.d.ts.map +1 -0
  123. package/dist/infrastructure/adapters/Pm2ServiceManagerAdapter.js +391 -0
  124. package/dist/infrastructure/adapters/Pm2ServiceManagerAdapter.js.map +1 -0
  125. package/dist/infrastructure/adapters/index.d.ts +4 -0
  126. package/dist/infrastructure/adapters/index.d.ts.map +1 -0
  127. package/dist/infrastructure/adapters/index.js +6 -0
  128. package/dist/infrastructure/adapters/index.js.map +1 -0
  129. package/dist/infrastructure/di/container.d.ts +47 -0
  130. package/dist/infrastructure/di/container.d.ts.map +1 -0
  131. package/dist/infrastructure/di/container.js +140 -0
  132. package/dist/infrastructure/di/container.js.map +1 -0
  133. package/dist/infrastructure/di/index.d.ts +2 -0
  134. package/dist/infrastructure/di/index.d.ts.map +1 -0
  135. package/dist/infrastructure/di/index.js +2 -0
  136. package/dist/infrastructure/di/index.js.map +1 -0
  137. package/dist/infrastructure/index.d.ts +5 -0
  138. package/dist/infrastructure/index.d.ts.map +1 -0
  139. package/dist/infrastructure/index.js +8 -0
  140. package/dist/infrastructure/index.js.map +1 -0
  141. package/dist/lib/admin-config.d.ts +66 -0
  142. package/dist/lib/admin-config.d.ts.map +1 -0
  143. package/dist/lib/admin-config.js +130 -0
  144. package/dist/lib/admin-config.js.map +1 -0
  145. package/dist/lib/mojang-api.d.ts +51 -0
  146. package/dist/lib/mojang-api.d.ts.map +1 -0
  147. package/dist/lib/mojang-api.js +136 -0
  148. package/dist/lib/mojang-api.js.map +1 -0
  149. package/dist/lib/player-cache.d.ts +79 -0
  150. package/dist/lib/player-cache.d.ts.map +1 -0
  151. package/dist/lib/player-cache.js +302 -0
  152. package/dist/lib/player-cache.js.map +1 -0
  153. package/dist/lib/player-json.d.ts +69 -0
  154. package/dist/lib/player-json.d.ts.map +1 -0
  155. package/dist/lib/player-json.js +96 -0
  156. package/dist/lib/player-json.js.map +1 -0
  157. package/dist/lib/pm2-utils.d.ts +71 -0
  158. package/dist/lib/pm2-utils.d.ts.map +1 -0
  159. package/dist/lib/pm2-utils.js +155 -0
  160. package/dist/lib/pm2-utils.js.map +1 -0
  161. package/dist/lib/prompts/action-select.d.ts +23 -0
  162. package/dist/lib/prompts/action-select.d.ts.map +1 -0
  163. package/dist/lib/prompts/action-select.js +124 -0
  164. package/dist/lib/prompts/action-select.js.map +1 -0
  165. package/dist/lib/prompts/index.d.ts +7 -0
  166. package/dist/lib/prompts/index.d.ts.map +1 -0
  167. package/dist/lib/prompts/index.js +7 -0
  168. package/dist/lib/prompts/index.js.map +1 -0
  169. package/dist/lib/prompts/ip-select.d.ts +25 -0
  170. package/dist/lib/prompts/ip-select.d.ts.map +1 -0
  171. package/dist/lib/prompts/ip-select.js +255 -0
  172. package/dist/lib/prompts/ip-select.js.map +1 -0
  173. package/dist/lib/prompts/player-select.d.ts +25 -0
  174. package/dist/lib/prompts/player-select.d.ts.map +1 -0
  175. package/dist/lib/prompts/player-select.js +122 -0
  176. package/dist/lib/prompts/player-select.js.map +1 -0
  177. package/dist/lib/prompts/server-select.d.ts +26 -0
  178. package/dist/lib/prompts/server-select.d.ts.map +1 -0
  179. package/dist/lib/prompts/server-select.js +99 -0
  180. package/dist/lib/prompts/server-select.js.map +1 -0
  181. package/dist/lib/rcon.d.ts +53 -0
  182. package/dist/lib/rcon.d.ts.map +1 -0
  183. package/dist/lib/rcon.js +131 -0
  184. package/dist/lib/rcon.js.map +1 -0
  185. package/dist/lib/shell.d.ts +111 -0
  186. package/dist/lib/shell.d.ts.map +1 -0
  187. package/dist/lib/shell.js +321 -0
  188. package/dist/lib/shell.js.map +1 -0
  189. package/dist/lib/sudo-utils.d.ts +14 -0
  190. package/dist/lib/sudo-utils.d.ts.map +1 -0
  191. package/dist/lib/sudo-utils.js +53 -0
  192. package/dist/lib/sudo-utils.js.map +1 -0
  193. package/package.json +8 -2
package/dist/index.js ADDED
@@ -0,0 +1,766 @@
1
+ #!/usr/bin/env node
2
+ import { Paths, log, colors } from '@minecraft-docker/shared';
3
+ import { initCommand, statusCommand, createCommand, deleteCommand, worldCommand, backupCommand, execCommand, configCommand, opCommand, serverBackupCommand, serverRestoreCommand, whitelistCommand, banCommand, kickCommand, playerOnlineCommand, playerCommand, migrateCommand, modCommand, consoleInitCommand, consoleServiceCommand, consoleUserCommand, consoleApiCommand, consoleRemoveCommand, } from './commands/index.js';
4
+ import { ShellExecutor } from './lib/shell.js';
5
+ const VERSION = '0.1.0';
6
+ /**
7
+ * Handle console command (shared by both 'console' and deprecated 'admin' commands)
8
+ */
9
+ async function handleConsoleCommand(subCommand, positional, flags, rootDir) {
10
+ switch (subCommand) {
11
+ case 'init':
12
+ return consoleInitCommand({
13
+ root: rootDir,
14
+ force: flags['force'] === true,
15
+ apiPort: flags['api-port'] ? parseInt(flags['api-port'], 10) : undefined,
16
+ consolePort: flags['console-port'] ? parseInt(flags['console-port'], 10) : undefined,
17
+ });
18
+ case 'service':
19
+ return consoleServiceCommand({
20
+ root: rootDir,
21
+ subCommand: positional[0],
22
+ apiOnly: flags['api'] === true,
23
+ consoleOnly: flags['console'] === true,
24
+ follow: flags['follow'] === true,
25
+ json: flags['json'] === true,
26
+ apiPort: flags['api-port'] ? parseInt(flags['api-port'], 10) : undefined,
27
+ consolePort: flags['console-port'] ? parseInt(flags['console-port'], 10) : undefined,
28
+ build: flags['build'] === true,
29
+ noBuild: flags['no-build'] === true,
30
+ });
31
+ case 'user': {
32
+ // Use Commander-based command
33
+ const userCmd = consoleUserCommand();
34
+ userCmd.parse(['node', 'mcctl', ...positional], { from: 'user' });
35
+ return 0;
36
+ }
37
+ case 'api': {
38
+ // Use Commander-based command
39
+ const apiCmd = consoleApiCommand();
40
+ apiCmd.parse(['node', 'mcctl', ...positional], { from: 'user' });
41
+ return 0;
42
+ }
43
+ case 'remove':
44
+ return consoleRemoveCommand({
45
+ root: rootDir,
46
+ force: flags['force'] === true,
47
+ keepConfig: flags['keep-config'] === true,
48
+ });
49
+ default:
50
+ log.error('Usage: mcctl console <init|service|user|api|remove>');
51
+ return 1;
52
+ }
53
+ }
54
+ /**
55
+ * Print usage information
56
+ */
57
+ function printUsage() {
58
+ console.log(`
59
+ ${colors.bold('mcctl')} - Minecraft Server Management CLI
60
+
61
+ ${colors.cyan('Usage:')}
62
+ mcctl <command> [options]
63
+
64
+ ${colors.cyan('Commands:')}
65
+ ${colors.bold('init')} Initialize platform (~/.minecraft-servers)
66
+ ${colors.bold('up')} Start all infrastructure (router + servers)
67
+ ${colors.bold('down')} Stop all infrastructure
68
+ ${colors.bold('router')} <start|stop|restart> Manage mc-router only
69
+ ${colors.bold('create')} <name> [options] Create a new server
70
+ ${colors.bold('delete')} <name> [--force] Delete a server (preserves world data)
71
+ ${colors.bold('status')} [options] Show status of all servers
72
+ ${colors.bold('status')} <server> Show detailed status of a server
73
+ ${colors.bold('status')} router Show mc-router status with routes
74
+ ${colors.bold('start')} <server> [--all] Start a server (--all for all servers)
75
+ ${colors.bold('stop')} <server> [--all] Stop a server (--all for all servers)
76
+ ${colors.bold('logs')} <server> [lines] View server logs
77
+ ${colors.bold('console')} <server> Connect to RCON console
78
+ ${colors.bold('exec')} <server> <cmd...> Execute RCON command
79
+ ${colors.bold('config')} <server> [key] [val] View/set server config
80
+
81
+ ${colors.cyan('Config Shortcuts:')}
82
+ --cheats, --no-cheats Enable/disable cheats (ALLOW_CHEATS)
83
+ --pvp, --no-pvp Enable/disable PvP
84
+ --command-block Enable/disable command blocks
85
+
86
+ ${colors.cyan('Player Management:')}
87
+ ${colors.bold('op')} <server> <action> [player] Manage operators
88
+ ${colors.bold('whitelist')} <server> <action> [player] Manage whitelist
89
+ ${colors.bold('ban')} <server> <action> [player/ip] Manage bans
90
+ ${colors.bold('kick')} <server> <player> [reason] Kick player
91
+
92
+ ${colors.cyan('Operator Actions:')}
93
+ list List current operators
94
+ add <player> Add operator (RCON + config)
95
+ remove <player> Remove operator
96
+
97
+ ${colors.cyan('Whitelist Actions:')}
98
+ list Show whitelisted players
99
+ add <player> Add to whitelist
100
+ remove <player> Remove from whitelist
101
+ on / off Enable/disable whitelist
102
+ status Show whitelist status
103
+
104
+ ${colors.cyan('Ban Actions:')}
105
+ list Show banned players
106
+ add <player> [reason] Ban player
107
+ remove <player> Unban player
108
+ ip list Show banned IPs
109
+ ip add <ip> [reason] Ban IP
110
+ ip remove <ip> Unban IP
111
+
112
+ ${colors.cyan('World Management:')}
113
+ ${colors.bold('world list')} [--json] List worlds and lock status
114
+ ${colors.bold('world new')} [name] [options] Create new world with seed
115
+ ${colors.bold('world assign')} <world> <srv> Lock world to server
116
+ ${colors.bold('world release')} <world> Release world lock
117
+ ${colors.bold('world delete')} <world> [-f] Delete world permanently
118
+
119
+ ${colors.cyan('Mod Management:')}
120
+ ${colors.bold('mod search')} <query> Search mods on Modrinth
121
+ ${colors.bold('mod add')} <srv> <mod...> Add mods to server
122
+ ${colors.bold('mod list')} <server> List configured mods
123
+ ${colors.bold('mod remove')} <srv> <mod...> Remove mods from server
124
+ ${colors.bold('mod sources')} Show available mod sources
125
+
126
+ ${colors.cyan('Mod Add Options:')}
127
+ --modrinth Use Modrinth (default)
128
+ --curseforge Use CurseForge (requires CF_API_KEY)
129
+ --spiget Use Spiget (SpigotMC plugins)
130
+ --url Direct JAR URL download
131
+
132
+ ${colors.cyan('Mod Management:')}
133
+ ${colors.bold('mod search')} <query> Search mods on Modrinth
134
+ ${colors.bold('mod add')} <srv> <mod...> Add mods to server
135
+ ${colors.bold('mod list')} <server> List configured mods
136
+ ${colors.bold('mod remove')} <srv> <mod...> Remove mods from server
137
+ ${colors.bold('mod sources')} Show available mod sources
138
+
139
+ ${colors.cyan('Mod Add Options:')}
140
+ --modrinth Use Modrinth (default)
141
+ --curseforge Use CurseForge (requires CF_API_KEY)
142
+ --spiget Use Spiget (SpigotMC plugins)
143
+ --url Direct JAR URL download
144
+
145
+ ${colors.cyan('World New Options:')}
146
+ --seed <seed> World seed (optional, random if empty)
147
+ --server <name> Server to assign world to
148
+ --no-start Don't start server after creation
149
+
150
+ ${colors.cyan('Player Management (Interactive):')}
151
+ ${colors.bold('player')} Interactive mode: server → player → action
152
+ ${colors.bold('player')} <server> Interactive mode for specific server
153
+ ${colors.bold('player info')} <name> Player info lookup (UUID, skin)
154
+ ${colors.bold('player info')} <name> --offline Get offline player info
155
+ ${colors.bold('player online')} <server> List online players
156
+ ${colors.bold('player online')} --all List online players on all servers
157
+ ${colors.bold('player cache')} clear Clear player info cache
158
+ ${colors.bold('player cache')} stats Show cache statistics
159
+
160
+ ${colors.cyan('Player Lookup (Legacy):')}
161
+ ${colors.bold('player lookup')} <name> Look up player info
162
+ ${colors.bold('player uuid')} <name> Get player UUID
163
+
164
+ ${colors.cyan('Server Backup/Restore:')}
165
+ ${colors.bold('server-backup')} <server> [-m msg] Backup server configuration
166
+ ${colors.bold('server-backup')} <server> --list List all backups
167
+ ${colors.bold('server-restore')} <server> [id] Restore from backup
168
+
169
+ ${colors.cyan('World Backup (requires .env config):')}
170
+ ${colors.bold('backup push')} [--message] Backup worlds to GitHub
171
+ ${colors.bold('backup status')} Show backup configuration
172
+ ${colors.bold('backup history')} [--json] Show backup history
173
+ ${colors.bold('backup restore')} <commit> Restore from commit
174
+
175
+ ${colors.cyan('Migration:')}
176
+ ${colors.bold('migrate worlds')} Migrate worlds to shared directory
177
+ ${colors.bold('migrate status')} Check migration status
178
+ ${colors.bold('migrate worlds')} --all Migrate all servers
179
+ ${colors.bold('migrate worlds')} --dry-run Preview changes without applying
180
+
181
+ ${colors.cyan('Console Management:')}
182
+ ${colors.bold('console init')} [options] Initialize console service (create admin user)
183
+ --force Reinitialize (delete existing config)
184
+ --api-port <port> API server port (default: 3001)
185
+ --console-port <port> Console server port (default: 3000)
186
+ ${colors.bold('console user')} <action> [name] Manage console users (list, add, remove, reset)
187
+ ${colors.bold('console api')} <action> Manage API settings (status, config, key)
188
+ ${colors.bold('console service')} <action> Manage services via PM2 (start, stop, restart, status, logs)
189
+ ${colors.bold('console remove')} [options] Remove console service completely
190
+ --force Skip confirmation prompt
191
+ --keep-config Don't delete configuration files
192
+ ${colors.dim('admin <cmd> (deprecated, use "console" instead)')}
193
+
194
+ ${colors.cyan('Console Options:')}
195
+ --api-port <port> API server port (default: 3001)
196
+ --console-port <port> Console server port (default: 3000)
197
+ --api Target API service only
198
+ --console Target console service only
199
+ -f, --follow Follow log output
200
+
201
+ ${colors.cyan('Create Options:')}
202
+ -t, --type TYPE Server type: PAPER, VANILLA, FORGE, FABRIC
203
+ -v, --version VERSION Minecraft version (e.g., 1.21.1)
204
+ -s, --seed NUMBER World seed
205
+ -u, --world-url URL Download world from ZIP URL
206
+ -w, --world NAME Use existing world (symlink)
207
+ --no-start Create without starting
208
+
209
+ ${colors.cyan('Status Options:')}
210
+ --detail, -d Show detailed info (memory, CPU, players)
211
+ --watch, -W Real-time monitoring mode
212
+ --interval <sec> Watch refresh interval (default: 5)
213
+
214
+ ${colors.cyan('Global Options:')}
215
+ --root <path> Custom data directory
216
+ --sudo-password <pwd> Sudo password for automation (or use MCCTL_SUDO_PASSWORD env)
217
+ --json Output in JSON format
218
+ -h, --help Show this help
219
+ --version Show version
220
+
221
+ ${colors.cyan('Examples:')}
222
+ mcctl init
223
+ mcctl up # Start everything
224
+ mcctl down # Stop everything
225
+ mcctl router start # Start mc-router only
226
+ mcctl router restart # Restart mc-router
227
+ mcctl start --all # Start all MC servers
228
+ mcctl stop --all # Stop all MC servers
229
+ mcctl create myserver -t FORGE -v 1.20.4
230
+ mcctl status --json
231
+ mcctl status --detail # Show detailed info
232
+ mcctl status --watch # Real-time monitoring
233
+ mcctl status --watch --interval 2 # Watch with 2s refresh
234
+ mcctl status myserver # Single server status
235
+ mcctl status router # mc-router status
236
+ mcctl logs myserver -f
237
+ mcctl exec myserver say "Hello!" # Execute RCON command
238
+ mcctl exec myserver list # List online players
239
+ mcctl config myserver # View all config
240
+ mcctl config myserver --cheats # Enable cheats
241
+ mcctl config myserver MOTD "Welcome!" # Set MOTD
242
+ mcctl op myserver list # List operators
243
+ mcctl op myserver add Notch # Add operator
244
+ mcctl whitelist myserver add Steve # Add to whitelist
245
+ mcctl ban myserver add Griefer # Ban player
246
+ mcctl kick myserver AFK "Too long" # Kick player
247
+ mcctl player online myserver # Show online players
248
+ mcctl server-backup myserver # Backup server config
249
+ mcctl server-restore myserver # Restore from backup
250
+ `);
251
+ }
252
+ /**
253
+ * Parse command line arguments
254
+ */
255
+ function parseArgs(args) {
256
+ const result = {
257
+ command: '',
258
+ subCommand: undefined,
259
+ positional: [],
260
+ flags: {},
261
+ };
262
+ let i = 0;
263
+ // Skip node and script path
264
+ while (i < args.length) {
265
+ const arg = args[i];
266
+ if (arg.startsWith('--')) {
267
+ const key = arg.slice(2);
268
+ const nextArg = args[i + 1];
269
+ // Boolean-only flags (never take a value)
270
+ const booleanOnlyFlags = ['all', 'json', 'help', 'version', 'force', 'yes', 'follow', 'detail', 'watch', 'offline', 'no-start', 'list', 'dry-run', 'api', 'console', 'build', 'no-build', 'keep-config'];
271
+ if (booleanOnlyFlags.includes(key)) {
272
+ result.flags[key] = true;
273
+ i++;
274
+ }
275
+ else if (nextArg && !nextArg.startsWith('-')) {
276
+ result.flags[key] = nextArg;
277
+ i += 2;
278
+ }
279
+ else {
280
+ result.flags[key] = true;
281
+ i++;
282
+ }
283
+ }
284
+ else if (arg.startsWith('-') && arg.length === 2) {
285
+ const key = arg.slice(1);
286
+ const nextArg = args[i + 1];
287
+ // Map short flags to long names
288
+ const flagMap = {
289
+ h: 'help',
290
+ t: 'type',
291
+ v: 'version',
292
+ s: 'seed',
293
+ u: 'world-url',
294
+ w: 'world',
295
+ f: 'follow',
296
+ n: 'lines',
297
+ m: 'message',
298
+ y: 'yes',
299
+ a: 'all',
300
+ d: 'detail',
301
+ W: 'watch',
302
+ };
303
+ const longKey = flagMap[key] ?? key;
304
+ // Boolean-only short flags
305
+ const booleanOnlyShortFlags = ['h', 'f', 'y', 'a', 'd', 'W'];
306
+ if (booleanOnlyShortFlags.includes(key)) {
307
+ result.flags[longKey] = true;
308
+ i++;
309
+ }
310
+ else if (nextArg && !nextArg.startsWith('-')) {
311
+ result.flags[longKey] = nextArg;
312
+ i += 2;
313
+ }
314
+ else {
315
+ result.flags[longKey] = true;
316
+ i++;
317
+ }
318
+ }
319
+ else {
320
+ if (!result.command) {
321
+ result.command = arg;
322
+ }
323
+ else if (!result.subCommand && ['world', 'player', 'backup', 'op', 'whitelist', 'ban', 'router', 'migrate', 'mod', 'console', 'admin'].includes(result.command)) {
324
+ result.subCommand = arg;
325
+ }
326
+ else {
327
+ result.positional.push(arg);
328
+ }
329
+ i++;
330
+ }
331
+ }
332
+ return result;
333
+ }
334
+ /**
335
+ * Main entry point
336
+ */
337
+ async function main() {
338
+ const args = process.argv.slice(2);
339
+ if (args.length === 0) {
340
+ printUsage();
341
+ process.exit(0);
342
+ }
343
+ const parsed = parseArgs(args);
344
+ const { command, subCommand, positional, flags } = parsed;
345
+ // Handle global flags
346
+ if (flags['help'] || command === 'help') {
347
+ printUsage();
348
+ process.exit(0);
349
+ }
350
+ if (flags['version'] || command === '--version') {
351
+ console.log(`mcctl version ${VERSION}`);
352
+ process.exit(0);
353
+ }
354
+ const rootDir = flags['root'];
355
+ const sudoPassword = flags['sudo-password'];
356
+ const paths = new Paths(rootDir);
357
+ const shell = new ShellExecutor({ paths, sudoPassword });
358
+ let exitCode = 0;
359
+ try {
360
+ switch (command) {
361
+ case 'init':
362
+ exitCode = await initCommand({
363
+ root: rootDir,
364
+ skipValidation: flags['skip-validation'] === true,
365
+ skipDocker: flags['skip-docker'] === true,
366
+ });
367
+ break;
368
+ case 'status':
369
+ exitCode = await statusCommand({
370
+ json: flags['json'] === true,
371
+ root: rootDir,
372
+ detail: flags['detail'] === true,
373
+ watch: flags['watch'] === true,
374
+ interval: flags['interval'] ? parseInt(flags['interval'], 10) : undefined,
375
+ serverName: positional[0],
376
+ });
377
+ break;
378
+ case 'up': {
379
+ if (!paths.isInitialized()) {
380
+ log.error('Platform not initialized. Run: mcctl init');
381
+ exitCode = 1;
382
+ break;
383
+ }
384
+ exitCode = await shell.up();
385
+ break;
386
+ }
387
+ case 'down': {
388
+ if (!paths.isInitialized()) {
389
+ log.error('Platform not initialized. Run: mcctl init');
390
+ exitCode = 1;
391
+ break;
392
+ }
393
+ exitCode = await shell.down();
394
+ break;
395
+ }
396
+ case 'create': {
397
+ // Use new interactive create command
398
+ // If no name provided, will run in interactive mode
399
+ exitCode = await createCommand({
400
+ root: rootDir,
401
+ name: positional[0],
402
+ type: flags['type'],
403
+ version: flags['version'],
404
+ seed: flags['seed'],
405
+ worldUrl: flags['world-url'],
406
+ worldName: flags['world'],
407
+ noStart: flags['no-start'] === true,
408
+ sudoPassword,
409
+ });
410
+ break;
411
+ }
412
+ case 'delete': {
413
+ // Use new interactive delete command
414
+ // If no name provided, will run in interactive mode with server selection
415
+ exitCode = await deleteCommand({
416
+ root: rootDir,
417
+ name: positional[0],
418
+ force: flags['force'] === true || flags['yes'] === true,
419
+ sudoPassword,
420
+ });
421
+ break;
422
+ }
423
+ case 'start': {
424
+ if (!paths.isInitialized()) {
425
+ log.error('Platform not initialized. Run: mcctl init');
426
+ exitCode = 1;
427
+ break;
428
+ }
429
+ if (flags['all']) {
430
+ exitCode = await shell.startAll();
431
+ }
432
+ else {
433
+ const mcctlArgs = [command, ...positional];
434
+ if (flags['json'])
435
+ mcctlArgs.push('--json');
436
+ exitCode = await shell.mcctl(mcctlArgs);
437
+ }
438
+ break;
439
+ }
440
+ case 'stop': {
441
+ if (!paths.isInitialized()) {
442
+ log.error('Platform not initialized. Run: mcctl init');
443
+ exitCode = 1;
444
+ break;
445
+ }
446
+ if (flags['all']) {
447
+ exitCode = await shell.stopAll();
448
+ }
449
+ else {
450
+ const mcctlArgs = [command, ...positional];
451
+ if (flags['json'])
452
+ mcctlArgs.push('--json');
453
+ exitCode = await shell.mcctl(mcctlArgs);
454
+ }
455
+ break;
456
+ }
457
+ case 'console': {
458
+ // Check if this is console management command (init, service, user, api, remove)
459
+ const consoleManagementSubCommands = ['init', 'service', 'user', 'api', 'remove'];
460
+ if (subCommand && consoleManagementSubCommands.includes(subCommand)) {
461
+ // Route to console management
462
+ exitCode = await handleConsoleCommand(subCommand, positional, flags, rootDir);
463
+ break;
464
+ }
465
+ // Otherwise, treat as RCON console (mcctl console <server>)
466
+ if (!paths.isInitialized()) {
467
+ log.error('Platform not initialized. Run: mcctl init');
468
+ exitCode = 1;
469
+ break;
470
+ }
471
+ // Pass through to mcctl.sh for RCON console
472
+ const mcctlArgs = [command, ...positional];
473
+ if (flags['json'])
474
+ mcctlArgs.push('--json');
475
+ if (flags['follow'])
476
+ mcctlArgs.push('-f');
477
+ if (flags['lines'])
478
+ mcctlArgs.push('-n', flags['lines']);
479
+ exitCode = await shell.mcctl(mcctlArgs);
480
+ break;
481
+ }
482
+ case 'logs': {
483
+ if (!paths.isInitialized()) {
484
+ log.error('Platform not initialized. Run: mcctl init');
485
+ exitCode = 1;
486
+ break;
487
+ }
488
+ // Pass through to mcctl.sh
489
+ const mcctlArgs = [command, ...positional];
490
+ if (flags['json'])
491
+ mcctlArgs.push('--json');
492
+ if (flags['follow'])
493
+ mcctlArgs.push('-f');
494
+ if (flags['lines'])
495
+ mcctlArgs.push('-n', flags['lines']);
496
+ exitCode = await shell.mcctl(mcctlArgs);
497
+ break;
498
+ }
499
+ case 'exec': {
500
+ exitCode = await execCommand({
501
+ root: rootDir,
502
+ serverName: positional[0],
503
+ command: positional.slice(1),
504
+ });
505
+ break;
506
+ }
507
+ case 'config': {
508
+ exitCode = await configCommand({
509
+ root: rootDir,
510
+ serverName: positional[0],
511
+ key: positional[1],
512
+ value: positional[2],
513
+ json: flags['json'] === true,
514
+ cheats: flags['cheats'] === true,
515
+ noCheats: flags['no-cheats'] === true,
516
+ pvp: flags['pvp'] === true,
517
+ noPvp: flags['no-pvp'] === true,
518
+ commandBlock: flags['command-block'] === true,
519
+ noCommandBlock: flags['no-command-block'] === true,
520
+ });
521
+ break;
522
+ }
523
+ case 'world': {
524
+ // Use new interactive world command
525
+ exitCode = await worldCommand({
526
+ root: rootDir,
527
+ subCommand: subCommand,
528
+ worldName: positional[0],
529
+ serverName: subCommand === 'new'
530
+ ? flags['server']
531
+ : positional[1],
532
+ seed: flags['seed'],
533
+ autoStart: flags['no-start'] === true ? false : undefined,
534
+ json: flags['json'] === true,
535
+ force: flags['force'] === true,
536
+ });
537
+ break;
538
+ }
539
+ case 'player': {
540
+ // Handle player subcommands
541
+ if (subCommand === 'online') {
542
+ // player online <server> - List online players
543
+ exitCode = await playerOnlineCommand({
544
+ root: rootDir,
545
+ serverName: positional[0],
546
+ all: flags['all'] === true,
547
+ json: flags['json'] === true,
548
+ });
549
+ }
550
+ else if (subCommand === 'info') {
551
+ // player info <name> - Player info lookup
552
+ exitCode = await playerCommand({
553
+ root: rootDir,
554
+ subCommand: 'info',
555
+ playerName: positional[0],
556
+ offline: flags['offline'] === true,
557
+ json: flags['json'] === true,
558
+ });
559
+ }
560
+ else if (subCommand === 'cache') {
561
+ // player cache <clear|stats> - Cache management
562
+ exitCode = await playerCommand({
563
+ root: rootDir,
564
+ subCommand: 'cache',
565
+ cacheAction: positional[0],
566
+ json: flags['json'] === true,
567
+ });
568
+ }
569
+ else if (subCommand === 'lookup' || subCommand === 'uuid') {
570
+ // Legacy lookup commands via shell.player
571
+ const playerArgs = [subCommand, ...positional];
572
+ if (flags['json'])
573
+ playerArgs.push('--json');
574
+ if (flags['offline'])
575
+ playerArgs.push('--offline');
576
+ exitCode = await shell.player(playerArgs);
577
+ }
578
+ else if (!subCommand || subCommand) {
579
+ // Interactive mode: player [server]
580
+ // If subCommand is provided but not a known command, treat it as server name
581
+ exitCode = await playerCommand({
582
+ root: rootDir,
583
+ serverName: subCommand || positional[0],
584
+ json: flags['json'] === true,
585
+ });
586
+ }
587
+ break;
588
+ }
589
+ case 'backup': {
590
+ // Use new interactive backup command
591
+ exitCode = await backupCommand({
592
+ root: rootDir,
593
+ subCommand: subCommand,
594
+ message: flags['message'],
595
+ commitHash: positional[0],
596
+ json: flags['json'] === true,
597
+ auto: flags['auto'] === true,
598
+ });
599
+ break;
600
+ }
601
+ case 'op': {
602
+ // op command: op <server> <action> [player]
603
+ // parseArgs treats second arg as subCommand, so:
604
+ // subCommand = server name, positional[0] = action, positional[1] = player
605
+ exitCode = await opCommand({
606
+ root: rootDir,
607
+ serverName: subCommand,
608
+ subCommand: positional[0],
609
+ playerName: positional[1],
610
+ json: flags['json'] === true,
611
+ });
612
+ break;
613
+ }
614
+ case 'server-backup': {
615
+ exitCode = await serverBackupCommand({
616
+ root: rootDir,
617
+ serverName: positional[0],
618
+ message: flags['message'],
619
+ list: flags['list'] === true,
620
+ json: flags['json'] === true,
621
+ });
622
+ break;
623
+ }
624
+ case 'server-restore': {
625
+ exitCode = await serverRestoreCommand({
626
+ root: rootDir,
627
+ serverName: positional[0],
628
+ backupId: positional[1],
629
+ force: flags['force'] === true,
630
+ dryRun: flags['dry-run'] === true,
631
+ json: flags['json'] === true,
632
+ });
633
+ break;
634
+ }
635
+ case 'whitelist': {
636
+ // whitelist command: whitelist <server> <action> [player]
637
+ // subCommand = server name, positional[0] = action, positional[1] = player
638
+ exitCode = await whitelistCommand({
639
+ root: rootDir,
640
+ serverName: subCommand,
641
+ subCommand: positional[0],
642
+ playerName: positional[1],
643
+ json: flags['json'] === true,
644
+ });
645
+ break;
646
+ }
647
+ case 'ban': {
648
+ // ban command: ban <server> <action> [target] [reason...]
649
+ // subCommand = server name, positional[0] = action, positional[1] = target
650
+ const action = positional[0];
651
+ let ipAction;
652
+ let target;
653
+ let reason;
654
+ if (action === 'ip') {
655
+ // ban <server> ip <list|add|remove> [ip] [reason]
656
+ ipAction = positional[1];
657
+ target = positional[2];
658
+ reason = positional.slice(3).join(' ') || undefined;
659
+ }
660
+ else {
661
+ target = positional[1];
662
+ reason = positional.slice(2).join(' ') || undefined;
663
+ }
664
+ exitCode = await banCommand({
665
+ root: rootDir,
666
+ serverName: subCommand,
667
+ subCommand: action,
668
+ target,
669
+ reason,
670
+ ipAction,
671
+ json: flags['json'] === true,
672
+ });
673
+ break;
674
+ }
675
+ case 'kick': {
676
+ // kick command: kick <server> <player> [reason...]
677
+ const serverName = positional[0];
678
+ const playerName = positional[1];
679
+ const reason = positional.slice(2).join(' ') || undefined;
680
+ exitCode = await kickCommand({
681
+ root: rootDir,
682
+ serverName,
683
+ playerName,
684
+ reason,
685
+ json: flags['json'] === true,
686
+ });
687
+ break;
688
+ }
689
+ case 'router': {
690
+ if (!paths.isInitialized()) {
691
+ log.error('Platform not initialized. Run: mcctl init');
692
+ exitCode = 1;
693
+ break;
694
+ }
695
+ switch (subCommand) {
696
+ case 'start':
697
+ exitCode = await shell.routerStart();
698
+ break;
699
+ case 'stop':
700
+ exitCode = await shell.routerStop();
701
+ break;
702
+ case 'restart':
703
+ exitCode = await shell.routerRestart();
704
+ break;
705
+ default:
706
+ log.error('Usage: mcctl router <start|stop|restart>');
707
+ exitCode = 1;
708
+ }
709
+ break;
710
+ }
711
+ case 'migrate': {
712
+ // migrate command: migrate [worlds|status] [options]
713
+ exitCode = await migrateCommand({
714
+ root: rootDir,
715
+ subCommand: subCommand,
716
+ serverName: flags['server'],
717
+ all: flags['all'] === true,
718
+ dryRun: flags['dry-run'] === true,
719
+ backup: flags['backup'] === true,
720
+ force: flags['force'] === true,
721
+ json: flags['json'] === true,
722
+ });
723
+ break;
724
+ }
725
+ case 'mod': {
726
+ // mod command: mod [search|add|list|remove|sources] [args...]
727
+ // subCommand = action, positional[0] = server or query, positional[1+] = mod names
728
+ const modSource = flags['modrinth'] ? 'modrinth' :
729
+ flags['curseforge'] ? 'curseforge' :
730
+ flags['spiget'] ? 'spiget' :
731
+ flags['url'] ? 'url' : 'modrinth';
732
+ exitCode = await modCommand({
733
+ root: rootDir,
734
+ subCommand: subCommand,
735
+ serverName: subCommand === 'search' ? undefined : positional[0],
736
+ query: subCommand === 'search' ? positional[0] : undefined,
737
+ modNames: subCommand === 'search' ? undefined : positional.slice(1),
738
+ source: modSource,
739
+ json: flags['json'] === true,
740
+ force: flags['force'] === true,
741
+ });
742
+ break;
743
+ }
744
+ case 'admin': {
745
+ // Show deprecation warning for 'admin' command
746
+ log.warn('The "admin" command is deprecated. Please use "console" instead.');
747
+ console.log(` Example: ${colors.cyan('mcctl console init')} instead of ${colors.dim('mcctl admin init')}`);
748
+ console.log('');
749
+ // Route to console management handler
750
+ exitCode = await handleConsoleCommand(subCommand, positional, flags, rootDir);
751
+ break;
752
+ }
753
+ default:
754
+ log.error(`Unknown command: ${command}`);
755
+ printUsage();
756
+ exitCode = 1;
757
+ }
758
+ }
759
+ catch (err) {
760
+ log.error(`Unexpected error: ${err instanceof Error ? err.message : String(err)}`);
761
+ exitCode = 1;
762
+ }
763
+ process.exit(exitCode);
764
+ }
765
+ main();
766
+ //# sourceMappingURL=index.js.map