# artifact-export - Render artifacts to binary formats # Usage: cat diagram.puml | artifact-export --format plantuml --to /tmp/diagram.svg # Usage: cat model.scad | artifact-export --format openscad --to /tmp/model.png name: artifact-export description: Render artifact source code to binary formats (PNG, SVG, PDF) category: Artifact arguments: - flag: --format variable: format required: true description: "Source format: plantuml, mermaid, openscad, svg, excalidraw, code" - flag: --to variable: output_path required: true description: Output file path (extension determines output format, e.g. .png, .svg, .pdf) steps: - type: code output_var: result code: | import subprocess import json import os from pathlib import Path source_code = input.strip() output = Path(output_path) output_ext = output.suffix.lower() # Ensure output directory exists output.parent.mkdir(parents=True, exist_ok=True) success = False message = "" if format == 'plantuml': # Write to temp file import tempfile with tempfile.NamedTemporaryFile(mode='w', suffix='.puml', delete=False) as f: f.write(source_code) temp_path = f.name format_flag = {'.svg': '-tsvg', '.png': '-tpng', '.eps': '-teps', '.pdf': '-tpdf'}.get(output_ext, '-tsvg') try: result = subprocess.run( ['plantuml', format_flag, '-o', str(output.parent), temp_path], capture_output=True, text=True, timeout=30 ) if result.returncode == 0: # PlantUML outputs to same name with different extension temp_base = Path(temp_path) generated = output.parent / (temp_base.stem + output_ext) if generated.exists(): import shutil shutil.move(str(generated), str(output)) success = True message = str(output) else: message = f"Generated file not found: {generated}" else: message = f"PlantUML error: {result.stderr}" finally: os.unlink(temp_path) elif format == 'mermaid': import tempfile with tempfile.NamedTemporaryFile(mode='w', suffix='.mmd', delete=False) as f: f.write(source_code) temp_path = f.name try: result = subprocess.run( ['mmdc', '-i', temp_path, '-o', str(output)], capture_output=True, text=True, timeout=30 ) if result.returncode == 0 and output.exists(): success = True message = str(output) else: message = f"Mermaid error: {result.stderr}" finally: os.unlink(temp_path) elif format == 'openscad': import tempfile with tempfile.NamedTemporaryFile(mode='w', suffix='.scad', delete=False) as f: f.write(source_code) temp_path = f.name try: result = subprocess.run( ['openscad', '-o', str(output), temp_path], capture_output=True, text=True, timeout=60 ) if result.returncode == 0 and output.exists(): success = True message = str(output) else: message = f"OpenSCAD error: {result.stderr}" finally: os.unlink(temp_path) elif format == 'svg': if output_ext == '.svg': output.write_text(source_code) success = True message = str(output) elif output_ext == '.png': import tempfile with tempfile.NamedTemporaryFile(mode='w', suffix='.svg', delete=False) as f: f.write(source_code) temp_path = f.name try: for cmd in [ ['inkscape', temp_path, '-o', str(output)], ['rsvg-convert', temp_path, '-o', str(output)], ]: try: result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) if result.returncode == 0 and output.exists(): success = True message = str(output) break except FileNotFoundError: continue if not success: message = "SVG to PNG requires inkscape or rsvg-convert" finally: os.unlink(temp_path) else: message = f"Unsupported output format: {output_ext}" elif format == 'code': output.write_text(source_code) success = True message = str(output) elif format == 'excalidraw': if output_ext == '.json': output.write_text(source_code) success = True message = str(output) else: message = "Excalidraw export currently only supports .json output" else: message = f"Unknown format: {format}" print(json.dumps({'success': success, 'path' if success else 'error': message}))