1use anyhow::{Context, Result, anyhow};
5use sha2::{Digest, Sha256};
6
7use crate::agent_pack::{DescribeMeta, agentic_worker_describe};
8
9pub fn publish_metadata(
13 pack_bytes: &[u8],
14 id: &str,
15 name: &str,
16 version: &str,
17 summary: &str,
18) -> Result<serde_json::Value> {
19 let sha = hex::encode(Sha256::digest(pack_bytes));
20 let describe = agentic_worker_describe(&DescribeMeta {
21 id: id.to_string(),
22 name: name.to_string(),
23 version: version.to_string(),
24 summary: summary.to_string(),
25 manifest_sha256: sha.clone(),
26 });
27 Ok(serde_json::json!({ "describe": describe, "artifactSha256": sha }))
28}
29
30pub async fn publish_agentic_worker(
35 store_url: &str,
36 token: &str,
37 metadata: &serde_json::Value,
38 pack_bytes: Vec<u8>,
39) -> Result<serde_json::Value> {
40 let url = format!("{}/api/v1/agentic-workers", store_url.trim_end_matches('/'));
41 let metadata_str = serde_json::to_string(metadata).context("encode metadata")?;
42 let part = reqwest::multipart::Part::bytes(pack_bytes)
43 .file_name("artifact.gtpack")
44 .mime_str("application/zip")?;
45 let form = reqwest::multipart::Form::new()
46 .text("metadata", metadata_str)
47 .part("artifact", part);
48 let resp = reqwest::Client::new()
49 .post(&url)
50 .bearer_auth(token)
51 .multipart(form)
52 .send()
53 .await
54 .context("POST agentic-workers")?;
55 let status = resp.status();
56 let body = resp.text().await.unwrap_or_default();
57 if status == reqwest::StatusCode::CONFLICT {
58 return Err(anyhow!("already published (409): {body}"));
59 }
60 if !status.is_success() {
61 return Err(anyhow!("store returned {}: {body}", status.as_u16()));
62 }
63 Ok(serde_json::from_str(&body).unwrap_or(serde_json::json!({ "raw": body })))
64}