--debug for CLI commands (#454)
``` (base) ➜ arcade-ai git:(debug) ✗ uv run arcade busted-command --host invalid-host ❌ Login failed with error: Not implemented ^C Aborted. (base) ➜ arcade-ai git:(debug) ✗ uv run arcade busted-command --host invalid-host --debug ❌ Login failed with error: Traceback (most recent call last): File "/Users/evan/workspace/arcade/Team/arcade-ai/libs/arcade-cli/arcade_cli/main.py", line 106, in login raise Exception("Not implemented") Exception: Not implemented ```
This commit is contained in:
parent
836c890840
commit
6889d8673b
1 changed files with 65 additions and 42 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
import threading
|
import threading
|
||||||
|
import traceback
|
||||||
import uuid
|
import uuid
|
||||||
import webbrowser
|
import webbrowser
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
@ -69,6 +70,21 @@ cli.add_typer(
|
||||||
console = Console()
|
console = Console()
|
||||||
|
|
||||||
|
|
||||||
|
def handle_cli_error(
|
||||||
|
message: str, error: Exception | None = None, debug: bool = True, should_exit: bool = True
|
||||||
|
) -> None:
|
||||||
|
"""Handle CLI error reporting with optional debug traceback and exit."""
|
||||||
|
if error and debug:
|
||||||
|
console.print(f"❌ {message}: {traceback.format_exc()}", style="bold red")
|
||||||
|
elif error:
|
||||||
|
console.print(f"❌ {message}: {escape(str(error))}", style="bold red")
|
||||||
|
else:
|
||||||
|
console.print(f"❌ {message}", style="bold red")
|
||||||
|
|
||||||
|
if should_exit:
|
||||||
|
raise typer.Exit(code=1)
|
||||||
|
|
||||||
|
|
||||||
@cli.command(help="Log in to Arcade Cloud", rich_help_panel="User")
|
@cli.command(help="Log in to Arcade Cloud", rich_help_panel="User")
|
||||||
def login(
|
def login(
|
||||||
host: str = typer.Option(
|
host: str = typer.Option(
|
||||||
|
|
@ -83,6 +99,7 @@ def login(
|
||||||
"--port",
|
"--port",
|
||||||
help="The port of the Arcade Cloud host (if running locally).",
|
help="The port of the Arcade Cloud host (if running locally).",
|
||||||
),
|
),
|
||||||
|
debug: bool = typer.Option(False, "--debug", "-d", help="Show debug information"),
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Logs the user into Arcade Cloud.
|
Logs the user into Arcade Cloud.
|
||||||
|
|
@ -115,22 +132,29 @@ def login(
|
||||||
server_thread.join()
|
server_thread.join()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
auth_server.shutdown_server()
|
auth_server.shutdown_server()
|
||||||
|
except Exception as e:
|
||||||
|
handle_cli_error("Login failed", e, debug)
|
||||||
finally:
|
finally:
|
||||||
if server_thread.is_alive():
|
if server_thread.is_alive():
|
||||||
server_thread.join() # Ensure the server thread completes and cleans up
|
server_thread.join() # Ensure the server thread completes and cleans up
|
||||||
|
|
||||||
|
|
||||||
@cli.command(help="Log out of Arcade Cloud", rich_help_panel="User")
|
@cli.command(help="Log out of Arcade Cloud", rich_help_panel="User")
|
||||||
def logout() -> None:
|
def logout(
|
||||||
|
debug: bool = typer.Option(False, "--debug", "-d", help="Show debug information"),
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Logs the user out of Arcade Cloud.
|
Logs the user out of Arcade Cloud.
|
||||||
"""
|
"""
|
||||||
# If the credentials file exists, delete it
|
try:
|
||||||
if os.path.exists(CREDENTIALS_FILE_PATH):
|
# If the credentials file exists, delete it
|
||||||
os.remove(CREDENTIALS_FILE_PATH)
|
if os.path.exists(CREDENTIALS_FILE_PATH):
|
||||||
console.print("You're now logged out.", style="bold")
|
os.remove(CREDENTIALS_FILE_PATH)
|
||||||
else:
|
console.print("You're now logged out.", style="bold")
|
||||||
console.print("You're not logged in.", style="bold red")
|
else:
|
||||||
|
console.print("You're not logged in.", style="bold red")
|
||||||
|
except Exception as e:
|
||||||
|
handle_cli_error("Logout failed", e, debug)
|
||||||
|
|
||||||
|
|
||||||
@cli.command(
|
@cli.command(
|
||||||
|
|
@ -143,6 +167,7 @@ def new(
|
||||||
metavar="TOOLKIT_NAME",
|
metavar="TOOLKIT_NAME",
|
||||||
),
|
),
|
||||||
directory: str = typer.Option(os.getcwd(), "--dir", help="tools directory path"),
|
directory: str = typer.Option(os.getcwd(), "--dir", help="tools directory path"),
|
||||||
|
debug: bool = typer.Option(False, "--debug", "-d", help="Show debug information"),
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Creates a new toolkit with the given name, description, and result type.
|
Creates a new toolkit with the given name, description, and result type.
|
||||||
|
|
@ -152,8 +177,7 @@ def new(
|
||||||
try:
|
try:
|
||||||
create_new_toolkit(directory, toolkit_name)
|
create_new_toolkit(directory, toolkit_name)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_message = f"❌ Failed to create new Toolkit: {escape(str(e))}"
|
handle_cli_error("Failed to create new Toolkit", e, debug)
|
||||||
console.print(error_message, style="bold red")
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command(
|
@cli.command(
|
||||||
|
|
@ -318,7 +342,7 @@ def chat(
|
||||||
tool_messages = chat_result.tool_messages
|
tool_messages = chat_result.tool_messages
|
||||||
|
|
||||||
except OpenAIError as e:
|
except OpenAIError as e:
|
||||||
console.print(f"❌ Arcade Chat failed with error: {e!s}", style="bold red")
|
handle_cli_error("Arcade Chat failed", e, debug, should_exit=False)
|
||||||
continue
|
continue
|
||||||
if debug:
|
if debug:
|
||||||
display_tool_messages(tool_messages)
|
display_tool_messages(tool_messages)
|
||||||
|
|
@ -328,9 +352,7 @@ def chat(
|
||||||
typer.Exit()
|
typer.Exit()
|
||||||
|
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
error_message = f"❌ Failed to run tool{': ' + escape(str(e)) if str(e) else ''}"
|
handle_cli_error("Failed to run tool", e, debug)
|
||||||
console.print(error_message, style="bold red")
|
|
||||||
raise typer.Exit()
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command(help="Run tool calling evaluations", rich_help_panel="Tool Development")
|
@cli.command(help="Run tool calling evaluations", rich_help_panel="Tool Development")
|
||||||
|
|
@ -376,6 +398,7 @@ def evals(
|
||||||
"--no-tls",
|
"--no-tls",
|
||||||
help="Whether to disable TLS for the connection to the Arcade Engine.",
|
help="Whether to disable TLS for the connection to the Arcade Engine.",
|
||||||
),
|
),
|
||||||
|
debug: bool = typer.Option(False, "--debug", help="Show debug information"),
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Find all files starting with 'eval_' in the given directory,
|
Find all files starting with 'eval_' in the given directory,
|
||||||
|
|
@ -463,7 +486,10 @@ def evals(
|
||||||
all_evaluations.extend(results)
|
all_evaluations.extend(results)
|
||||||
display_eval_results(all_evaluations, show_details=show_details)
|
display_eval_results(all_evaluations, show_details=show_details)
|
||||||
|
|
||||||
asyncio.run(run_evaluations())
|
try:
|
||||||
|
asyncio.run(run_evaluations())
|
||||||
|
except Exception as e:
|
||||||
|
handle_cli_error("Failed to run evaluations", e, debug)
|
||||||
|
|
||||||
|
|
||||||
@cli.command(
|
@cli.command(
|
||||||
|
|
@ -523,9 +549,7 @@ def serve(
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
typer.Exit()
|
typer.Exit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_message = f"❌ Failed to start Arcade Worker: {escape(str(e))}"
|
handle_cli_error("Failed to start Arcade Worker", e, debug)
|
||||||
console.print(error_message, style="bold red")
|
|
||||||
typer.Exit(code=1)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command(
|
@cli.command(
|
||||||
|
|
@ -574,9 +598,7 @@ def workerup(
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
typer.Exit()
|
typer.Exit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_message = f"❌ Failed to start Arcade Toolkit Server: {escape(str(e))}"
|
handle_cli_error("Failed to start Arcade Toolkit Server", e, debug)
|
||||||
console.print(error_message, style="bold red")
|
|
||||||
typer.Exit(code=1)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command(help="Deploy toolkits to Arcade Cloud", rich_help_panel="Deployment")
|
@cli.command(help="Deploy toolkits to Arcade Cloud", rich_help_panel="Deployment")
|
||||||
|
|
@ -620,6 +642,7 @@ def deploy(
|
||||||
"--no-tls",
|
"--no-tls",
|
||||||
help="Whether to disable TLS for the connection to the Arcade Engine.",
|
help="Whether to disable TLS for the connection to the Arcade Engine.",
|
||||||
),
|
),
|
||||||
|
debug: bool = typer.Option(False, "--debug", help="Show debug information"),
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Deploy a worker to Arcade Cloud.
|
Deploy a worker to Arcade Cloud.
|
||||||
|
|
@ -637,8 +660,7 @@ def deploy(
|
||||||
try:
|
try:
|
||||||
deployment = Deployment.from_toml(Path(deployment_file))
|
deployment = Deployment.from_toml(Path(deployment_file))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.print(f"❌ Failed to parse deployment file: {e}", style="bold red")
|
handle_cli_error("Failed to parse deployment file", e, debug)
|
||||||
raise typer.Exit(code=1)
|
|
||||||
|
|
||||||
with console.status(f"Deploying {len(deployment.worker)} workers"):
|
with console.status(f"Deploying {len(deployment.worker)} workers"):
|
||||||
for worker in deployment.worker:
|
for worker in deployment.worker:
|
||||||
|
|
@ -648,10 +670,7 @@ def deploy(
|
||||||
worker.request().execute(cloud_client, engine_client)
|
worker.request().execute(cloud_client, engine_client)
|
||||||
console.log(f"✅ Worker '{worker.config.id}' deployed successfully.", style="dim")
|
console.log(f"✅ Worker '{worker.config.id}' deployed successfully.", style="dim")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
console.log(
|
handle_cli_error(f"Failed to deploy worker '{worker.config.id}'", e, debug)
|
||||||
f"❌ Failed to deploy worker '{worker.config.id}': {e}", style="bold red"
|
|
||||||
)
|
|
||||||
raise typer.Exit(code=1)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command(help="Open the Arcade Dashboard in a web browser", rich_help_panel="User")
|
@cli.command(help="Open the Arcade Dashboard in a web browser", rich_help_panel="User")
|
||||||
|
|
@ -684,30 +703,34 @@ def dashboard(
|
||||||
"--no-tls",
|
"--no-tls",
|
||||||
help="Whether to disable TLS for the connection to the Arcade Engine.",
|
help="Whether to disable TLS for the connection to the Arcade Engine.",
|
||||||
),
|
),
|
||||||
|
debug: bool = typer.Option(False, "--debug", "-d", help="Show debug information"),
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Opens the Arcade Dashboard in a web browser.
|
"""Opens the Arcade Dashboard in a web browser.
|
||||||
|
|
||||||
The Dashboard is a web-based Arcade user interface that is served by the Arcade Engine.
|
The Dashboard is a web-based Arcade user interface that is served by the Arcade Engine.
|
||||||
"""
|
"""
|
||||||
if local:
|
try:
|
||||||
host = "localhost"
|
if local:
|
||||||
|
host = "localhost"
|
||||||
|
|
||||||
# Construct base URL (for both health check and dashboard)
|
# Construct base URL (for both health check and dashboard)
|
||||||
base_url = compute_base_url(force_tls, force_no_tls, host, port)
|
base_url = compute_base_url(force_tls, force_no_tls, host, port)
|
||||||
dashboard_url = f"{base_url}/dashboard"
|
dashboard_url = f"{base_url}/dashboard"
|
||||||
|
|
||||||
# Try to hit /health endpoint on engine and warn if it is down
|
# Try to hit /health endpoint on engine and warn if it is down
|
||||||
config = validate_and_get_config()
|
config = validate_and_get_config()
|
||||||
with Arcade(api_key=config.api.key, base_url=base_url) as client:
|
with Arcade(api_key=config.api.key, base_url=base_url) as client:
|
||||||
log_engine_health(client)
|
log_engine_health(client)
|
||||||
|
|
||||||
# Open the dashboard in a browser
|
# Open the dashboard in a browser
|
||||||
console.print(f"Opening Arcade Dashboard at {dashboard_url}")
|
console.print(f"Opening Arcade Dashboard at {dashboard_url}")
|
||||||
if not webbrowser.open(dashboard_url):
|
if not webbrowser.open(dashboard_url):
|
||||||
console.print(
|
console.print(
|
||||||
f"If a browser doesn't open automatically, copy this URL and paste it into your browser: {dashboard_url}",
|
f"If a browser doesn't open automatically, copy this URL and paste it into your browser: {dashboard_url}",
|
||||||
style="dim",
|
style="dim",
|
||||||
)
|
)
|
||||||
|
except Exception as e:
|
||||||
|
handle_cli_error("Failed to open dashboard", e, debug)
|
||||||
|
|
||||||
|
|
||||||
@cli.callback()
|
@cli.callback()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue