systemprompt_cli/commands/web/assets/
show.rs1use anyhow::{Context, Result, anyhow};
7use chrono::{DateTime, Utc};
8use clap::Args;
9use std::fs;
10
11use crate::CliConfig;
12use crate::shared::CommandOutput;
13use systemprompt_config::ProfileBootstrap;
14
15use super::super::paths::WebPaths;
16use super::super::types::AssetDetailOutput;
17use super::asset_type::determine_asset_type;
18
19#[derive(Debug, Args)]
20pub struct ShowArgs {
21 #[arg(help = "Asset path (relative to assets directory)")]
22 pub path: String,
23}
24
25pub(super) fn execute(args: &ShowArgs, _config: &CliConfig) -> Result<CommandOutput> {
26 let profile = ProfileBootstrap::get().context("Failed to get profile")?;
27 let web_paths = WebPaths::resolve()?;
28 let assets_dir = &web_paths.assets;
29 let asset_path = assets_dir.join(&args.path);
30
31 if !asset_path.exists() {
32 return Err(anyhow!("Asset '{}' not found", args.path));
33 }
34
35 if !asset_path.is_file() {
36 return Err(anyhow!("'{}' is not a file", args.path));
37 }
38
39 let metadata = asset_path
40 .metadata()
41 .context("Failed to get file metadata")?;
42 let size_bytes = metadata.len();
43 let modified = metadata.modified().ok().map_or_else(
44 || "unknown".to_owned(),
45 |t| {
46 let datetime: DateTime<Utc> = t.into();
47 datetime.format("%Y-%m-%dT%H:%M:%SZ").to_string()
48 },
49 );
50
51 let asset_type = determine_asset_type(&asset_path, &args.path);
52 let referenced_in = find_config_references(&args.path, profile);
53
54 let output = AssetDetailOutput {
55 path: args.path.clone(),
56 absolute_path: asset_path.to_string_lossy().to_string(),
57 asset_type,
58 size_bytes,
59 modified,
60 referenced_in,
61 };
62
63 Ok(CommandOutput::card_value(
64 format!("Asset: {}", args.path),
65 &output,
66 ))
67}
68
69pub fn find_config_references(
70 asset_path: &str,
71 profile: &systemprompt_models::Profile,
72) -> Vec<String> {
73 let mut references = Vec::new();
74
75 let web_config_path = profile.paths.web_config();
76 if let Ok(content) = fs::read_to_string(&web_config_path) {
77 let search_patterns = [
78 format!("/assets/{}", asset_path),
79 format!("assets/{}", asset_path),
80 asset_path.to_owned(),
81 ];
82
83 for pattern in &search_patterns {
84 if content.contains(pattern) {
85 references.push(format!("web config: {}", web_config_path));
86 break;
87 }
88 }
89 }
90
91 let metadata_path = profile.paths.web_metadata();
92 if let Ok(content) = fs::read_to_string(&metadata_path) {
93 let search_patterns = [
94 format!("/assets/{}", asset_path),
95 format!("assets/{}", asset_path),
96 asset_path.to_owned(),
97 ];
98
99 for pattern in &search_patterns {
100 if content.contains(pattern) {
101 references.push(format!("metadata: {}", metadata_path));
102 break;
103 }
104 }
105 }
106
107 references
108}