Skip to main content

mesh_llm_cli/
parser.rs

1mod commands;
2mod normalization;
3mod runtime_surface_help;
4mod validation;
5
6pub use commands::{
7    AuthCommand, BinaryFlavor, Cli, Command, ConfigCommand, DiscoveryScope, DoctorCommand,
8    GpuCommand, MeshDiscoveryMode, MeshGuardrailCliMode, PluginCommand, SkillAgentArg,
9    SkillCommand, TrustCommand, TrustPolicy,
10};
11pub use normalization::{
12    NormalizedRuntimeArgs, RuntimeSurface, legacy_runtime_surface_warning,
13    normalize_runtime_surface_args,
14};
15pub use runtime_surface_help::runtime_surface_help;
16pub use validation::validate_discovery_mode_args;
17
18#[cfg(test)]
19mod setup_tests;
20
21#[cfg(test)]
22mod uninstall_tests;
23
24#[cfg(test)]
25use std::path::PathBuf;
26
27#[cfg(test)]
28use clap::Parser;
29
30#[cfg(test)]
31use crate::runtime::RuntimeCommand;
32
33#[cfg(test)]
34pub fn assert_mesh_requirements_docs_examples_parse() {
35    let unrestricted_args =
36        normalize_runtime_surface_args(["mesh-llm", "serve", "--model", "Qwen3-8B-Q4_K_M"]);
37    let unrestricted = Cli::parse_from(unrestricted_args.normalized.clone());
38    assert!(unrestricted.command.is_none());
39    assert_eq!(unrestricted.model, vec![PathBuf::from("Qwen3-8B-Q4_K_M")]);
40    assert!(!unrestricted.publish);
41
42    let signed_public_args = normalize_runtime_surface_args([
43        "mesh-llm",
44        "serve",
45        "--model",
46        "Qwen3-8B-Q4_K_M",
47        "--publish",
48        "--require-release-attestation",
49        "--release-signer-key",
50        "ed25519:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
51        "--owner-key",
52        "~/.mesh-llm/owner-keystore.json",
53        "--owner-required",
54        "--trust-policy",
55        "require-owned",
56        "--node-label",
57        "lab-a",
58    ]);
59    let signed_public = Cli::parse_from(signed_public_args.normalized.clone());
60    assert!(signed_public.command.is_none());
61    assert_eq!(signed_public.model, vec![PathBuf::from("Qwen3-8B-Q4_K_M")]);
62    assert!(signed_public.publish);
63    assert!(signed_public.require_release_attestation);
64    assert_eq!(
65        signed_public.release_signer_key,
66        vec![
67            "ed25519:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".to_string()
68        ]
69    );
70    assert_eq!(
71        signed_public.owner_key,
72        Some(PathBuf::from("~/.mesh-llm/owner-keystore.json"))
73    );
74    assert!(signed_public.owner_required);
75    assert_eq!(signed_public.trust_policy, Some(TrustPolicy::RequireOwned));
76    assert_eq!(signed_public.node_label, Some("lab-a".to_string()));
77
78    let signed_bootstrap_args =
79        normalize_runtime_surface_args(["mesh-llm", "serve", "--join", "signed-bootstrap-token"]);
80    let signed_bootstrap = Cli::parse_from(signed_bootstrap_args.normalized.clone());
81    assert!(signed_bootstrap.command.is_none());
82    assert_eq!(
83        signed_bootstrap.join,
84        vec!["signed-bootstrap-token".to_string()]
85    );
86
87    let runtime_bootstrap = Cli::parse_from(["mesh-llm", "runtime", "bootstrap", "--port", "3131"]);
88    match runtime_bootstrap.command.expect("runtime command expected") {
89        Command::Runtime {
90            command: Some(RuntimeCommand::Bootstrap { port, json }),
91        } => {
92            assert_eq!(port, 3131);
93            assert!(!json);
94        }
95        other => panic!("unexpected command: {other:?}"),
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::{Cli, Command};
102    use crate::models::ModelsCommand;
103    use clap::Parser;
104
105    #[test]
106    fn mesh_requirements_docs_examples_parse() {
107        super::assert_mesh_requirements_docs_examples_parse();
108    }
109
110    #[test]
111    fn split_topology_lock_requires_split_mode() {
112        let error = Cli::try_parse_from([
113            "mesh-llm",
114            "--model",
115            "model.gguf",
116            "--split-topology-lock",
117            "topology.json",
118        ])
119        .expect_err("topology lock without --split should fail");
120
121        assert!(error.to_string().contains("--split"));
122    }
123
124    #[test]
125    fn split_topology_lock_parses_with_split_mode() {
126        let cli = Cli::try_parse_from([
127            "mesh-llm",
128            "--model",
129            "model.gguf",
130            "--split",
131            "--split-topology-lock",
132            "topology.json",
133        ])
134        .expect("locked split CLI should parse");
135
136        assert!(cli.split);
137        assert_eq!(
138            cli.split_topology_lock,
139            Some(std::path::PathBuf::from("topology.json"))
140        );
141    }
142
143    #[test]
144    fn models_package_parses_experimental_publication() {
145        let cli = Cli::parse_from([
146            "mesh-llm",
147            "models",
148            "package",
149            "unsloth/inkling-GGUF:UD-Q2_K_XL",
150            "--experimental",
151            "--dry-run",
152        ]);
153
154        match cli.command.expect("models command expected") {
155            Command::Models {
156                command:
157                    ModelsCommand::Package {
158                        source_repo: Some(source_repo),
159                        experimental: true,
160                        dry_run: true,
161                        ..
162                    },
163            } => assert_eq!(source_repo, "unsloth/inkling-GGUF:UD-Q2_K_XL"),
164            other => panic!("unexpected command: {other:?}"),
165        }
166    }
167}