#!/usr/bin/env python3 """ Electron Bridge Script for 4DSTAR Bundle Management UPDATED ARCHITECTURE (2025-08-09): ===================================== This bridge script has been simplified to work with the refactored core functions that now return JSON directly. No more complex stdout mixing or data wrapping. Key Changes: - Core functions return JSON-serializable dictionaries directly - Progress messages go to stderr only (never mixed with JSON output) - Clean JSON output to stdout for Electron to parse - Simplified error handling with consistent JSON error format """ import sys import json import inspect import traceback from pathlib import Path import datetime # Custom JSON encoder to handle Path and datetime objects class FourdstEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, Path): return str(o) if isinstance(o, (datetime.datetime, datetime.date)): return o.isoformat() return super().default(o) # Add the project root to the Python path to allow importing 'fourdst' project_root = Path(__file__).resolve().parent.parent sys.path.insert(0, str(project_root)) from fourdst.core import bundle def main(): # Use stderr for all logging to avoid interfering with JSON output on stdout log_file = sys.stderr print("--- Python backend bridge started ---", file=log_file, flush=True) if len(sys.argv) < 3: print(f"FATAL: Not enough arguments provided. Got {len(sys.argv)}. Exiting.", file=log_file, flush=True) # Return JSON error even for argument errors error_response = { 'success': False, 'error': f'Invalid arguments. Expected: . Got {len(sys.argv)} args.' } print(json.dumps(error_response), flush=True) sys.exit(1) command = sys.argv[1] args_json = sys.argv[2] print(f"[BRIDGE_INFO] Received command: {command}", file=log_file, flush=True) print(f"[BRIDGE_INFO] Received raw args: {args_json}", file=log_file, flush=True) try: kwargs = json.loads(args_json) print(f"[BRIDGE_INFO] Parsed kwargs: {kwargs}", file=log_file, flush=True) # Convert path strings to Path objects where needed for key, value in kwargs.items(): if isinstance(value, str) and ('path' in key.lower() or 'key' in key.lower()): kwargs[key] = Path(value) elif isinstance(value, list) and 'dirs' in key.lower(): kwargs[key] = [Path(p) for p in value] func = getattr(bundle, command) # Create progress callback that sends structured progress to stderr # This keeps progress separate from the final JSON result on stdout def progress_callback(message): # Progress goes to stderr to avoid mixing with JSON output if isinstance(message, dict): # Structured progress message (e.g., from fill_bundle) progress_msg = f"[PROGRESS] {json.dumps(message)}" else: # Simple string message progress_msg = f"[PROGRESS] {message}" print(progress_msg, file=log_file, flush=True) # Inspect the function signature to see if it accepts 'progress_callback'. sig = inspect.signature(func) if 'progress_callback' in sig.parameters: kwargs['progress_callback'] = progress_callback print(f"[BRIDGE_INFO] Calling function `bundle.{command}`...", file=log_file, flush=True) result = func(**kwargs) print(f"[BRIDGE_INFO] Function returned successfully.", file=log_file, flush=True) # Core functions now return JSON-serializable dictionaries directly # No need for wrapping or complex data transformation if result is None: # Fallback for functions that might still return None result = { 'success': True, 'message': f'{command} completed successfully.' } # Send the result directly as JSON to stdout print("[BRIDGE_INFO] Sending JSON response to stdout.", file=log_file, flush=True) json_response = json.dumps(result, cls=FourdstEncoder) print(json_response, flush=True) print("--- Python backend bridge finished successfully ---", file=log_file, flush=True) except Exception as e: # Get the full traceback for detailed debugging tb_str = traceback.format_exc() # Print the traceback to stderr so it appears in the terminal print(f"[BRIDGE_ERROR] Exception occurred: {tb_str}", file=sys.stderr, flush=True) # Send consistent JSON error response to stdout error_response = { 'success': False, 'error': f'Bridge error in {command}: {str(e)}', 'traceback': tb_str # Include traceback for debugging } json_response = json.dumps(error_response, cls=FourdstEncoder) print(json_response, flush=True) print("--- Python backend bridge finished with error ---", file=sys.stderr, flush=True) sys.exit(1) if __name__ == "__main__": main()