refactor(cli/setup.py): shared _info() line printer; pack print_info runs across setup modules
This commit is contained in:
@@ -48,6 +48,12 @@ from hermes_cli.cli_output import print_error, print_info, print_success, print_
|
||||
from hermes_cli.secret_prompt import masked_secret_prompt # noqa: E402
|
||||
|
||||
|
||||
def _info(*lines: str | None) -> None:
|
||||
"""print_info each line in order; ``None`` emits a bare blank ``print()``."""
|
||||
for line in lines:
|
||||
print() if line is None else print_info(line)
|
||||
|
||||
|
||||
def _current_reasoning_effort(config: dict) -> str:
|
||||
agent_cfg = config.get("agent")
|
||||
if isinstance(agent_cfg, dict):
|
||||
@@ -81,16 +87,13 @@ def print_noninteractive_setup_guidance(reason: str | None = None) -> None:
|
||||
print()
|
||||
if reason:
|
||||
print_info(reason)
|
||||
print_info("The interactive wizard cannot be used here.")
|
||||
print()
|
||||
print_info("Configure Hermes using environment variables or config commands:")
|
||||
print_info(" hermes config set model.provider custom")
|
||||
print_info(" hermes config set model.base_url http://localhost:8080/v1")
|
||||
print_info(" hermes config set model.default your-model-name")
|
||||
print()
|
||||
print_info("Or set OPENROUTER_API_KEY / OPENAI_API_KEY in your environment.")
|
||||
print_info("Run 'hermes setup' in an interactive terminal to use the full wizard.")
|
||||
print()
|
||||
_info("The interactive wizard cannot be used here.", None,
|
||||
"Configure Hermes using environment variables or config commands:",
|
||||
" hermes config set model.provider custom",
|
||||
" hermes config set model.base_url http://localhost:8080/v1",
|
||||
" hermes config set model.default your-model-name", None,
|
||||
"Or set OPENROUTER_API_KEY / OPENAI_API_KEY in your environment.",
|
||||
"Run 'hermes setup' in an interactive terminal to use the full wizard.", None)
|
||||
|
||||
|
||||
_BRACKETED_PASTE_PATTERN = re.compile(r"\x1b\[\s*200~|\x1b\[\s*201~")
|
||||
@@ -473,11 +476,9 @@ def _apply_default_agent_settings(config: dict):
|
||||
|
||||
save_config(config)
|
||||
print_success("Applied recommended defaults:")
|
||||
print_info(" Max iterations: 150")
|
||||
print_info(" Tool progress: all")
|
||||
print_info(" Compression threshold: 0.50")
|
||||
print_info(" Session reset: never (use /reset or compression)")
|
||||
print_info(" Run `hermes setup agent` later to customize.")
|
||||
_info(" Max iterations: 150", " Tool progress: all", " Compression threshold: 0.50",
|
||||
" Session reset: never (use /reset or compression)",
|
||||
" Run `hermes setup agent` later to customize.")
|
||||
|
||||
|
||||
def _prompt_int_setting(section: dict, key: str, label: str, current, accept) -> None:
|
||||
@@ -786,10 +787,9 @@ def _run_full_setup(config: dict, hermes_home, *, is_existing: bool, migration_r
|
||||
print_info("You can edit these files directly or use 'hermes config edit'")
|
||||
|
||||
if migration_ran:
|
||||
print()
|
||||
print_info("Settings were imported from OpenClaw.")
|
||||
print_info("Each section below will show what was imported — press Enter to keep,")
|
||||
print_info("or choose to reconfigure if needed.")
|
||||
_info(None, "Settings were imported from OpenClaw.",
|
||||
"Each section below will show what was imported — press Enter to keep,",
|
||||
"or choose to reconfigure if needed.")
|
||||
|
||||
# Agent Settings are not prompted: first installs get defaults, existing keep theirs.
|
||||
if not is_existing:
|
||||
@@ -896,11 +896,10 @@ def _run_setup_wizard_impl(args):
|
||||
print()
|
||||
print_header("Reconfigure")
|
||||
print_success("You already have Hermes configured.")
|
||||
print_info("Running the full wizard — each prompt shows your current value.")
|
||||
print_info("Press Enter to keep it, or type a new value to change it.")
|
||||
print_info("")
|
||||
print_info("Tip: jump straight to a section with 'hermes setup model|terminal|")
|
||||
print_info(" gateway|tools|agent', or fill only missing items with --quick.")
|
||||
_info("Running the full wizard — each prompt shows your current value.",
|
||||
"Press Enter to keep it, or type a new value to change it.", "",
|
||||
"Tip: jump straight to a section with 'hermes setup model|terminal|",
|
||||
" gateway|tools|agent', or fill only missing items with --quick.")
|
||||
# --reconfigure is kept for backwards compatibility and is a no-op here.
|
||||
else:
|
||||
# ── First-Time Setup ── (--reconfigure / --quick are meaningless here; fall through)
|
||||
|
||||
@@ -15,13 +15,6 @@ def _is_valid_telegram_bot_token(token: str) -> bool:
|
||||
return bool(_TELEGRAM_BOT_TOKEN_RE.match(token))
|
||||
|
||||
|
||||
def _info(*lines: str | None) -> None:
|
||||
"""print_info each line in order; ``None`` emits a bare blank ``print()``."""
|
||||
from hermes_cli.setup import print_info
|
||||
for line in lines:
|
||||
print() if line is None else print_info(line)
|
||||
|
||||
|
||||
def _profile_name_from_hermes_home(hermes_home) -> str | None:
|
||||
"""Return the active profile name when HERMES_HOME is a profile dir."""
|
||||
if hermes_home.parent.name == "profiles":
|
||||
@@ -108,7 +101,7 @@ def _telegram_allowlist_nudge() -> None:
|
||||
|
||||
def _obtain_telegram_token():
|
||||
"""Return (token, setup_result); auto flow first when chosen, else manual paste."""
|
||||
from hermes_cli.setup import print_error, prompt
|
||||
from hermes_cli.setup import _info, print_error, prompt
|
||||
_info("How would you like to create your Telegram bot?", None,
|
||||
" [1] Automatic (recommended)",
|
||||
" Scan a QR code → confirm in Telegram → done.",
|
||||
@@ -139,7 +132,7 @@ def _obtain_telegram_token():
|
||||
def _setup_telegram():
|
||||
"""Configure Telegram bot credentials and allowlist."""
|
||||
from hermes_cli.setup import print_header, print_info, print_success, prompt, prompt_yes_no
|
||||
from hermes_cli.setup import save_env_value
|
||||
from hermes_cli.setup import _info, save_env_value
|
||||
print_header("Telegram")
|
||||
if _declines_reconfigure("TELEGRAM_BOT_TOKEN", "Telegram", "Reconfigure Telegram?"):
|
||||
_telegram_allowlist_nudge()
|
||||
@@ -205,7 +198,7 @@ def _setup_telegram():
|
||||
def _setup_bluebubbles():
|
||||
"""Configure BlueBubbles iMessage gateway."""
|
||||
from hermes_cli.setup import print_header, print_info, print_success, print_warning, prompt
|
||||
from hermes_cli.setup import prompt_yes_no, save_env_value
|
||||
from hermes_cli.setup import _info, prompt_yes_no, save_env_value
|
||||
print_header("BlueBubbles (iMessage)")
|
||||
if _declines_reconfigure("BLUEBUBBLES_SERVER_URL", "BlueBubbles", "Reconfigure BlueBubbles?"):
|
||||
return
|
||||
@@ -252,7 +245,7 @@ def _setup_bluebubbles():
|
||||
|
||||
def _setup_webhooks():
|
||||
"""Configure webhook integration."""
|
||||
from hermes_cli.setup import print_header, print_success, print_warning, prompt, save_env_value
|
||||
from hermes_cli.setup import _info, print_header, print_success, print_warning, prompt, save_env_value
|
||||
print_header("Webhooks")
|
||||
if _declines_reconfigure("WEBHOOK_ENABLED", "Webhooks", "Reconfigure webhooks?"):
|
||||
return
|
||||
@@ -308,7 +301,7 @@ def _is_progress(status: str) -> bool:
|
||||
|
||||
def _warn_missing_home_channels() -> None:
|
||||
"""Platforms with a token but no home channel."""
|
||||
from hermes_cli.setup import get_env_value, print_warning
|
||||
from hermes_cli.setup import get_env_value, _info, print_warning
|
||||
missing_home = [
|
||||
plat for plat, token_var, home_vars in _HOME_CHANNEL_CHECKS
|
||||
if get_env_value(token_var) and not any(get_env_value(v) for v in home_vars)
|
||||
@@ -362,7 +355,7 @@ def _restart_running_gateway(any_messaging: bool, supports_systemd: bool) -> Non
|
||||
|
||||
def setup_gateway(config: dict):
|
||||
"""Configure messaging platform integrations."""
|
||||
from hermes_cli.setup import print_header, print_info, print_success, prompt_checklist
|
||||
from hermes_cli.setup import _info, print_header, print_info, print_success, prompt_checklist
|
||||
from hermes_cli.gateway import _all_platforms, _platform_status, _configure_platform
|
||||
print_header("Messaging Platforms")
|
||||
_info("Connect to messaging platforms to chat with Hermes from anywhere.",
|
||||
|
||||
@@ -38,18 +38,15 @@ def _run_portal_one_shot(config: dict) -> None:
|
||||
— the same flow ``_run_first_time_quick_setup`` and ``hermes model`` use for Nous —
|
||||
so there is a single source of truth and ``hermes portal`` always offers a picker.
|
||||
"""
|
||||
from hermes_cli.setup import Colors, color, load_config, print_error, print_info, print_success
|
||||
from hermes_cli.setup import Colors, color, _info, load_config, print_error, print_info, print_success
|
||||
print()
|
||||
print(color("┌─────────────────────────────────────────────────────────┐", Colors.MAGENTA))
|
||||
print(color("│ ⚕ Hermes Setup — Nous Portal (one-shot) │", Colors.MAGENTA))
|
||||
print(color("└─────────────────────────────────────────────────────────┘", Colors.MAGENTA))
|
||||
print()
|
||||
print_info(" One subscription, 300+ models, plus the Tool Gateway:")
|
||||
print_info(" web search, image generation, TTS, browser automation")
|
||||
print_info(" — all routed through your Nous Portal sub.")
|
||||
print()
|
||||
print_info(" Sign up: https://portal.nousresearch.com/manage-subscription")
|
||||
print()
|
||||
_info(None, " One subscription, 300+ models, plus the Tool Gateway:",
|
||||
" web search, image generation, TTS, browser automation",
|
||||
" — all routed through your Nous Portal sub.", None,
|
||||
" Sign up: https://portal.nousresearch.com/manage-subscription", None)
|
||||
|
||||
# Handles both the logged-out path (device-code OAuth, selects a model) and the
|
||||
# logged-in path (curated picker), then Tool Gateway opt-in; sets provider=nous.
|
||||
@@ -61,9 +58,7 @@ def _run_portal_one_shot(config: dict) -> None:
|
||||
# _login_nous raises SystemExit(130)/(1) on cancel/failure; the expired-
|
||||
# session re-login path inside _model_flow_nous only catches Exception,
|
||||
# so a SystemExit there would otherwise escape and kill the whole CLI.
|
||||
print()
|
||||
print_info(" Setup cancelled.")
|
||||
print_info(" You can retry later with `hermes portal`.")
|
||||
_info(None, " Setup cancelled.", " You can retry later with `hermes portal`.")
|
||||
return
|
||||
except Exception as exc:
|
||||
logger.debug("_model_flow_nous error during `hermes portal`: %s", exc)
|
||||
@@ -95,17 +90,16 @@ def _run_first_time_quick_setup(config: dict, hermes_home, is_existing: bool):
|
||||
<section>`` or switch providers with ``hermes model``.
|
||||
"""
|
||||
from hermes_cli.setup import (
|
||||
_apply_default_agent_settings, _print_macos_fda_tip, _print_setup_summary, print_header,
|
||||
print_info, print_success, print_warning, prompt_choice, save_config, setup_gateway,
|
||||
_apply_default_agent_settings, _info, print_header, print_info, _print_macos_fda_tip,
|
||||
_print_setup_summary, print_success, print_warning, prompt_choice, save_config, setup_gateway,
|
||||
setup_terminal_backend,
|
||||
)
|
||||
# Step 1: Nous Portal — OAuth login + model selection (provider set to "nous" by the save).
|
||||
print()
|
||||
print_header("Nous Portal")
|
||||
print_info("One subscription, 300+ models, plus the Tool Gateway:")
|
||||
print_info(" web search, image generation, TTS, browser automation.")
|
||||
print_info("Sign up: https://portal.nousresearch.com/manage-subscription")
|
||||
print()
|
||||
_info("One subscription, 300+ models, plus the Tool Gateway:",
|
||||
" web search, image generation, TTS, browser automation.",
|
||||
"Sign up: https://portal.nousresearch.com/manage-subscription", None)
|
||||
try:
|
||||
from hermes_cli.main import _model_flow_nous
|
||||
_model_flow_nous(config)
|
||||
@@ -161,7 +155,7 @@ def _print_macos_fda_tip() -> None:
|
||||
(the TCC db dir is FDA-gated but probing it never triggers a dialog).
|
||||
Silent on non-macOS and when FDA is already granted or indeterminate.
|
||||
"""
|
||||
from hermes_cli.setup import print_info
|
||||
from hermes_cli.setup import _info
|
||||
if sys.platform != "darwin":
|
||||
return
|
||||
tcc_dir = Path.home() / "Library" / "Application Support" / "com.apple.TCC"
|
||||
@@ -172,12 +166,11 @@ def _print_macos_fda_tip() -> None:
|
||||
pass
|
||||
except OSError:
|
||||
return # indeterminate — don't nag
|
||||
print()
|
||||
print_info(" macOS tip: silence ALL folder permission prompts with one switch —")
|
||||
print_info(" System Settings → Privacy & Security → Full Disk Access → enable")
|
||||
print_info(" your terminal (and Hermes.app if you use Desktop), or run:")
|
||||
print_info(" open \"x-apple.systempreferences:com.apple.preference" ".security?Privacy_AllFiles\"")
|
||||
print_info(" The grant is permanent — it survives every Hermes update.")
|
||||
_info(None, " macOS tip: silence ALL folder permission prompts with one switch —",
|
||||
" System Settings → Privacy & Security → Full Disk Access → enable",
|
||||
" your terminal (and Hermes.app if you use Desktop), or run:",
|
||||
" open \"x-apple.systempreferences:com.apple.preference" ".security?Privacy_AllFiles\"",
|
||||
" The grant is permanent — it survives every Hermes update.")
|
||||
|
||||
|
||||
def _blank_slate_minimal_toolsets(config: dict):
|
||||
@@ -240,22 +233,20 @@ def _run_blank_slate_setup(config: dict, hermes_home, is_existing: bool):
|
||||
else OFF; then either finish now or walk through opting capabilities back in.
|
||||
Nothing is enabled that the user did not explicitly choose."""
|
||||
from hermes_cli.setup import (
|
||||
_blank_slate_minimal_toolsets, _blank_slate_minimize_config, _blank_slate_walkthrough,
|
||||
_print_setup_summary, print_header, print_info, print_success, prompt_choice, save_config,
|
||||
_blank_slate_minimal_toolsets, _blank_slate_minimize_config, _blank_slate_walkthrough, _info,
|
||||
print_header, print_info, _print_setup_summary, print_success, prompt_choice, save_config,
|
||||
setup_model_provider, setup_terminal_backend,
|
||||
)
|
||||
print()
|
||||
print_header("Blank Slate Setup")
|
||||
print_info("Everything starts OFF. First we force-enable only what's required")
|
||||
print_info("to run an agent, then you choose whether to stop there or walk")
|
||||
print_info("through enabling more — opting in to exactly what you want.")
|
||||
print_info("")
|
||||
print_info("Forced on: Provider & Model, File Operations, Terminal, Vision, Skills.")
|
||||
print_info("Everything else (web, browser, code exec, memory,")
|
||||
print_info("delegation, cron, plugins, MCP, …) starts disabled. The")
|
||||
print_info("essential `hermes-agent` skill is always kept so the agent")
|
||||
print_info("can help you drive and configure Hermes itself.")
|
||||
print()
|
||||
_info("Everything starts OFF. First we force-enable only what's required",
|
||||
"to run an agent, then you choose whether to stop there or walk",
|
||||
"through enabling more — opting in to exactly what you want.", "",
|
||||
"Forced on: Provider & Model, File Operations, Terminal, Vision, Skills.",
|
||||
"Everything else (web, browser, code exec, memory,",
|
||||
"delegation, cron, plugins, MCP, …) starts disabled. The",
|
||||
"essential `hermes-agent` skill is always kept so the agent",
|
||||
"can help you drive and configure Hermes itself.", None)
|
||||
|
||||
# Step 1: Provider & Model (REQUIRED — the agent cannot run without it)
|
||||
print_header("Step 1 — Provider & Model (required)")
|
||||
@@ -296,13 +287,9 @@ def _run_blank_slate_setup(config: dict, hermes_home, is_existing: bool):
|
||||
logger.debug("blank-slate skill opt-out error: %s", exc)
|
||||
print()
|
||||
print_success("Blank Slate setup complete — minimal agent ready.")
|
||||
print_info("Enable anything later, on demand:")
|
||||
print_info(" Enable tools: hermes tools")
|
||||
print_info(" Seed skills: hermes skills opt-in --sync")
|
||||
print_info(" Add MCP servers: hermes mcp add")
|
||||
print_info(" Enable plugins: hermes plugins")
|
||||
print_info(" Tune agent settings: hermes setup agent")
|
||||
print()
|
||||
_info("Enable anything later, on demand:", " Enable tools: hermes tools",
|
||||
" Seed skills: hermes skills opt-in --sync", " Add MCP servers: hermes mcp add",
|
||||
" Enable plugins: hermes plugins", " Tune agent settings: hermes setup agent", None)
|
||||
_print_setup_summary(config, hermes_home)
|
||||
return
|
||||
|
||||
@@ -312,8 +299,8 @@ def _run_blank_slate_setup(config: dict, hermes_home, is_existing: bool):
|
||||
def _blank_slate_walkthrough(config: dict, hermes_home):
|
||||
"""Opt-in walkthrough for Blank Slate: skills, tools, plugins, MCP, gateway."""
|
||||
from hermes_cli.setup import (
|
||||
_print_setup_summary, print_header, print_info, print_success, print_warning,
|
||||
prompt_yes_no, save_config, setup_gateway,
|
||||
_info, print_header, print_info, _print_setup_summary, print_success, print_warning, prompt_yes_no,
|
||||
save_config, setup_gateway,
|
||||
)
|
||||
# Bundled skills — default to NONE, offer to seed all
|
||||
print()
|
||||
@@ -334,10 +321,10 @@ def _blank_slate_walkthrough(config: dict, hermes_home):
|
||||
set_bundled_skills_opt_out(True)
|
||||
# Essential skills (`hermes-agent`) are still seeded for an opted-out profile.
|
||||
sync_skills(quiet=True)
|
||||
print_info("No skills seeded (except the essential `hermes-agent`")
|
||||
print_info("skill). A .no-bundled-skills marker keeps future")
|
||||
print_info("`hermes update` runs from re-injecting them. Opt back in any")
|
||||
print_info("time with `hermes skills opt-in --sync`.")
|
||||
_info("No skills seeded (except the essential `hermes-agent`",
|
||||
"skill). A .no-bundled-skills marker keeps future",
|
||||
"`hermes update` runs from re-injecting them. Opt back in any",
|
||||
"time with `hermes skills opt-in --sync`.")
|
||||
except Exception as exc:
|
||||
logger.debug("blank-slate skill handling error: %s", exc)
|
||||
print_warning(f"Skill setup step encountered an error: {exc}")
|
||||
@@ -345,9 +332,8 @@ def _blank_slate_walkthrough(config: dict, hermes_home):
|
||||
# Walk through enabling additional tools
|
||||
print()
|
||||
print_header("Tools")
|
||||
print_info("Pick exactly which additional toolsets to turn on.")
|
||||
print_info("(file and terminal are already on; leave the rest off if you want")
|
||||
print_info(" the most minimal agent.)")
|
||||
_info("Pick exactly which additional toolsets to turn on.",
|
||||
"(file and terminal are already on; leave the rest off if you want", " the most minimal agent.)")
|
||||
if prompt_yes_no("Open the tool selector to enable more tools?", default=False):
|
||||
try:
|
||||
from hermes_cli.tools_config import tools_command
|
||||
@@ -384,11 +370,8 @@ def _blank_slate_walkthrough(config: dict, hermes_home):
|
||||
|
||||
print()
|
||||
print_success("Blank Slate setup complete — minimal agent ready.")
|
||||
print_info(" Enable more tools: hermes tools")
|
||||
print_info(" Seed skills: hermes skills opt-in --sync")
|
||||
print_info(" Add MCP servers: hermes mcp add")
|
||||
print_info(" Tune agent settings: hermes setup agent")
|
||||
print()
|
||||
_info(" Enable more tools: hermes tools", " Seed skills: hermes skills opt-in --sync",
|
||||
" Add MCP servers: hermes mcp add", " Tune agent settings: hermes setup agent", None)
|
||||
|
||||
_print_setup_summary(config, hermes_home)
|
||||
|
||||
@@ -396,8 +379,8 @@ def _blank_slate_walkthrough(config: dict, hermes_home):
|
||||
def _run_quick_setup(config: dict, hermes_home):
|
||||
"""Quick setup — only configure items that are missing."""
|
||||
from hermes_cli.setup import (
|
||||
Colors, _print_setup_summary, _prompt_api_key, color, print_header, print_info,
|
||||
print_success, print_warning, prompt_checklist, save_config, save_env_value,
|
||||
Colors, color, _info, print_header, print_info, _print_setup_summary, print_success, print_warning,
|
||||
_prompt_api_key, prompt_checklist, save_config, save_env_value,
|
||||
)
|
||||
from hermes_cli.config import (get_missing_env_vars, get_missing_config_fields, check_config_version)
|
||||
print()
|
||||
@@ -411,9 +394,8 @@ def _run_quick_setup(config: dict, hermes_home):
|
||||
|
||||
if not (missing_required or missing_optional or missing_config or current_ver < latest_ver):
|
||||
print_success("Everything is configured! Nothing to do.")
|
||||
print()
|
||||
print_info("Run 'hermes setup' and choose 'Full Setup' to reconfigure,")
|
||||
print_info("or pick a specific section from the menu.")
|
||||
_info(None, "Run 'hermes setup' and choose 'Full Setup' to reconfigure,",
|
||||
"or pick a specific section from the menu.")
|
||||
return
|
||||
|
||||
# Handle missing required env vars
|
||||
|
||||
@@ -255,8 +255,8 @@ def _print_section_header(title):
|
||||
def _print_setup_summary(config: dict, hermes_home):
|
||||
"""Print the setup completion summary."""
|
||||
from hermes_cli.setup import (
|
||||
print_header, print_info, print_warning, get_config_path, get_env_path,
|
||||
get_nous_subscription_features, Colors, color,
|
||||
Colors, color, get_config_path, get_env_path, get_nous_subscription_features, _info, print_header,
|
||||
print_info, print_warning,
|
||||
)
|
||||
# Provider readiness — the one thing setup absolutely must produce. Previously a user
|
||||
# could cancel the API-key prompt mid-wizard (Enter → "Cancelled."), watch the wizard
|
||||
@@ -272,9 +272,9 @@ def _print_setup_summary(config: dict, hermes_home):
|
||||
if not _provider_ready:
|
||||
print()
|
||||
print_warning("No inference provider is configured — Hermes cannot chat yet.")
|
||||
print_info(" Finish this one step with either of:")
|
||||
print_info(" hermes model (pick any provider/model)")
|
||||
print_info(" hermes setup --portal (Nous Portal OAuth, no API key)")
|
||||
_info(" Finish this one step with either of:",
|
||||
" hermes model (pick any provider/model)",
|
||||
" hermes setup --portal (Nous Portal OAuth, no API key)")
|
||||
|
||||
print()
|
||||
print_header("Tool Availability Summary")
|
||||
|
||||
@@ -18,13 +18,11 @@ _SANDBOX_IMAGE = "nikolaik/python-nodejs:python3.11-nodejs20"
|
||||
def _prompt_vercel_sandbox_settings(config: dict):
|
||||
"""Prompt for Vercel Sandbox settings without exposing unsupported disk sizing."""
|
||||
from hermes_cli.setup import (
|
||||
get_env_value, print_info, print_warning, prompt, remove_env_value, save_env_value,
|
||||
get_env_value, _info, print_info, print_warning, prompt, remove_env_value, save_env_value,
|
||||
)
|
||||
terminal = config.setdefault("terminal", {})
|
||||
print()
|
||||
print_info("Vercel Sandbox settings:")
|
||||
print_info(" Filesystem persistence uses Vercel snapshots.")
|
||||
print_info(" Snapshots restore files only; live processes do not continue after sandbox recreation.")
|
||||
_info(None, "Vercel Sandbox settings:", " Filesystem persistence uses Vercel snapshots.",
|
||||
" Snapshots restore files only; live processes do not continue after sandbox recreation.")
|
||||
|
||||
from tools.terminal_tool import _SUPPORTED_VERCEL_RUNTIMES
|
||||
|
||||
@@ -57,9 +55,7 @@ def _prompt_vercel_sandbox_settings(config: dict):
|
||||
)
|
||||
terminal["container_disk"] = 51200
|
||||
|
||||
print()
|
||||
print_info("Vercel authentication:")
|
||||
print_info(" Use a long-lived Vercel access token plus project/team IDs.")
|
||||
_info(None, "Vercel authentication:", " Use a long-lived Vercel access token plus project/team IDs.")
|
||||
linked = _read_nearest_vercel_project()
|
||||
if linked:
|
||||
print_info(" Found defaults in nearest .vercel/project.json.")
|
||||
@@ -136,7 +132,7 @@ def _setup_backend_local(config: dict) -> None:
|
||||
|
||||
|
||||
def _setup_backend_docker(config: dict) -> None:
|
||||
from hermes_cli.setup import print_info, print_success, print_warning, prompt_yes_no
|
||||
from hermes_cli.setup import _info, print_info, print_success, print_warning, prompt_yes_no
|
||||
print_success("Terminal backend: Docker")
|
||||
docker_bin = shutil.which("docker")
|
||||
if not docker_bin:
|
||||
@@ -147,13 +143,10 @@ def _setup_backend_docker(config: dict) -> None:
|
||||
|
||||
# Image and resource limits use defaults; tune via `hermes setup terminal`.
|
||||
config["terminal"].setdefault("docker_image", _SANDBOX_IMAGE)
|
||||
print()
|
||||
print_info("Docker sandboxes can be protected with the egress credential firewall.")
|
||||
print_info(
|
||||
"It routes sandbox traffic through iron-proxy so containers receive "
|
||||
"proxy tokens instead of real API keys."
|
||||
)
|
||||
print_info(" Docker only for now; Modal, SSH, Daytona, and Singularity are not wired yet.")
|
||||
_info(None, "Docker sandboxes can be protected with the egress credential firewall.",
|
||||
"It routes sandbox traffic through iron-proxy so containers receive "
|
||||
"proxy tokens instead of real API keys.",
|
||||
" Docker only for now; Modal, SSH, Daytona, and Singularity are not wired yet.")
|
||||
if prompt_yes_no(" Enable egress firewall for Docker sandboxes?", False):
|
||||
proxy_cfg = config.setdefault("proxy", {})
|
||||
proxy_cfg["enabled"] = True
|
||||
@@ -179,7 +172,7 @@ def _setup_backend_singularity(config: dict) -> None:
|
||||
|
||||
def _setup_backend_modal(config: dict) -> None:
|
||||
from hermes_cli.setup import (
|
||||
cfg_get, get_env_value, get_nous_subscription_features, managed_nous_tools_enabled,
|
||||
cfg_get, get_env_value, get_nous_subscription_features, _info, managed_nous_tools_enabled,
|
||||
print_info, print_success, prompt_choice, prompt_yes_no,
|
||||
)
|
||||
print_success("Terminal backend: Modal")
|
||||
@@ -217,9 +210,7 @@ def _setup_backend_modal(config: dict) -> None:
|
||||
config["terminal"]["modal_mode"] = "direct"
|
||||
print_info("Requires a Modal account: https://modal.com")
|
||||
_ensure_sdk("modal", "uv pip install modal")
|
||||
print()
|
||||
print_info("Modal authentication:")
|
||||
print_info(" Get your token at: https://modal.com/settings")
|
||||
_info(None, "Modal authentication:", " Get your token at: https://modal.com/settings")
|
||||
if get_env_value("MODAL_TOKEN_ID"):
|
||||
print_info(" Modal token: already configured")
|
||||
if not prompt_yes_no(" Update Modal credentials?", False):
|
||||
@@ -229,11 +220,11 @@ def _setup_backend_modal(config: dict) -> None:
|
||||
|
||||
|
||||
def _setup_backend_daytona(config: dict) -> None:
|
||||
from hermes_cli.setup import get_env_value, print_info, print_success, prompt_yes_no
|
||||
from hermes_cli.setup import get_env_value, _info, print_info, print_success, prompt_yes_no
|
||||
print_success("Terminal backend: Daytona")
|
||||
print_info("Persistent cloud development environments.")
|
||||
print_info("Each session gets a dedicated sandbox with filesystem persistence.")
|
||||
print_info("Sign up at: https://daytona.io")
|
||||
_info("Persistent cloud development environments.",
|
||||
"Each session gets a dedicated sandbox with filesystem persistence.",
|
||||
"Sign up at: https://daytona.io")
|
||||
_ensure_sdk("daytona", "uv pip install daytona", show_stderr=True)
|
||||
print()
|
||||
if get_env_value("DAYTONA_API_KEY"):
|
||||
|
||||
@@ -46,7 +46,7 @@ _ESPEAK_INSTALL = {
|
||||
|
||||
def _install_neutts_deps() -> bool:
|
||||
"""Install NeuTTS dependencies with user approval. Returns True on success."""
|
||||
from hermes_cli.setup import print_info, print_success, print_warning, prompt_yes_no
|
||||
from hermes_cli.setup import _info, print_info, print_success, print_warning, prompt_yes_no
|
||||
if not _check_espeak_ng():
|
||||
hint, install_cmd = _ESPEAK_INSTALL.get(sys.platform, _ESPEAK_INSTALL["linux"])
|
||||
print()
|
||||
@@ -64,23 +64,19 @@ def _install_neutts_deps() -> bool:
|
||||
else:
|
||||
print_warning("espeak-ng is required for NeuTTS. Install it manually before using NeuTTS.")
|
||||
|
||||
print()
|
||||
print_info("Installing neutts Python package...")
|
||||
print_info("This will also download the TTS model (~300MB) on first use.")
|
||||
print()
|
||||
_info(None, "Installing neutts Python package...",
|
||||
"This will also download the TTS model (~300MB) on first use.", None)
|
||||
return _pip_install_tts_package("neutts", ["-U", "neutts[all]", "--quiet"], "uv pip install -U 'neutts[all]'")
|
||||
|
||||
|
||||
def _install_kittentts_deps() -> bool:
|
||||
"""Install KittenTTS dependencies with user approval. Returns True on success."""
|
||||
from hermes_cli.setup import print_info
|
||||
from hermes_cli.setup import _info
|
||||
wheel_url = (
|
||||
"https://github.com/KittenML/KittenTTS/releases/download/"
|
||||
"0.8.1/kittentts-0.8.1-py3-none-any.whl"
|
||||
)
|
||||
print()
|
||||
print_info("Installing kittentts Python package (~25-80MB model downloaded on first use)...")
|
||||
print()
|
||||
_info(None, "Installing kittentts Python package (~25-80MB model downloaded on first use)...", None)
|
||||
return _pip_install_tts_package(
|
||||
"kittentts", ["-U", wheel_url, "soundfile", "--quiet"], f"uv pip install -U '{wheel_url}' soundfile",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user