soft3/neuron/cli/src/main.rs

use cell_engine::{Config, Engine, GraphPort, Progress};
use cell_model::{Lifecycle, Particle, Reader};
use cell_node::{Graph, Rune};
use clap::{Parser, Subcommand};
use serde_json::{Value, json};
use std::path::PathBuf;

#[derive(Parser)]
#[command(
    name = "cell",
    version,
    about = "Local stateful rune abilities with durable graph history"
)]
struct Args {
    #[arg(long, global = true, help = "BBG database directory (default: bbg)")]
    store: Option<PathBuf>,
    #[command(subcommand)]
    command: Command,
}
#[derive(Subcommand)]
enum Command {
    #[command(flatten)]
    Graph(GraphCommand),
    /// Import an old application redb file into a fresh BBG directory.
    #[cfg(feature = "legacy-redb-migration")]
    MigrateRedb {
        source: PathBuf,
        destination: PathBuf,
    },
}
#[derive(Subcommand)]
enum GraphCommand {
    Create {
        source: PathBuf,
        #[arg(long, default_value = "0")]
        initial: String,
        #[arg(long)]
        nonce: Option<String>,
        #[arg(long, default_value_t = 1_000_000)]
        steps: u64,
        #[arg(long)]
        allow: Vec<String>,
    },
    Submit {
        cell: String,
        input: String,
        #[arg(long)]
        nonce: Option<String>,
        #[arg(long)]
        context: Option<String>,
        #[arg(long)]
        queue_only: bool,
    },
    Run {
        cell: String,
    },
    Inspect {
        cell: String,
    },
    History {
        cell: String,
        #[arg(long)]
        after: Option<u64>,
        #[arg(long, default_value_t = 50)]
        limit: usize,
    },
    Take {
        cell: String,
        operation: String,
    },
    Outcome {
        cell: String,
        operation: String,
        attempt: String,
        value: String,
    },
    Fail {
        cell: String,
        operation: String,
        attempt: String,
        reason: String,
    },
    Pause {
        cell: String,
    },
    Resume {
        cell: String,
    },
    Cancel {
        cell: String,
    },
    Retire {
        cell: String,
    },
}
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
fn particle(text: &str) -> Result<Particle> {
    if text.len() != 64 || !text.is_ascii() {
        return Err("expected 64 hexadecimal characters".into());
    }
    let mut id = [0; 32];
    for (i, byte) in id.iter_mut().enumerate() {
        *byte = u8::from_str_radix(&text[i * 2..i * 2 + 2], 16)?;
    }
    Ok(id)
}
fn hex(id: Particle) -> String {
    id.iter().map(|b| format!("{b:02x}")).collect()
}
fn nonce(value: Option<String>) -> Result<Particle> {
    if let Some(value) = value {
        return particle(&value);
    }
    let mut bytes = [0; 32];
    getrandom::fill(&mut bytes).map_err(|e| e.to_string())?;
    Ok(bytes)
}
fn act(name: &str) -> Result<u64> {
    let ordinal = match name {
        "emit" => 1,
        "query" => 2,
        "link" => 3,
        "seal" => 4,
        "subscribe" => 5,
        "host" => 6,
        _ => return Err("unknown act; use emit/query/link/seal/subscribe/host".into()),
    };
    Ok(0xAC75_0000_0000_0000 + ordinal)
}
fn inspection(engine: &Engine<Graph, Rune>, cell: Particle) -> Result<Value> {
    let view = engine.inspect(cell)?;
    Ok(
        json!({ "cell": hex(cell), "head": {"index": view.head.index, "commit": hex(view.head.commit)},
        "lifecycle": view.snapshot.lifecycle.name(), "state": cell_rune::display(&view.state)?,
        "durability": "LocalDurable", "finality": "LocalAuthority",
        "profile": "local-experimental/1", "fault": view.fault,
        "invocation": view.live.as_ref().map(|v| json!({"event": hex(v.event), "context": v.context.map(hex),
            "charged_steps": v.charged, "reserved_steps": v.reserved, "step_limit": v.limit,
            "pending": v.pending.as_ref().map(|p| json!({"operation": hex(p.id), "tag": p.tag, "stage": if p.stage == "attempt-recorded" {"unknown"} else {p.stage}})) })) }),
    )
}
fn run(engine: &Engine<Graph, Rune>, cell: Particle) -> Result<Value> {
    let mut emitted = Vec::new();
    for _ in 0..2000 {
        match engine.tick(cell)? {
            Progress::Advanced(_) => (),
            Progress::Complete(_) | Progress::Idle => {
                let mut result = inspection(engine, cell)?;
                result["emitted"] = json!(emitted);
                return Ok(result);
            }
            Progress::Awaiting {
                operation,
                tag: 0xAC75_0000_0000_0001,
                ..
            } => {
                let receipt = engine.begin_attempt(cell, operation)?;
                engine.record_outcome(cell, operation, receipt.attempt, cell_rune::value("0")?)?;
                emitted.push(json!({"operation": hex(operation), "value": cell_rune::display(&receipt.arguments)?}));
            }
            Progress::Awaiting {
                operation,
                tag,
                arguments,
            } => {
                return Ok(
                    json!({"cell": hex(cell), "status": "awaiting-executor", "operation": hex(operation), "tag": tag, "arguments": cell_rune::display(&arguments)?}),
                );
            }
            Progress::Unknown { operation, attempt } => {
                return Ok(
                    json!({"cell": hex(cell), "status": "unknown-outcome", "operation": hex(operation), "attempt": hex(attempt)}),
                );
            }
        }
    }
    Err("host slice limit reached; run the same cell again to continue".into())
}
fn execute(args: Args) -> Result<Value> {
    match args.command {
        Command::Graph(command) => execute_graph(args.store, command),
        #[cfg(feature = "legacy-redb-migration")]
        Command::MigrateRedb {
            source,
            destination,
        } => {
            Graph::migrate_redb(&source, &destination)?;
            Ok(json!({"source": source, "destination": destination, "status": "migrated"}))
        }
    }
}
fn execute_graph(store: Option<PathBuf>, command: GraphCommand) -> Result<Value> {
    let store = match store {
        Some(path) => path,
        None => {
            if std::path::Path::new("cell.redb").try_exists()? {
                return Err("legacy cell.redb exists; migrate it with a legacy-redb-migration build, or select a BBG directory explicitly with --store".into());
            }
            PathBuf::from("bbg")
        }
    };
    let graph = Graph::open(store)?;
    let engine = Engine::with_ward(graph, Rune, cell_node::LocalWard);
    match command {
        GraphCommand::Create {
            source,
            initial,
            nonce: n,
            steps,
            allow,
        } => {
            if std::fs::metadata(&source)?.len() > 8192 {
                return Err("source exceeds 8192 bytes".into());
            }
            let nonce = nonce(n)?;
            let config = Config {
                step_limit: steps,
                allowed_acts: allow.iter().map(|v| act(v)).collect::<Result<Vec<_>>>()?,
            };
            let cell = engine.create(
                std::fs::read(source)?,
                cell_rune::value(&initial)?,
                nonce,
                config,
            )?;
            let mut result = inspection(&engine, cell)?;
            result["birth_nonce"] = json!(hex(nonce));
            Ok(result)
        }
        GraphCommand::Submit {
            cell,
            input,
            nonce: n,
            context,
            queue_only,
        } => {
            let cell = particle(&cell)?;
            let nonce = nonce(n)?;
            let context = context.map(|v| particle(&v)).transpose()?;
            engine.submit(cell, nonce, cell_rune::value(&input)?, context)?;
            let mut result = if queue_only {
                inspection(&engine, cell)?
            } else {
                run(&engine, cell)?
            };
            result["request_nonce"] = json!(hex(nonce));
            Ok(result)
        }
        GraphCommand::Run { cell } => run(&engine, particle(&cell)?),
        GraphCommand::Inspect { cell } => inspection(&engine, particle(&cell)?),
        GraphCommand::History { cell, after, limit } => {
            let cell = particle(&cell)?;
            let mut entries = Vec::new();
            for head in engine.graph.history(cell, after, limit)? {
                let mut events = Vec::new();
                if head.index > 0 {
                    let mut r = Reader::new(&engine.graph, 20_000);
                    let c = r.record(head.commit, "cell/commit/1", 10)?;
                    for event in r.list(c[5], 256)? {
                        let id = r.reference(event)?;
                        let fields = r.record(id, "cell/event/1", 10)?;
                        events.push(json!({"event": hex(id), "entry": r.text(fields[3])?, "payload": hex(r.reference(fields[4])?), "context": r.optional_ref(fields[5])?.map(hex)}));
                    }
                }
                entries.push(
                    json!({"index": head.index, "commit": hex(head.commit), "events": events}),
                );
            }
            Ok(json!({"cell": hex(cell), "history": entries}))
        }
        GraphCommand::Take { cell, operation } => {
            let receipt = engine.begin_attempt(particle(&cell)?, particle(&operation)?)?;
            Ok(
                json!({"operation": hex(receipt.operation), "attempt": hex(receipt.attempt), "tag": receipt.tag, "arguments": cell_rune::display(&receipt.arguments)?}),
            )
        }
        GraphCommand::Outcome {
            cell,
            operation,
            attempt,
            value,
        } => {
            let cell = particle(&cell)?;
            engine.record_outcome(
                cell,
                particle(&operation)?,
                particle(&attempt)?,
                cell_rune::value(&value)?,
            )?;
            if engine.inspect(cell)?.snapshot.lifecycle == Lifecycle::Active {
                run(&engine, cell)
            } else {
                inspection(&engine, cell)
            }
        }
        GraphCommand::Fail {
            cell,
            operation,
            attempt,
            reason,
        } => {
            let cell = particle(&cell)?;
            engine.record_failure(cell, particle(&operation)?, particle(&attempt)?, reason)?;
            inspection(&engine, cell)
        }
        GraphCommand::Pause { cell } => {
            let cell = particle(&cell)?;
            engine.manage(cell, Lifecycle::Paused)?;
            inspection(&engine, cell)
        }
        GraphCommand::Resume { cell } => {
            let cell = particle(&cell)?;
            engine.manage(cell, Lifecycle::Active)?;
            run(&engine, cell)
        }
        GraphCommand::Cancel { cell } => {
            let cell = particle(&cell)?;
            engine.cancel(cell)?;
            inspection(&engine, cell)
        }
        GraphCommand::Retire { cell } => {
            let cell = particle(&cell)?;
            engine.manage(cell, Lifecycle::Retiring)?;
            engine.manage(cell, Lifecycle::Retired)?;
            inspection(&engine, cell)
        }
    }
}
fn main() {
    match execute(Args::parse()) {
        Ok(value) => println!("{value}"),
        Err(error) => {
            eprintln!("{}", json!({"error": error.to_string()}));
            std::process::exit(1);
        }
    }
}

Homonyms

cyber/src/main.rs
cyb/cli/src/main.rs
cyb/optica/src/main.rs
soft3/nox/cli/main.rs
cyb/shell/src/main.rs
soft3/glia/import/main.rs
warriors/joy/cli/main.rs
cyb/apps/src/main.rs
warriors/trisha/cli/main.rs
neural/trident/src/main.rs
neural/rune/cli/main.rs
cyberia/cyberia-my/src/main.rs
soft3/tru/cli/main.rs
soft3/radio/iroh-dns-server/src/main.rs
neural/eidos/cli/src/main.rs
neural/rs/link/src/main.rs
neural/rs/pure-rust-check/src/main.rs
soft3/radio/iroh-relay/src/main.rs
soft3/radio/radio-cli/src/main.rs
soft3/glia/run/cli/main.rs
soft3/cybergraph/cli/src/main.rs
soft3/hemera/cli/src/main.rs
neural/rs/cli/src/main.rs
warriors/erga/cli/src/main.rs
neural/rs/macho-linker/src/main.rs
soft3/radio/particle/src/main.rs
soft3/lens/cli/src/main.rs
soft3/bbg/cli/src/main.rs
soft3/strata/cli/src/main.rs
neural/rs/rsc/src/main.rs
soft3/foculus/src/bin/main.rs
soft3/zheng/cli/src/main.rs
soft3/strata/jali/cli/src/main.rs
warriors/erga/rs/mine-bench/src/main.rs
neural/trident/silicon/src/bin/main.rs
cyb/wysm/crates/cli/src/main.rs
soft3/strata/genies/cli/src/main.rs
soft3/lytics/rs/agent/src/main.rs
warriors/erga/rs/rtable-bench/src/main.rs
cyb/honeycrisp/rane/src/probe/main.rs
soft3/strata/kuro/cli/src/main.rs
warriors/erga/rs/blake-bench/src/main.rs
soft3/mudra/audit/quantus-mldsa-crosscheck/src/main.rs
neural/inf/rs/cli/src/main.rs
cyb/honeycrisp/acpu/src/probe/main.rs
soft3/strata/trop/cli/src/main.rs
soft3/strata/nebu/cli/src/main.rs
warriors/trisha/tools/neptune-policy-oracle/src/main.rs
soft3/lytics/rs/ingest/src/main.rs
cyb/honeycrisp/unimem/experiments/hyp_probe/src/main.rs
cyb/honeycrisp/unimem/experiments/iosurface_probe/src/main.rs
soft3/mudra/audit/signature-optimality/xnt-predicate-check/src/main.rs
cyb/honeycrisp/unimem/experiments/dext_contiguous_alloc/client/src/main.rs
cyb/honeycrisp/unimem/experiments/dext_iosurface_pa/client/src/main.rs

Graph