UNPKG

automagik-genie

Version:

Self-evolving AI agent orchestration framework with Model Context Protocol support

278 lines (277 loc) 12.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.runUpdate = runUpdate; const child_process_1 = require("child_process"); const service_config_js_1 = require("../lib/service-config.js"); const util_1 = require("util"); const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); const gradient_string_1 = __importDefault(require("gradient-string")); const view_helpers_1 = require("../lib/view-helpers"); const common_1 = require("../views/common"); const package_1 = require("../lib/package"); const upgrade_1 = require("../lib/upgrade"); const forge_manager_1 = require("../lib/forge-manager"); const execAsync = (0, util_1.promisify)(child_process_1.exec); // Genie-themed gradients const successGradient = (0, gradient_string_1.default)(['#00ff88', '#00ccff', '#0099ff']); const performanceGradient = (0, gradient_string_1.default)(['#ffd700', '#ff8c00', '#ff6347']); /** * Detect which npm channel the user is on (@next or @latest) * Returns 'next' for RC versions, 'latest' for stable versions */ function detectChannel(version) { // RC versions are on @next channel if (version.includes('-rc.')) { return 'next'; } // Stable versions are on @latest channel return 'latest'; } /** * Update command - updates the global npm package * * NO workspace changes, NO backups - just updates the global package. * Workspace upgrade happens automatically when you run `genie` or `genie init` after update. */ async function runUpdate(parsed, _config, _paths) { try { const flags = parseFlags(parsed.commandArgs); const currentVersion = (0, package_1.getPackageVersion)(); // MASTER GENIE DETECTION: Check if we're in the template repo const workspacePackageJson = path_1.default.join(process.cwd(), 'package.json'); let isMasterGenie = false; if (fs_1.default.existsSync(workspacePackageJson)) { try { const workspacePkg = JSON.parse(fs_1.default.readFileSync(workspacePackageJson, 'utf8')); if (workspacePkg.name === 'automagik-genie') { isMasterGenie = true; } } catch { // Not master genie if can't read package.json } } // If master genie, check npm for latest published version (not local package.json) if (isMasterGenie) { // Detect package manager (prefer pnpm, fallback to npm) let packageManager = 'npm'; try { await execAsync('pnpm --version'); packageManager = 'pnpm'; } catch { // pnpm not available, use npm } // Check npm registry for latest published version console.log('📦 Checking npm for updates...'); console.log(''); // Get currently installed global version FIRST (to detect channel) let globalVersion; try { const { stdout } = await execAsync(`${packageManager} list -g automagik-genie --depth=0 --json`); const globalData = JSON.parse(stdout); // pnpm returns an array, npm returns an object if (Array.isArray(globalData)) { globalVersion = globalData[0]?.dependencies?.['automagik-genie']?.version || ''; } else { globalVersion = globalData.dependencies?.['automagik-genie']?.version || ''; } // Handle file:/link: protocols (when installed from local directory) if (globalVersion.startsWith('file:') || globalVersion.startsWith('link:')) { try { const filePath = globalVersion.replace(/^(file:|link:)/, ''); const globalPackageJson = path_1.default.join(filePath, 'package.json'); if (fs_1.default.existsSync(globalPackageJson)) { const globalPkg = JSON.parse(fs_1.default.readFileSync(globalPackageJson, 'utf8')); globalVersion = globalPkg.version || ''; } } catch { globalVersion = ''; } } } catch { globalVersion = ''; } // Detect which channel user is on (next or latest) const channel = detectChannel(globalVersion || currentVersion); // Check for updates using correct channel const updateCheck = await (0, upgrade_1.checkForUpdates)(currentVersion, 'unknown', channel); // If global already matches latest npm version, nothing to do if (globalVersion === updateCheck.latestVersion) { console.log(successGradient('━'.repeat(60))); console.log(successGradient(' 🧞 ✨ MASTER GENIE - ALREADY UP TO DATE ✨ 🧞 ')); console.log(successGradient('━'.repeat(60))); console.log(''); console.log(`Your global Genie matches npm @${channel}: ` + successGradient(updateCheck.latestVersion)); console.log(''); console.log('✨ Nothing to update!'); console.log(''); return; } // Update available from npm - install latest from registry console.log(successGradient('━'.repeat(60))); console.log(successGradient(' 🧞 ✨ MASTER GENIE UPDATE ✨ 🧞 ')); console.log(successGradient('━'.repeat(60))); console.log(''); console.log(`Updating global Genie: ${performanceGradient(globalVersion || '(not installed)')}${successGradient(updateCheck.latestVersion)}`); console.log(`Channel: @${channel}`); console.log(''); console.log(`Installing from npm (using ${packageManager})...`); console.log(''); try { await execAsync(`${packageManager} install -g automagik-genie@${channel}`, { cwd: process.cwd() }); console.log(''); console.log(successGradient('✅ Successfully updated global Genie from npm!')); console.log(''); console.log(`Your global Genie is now: ${successGradient(updateCheck.latestVersion)} (@${channel})`); console.log(''); return; } catch (error) { throw new Error(`Failed to install from npm: ${error.message}\n\n` + `Please try manually: ${packageManager} install -g automagik-genie@${channel}`); } } // NOT master genie - proceed with npm update flow // Detect which channel user is on (next or latest) const channel = detectChannel(currentVersion); // Check for updates console.log('📦 Checking for updates...'); console.log(''); const updateCheck = await (0, upgrade_1.checkForUpdates)(currentVersion, 'unknown', channel); if (!updateCheck.available) { await (0, view_helpers_1.emitView)((0, common_1.buildInfoView)('Already up to date', [ `Current version: ${currentVersion}`, `Latest version: ${updateCheck.latestVersion}`, '✅ No updates available' ]), parsed.options); return; } // Show update info await (0, view_helpers_1.emitView)((0, common_1.buildInfoView)('Update available', [ `Current: ${currentVersion}`, `Available: ${updateCheck.latestVersion}`, '', 'Changes:', ...updateCheck.changes.map(c => ` ${c}`) ]), parsed.options); console.log(''); // Prompt for confirmation (unless --force) if (!flags.force) { const { default: prompts } = await import('prompts'); const response = await prompts({ type: 'confirm', name: 'proceed', message: 'Update global Genie package?', initial: true }); if (!response.proceed) { console.log('Update cancelled.'); return; } } console.log(''); console.log('📦 Updating global package...'); console.log(''); // Detect package manager (prefer pnpm, fallback to npm) let packageManager = 'npm'; try { await execAsync('pnpm --version'); packageManager = 'pnpm'; } catch { // pnpm not available, use npm } // Update global package using detected channel (already detected above) const updateCommand = `${packageManager} install -g automagik-genie@${channel}`; console.log(` Running: ${updateCommand}`); console.log(` Channel: @${channel}`); console.log(''); try { const { stdout, stderr } = await execAsync(updateCommand); if (stdout) console.log(stdout); if (stderr && !stderr.includes('npm WARN')) console.error(stderr); } catch (error) { throw new Error(`Failed to update package: ${error.message}\n\n` + `Please try manually: ${updateCommand}`); } console.log(''); console.log(`✅ I've evolved to ${updateCheck.latestVersion}!`); console.log(''); // Check if Forge is already running const { baseUrl } = (0, service_config_js_1.getForgeConfig)(); const forgeRunning = await (0, forge_manager_1.isForgeRunning)(baseUrl); if (forgeRunning) { // Forge is running - your clone needs to learn the new powers await (0, view_helpers_1.emitView)((0, common_1.buildInfoView)('Master Genie Updated', [ '✨ I (the Master) have learned new powers!', '', '💡 Your clone is still running the old me...', ' Run `genie` again to teach your clone these new powers', ' (I\'ll sync all my latest knowledge to your workspace)', '', '🔮 Your clone will inherit everything I just learned!' ]), parsed.options); } else { // Forge not running - offer to summon the clone now console.log('💡 Next time you summon me, I\'ll teach your clone everything new!'); console.log(''); // Prompt to start genie now (unless --force which skips all prompts) if (!flags.force) { const { default: prompts } = await import('prompts'); const response = await prompts({ type: 'confirm', name: 'startNow', message: 'Summon your Genie clone now to learn these powers?', initial: true }); if (response.startNow) { console.log(''); console.log('🔮 Preparing to summon your clone...'); console.log(''); await (0, view_helpers_1.emitView)((0, common_1.buildInfoView)('Ready to Summon', [ 'Run `genie` to summon your clone', '(I\'ll automatically teach it everything I just learned!)' ]), parsed.options); } else { await (0, view_helpers_1.emitView)((0, common_1.buildInfoView)('Master Genie Updated', [ '✨ I\'m ready when you are!', ' Run `genie` anytime to teach your clone these new powers' ]), parsed.options); } } else { await (0, view_helpers_1.emitView)((0, common_1.buildInfoView)('Master Genie Updated', [ '✨ Run `genie` to teach your clone these new powers!' ]), parsed.options); } } } catch (error) { const message = error instanceof Error ? error.message : String(error); await (0, view_helpers_1.emitView)((0, common_1.buildErrorView)('Update failed', message), parsed.options, { stream: process.stderr }); process.exitCode = 1; } } function parseFlags(args) { const flags = {}; for (let i = 0; i < args.length; i++) { const token = args[i]; if (token === '--force' || token === '-f') { flags.force = true; } } return flags; }