mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-01 18:20:16 +08:00
Compare commits
10 Commits
codex/add-
...
v4.26.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
952d1bfad7 | ||
|
|
0cfe4163cd | ||
|
|
d90530af8e | ||
|
|
2bda4e4d96 | ||
|
|
bc117038fb | ||
|
|
b7cadfe704 | ||
|
|
6a343b7656 | ||
|
|
c89984bf42 | ||
|
|
421d718041 | ||
|
|
967ed01cf7 |
2
.github/workflows/build-docs.yml
vendored
2
.github/workflows/build-docs.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest # 运行环境
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v6.0.9
|
||||
with:
|
||||
|
||||
2
.github/workflows/code-format.yml
vendored
2
.github/workflows/code-format.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
|
||||
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@@ -56,7 +56,7 @@ jobs:
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
2
.github/workflows/coverage_test.yml
vendored
2
.github/workflows/coverage_test.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
2
.github/workflows/dashboard_ci.yml
vendored
2
.github/workflows/dashboard_ci.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v6.0.9
|
||||
|
||||
4
.github/workflows/docker-image.yml
vendored
4
.github/workflows/docker-image.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
fetch-tag: true
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 1
|
||||
fetch-tag: true
|
||||
|
||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
@@ -128,7 +128,7 @@ jobs:
|
||||
- build-dashboard
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
@@ -208,7 +208,7 @@ jobs:
|
||||
- publish-release
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ inputs.ref || github.ref }}
|
||||
|
||||
2
.github/workflows/smoke_test.yml
vendored
2
.github/workflows/smoke_test.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
2
.github/workflows/sync-wiki.yml
vendored
2
.github/workflows/sync-wiki.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
exit 1
|
||||
|
||||
- name: Check out docs repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
|
||||
2
.github/workflows/unit_tests.yml
vendored
2
.github/workflows/unit_tests.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import logging
|
||||
|
||||
__version__ = "4.26.0-beta.12"
|
||||
__version__ = "4.26.0"
|
||||
logger = logging.getLogger("astrbot")
|
||||
|
||||
@@ -136,8 +136,8 @@ DEFAULT_CONFIG = {
|
||||
),
|
||||
"llm_compress_keep_recent_ratio": 0.15,
|
||||
"llm_compress_provider_id": "",
|
||||
"max_context_length": 50,
|
||||
"dequeue_context_length": 10,
|
||||
"max_context_length": -1, # 默认不限制
|
||||
"dequeue_context_length": 1,
|
||||
"streaming_response": False,
|
||||
"show_tool_use_status": False,
|
||||
"show_tool_call_result": False,
|
||||
|
||||
@@ -25,7 +25,7 @@ from tenacity import (
|
||||
|
||||
from astrbot.api import logger
|
||||
from astrbot.api.event import AstrMessageEvent, MessageChain
|
||||
from astrbot.api.message_components import File, Image, Plain, Record, Video
|
||||
from astrbot.api.message_components import At, File, Image, Plain, Record, Video
|
||||
from astrbot.api.platform import AstrBotMessage, PlatformMetadata
|
||||
from astrbot.core.utils.media_utils import MediaResolver, file_uri_to_path, is_file_uri
|
||||
|
||||
@@ -747,6 +747,10 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
||||
file_source = file_path
|
||||
elif i.url:
|
||||
file_source = i.url
|
||||
elif isinstance(i, At):
|
||||
qq_id = getattr(i, "qq", "")
|
||||
if qq_id and qq_id != "all":
|
||||
plain_text += f"<@{qq_id}>"
|
||||
else:
|
||||
logger.debug(f"qq_official 忽略 {i.type}")
|
||||
return (
|
||||
|
||||
@@ -74,6 +74,18 @@ async def get_version(
|
||||
return await _run(service.get_version())
|
||||
|
||||
|
||||
@router.get("/stats/versions")
|
||||
async def get_public_versions(
|
||||
request: Request,
|
||||
service: StatService = Depends(get_service),
|
||||
):
|
||||
return ok(
|
||||
await service.get_public_versions(
|
||||
getattr(request.app.state, "dashboard_static_folder", None)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@router.get("/stats/first-notice")
|
||||
async def get_first_notice(
|
||||
locale: str | None = None,
|
||||
@@ -167,6 +179,18 @@ async def get_dashboard_version(
|
||||
return await _run(service.get_version())
|
||||
|
||||
|
||||
@legacy_router.get("/versions")
|
||||
async def get_dashboard_public_versions(
|
||||
request: Request,
|
||||
service: StatService = Depends(get_service),
|
||||
):
|
||||
return ok(
|
||||
await service.get_public_versions(
|
||||
getattr(request.app.state, "dashboard_static_folder", None)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@legacy_router.get("/start-time")
|
||||
async def get_dashboard_start_time(
|
||||
service: StatService = Depends(get_service),
|
||||
|
||||
@@ -265,6 +265,7 @@ class AstrBotDashboard:
|
||||
"/api/auth/logout",
|
||||
"/api/auth/setup-status",
|
||||
"/api/auth/setup",
|
||||
"/api/stat/versions",
|
||||
}
|
||||
allowed_endpoint_prefixes = [
|
||||
"/api/file",
|
||||
@@ -278,37 +279,74 @@ class AstrBotDashboard:
|
||||
):
|
||||
return None
|
||||
is_plugin_page_path = PluginPageAuth.is_protected_path(path)
|
||||
token = self._extract_dashboard_jwt(current_request)
|
||||
if not token and is_plugin_page_path:
|
||||
token = PluginPageAuth.extract_asset_token(current_request.query_params)
|
||||
if not token:
|
||||
dashboard_token = self._extract_dashboard_jwt(current_request)
|
||||
asset_token = (
|
||||
PluginPageAuth.extract_asset_token(current_request.query_params)
|
||||
if is_plugin_page_path
|
||||
else None
|
||||
)
|
||||
token_candidates = []
|
||||
if dashboard_token:
|
||||
token_candidates.append(dashboard_token)
|
||||
if asset_token and asset_token != dashboard_token:
|
||||
token_candidates.append(asset_token)
|
||||
if not token_candidates:
|
||||
r = JSONResponse(error("未授权"))
|
||||
r.status_code = 401
|
||||
return r
|
||||
|
||||
token_errors: list[str] = []
|
||||
for token in token_candidates:
|
||||
payload, token_error = self._validate_dashboard_token(token, path)
|
||||
if payload is not None:
|
||||
current_request.state.dashboard_g.username = cast(
|
||||
str, payload["username"]
|
||||
)
|
||||
return None
|
||||
token_errors.append(token_error)
|
||||
|
||||
error_message = (
|
||||
"Token 过期"
|
||||
if token_errors and all(item == "Token 过期" for item in token_errors)
|
||||
else "Token 无效"
|
||||
)
|
||||
r = JSONResponse(error(error_message))
|
||||
r.status_code = 401
|
||||
return r
|
||||
|
||||
def _validate_dashboard_token(
|
||||
self,
|
||||
token: str,
|
||||
path: str,
|
||||
) -> tuple[dict[str, Any] | None, str]:
|
||||
"""Validate a dashboard JWT or scoped plugin page asset token.
|
||||
|
||||
Args:
|
||||
token: JWT value from the Authorization header, cookie, or query string.
|
||||
path: Current request path used for plugin page asset token scope checks.
|
||||
|
||||
Returns:
|
||||
A tuple of the decoded payload and an error message. The payload is
|
||||
present only when the token is valid for the current request path.
|
||||
"""
|
||||
try:
|
||||
payload = jwt.decode(token, self._jwt_secret, algorithms=["HS256"])
|
||||
if PluginPageAuth.is_asset_token(
|
||||
payload
|
||||
) and not PluginPageAuth.is_scope_valid(
|
||||
payload,
|
||||
path,
|
||||
):
|
||||
r = JSONResponse(error("Token 无效"))
|
||||
r.status_code = 401
|
||||
return r
|
||||
|
||||
username = payload.get("username")
|
||||
if not isinstance(username, str) or not username.strip():
|
||||
raise jwt.InvalidTokenError("missing username in token payload")
|
||||
current_request.state.dashboard_g.username = username
|
||||
except jwt.ExpiredSignatureError:
|
||||
r = JSONResponse(error("Token 过期"))
|
||||
r.status_code = 401
|
||||
return r
|
||||
return None, "Token 过期"
|
||||
except jwt.InvalidTokenError:
|
||||
r = JSONResponse(error("Token 无效"))
|
||||
r.status_code = 401
|
||||
return r
|
||||
return None, "Token 无效"
|
||||
|
||||
if PluginPageAuth.is_asset_token(payload) and not PluginPageAuth.is_scope_valid(
|
||||
payload,
|
||||
path,
|
||||
):
|
||||
return None, "Token 无效"
|
||||
|
||||
username = payload.get("username")
|
||||
if not isinstance(username, str) or not username.strip():
|
||||
return None, "Token 无效"
|
||||
|
||||
return payload, ""
|
||||
|
||||
async def _apply_auth_rate_limit(
|
||||
self,
|
||||
|
||||
@@ -54,6 +54,7 @@ ALL_OPEN_API_SCOPES = (
|
||||
"im",
|
||||
"config",
|
||||
"chat",
|
||||
"data",
|
||||
"file",
|
||||
"plugin",
|
||||
"mcp",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
import asyncio
|
||||
import re
|
||||
import threading
|
||||
@@ -25,7 +26,7 @@ from astrbot.core.utils.auth_password import (
|
||||
is_default_dashboard_password,
|
||||
is_md5_dashboard_password,
|
||||
)
|
||||
from astrbot.core.utils.io import get_dashboard_version
|
||||
from astrbot.core.utils.io import get_dashboard_dist_version, get_dashboard_version
|
||||
from astrbot.core.utils.storage_cleaner import StorageCleaner
|
||||
from astrbot.core.utils.version_comparator import VersionComparator
|
||||
from astrbot.dashboard.password_state import (
|
||||
@@ -104,6 +105,68 @@ class StatService:
|
||||
"password_upgrade_required": not storage_upgraded,
|
||||
}
|
||||
|
||||
async def get_public_versions(
|
||||
self,
|
||||
dashboard_static_folder: str | None = None,
|
||||
) -> dict:
|
||||
"""Return version details that are safe to expose before login.
|
||||
|
||||
Args:
|
||||
dashboard_static_folder: Static WebUI dist directory currently served by
|
||||
the dashboard, when available.
|
||||
|
||||
Returns:
|
||||
Public WebUI and AstrBot version information.
|
||||
"""
|
||||
|
||||
def read_code_version() -> str | None:
|
||||
"""Read the AstrBot code version from the package file.
|
||||
|
||||
Returns:
|
||||
The version string from disk, or None when it is unavailable.
|
||||
"""
|
||||
|
||||
version_file = Path(get_astrbot_path()) / "astrbot" / "__init__.py"
|
||||
module = ast.parse(version_file.read_text(encoding="utf-8"))
|
||||
for statement in module.body:
|
||||
if not isinstance(statement, ast.Assign):
|
||||
continue
|
||||
if not any(
|
||||
isinstance(target, ast.Name) and target.id == "__version__"
|
||||
for target in statement.targets
|
||||
):
|
||||
continue
|
||||
if isinstance(statement.value, ast.Constant) and isinstance(
|
||||
statement.value.value,
|
||||
str,
|
||||
):
|
||||
return statement.value.value.strip()
|
||||
return None
|
||||
return None
|
||||
|
||||
dashboard_version = None
|
||||
try:
|
||||
if dashboard_static_folder:
|
||||
dashboard_version = get_dashboard_dist_version(
|
||||
Path(dashboard_static_folder)
|
||||
)
|
||||
if dashboard_version is None:
|
||||
dashboard_version = await get_dashboard_version()
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to read public WebUI version: %s", exc)
|
||||
|
||||
code_version = None
|
||||
try:
|
||||
code_version = await asyncio.to_thread(read_code_version)
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to read AstrBot code version from disk: %s", exc)
|
||||
|
||||
return {
|
||||
"webui_version": dashboard_version,
|
||||
"astrbot_version": VERSION,
|
||||
"astrbot_code_version": code_version,
|
||||
}
|
||||
|
||||
def get_start_time(self) -> dict:
|
||||
return {"start_time": self.core_lifecycle.start_time}
|
||||
|
||||
|
||||
154
changelogs/v4.26.0.md
Normal file
154
changelogs/v4.26.0.md
Normal file
@@ -0,0 +1,154 @@
|
||||
|
||||
> Note:
|
||||
> 1. WebUI “Config -> System Config” has moved to “Settings” at the bottom of the WebUI sidebar.
|
||||
> 2. It's recommended to update to v4.25.6 before updating to this version.
|
||||
>
|
||||
> 提醒:
|
||||
> 1. WebUI 的“配置 -> 系统配置”已迁移至 WebUI 侧边栏下方的“设置”。
|
||||
> 2. 建议先升级到 v4.25.6 再升级到此版本。
|
||||
>
|
||||
|
||||
- [更新日志(简体中文)](#chinese)
|
||||
- [Changelog(English)](#english)
|
||||
|
||||
<a id="chinese"></a>
|
||||
|
||||
## What's Changed
|
||||
|
||||
### ✨ 新功能
|
||||
|
||||
- 后端架构从 Quart 迁移至 FastAPI。新增多个 AstrBot OpenAPI。([#8688](https://github.com/AstrBotDevs/AstrBot/pull/8688))
|
||||
- 统一全平台消息媒体文件的处理逻辑,提升图片、音频、文件和引用消息媒体的解析一致性,对腾讯系 Silk 格式的语音文件不再使用 pilk 库。([#8764](https://github.com/AstrBotDevs/AstrBot/pull/8764))
|
||||
- WebUI 新增函数工具的逐工具权限管理,支持在工具面板中查看和切换工具权限。([#8693](https://github.com/AstrBotDevs/AstrBot/pull/8693))
|
||||
- WebUI 新增浅色、深色、跟随系统三种主题模式,并集中处理系统主题同步。([#8648](https://github.com/AstrBotDevs/AstrBot/pull/8648))
|
||||
- 重组 WebUI 系统配置页面,将系统配置入口迁移到侧边栏下方的设置区域,并优化相关设置项、自动保存和重启提示体验。([#8777](https://github.com/AstrBotDevs/AstrBot/pull/8777))
|
||||
- 新增 QQ 官方机器人 WebSocket 适配器扫码绑定流程,可通过 WebUI 一键扫码获取并回填 AppID 与 Secret,同时将 WebSocket 模板标记为推荐。([#8821](https://github.com/AstrBotDevs/AstrBot/pull/8821))
|
||||
- 增强 QQ 官方机器人群聊能力,支持群消息创建类型,并允许 Webhook 适配器在无缓存 `msg_id` 时主动发送群消息。([#8838](https://github.com/AstrBotDevs/AstrBot/pull/8838), [#8841](https://github.com/AstrBotDevs/AstrBot/pull/8841))
|
||||
- 为 OpenAI、Gemini、Anthropic 等模型请求加入可配置的重试机制,并新增请求最大重试次数配置,提升临时网络错误与 5xx 服务端错误下的稳定性。([#8893](https://github.com/AstrBotDevs/AstrBot/pull/8893))
|
||||
- 现在更新项目时,下载 AstrBot Core 会走 AstrBot 官方托管地址,提高网络稳定性。([#8888](https://github.com/AstrBotDevs/AstrBot/pull/8888))
|
||||
- 支持在请求中加载 workspace skills,并加固 workspace skill 发现流程。([#8884](https://github.com/AstrBotDevs/AstrBot/pull/8884))
|
||||
- 新增 Exa Web Search 提供商。([#8973](https://github.com/AstrBotDevs/AstrBot/pull/8973))
|
||||
- 新增 ElevenLabs TTS API Provider。([commit](https://github.com/AstrBotDevs/AstrBot/commit/0b2234936))
|
||||
- 新增启动时重置 WebUI 密码的命令行开关,便于无法登录时恢复访问。([commit](https://github.com/AstrBotDevs/AstrBot/commit/4f5075e60))
|
||||
- 新增预发布版本可见性开关。([commit](https://github.com/AstrBotDevs/AstrBot/commit/f9d408221))
|
||||
- 登录页新增公开版本详情展示。([#8986](https://github.com/AstrBotDevs/AstrBot/pull/8986))
|
||||
- 备份功能现在会包含 skills 目录。([#8700](https://github.com/AstrBotDevs/AstrBot/pull/8700))
|
||||
|
||||
### 优化
|
||||
|
||||
- 加强未来任务所有者校验,避免越权访问定时任务。([#8881](https://github.com/AstrBotDevs/AstrBot/pull/8881))
|
||||
- 优化知识库上传文件名路径穿越风险。([#8971](https://github.com/AstrBotDevs/AstrBot/pull/8971))
|
||||
- 优化插件上传文件名路径穿越风险。([#8968](https://github.com/AstrBotDevs/AstrBot/pull/8968))
|
||||
- 在受限本地文件系统工具中拒绝 hardlink 文件,避免通过工作区 hardlink 别名读写允许目录外的文件。
|
||||
- 加固沙箱文件传输与 CUA 健康检查流程,降低异常环境下的文件操作风险。([#8840](https://github.com/AstrBotDevs/AstrBot/pull/8840))
|
||||
- 消息组件日志输出现在会截断过长的 base64 字段,避免日志中出现大体积内联媒体内容。([#8591](https://github.com/AstrBotDevs/AstrBot/pull/8591))
|
||||
- 群聊上下文现在会展示被引用消息的内容。([commit](https://github.com/AstrBotDevs/AstrBot/commit/32cfcbf52))
|
||||
- 对话上下文新增当前星期信息。([#8669](https://github.com/AstrBotDevs/AstrBot/pull/8669))
|
||||
- 使用原子写入方式保存配置文件,降低写入中断导致配置损坏的概率。([#8793](https://github.com/AstrBotDevs/AstrBot/pull/8793))
|
||||
- 新增 GitHub 代理 `gh.dpik.top`,并移除失效代理 `gh.llkk.cc`。([#8772](https://github.com/AstrBotDevs/AstrBot/pull/8772), [#8761](https://github.com/AstrBotDevs/AstrBot/pull/8761))
|
||||
|
||||
### 修复
|
||||
|
||||
- 修复 aiocqhttp 平台适配器与消息事件处理器在多处 API 调用中缺少 `self_id` 路由参数的问题。([#8779](https://github.com/AstrBotDevs/AstrBot/pull/8779))
|
||||
- 修复生成平台 ID 时可能包含空白字符的问题。([#8768](https://github.com/AstrBotDevs/AstrBot/pull/8768))
|
||||
- 修复 Gemini Provider 工具定义没有正确传回模型,导致重复工具调用的问题。([#8833](https://github.com/AstrBotDevs/AstrBot/pull/8833))
|
||||
- 修复提供商源修改 ID 后保存被静默还原的问题。([#8915](https://github.com/AstrBotDevs/AstrBot/pull/8915))
|
||||
- 修复插件 LLM Tools 开关的归属校验问题,避免误操作其他插件的工具配置。([commit](https://github.com/AstrBotDevs/AstrBot/commit/fadada3d6))
|
||||
- 完善插件命名模式校验和边界场景处理。([commit](https://github.com/AstrBotDevs/AstrBot/commit/992aea986))
|
||||
- 修复插件重装后仓库来源丢失的问题。([commit](https://github.com/AstrBotDevs/AstrBot/commit/a3c25ec2c))
|
||||
- 修复子目录工具的 `handler_module_path` 不一致问题。([#8578](https://github.com/AstrBotDevs/AstrBot/pull/8578))
|
||||
- 修复 run-based tools 的保护逻辑,避免受保护工具在注册流程中被错误丢失。([#8790](https://github.com/AstrBotDevs/AstrBot/pull/8790))
|
||||
- 修复 persona 工具列表场景下系统工具被移除的问题。([#8908](https://github.com/AstrBotDevs/AstrBot/pull/8908))
|
||||
- 修复人格设定中将工具和 Skills 从指定列表切回“默认使用全部”后不生效的问题。([#8835](https://github.com/AstrBotDevs/AstrBot/pull/8835))
|
||||
- 修复新版 MCP 中 Streamable HTTP client 重命名导致的兼容问题,并保持 `mcp` 依赖小于 2。
|
||||
- 修复本地 Python 工具没有在当前 session workspace 中运行的问题。([#8792](https://github.com/AstrBotDevs/AstrBot/pull/8792))
|
||||
- 修复静态资源缺失时仍显示 WebUI ready banner 的问题。([#8804](https://github.com/AstrBotDevs/AstrBot/pull/8804))
|
||||
- 修复 Dashboard 创建文件夹时按 Enter 无法提交的问题。([#8597](https://github.com/AstrBotDevs/AstrBot/pull/8597))
|
||||
- 修复聊天输入框在非末尾位置使用输入法组合输入时可能丢失字符的问题。([#8811](https://github.com/AstrBotDevs/AstrBot/pull/8811))
|
||||
- 修复 changelog 弹窗中的锚点链接处理。([#8750](https://github.com/AstrBotDevs/AstrBot/pull/8750))
|
||||
- 修复 onboarding 平台配置与备份上传相关问题。([#8834](https://github.com/AstrBotDevs/AstrBot/pull/8834))
|
||||
- 将知识库上下文作为临时 user 内容注入,修复模型请求中知识库上下文角色不准确的问题。([#8904](https://github.com/AstrBotDevs/AstrBot/pull/8904))
|
||||
- 修复 cron 星期调度规范化问题。([#8984](https://github.com/AstrBotDevs/AstrBot/pull/8984))
|
||||
- 修复 QQ 官方平台发送消息时 At 组件被丢失的问题。([#8983](https://github.com/AstrBotDevs/AstrBot/pull/8983))
|
||||
- 修复引用消息中的 image caption 可能重复显示的问题。([#8718](https://github.com/AstrBotDevs/AstrBot/pull/8718))
|
||||
- 修复 Embedding API version 后缀被错误截断的问题。([#8736](https://github.com/AstrBotDevs/AstrBot/pull/8736))
|
||||
- 延迟导入 FAISS C 库,避免部分环境启动时进程卡住。([#8696](https://github.com/AstrBotDevs/AstrBot/pull/8696))
|
||||
- 关闭时主动释放数据库 engine,减少会话和测试环境中的资源残留。([#8650](https://github.com/AstrBotDevs/AstrBot/pull/8650))
|
||||
- 修复 CLI 版本来源不正确的问题。([#8692](https://github.com/AstrBotDevs/AstrBot/pull/8692))
|
||||
- 修复执行 `astrbot` 命令时不必要地创建 data 目录的问题。([#8932](https://github.com/AstrBotDevs/AstrBot/pull/8932))
|
||||
- 修复 sdist 构建产物路径,确保 Dashboard artifact 可被包含。([#8933](https://github.com/AstrBotDevs/AstrBot/pull/8933))
|
||||
- 修复插件页资源 token fallback。([#8970](https://github.com/AstrBotDevs/AstrBot/pull/8970))
|
||||
- 更新 `max_context_length` 与 `dequeue_context_length` 默认值。
|
||||
- 稳定 FastAPI Dashboard 路由注册测试,兼容 included router 节点。([commit](https://github.com/AstrBotDevs/AstrBot/commit/ad1b64d12))
|
||||
- 稳定 Dashboard 路由相关测试,兼容最新 FastAPI 行为。([commit](https://github.com/AstrBotDevs/AstrBot/commit/a2b6aad84))
|
||||
|
||||
<a id="english"></a>
|
||||
|
||||
## What's Changed (EN)
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
- Migrated the backend architecture from Quart to FastAPI and added multiple OpenAPI definitions. ([#8688](https://github.com/AstrBotDevs/AstrBot/pull/8688))
|
||||
- Unified media file handling across platforms, improving consistency for images, audio, files, and quoted-message media. Tencent Silk voice files no longer use the pilk library. ([#8764](https://github.com/AstrBotDevs/AstrBot/pull/8764))
|
||||
- Added per-tool permission management for function tools in WebUI, with support for viewing and toggling tool permissions from the tools panel. ([#8693](https://github.com/AstrBotDevs/AstrBot/pull/8693))
|
||||
- Added light, dark, and system theme modes to WebUI, with centralized system theme synchronization. ([#8648](https://github.com/AstrBotDevs/AstrBot/pull/8648))
|
||||
- Reorganized the WebUI system configuration page. The system configuration entry has moved to the Settings area at the bottom of the sidebar, with improved settings, autosave, and restart notices. ([#8777](https://github.com/AstrBotDevs/AstrBot/pull/8777))
|
||||
- Added a QR binding flow for the QQ Official Bot WebSocket adapter. WebUI can now fetch and autofill AppID and Secret through one-click QR setup, and the WebSocket template is marked as recommended. ([#8821](https://github.com/AstrBotDevs/AstrBot/pull/8821))
|
||||
- Enhanced QQ Official Bot group chat support by adding group message creation types and allowing the Webhook adapter to proactively send group messages without a cached `msg_id`. ([#8838](https://github.com/AstrBotDevs/AstrBot/pull/8838), [#8841](https://github.com/AstrBotDevs/AstrBot/pull/8841))
|
||||
- Added configurable retry handling for OpenAI, Gemini, Anthropic, and related model requests, including a maximum request retry setting for better stability during temporary network errors and 5xx server errors. ([#8893](https://github.com/AstrBotDevs/AstrBot/pull/8893))
|
||||
- AstrBot Core downloads now use AstrBot's officially hosted source during project updates, improving network stability. ([#8888](https://github.com/AstrBotDevs/AstrBot/pull/8888))
|
||||
- Added support for loading workspace skills in requests and hardened workspace skill discovery. ([#8884](https://github.com/AstrBotDevs/AstrBot/pull/8884))
|
||||
- Added Exa as a web search provider. ([#8973](https://github.com/AstrBotDevs/AstrBot/pull/8973))
|
||||
- Added the ElevenLabs TTS API provider. ([commit](https://github.com/AstrBotDevs/AstrBot/commit/0b2234936))
|
||||
- Added a startup flag to reset the WebUI password, making it easier to recover access when login is unavailable. ([commit](https://github.com/AstrBotDevs/AstrBot/commit/4f5075e60))
|
||||
- Added a prerelease visibility toggle. ([commit](https://github.com/AstrBotDevs/AstrBot/commit/f9d408221))
|
||||
- Added public version details to the login page. ([#8986](https://github.com/AstrBotDevs/AstrBot/pull/8986))
|
||||
- Backups now include the skills directory. ([#8700](https://github.com/AstrBotDevs/AstrBot/pull/8700))
|
||||
|
||||
### Improvements
|
||||
|
||||
- Strengthened Future Task owner checks to prevent unauthorized scheduled-task access. ([#8881](https://github.com/AstrBotDevs/AstrBot/pull/8881))
|
||||
- Hardened knowledge base upload filename handling against path traversal risks. ([#8971](https://github.com/AstrBotDevs/AstrBot/pull/8971))
|
||||
- Hardened plugin upload filename handling against path traversal risks. ([#8968](https://github.com/AstrBotDevs/AstrBot/pull/8968))
|
||||
- Rejected hardlinked files in restricted local filesystem tools to prevent workspace hardlink aliases from reading or writing files outside allowed directories.
|
||||
- Hardened sandbox file transfers and CUA health checks to reduce file-operation risks in abnormal environments. ([#8840](https://github.com/AstrBotDevs/AstrBot/pull/8840))
|
||||
- Message component logs now truncate long base64 fields to avoid large inline media payloads in logs. ([#8591](https://github.com/AstrBotDevs/AstrBot/pull/8591))
|
||||
- Group chat context now includes quoted message content. ([commit](https://github.com/AstrBotDevs/AstrBot/commit/32cfcbf52))
|
||||
- Added current weekday information to conversation context. ([#8669](https://github.com/AstrBotDevs/AstrBot/pull/8669))
|
||||
- Configuration files are now saved atomically, reducing the chance of corruption during interrupted writes. ([#8793](https://github.com/AstrBotDevs/AstrBot/pull/8793))
|
||||
- Added GitHub proxy `gh.dpik.top` and removed the invalid `gh.llkk.cc` proxy. ([#8772](https://github.com/AstrBotDevs/AstrBot/pull/8772), [#8761](https://github.com/AstrBotDevs/AstrBot/pull/8761))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fixed missing `self_id` routing parameters across multiple aiocqhttp platform adapter and message event API calls. ([#8779](https://github.com/AstrBotDevs/AstrBot/pull/8779))
|
||||
- Fixed generated platform IDs potentially containing whitespace characters. ([#8768](https://github.com/AstrBotDevs/AstrBot/pull/8768))
|
||||
- Fixed Gemini Provider tool definitions not being passed back to the model correctly, which could cause repeated tool calls. ([#8833](https://github.com/AstrBotDevs/AstrBot/pull/8833))
|
||||
- Fixed provider source ID edits being silently restored after saving. ([#8915](https://github.com/AstrBotDevs/AstrBot/pull/8915))
|
||||
- Fixed ownership checks when toggling plugin LLM tools to prevent accidental changes to other plugins' tool settings. ([commit](https://github.com/AstrBotDevs/AstrBot/commit/fadada3d6))
|
||||
- Improved plugin naming pattern validation and edge-case handling. ([commit](https://github.com/AstrBotDevs/AstrBot/commit/992aea986))
|
||||
- Fixed repository source information being lost after reinstalling plugins. ([commit](https://github.com/AstrBotDevs/AstrBot/commit/a3c25ec2c))
|
||||
- Fixed inconsistent `handler_module_path` values for tools inside subdirectories. ([#8578](https://github.com/AstrBotDevs/AstrBot/pull/8578))
|
||||
- Fixed run-based tool protection so protected tools are not incorrectly dropped during registration. ([#8790](https://github.com/AstrBotDevs/AstrBot/pull/8790))
|
||||
- Fixed system tools being removed when persona tool lists are configured. ([#8908](https://github.com/AstrBotDevs/AstrBot/pull/8908))
|
||||
- Fixed persona tool and Skill settings not taking effect after switching from selected items back to "use all by default". ([#8835](https://github.com/AstrBotDevs/AstrBot/pull/8835))
|
||||
- Fixed compatibility with the renamed Streamable HTTP client in newer MCP versions while keeping the `mcp` dependency below 2.
|
||||
- Fixed local Python tools not running inside the current session workspace. ([#8792](https://github.com/AstrBotDevs/AstrBot/pull/8792))
|
||||
- Fixed the WebUI ready banner being shown even when static assets are missing. ([#8804](https://github.com/AstrBotDevs/AstrBot/pull/8804))
|
||||
- Fixed Dashboard folder creation not being submitted when pressing Enter. ([#8597](https://github.com/AstrBotDevs/AstrBot/pull/8597))
|
||||
- Fixed possible IME composition character loss when typing at a non-terminal cursor position in the chat input. ([#8811](https://github.com/AstrBotDevs/AstrBot/pull/8811))
|
||||
- Fixed changelog anchor link handling in the Dashboard dialog. ([#8750](https://github.com/AstrBotDevs/AstrBot/pull/8750))
|
||||
- Fixed onboarding platform configuration and backup upload issues. ([#8834](https://github.com/AstrBotDevs/AstrBot/pull/8834))
|
||||
- Injected knowledge base context as temporary user content, fixing the role used for knowledge context in model requests. ([#8904](https://github.com/AstrBotDevs/AstrBot/pull/8904))
|
||||
- Fixed cron weekday scheduling normalization. ([#8984](https://github.com/AstrBotDevs/AstrBot/pull/8984))
|
||||
- Fixed At components being dropped when sending messages on the QQ Official platform. ([#8983](https://github.com/AstrBotDevs/AstrBot/pull/8983))
|
||||
- Fixed duplicate captions for quoted images. ([#8718](https://github.com/AstrBotDevs/AstrBot/pull/8718))
|
||||
- Fixed Embedding API version suffixes being truncated incorrectly. ([#8736](https://github.com/AstrBotDevs/AstrBot/pull/8736))
|
||||
- Deferred FAISS C library imports to avoid startup hangs in some environments. ([#8696](https://github.com/AstrBotDevs/AstrBot/pull/8696))
|
||||
- Disposed the database engine on shutdown to reduce resource leftovers in sessions and tests. ([#8650](https://github.com/AstrBotDevs/AstrBot/pull/8650))
|
||||
- Fixed the CLI version source. ([#8692](https://github.com/AstrBotDevs/AstrBot/pull/8692))
|
||||
- Fixed unnecessary data directory creation when executing the `astrbot` command. ([#8932](https://github.com/AstrBotDevs/AstrBot/pull/8932))
|
||||
- Fixed the sdist build artifact path so the Dashboard artifact can be included. ([#8933](https://github.com/AstrBotDevs/AstrBot/pull/8933))
|
||||
- Fixed plugin page asset token fallback. ([#8970](https://github.com/AstrBotDevs/AstrBot/pull/8970))
|
||||
- Updated the `max_context_length` and `dequeue_context_length` defaults.
|
||||
- Stabilized FastAPI Dashboard route registration tests for included router nodes. ([commit](https://github.com/AstrBotDevs/AstrBot/commit/ad1b64d12))
|
||||
- Stabilized Dashboard route tests for the latest FastAPI behavior. ([commit](https://github.com/AstrBotDevs/AstrBot/commit/a2b6aad84))
|
||||
File diff suppressed because one or more lines are too long
@@ -86,6 +86,9 @@ export type ChatProjectRequest = {
|
||||
};
|
||||
|
||||
export type ChatRequest = {
|
||||
/**
|
||||
* Caller-declared WebChat sender/session owner. This value is used as the message sender identity and may participate in sender-ID-based command permission checks. Treat chat-scoped API keys as trusted backend credentials and map or validate usernames before accepting end-user input.
|
||||
*/
|
||||
username?: string;
|
||||
session_id?: string;
|
||||
/**
|
||||
@@ -191,7 +194,7 @@ export type ConversationRef = {
|
||||
|
||||
export type CreateApiKeyRequest = {
|
||||
name: string;
|
||||
scopes?: Array<('bot' | 'provider' | 'persona' | 'im' | 'config' | 'chat' | 'file' | 'plugin' | 'mcp' | 'skill')>;
|
||||
scopes?: Array<('bot' | 'provider' | 'persona' | 'im' | 'config' | 'chat' | 'data' | 'file' | 'plugin' | 'mcp' | 'skill')>;
|
||||
expires_at?: string;
|
||||
expires_in_days?: number;
|
||||
};
|
||||
@@ -3091,6 +3094,10 @@ export type GetVersionResponse = (SuccessEnvelope);
|
||||
|
||||
export type GetVersionError = unknown;
|
||||
|
||||
export type GetPublicVersionsResponse = (SuccessEnvelope);
|
||||
|
||||
export type GetPublicVersionsError = unknown;
|
||||
|
||||
export type GetFirstNoticeData = {
|
||||
query?: {
|
||||
locale?: string;
|
||||
|
||||
@@ -105,6 +105,13 @@ export interface VersionData {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
export interface PublicVersionData {
|
||||
webui_version?: string | null;
|
||||
astrbot_version?: string | null;
|
||||
astrbot_code_version?: string | null;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
type StartTimeData = {
|
||||
start_time?: number | string | null;
|
||||
};
|
||||
@@ -1723,6 +1730,15 @@ export const statsApi = {
|
||||
},
|
||||
};
|
||||
|
||||
export const publicApi = {
|
||||
versions() {
|
||||
return withLegacyFallback<PublicVersionData>(
|
||||
openApiV1.getPublicVersions(),
|
||||
() => httpClient.get<ApiEnvelope<PublicVersionData>>('/api/stat/versions'),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
export const changelogApi = {
|
||||
listVersions() {
|
||||
return typed<{ versions?: string[] }>(openApiV1.listChangelogVersions());
|
||||
|
||||
@@ -56,6 +56,19 @@
|
||||
"totpTitle": "Two-Factor Verification",
|
||||
"subtitle": "Welcome"
|
||||
},
|
||||
"versions": {
|
||||
"webui": "WebUI",
|
||||
"astrbotRuntime": "AstrBot",
|
||||
"astrbotCode": "AstrBot(Code)",
|
||||
"mismatchTooltip": "Version mismatch",
|
||||
"dialogTitle": "Version status needs attention",
|
||||
"webuiMismatchTitle": "WebUI does not match the current AstrBot version",
|
||||
"webuiMismatchMessage": "This WebUI may not have been built for the running AstrBot version, so some pages or APIs may behave unexpectedly. Restart AstrBot manually first. If the mismatch remains, delete data/dist in the runtime directory and restart again so AstrBot can download the matching WebUI.",
|
||||
"runtimeMismatchTitle": "Running AstrBot does not match the code version",
|
||||
"runtimeMismatchMessage": "The code has been updated, but the current process is still running an older version. Restart AstrBot manually to load the new code.",
|
||||
"faq": "View FAQ",
|
||||
"close": "Close"
|
||||
},
|
||||
"theme": {
|
||||
"light": "Light Mode",
|
||||
"dark": "Dark Mode",
|
||||
|
||||
@@ -56,6 +56,19 @@
|
||||
"totpTitle": "Двухфакторная аутентификация",
|
||||
"subtitle": "Добро пожаловать"
|
||||
},
|
||||
"versions": {
|
||||
"webui": "WebUI",
|
||||
"astrbotRuntime": "AstrBot",
|
||||
"astrbotCode": "AstrBot(Code)",
|
||||
"mismatchTooltip": "Несовпадение версий",
|
||||
"dialogTitle": "Проверьте состояние версий",
|
||||
"webuiMismatchTitle": "WebUI не соответствует текущей версии AstrBot",
|
||||
"webuiMismatchMessage": "Этот WebUI мог быть собран не для запущенной версии AstrBot, поэтому некоторые страницы или API могут работать нестабильно. Сначала перезапустите AstrBot вручную. Если несовпадение останется, удалите data/dist в рабочем каталоге и перезапустите снова, чтобы AstrBot скачал подходящий WebUI.",
|
||||
"runtimeMismatchTitle": "Запущенный AstrBot не соответствует версии кода",
|
||||
"runtimeMismatchMessage": "Код уже обновлен, но текущий процесс все еще работает на старой версии. Перезапустите AstrBot вручную, чтобы загрузить новый код.",
|
||||
"faq": "Открыть FAQ",
|
||||
"close": "Закрыть"
|
||||
},
|
||||
"theme": {
|
||||
"light": "Светлая тема",
|
||||
"dark": "Темная тема",
|
||||
|
||||
@@ -56,6 +56,19 @@
|
||||
"totpTitle": "两步验证",
|
||||
"subtitle": "欢迎使用"
|
||||
},
|
||||
"versions": {
|
||||
"webui": "WebUI",
|
||||
"astrbotRuntime": "AstrBot",
|
||||
"astrbotCode": "AstrBot(Code)",
|
||||
"mismatchTooltip": "版本不一致",
|
||||
"dialogTitle": "版本状态需要确认",
|
||||
"webuiMismatchTitle": "WebUI 与当前 AstrBot 版本不匹配",
|
||||
"webuiMismatchMessage": "当前 WebUI 可能不是为此 AstrBot 版本构建,部分页面或接口可能出现异常。请先手动重启 AstrBot;若仍不一致,删除运行目录下的 data/dist 后再次重启,AstrBot 会重新下载匹配的 WebUI。",
|
||||
"runtimeMismatchTitle": "运行中的 AstrBot 与代码版本不一致",
|
||||
"runtimeMismatchMessage": "代码已经更新,但当前进程仍在运行旧版本。手动重启 AstrBot 后,将加载新的代码版本。",
|
||||
"faq": "查看 FAQ",
|
||||
"close": "关闭"
|
||||
},
|
||||
"theme": {
|
||||
"light": "浅色模式",
|
||||
"dark": "深色模式",
|
||||
|
||||
@@ -513,6 +513,7 @@ const availableScopes = [
|
||||
{ value: 'im', label: 'im' },
|
||||
{ value: 'config', label: 'config' },
|
||||
{ value: 'chat', label: 'chat' },
|
||||
{ value: 'data', label: 'data' },
|
||||
{ value: 'file', label: 'file' },
|
||||
{ value: 'plugin', label: 'plugin' },
|
||||
{ value: 'mcp', label: 'mcp' },
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useRouter } from 'vue-router';
|
||||
import { useCustomizerStore } from "@/stores/customizer";
|
||||
import { useModuleI18n } from '@/i18n/composables';
|
||||
import { useTheme } from 'vuetify';
|
||||
import { authApi } from '@/api/v1';
|
||||
import { authApi, publicApi, type PublicVersionData } from '@/api/v1';
|
||||
|
||||
const cardVisible = ref(false);
|
||||
const router = useRouter();
|
||||
@@ -16,6 +16,10 @@ const customizer = useCustomizerStore();
|
||||
const { tm: t } = useModuleI18n('features/auth');
|
||||
const theme = useTheme();
|
||||
const authLoginRef = ref<InstanceType<typeof AuthLogin> | null>(null);
|
||||
const publicVersions = ref<PublicVersionData | null>(null);
|
||||
const versionDialogVisible = ref(false);
|
||||
type VersionItem = { key: string; label: string; value: string };
|
||||
type VersionWarning = { key: string; title: string; message: string };
|
||||
|
||||
const logoTitle = computed(() => {
|
||||
if (authLoginRef.value?.stage === 'totp' || authLoginRef.value?.stage === 'recovery') {
|
||||
@@ -41,7 +45,90 @@ const currentThemeIcon = computed(() => {
|
||||
return 'mdi-white-balance-sunny';
|
||||
});
|
||||
|
||||
const versionValues = computed(() => {
|
||||
const versions = publicVersions.value;
|
||||
if (!versions) {
|
||||
return { webui: '', runtime: '', code: '' };
|
||||
}
|
||||
|
||||
return {
|
||||
webui: String(versions.webui_version || '').trim(),
|
||||
runtime: String(versions.astrbot_version || '').trim(),
|
||||
code: String(versions.astrbot_code_version || '').trim(),
|
||||
};
|
||||
});
|
||||
|
||||
const normalizedVersionValues = computed(() => {
|
||||
return {
|
||||
webui: versionValues.value.webui.replace(/^v/i, ''),
|
||||
runtime: versionValues.value.runtime.replace(/^v/i, ''),
|
||||
code: versionValues.value.code.replace(/^v/i, ''),
|
||||
};
|
||||
});
|
||||
|
||||
const versionWarnings = computed(() => {
|
||||
const normalized = normalizedVersionValues.value;
|
||||
const warnings: VersionWarning[] = [];
|
||||
|
||||
if (normalized.webui && normalized.runtime && normalized.webui !== normalized.runtime) {
|
||||
warnings.push({
|
||||
key: 'webui-runtime',
|
||||
title: t('versions.webuiMismatchTitle'),
|
||||
message: t('versions.webuiMismatchMessage'),
|
||||
});
|
||||
}
|
||||
if (normalized.runtime && normalized.code && normalized.runtime !== normalized.code) {
|
||||
warnings.push({
|
||||
key: 'runtime-code',
|
||||
title: t('versions.runtimeMismatchTitle'),
|
||||
message: t('versions.runtimeMismatchMessage'),
|
||||
});
|
||||
}
|
||||
|
||||
return warnings;
|
||||
});
|
||||
|
||||
const versionItems = computed(() => {
|
||||
const { webui, runtime, code } = versionValues.value;
|
||||
const normalized = normalizedVersionValues.value;
|
||||
const items: VersionItem[] = [];
|
||||
|
||||
if (webui) {
|
||||
items.push({
|
||||
key: 'webui',
|
||||
label: t('versions.webui'),
|
||||
value: webui,
|
||||
});
|
||||
}
|
||||
if (runtime) {
|
||||
items.push({
|
||||
key: 'astrbot',
|
||||
label: t('versions.astrbotRuntime'),
|
||||
value: runtime,
|
||||
});
|
||||
}
|
||||
if (runtime && code && normalized.runtime !== normalized.code) {
|
||||
items.push({
|
||||
key: 'astrbot-code',
|
||||
label: t('versions.astrbotCode'),
|
||||
value: code,
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
publicApi.versions()
|
||||
.then((res) => {
|
||||
publicVersions.value = res.data?.data || null;
|
||||
})
|
||||
.catch((error) => {
|
||||
if (import.meta.env.DEV) {
|
||||
console.warn('Failed to load public versions:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// 检查用户是否已登录,如果已登录则重定向
|
||||
if (authStore.has_token()) {
|
||||
const onboardingCompleted = await authStore.checkOnboardingCompleted();
|
||||
@@ -140,7 +227,60 @@ onMounted(async () => {
|
||||
<v-card-text>
|
||||
<AuthLogin ref="authLoginRef" />
|
||||
</v-card-text>
|
||||
<div v-if="versionItems.length" class="login-version-info">
|
||||
<span v-for="item in versionItems" :key="item.key" class="login-version-item">
|
||||
<span class="login-version-label">{{ item.label }}</span>
|
||||
<span class="login-version-value">{{ item.value }}</span>
|
||||
</span>
|
||||
<v-btn
|
||||
v-if="versionWarnings.length"
|
||||
class="version-help-btn"
|
||||
icon
|
||||
variant="text"
|
||||
size="x-small"
|
||||
:aria-label="t('versions.mismatchTooltip')"
|
||||
@click="versionDialogVisible = true"
|
||||
>
|
||||
<v-icon size="16">mdi-help-circle-outline</v-icon>
|
||||
<v-tooltip activator="parent" location="top">
|
||||
{{ t('versions.mismatchTooltip') }}
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-card>
|
||||
<v-dialog v-model="versionDialogVisible" max-width="460">
|
||||
<v-card class="version-dialog-card">
|
||||
<v-card-title class="version-dialog-title">
|
||||
<v-icon size="20" color="warning">mdi-alert-circle-outline</v-icon>
|
||||
<span>{{ t('versions.dialogTitle') }}</span>
|
||||
</v-card-title>
|
||||
<v-card-text class="version-dialog-content">
|
||||
<div
|
||||
v-for="warning in versionWarnings"
|
||||
:key="warning.key"
|
||||
class="version-warning-block"
|
||||
>
|
||||
<div class="version-warning-title">{{ warning.title }}</div>
|
||||
<div class="version-warning-message">{{ warning.message }}</div>
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-actions class="version-dialog-actions">
|
||||
<v-btn
|
||||
href="https://docs.astrbot.app/faq.html"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
variant="text"
|
||||
prepend-icon="mdi-help-circle-outline"
|
||||
>
|
||||
{{ t('versions.faq') }}
|
||||
</v-btn>
|
||||
<v-spacer />
|
||||
<v-btn color="primary" variant="text" @click="versionDialogVisible = false">
|
||||
{{ t('versions.close') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -160,4 +300,71 @@ onMounted(async () => {
|
||||
width: 400px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.login-version-info {
|
||||
align-items: center;
|
||||
color: rgba(var(--v-theme-on-surface), 0.56);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px 8px;
|
||||
justify-content: center;
|
||||
line-height: 1.45;
|
||||
padding: 0 14px 10px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.login-version-item {
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.login-version-label {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.version-help-btn {
|
||||
color: rgba(var(--v-theme-warning), 0.95);
|
||||
margin-left: -2px;
|
||||
}
|
||||
|
||||
.version-dialog-card {
|
||||
border-radius: 8px !important;
|
||||
}
|
||||
|
||||
.version-dialog-title {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
font-size: 17px;
|
||||
line-height: 1.35;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
.version-dialog-content {
|
||||
padding-top: 4px !important;
|
||||
}
|
||||
|
||||
.version-warning-block + .version-warning-block {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
||||
.version-warning-title {
|
||||
color: rgba(var(--v-theme-on-surface), 0.88);
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.version-warning-message {
|
||||
color: rgba(var(--v-theme-on-surface), 0.68);
|
||||
font-size: 13px;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.version-dialog-actions {
|
||||
padding-top: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -27,7 +27,9 @@ Set dashboard.host in data/cmd_config.json to enable remote access.
|
||||
|
||||
### Forgot Dashboard Password
|
||||
|
||||
If you forgot your AstrBot dashboard password, find the `"dashboard"` field in `AstrBot/data/cmd_config.json`, for example:
|
||||
If you forgot your AstrBot dashboard password, you can use the CLI tool `astrbot password` to change the password.
|
||||
|
||||
Another approach you can take is to find the `"dashboard"` field in `AstrBot/data/cmd_config.json`, for example:
|
||||
|
||||
```json
|
||||
"dashboard": {
|
||||
|
||||
@@ -28,7 +28,9 @@ Set dashboard.host in data/cmd_config.json to enable remote access.
|
||||
|
||||
### 管理面板的密码忘记了
|
||||
|
||||
如果你忘记了 AstrBot 管理面板的密码,你可以在 `AstrBot/data/cmd_config.json` 配置文件中找到 `"dashboard"` 字段,如下:
|
||||
如果你忘记了 AstrBot 管理面板的密码,你可以直接使用CLI工具`astrbot password`来更改密码
|
||||
|
||||
另外,你也可以在 `AstrBot/data/cmd_config.json` 配置文件中找到 `"dashboard"` 字段,如下:
|
||||
|
||||
```json
|
||||
"dashboard": {
|
||||
|
||||
@@ -8,7 +8,7 @@ info:
|
||||
JSON objects because their schemas are provided at runtime by template
|
||||
endpoints.
|
||||
Developer API keys currently support these scopes only: bot, provider,
|
||||
persona, im, config, chat, file, plugin, mcp, skill. The config scope also
|
||||
persona, im, config, chat, data, file, plugin, mcp, skill. The config scope also
|
||||
grants bot and provider access.
|
||||
servers:
|
||||
- url: http://localhost:6185
|
||||
@@ -4135,6 +4135,16 @@ paths:
|
||||
"200":
|
||||
$ref: "#/components/responses/Ok"
|
||||
|
||||
/api/v1/stats/versions:
|
||||
get:
|
||||
tags: [Stats]
|
||||
summary: Get public WebUI and AstrBot versions
|
||||
operationId: getPublicVersions
|
||||
security: []
|
||||
responses:
|
||||
"200":
|
||||
$ref: "#/components/responses/Ok"
|
||||
|
||||
/api/v1/stats/first-notice:
|
||||
get:
|
||||
tags: [Stats]
|
||||
@@ -5127,8 +5137,8 @@ components:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
enum: [bot, provider, persona, im, config, chat, file, plugin, mcp, skill]
|
||||
example: [bot, provider, persona, im, config, chat, file, plugin, mcp, skill]
|
||||
enum: [bot, provider, persona, im, config, chat, data, file, plugin, mcp, skill]
|
||||
example: [bot, provider, persona, im, config, chat, data, file, plugin, mcp, skill]
|
||||
expires_at:
|
||||
type: string
|
||||
format: date-time
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "AstrBot"
|
||||
version = "4.26.0-beta.12"
|
||||
version = "4.26.0"
|
||||
description = "Easy-to-use multi-platform LLM chatbot and development framework"
|
||||
readme = "README.md"
|
||||
license = { text = "AGPL-3.0-or-later" }
|
||||
|
||||
@@ -1248,6 +1248,23 @@ async def test_version_endpoints_use_md5_password_hint(
|
||||
assert _removed_md5_hint_alias_key() not in data["data"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_public_versions_endpoint_does_not_require_auth(app: FastAPIAppAdapter):
|
||||
test_client = app.test_client()
|
||||
|
||||
response = await test_client.get("/api/stat/versions")
|
||||
data = await response.get_json()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert data["status"] == "ok"
|
||||
assert data["data"]["astrbot_version"]
|
||||
assert "webui_version" in data["data"]
|
||||
assert "astrbot_code_version" in data["data"]
|
||||
assert "change_pwd_hint" not in data["data"]
|
||||
assert "md5_pwd_hint" not in data["data"]
|
||||
assert "password_upgrade_required" not in data["data"]
|
||||
|
||||
|
||||
def test_password_hash_lookup_falls_back_to_md5_when_pbkdf2_missing(
|
||||
core_lifecycle_td: AstrBotCoreLifecycle,
|
||||
):
|
||||
@@ -1756,6 +1773,12 @@ async def test_plugin_page_content_issues_scoped_asset_token(
|
||||
css_response = await anonymous_client.get(css_url.group(1))
|
||||
assert css_response.status_code == 200
|
||||
|
||||
stale_cookie_response = await anonymous_client.get(
|
||||
app_js_url.group(1),
|
||||
headers={"Cookie": f"{DASHBOARD_JWT_COOKIE_NAME}=stale.dashboard.token"},
|
||||
)
|
||||
assert stale_cookie_response.status_code == 200
|
||||
|
||||
out_of_scope_response = await anonymous_client.get(
|
||||
f"/api/plugin/get?asset_token={asset_token}"
|
||||
)
|
||||
|
||||
@@ -981,6 +981,40 @@ def _jwt_headers() -> dict[str, str]:
|
||||
return {"Authorization": f"Bearer {token}"}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_public_versions_route_uses_static_folder(
|
||||
fake_core_lifecycle,
|
||||
fake_db: FakeDb,
|
||||
tmp_path: Path,
|
||||
):
|
||||
static_folder = tmp_path / "dist"
|
||||
assets_folder = static_folder / "assets"
|
||||
assets_folder.mkdir(parents=True)
|
||||
(static_folder / "index.html").write_text("<!doctype html>", encoding="utf-8")
|
||||
(assets_folder / "version").write_text("v9.8.7", encoding="utf-8")
|
||||
|
||||
app = create_dashboard_asgi_app(
|
||||
core_lifecycle=fake_core_lifecycle,
|
||||
db=fake_db,
|
||||
jwt_secret=JWT_SECRET,
|
||||
static_folder=str(static_folder),
|
||||
)
|
||||
transport = httpx.ASGITransport(app=app)
|
||||
async with httpx.AsyncClient(
|
||||
transport=transport,
|
||||
base_url="http://testserver",
|
||||
) as client:
|
||||
response = await client.get("/api/v1/stats/versions")
|
||||
|
||||
data = response.json()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert data["status"] == "ok"
|
||||
assert data["data"]["webui_version"] == "v9.8.7"
|
||||
assert data["data"]["astrbot_version"]
|
||||
assert "astrbot_code_version" in data["data"]
|
||||
|
||||
|
||||
def test_fastapi_app_adapter_registers_on_app_state():
|
||||
app = FastAPI()
|
||||
adapter = FastAPIAppAdapter(app)
|
||||
|
||||
Reference in New Issue
Block a user