runner-core 0.5.21

Core helpers for Greentic runner: pack resolution, signing, session/state glue, and utilities
Documentation
use std::fs;
use std::io::Write;
use std::path::Path;

use anyhow::{Context, Result};
use greentic_config_types::{PackSourceConfig, PacksConfig};
use greentic_pack::builder::{FlowBundle, PACK_VERSION, PackBuilder, PackMeta};
use runner_core::{Index, PackConfig, PackManager};
use semver::Version;
use serde_json::json;
use tempfile::NamedTempFile;

fn main() -> Result<()> {
    ensure_example_pack()?;
    let packs_cfg = PacksConfig {
        source: PackSourceConfig::LocalIndex {
            path: Path::new("./examples/index.json").to_path_buf(),
        },
        cache_dir: Path::new("./.packs").to_path_buf(),
        index_cache_ttl_secs: None,
        trust: None,
    };
    let cfg = PackConfig::from_packs(&packs_cfg)?;
    let index = Index::load(&cfg.index_location)?;
    let manager = PackManager::new(cfg.clone())?;

    let resolved = manager.resolve_all_for_index(&index)?;
    for (tenant, packs) in resolved.tenants() {
        println!(
            "tenant `{tenant}` main pack {}@{} cached at {}",
            packs.main.reference.name,
            packs.main.reference.version.cache_label(),
            packs.main.path.display()
        );
    }
    Ok(())
}

fn ensure_example_pack() -> Result<()> {
    let pack_path = Path::new("examples/packs/demo.gtpack");
    if pack_path.exists() {
        return Ok(());
    }

    if let Some(parent) = pack_path.parent() {
        fs::create_dir_all(parent)
            .with_context(|| format!("failed to create {}", parent.display()))?;
    }

    let mut wasm_file = NamedTempFile::new()?;
    wasm_file.write_all(b"\0asm\x01\0\0\0")?;

    PackBuilder::new(example_meta())
        .with_flow(example_flow())
        .with_component_wasm("demo.component", Version::parse("1.0.0")?, wasm_file.path())
        .build(pack_path)?;

    println!("built example pack at {}", pack_path.display());
    Ok(())
}

fn example_meta() -> PackMeta {
    PackMeta {
        pack_version: PACK_VERSION,
        pack_id: "ai.greentic.runner.example".into(),
        version: Version::parse("0.1.0").unwrap(),
        name: "Runner Example".into(),
        kind: None,
        description: Some("Minimal pack for fs resolver showcase".into()),
        authors: vec!["Greentic".into()],
        license: None,
        homepage: None,
        support: None,
        vendor: None,
        imports: vec![],
        entry_flows: vec!["qa".into()],
        created_at_utc: "2025-01-01T00:00:00Z".into(),
        events: None,
        repo: None,
        messaging: None,
        interfaces: Vec::new(),
        annotations: serde_json::Map::new(),
        distribution: None,
        components: Vec::new(),
    }
}

fn example_flow() -> FlowBundle {
    let flow_json = json!({
        "id": "qa",
        "kind": "flow/v1",
        "entry": "start",
        "nodes": []
    });
    let hash = blake3::hash(&serde_json::to_vec(&flow_json).unwrap())
        .to_hex()
        .to_string();
    FlowBundle {
        id: "qa".into(),
        kind: "flow/v1".into(),
        entry: "start".into(),
        yaml: "id: qa\nentry: start\n".into(),
        json: flow_json,
        hash_blake3: hash,
        nodes: Vec::new(),
    }
}