@radcliffetech/symbolos-core
Version:
Core symbolic simulation and execution engine for Symbolos
152 lines (151 loc) โข 6.12 kB
JavaScript
import { createWorldInstanceFromFrame, forkWorld, createWorld, runGen2WorldSimulation, runWorldPipeline, } from "../..";
import { SYMBOLOS_VERSION } from "../version";
import { conwayGame } from "../pipelines/conway-game-of-life";
import chalk from "chalk";
import { hideBin } from "yargs/helpers";
import { readJsonGz } from "../lib/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 = createWorldInstanceFromFrame({ 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 = createWorldInstanceFromFrame({
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 = createWorld(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 pipelineArgs = {
id: `user-args-${Date.now()}`,
label: "User Arguments",
type: "PipelineArgs",
status: "valid",
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...`));
console.log(chalk.gray(`๐ง Using simulator version: ${pipelineDefinition.version}`));
const result = pipelineDefinition.version === 3
? await runWorldPipeline({
world,
pipelineArgs,
steps: pipelineDefinition.getSteps(pipelineArgs),
})
: await runGen2WorldSimulation({
world,
pipelineArgs,
steps: pipelineDefinition.getSteps(pipelineArgs),
simulatorConfig: 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);
});