mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-15 17:30:13 +08:00
feat(cli): enhance run/init/uninstall for systemd & debug; refactor(env): standardize vars
This commit is contained in:
93
.env.example
Normal file
93
.env.example
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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"}
|
||||
|
||||
@@ -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", "")
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user