11//! Session import command for Cortex CLI.
22//!
3- //! Imports a session from a portable JSON format (exported or shared).
3+ //! Imports a session from a portable JSON or YAML format (exported or shared).
44
55use anyhow:: { Context , Result , bail} ;
66use clap:: Parser ;
@@ -21,10 +21,10 @@ use crate::export_cmd::{ExportMessage, SessionExport};
2121/// Maximum depth for processing messages to prevent stack overflow from deeply nested structures.
2222const MAX_PROCESSING_DEPTH : usize = 10000 ;
2323
24- /// Import a session from JSON format.
24+ /// Import a session from JSON or YAML format.
2525#[ derive( Debug , Parser ) ]
2626pub struct ImportCommand {
27- /// Path to the JSON file to import, URL to fetch, or "-" for stdin
27+ /// Path to the JSON/YAML file to import, URL to fetch, or "-" for stdin
2828 #[ arg( value_name = "FILE_OR_URL" ) ]
2929 pub source : String ,
3030
@@ -50,7 +50,7 @@ impl ImportCommand {
5050 . ok_or_else ( || anyhow:: anyhow!( "Could not determine home directory" ) ) ?;
5151
5252 // Read the export data
53- let ( json_content , is_from_url) = if self . source == "-" {
53+ let ( export_content , is_from_url) = if self . source == "-" {
5454 // Read from stdin
5555 use std:: io:: Read ;
5656 let mut content = String :: new ( ) ;
@@ -70,41 +70,7 @@ impl ImportCommand {
7070 } ;
7171
7272 // Parse the export with helpful error messages
73- let export: SessionExport = serde_json:: from_str ( & json_content) . map_err ( |e| {
74- // Create a helpful error message with content preview
75- let preview_len = json_content. len ( ) . min ( 200 ) ;
76- let content_preview = & json_content[ ..preview_len] ;
77- let truncated = if json_content. len ( ) > 200 {
78- "..."
79- } else {
80- ""
81- } ;
82-
83- let source_type = if is_from_url { "URL" } else { "file" } ;
84-
85- // Detect common non-JSON content types
86- let hint = if content_preview. trim_start ( ) . starts_with ( "<!DOCTYPE" )
87- || content_preview. trim_start ( ) . starts_with ( "<html" )
88- {
89- "\n Hint: The URL returned HTML content, not JSON. Make sure the URL points directly to a JSON export file."
90- } else if content_preview. trim_start ( ) . starts_with ( "<?xml" ) {
91- "\n Hint: The URL returned XML content, not JSON. Make sure the URL points directly to a JSON export file."
92- } else if content_preview. is_empty ( ) {
93- "\n Hint: The response was empty. Make sure the URL is accessible and returns JSON content."
94- } else {
95- "\n Hint: Ensure the file contains valid JSON. Check for syntax errors like missing commas, unclosed brackets, or invalid characters."
96- } ;
97-
98- anyhow:: anyhow!(
99- "Failed to parse JSON from {}: {}\n \n Received content (first {} bytes):\n {}{}\n {}" ,
100- source_type,
101- e,
102- preview_len,
103- content_preview,
104- truncated,
105- hint
106- )
107- } ) ?;
73+ let export = parse_session_export ( & export_content, is_from_url) ?;
10874
10975 // Validate version
11076 if export. version != 1 {
@@ -236,6 +202,50 @@ impl ImportCommand {
236202 }
237203}
238204
205+ fn parse_session_export ( content : & str , is_from_url : bool ) -> Result < SessionExport > {
206+ match serde_json:: from_str ( content) {
207+ Ok ( export) => return Ok ( export) ,
208+ Err ( json_err) => {
209+ let yaml_err = match serde_yaml:: from_str ( content) {
210+ Ok ( export) => return Ok ( export) ,
211+ Err ( err) => err,
212+ } ;
213+
214+ let content_preview: String = content. chars ( ) . take ( 200 ) . collect ( ) ;
215+ let truncated = if content. chars ( ) . count ( ) > 200 {
216+ "..."
217+ } else {
218+ ""
219+ } ;
220+
221+ let source_type = if is_from_url { "URL" } else { "file" } ;
222+
223+ // Detect common non-export content types
224+ let hint = if content_preview. trim_start ( ) . starts_with ( "<!DOCTYPE" )
225+ || content_preview. trim_start ( ) . starts_with ( "<html" )
226+ {
227+ "\n Hint: The URL returned HTML content, not a JSON or YAML export file. Make sure the URL points directly to a session export file."
228+ } else if content_preview. trim_start ( ) . starts_with ( "<?xml" ) {
229+ "\n Hint: The URL returned XML content, not a JSON or YAML export file. Make sure the URL points directly to a session export file."
230+ } else if content_preview. is_empty ( ) {
231+ "\n Hint: The response was empty. Make sure the URL is accessible and returns a JSON or YAML export file."
232+ } else {
233+ "\n Hint: Ensure the file contains valid JSON or YAML exported by `cortex export`."
234+ } ;
235+
236+ bail ! (
237+ "Failed to parse session export from {} as JSON or YAML. JSON error: {}. YAML error: {}\n \n Received content (first 200 characters):\n {}{}\n {}" ,
238+ source_type,
239+ json_err,
240+ yaml_err,
241+ content_preview,
242+ truncated,
243+ hint
244+ )
245+ }
246+ }
247+ }
248+
239249/// Fetch content from a URL.
240250async fn fetch_url ( url : & str ) -> Result < String > {
241251 // Use curl for fetching
0 commit comments