kustom-mc 0.1.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.
Files changed (50) hide show
  1. package/README.md +809 -0
  2. package/dist/commands/build.d.ts +2 -0
  3. package/dist/commands/build.js +447 -0
  4. package/dist/commands/bundle.d.ts +2 -0
  5. package/dist/commands/bundle.js +134 -0
  6. package/dist/commands/init.d.ts +2 -0
  7. package/dist/commands/init.js +219 -0
  8. package/dist/commands/list.d.ts +10 -0
  9. package/dist/commands/list.js +167 -0
  10. package/dist/commands/login.d.ts +9 -0
  11. package/dist/commands/login.js +167 -0
  12. package/dist/commands/new.d.ts +2 -0
  13. package/dist/commands/new.js +132 -0
  14. package/dist/commands/prepare.d.ts +9 -0
  15. package/dist/commands/prepare.js +267 -0
  16. package/dist/commands/push.d.ts +9 -0
  17. package/dist/commands/push.js +205 -0
  18. package/dist/commands/validate.d.ts +2 -0
  19. package/dist/commands/validate.js +191 -0
  20. package/dist/compiler/async-transform.d.ts +21 -0
  21. package/dist/compiler/async-transform.js +158 -0
  22. package/dist/compiler/inline.d.ts +32 -0
  23. package/dist/compiler/inline.js +87 -0
  24. package/dist/compiler/postprocess.d.ts +19 -0
  25. package/dist/compiler/postprocess.js +134 -0
  26. package/dist/compiler/rhino-plugin.d.ts +17 -0
  27. package/dist/compiler/rhino-plugin.js +324 -0
  28. package/dist/compiler/transform.d.ts +18 -0
  29. package/dist/compiler/transform.js +59 -0
  30. package/dist/config.d.ts +86 -0
  31. package/dist/config.js +166 -0
  32. package/dist/credentials.d.ts +65 -0
  33. package/dist/credentials.js +136 -0
  34. package/dist/index.d.ts +2 -0
  35. package/dist/index.js +28 -0
  36. package/dist/runtime.d.ts +116 -0
  37. package/dist/runtime.js +96 -0
  38. package/dist/types/globals.d.ts +80 -0
  39. package/dist/types/globals.js +10 -0
  40. package/dist/types/index.d.ts +2094 -0
  41. package/dist/types/index.js +9 -0
  42. package/package.json +57 -0
  43. package/templates/project/kustom.config.json +26 -0
  44. package/templates/project/scripts/example.ts +17 -0
  45. package/templates/project/scripts/lib/utils.ts +19 -0
  46. package/templates/project/tsconfig.json +27 -0
  47. package/templates/scripts/block.ts.hbs +14 -0
  48. package/templates/scripts/gui.ts.hbs +28 -0
  49. package/templates/scripts/item.ts.hbs +13 -0
  50. package/templates/scripts/script.ts.hbs +18 -0
@@ -0,0 +1,219 @@
1
+ import { Command } from 'commander';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import chalk from 'chalk';
5
+ export const initCommand = new Command('init')
6
+ .description('Initialize a new kustompack project')
7
+ .argument('[project-name]', 'Name of the project directory')
8
+ .option('-f, --force', 'Overwrite existing files')
9
+ .action(async (projectName, options) => {
10
+ const targetDir = projectName ? path.resolve(process.cwd(), projectName) : process.cwd();
11
+ const dirName = path.basename(targetDir);
12
+ // Convert directory name to valid pack ID (lowercase, alphanumeric, hyphens)
13
+ const packId = dirName.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-');
14
+ console.log(chalk.blue(`Initializing kustompack project: ${chalk.bold(packId)}`));
15
+ console.log(` Directory: ${targetDir}`);
16
+ // Create directory if it doesn't exist
17
+ if (projectName && !fs.existsSync(targetDir)) {
18
+ fs.mkdirSync(targetDir, { recursive: true });
19
+ }
20
+ // Check for existing files
21
+ const existingFiles = ['tsconfig.json', 'kustom.config.json', 'package.json'];
22
+ for (const file of existingFiles) {
23
+ const filePath = path.join(targetDir, file);
24
+ if (fs.existsSync(filePath) && !options.force) {
25
+ console.error(chalk.red(`Error: ${file} already exists. Use --force to overwrite.`));
26
+ process.exit(1);
27
+ }
28
+ }
29
+ // Create directory structure
30
+ const dirs = [
31
+ 'scripts/lib',
32
+ 'blocks',
33
+ 'items',
34
+ 'textures',
35
+ 'gui',
36
+ 'models',
37
+ 'sounds',
38
+ 'sounds/music',
39
+ 'lang',
40
+ 'definitions'
41
+ ];
42
+ for (const dir of dirs) {
43
+ const dirPath = path.join(targetDir, dir);
44
+ if (!fs.existsSync(dirPath)) {
45
+ fs.mkdirSync(dirPath, { recursive: true });
46
+ // Add .gitkeep to empty directories
47
+ if (!['scripts', 'scripts/lib'].includes(dir)) {
48
+ fs.writeFileSync(path.join(dirPath, '.gitkeep'), '');
49
+ }
50
+ }
51
+ }
52
+ // Write tsconfig.json
53
+ const tsconfig = {
54
+ compilerOptions: {
55
+ target: "ES2020",
56
+ module: "ESNext",
57
+ lib: ["ES2020"],
58
+ moduleResolution: "node",
59
+ strict: true,
60
+ noEmit: true,
61
+ isolatedModules: true,
62
+ esModuleInterop: true,
63
+ skipLibCheck: true,
64
+ baseUrl: ".",
65
+ paths: {
66
+ "@lib/*": ["scripts/lib/*"]
67
+ }
68
+ },
69
+ include: [
70
+ "scripts/**/*.ts",
71
+ "blocks/**/*.ts",
72
+ "items/**/*.ts",
73
+ "dist/types/**/*.d.ts",
74
+ ".kustom/types/**/*.d.ts",
75
+ "node_modules/kustom-mc/dist/types/globals.d.ts"
76
+ ],
77
+ exclude: []
78
+ };
79
+ fs.writeFileSync(path.join(targetDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2));
80
+ // Write kustom.config.json with manifest
81
+ const kustomConfig = {
82
+ include: [
83
+ "scripts/**/*.ts",
84
+ "blocks/**/*.ts",
85
+ "items/**/*.ts"
86
+ ],
87
+ exclude: [
88
+ "**/*.test.ts",
89
+ "**/*.spec.ts"
90
+ ],
91
+ outDir: ".",
92
+ lib: ["scripts/lib"],
93
+ manifest: {
94
+ id: packId,
95
+ name: dirName,
96
+ version: "1.0.0",
97
+ description: "A kustompack project",
98
+ author: "",
99
+ scope: "global",
100
+ priority: 0
101
+ },
102
+ dependencies: [],
103
+ server: {
104
+ url: "http://localhost:8765"
105
+ },
106
+ deploy: {
107
+ target: "../run/plugins/kustom-plugin/packs",
108
+ reloadCommand: "/kustom pack reload"
109
+ },
110
+ bundle: {
111
+ output: `dist/${packId}.zip`,
112
+ include: [
113
+ "**/*.js",
114
+ "textures/**/*",
115
+ "gui/**/*",
116
+ "models/**/*",
117
+ "sounds/**/*",
118
+ "lang/**/*",
119
+ "definitions/**/*"
120
+ ]
121
+ }
122
+ };
123
+ fs.writeFileSync(path.join(targetDir, 'kustom.config.json'), JSON.stringify(kustomConfig, null, 2));
124
+ // Write package.json
125
+ const packageJson = {
126
+ name: packId,
127
+ version: "1.0.0",
128
+ type: "module",
129
+ scripts: {
130
+ build: "kustom build",
131
+ watch: "kustom build --watch",
132
+ deploy: "kustom build --deploy",
133
+ bundle: "kustom bundle",
134
+ validate: "kustom validate",
135
+ push: "kustom push"
136
+ },
137
+ devDependencies: {
138
+ "kustom-mc": "^0.1.0",
139
+ "typescript": "^5.0.0"
140
+ }
141
+ };
142
+ fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(packageJson, null, 2));
143
+ // Write example script
144
+ const exampleScript = `import { defineScript, Props } from 'kustom-mc';
145
+
146
+ export default defineScript({
147
+ props: {
148
+ message: Props.String("Hello from ${packId}!"),
149
+ },
150
+
151
+ run({ executor, props }) {
152
+ const player = executor.asPlayer();
153
+ if (!player) {
154
+ console.error("This script requires a player executor");
155
+ return;
156
+ }
157
+
158
+ player.sendMessage(props.message);
159
+ }
160
+ });
161
+ `;
162
+ fs.writeFileSync(path.join(targetDir, 'scripts', 'example.ts'), exampleScript);
163
+ // Write lib/utils.ts
164
+ const utilsScript = `/**
165
+ * Shared utility functions for ${packId} scripts.
166
+ * Import these in your scripts using:
167
+ * import { formatMessage } from './lib/utils';
168
+ */
169
+
170
+ /**
171
+ * Format a message with a prefix.
172
+ */
173
+ export function formatMessage(prefix: string, message: string): string {
174
+ return \`[\${prefix}] \${message}\`;
175
+ }
176
+
177
+ /**
178
+ * Wait for a specified number of milliseconds.
179
+ */
180
+ export function delay(ms: number): Promise<void> {
181
+ return new Promise(resolve => setTimeout(resolve, ms));
182
+ }
183
+ `;
184
+ fs.writeFileSync(path.join(targetDir, 'scripts', 'lib', 'utils.ts'), utilsScript);
185
+ // Write .gitignore
186
+ const gitignore = `node_modules/
187
+ dist/
188
+ .kustom/
189
+ *.js
190
+ !scripts/lib/**/*.js
191
+ `;
192
+ fs.writeFileSync(path.join(targetDir, '.gitignore'), gitignore);
193
+ console.log(chalk.green('\nProject initialized successfully!'));
194
+ console.log('\n' + chalk.bold('Project structure:'));
195
+ console.log(` ${packId}/
196
+ ├── kustom.config.json # Pack configuration & manifest
197
+ ├── tsconfig.json # TypeScript configuration
198
+ ├── package.json # npm configuration
199
+ ├── scripts/ # General scripts
200
+ │ ├── example.ts # Example script
201
+ │ └── lib/ # Shared utilities
202
+ ├── blocks/ # Block definitions
203
+ ├── items/ # Item definitions
204
+ ├── textures/ # Texture files
205
+ ├── models/ # Model files
206
+ ├── sounds/ # Sound files
207
+ └── lang/ # Language files`);
208
+ console.log('\n' + chalk.bold('Next steps:'));
209
+ if (projectName) {
210
+ console.log(chalk.cyan(` cd ${projectName}`));
211
+ }
212
+ console.log(chalk.cyan(' npm install'));
213
+ console.log(chalk.cyan(' npm run build'));
214
+ console.log('\n' + chalk.bold('Available commands:'));
215
+ console.log(' npm run build - Compile TypeScript');
216
+ console.log(' npm run watch - Watch mode');
217
+ console.log(' npm run deploy - Build and deploy to server');
218
+ console.log(' npm run bundle - Create distributable zip');
219
+ });
@@ -0,0 +1,10 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * List command - fetch and display packs from server.
4
+ *
5
+ * Usage:
6
+ * npx kustom-mc list # List all packs
7
+ * npx kustom-mc list <pack-id> # Show details for specific pack
8
+ * npx kustom-mc list --verbose # Show detailed info for all packs
9
+ */
10
+ export declare const listCommand: Command;
@@ -0,0 +1,167 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { loadConfig } from '../config.js';
4
+ import { getServerToken, normalizeServerUrl } from '../credentials.js';
5
+ /**
6
+ * Fetch pack list from server.
7
+ */
8
+ async function fetchPackList(serverUrl) {
9
+ const url = `${normalizeServerUrl(serverUrl)}/packs`;
10
+ const response = await fetch(url, {
11
+ method: 'GET',
12
+ headers: {
13
+ 'Accept': 'application/json'
14
+ }
15
+ });
16
+ if (!response.ok) {
17
+ if (response.status === 404) {
18
+ throw new Error('Server does not have registry API enabled');
19
+ }
20
+ throw new Error(`Server returned ${response.status}: ${response.statusText}`);
21
+ }
22
+ const data = await response.json();
23
+ return data.packs || [];
24
+ }
25
+ /**
26
+ * Fetch detailed info for a specific pack.
27
+ */
28
+ async function fetchPackInfo(serverUrl, packId) {
29
+ const url = `${normalizeServerUrl(serverUrl)}/packs/${encodeURIComponent(packId)}`;
30
+ const response = await fetch(url, {
31
+ method: 'GET',
32
+ headers: {
33
+ 'Accept': 'application/json'
34
+ }
35
+ });
36
+ if (response.status === 404) {
37
+ return null;
38
+ }
39
+ if (!response.ok) {
40
+ throw new Error(`Server returned ${response.status}: ${response.statusText}`);
41
+ }
42
+ return await response.json();
43
+ }
44
+ /**
45
+ * Format a pack for display.
46
+ */
47
+ function formatPack(pack, verbose) {
48
+ const lines = [];
49
+ const nameDisplay = pack.name !== pack.id ? `${pack.name} (${pack.id})` : pack.id;
50
+ lines.push(`${chalk.cyan(nameDisplay)} ${chalk.gray(`v${pack.version}`)}`);
51
+ if (verbose) {
52
+ if (pack.description) {
53
+ lines.push(` ${pack.description}`);
54
+ }
55
+ if (pack.author) {
56
+ lines.push(` ${chalk.gray('Author:')} ${pack.author}`);
57
+ }
58
+ lines.push(` ${chalk.gray('Scope:')} ${pack.scope} ${chalk.gray('Priority:')} ${pack.priority}`);
59
+ const counts = [];
60
+ if (pack.scriptCount > 0)
61
+ counts.push(`${pack.scriptCount} scripts`);
62
+ if (pack.itemCount > 0)
63
+ counts.push(`${pack.itemCount} items`);
64
+ if (pack.shaperCount > 0)
65
+ counts.push(`${pack.shaperCount} blocks`);
66
+ if (pack.dependencyCount > 0)
67
+ counts.push(`${pack.dependencyCount} dependencies`);
68
+ if (counts.length > 0) {
69
+ lines.push(` ${chalk.gray('Contains:')} ${counts.join(', ')}`);
70
+ }
71
+ }
72
+ else {
73
+ const info = [`v${pack.version}`];
74
+ if (pack.scope !== 'global')
75
+ info.push(pack.scope);
76
+ if (pack.author)
77
+ info.push(`by ${pack.author}`);
78
+ // Already included version above, just add description if short
79
+ if (pack.description && pack.description.length < 50) {
80
+ lines[0] = `${chalk.cyan(nameDisplay)} ${chalk.gray(`v${pack.version}`)} - ${pack.description}`;
81
+ }
82
+ }
83
+ return lines.join('\n');
84
+ }
85
+ /**
86
+ * List command - fetch and display packs from server.
87
+ *
88
+ * Usage:
89
+ * npx kustom-mc list # List all packs
90
+ * npx kustom-mc list <pack-id> # Show details for specific pack
91
+ * npx kustom-mc list --verbose # Show detailed info for all packs
92
+ */
93
+ export const listCommand = new Command('list')
94
+ .description('List packs available on the connected server')
95
+ .argument('[pack-id]', 'Show details for a specific pack')
96
+ .option('-s, --server <url>', 'Server URL (overrides config)')
97
+ .option('-v, --verbose', 'Show detailed information')
98
+ .option('--json', 'Output as JSON')
99
+ .action(async (packId, options) => {
100
+ // Determine server URL
101
+ let serverUrl;
102
+ if (options?.server) {
103
+ serverUrl = options.server;
104
+ }
105
+ else {
106
+ const config = loadConfig(process.cwd());
107
+ if (!config.server?.url) {
108
+ console.error(chalk.red('Error: No server URL configured'));
109
+ console.log('Set server.url in kustom.config.json or use --server flag');
110
+ process.exit(1);
111
+ }
112
+ serverUrl = config.server.url;
113
+ }
114
+ serverUrl = normalizeServerUrl(serverUrl);
115
+ // Check if logged in (for display purposes)
116
+ const token = getServerToken(serverUrl);
117
+ const loggedIn = token !== null;
118
+ try {
119
+ if (packId) {
120
+ // Fetch specific pack
121
+ console.log(chalk.blue(`Fetching pack info from ${serverUrl}...`));
122
+ const pack = await fetchPackInfo(serverUrl, packId);
123
+ if (!pack) {
124
+ console.error(chalk.red(`Pack not found: ${packId}`));
125
+ process.exit(1);
126
+ }
127
+ if (options?.json) {
128
+ console.log(JSON.stringify(pack, null, 2));
129
+ }
130
+ else {
131
+ console.log();
132
+ console.log(formatPack(pack, true));
133
+ console.log();
134
+ }
135
+ }
136
+ else {
137
+ // Fetch all packs
138
+ console.log(chalk.blue(`Fetching pack list from ${serverUrl}...`));
139
+ const packs = await fetchPackList(serverUrl);
140
+ if (options?.json) {
141
+ console.log(JSON.stringify(packs, null, 2));
142
+ return;
143
+ }
144
+ if (packs.length === 0) {
145
+ console.log(chalk.yellow('\nNo packs found on server.'));
146
+ return;
147
+ }
148
+ console.log(chalk.green(`\nFound ${packs.length} pack(s):\n`));
149
+ for (const pack of packs) {
150
+ console.log(formatPack(pack, options?.verbose || false));
151
+ if (options?.verbose) {
152
+ console.log(); // Extra spacing in verbose mode
153
+ }
154
+ }
155
+ if (!options?.verbose) {
156
+ console.log(chalk.gray('\nUse --verbose for more details, or list <pack-id> for specific pack.'));
157
+ }
158
+ }
159
+ if (!loggedIn) {
160
+ console.log(chalk.gray('\nTip: Run `npx kustom-mc login` to authenticate for push access.'));
161
+ }
162
+ }
163
+ catch (error) {
164
+ console.error(chalk.red(`Failed to fetch packs: ${error instanceof Error ? error.message : error}`));
165
+ process.exit(1);
166
+ }
167
+ });
@@ -0,0 +1,9 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * Login command - authenticate with a server using a token.
4
+ *
5
+ * Usage:
6
+ * npx kustom-mc login <server-url> <token>
7
+ * npx kustom-mc login <token> (uses server.url from config)
8
+ */
9
+ export declare const loginCommand: Command;
@@ -0,0 +1,167 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { loadConfig } from '../config.js';
4
+ import { saveServerToken, getServerCredential, normalizeServerUrl, listStoredServers, removeServerCredential } from '../credentials.js';
5
+ /**
6
+ * Validate a token with the server.
7
+ */
8
+ async function validateToken(serverUrl, token) {
9
+ const url = `${normalizeServerUrl(serverUrl)}/token/validate`;
10
+ const response = await fetch(url, {
11
+ method: 'POST',
12
+ headers: {
13
+ 'Content-Type': 'application/json'
14
+ },
15
+ body: JSON.stringify({ token })
16
+ });
17
+ if (!response.ok) {
18
+ if (response.status === 404) {
19
+ throw new Error('Server does not support token validation. Is the registry API enabled?');
20
+ }
21
+ throw new Error(`Server returned ${response.status}: ${response.statusText}`);
22
+ }
23
+ return await response.json();
24
+ }
25
+ /**
26
+ * Login command - authenticate with a server using a token.
27
+ *
28
+ * Usage:
29
+ * npx kustom-mc login <server-url> <token>
30
+ * npx kustom-mc login <token> (uses server.url from config)
31
+ */
32
+ export const loginCommand = new Command('login')
33
+ .description('Authenticate with a Kustom server using a token')
34
+ .argument('[server-url]', 'Server URL (or use server.url from config)')
35
+ .argument('[token]', 'Authentication token from /kustom token command')
36
+ .option('--status', 'Show current login status')
37
+ .option('--logout', 'Remove stored credentials for a server')
38
+ .option('--list', 'List all stored server credentials')
39
+ .action(async (serverUrlArg, tokenArg, options) => {
40
+ // Handle --list flag
41
+ if (options?.list) {
42
+ const servers = listStoredServers();
43
+ if (servers.length === 0) {
44
+ console.log(chalk.yellow('No stored credentials.'));
45
+ return;
46
+ }
47
+ console.log(chalk.blue('Stored server credentials:'));
48
+ for (const server of servers) {
49
+ const cred = getServerCredential(server);
50
+ if (cred) {
51
+ console.log(` ${chalk.cyan(server)}`);
52
+ if (cred.playerName) {
53
+ console.log(` Player: ${cred.playerName}`);
54
+ }
55
+ console.log(` Saved: ${cred.savedAt}`);
56
+ }
57
+ }
58
+ return;
59
+ }
60
+ // Determine server URL
61
+ let serverUrl;
62
+ let token;
63
+ // If only one argument provided, it might be the token (use config for server)
64
+ if (serverUrlArg && !tokenArg && !serverUrlArg.includes('://') && !serverUrlArg.includes(':')) {
65
+ // Looks like a token, not a URL
66
+ token = serverUrlArg;
67
+ const config = loadConfig(process.cwd());
68
+ if (!config.server?.url) {
69
+ console.error(chalk.red('Error: No server URL provided and none configured in kustom.config.json'));
70
+ console.log('Usage: npx kustom-mc login <server-url> <token>');
71
+ console.log(' or: npx kustom-mc login <token> (with server.url in config)');
72
+ process.exit(1);
73
+ }
74
+ serverUrl = config.server.url;
75
+ }
76
+ else if (serverUrlArg) {
77
+ serverUrl = serverUrlArg;
78
+ token = tokenArg;
79
+ }
80
+ else {
81
+ // No arguments - use config
82
+ const config = loadConfig(process.cwd());
83
+ if (!config.server?.url) {
84
+ console.error(chalk.red('Error: No server URL provided'));
85
+ console.log('Usage: npx kustom-mc login <server-url> <token>');
86
+ process.exit(1);
87
+ }
88
+ serverUrl = config.server.url;
89
+ }
90
+ serverUrl = normalizeServerUrl(serverUrl);
91
+ // Handle --logout flag
92
+ if (options?.logout) {
93
+ const removed = removeServerCredential(serverUrl);
94
+ if (removed) {
95
+ console.log(chalk.green(`Logged out from ${serverUrl}`));
96
+ }
97
+ else {
98
+ console.log(chalk.yellow(`No credentials stored for ${serverUrl}`));
99
+ }
100
+ return;
101
+ }
102
+ // Handle --status flag
103
+ if (options?.status) {
104
+ const cred = getServerCredential(serverUrl);
105
+ if (!cred) {
106
+ console.log(chalk.yellow(`Not logged in to ${serverUrl}`));
107
+ return;
108
+ }
109
+ console.log(chalk.blue(`Login status for ${serverUrl}:`));
110
+ console.log(` Token: ${cred.token.substring(0, 8)}...`);
111
+ if (cred.playerName) {
112
+ console.log(` Player: ${cred.playerName}`);
113
+ }
114
+ console.log(` Saved: ${cred.savedAt}`);
115
+ // Validate token is still valid
116
+ console.log('\nValidating token with server...');
117
+ try {
118
+ const result = await validateToken(serverUrl, cred.token);
119
+ if (result.valid) {
120
+ console.log(chalk.green(' Token is valid'));
121
+ if (result.expiresAt) {
122
+ console.log(` Expires: ${result.expiresAt}`);
123
+ }
124
+ }
125
+ else {
126
+ console.log(chalk.red(' Token is invalid or expired'));
127
+ console.log(chalk.yellow(' Run: npx kustom-mc login --logout'));
128
+ }
129
+ }
130
+ catch (error) {
131
+ console.log(chalk.yellow(` Could not validate: ${error instanceof Error ? error.message : error}`));
132
+ }
133
+ return;
134
+ }
135
+ // Login with token
136
+ if (!token) {
137
+ console.error(chalk.red('Error: Token is required'));
138
+ console.log('Usage: npx kustom-mc login <server-url> <token>');
139
+ console.log('\nTo get a token, run /kustom token in-game on the server.');
140
+ process.exit(1);
141
+ }
142
+ console.log(chalk.blue(`Logging in to ${serverUrl}...`));
143
+ try {
144
+ const result = await validateToken(serverUrl, token);
145
+ if (!result.valid) {
146
+ console.error(chalk.red('Login failed: Invalid or expired token'));
147
+ if (result.error) {
148
+ console.error(chalk.red(` ${result.error}`));
149
+ }
150
+ process.exit(1);
151
+ }
152
+ // Save credentials
153
+ saveServerToken(serverUrl, token, result.playerName);
154
+ console.log(chalk.green('\nLogin successful!'));
155
+ if (result.playerName) {
156
+ console.log(` Player: ${result.playerName}`);
157
+ }
158
+ if (result.expiresAt) {
159
+ console.log(` Token expires: ${result.expiresAt}`);
160
+ }
161
+ console.log(`\nCredentials saved to ~/.kustom/credentials.json`);
162
+ }
163
+ catch (error) {
164
+ console.error(chalk.red(`Login failed: ${error instanceof Error ? error.message : error}`));
165
+ process.exit(1);
166
+ }
167
+ });
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const newCommand: Command;