@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.
@@ -0,0 +1,114 @@
1
+ ---
2
+ layout: default
3
+ title: Contributing
4
+ nav_order: 10
5
+ description: "How to contribute to CLIcraft"
6
+ permalink: /contributing
7
+ ---
8
+
9
+ # Contributing to CLIcraft
10
+
11
+ Thank you for your interest in contributing to CLIcraft! We welcome contributions from the community and appreciate your help in making this tool better.
12
+
13
+ ## 🚀 Quick Links
14
+
15
+ - **Full Contributing Guide**: [CONTRIBUTING.md](https://github.com/theinfamousben/clicraft/blob/main/CONTRIBUTING.md)
16
+ - **Issue Tracker**: [GitHub Issues](https://github.com/theinfamousben/clicraft/issues)
17
+ - **Pull Requests**: [GitHub PRs](https://github.com/theinfamousben/clicraft/pulls)
18
+ - **Repository**: [theinfamousben/clicraft](https://github.com/theinfamousben/clicraft)
19
+
20
+ ## 📋 Ways to Contribute
21
+
22
+ There are many ways you can contribute to CLIcraft:
23
+
24
+ ### 🐛 Report Bugs
25
+
26
+ Found a bug? Please [open an issue](https://github.com/theinfamousben/clicraft/issues/new) with:
27
+ - A clear description of the problem
28
+ - Steps to reproduce the issue
29
+ - Your environment (OS, Node.js version, etc.)
30
+ - Expected vs actual behavior
31
+
32
+ ### ✨ Suggest Features
33
+
34
+ Have an idea for a new feature? We'd love to hear it! [Open an issue](https://github.com/theinfamousben/clicraft/issues/new) and describe:
35
+ - What problem the feature would solve
36
+ - How you envision it working
37
+ - Any examples or mockups (if applicable)
38
+
39
+ ### 📝 Improve Documentation
40
+
41
+ Documentation improvements are always welcome:
42
+ - Fix typos or clarify existing docs
43
+ - Add examples or use cases
44
+ - Write tutorials or guides
45
+ - Improve code comments
46
+
47
+ ### 💻 Submit Code
48
+
49
+ Ready to write some code? Here's how to get started:
50
+
51
+ 1. **Fork the repository** and clone it locally
52
+ 2. **Set up your development environment** (see below)
53
+ 3. **Create a branch** for your changes
54
+ 4. **Make your changes** following our coding guidelines
55
+ 5. **Test your changes** thoroughly
56
+ 6. **Submit a pull request** with a clear description
57
+
58
+ ## 🛠️ Development Setup
59
+
60
+ ### Prerequisites
61
+
62
+ - Node.js 18 or higher
63
+ - Java 21 or higher (for testing Minecraft functionality)
64
+ - Git
65
+
66
+ ### Quick Setup
67
+
68
+ ```bash
69
+ # Clone your fork
70
+ git clone https://github.com/YOUR-USERNAME/clicraft.git
71
+ cd clicraft
72
+
73
+ # Install dependencies
74
+ npm install
75
+
76
+ # Link globally for testing
77
+ npm link
78
+
79
+ # Verify installation
80
+ clicraft --version
81
+ ```
82
+
83
+ ## 📖 Detailed Guide
84
+
85
+ For a comprehensive guide on contributing, including:
86
+ - Code style guidelines
87
+ - Pull request process
88
+ - Testing procedures
89
+ - Best practices
90
+
91
+ Please read our full [Contributing Guide](https://github.com/theinfamousben/clicraft/blob/main/CONTRIBUTING.md).
92
+
93
+ ## 🤝 Code of Conduct
94
+
95
+ We are committed to providing a welcoming and inclusive environment. Please:
96
+ - Be respectful and constructive
97
+ - Welcome and help newcomers
98
+ - Provide helpful feedback
99
+ - Be patient and understanding
100
+
101
+ ## 💬 Getting Help
102
+
103
+ Need help getting started?
104
+ - Check the [Installation Guide](installation.md)
105
+ - Review the [Commands Documentation](commands.md)
106
+ - Ask questions in [GitHub Issues](https://github.com/theinfamousben/clicraft/issues)
107
+
108
+ ## 📜 License
109
+
110
+ By contributing to CLIcraft, you agree that your contributions will be licensed under the ISC License.
111
+
112
+ ---
113
+
114
+ **Ready to contribute?** Check out our [full Contributing Guide](https://github.com/theinfamousben/clicraft/blob/main/CONTRIBUTING.md) to get started!
package/docs/index.md ADDED
@@ -0,0 +1,82 @@
1
+ ---
2
+ layout: default
3
+ title: Home
4
+ nav_order: 1
5
+ description: "CLIcraft - A simple Minecraft Mod Package Manager"
6
+ permalink: /
7
+ ---
8
+
9
+ # CLIcraft Documentation
10
+
11
+ Welcome to **CLIcraft** - A simple, powerful Minecraft Mod Package Manager written in Node.js.
12
+
13
+ ## 🎮 What is CLIcraft?
14
+
15
+ CLIcraft is a command-line tool that simplifies managing Minecraft instances and mods. Create instances with Fabric or Forge, search and install mods from Modrinth, and launch the game directly from your terminal.
16
+
17
+ ## ✨ Key Features
18
+
19
+ - **🎮 Create Instances** - Set up new Minecraft client or server instances with Fabric or Forge
20
+ - **🔍 Search Mods** - Find mods on Modrinth with filters for version, loader, and more
21
+ - **📦 Install Mods** - Download and install mods directly to your instance
22
+ - **🔄 Upgrade** - Update mods, mod loader, or Minecraft version
23
+ - **ℹ️ Instance Info** - View detailed information about your instances
24
+ - **🔐 Microsoft Login** - Authenticate with your Microsoft account to play online
25
+ - **🚀 Launch Game** - Start Minecraft directly from the terminal
26
+
27
+ ## 🚀 Quick Start
28
+
29
+ ```bash
30
+ # Create a new instance
31
+ clicraft create
32
+
33
+ # Search for mods
34
+ clicraft search sodium
35
+
36
+ # Install a mod
37
+ cd my-instance
38
+ clicraft install sodium
39
+
40
+ # Launch the game
41
+ clicraft launch
42
+ ```
43
+
44
+ ## 📖 Documentation
45
+
46
+ ### Getting Started
47
+ - [Installation](installation.md) - Install CLIcraft on your system
48
+ - [Commands Overview](commands.md) - List of all available commands
49
+
50
+ ### Commands
51
+ - [`create`](commands/create.md) - Create a new Minecraft instance
52
+ - [`search`](commands/search.md) - Search for mods on Modrinth
53
+ - [`install`](commands/install.md) - Install mods to your instance
54
+ - [`login`](commands/login.md) - Authenticate with Microsoft account
55
+ - [`launch`](commands/launch.md) - Launch your Minecraft instance
56
+ - [`info`](commands/info.md) - View instance information
57
+ - [`upgrade`](commands/upgrade.md) - Upgrade mods and loaders
58
+
59
+ ### Configuration
60
+ - [Configuration Guide](configuration.md) - Instance and authentication configuration
61
+
62
+ ### Contributing
63
+ - [Contributing Guide](contributing.md) - How to contribute to CLIcraft
64
+
65
+ ## 🎯 Requirements
66
+
67
+ - **Node.js** 18 or higher
68
+ - **Java** 21 or higher (for running Minecraft)
69
+
70
+ ## 🌐 Supported Platforms
71
+
72
+ - ✅ Linux
73
+ - ✅ macOS
74
+ - ✅ Windows
75
+
76
+ ## 🤝 Contributing
77
+
78
+ Contributions are welcome! Please read our [Contributing Guide](https://github.com/theinfamousben/clicraft/blob/main/CONTRIBUTING.md) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes. You can also visit our [GitHub repository](https://github.com/theinfamousben/clicraft) to open issues or submit pull requests.
79
+
80
+ ## 📄 License
81
+
82
+ ISC - See the [LICENSE](https://github.com/theinfamousben/clicraft/blob/main/LICENSE) file for details.
@@ -0,0 +1,162 @@
1
+ ---
2
+ layout: default
3
+ title: Installation
4
+ nav_order: 2
5
+ description: "Install CLIcraft on your system"
6
+ permalink: /installation
7
+ ---
8
+
9
+ # Installation
10
+
11
+ This guide will help you install CLIcraft on your system.
12
+
13
+ ## 📋 Prerequisites
14
+
15
+ Before installing CLIcraft, ensure you have the following installed:
16
+
17
+ - **Node.js** 18 or higher - [Download Node.js](https://nodejs.org/)
18
+ - **Java** 21 or higher - Required for running Minecraft - [Download Java](https://adoptium.net/)
19
+
20
+ You can verify your installations by running:
21
+
22
+ ```bash
23
+ node --version
24
+ java --version
25
+ ```
26
+
27
+ ## 📦 Installation Methods
28
+
29
+ ### Method 1: NPM Package (Coming Soon!)
30
+
31
+ **Coming Soon!** The npm package will be available soon. Once released, you'll be able to install globally with:
32
+
33
+ ```bash
34
+ npm install -g clicraft
35
+ ```
36
+
37
+ ### Method 2: Clone from Source
38
+
39
+ This is currently the recommended way to install CLIcraft:
40
+
41
+ ```bash
42
+ # Clone the repository
43
+ git clone https://github.com/theinfamousben/clicraft.git
44
+ cd clicraft
45
+
46
+ # Install dependencies
47
+ npm install
48
+
49
+ # Link globally (allows you to use 'clicraft' command anywhere)
50
+ npm link
51
+ ```
52
+
53
+ After linking, you can use the `clicraft` command from any directory:
54
+
55
+ ```bash
56
+ clicraft --version
57
+ ```
58
+
59
+ ## 🔧 Verifying Installation
60
+
61
+ To verify that CLIcraft is installed correctly, run:
62
+
63
+ ```bash
64
+ clicraft --version
65
+ ```
66
+
67
+ This should display the current version of CLIcraft.
68
+
69
+ ## 📁 Configuration Files
70
+
71
+ CLIcraft stores configuration files in the following locations:
72
+
73
+ - **Authentication tokens**: `~/.mcpkg/auth.json`
74
+ - **Instance configuration**: `mcconfig.json` (in each instance directory)
75
+
76
+ These files are created automatically when you use CLIcraft for the first time.
77
+
78
+ ## ⬆️ Updating CLIcraft
79
+
80
+ ### If installed from source:
81
+
82
+ ```bash
83
+ cd clicraft
84
+ git pull origin main
85
+ npm install
86
+ ```
87
+
88
+ ### If installed via npm (when available):
89
+
90
+ ```bash
91
+ npm update -g clicraft
92
+ ```
93
+
94
+ ## 🗑️ Uninstalling
95
+
96
+ ### If installed from source:
97
+
98
+ ```bash
99
+ npm unlink clicraft
100
+ ```
101
+
102
+ Then delete the cloned repository folder.
103
+
104
+ ### If installed via npm (when available):
105
+
106
+ ```bash
107
+ npm uninstall -g clicraft
108
+ ```
109
+
110
+ To also remove configuration files:
111
+
112
+ ```bash
113
+ rm -rf ~/.mcpkg
114
+ ```
115
+
116
+ ## 🐛 Troubleshooting
117
+
118
+ ### "command not found: clicraft"
119
+
120
+ If you see this error after installation:
121
+
122
+ 1. Make sure you ran `npm link` in the clicraft directory
123
+ 2. Check that your npm global bin directory is in your PATH
124
+ 3. Try running `npm config get prefix` to see where global packages are installed
125
+
126
+ ### Permission errors on Linux/macOS
127
+
128
+ If you encounter permission errors during `npm link`, you may need to use:
129
+
130
+ ```bash
131
+ sudo npm link
132
+ ```
133
+
134
+ ### Node.js version issues
135
+
136
+ Make sure you're using Node.js 18 or higher. You can use [nvm](https://github.com/nvm-sh/nvm) to manage multiple Node.js versions:
137
+
138
+ ```bash
139
+ nvm install 18
140
+ nvm use 18
141
+ ```
142
+
143
+ ## 🆘 Getting Help
144
+
145
+ If you encounter any issues during installation:
146
+
147
+ 1. Check the [GitHub Issues](https://github.com/theinfamousben/clicraft/issues) page
148
+ 2. Open a new issue with details about your problem
149
+ 3. Include your operating system, Node.js version, and any error messages
150
+
151
+ ## ⏭️ Next Steps
152
+
153
+ Once CLIcraft is installed, you're ready to:
154
+
155
+ 1. [Create your first instance](commands/create.md)
156
+ 2. [Search for mods](commands/search.md)
157
+ 3. [Install mods](commands/install.md)
158
+ 4. [Launch the game](commands/launch.md)
159
+
160
+ ---
161
+
162
+ [← Back to Home](index.md) | [View All Commands →](commands.md)
@@ -0,0 +1,20 @@
1
+ // Re-export everything from auth modules
2
+ export {
3
+ loadAccounts,
4
+ saveAccounts,
5
+ getCurrentAccount,
6
+ getAccount,
7
+ addAccount,
8
+ removeAccount,
9
+ switchAccount,
10
+ getAllAccounts,
11
+ getAccountCount,
12
+ accountExists,
13
+ updateAccount,
14
+ migrateFromLegacy
15
+ } from './storage.js';
16
+
17
+ export {
18
+ performAuthentication,
19
+ refreshAccountAuth
20
+ } from './microsoft.js';
@@ -0,0 +1,329 @@
1
+ import readline from 'readline';
2
+
3
+ // Using the legacy live.com OAuth with the special desktop redirect URI
4
+ // This is the official approach for native/desktop applications
5
+ const CLIENT_ID = '00000000402b5328'; // Official Minecraft launcher client ID
6
+ const REDIRECT_URI = 'https://login.live.com/oauth20_desktop.srf';
7
+
8
+ /**
9
+ * Prompt user for input
10
+ */
11
+ export function prompt(question) {
12
+ const rl = readline.createInterface({
13
+ input: process.stdin,
14
+ output: process.stdout
15
+ });
16
+
17
+ return new Promise((resolve) => {
18
+ rl.question(question, (answer) => {
19
+ rl.close();
20
+ resolve(answer.trim());
21
+ });
22
+ });
23
+ }
24
+
25
+ /**
26
+ * Extract authorization code from redirect URL
27
+ */
28
+ export function extractCodeFromUrl(url) {
29
+ try {
30
+ if (url.startsWith('http')) {
31
+ const parsed = new URL(url);
32
+ return parsed.searchParams.get('code');
33
+ }
34
+ return url;
35
+ } catch {
36
+ return url;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Open URL in browser
42
+ */
43
+ export async function openBrowser(url) {
44
+ const { exec } = await import('child_process');
45
+ const { promisify } = await import('util');
46
+ const execAsync = promisify(exec);
47
+
48
+ const platform = process.platform;
49
+ let command;
50
+
51
+ if (platform === 'darwin') {
52
+ command = `open "${url}"`;
53
+ } else if (platform === 'win32') {
54
+ command = `start "" "${url}"`;
55
+ } else {
56
+ command = `xdg-open "${url}" || sensible-browser "${url}" || x-www-browser "${url}" || gnome-open "${url}"`;
57
+ }
58
+
59
+ try {
60
+ await execAsync(command);
61
+ return true;
62
+ } catch {
63
+ return false;
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Get OAuth URL for Microsoft login
69
+ */
70
+ export function getOAuthUrl() {
71
+ return `https://login.live.com/oauth20_authorize.srf?` +
72
+ `client_id=${CLIENT_ID}` +
73
+ `&response_type=code` +
74
+ `&redirect_uri=${encodeURIComponent(REDIRECT_URI)}` +
75
+ `&scope=${encodeURIComponent('service::user.auth.xboxlive.com::MBI_SSL')}`;
76
+ }
77
+
78
+ /**
79
+ * Exchange code for Microsoft token
80
+ */
81
+ export async function getMicrosoftToken(code) {
82
+ const response = await fetch('https://login.live.com/oauth20_token.srf', {
83
+ method: 'POST',
84
+ headers: {
85
+ 'Content-Type': 'application/x-www-form-urlencoded'
86
+ },
87
+ body: new URLSearchParams({
88
+ client_id: CLIENT_ID,
89
+ code: code,
90
+ grant_type: 'authorization_code',
91
+ redirect_uri: REDIRECT_URI,
92
+ scope: 'service::user.auth.xboxlive.com::MBI_SSL'
93
+ })
94
+ });
95
+
96
+ if (!response.ok) {
97
+ const text = await response.text();
98
+ throw new Error(`Failed to get Microsoft token: ${text}`);
99
+ }
100
+
101
+ return await response.json();
102
+ }
103
+
104
+ /**
105
+ * Refresh Microsoft token
106
+ */
107
+ export async function refreshMicrosoftToken(refreshToken) {
108
+ const response = await fetch('https://login.live.com/oauth20_token.srf', {
109
+ method: 'POST',
110
+ headers: {
111
+ 'Content-Type': 'application/x-www-form-urlencoded'
112
+ },
113
+ body: new URLSearchParams({
114
+ client_id: CLIENT_ID,
115
+ refresh_token: refreshToken,
116
+ grant_type: 'refresh_token',
117
+ scope: 'service::user.auth.xboxlive.com::MBI_SSL'
118
+ })
119
+ });
120
+
121
+ if (!response.ok) {
122
+ return null;
123
+ }
124
+
125
+ return await response.json();
126
+ }
127
+
128
+ /**
129
+ * Get Xbox Live token
130
+ */
131
+ export async function getXboxLiveToken(msAccessToken) {
132
+ const response = await fetch('https://user.auth.xboxlive.com/user/authenticate', {
133
+ method: 'POST',
134
+ headers: {
135
+ 'Content-Type': 'application/json',
136
+ 'Accept': 'application/json'
137
+ },
138
+ body: JSON.stringify({
139
+ Properties: {
140
+ AuthMethod: 'RPS',
141
+ SiteName: 'user.auth.xboxlive.com',
142
+ RpsTicket: msAccessToken
143
+ },
144
+ RelyingParty: 'http://auth.xboxlive.com',
145
+ TokenType: 'JWT'
146
+ })
147
+ });
148
+
149
+ if (!response.ok) {
150
+ const text = await response.text();
151
+ throw new Error(`Failed to get Xbox Live token: ${text}`);
152
+ }
153
+
154
+ return await response.json();
155
+ }
156
+
157
+ /**
158
+ * Get XSTS token
159
+ */
160
+ export async function getXSTSToken(xblToken) {
161
+ const response = await fetch('https://xsts.auth.xboxlive.com/xsts/authorize', {
162
+ method: 'POST',
163
+ headers: {
164
+ 'Content-Type': 'application/json',
165
+ 'Accept': 'application/json'
166
+ },
167
+ body: JSON.stringify({
168
+ Properties: {
169
+ SandboxId: 'RETAIL',
170
+ UserTokens: [xblToken]
171
+ },
172
+ RelyingParty: 'rp://api.minecraftservices.com/',
173
+ TokenType: 'JWT'
174
+ })
175
+ });
176
+
177
+ if (!response.ok) {
178
+ const error = await response.json().catch(() => ({}));
179
+ if (error.XErr === 2148916233) {
180
+ throw new Error('This Microsoft account does not have an Xbox account. Please create one at xbox.com');
181
+ } else if (error.XErr === 2148916238) {
182
+ throw new Error('This account belongs to someone under 18 and needs to be added to a family');
183
+ }
184
+ throw new Error(`Failed to get XSTS token: ${JSON.stringify(error)}`);
185
+ }
186
+
187
+ return await response.json();
188
+ }
189
+
190
+ /**
191
+ * Get Minecraft token
192
+ */
193
+ export async function getMinecraftToken(xstsToken, userHash) {
194
+ const response = await fetch('https://api.minecraftservices.com/authentication/login_with_xbox', {
195
+ method: 'POST',
196
+ headers: {
197
+ 'Content-Type': 'application/json'
198
+ },
199
+ body: JSON.stringify({
200
+ identityToken: `XBL3.0 x=${userHash};${xstsToken}`
201
+ })
202
+ });
203
+
204
+ if (!response.ok) {
205
+ const text = await response.text();
206
+ throw new Error(`Failed to get Minecraft token: ${text}`);
207
+ }
208
+
209
+ return await response.json();
210
+ }
211
+
212
+ /**
213
+ * Get Minecraft profile
214
+ */
215
+ export async function getMinecraftProfile(mcAccessToken) {
216
+ const response = await fetch('https://api.minecraftservices.com/minecraft/profile', {
217
+ headers: {
218
+ 'Authorization': `Bearer ${mcAccessToken}`
219
+ }
220
+ });
221
+
222
+ if (!response.ok) {
223
+ if (response.status === 404) {
224
+ throw new Error('You do not own Minecraft Java Edition. Please purchase it at minecraft.net');
225
+ }
226
+ throw new Error('Failed to get Minecraft profile');
227
+ }
228
+
229
+ return await response.json();
230
+ }
231
+
232
+ /**
233
+ * Full authentication flow - returns account data
234
+ */
235
+ export async function performAuthentication(onStatus) {
236
+ const log = onStatus || (() => {});
237
+
238
+ const authUrl = getOAuthUrl();
239
+
240
+ log('open_url', authUrl);
241
+
242
+ const opened = await openBrowser(authUrl);
243
+ log('browser_opened', opened);
244
+
245
+ const redirectUrl = await prompt('\nPaste the redirect URL here: ');
246
+
247
+ if (!redirectUrl) {
248
+ throw new Error('No URL provided');
249
+ }
250
+
251
+ const code = extractCodeFromUrl(redirectUrl);
252
+ if (!code) {
253
+ throw new Error('Could not extract authorization code from URL');
254
+ }
255
+
256
+ log('status', 'Getting Microsoft token...');
257
+ const msToken = await getMicrosoftToken(code);
258
+
259
+ log('status', 'Getting Xbox Live token...');
260
+ const xblResponse = await getXboxLiveToken(msToken.access_token);
261
+ const xblToken = xblResponse.Token;
262
+ const userHash = xblResponse.DisplayClaims.xui[0].uhs;
263
+
264
+ log('status', 'Getting XSTS token...');
265
+ const xstsResponse = await getXSTSToken(xblToken);
266
+ const xstsToken = xstsResponse.Token;
267
+
268
+ log('status', 'Getting Minecraft token...');
269
+ const mcToken = await getMinecraftToken(xstsToken, userHash);
270
+
271
+ log('status', 'Getting Minecraft profile...');
272
+ const profile = await getMinecraftProfile(mcToken.access_token);
273
+
274
+ return {
275
+ accessToken: mcToken.access_token,
276
+ refreshToken: msToken.refresh_token,
277
+ expiresAt: Date.now() + (mcToken.expires_in * 1000),
278
+ uuid: profile.id,
279
+ username: profile.name,
280
+ authenticatedAt: new Date().toISOString()
281
+ };
282
+ }
283
+
284
+ /**
285
+ * Refresh authentication for an account
286
+ */
287
+ export async function refreshAccountAuth(accountData) {
288
+ if (!accountData.refreshToken) {
289
+ return null;
290
+ }
291
+
292
+ const msToken = await refreshMicrosoftToken(accountData.refreshToken);
293
+ if (!msToken) {
294
+ return null;
295
+ }
296
+
297
+ const xblResponse = await getXboxLiveToken(msToken.access_token);
298
+ const xstsResponse = await getXSTSToken(xblResponse.Token);
299
+ const mcToken = await getMinecraftToken(
300
+ xstsResponse.Token,
301
+ xblResponse.DisplayClaims.xui[0].uhs
302
+ );
303
+ const profile = await getMinecraftProfile(mcToken.access_token);
304
+
305
+ return {
306
+ accessToken: mcToken.access_token,
307
+ refreshToken: msToken.refresh_token,
308
+ expiresAt: Date.now() + (mcToken.expires_in * 1000),
309
+ uuid: profile.id,
310
+ username: profile.name,
311
+ authenticatedAt: accountData.authenticatedAt,
312
+ refreshedAt: new Date().toISOString()
313
+ };
314
+ }
315
+
316
+ export default {
317
+ prompt,
318
+ extractCodeFromUrl,
319
+ openBrowser,
320
+ getOAuthUrl,
321
+ getMicrosoftToken,
322
+ refreshMicrosoftToken,
323
+ getXboxLiveToken,
324
+ getXSTSToken,
325
+ getMinecraftToken,
326
+ getMinecraftProfile,
327
+ performAuthentication,
328
+ refreshAccountAuth
329
+ };