cgcardona / muse public
open_cmd.py python
66 lines 2.1 KB
12901c5a Initial extraction from tellurstori/maestro cgcardona <gabriel@tellurstori.com> 4d ago
1 """muse open — open a Muse artifact with the macOS system default application.
2
3 Dispatches to ``open`` (macOS ``NSWorkspace``), which opens the file in
4 whatever the system default app is for that file type:
5
6 - ``.mid`` → Stori DAW or GarageBand
7 - ``.webp`` / ``.png`` → Preview
8 - ``.mp3`` → QuickTime
9
10 macOS-only. Exits 1 with a clear error on other platforms.
11 """
12 from __future__ import annotations
13
14 import asyncio
15 import logging
16 import pathlib
17 import platform
18 import subprocess
19
20 import typer
21
22 from maestro.muse_cli._repo import require_repo
23 from maestro.muse_cli.artifact_resolver import resolve_artifact_async
24 from maestro.muse_cli.db import open_session
25 from maestro.muse_cli.errors import ExitCode
26
27 logger = logging.getLogger(__name__)
28
29 app = typer.Typer()
30
31
32 def _guard_macos() -> None:
33 """Exit 1 with a clear message if not running on macOS."""
34 if platform.system() != "Darwin":
35 typer.echo("❌ muse open requires macOS.")
36 raise typer.Exit(code=ExitCode.USER_ERROR)
37
38
39 @app.callback(invoke_without_command=True)
40 def open_artifact(
41 ctx: typer.Context,
42 path_or_id: str = typer.Argument(..., help="File path or short commit ID."),
43 ) -> None:
44 """Open an artifact with the macOS system default application."""
45 _guard_macos()
46 root = require_repo()
47
48 async def _run() -> None:
49 async with open_session() as session:
50 path = await resolve_artifact_async(path_or_id, root=root, session=session)
51 subprocess.run(["open", str(path)], check=True)
52 typer.echo(f"✅ Opened {path}")
53 logger.info("✅ muse open: %s", path)
54
55 try:
56 asyncio.run(_run())
57 except typer.Exit:
58 raise
59 except subprocess.CalledProcessError as exc:
60 typer.echo(f"❌ muse open failed: {exc}")
61 logger.error("❌ muse open subprocess error: %s", exc)
62 raise typer.Exit(code=ExitCode.INTERNAL_ERROR)
63 except Exception as exc:
64 typer.echo(f"❌ muse open failed: {exc}")
65 logger.error("❌ muse open error: %s", exc, exc_info=True)
66 raise typer.Exit(code=ExitCode.INTERNAL_ERROR)