feat(group-blobs): pic kv ls/get --group + journey + docs (M4.3+M4.4)

- `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>
This commit is contained in:
MechaCat02
2026-07-01 21:26:12 +02:00
parent c21e82fb9f
commit 24d834a102
5 changed files with 162 additions and 12 deletions

View File

@@ -7,16 +7,27 @@
use std::io::Write;
use anyhow::Result;
use anyhow::{bail, Result};
use crate::client::Client;
use crate::config;
use crate::output::{OutputMode, Table};
pub async fn ls(app: &str, collection: &str, limit: u32, mode: OutputMode) -> Result<()> {
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 = client.kv_list(app, collection, limit).await?;
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]);
@@ -31,10 +42,19 @@ pub async fn ls(app: &str, collection: &str, limit: u32, mode: OutputMode) -> Re
Ok(())
}
pub async fn get(app: &str, collection: &str, key: &str) -> Result<()> {
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 = client.kv_get(app, collection, key).await?;
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}");