feat: 管理控制台(JLine)与内置命令

- core.admin.AdminView:在线玩家(地址、TCP/WebSocket、所在服务器、在线时长)、服务器
  (地址、人数、来源)与状态(版本、运行时长、传输层、监听地址、堆内存、最近 10 分钟活动)
  的只读快照;不从控制台线程直接读取会话状态
- Session 在发布玩家时记录连接详情;ProxySessionListener.onlinePlayers()
- ConnectionLog 用 RecentCounter(10 个一分钟桶)统计加入、离开、拒绝、转服与失败;
  连接数上限与插件池拒绝计入过载
- app.console:Console 在终端中使用 JLine(提示符固定在底部、历史、Tab 补全),否则按行读取
  标准输入;ConsoleLogAppender 让日志打印在提示符上方并为警告/错误着色
- 内置命令 help/list/servers/player/status/send/kick/loglevel/stop,先于插件控制台命令;
  同名插件命令在启动时警告;send/kick/loglevel/stop 写入 moonbridge.admin
- 依赖:JLine 3.30.17(reader、terminal、FFM 提供者),logback 改为编译期依赖
- 文档:operations.md 控制台命令、logging.md、CLAUDE.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
layue13andClaude Opus 5.5 committed 2026-10-22 21:59:09 +08:00
1 parent 1d712ac5c6
commit 284c2f81cf
27 files changed
+1044 -69

No files matched your search

+2
View File
@@ -61,6 +61,8 @@ testing/smoke/ smoke scripts and dated records; testing/benchmarks/ likewis
**Operator logs.** docs/logging.md is the policy: INFO is one line per event an operator asks about, WARN is something to act on, protocol steps stay at DEBUG. Player and server events go through `core.log.ConnectionLog` (loggers `moonbridge.players` / `moonbridge.servers`), which cleans peer text with `LogText` and honours `logging.playerAddresses`; floods use `ThrottledWarning`. Sessions record why they ended with `endBecause` (first reason wins) and log it in `closePair`.
**Admin console.** `app.console.Console` reads the console with JLine in a terminal (prompt kept at the bottom by `ConsoleLogAppender`, which logback.xml uses for console output) or plain stdin otherwise, and sends each line to `BuiltinCommands` before plugin console commands. Commands read `core.admin.AdminView`, immutable snapshots built from `ProxySessionListener.onlinePlayers()`, the catalog and `ConnectionLog.activity()` (ten one-minute `RecentCounter` buckets), and act through `AdminActions`; never read session state from the console thread directly.
**Backends and plugins.** Backends come from static config, plugin registration (`Servers.register`, owner-scoped handles with generations) or the optional control channel (`BackendControlService`: separate authenticated blocking sockets on virtual threads, leases that outlive short disconnects). Initial routing is fail-closed: with no placement plugin it uses only `initialRouting.servers`. Permissions are a provider interface (`PermissionService`) with per-connection subjects that are prepared before login admission and released on any exit.
## Testing notes