feat(cli): enhance run/init/uninstall for systemd & debug; refactor(env): standardize vars

This commit is contained in:
LIghtJUNction
2026-03-18 13:52:00 +08:00
parent b839de2050
commit 12cdf454b4
7 changed files with 262 additions and 21 deletions

93
.env.example Normal file
View File

@@ -0,0 +1,93 @@
# ==========================================
# AstrBot Environment Configuration Example
# ==========================================
# Copy this file to .env and adjust the values as needed.
# Note: Variables set here will override default configurations.
# ------------------------------------------
# Core Configuration (核心配置)
# ------------------------------------------
# AstrBot root directory path. Defaults to current working directory or ~/.astrbot for desktop client.
# ASTRBOT_ROOT=/path/to/astrbot
# Log level. Options: DEBUG, INFO, WARNING, ERROR, CRITICAL. Default: INFO.
# ASTRBOT_LOG_LEVEL=INFO
# Enable plugin auto-reload. Set to "1" to enable. Useful for development.
# ASTRBOT_RELOAD=0
# Disable metrics upload. Set to "1" to disable anonymous usage statistics.
# ASTRBOT_DISABLE_METRICS=0
# Python executable path override (used for local code execution feature).
# PYTHON=/usr/bin/python3
# Enable demo mode (might restrict some features).
# DEMO_MODE=False
# Enable testing mode (affects logging and some behaviors).
# TESTING=False
# Flag indicating execution via desktop client (Internal use mostly).
# ASTRBOT_DESKTOP_CLIENT=0
# Flag indicating execution via systemd service.
# ASTRBOT_SYSTEMD=0
# ------------------------------------------
# Dashboard Configuration (管理面板配置)
# ------------------------------------------
# Enable or disable the WebUI Dashboard. Default: True.
# ASTRBOT_DASHBOARD_ENABLE=True
# Dashboard bind host. Default: 0.0.0.0 (listen on all interfaces).
# ASTRBOT_DASHBOARD_HOST=0.0.0.0
# Dashboard bind port. Default: 6185.
# ASTRBOT_DASHBOARD_PORT=6185
# Enable SSL (HTTPS) for the dashboard.
# ASTRBOT_DASHBOARD_SSL_ENABLE=False
# SSL Certificate path (required if SSL is enabled).
# ASTRBOT_DASHBOARD_SSL_CERT=/path/to/cert.pem
# SSL Key path (required if SSL is enabled).
# ASTRBOT_DASHBOARD_SSL_KEY=/path/to/key.pem
# SSL CA Certificates path (optional).
# ASTRBOT_DASHBOARD_SSL_CA_CERTS=/path/to/ca.pem
# ------------------------------------------
# Network Configuration (网络配置)
# ------------------------------------------
# HTTP/HTTPS Proxy URL (e.g., http://127.0.0.1:7890).
# http_proxy=
# https_proxy=
# No proxy list (comma-separated domains/IPs to bypass proxy).
# no_proxy=localhost,127.0.0.1
# ------------------------------------------
# Integrations (第三方集成)
# ------------------------------------------
# Alibaba DashScope API Key (used for Rerank service).
# DASHSCOPE_API_KEY=sk-xxxxxxxxxxxx
# Coze Integration
# COZE_API_KEY=
# COZE_BOT_ID=
# Computer Use data directory (for screenshot/file storage related to computer control).
# BAY_DATA_DIR=
# ------------------------------------------
# Platform Specific (平台特定配置)
# ------------------------------------------
# Test mode for QQ Official Bot.
# TEST_MODE=off

View File

@@ -30,6 +30,12 @@ Runs on `http://localhost:3000` by default.
5. Use English for all new comments.
6. For path handling, use `pathlib.Path` instead of string paths, and use `astrbot.core.utils.path_utils` to get the AstrBot data and temp directory.
7. Use Python 3.12+ type hinting syntax (e.g., `list[str]` over `List[str]`, `int | None` over `Optional[int]`). Avoid using `Any` and ensure comprehensive type annotations are provided.
8. When introducing new environment variables:
- Use the `ASTRBOT_` prefix for naming (e.g., `ASTRBOT_ENABLE_FEATURE`).
- Add the variable and description to `.env.example`.
- Update `astrbot/cli/commands/cmd_run.py`:
- Add to the module docstring under "Environment Variables Used in Project".
- Add to the `keys_to_print` list in the `run` function for debug output.
## PR instructions

View File

@@ -1,4 +1,5 @@
import asyncio
import os
from pathlib import Path
import click
@@ -44,7 +45,11 @@ async def initialize_astrbot(
default=True,
)
):
await check_dashboard(astrbot_root)
# 避免在 systemd 模式下因等待输入而阻塞
if os.environ.get("ASTRBOT_SYSTEMD") == "1":
click.echo("Systemd detected: Skipping dashboard check.")
else:
await check_dashboard(astrbot_root)
else:
click.echo("你可以使用在线面版v4.14.4+),填写后端地址的方式来控制。")
@@ -56,6 +61,9 @@ def init(yes: bool, backend_only: bool) -> None:
"""Initialize AstrBot"""
click.echo("Initializing AstrBot...")
if os.environ.get("ASTRBOT_SYSTEMD") == "1":
yes = True
astrbot_root = astrbot_paths.root
lock_file = astrbot_root / "astrbot.lock"
lock = FileLock(lock_file, timeout=5)

View File

@@ -1,3 +1,40 @@
"""AstrBot Run
Environment Variables Used in Project:
Core:
- `ASTRBOT_ROOT`: AstrBot root directory path.
- `ASTRBOT_LOG_LEVEL`: Log level (e.g. INFO, DEBUG).
- `ASTRBOT_CLI`: Flag indicating execution via CLI.
- `ASTRBOT_DESKTOP_CLIENT`: Flag indicating execution via desktop client.
- `ASTRBOT_SYSTEMD`: Flag indicating execution via systemd service.
- `ASTRBOT_RELOAD`: Enable plugin auto-reload (set to "1").
- `ASTRBOT_DISABLE_METRICS`: Disable metrics upload (set to "1").
- `TESTING`: Enable testing mode.
- `DEMO_MODE`: Enable demo mode.
- `PYTHON`: Python executable path override (for local code execution).
Dashboard:
- `ASTRBOT_DASHBOARD_ENABLE` / `DASHBOARD_ENABLE`: Enable/Disable Dashboard.
- `ASTRBOT_DASHBOARD_HOST` / `DASHBOARD_HOST`: Dashboard bind host.
- `ASTRBOT_DASHBOARD_PORT` / `DASHBOARD_PORT`: Dashboard bind port.
- `ASTRBOT_DASHBOARD_SSL_ENABLE` / `DASHBOARD_SSL_ENABLE`: Enable SSL.
- `ASTRBOT_DASHBOARD_SSL_CERT` / `DASHBOARD_SSL_CERT`: SSL Certificate path.
- `ASTRBOT_DASHBOARD_SSL_KEY` / `DASHBOARD_SSL_KEY`: SSL Key path.
- `ASTRBOT_DASHBOARD_SSL_CA_CERTS` / `DASHBOARD_SSL_CA_CERTS`: SSL CA Certs path.
Network:
- `http_proxy` / `https_proxy`: Proxy URL.
- `no_proxy`: No proxy list.
Integrations:
- `DASHSCOPE_API_KEY`: Alibaba DashScope API Key (for Rerank).
- `COZE_API_KEY` / `COZE_BOT_ID`: Coze integration.
- `BAY_DATA_DIR`: Computer Use data directory.
Platform Specific:
- `TEST_MODE`: Test mode for QQOfficial.
"""
import asyncio
import os
import sys
@@ -21,7 +58,9 @@ async def run_astrbot(astrbot_root: Path) -> None:
os.environ.get("ASTRBOT_DASHBOARD_ENABLE", os.environ.get("DASHBOARD_ENABLE"))
== "True"
):
await check_dashboard(astrbot_root)
# 避免在 systemd 模式下因等待输入而阻塞
if os.environ.get("ASTRBOT_SYSTEMD") != "1":
await check_dashboard(astrbot_root)
log_broker = LogBroker()
LogManager.set_queue_handler(logger, log_broker)
@@ -35,6 +74,7 @@ async def run_astrbot(astrbot_root: Path) -> None:
@click.option("--reload", "-r", is_flag=True, help="Auto-reload plugins")
@click.option("--host", "-H", help="AstrBot Dashboard Host", required=False, type=str)
@click.option("--port", "-p", help="AstrBot Dashboard port", required=False, type=str)
@click.option("--root", help="AstrBot root directory", required=False, type=str)
@click.option(
"--backend-only",
is_flag=True,
@@ -48,12 +88,43 @@ async def run_astrbot(astrbot_root: Path) -> None:
type=str,
default="INFO",
)
@click.option("--debug", is_flag=True, help="Enable debug mode")
@click.command()
def run(reload: bool, host: str, port: str, backend_only: bool, log_level: str) -> None:
def run(
reload: bool,
host: str,
port: str,
root: str,
backend_only: bool,
log_level: str,
debug: bool,
) -> None:
"""Run AstrBot"""
try:
if debug:
log_level = "DEBUG"
# Normalize environment variables for backward compatibility
# If the legacy env var is set but the new one isn't, copy it over.
env_map = {
"DASHBOARD_ENABLE": "ASTRBOT_DASHBOARD_ENABLE",
"DASHBOARD_HOST": "ASTRBOT_DASHBOARD_HOST",
"DASHBOARD_PORT": "ASTRBOT_DASHBOARD_PORT",
"DASHBOARD_SSL_ENABLE": "ASTRBOT_DASHBOARD_SSL_ENABLE",
"DASHBOARD_SSL_CERT": "ASTRBOT_DASHBOARD_SSL_CERT",
"DASHBOARD_SSL_KEY": "ASTRBOT_DASHBOARD_SSL_KEY",
"DASHBOARD_SSL_CA_CERTS": "ASTRBOT_DASHBOARD_SSL_CA_CERTS",
}
for legacy, new in env_map.items():
if legacy in os.environ and new not in os.environ:
os.environ[new] = os.environ[legacy]
os.environ["ASTRBOT_CLI"] = "1"
astrbot_root = astrbot_paths.root
if root:
os.environ["ASTRBOT_ROOT"] = root
astrbot_root = Path(root)
else:
astrbot_root = astrbot_paths.root
if not check_astrbot_root(astrbot_root):
raise click.ClickException(
@@ -77,6 +148,53 @@ def run(reload: bool, host: str, port: str, backend_only: bool, log_level: str)
click.echo("Plugin auto-reload enabled")
os.environ["ASTRBOT_RELOAD"] = "1"
if debug:
keys_to_print = [
"ASTRBOT_ROOT",
"ASTRBOT_LOG_LEVEL",
"ASTRBOT_CLI",
"ASTRBOT_DESKTOP_CLIENT",
"ASTRBOT_SYSTEMD",
"ASTRBOT_RELOAD",
"ASTRBOT_DISABLE_METRICS",
"TESTING",
"DEMO_MODE",
"PYTHON",
"ASTRBOT_DASHBOARD_ENABLE",
"DASHBOARD_ENABLE",
"ASTRBOT_DASHBOARD_HOST",
"DASHBOARD_HOST",
"ASTRBOT_DASHBOARD_PORT",
"DASHBOARD_PORT",
"ASTRBOT_DASHBOARD_SSL_ENABLE",
"DASHBOARD_SSL_ENABLE",
"ASTRBOT_DASHBOARD_SSL_CERT",
"DASHBOARD_SSL_CERT",
"ASTRBOT_DASHBOARD_SSL_KEY",
"DASHBOARD_SSL_KEY",
"ASTRBOT_DASHBOARD_SSL_CA_CERTS",
"DASHBOARD_SSL_CA_CERTS",
"http_proxy",
"https_proxy",
"no_proxy",
"DASHSCOPE_API_KEY",
"COZE_API_KEY",
"COZE_BOT_ID",
"BAY_DATA_DIR",
"TEST_MODE",
]
click.secho("\n[Debug Mode] Environment Variables:", fg="yellow", bold=True)
for key in keys_to_print:
if key in os.environ:
val = os.environ[key]
if "KEY" in key or "PASSWORD" in key or "SECRET" in key:
if len(val) > 8:
val = val[:4] + "****" + val[-4:]
else:
val = "****"
click.echo(f" {click.style(key, fg='cyan')}: {val}")
click.echo("")
lock_file = astrbot_root / "astrbot.lock"
lock = FileLock(lock_file, timeout=5)
with lock.acquire():

View File

@@ -1,3 +1,4 @@
import os
import platform
import shutil
import subprocess
@@ -16,6 +17,9 @@ from astrbot.core.utils.astrbot_path import astrbot_paths
def uninstall(yes: bool, keep_data: bool) -> None:
"""Uninstall AstrBot systemd service and cleanup data"""
if os.environ.get("ASTRBOT_SYSTEMD") == "1":
yes = True
# 1. Remove Systemd Service
if platform.system() == "Linux" and shutil.which("systemctl"):
service_path = Path.home() / ".config" / "systemd" / "user" / "astrbot.service"
@@ -26,10 +30,15 @@ def uninstall(yes: bool, keep_data: bool) -> None:
default=True,
):
try:
click.echo("Stopping AstrBot service...")
subprocess.run(
["systemctl", "--user", "stop", "astrbot"], check=False
)
if os.environ.get("ASTRBOT_SYSTEMD") != "1":
click.echo("Stopping AstrBot service...")
subprocess.run(
["systemctl", "--user", "stop", "astrbot"], check=False
)
else:
click.echo(
"Skipping stop service (running as systemd service)."
)
click.echo("Disabling AstrBot service...")
subprocess.run(

View File

@@ -22,8 +22,8 @@ def _get_dashboard_port() -> int:
def _is_dashboard_ssl_enabled() -> bool:
env_ssl = os.environ.get("DASHBOARD_SSL_ENABLE") or os.environ.get(
"ASTRBOT_DASHBOARD_SSL_ENABLE"
env_ssl = os.environ.get("ASTRBOT_DASHBOARD_SSL_ENABLE") or os.environ.get(
"DASHBOARD_SSL_ENABLE"
)
if env_ssl is not None:
return env_ssl.strip().lower() in {"1", "true", "yes", "on"}

View File

@@ -90,7 +90,9 @@ class AstrBotDashboard:
def _check_webui_enabled(self) -> bool:
cfg = self.config.get("dashboard", {})
_env = os.environ.get("DASHBOARD_ENABLE")
_env = os.environ.get(
"ASTRBOT_DASHBOARD_ENABLE", os.environ.get("DASHBOARD_ENABLE")
)
if _env is not None:
return _env.lower() in ("true", "1", "yes")
return cfg.get("enable", True)
@@ -384,15 +386,20 @@ class AstrBotDashboard:
)
dashboard_config = self.config.get("dashboard", {})
host = os.environ.get("DASHBOARD_HOST") or dashboard_config.get(
"host", "0.0.0.0"
host = (
os.environ.get("ASTRBOT_DASHBOARD_HOST")
or os.environ.get("DASHBOARD_HOST")
or dashboard_config.get("host", "0.0.0.0")
)
port = int(
os.environ.get("DASHBOARD_PORT") or dashboard_config.get("port", 6185)
os.environ.get("ASTRBOT_DASHBOARD_PORT")
or os.environ.get("DASHBOARD_PORT")
or dashboard_config.get("port", 6185)
)
ssl_config = dashboard_config.get("ssl", {})
ssl_enable = _parse_env_bool(
os.environ.get("DASHBOARD_SSL_ENABLE"),
os.environ.get("ASTRBOT_DASHBOARD_SSL_ENABLE")
or os.environ.get("DASHBOARD_SSL_ENABLE"),
ssl_config.get("enable", False),
)
@@ -434,18 +441,18 @@ class AstrBotDashboard:
if ssl_enable:
cert_file = (
os.environ.get("DASHBOARD_SSL_CERT")
or os.environ.get("ASTRBOT_DASHBOARD_SSL_CERT")
os.environ.get("ASTRBOT_DASHBOARD_SSL_CERT")
or os.environ.get("DASHBOARD_SSL_CERT")
or ssl_config.get("cert_file", "")
)
key_file = (
os.environ.get("DASHBOARD_SSL_KEY")
or os.environ.get("ASTRBOT_DASHBOARD_SSL_KEY")
os.environ.get("ASTRBOT_DASHBOARD_SSL_KEY")
or os.environ.get("DASHBOARD_SSL_KEY")
or ssl_config.get("key_file", "")
)
ca_certs = (
os.environ.get("DASHBOARD_SSL_CA_CERTS")
or os.environ.get("ASTRBOT_DASHBOARD_SSL_CA_CERTS")
os.environ.get("ASTRBOT_DASHBOARD_SSL_CA_CERTS")
or os.environ.get("DASHBOARD_SSL_CA_CERTS")
or ssl_config.get("ca_certs", "")
)