use crate::agent::CliError;
use image::GenericImageView;
use std::collections::BTreeMap;
use std::path::PathBuf;
#[derive(clap::Args)]
pub(crate) struct Args {
#[command(subcommand)]
operation: Operation,
}
#[derive(clap::Subcommand)]
enum Operation {
Import {
#[arg(long)]
reference: PathBuf,
#[arg(long)]
mask: PathBuf,
#[arg(long)]
phrase: String,
#[arg(long)]
out: PathBuf,
},
Status,
#[cfg(feature = "semantic-regions")]
Cache {
#[arg(long)]
manifest: PathBuf,
#[arg(long)]
cache: PathBuf,
},
#[cfg(feature = "semantic-regions")]
RuntimeProbe {
#[arg(long)]
manifest: PathBuf,
#[arg(long)]
cache: PathBuf,
#[arg(long)]
library: PathBuf,
},
}
pub(crate) fn run(args: Args) -> Result<u8, CliError> {
match args.operation {
Operation::Status => {
crate::emit(&format!(
"{}\n",
serde_json::json!({"mask_import":"available","semantic_regions_feature":cfg!(feature="semantic-regions"),"text_to_mask":"unavailable","reason":"checkpoint-specific detector/SAM 2.1 ONNX export and source parity remain unqualified; import a reviewed inclusion mask","runtime":"ONNX Runtime 1.22 via ort 2.0.0-rc.10"})
))?;
}
Operation::Import {
reference,
mask,
phrase,
out,
} => {
if phrase.trim().is_empty() {
return Err(CliError::usage("region phrase must be nonempty"));
}
let reference_bytes =
std::fs::read(&reference).map_err(|e| CliError::io(e.to_string()))?;
let image = image::load_from_memory(&reference_bytes)
.map_err(|e| CliError::usage(e.to_string()))?;
let mask_bytes = std::fs::read(mask).map_err(|e| CliError::io(e.to_string()))?;
let mask =
image::load_from_memory(&mask_bytes).map_err(|e| CliError::usage(e.to_string()))?;
if mask.color() != image::ColorType::L8
|| mask.dimensions() != image.dimensions()
|| mask.as_bytes().iter().any(|&v| v != 0 && v != 255)
{
return Err(CliError::usage(
"import needs binary L8 inclusion mask with reference dimensions",
));
}
let region = saccade_core::localized::freeze(
saccade_core::localized::digest(&reference_bytes),
[image.width(), image.height()],
mask.as_bytes()
.iter()
.map(|&v| u8::from(v == 255))
.collect(),
BTreeMap::from([
("method".into(), "phrase_mask_import".into()),
("original_phrase".into(), phrase),
("selection_status".into(), "explicit_import".into()),
(
"source_mask_sha256".into(),
saccade_core::localized::digest(&mask_bytes),
),
("model_inference".into(), "not_run".into()),
]),
)?;
let file = std::fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(out)
.map_err(|e| CliError::io(e.to_string()))?;
serde_json::to_writer_pretty(file, ®ion)?;
crate::emit(&format!(
"frozen inclusion region {}; model inference not run\n",
region.mask_sha256
))?;
}
#[cfg(feature = "semantic-regions")]
Operation::Cache { manifest, cache } => {
let model = crate::parse_contract(
&std::fs::read(manifest).map_err(|e| CliError::io(e.to_string()))?,
"saccade-region-models.v1",
)?;
let paths = saccade_core::semantic::cache_models(&model, &cache)?;
crate::emit(&format!(
"{} hash-verified artifacts cached; inference/parity remain unqualified\n",
paths.len()
))?;
}
#[cfg(feature = "semantic-regions")]
Operation::RuntimeProbe {
manifest,
cache,
library,
} => {
let model = crate::parse_contract(
&std::fs::read(manifest).map_err(|e| CliError::io(e.to_string()))?,
"saccade-region-models.v1",
)?;
let loaded = saccade_core::semantic::probe_runtime(&model, &cache, &library)?;
crate::emit(&format!(
"{} graphs loaded; source parity and text-to-mask inference remain unqualified\n",
loaded.len()
))?;
}
}
Ok(0)
}