138 lines
4.4 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
import argparse
import os
import subprocess
import sys
import textwrap
import threading
import signal
from cli.sounds import Sound
def format_command_help(name, description, indent=2, col_width=36, width=80):
prefix = " " * indent + f"{name:<{col_width - indent}}"
wrapper = textwrap.TextWrapper(
width=width,
initial_indent=prefix,
subsequent_indent=" " * col_width
)
return wrapper.fill(description)
def list_cli_commands(cli_dir):
return sorted(
os.path.splitext(f.name)[0] for f in os.scandir(cli_dir)
if f.is_file() and f.name.endswith(".py") and not f.name.startswith("__")
)
def extract_description_via_help(cli_script_path):
try:
result = subprocess.run(
[sys.executable, cli_script_path, "--help"],
capture_output=True,
text=True,
check=True
)
lines = result.stdout.splitlines()
for i, line in enumerate(lines):
if line.strip().startswith("usage:"):
continue
if line.strip() == "":
for j in range(i+1, len(lines)):
desc = lines[j].strip()
if desc:
return desc
return "-"
except Exception:
return "-"
def play_start_intro():
Sound.play_start_sound()
Sound.play_cymais_intro_sound()
def failure_with_warning_loop():
Sound.play_finished_failed_sound()
print("Warning: command failed. Press Ctrl+C to stop sound warnings.")
try:
while True:
Sound.play_warning_sound()
except KeyboardInterrupt:
print("Warnings stopped by user.")
if __name__ == "__main__":
# Parse --no-sound early and remove from args
no_sound = False
if '--no-sound' in sys.argv:
no_sound = True
sys.argv.remove('--no-sound')
# Setup segfault handler to catch crashes
def segv_handler(signum, frame):
if not no_sound:
Sound.play_finished_failed_sound()
# Loop warning until interrupted
try:
while True:
Sound.play_warning_sound()
except KeyboardInterrupt:
pass
print("Segmentation fault detected. Exiting.")
sys.exit(1)
signal.signal(signal.SIGSEGV, segv_handler)
# Play intro sounds
if not no_sound:
threading.Thread(target=play_start_intro, daemon=True).start()
# Change to script directory
script_dir = os.path.dirname(os.path.realpath(__file__))
cli_dir = os.path.join(script_dir, "cli")
os.chdir(script_dir)
available_cli_commands = list_cli_commands(cli_dir)
# Handle help invocation
if len(sys.argv) == 1 or sys.argv[1] in ('-h', '--help'):
print("CyMaIS CLI proxy to tools in ./cli/")
print("Usage: cymais [--no-sound] <command> [options]")
print("Options:")
print(" --no-sound Suppress all sounds during execution")
print(" -h, --help Show this help message and exit")
print("Available commands:")
for cmd in available_cli_commands:
path = os.path.join(cli_dir, f"{cmd}.py")
desc = extract_description_via_help(path)
print(format_command_help(cmd, desc))
sys.exit(0)
# Special-case per-command help
if len(sys.argv) >= 3 and sys.argv[1] in available_cli_commands and sys.argv[2] in ('-h', '--help'):
subprocess.run([sys.executable, os.path.join(cli_dir, f"{sys.argv[1]}.py"), "--help"])
sys.exit(0)
# Execute chosen command
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('cli_command', choices=available_cli_commands)
parser.add_argument('cli_args', nargs=argparse.REMAINDER)
args = parser.parse_args()
cmd_path = os.path.join(cli_dir, f"{args.cli_command}.py")
full_cmd = [sys.executable, cmd_path] + args.cli_args
try:
proc = subprocess.Popen(full_cmd)
proc.wait()
rc = proc.returncode
if rc != 0:
print(f"Command '{args.cli_command}' failed with exit code {rc}.")
failure_with_warning_loop()
sys.exit(rc)
else:
if not no_sound:
Sound.play_finished_successfully_sound()
sys.exit(0)
except Exception as e:
print(f"Exception running command: {e}")
failure_with_warning_loop()
sys.exit(1)