UNPKG

@radcliffetech/symbolos-core

Version:

Core symbolic simulation and execution engine for Symbolos

147 lines (146 loc) โ€ข 5.84 kB
import { createWorldFromFrame, forkWorld, makeNewWorld, runWorldPipeline, } from '@core/simulators/world-simulator'; import { SYMBOLOS_VERSION } from '../version'; import { conwayGame } from '@core/pipelines/examples/conway-game-of-life'; import chalk from 'chalk'; import { hideBin } from 'yargs/helpers'; import { readJsonGz } from '@core/lib/utils/file-utils'; import yargs from 'yargs/yargs'; const getPipelineRegistry = () => ({ [conwayGame.id]: conwayGame, }); const defaultPipelineId = conwayGame.id; async function main() { console.log(chalk.gray(`๐Ÿชช Symbolos Core v${SYMBOLOS_VERSION}`)); const args = await yargs(hideBin(process.argv)) .option('fromFrame', { type: 'string', describe: 'Path to .world.json.gz frame to resume from', }) .option('fromArchive', { type: 'string', describe: 'Path to .world.json.gz archive to fork from', }) .option('pipelineId', { type: 'string', default: defaultPipelineId, describe: 'Pipeline ID', }) .option('verbose', { type: 'boolean', default: false }) .option('params', { type: 'array', describe: 'Pipeline parameters as key=value pairs', }) .option('list', { type: 'boolean', describe: 'List available pipeline IDs and exit', }) .parse(); let pipelineId = args.pipelineId || defaultPipelineId; const pipelineRegistry = getPipelineRegistry(); if (args.list) { console.log('๐Ÿ“š Available pipelines:'); Object.entries(pipelineRegistry).forEach(([id, def]) => { console.log(`- ${id} โ€” ${def.label}`); }); process.exit(0); } const pipelineDefinition = pipelineRegistry[pipelineId]; if (!pipelineDefinition) { console.error(chalk.red(`โŒ Pipeline with ID "${pipelineId}" not found!`)); console.error(chalk.red(`Available pipelines: ${Object.keys(pipelineRegistry).join(', ')}`)); process.exit(1); } const framePath = args.fromFrame; const verbose = args.verbose; const cliParams = (args.params || []).reduce((acc, pair) => { const [key, value] = pair.split('='); acc[key] = isNaN(Number(value)) ? value : Number(value); return acc; }, {}); let world; const runId = crypto.randomUUID(); if (framePath) { console.log(chalk.cyan(`๐Ÿ“‚ Resuming world from: ${framePath}`)); const frame = await readJsonGz(framePath); world = createWorldFromFrame({ frame, pipelineId, runId }); pipelineId = world.pipelineId; console.log(chalk.green(`โœ… World restored at tick ${world.tick}, step ${world.step}`)); } else if (args.fromArchive) { console.log(chalk.cyan(`๐Ÿ“‚ Forking world from archive: ${args.fromArchive}`)); const archive = await readJsonGz(args.fromArchive); const baseWorld = createWorldFromFrame({ frame: archive, pipelineId: archive.pipelineId || defaultPipelineId, runId: archive.runId || crypto.randomUUID(), }); world = forkWorld(baseWorld, cliParams); pipelineId = world.pipelineId; console.log(chalk.green(`โœ… Forked world created with new runId: ${world.runId}`)); } else { console.log(chalk.yellow(`๐ŸŒฑ Starting new world for pipeline: ${pipelineId}`)); world = makeNewWorld(pipelineId); console.log(chalk.green(`โœ… New world initialized with runId: ${world.runId}`)); } console.log(chalk.blue(`๐Ÿงช Using pipeline: ${pipelineId}`)); console.log(chalk.blueBright(`๐Ÿ”ง Pipeline Definition: ${pipelineDefinition.label}`)); const mergedParams = { ...(pipelineDefinition.args?.defaults || {}), ...cliParams, }; const userArgs = { id: `user-args-${Date.now()}`, label: 'User Arguments', type: 'PipelineArgs', status: 'valid', pipelineId: pipelineDefinition.id, runId, createdAt: new Date().toISOString(), params: mergedParams, }; const pipelineConfig = { verbose, }; if (Object.keys(mergedParams).length > 0) { console.log(chalk.gray('๐Ÿ“ฆ Final parameters:')); Object.entries(mergedParams).forEach(([key, value]) => { console.log(chalk.gray(` ${key}: ${value}`)); }); } console.log(chalk.blueBright(`๐Ÿš€ Running simulation...`)); const result = await runWorldPipeline({ world, userArgs, steps: pipelineDefinition.steps(userArgs), config: pipelineConfig, }); if (!result || typeof result.tick !== 'number') { console.error(chalk.red('โš ๏ธ Simulation failed or returned invalid result.')); process.exit(1); } console.log(chalk.green(`โœ… Simulation completed! Final tick: ${result.tick}`)); if (result.context && result.context._artifactsById) { const archive = Array.from(result.context._artifactsById.values()).find((o) => o.type === 'WorldArchive'); const typedArchive = archive; if (typedArchive?.filePath) { console.log(chalk.gray(`๐Ÿ“„ Archive saved to: ${typedArchive.filePath}`)); } else { console.log(chalk.gray(`๐Ÿ“„ Archive complete, but file path not recorded.`)); } } // Summary output console.log(chalk.magenta('\n๐Ÿ“ Simulation Summary:')); console.log(`- Pipeline ID: ${pipelineId}`); console.log(`- Run ID: ${runId}`); console.log(`- Final Tick: ${result.tick}`); console.log(`- Parameters:`); Object.entries(mergedParams).forEach(([key, value]) => { console.log(` โ€ข ${key}: ${value}`); }); } main().catch((err) => { console.error(chalk.red('โŒ Error during simulation:'), err); process.exit(1); });