- `pic kv ls --group` / `pic kv get --group` (mutually exclusive with --app) hit the M4 admin API; client group_kv_list/group_kv_get. - collections journey: a script writes a shared KV value, the operator lists + fetches it via the admin API with no script. - docs: §11.6 + CLAUDE.md record M3 quotas + M4 read-only admin API. Completes M4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
63 lines
2.0 KiB
Rust
63 lines
2.0 KiB
Rust
//! `pic kv ls | get` — read-only KV inspection (G2).
|
|
//!
|
|
//! Wraps the read-only `/api/v1/admin/apps/{id}/kv*` surface. There is no
|
|
//! `set`/`rm` on purpose: KV writes go through `kv::set` in scripts (which
|
|
//! emit the change events triggers depend on); an admin write would bypass
|
|
//! that, so the CLI stays read-only (matching the queues precedent).
|
|
|
|
use std::io::Write;
|
|
|
|
use anyhow::{bail, Result};
|
|
|
|
use crate::client::Client;
|
|
use crate::config;
|
|
use crate::output::{OutputMode, Table};
|
|
|
|
pub async fn ls(
|
|
app: Option<&str>,
|
|
group: Option<&str>,
|
|
collection: &str,
|
|
limit: u32,
|
|
mode: OutputMode,
|
|
) -> Result<()> {
|
|
let creds = config::resolve()?;
|
|
let client = Client::from_creds(&creds)?;
|
|
let page = match (app, group) {
|
|
(Some(a), None) => client.kv_list(a, collection, limit).await?,
|
|
// §11.6 M4: a group's shared-KV collection.
|
|
(None, Some(g)) => client.group_kv_list(g, collection, limit).await?,
|
|
_ => bail!("provide exactly one of --app or --group"),
|
|
};
|
|
let mut table = Table::new(["key"]);
|
|
for k in page.keys {
|
|
table.row([k]);
|
|
}
|
|
table.print(mode);
|
|
if page.next_cursor.is_some() {
|
|
let _ = writeln!(
|
|
std::io::stderr(),
|
|
"(more keys available — raise --limit to see them)"
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn get(
|
|
app: Option<&str>,
|
|
group: Option<&str>,
|
|
collection: &str,
|
|
key: &str,
|
|
) -> Result<()> {
|
|
let creds = config::resolve()?;
|
|
let client = Client::from_creds(&creds)?;
|
|
let value = match (app, group) {
|
|
(Some(a), None) => client.kv_get(a, collection, key).await?,
|
|
(None, Some(g)) => client.group_kv_get(g, collection, key).await?,
|
|
_ => bail!("provide exactly one of --app or --group"),
|
|
};
|
|
// Always emit the JSON value (pretty) so it pipes cleanly into jq.
|
|
let pretty = serde_json::to_string_pretty(&value).unwrap_or_else(|_| value.to_string());
|
|
println!("{pretty}");
|
|
Ok(())
|
|
}
|