Skip to main content

vta_cli_common/commands/
config.rs

1use std::collections::HashMap;
2
3use vta_sdk::prelude::*;
4use vta_sdk::protocols::vta_management::update_config::UpdateConfigBody;
5
6/// Print the configuration registry as canonical `config/show/0.1` returns it.
7///
8/// Boot-stable keys are marked, so an operator can see before patching that a
9/// change will not take effect until a restart.
10pub async fn cmd_config_get(
11    client: &VtaClient,
12    label_prefix: &str,
13) -> Result<(), Box<dyn std::error::Error>> {
14    let resp = client.get_config().await?;
15    for field in &resp.config.fields {
16        let value = match &field.value {
17            serde_json::Value::Null => "(not set)".to_string(),
18            serde_json::Value::String(s) => s.clone(),
19            other => other.to_string(),
20        };
21        let restart = if field.requires_restart {
22            "  (requires restart)"
23        } else {
24            ""
25        };
26        println!(
27            "{label_prefix}{:<12} {value}  [{}]{restart}",
28            format!("{}:", field.key),
29            field.source
30        );
31    }
32    Ok(())
33}
34
35/// Patch configuration keys.
36///
37/// `vta_did` is deliberately **not** a parameter: the VTA's own identity is
38/// set at setup and is immutable at runtime, so there is no flag to attempt
39/// it with. A caller that names it anyway (over the wire) is answered with a
40/// rejection, which this command prints — the operator learns the rule rather
41/// than silently re-pointing the agent's identity, which is what the
42/// pre-canonical surface did.
43pub async fn cmd_config_update(
44    client: &VtaClient,
45    label_prefix: &str,
46    vta_name: Option<String>,
47    public_url: Option<String>,
48) -> Result<(), Box<dyn std::error::Error>> {
49    let mut overrides = HashMap::new();
50    if let Some(v) = vta_name {
51        overrides.insert("vta_name".to_string(), serde_json::Value::String(v));
52    }
53    if let Some(v) = public_url {
54        overrides.insert("public_url".to_string(), serde_json::Value::String(v));
55    }
56    if overrides.is_empty() {
57        println!("Nothing to update — pass at least one of --vta-name or --public-url.");
58        return Ok(());
59    }
60
61    let resp = client
62        .update_config(UpdateConfigRequest {
63            patch: UpdateConfigBody { overrides },
64        })
65        .await?;
66
67    if !resp.applied.is_empty() {
68        println!(
69            "{label_prefix}Applied:          {}",
70            resp.applied.join(", ")
71        );
72    }
73    if !resp.pending_restart.is_empty() {
74        println!(
75            "{label_prefix}Pending restart:  {}",
76            resp.pending_restart.join(", ")
77        );
78        println!("{label_prefix}  Stored, but not in effect until the VTA restarts.");
79    }
80    for rejected in &resp.rejected {
81        println!(
82            "{label_prefix}Rejected {}: {}",
83            rejected.key, rejected.reason
84        );
85    }
86    Ok(())
87}