feat: add client mode with streaming support
Add client mode enabling the keep CLI to connect to a remote keep server over HTTP. Local plugins (compression, meta, filters) run on the client; the server stores/retrieves binary blobs. Architecture: - Client save uses 3-thread streaming pipeline: reader thread (stdin → tee/stdout → hash → compress), OS pipe, streamer thread (pipe → chunked HTTP POST). Memory usage is O(PIPESIZE) regardless of data size. - Server accepts compress=false, meta=false, decompress=false query params for granular control of server-side processing. - Streaming body handling on server via async channel → sync reader bridge (ChannelReader). Key additions: - src/client.rs: KeepClient with post_stream() for chunked upload - src/modes/client/: save, get, list, info, delete, diff, status - --client-url / KEEP_CLIENT_URL configuration - --client-password / KEEP_CLIENT_PASSWORD for auth - os_pipe dependency for zero-copy pipe streaming Co-Authored-By: andrew/openrouter/hunter-alpha <noreply@opencode.ai>
This commit is contained in:
65
src/modes/client/info.rs
Normal file
65
src/modes/client/info.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use crate::client::KeepClient;
|
||||
use crate::modes::common::{OutputFormat, format_size, settings_output_format};
|
||||
use clap::Command;
|
||||
use log::debug;
|
||||
|
||||
pub fn mode(
|
||||
client: &KeepClient,
|
||||
_cmd: &mut Command,
|
||||
settings: &crate::config::Settings,
|
||||
ids: &[i64],
|
||||
tags: &[String],
|
||||
) -> Result<(), anyhow::Error> {
|
||||
debug!("CLIENT_INFO: Getting item info via remote server");
|
||||
|
||||
let output_format = settings_output_format(settings);
|
||||
|
||||
// If tags provided, find matching item first
|
||||
let item_ids: Vec<i64> = if !tags.is_empty() {
|
||||
let items = client.list_items(tags, "newest", 0, 1)?;
|
||||
if items.is_empty() {
|
||||
return Err(anyhow::anyhow!("No items found matching tags: {:?}", tags));
|
||||
}
|
||||
items.into_iter().map(|i| i.id).collect()
|
||||
} else {
|
||||
ids.to_vec()
|
||||
};
|
||||
|
||||
for &id in &item_ids {
|
||||
let item = client.get_item_info(id)?;
|
||||
|
||||
match output_format {
|
||||
OutputFormat::Json => {
|
||||
println!("{}", serde_json::to_string_pretty(&item)?);
|
||||
}
|
||||
OutputFormat::Yaml => {
|
||||
println!("{}", serde_yaml::to_string(&item)?);
|
||||
}
|
||||
OutputFormat::Table => {
|
||||
use comfy_table::{Table, presets::UTF8_FULL};
|
||||
|
||||
let mut table = Table::new();
|
||||
table.load_preset(UTF8_FULL);
|
||||
|
||||
let size_str = item
|
||||
.size
|
||||
.map(|s| format_size(s as u64, settings.human_readable))
|
||||
.unwrap_or_else(|| "N/A".to_string());
|
||||
|
||||
table.add_row(vec!["ID".to_string(), item.id.to_string()]);
|
||||
table.add_row(vec!["Time".to_string(), item.ts.clone()]);
|
||||
table.add_row(vec!["Size".to_string(), size_str]);
|
||||
table.add_row(vec!["Compression".to_string(), item.compression.clone()]);
|
||||
table.add_row(vec!["Tags".to_string(), item.tags.join(", ")]);
|
||||
|
||||
for (key, value) in &item.metadata {
|
||||
table.add_row(vec![format!("Meta: {}", key), value.clone()]);
|
||||
}
|
||||
|
||||
println!("{table}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user