UNPKG

@kya-os/cli

Version:

CLI for MCP-I setup and management

167 lines • 8.76 kB
import chalk from "chalk"; import ora from "ora"; import inquirer from "inquirer"; import { showSuccess, showError, showInfo } from "../utils/prompts.js"; import { hasDelegation, isKTAAvailable } from "../utils/kta-api.js"; import { loadCurrentIdentity, archiveOldKey, generateNewIdentity, saveIdentityToDev, generateAuditLine, } from "../utils/identity-manager.js"; import contractsCli from "@kya-os/contracts/cli"; const { CLI_EXIT_CODES } = contractsCli; /** * Rotate identity keys with dev/prod mode handling * Requirements: 2.3, 2.4, 2.5, 2.6 */ export async function rotate(options = {}) { console.log(chalk.cyan("\nšŸ”„ Rotating Identity Keys\n")); const { force = false, reason, newDid = false, yes = false } = options; try { // Determine environment const isDev = !process.env.AGENT_PRIVATE_KEY; const mode = isDev ? "dev" : "prod"; console.log(`${chalk.bold("Environment:")} ${chalk.gray(mode)}`); if (reason) { console.log(`${chalk.bold("Reason:")} ${chalk.gray(reason)}`); } // Load current identity const currentIdentity = loadCurrentIdentity(); if (!currentIdentity) { showError("No current identity found. Run 'mcpi init' first."); process.exit(CLI_EXIT_CODES.NOIDENTITY); } console.log(`\n${chalk.bold("Current Identity:")}`); console.log(` DID: ${chalk.gray(currentIdentity.did)}`); console.log(` Key ID: ${chalk.gray(currentIdentity.kid)}`); // Production mode validation if (!isDev) { console.log(`\n${chalk.bold("šŸ”’ Production Mode Detected")}`); if (!force) { // Check delegation via KTA lookup by DID/KID (Requirement 2.4) const spinner = ora("Checking delegation status via KTA...").start(); try { const ktaAvailable = await isKTAAvailable(); if (!ktaAvailable) { spinner.fail("KTA is not reachable"); showError("Cannot verify delegation status - KTA is unavailable"); console.log(`\n${chalk.gray("To proceed:")}`); console.log(`${chalk.gray("• Check your internet connection")}`); console.log(`${chalk.gray("• Verify KTA service status")}`); console.log(`${chalk.gray("• Or use --force with --yes for emergency rotation")}`); process.exit(CLI_EXIT_CODES.CONFIG); } const delegationExists = await hasDelegation(currentIdentity.did, currentIdentity.kid); if (delegationExists) { spinner.succeed("Delegation verified via KTA"); } else { spinner.fail("No delegation found for current key"); showError("Key rotation requires delegation to new key ID or --force flag"); console.log(`\n${chalk.gray("To proceed:")}`); console.log(`${chalk.gray("• Set up delegation via KTA for the new key")}`); console.log(`${chalk.gray("• Or use --force with --yes for emergency rotation")}`); process.exit(CLI_EXIT_CODES.CONFIG); } } catch (error) { spinner.fail("Failed to check delegation"); showError(`Delegation check failed: ${error}`); console.log(`\n${chalk.gray("To proceed:")}`); console.log(`${chalk.gray("• Check your KYA_VOUCHED_API_KEY environment variable")}`); console.log(`${chalk.gray("• Verify your network connection")}`); console.log(`${chalk.gray("• Or use --force with --yes for emergency rotation")}`); process.exit(CLI_EXIT_CODES.CONFIG); } } else { // Force mode requires confirmation (Requirement 2.5) if (!yes) { console.log(`\n${chalk.red("āš ļø FORCE ROTATION WARNING")}`); console.log(`${chalk.gray("This will rotate keys without delegation check.")}`); console.log(`${chalk.gray("Current DID:")} ${chalk.yellow(currentIdentity.did)}`); console.log(`${chalk.gray("Current Key ID:")} ${chalk.yellow(currentIdentity.kid)}`); const { confirmed } = await inquirer.prompt([ { type: "confirm", name: "confirmed", message: "Are you sure you want to force rotate keys?", default: false, }, ]); if (!confirmed) { console.log(chalk.yellow("Key rotation cancelled.")); return; } } } } // Development mode - archive old key (Requirement 2.3) let archiveFile; if (isDev) { const spinner = ora("Archiving old key...").start(); try { archiveFile = archiveOldKey(currentIdentity, reason); spinner.succeed(`Old key archived to: ${archiveFile}`); } catch (error) { spinner.fail("Failed to archive old key"); throw error; } } // Generate new keys const spinner = ora("Generating new keys...").start(); try { // Generate new identity (keeping same DID unless --new-did) const preserveDID = newDid ? undefined : currentIdentity.did; const newIdentity = generateNewIdentity(preserveDID); // Save new identity in development mode if (isDev) { saveIdentityToDev(newIdentity); } spinner.succeed("New keys generated successfully!"); console.log(`\n${chalk.bold("New Identity:")}`); console.log(` DID: ${chalk.gray(newIdentity.did)}`); console.log(` Key ID: ${chalk.gray(newIdentity.kid)}`); // Emit audit line (Requirement 2.6) const delegated = !isDev && !force; const auditLine = generateAuditLine(currentIdentity.kid, newIdentity.kid, newIdentity.did, mode, delegated, force); console.log(`\n${chalk.bold("šŸ“‹ Audit Log:")}`); console.log(` ${chalk.gray(auditLine)}`); showSuccess("Key rotation completed successfully!"); console.log(`\n${chalk.bold("šŸš€ Next Steps:")}`); if (isDev) { console.log(` ${chalk.gray("• Restart your development server")}`); console.log(` ${chalk.gray("• Test with:")} ${chalk.cyan("mcpi dev")}`); if (archiveFile) { console.log(` ${chalk.gray("• Old key archived at:")} ${chalk.cyan(archiveFile)}`); } } else { console.log(` ${chalk.gray("• Update production environment variables:")}`); console.log(` ${chalk.gray("AGENT_PRIVATE_KEY=")}${chalk.yellow(newIdentity.privateKey)}`); console.log(` ${chalk.gray("AGENT_KEY_ID=")}${chalk.yellow(newIdentity.kid)}`); if (newDid) { console.log(` ${chalk.gray("AGENT_DID=")}${chalk.yellow(newIdentity.did)}`); } console.log(` ${chalk.gray("• Deploy updated configuration")}`); console.log(` ${chalk.gray("• Verify with:")} ${chalk.cyan("mcpi status")}`); } if (!newDid) { showInfo("DID remained unchanged. Use --new-did to generate a new DID."); } if (newDid && isDev) { showInfo("New DID generated. You may need to re-register with KTA."); } } catch (error) { spinner.fail("Key rotation failed"); throw error; } } catch (error) { showError(`Key rotation failed: ${error.message}`); console.log(`\n${chalk.gray("You can:")}`); console.log(`${chalk.gray("• Check your current identity with:")} ${chalk.cyan("mcpi status")}`); console.log(`${chalk.gray("• Try with --force flag (production only)")}`); console.log(`${chalk.gray("• Check file permissions and network connectivity")}`); process.exit(CLI_EXIT_CODES.GENERAL_ERROR); } } //# sourceMappingURL=rotate.js.map