UNPKG

@origami-minecraft/devbuilds

Version:

Origami is a terminal-first Minecraft launcher that supports authentication, installation, and launching of Minecraft versions — with built-in support for Microsoft accounts, mod loaders, profile management, and more. Designed for power users, modders, an

352 lines (292 loc) 11.9 kB
#!/usr/bin/env node import { Command } from 'commander'; import { Runtime } from '../core/game/launch/runtime'; import { parse_input, valid_string } from '../core/utils/common'; import compareVersions from 'compare-versions'; import chalk from 'chalk'; import inquirer from 'inquirer'; import { Credentials } from '../types/account'; import fetch from 'node-fetch'; import temurin from '../java'; import { ModInstaller } from '../core/game/install/packs/install'; import { logger } from '../core/game/launch/handler'; import { getAuthProviders } from '../core/game/account'; import { createProvider, deleteProvider } from '../core/game/account/auth_types/create'; const program = new Command(); const runtime = new Runtime(); program .name('origami') .description('✨ Lightweight Minecraft CLI Launcher') .version(runtime.version); program .command('menu') .description('Start the full interactive menu UI') .action(() => { runtime.start(); }); program .command('modrinth') .description('Install mods, shaders, and resource packs from Modrinth') .action(async () => { const profile = runtime.handler.profiles.getSelectedProfile(); if (!profile) { console.log(chalk.red('❌ No profile selected.')); process.exit(1); } const installer = new ModInstaller(logger); await installer.install_modrinth_content(profile); process.exit(0); }); program .command('java') .description('Download or select a Temurin JDK') .option('-i, --install', 'Download and install a Temurin JDK') .option('-s, --select', 'Choose and set the current JDK') .option('-d, --delete', 'Delete installed Java') .action(async (options) => { if (options.install && options.select) { console.error('❌ Please choose either --install or --select, not both.'); process.exit(1); } try { if (options.install) { await temurin.download(); } else if (options.select) { const java = await temurin.select(true); console.log('✅ Java set to:', java.version, java.path); } else if (options.delete) { await temurin.delete(); } else { program.commands.find(c => c.name() === 'java')!.help(); } } catch (err: any) { console.error('😿', err.message); process.exit(1); } }); program .command('manage') .description('Manage installed mods, shaders, and resource packs') .action(async () => { const profile = runtime.handler.profiles.getSelectedProfile(); if (!profile) { console.log(chalk.red('❌ No profile selected.')); process.exit(1); } await runtime['manageInstallationsMenu'](profile); process.exit(0); }); program .command('launch') .description('Launch Minecraft using current profile') .action(async () => { await runtime['launch'](); process.exit(0); }); function valid_profile(input: string | boolean | string[]) { const raw = parse_input(input); if (!valid_string(raw)) { throw new Error('❌ Invalid profile format: Must be a non-empty string.'); } const list = runtime.handler.profiles.listProfiles(); if (!list.includes(raw)) { throw new Error( `❌ Profile "${raw}" not found.\n\nTip: Run "origami profile --list" to see all profiles.` ); } return raw; }; program .command('profile') .description('Open Minecraft Profile Manager') .option('-s, --select <profile>', 'Select a specific profile', valid_profile) .option('-d, --delete', 'Opens the Profile Deletion Menu') .option('-i, --install', 'Install a version') .option('-l, --list', 'List all profiles') .action(async (options) => { const hasOptions = Object.keys(options).length > 0; if(!hasOptions) { await runtime['handler'].choose_profile(); } if(options.install) { await runtime.handler.install_version(); } if(options.delete) { await runtime.handler.delete_profile(); } if(options.list) { const profiles = runtime.handler.profiles.listProfiles(); if (!profiles.length) { console.log('⚠️ No profiles found.'); } else { console.log('📂 Available profiles:\n'); for (const name of profiles) { console.log(` - ${name}`); } } process.exit(0); } if (options.select) { const selected = options.select; runtime.handler.profiles.selectProfile(selected); console.log(`✅ Profile set to: ${selected}`); process.exit(0); } process.exit(1); }); program .command('authprovider') .description('User added Authentication Provider manager') .option('-c, --create', 'Add a Custom Yggdrasil Server') .option('-d, --delete', 'Delete a Custom Yggdrasil Server') .action(async (options) => { const hasOptions = Object.keys(options).length > 0; if (!hasOptions) { await runtime['authenticatorMenu'](); } else { if (options.create) { await createProvider(); } if (options.delete) { await deleteProvider(); } } process.exit(0); }); program .command('auth') .description('Open Minecraft Authenticator') .option('-l, --login <provider>', 'Login to a provider (e.g. microsoft, littleskin)') .option('-r, --remove <account>', 'Remove a specific account') .option('-c, --choose', 'Choose an account') .action(async (options) => { const providers = await getAuthProviders(); const hasOptions = Object.keys(options).length > 0; if (!hasOptions) { await runtime['authenticatorMenu'](); } else { const handler = runtime['handler']; if (options.login) { const provider = options.login.trim().toLowerCase(); if (!Object.keys(providers).includes(provider)) { console.log(chalk.red(`❌ Invalid auth provider: "${provider}"`)); console.log(`Available providers: ${Object.keys(providers).join(', ')}`); process.exit(1); } let credentials: Credentials = { email: '', password: '' }; if (provider !== 'microsoft') { credentials = await inquirer.prompt([ { type: 'input', name: 'email', message: 'Email or Username:', }, { type: 'password', name: 'password', message: 'Password:', mask: '*' }, ]); } const result = await handler.login(credentials, provider); if (result) { console.log(chalk.green(`✅ Logged in as ${result.name}`)); } else { console.log(chalk.redBright('❌ Login failed.')); } } if (options.remove) { const id = options.remove.trim(); const account = await handler.accounts.getAccount(id); if (!account) { console.log(chalk.red(`❌ Account with ID "${id}" not found.`)); } else { const confirm = await inquirer.prompt([ { type: 'confirm', name: 'confirm', message: chalk.red(`Are you sure you want to remove account "${account.name}" (${id})?`), default: false } ]); if (confirm.confirm) { const removed = await handler.accounts.deleteAccount(id); if (removed) { console.log(chalk.green(`🗑️ Removed account "${account.name}" successfully.`)); } else { console.log(chalk.red(`❌ Failed to remove account "${account.name}".`)); } } else { console.log(chalk.gray('❎ Account removal cancelled.')); } } } if (options.choose) { await handler.choose_account(); } } process.exit(0); }); program .command('clean') .description('Reset Origami and/or Minecraft data directories') .option('--minecraft', 'Reset the .minecraft directory') .option('--origami', 'Reset Origami config/data (e.g. profiles, accounts)') .option('--all', 'Reset everything (same as --minecraft + --origami)') .action(async (options) => { const doMinecraft = options.all || options.minecraft; const doOrigami = options.all || options.origami; if (!doMinecraft && !doOrigami) { console.log(chalk.red('❌ Please specify what to reset: --minecraft, --origami, or --all')); process.exit(1); } if (doMinecraft) { await runtime.resetMinecraft(); } if (doOrigami) { await runtime.resetOrigami(); } console.log(chalk.green('✅ Done!')); process.exit(0); }); export async function checkForLatestVersion(currentVersion: string) { const stableURL = 'https://registry.npmjs.org/@origami-minecraft/stable'; const devURL = 'https://registry.npmjs.org/@origami-minecraft/devbuilds'; try { const [stableRes, devRes] = await Promise.all([ fetch(stableURL), fetch(devURL) ]); if (!stableRes.ok || !devRes.ok) return; const stableData = await stableRes.json(); const devData = await devRes.json(); const latestStable = stableData['dist-tags']?.latest ?? ''; const latestDev = devData['dist-tags']?.latest ?? devData['dist-tags']?.dev ?? ''; const isCurrentDev = currentVersion.includes('-dev'); const cmpStable = compareVersions.compareVersions(latestStable, currentVersion); const cmpDev = compareVersions.compareVersions(latestDev, currentVersion); if (!isCurrentDev && cmpStable > 0) { console.log( chalk.yellow(`⚠️ A new stable version is available: ${latestStable}\nRun:`), chalk.cyan(`npm install -g @origami-minecraft/stable@${latestStable}`) ); } else if (isCurrentDev && cmpStable > 0) { console.log( chalk.yellow(`⚠️ You're on a dev build (${currentVersion}), but a new stable version is available: ${latestStable}\nRun:`), chalk.cyan(`npm install -g @origami-minecraft/stable@${latestStable}`) ); } else if (isCurrentDev && cmpDev > 0) { console.log( chalk.yellow(`⚠️ A new dev build is available: ${latestDev}\nRun:`), chalk.cyan(`npm install -g @origami-minecraft/devbuilds@${latestDev}`) ); } } catch { // Silent fail } } checkForLatestVersion(runtime.version).then(() => program.parse(process.argv)); if (!process.argv.slice(2).length) { program.outputHelp(); }