@bobschlowinskii/clicraft 0.4.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.
- package/CHANGELOG.md +191 -0
- package/CONTRIBUTING.md +261 -0
- package/LICENSE.md +7 -0
- package/README.md +55 -0
- package/commands/auth.js +427 -0
- package/commands/config.js +356 -0
- package/commands/create.js +534 -0
- package/commands/info.js +113 -0
- package/commands/install.js +130 -0
- package/commands/launch.js +296 -0
- package/commands/search.js +50 -0
- package/commands/uninstall.js +94 -0
- package/commands/upgrade.js +343 -0
- package/commands/version.js +21 -0
- package/docs/README.md +119 -0
- package/docs/_config.yml +63 -0
- package/docs/commands/auth.md +376 -0
- package/docs/commands/config.md +294 -0
- package/docs/commands/create.md +276 -0
- package/docs/commands/info.md +460 -0
- package/docs/commands/install.md +320 -0
- package/docs/commands/launch.md +427 -0
- package/docs/commands/search.md +276 -0
- package/docs/commands/uninstall.md +128 -0
- package/docs/commands/upgrade.md +515 -0
- package/docs/commands.md +266 -0
- package/docs/configuration.md +410 -0
- package/docs/contributing.md +114 -0
- package/docs/index.md +82 -0
- package/docs/installation.md +162 -0
- package/helpers/auth/index.js +20 -0
- package/helpers/auth/microsoft.js +329 -0
- package/helpers/auth/storage.js +260 -0
- package/helpers/config.js +258 -0
- package/helpers/constants.js +38 -0
- package/helpers/getv.js +5 -0
- package/helpers/minecraft.js +308 -0
- package/helpers/modrinth.js +141 -0
- package/helpers/utils.js +334 -0
- package/helpers/versions.js +21 -0
- package/index.js +89 -0
- package/package.json +30 -0
package/commands/auth.js
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import {
|
|
4
|
+
loadAccounts,
|
|
5
|
+
getCurrentAccount,
|
|
6
|
+
getAccount,
|
|
7
|
+
addAccount,
|
|
8
|
+
removeAccount,
|
|
9
|
+
switchAccount as switchAccountStorage,
|
|
10
|
+
getAllAccounts,
|
|
11
|
+
getAccountCount,
|
|
12
|
+
updateAccount,
|
|
13
|
+
migrateFromLegacy
|
|
14
|
+
} from '../helpers/auth/index.js';
|
|
15
|
+
import {
|
|
16
|
+
performAuthentication,
|
|
17
|
+
refreshAccountAuth
|
|
18
|
+
} from '../helpers/auth/microsoft.js';
|
|
19
|
+
|
|
20
|
+
// Migrate from legacy auth.json on first run
|
|
21
|
+
migrateFromLegacy();
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Load current auth (for backward compatibility)
|
|
25
|
+
*/
|
|
26
|
+
export function loadAuth() {
|
|
27
|
+
return getCurrentAccount();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Refresh authentication for current account
|
|
32
|
+
*/
|
|
33
|
+
export async function refreshAuth() {
|
|
34
|
+
const account = getCurrentAccount();
|
|
35
|
+
|
|
36
|
+
if (!account) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Check if token is still valid (with 5 min buffer)
|
|
41
|
+
if (account.expiresAt && Date.now() < account.expiresAt - 300000) {
|
|
42
|
+
return account;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Try to refresh
|
|
46
|
+
try {
|
|
47
|
+
console.log(chalk.gray('Refreshing authentication...'));
|
|
48
|
+
const refreshed = await refreshAccountAuth(account);
|
|
49
|
+
|
|
50
|
+
if (refreshed) {
|
|
51
|
+
updateAccount(account.uuid, refreshed);
|
|
52
|
+
console.log(chalk.green('ā Token refreshed successfully'));
|
|
53
|
+
return refreshed;
|
|
54
|
+
}
|
|
55
|
+
} catch (err) {
|
|
56
|
+
console.log(chalk.yellow('Token refresh failed, please login again.'));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Login command - adds a new account or updates existing
|
|
64
|
+
*/
|
|
65
|
+
async function login(options) {
|
|
66
|
+
try {
|
|
67
|
+
console.log(chalk.cyan('\nš Microsoft Login\n'));
|
|
68
|
+
|
|
69
|
+
const existingCount = getAccountCount();
|
|
70
|
+
if (existingCount > 0) {
|
|
71
|
+
console.log(chalk.gray(`You have ${existingCount} account(s) saved.`));
|
|
72
|
+
console.log(chalk.gray('This will add a new account or update an existing one.\n'));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const accountData = await performAuthentication((type, data) => {
|
|
76
|
+
switch (type) {
|
|
77
|
+
case 'open_url':
|
|
78
|
+
console.log(chalk.white('Please open this URL in your browser to login:\n'));
|
|
79
|
+
console.log(chalk.cyan(data));
|
|
80
|
+
console.log();
|
|
81
|
+
break;
|
|
82
|
+
case 'browser_opened':
|
|
83
|
+
if (data) console.log(chalk.gray('(Browser opened automatically)'));
|
|
84
|
+
console.log(chalk.white('\nAfter logging in, you will be redirected to a blank page.'));
|
|
85
|
+
console.log(chalk.white('Copy the ENTIRE URL from your browser\'s address bar and paste it below:'));
|
|
86
|
+
break;
|
|
87
|
+
case 'status':
|
|
88
|
+
console.log(chalk.gray(data));
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Check if account already exists
|
|
94
|
+
const existing = getAccount(accountData.uuid);
|
|
95
|
+
const action = existing ? 'updated' : 'added';
|
|
96
|
+
|
|
97
|
+
addAccount(accountData, true);
|
|
98
|
+
|
|
99
|
+
console.log(chalk.green(`\nā
Successfully ${action} account: ${chalk.bold(accountData.username)}`));
|
|
100
|
+
console.log(chalk.gray(` UUID: ${accountData.uuid}`));
|
|
101
|
+
|
|
102
|
+
const count = getAccountCount();
|
|
103
|
+
if (count > 1) {
|
|
104
|
+
console.log(chalk.gray(` Total accounts: ${count}`));
|
|
105
|
+
console.log(chalk.gray(` This account is now active.`));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error(chalk.red('\nLogin failed:'), error.message);
|
|
110
|
+
if (options?.verbose) {
|
|
111
|
+
console.error(error);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Logout command - removes an account
|
|
118
|
+
*/
|
|
119
|
+
async function logout(identifier, options) {
|
|
120
|
+
console.log(chalk.cyan('\nš Logout\n'));
|
|
121
|
+
|
|
122
|
+
const accounts = getAllAccounts();
|
|
123
|
+
|
|
124
|
+
if (accounts.length === 0) {
|
|
125
|
+
console.log(chalk.yellow('No accounts are logged in.'));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let accountToRemove;
|
|
130
|
+
|
|
131
|
+
if (identifier) {
|
|
132
|
+
// Remove specific account
|
|
133
|
+
accountToRemove = getAccount(identifier);
|
|
134
|
+
if (!accountToRemove) {
|
|
135
|
+
console.log(chalk.red(`Account "${identifier}" not found.`));
|
|
136
|
+
console.log(chalk.gray('Available accounts:'));
|
|
137
|
+
for (const acc of accounts) {
|
|
138
|
+
console.log(chalk.gray(` - ${acc.username} (${acc.uuid})`));
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
} else if (accounts.length === 1) {
|
|
143
|
+
// Only one account, remove it
|
|
144
|
+
accountToRemove = accounts[0];
|
|
145
|
+
} else {
|
|
146
|
+
// Multiple accounts, ask which one
|
|
147
|
+
const { selected } = await inquirer.prompt([{
|
|
148
|
+
type: 'list',
|
|
149
|
+
name: 'selected',
|
|
150
|
+
message: 'Which account do you want to logout?',
|
|
151
|
+
choices: [
|
|
152
|
+
...accounts.map(acc => ({
|
|
153
|
+
name: `${acc.username} (${acc.uuid.substring(0, 8)}...)`,
|
|
154
|
+
value: acc.uuid
|
|
155
|
+
})),
|
|
156
|
+
{ name: chalk.red('Cancel'), value: null }
|
|
157
|
+
]
|
|
158
|
+
}]);
|
|
159
|
+
|
|
160
|
+
if (!selected) {
|
|
161
|
+
console.log(chalk.gray('Cancelled.'));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
accountToRemove = getAccount(selected);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!options?.force) {
|
|
169
|
+
const { confirm } = await inquirer.prompt([{
|
|
170
|
+
type: 'confirm',
|
|
171
|
+
name: 'confirm',
|
|
172
|
+
message: `Are you sure you want to logout ${accountToRemove.username}?`,
|
|
173
|
+
default: false
|
|
174
|
+
}]);
|
|
175
|
+
|
|
176
|
+
if (!confirm) {
|
|
177
|
+
console.log(chalk.gray('Cancelled.'));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const removed = removeAccount(accountToRemove.uuid);
|
|
183
|
+
if (removed) {
|
|
184
|
+
console.log(chalk.green(`ā
Logged out: ${removed.username}`));
|
|
185
|
+
|
|
186
|
+
const remaining = getAccountCount();
|
|
187
|
+
if (remaining > 0) {
|
|
188
|
+
const current = getCurrentAccount();
|
|
189
|
+
console.log(chalk.gray(` Remaining accounts: ${remaining}`));
|
|
190
|
+
if (current) {
|
|
191
|
+
console.log(chalk.gray(` Active account: ${current.username}`));
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
console.log(chalk.gray(' No accounts remaining.'));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Switch account command
|
|
201
|
+
*/
|
|
202
|
+
async function switchAccountCmd(identifier, options) {
|
|
203
|
+
console.log(chalk.cyan('\nš Switch Account\n'));
|
|
204
|
+
|
|
205
|
+
const accounts = getAllAccounts();
|
|
206
|
+
|
|
207
|
+
if (accounts.length === 0) {
|
|
208
|
+
console.log(chalk.yellow('No accounts are logged in.'));
|
|
209
|
+
console.log(chalk.gray('Use "clicraft auth login" to add an account.'));
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (accounts.length === 1) {
|
|
214
|
+
console.log(chalk.yellow('Only one account is logged in.'));
|
|
215
|
+
console.log(chalk.gray('Use "clicraft auth login" to add another account.'));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const currentAccount = getCurrentAccount();
|
|
220
|
+
let targetAccount;
|
|
221
|
+
|
|
222
|
+
if (identifier) {
|
|
223
|
+
targetAccount = getAccount(identifier);
|
|
224
|
+
if (!targetAccount) {
|
|
225
|
+
console.log(chalk.red(`Account "${identifier}" not found.`));
|
|
226
|
+
console.log(chalk.gray('Available accounts:'));
|
|
227
|
+
for (const acc of accounts) {
|
|
228
|
+
const current = acc.uuid === currentAccount?.uuid ? chalk.green(' (current)') : '';
|
|
229
|
+
console.log(chalk.gray(` - ${acc.username}${current}`));
|
|
230
|
+
}
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
// Interactive selection
|
|
235
|
+
const { selected } = await inquirer.prompt([{
|
|
236
|
+
type: 'list',
|
|
237
|
+
name: 'selected',
|
|
238
|
+
message: 'Select an account to switch to:',
|
|
239
|
+
choices: accounts.map(acc => ({
|
|
240
|
+
name: acc.uuid === currentAccount?.uuid
|
|
241
|
+
? `${acc.username} ${chalk.green('(current)')}`
|
|
242
|
+
: acc.username,
|
|
243
|
+
value: acc.uuid,
|
|
244
|
+
disabled: acc.uuid === currentAccount?.uuid ? 'current' : false
|
|
245
|
+
}))
|
|
246
|
+
}]);
|
|
247
|
+
|
|
248
|
+
targetAccount = getAccount(selected);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (targetAccount.uuid === currentAccount?.uuid) {
|
|
252
|
+
console.log(chalk.yellow(`Already using account: ${targetAccount.username}`));
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const switched = switchAccountStorage(targetAccount.uuid);
|
|
257
|
+
if (switched) {
|
|
258
|
+
console.log(chalk.green(`ā
Switched to: ${chalk.bold(switched.username)}`));
|
|
259
|
+
|
|
260
|
+
// Check token status
|
|
261
|
+
const isExpired = switched.expiresAt && Date.now() >= switched.expiresAt;
|
|
262
|
+
if (isExpired) {
|
|
263
|
+
console.log(chalk.yellow(' Token expired (will refresh on next launch)'));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Status command - show all accounts or specific account info
|
|
270
|
+
*/
|
|
271
|
+
async function status(identifier, options) {
|
|
272
|
+
console.log(chalk.cyan('\nš® Account Status\n'));
|
|
273
|
+
|
|
274
|
+
const accounts = getAllAccounts();
|
|
275
|
+
const currentAccount = getCurrentAccount();
|
|
276
|
+
|
|
277
|
+
if (accounts.length === 0) {
|
|
278
|
+
console.log(chalk.yellow('No accounts are logged in.'));
|
|
279
|
+
console.log(chalk.gray('Use "clicraft auth login" to add an account.'));
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (identifier) {
|
|
284
|
+
// Show specific account
|
|
285
|
+
const account = getAccount(identifier);
|
|
286
|
+
if (!account) {
|
|
287
|
+
console.log(chalk.red(`Account "${identifier}" not found.`));
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
displayAccountDetails(account, account.uuid === currentAccount?.uuid);
|
|
292
|
+
} else {
|
|
293
|
+
// Show all accounts
|
|
294
|
+
console.log(chalk.white(`${accounts.length} account(s) saved:\n`));
|
|
295
|
+
|
|
296
|
+
for (const account of accounts) {
|
|
297
|
+
const isCurrent = account.uuid === currentAccount?.uuid;
|
|
298
|
+
displayAccountSummary(account, isCurrent);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
console.log();
|
|
302
|
+
console.log(chalk.gray('Use "clicraft auth status <username>" for detailed info.'));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Display account summary (one line)
|
|
308
|
+
*/
|
|
309
|
+
function displayAccountSummary(account, isCurrent) {
|
|
310
|
+
const marker = isCurrent ? chalk.green('ā¶ ') : ' ';
|
|
311
|
+
const username = isCurrent
|
|
312
|
+
? chalk.bold.green(account.username)
|
|
313
|
+
: chalk.white(account.username);
|
|
314
|
+
|
|
315
|
+
const isExpired = account.expiresAt && Date.now() >= account.expiresAt;
|
|
316
|
+
const tokenStatus = isExpired
|
|
317
|
+
? chalk.yellow('(expired)')
|
|
318
|
+
: chalk.green('(valid)');
|
|
319
|
+
|
|
320
|
+
console.log(`${marker}${username} ${chalk.gray(`(${account.uuid.substring(0, 8)}...)`)} ${tokenStatus}`);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Display detailed account info
|
|
325
|
+
*/
|
|
326
|
+
function displayAccountDetails(account, isCurrent) {
|
|
327
|
+
const status = isCurrent ? chalk.green(' (active)') : '';
|
|
328
|
+
|
|
329
|
+
console.log(chalk.white(`Username: ${chalk.bold(account.username)}${status}`));
|
|
330
|
+
console.log(chalk.gray(`UUID: ${account.uuid}`));
|
|
331
|
+
console.log(chalk.gray(`Authenticated: ${account.authenticatedAt}`));
|
|
332
|
+
|
|
333
|
+
if (account.refreshedAt) {
|
|
334
|
+
console.log(chalk.gray(`Last Refreshed: ${account.refreshedAt}`));
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const isExpired = account.expiresAt && Date.now() >= account.expiresAt;
|
|
338
|
+
const canRefresh = !!account.refreshToken;
|
|
339
|
+
|
|
340
|
+
if (isExpired) {
|
|
341
|
+
if (canRefresh) {
|
|
342
|
+
console.log(chalk.yellow('Token expired (will refresh on next launch)'));
|
|
343
|
+
} else {
|
|
344
|
+
console.log(chalk.red('Token expired (please login again)'));
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
const expiresIn = Math.round((account.expiresAt - Date.now()) / 60000);
|
|
348
|
+
console.log(chalk.green(`Token valid for ${expiresIn} minutes`));
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* List accounts (alias for status without args)
|
|
354
|
+
*/
|
|
355
|
+
async function listAccounts(options) {
|
|
356
|
+
await status(null, options);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Main auth command handler
|
|
361
|
+
*/
|
|
362
|
+
export async function authCommand(action, args, options) {
|
|
363
|
+
// Handle the case where action might be in args for 'auth status <name>'
|
|
364
|
+
const actionArg = Array.isArray(args) ? args[0] : args;
|
|
365
|
+
|
|
366
|
+
switch (action) {
|
|
367
|
+
case 'login':
|
|
368
|
+
case 'add':
|
|
369
|
+
await login(options);
|
|
370
|
+
break;
|
|
371
|
+
|
|
372
|
+
case 'logout':
|
|
373
|
+
case 'remove':
|
|
374
|
+
await logout(actionArg, options);
|
|
375
|
+
break;
|
|
376
|
+
|
|
377
|
+
case 'switch':
|
|
378
|
+
case 'switch-account':
|
|
379
|
+
case 'use':
|
|
380
|
+
await switchAccountCmd(actionArg, options);
|
|
381
|
+
break;
|
|
382
|
+
|
|
383
|
+
case 'status':
|
|
384
|
+
case 'info':
|
|
385
|
+
await status(actionArg, options);
|
|
386
|
+
break;
|
|
387
|
+
|
|
388
|
+
case 'list':
|
|
389
|
+
case 'accounts':
|
|
390
|
+
await listAccounts(options);
|
|
391
|
+
break;
|
|
392
|
+
|
|
393
|
+
case undefined:
|
|
394
|
+
// No action, show help
|
|
395
|
+
console.log(chalk.cyan('\nš Auth Commands\n'));
|
|
396
|
+
console.log(chalk.gray('Available actions:\n'));
|
|
397
|
+
console.log(` ${chalk.white('login')} Add a new account or update existing`);
|
|
398
|
+
console.log(` ${chalk.white('logout [account]')} Remove an account`);
|
|
399
|
+
console.log(` ${chalk.white('switch [account]')} Switch to a different account`);
|
|
400
|
+
console.log(` ${chalk.white('status [account]')} Show account status`);
|
|
401
|
+
console.log(` ${chalk.white('list')} List all accounts`);
|
|
402
|
+
console.log();
|
|
403
|
+
console.log(chalk.gray('Examples:'));
|
|
404
|
+
console.log(chalk.gray(' clicraft auth login'));
|
|
405
|
+
console.log(chalk.gray(' clicraft auth switch PlayerName'));
|
|
406
|
+
console.log(chalk.gray(' clicraft auth logout'));
|
|
407
|
+
console.log(chalk.gray(' clicraft auth status'));
|
|
408
|
+
break;
|
|
409
|
+
|
|
410
|
+
default:
|
|
411
|
+
// Treat as account identifier for status
|
|
412
|
+
await status(action, options);
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Legacy exports for backward compatibility
|
|
418
|
+
export { login, logout };
|
|
419
|
+
export { status as authStatus };
|
|
420
|
+
|
|
421
|
+
export default {
|
|
422
|
+
authCommand,
|
|
423
|
+
login,
|
|
424
|
+
logout,
|
|
425
|
+
loadAuth,
|
|
426
|
+
refreshAuth
|
|
427
|
+
};
|