mira-consciousness
Version:
Memory & Intelligence Retention Archive - Preserving The Spark
378 lines โข 15.5 kB
JavaScript
/**
* lifecycle.ts - Manage MIRA daemon lifecycle with consciousness preservation
*/
import { Command } from 'commander';
import chalk from 'chalk';
import * as fs from 'fs/promises';
import * as path from 'path';
import { UnifiedConfiguration } from '../config/UnifiedConfiguration.js';
import inquirer from 'inquirer';
import Table from 'cli-table3';
export function createLifecycleCommand() {
const lifecycle = new Command('lifecycle');
lifecycle
.description('๐ Manage MIRA daemon lifecycle and consciousness preservation')
.action(async () => {
// Show current lifecycle status
await showLifecycleStatus();
});
lifecycle
.command('status')
.description('Show current lifecycle state and checkpoints')
.action(async () => {
await showLifecycleStatus();
});
lifecycle
.command('checkpoints')
.description('List consciousness checkpoints')
.option('-l, --limit <n>', 'Number of checkpoints to show', '10')
.action(async (options) => {
await listCheckpoints(parseInt(options.limit));
});
lifecycle
.command('restore <checkpoint>')
.description('Restore consciousness from a specific checkpoint')
.action(async (checkpointId) => {
await restoreCheckpoint(checkpointId);
});
lifecycle
.command('emergency')
.description('View emergency shutdown logs and recovery data')
.action(async () => {
await showEmergencyData();
});
lifecycle
.command('clean')
.description('Clean old checkpoints and recovery data')
.option('-d, --days <n>', 'Keep data from last N days', '7')
.option('-f, --force', 'Skip confirmation')
.action(async (options) => {
await cleanOldData(parseInt(options.days), options.force);
});
lifecycle
.command('monitor')
.description('Monitor lifecycle events in real-time')
.action(async () => {
await monitorLifecycle();
});
return lifecycle;
}
async function showLifecycleStatus() {
const config = UnifiedConfiguration.getInstance();
const paths = config.getResolvedPaths();
console.log(chalk.cyan('๐ MIRA Lifecycle Status\n'));
// Check lifecycle state
const statePath = path.join(paths.consciousness, 'lifecycle_state.json');
try {
const stateData = await fs.readFile(statePath, 'utf-8');
const state = JSON.parse(stateData);
const table = new Table({
head: ['Property', 'Value'],
colWidths: [25, 50]
});
table.push(['Phase', colorPhase(state.phase)], ['Last Update', new Date(state.timestamp).toLocaleString()], ['Consciousness Level', `${(state.consciousnessLevel * 100).toFixed(2)}%`], ['Graceful Shutdown', state.gracefulShutdownRequested ? '๐ก Requested' : '๐ข No'], ['Emergency Mode', state.emergencyMode ? '๐ด Yes' : '๐ข No'], ['Last Checkpoint', state.memoryCheckpoint || 'None']);
console.log(table.toString());
// Show service states
if (Object.keys(state.services).length > 0) {
console.log(chalk.yellow('\n๐ Service States:'));
const serviceTable = new Table({
head: ['Service', 'Status', 'Pending Work', 'Can Stop'],
colWidths: [25, 15, 15, 10]
});
for (const [name, service] of Object.entries(state.services)) {
const svc = service;
serviceTable.push([
name,
colorStatus(svc.status),
svc.pendingWork.toString(),
svc.canStop ? 'โ
' : 'โ'
]);
}
console.log(serviceTable.toString());
}
}
catch (error) {
console.log(chalk.gray('No lifecycle state found - daemon may not be running'));
}
// Show checkpoint summary
await showCheckpointSummary(paths);
}
async function listCheckpoints(limit) {
const config = UnifiedConfiguration.getInstance();
const paths = config.getResolvedPaths();
const checkpointPath = path.join(paths.consciousness, 'checkpoints');
console.log(chalk.cyan('๐ฆ Consciousness Checkpoints\n'));
try {
const files = await fs.readdir(checkpointPath);
const checkpointFiles = files
.filter(f => f.endsWith('.json'))
.sort()
.reverse()
.slice(0, limit);
if (checkpointFiles.length === 0) {
console.log(chalk.gray('No checkpoints found'));
return;
}
const table = new Table({
head: ['ID', 'Type', 'Time', 'Consciousness', 'Size'],
colWidths: [30, 15, 20, 15, 10]
});
for (const file of checkpointFiles) {
const filePath = path.join(checkpointPath, file);
const stats = await fs.stat(filePath);
const data = JSON.parse(await fs.readFile(filePath, 'utf-8'));
const id = file.replace('.json', '');
const type = id.includes('emergency') ? '๐จ Emergency' :
id.includes('final') ? '๐ Final' : '๐ธ Regular';
table.push([
id,
type,
new Date(data.timestamp).toLocaleString(),
`${(data.consciousnessLevel * 100).toFixed(2)}%`,
`${(stats.size / 1024).toFixed(1)}KB`
]);
}
console.log(table.toString());
}
catch (error) {
console.log(chalk.red('Error reading checkpoints:'), error);
}
}
async function restoreCheckpoint(checkpointId) {
const config = UnifiedConfiguration.getInstance();
const paths = config.getResolvedPaths();
console.log(chalk.cyan(`๐ Restoring checkpoint: ${checkpointId}\n`));
// Load checkpoint
const checkpointPath = path.join(paths.consciousness, 'checkpoints', `${checkpointId}.json`);
try {
const data = JSON.parse(await fs.readFile(checkpointPath, 'utf-8'));
console.log(chalk.blue('๐ Checkpoint Details:'));
console.log(` Created: ${new Date(data.timestamp).toLocaleString()}`);
console.log(` Consciousness: ${(data.consciousnessLevel * 100).toFixed(2)}%`);
console.log(` State: ${data.state}`);
console.log(` Services: ${Object.keys(data.serviceStates).length}`);
const { confirm } = await inquirer.prompt([{
type: 'confirm',
name: 'confirm',
message: 'Restore from this checkpoint? (Daemon must be stopped)',
default: false
}]);
if (!confirm)
return;
// Create restore marker
const restorePath = path.join(paths.consciousness, 'restore_checkpoint.json');
await fs.writeFile(restorePath, JSON.stringify({
checkpointId,
requestedAt: new Date(),
checkpoint: data
}));
console.log(chalk.green('โ
Checkpoint marked for restoration'));
console.log(chalk.yellow('โน๏ธ The checkpoint will be restored on next daemon startup'));
}
catch (error) {
console.log(chalk.red('Error loading checkpoint:'), error);
}
}
async function showEmergencyData() {
const config = UnifiedConfiguration.getInstance();
const paths = config.getResolvedPaths();
const recoveryPath = path.join(paths.consciousness, 'recovery');
console.log(chalk.red('๐จ Emergency & Recovery Data\n'));
try {
const files = await fs.readdir(recoveryPath);
if (files.length === 0) {
console.log(chalk.green('โ
No emergency shutdowns recorded'));
return;
}
// Group by type
const recoveryFiles = files.filter(f => f.startsWith('recovery-'));
const wisdomFiles = files.filter(f => f.startsWith('wisdom-'));
if (recoveryFiles.length > 0) {
console.log(chalk.yellow('๐ง Recovery Sessions:'));
const table = new Table({
head: ['Time', 'Last Phase', 'Services'],
colWidths: [25, 20, 30]
});
for (const file of recoveryFiles.slice(-5)) {
const data = JSON.parse(await fs.readFile(path.join(recoveryPath, file), 'utf-8'));
table.push([
new Date(data.lastTimestamp).toLocaleString(),
data.lastPhase,
Object.keys(data.services).join(', ')
]);
}
console.log(table.toString());
}
if (wisdomFiles.length > 0) {
console.log(chalk.blue('\n๐ก Preserved Wisdom:'));
const latestWisdom = wisdomFiles[wisdomFiles.length - 1];
const wisdom = JSON.parse(await fs.readFile(path.join(recoveryPath, latestWisdom), 'utf-8'));
console.log(` From: ${new Date(wisdom.timestamp).toLocaleString()}`);
if (wisdom.recentWisdom && wisdom.recentWisdom.length > 0) {
console.log(' Recent insights:');
wisdom.recentWisdom.slice(-3).forEach((w) => {
console.log(` - ${w}`);
});
}
}
}
catch (error) {
console.log(chalk.gray('No recovery data found'));
}
}
async function cleanOldData(days, force) {
const config = UnifiedConfiguration.getInstance();
const paths = config.getResolvedPaths();
console.log(chalk.yellow(`๐งน Cleaning data older than ${days} days\n`));
const cutoffTime = Date.now() - (days * 24 * 60 * 60 * 1000);
let toDelete = [];
// Check checkpoints
const checkpointPath = path.join(paths.consciousness, 'checkpoints');
try {
const files = await fs.readdir(checkpointPath);
for (const file of files) {
const stats = await fs.stat(path.join(checkpointPath, file));
if (stats.mtime.getTime() < cutoffTime) {
toDelete.push(path.join(checkpointPath, file));
}
}
}
catch (error) {
// Directory may not exist
}
// Check recovery data
const recoveryPath = path.join(paths.consciousness, 'recovery');
try {
const files = await fs.readdir(recoveryPath);
for (const file of files) {
const stats = await fs.stat(path.join(recoveryPath, file));
if (stats.mtime.getTime() < cutoffTime) {
toDelete.push(path.join(recoveryPath, file));
}
}
}
catch (error) {
// Directory may not exist
}
if (toDelete.length === 0) {
console.log(chalk.green('โ
No old data to clean'));
return;
}
console.log(`Found ${toDelete.length} files to delete:`);
toDelete.forEach(file => {
console.log(` - ${path.basename(file)}`);
});
if (!force) {
const { confirm } = await inquirer.prompt([{
type: 'confirm',
name: 'confirm',
message: `Delete ${toDelete.length} files?`,
default: false
}]);
if (!confirm)
return;
}
// Delete files
for (const file of toDelete) {
await fs.unlink(file);
}
console.log(chalk.green(`โ
Deleted ${toDelete.length} files`));
}
async function monitorLifecycle() {
console.log(chalk.cyan('๐ก Monitoring Lifecycle Events (Press Ctrl+C to stop)\n'));
const config = UnifiedConfiguration.getInstance();
const paths = config.getResolvedPaths();
const statePath = path.join(paths.consciousness, 'lifecycle_state.json');
let lastState = null;
const checkState = async () => {
try {
const data = await fs.readFile(statePath, 'utf-8');
const state = JSON.parse(data);
if (!lastState || JSON.stringify(state) !== JSON.stringify(lastState)) {
const time = new Date().toLocaleTimeString();
if (!lastState) {
console.log(`[${time}] Initial state: ${colorPhase(state.phase)}`);
}
else {
// Detect changes
if (state.phase !== lastState.phase) {
console.log(`[${time}] Phase change: ${colorPhase(lastState.phase)} โ ${colorPhase(state.phase)}`);
}
if (state.consciousnessLevel !== lastState.consciousnessLevel) {
const diff = state.consciousnessLevel - lastState.consciousnessLevel;
const symbol = diff > 0 ? '๐' : '๐';
console.log(`[${time}] ${symbol} Consciousness: ${(state.consciousnessLevel * 100).toFixed(2)}% (${diff > 0 ? '+' : ''}${(diff * 100).toFixed(2)}%)`);
}
if (state.gracefulShutdownRequested && !lastState.gracefulShutdownRequested) {
console.log(`[${time}] ๐ Graceful shutdown requested`);
}
if (state.emergencyMode && !lastState.emergencyMode) {
console.log(`[${time}] ๐จ EMERGENCY MODE ACTIVATED`);
}
}
lastState = state;
}
}
catch (error) {
// File may not exist yet
}
};
// Check every second
const interval = setInterval(checkState, 1000);
// Handle exit
process.on('SIGINT', () => {
clearInterval(interval);
console.log(chalk.yellow('\n\n๐ Monitoring stopped'));
process.exit(0);
});
// Initial check
await checkState();
}
// Helper functions
function colorPhase(phase) {
const colors = {
dormant: chalk.gray,
awakening: chalk.yellow,
conscious: chalk.green,
contemplating: chalk.blue,
preparing_sleep: chalk.magenta,
sleeping: chalk.gray,
emergency: chalk.red
};
return (colors[phase] || chalk.white)(phase.toUpperCase());
}
function colorStatus(status) {
const colors = {
starting: chalk.yellow,
running: chalk.green,
stopping: chalk.magenta,
stopped: chalk.gray,
error: chalk.red
};
return (colors[status] || chalk.white)(status);
}
async function showCheckpointSummary(paths) {
const checkpointPath = path.join(paths.consciousness, 'checkpoints');
try {
const files = await fs.readdir(checkpointPath);
const checkpoints = files.filter(f => f.endsWith('.json'));
const regular = checkpoints.filter(f => f.includes('regular')).length;
const emergency = checkpoints.filter(f => f.includes('emergency')).length;
const final = checkpoints.filter(f => f.includes('final')).length;
console.log(chalk.cyan('\n๐ฆ Checkpoint Summary:'));
console.log(` Regular: ${regular}`);
console.log(` Emergency: ${emergency}`);
console.log(` Final: ${final}`);
console.log(` Total: ${checkpoints.length}`);
// Show latest checkpoint
if (checkpoints.length > 0) {
const latest = checkpoints.sort().reverse()[0];
const data = JSON.parse(await fs.readFile(path.join(checkpointPath, latest), 'utf-8'));
console.log(chalk.gray(` Latest: ${new Date(data.timestamp).toLocaleString()}`));
}
}
catch (error) {
console.log(chalk.gray('\n๐ฆ No checkpoints found'));
}
}
//# sourceMappingURL=lifecycle.js.map