feat(toolbox): ui update
This commit is contained in:
127
electron/bridge.py
Normal file
127
electron/bridge.py
Normal file
@@ -0,0 +1,127 @@
|
||||
#!/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: <command> <json_args>. 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()
|
||||
Reference in New Issue
Block a user