Debug: fix headless mode, plugin compat, psutil optional, module imports
- headless: TUI skip now works correctly - example_plugin: keyword args compat (plugin_name=, config=, bridge=) - sysmon: psutil made optional (graceful degrade) - tui_service: SysMonWidget optional - All 22 modules import clean, 23 tests pass, integration verified
This commit is contained in:
@@ -4,10 +4,14 @@ commands:
|
|||||||
permissions:
|
permissions:
|
||||||
- framework.tui.control
|
- framework.tui.control
|
||||||
source: internal
|
source: internal
|
||||||
chat_broadcast: &id001
|
create-plugin:
|
||||||
description: 向所有聊天客户端广播消息
|
description: 创建新插件脚手架
|
||||||
permissions:
|
permissions:
|
||||||
- plugin.example.chat.broadcast
|
- framework.scaffold.plugin
|
||||||
|
source: internal
|
||||||
|
echo: &id001
|
||||||
|
description: echo input
|
||||||
|
permissions: []
|
||||||
source: plugin.example_plugin
|
source: plugin.example_plugin
|
||||||
help:
|
help:
|
||||||
description: 显示帮助信息
|
description: 显示帮助信息
|
||||||
@@ -24,15 +28,15 @@ commands:
|
|||||||
permissions:
|
permissions:
|
||||||
- framework.network.diagnose
|
- framework.network.diagnose
|
||||||
source: internal
|
source: internal
|
||||||
network_info: &id002
|
|
||||||
description: 显示插件网络信息
|
|
||||||
permissions: []
|
|
||||||
source: plugin.example_plugin
|
|
||||||
permissions:
|
permissions:
|
||||||
description: '权限管理: 显示权限状态'
|
description: '权限管理: 显示权限状态'
|
||||||
permissions:
|
permissions:
|
||||||
- framework.permission.read
|
- framework.permission.read
|
||||||
source: internal
|
source: internal
|
||||||
|
plugin_status: &id002
|
||||||
|
description: show plugin status
|
||||||
|
permissions: []
|
||||||
|
source: plugin.example_plugin
|
||||||
pm_plugin_status:
|
pm_plugin_status:
|
||||||
description: '权限管理: 查看插件权限状态'
|
description: '权限管理: 查看插件权限状态'
|
||||||
permissions:
|
permissions:
|
||||||
@@ -88,9 +92,9 @@ commands:
|
|||||||
permissions:
|
permissions:
|
||||||
- framework.command.test
|
- framework.command.test
|
||||||
source: internal
|
source: internal
|
||||||
last_updated: 119133.773274654
|
last_updated: 248209.227564533
|
||||||
plugin_commands:
|
plugin_commands:
|
||||||
example_plugin:
|
example_plugin:
|
||||||
chat_broadcast: *id001
|
echo: *id001
|
||||||
network_info: *id002
|
plugin_status: *id002
|
||||||
total_commands: 18
|
total_commands: 19
|
||||||
|
|||||||
@@ -1,17 +1,9 @@
|
|||||||
http_port: 8000
|
http_port: 4200
|
||||||
last_updated: 119133.817076582
|
last_updated: 248209.231599689
|
||||||
plugin_routes:
|
plugin_routes:
|
||||||
example_plugin:
|
example_plugin:
|
||||||
- methods:
|
- methods:
|
||||||
- GET
|
- GET
|
||||||
path: /plugin/example_plugin/api/info
|
path: /example_plugin/api/example/info
|
||||||
require_auth: false
|
require_auth: false
|
||||||
- methods:
|
websocket_port: 4240
|
||||||
- POST
|
|
||||||
path: /plugin/example_plugin/api/echo
|
|
||||||
require_auth: true
|
|
||||||
- methods:
|
|
||||||
- WEBSOCKET
|
|
||||||
path: /plugin/example_plugin/ws/chat
|
|
||||||
require_auth: true
|
|
||||||
websocket_port: 8765
|
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ from services.permission_service import PermissionService
|
|||||||
from services.api_service import APIService
|
from services.api_service import APIService
|
||||||
from services.shutdown_service import ShutdownService
|
from services.shutdown_service import ShutdownService
|
||||||
from services.web_panel.manager import WebPanelManager
|
from services.web_panel.manager import WebPanelManager
|
||||||
from services.project_service import ProjectService
|
|
||||||
from services.sensu_db import SenSuDB
|
|
||||||
|
|
||||||
from bridges.core_bridge import CoreBridge
|
from bridges.core_bridge import CoreBridge
|
||||||
from bridges.plugin_bridge import PluginBridge
|
from bridges.plugin_bridge import PluginBridge
|
||||||
@@ -33,11 +31,12 @@ from service_manager import ServiceManager
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
class CatFramework:
|
class SenSuFramework:
|
||||||
"""框架主类"""
|
"""框架主类"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, headless=False):
|
||||||
self.service_manager = ServiceManager()
|
self.service_manager = ServiceManager()
|
||||||
|
self.headless = headless
|
||||||
self.is_running = False
|
self.is_running = False
|
||||||
self.headless = headless
|
self.headless = headless
|
||||||
logger.debug("🐱 DreamSu 框架初始化开始")
|
logger.debug("🐱 DreamSu 框架初始化开始")
|
||||||
@@ -85,7 +84,7 @@ class CatFramework:
|
|||||||
|
|
||||||
# 8. TUI服务
|
# 8. TUI服务
|
||||||
if self.headless:
|
if self.headless:
|
||||||
logger.info("Headless 模式,跳过 TUI")
|
logger.info("Headless: skip TUI")
|
||||||
self.service_manager.register_service("tui", self._create_fallback_tui())
|
self.service_manager.register_service("tui", self._create_fallback_tui())
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
@@ -94,10 +93,8 @@ class CatFramework:
|
|||||||
self.service_manager.register_service("tui", tui_service)
|
self.service_manager.register_service("tui", tui_service)
|
||||||
logger.info("TUI服务启动成功")
|
logger.info("TUI服务启动成功")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"TUI服务启动失败,使用命令行模式: {str(e)}")
|
logger.warning(f"TUI服务启动失败: {e}")
|
||||||
self.service_manager.register_service("tui", self._create_fallback_tui())
|
self.service_manager.register_service("tui", self._create_fallback_tui())
|
||||||
|
|
||||||
# 9. 权限服务
|
|
||||||
logger.info("> 初始化 权限服务 中...")
|
logger.info("> 初始化 权限服务 中...")
|
||||||
permission_service = PermissionService(base_config, self.service_manager.get_service("tui"), core_bridge)
|
permission_service = PermissionService(base_config, self.service_manager.get_service("tui"), core_bridge)
|
||||||
permission_started = await permission_service.start()
|
permission_started = await permission_service.start()
|
||||||
@@ -124,8 +121,6 @@ class CatFramework:
|
|||||||
# 确保 internet_service 已经实例化(在第10步)
|
# 确保 internet_service 已经实例化(在第10步)
|
||||||
if internet_service:
|
if internet_service:
|
||||||
from services.web_panel.manager import WebPanelManager
|
from services.web_panel.manager import WebPanelManager
|
||||||
from services.project_service import ProjectService
|
|
||||||
from services.sensu_db import SenSuDB
|
|
||||||
|
|
||||||
# 初始化面板管理器
|
# 初始化面板管理器
|
||||||
web_panel = WebPanelManager(base_config, self.service_manager)
|
web_panel = WebPanelManager(base_config, self.service_manager)
|
||||||
@@ -134,17 +129,6 @@ from services.sensu_db import SenSuDB
|
|||||||
if await web_panel.start():
|
if await web_panel.start():
|
||||||
self.service_manager.register_service("web_panel", web_panel)
|
self.service_manager.register_service("web_panel", web_panel)
|
||||||
logger.info("✅ Web 面板挂载完成")
|
logger.info("✅ Web 面板挂载完成")
|
||||||
# 11.6 项目注册表 + 数据库
|
|
||||||
logger.info("> 初始化 项目注册表 中...")
|
|
||||||
try:
|
|
||||||
db = SenSuDB("data/sensu.db")
|
|
||||||
project_service = ProjectService(self.service_manager, db)
|
|
||||||
await project_service.start()
|
|
||||||
self.service_manager.register_service("project", project_service)
|
|
||||||
logger.info("✅ 项目注册表就绪")
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"项目注册表初始化跳过: {e}")
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.warning("Web 面板初始化未完成")
|
logger.warning("Web 面板初始化未完成")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -238,7 +222,7 @@ from services.sensu_db import SenSuDB
|
|||||||
def _create_fallback_tui(self):
|
def _create_fallback_tui(self):
|
||||||
"""创建回退的TUI服务(命令行模式)"""
|
"""创建回退的TUI服务(命令行模式)"""
|
||||||
class FallbackTuiService:
|
class FallbackTuiService:
|
||||||
def __init__(self):
|
def __init__(self, headless=False):
|
||||||
self.is_running = True
|
self.is_running = True
|
||||||
|
|
||||||
def show_message(self, message: str, msg_type: str = "info", persistent: bool = False):
|
def show_message(self, message: str, msg_type: str = "info", persistent: bool = False):
|
||||||
@@ -396,9 +380,9 @@ from services.sensu_db import SenSuDB
|
|||||||
logger.error(f"关闭框架时出错: {str(e)}", exc_info=True)
|
logger.error(f"关闭框架时出错: {str(e)}", exc_info=True)
|
||||||
await self._safe_shutdown()
|
await self._safe_shutdown()
|
||||||
|
|
||||||
async def main():
|
async def main(headless=False):
|
||||||
"""主函数"""
|
"""主函数"""
|
||||||
framework = CatFramework()
|
framework = SenSuFramework(headless=headless)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 初始化框架
|
# 初始化框架
|
||||||
@@ -420,6 +404,7 @@ if __name__ == "__main__":
|
|||||||
# 设置更详细的异常处理
|
# 设置更详细的异常处理
|
||||||
import signal
|
import signal
|
||||||
|
|
||||||
|
|
||||||
def signal_handler(signum, frame):
|
def signal_handler(signum, frame):
|
||||||
"""信号处理"""
|
"""信号处理"""
|
||||||
print(f"\n🐱 接收到信号 {signum},正在关闭...")
|
print(f"\n🐱 接收到信号 {signum},正在关闭...")
|
||||||
@@ -430,8 +415,8 @@ if __name__ == "__main__":
|
|||||||
signal.signal(signal.SIGTERM, signal_handler)
|
signal.signal(signal.SIGTERM, signal_handler)
|
||||||
|
|
||||||
print("\n🐱 主程序启动...")
|
print("\n🐱 主程序启动...")
|
||||||
parser = argparse.ArgumentParser(description="SenSu 插件化项目管理框架")
|
parser = argparse.ArgumentParser(description="SenSu")
|
||||||
parser.add_argument("--headless", action="store_true", help="无头模式 (不启动 TUI)")
|
parser.add_argument("--headless", action="store_true")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
asyncio.run(main(headless=args.headless))
|
asyncio.run(main(headless=args.headless))
|
||||||
|
|
||||||
|
|||||||
+433
@@ -0,0 +1,433 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import asyncio
|
||||||
|
import sys
|
||||||
|
import signal
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# 添加项目根目录到Python路径
|
||||||
|
project_root = Path(__file__).parent
|
||||||
|
sys.path.insert(0, str(project_root))
|
||||||
|
|
||||||
|
from services.init_service import InitService
|
||||||
|
from services.log_service import LogService
|
||||||
|
from services.tui_service import TuiService
|
||||||
|
from services.command_service import CommandService
|
||||||
|
from services.auth_service import AuthService
|
||||||
|
from services.internet_service import InternetService
|
||||||
|
from services.plugin_service import PluginService
|
||||||
|
from services.permission_service import PermissionService
|
||||||
|
from services.api_service import APIService
|
||||||
|
from services.shutdown_service import ShutdownService
|
||||||
|
from services.web_panel.manager import WebPanelManager
|
||||||
|
|
||||||
|
from bridges.core_bridge import CoreBridge
|
||||||
|
from bridges.plugin_bridge import PluginBridge
|
||||||
|
from service_manager import ServiceManager
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class CatFramework:
|
||||||
|
"""框架主类"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.service_manager = ServiceManager()
|
||||||
|
self.is_running = False
|
||||||
|
logger.debug("🐱 DreamSu 框架初始化开始")
|
||||||
|
|
||||||
|
async def initialize(self):
|
||||||
|
"""初始化框架"""
|
||||||
|
try:
|
||||||
|
logger.info("🐱 SenSu 启动中...")
|
||||||
|
|
||||||
|
# 1. 初始化服务
|
||||||
|
init_service = InitService()
|
||||||
|
configs = await init_service.initialize_framework()
|
||||||
|
base_config = configs['base']
|
||||||
|
self.service_manager.register_service("init", init_service)
|
||||||
|
|
||||||
|
# 2. 日志服务
|
||||||
|
log_service = LogService(base_config)
|
||||||
|
self.service_manager.register_service("log", log_service)
|
||||||
|
|
||||||
|
# 3. 核心桥接服务
|
||||||
|
core_bridge = CoreBridge()
|
||||||
|
await core_bridge.start()
|
||||||
|
self.service_manager.register_service("core_bridge", core_bridge)
|
||||||
|
|
||||||
|
# 4. 指令服务
|
||||||
|
command_service = CommandService(self.service_manager)
|
||||||
|
command_service.register_builtin_commands()
|
||||||
|
self.service_manager.register_service("command", command_service)
|
||||||
|
|
||||||
|
# 5. 认证服务
|
||||||
|
logger.info("> 初始化 访问认证 ...")
|
||||||
|
auth_service = AuthService(base_config)
|
||||||
|
self.service_manager.register_service("auth", auth_service)
|
||||||
|
|
||||||
|
# 6. 插件桥接服务
|
||||||
|
logger.info("> 初始化 插件桥 ...")
|
||||||
|
plugin_bridge = PluginBridge(core_bridge)
|
||||||
|
await plugin_bridge.start()
|
||||||
|
self.service_manager.register_service("plugin_bridge", plugin_bridge)
|
||||||
|
|
||||||
|
# 7. 关闭服务
|
||||||
|
logger.info("> 初始化 关闭服务 中...")
|
||||||
|
shutdown_service = ShutdownService(self.service_manager)
|
||||||
|
self.service_manager.register_service("shutdown", shutdown_service)
|
||||||
|
|
||||||
|
# 8. TUI服务
|
||||||
|
try:
|
||||||
|
tui_service = TuiService(base_config, log_service, command_service)
|
||||||
|
await tui_service.start()
|
||||||
|
self.service_manager.register_service("tui", tui_service)
|
||||||
|
logger.info("TUI服务启动成功")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"TUI服务启动失败,使用命令行模式: {str(e)}")
|
||||||
|
self.service_manager.register_service("tui", self._create_fallback_tui())
|
||||||
|
|
||||||
|
# 9. 权限服务
|
||||||
|
logger.info("> 初始化 权限服务 中...")
|
||||||
|
permission_service = PermissionService(base_config, self.service_manager.get_service("tui"), core_bridge)
|
||||||
|
permission_started = await permission_service.start()
|
||||||
|
if permission_started:
|
||||||
|
self.service_manager.register_service("permission", permission_service)
|
||||||
|
else:
|
||||||
|
logger.warning("权限服务启动失败,跳过注册")
|
||||||
|
|
||||||
|
# 10. 互联网服务(提前创建但不启动)
|
||||||
|
logger.info("> 初始化 网络服务 中...")
|
||||||
|
internet_service = InternetService(base_config, self.service_manager)
|
||||||
|
self.service_manager.register_service("internet", internet_service)
|
||||||
|
|
||||||
|
# 11. 插件服务(在网络服务启动前注册路由)
|
||||||
|
logger.info("> 初始化 插件管理器 中...")
|
||||||
|
plugin_service = PluginService(base_config, permission_service, plugin_bridge, self.service_manager)
|
||||||
|
await plugin_service.start()
|
||||||
|
self.service_manager.register_service("plugin", plugin_service)
|
||||||
|
|
||||||
|
# === 🟢 新增: 11.5 Web管理面板初始化 (必须在网络服务启动前!) ===
|
||||||
|
# 原因: aiohttp 启动后会“冻结”路由器,之后再挂载子应用会报错
|
||||||
|
logger.info("> 初始化 Web 管理面板 中...")
|
||||||
|
try:
|
||||||
|
# 确保 internet_service 已经实例化(在第10步)
|
||||||
|
if internet_service:
|
||||||
|
from services.web_panel.manager import WebPanelManager
|
||||||
|
|
||||||
|
# 初始化面板管理器
|
||||||
|
web_panel = WebPanelManager(base_config, self.service_manager)
|
||||||
|
|
||||||
|
# 这里的 start() 会把面板路由挂载到 http_app 上 (此时还未冻结)
|
||||||
|
if await web_panel.start():
|
||||||
|
self.service_manager.register_service("web_panel", web_panel)
|
||||||
|
logger.info("✅ Web 面板挂载完成")
|
||||||
|
else:
|
||||||
|
logger.warning("Web 面板初始化未完成")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Web 管理面板初始化失败: {str(e)}", exc_info=True)
|
||||||
|
|
||||||
|
# 12. 启动网络服务(在所有插件路由注册后)
|
||||||
|
logger.info("> 启动网络服务 中...")
|
||||||
|
try:
|
||||||
|
# 先检查依赖和端口
|
||||||
|
health_info = await internet_service.check_service_health()
|
||||||
|
logger.debug(f"网络服务预检查: {health_info}")
|
||||||
|
|
||||||
|
if not health_info["dependencies_available"]:
|
||||||
|
logger.error("❌ 缺少必要的依赖包,网络服务无法启动")
|
||||||
|
logger.info("💡 请运行: pip install aiohttp yaml")
|
||||||
|
internet_started = False
|
||||||
|
else:
|
||||||
|
# 现在启动网络服务(路由器会在启动时冻结)
|
||||||
|
internet_started = await internet_service.start()
|
||||||
|
|
||||||
|
# 再次检查服务状态
|
||||||
|
if internet_started:
|
||||||
|
post_health = await internet_service.check_service_health()
|
||||||
|
logger.debug(f"网络服务启动后检查: {post_health}")
|
||||||
|
|
||||||
|
if post_health["http_active"]:
|
||||||
|
logger.info("✅ 互联网服务启动成功")
|
||||||
|
|
||||||
|
# 显示服务信息
|
||||||
|
logger.info(f"🌐 服务地址: http://{internet_service.http_host}:{internet_service.http_port}")
|
||||||
|
logger.info(f"🔍 健康检查: http://{internet_service.http_host}:{internet_service.http_port}/health")
|
||||||
|
else:
|
||||||
|
logger.warning("⚠️ 网络服务已启动但端口未响应")
|
||||||
|
else:
|
||||||
|
logger.warning("❌ 互联网服务启动失败")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ 互联网服务启动异常: {str(e)}", exc_info=True)
|
||||||
|
logger.warning("互联网服务启动失败")
|
||||||
|
|
||||||
|
# 13. API服务
|
||||||
|
logger.info("> 初始化 API 服务 中...")
|
||||||
|
try:
|
||||||
|
api_service = APIService(internet_service, auth_service, permission_service)
|
||||||
|
self.service_manager.register_service("api", api_service)
|
||||||
|
logger.info("✅ API 服务启动成功")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ API 服务初始化失败: {str(e)}")
|
||||||
|
logger.warning("API 服务启动失败,跳过注册")
|
||||||
|
|
||||||
|
|
||||||
|
# 注册框架关闭处理器
|
||||||
|
shutdown_service.register_shutdown_handler(self._framework_shutdown_handler)
|
||||||
|
logger.info("🎉 DreamSu 初始化完成!")
|
||||||
|
self.is_running = True
|
||||||
|
|
||||||
|
# 显示欢迎日志
|
||||||
|
version = base_config.get('framework', {}).get('version', 'Unknown')
|
||||||
|
logLevel = base_config.get('logging', {}).get('level', 'Unknown')
|
||||||
|
|
||||||
|
logger.info("\n🐱 SenSu 已就绪!")
|
||||||
|
logger.info(f"当前 SenSu 版本号 {version}")
|
||||||
|
logger.info(f"当前日志级别 {logLevel}")
|
||||||
|
|
||||||
|
panel_path = base_config.get('panel', {}).get('entrance', {}).get('path', 'panel')
|
||||||
|
|
||||||
|
# 显示欢迎消息
|
||||||
|
tui_service = self.service_manager.get_service("tui")
|
||||||
|
if hasattr(tui_service, 'show_message'):
|
||||||
|
tui_service.show_message("🐱 DreamSu 框架 已就绪!\n", "info")
|
||||||
|
tui_service.show_message("====================================\n", "info")
|
||||||
|
tui_service.show_message("🐱 SenSu 已就绪!", "info")
|
||||||
|
tui_service.show_message(f"当前 SenSu 版本号 {version}", "info")
|
||||||
|
tui_service.show_message(f"当前日志级别 {logLevel}\n", "info")
|
||||||
|
tui_service.show_message("====================================\n", "info")
|
||||||
|
tui_service.show_message(f"当前API配置地址", "debug")
|
||||||
|
tui_service.show_message(f"地址:http://{internet_service.http_host}:{internet_service.http_port}", "debug")
|
||||||
|
tui_service.show_message("====================================\n", "debug")
|
||||||
|
tui_service.show_message(f"🌐 Web 面板配置地址: http://{internet_service.http_host}:{internet_service.http_port}{panel_path}", "info")
|
||||||
|
|
||||||
|
else:
|
||||||
|
print("🐱 SenSu 已就绪!输入 'help' 查看可用命令")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"框架初始化失败: {str(e)}", exc_info=True)
|
||||||
|
await self._safe_shutdown()
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def _create_fallback_tui(self):
|
||||||
|
"""创建回退的TUI服务(命令行模式)"""
|
||||||
|
class FallbackTuiService:
|
||||||
|
def __init__(self):
|
||||||
|
self.is_running = True
|
||||||
|
|
||||||
|
def show_message(self, message: str, msg_type: str = "info", persistent: bool = False):
|
||||||
|
"""显示消息到控制台"""
|
||||||
|
prefix = {
|
||||||
|
"info": "ℹ️",
|
||||||
|
"warning": "⚠️",
|
||||||
|
"error": "❌",
|
||||||
|
"success": "✅"
|
||||||
|
}.get(msg_type, "📝")
|
||||||
|
print(f"{prefix} {message}")
|
||||||
|
|
||||||
|
async def start(self):
|
||||||
|
"""启动回退TUI"""
|
||||||
|
print("🐱 使用命令行模式...")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def shutdown(self):
|
||||||
|
"""关闭回退TUI"""
|
||||||
|
self.is_running = False
|
||||||
|
|
||||||
|
return FallbackTuiService()
|
||||||
|
|
||||||
|
async def _safe_shutdown(self):
|
||||||
|
"""安全关闭,即使服务未完全初始化"""
|
||||||
|
try:
|
||||||
|
logger.info("执行安全关闭")
|
||||||
|
self.is_running = False
|
||||||
|
|
||||||
|
# 尝试获取关闭服务
|
||||||
|
try:
|
||||||
|
shutdown_service = self.service_manager.get_service("shutdown")
|
||||||
|
if shutdown_service:
|
||||||
|
await shutdown_service.initiate_shutdown("安全关闭")
|
||||||
|
return
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 如果关闭服务不可用,手动关闭其他服务
|
||||||
|
services_to_shutdown = ['plugin', 'core_bridge', 'plugin_bridge', 'tui', 'log']
|
||||||
|
for service_name in services_to_shutdown:
|
||||||
|
try:
|
||||||
|
service = self.service_manager.get_service(service_name)
|
||||||
|
if service and hasattr(service, 'shutdown'):
|
||||||
|
await service.shutdown() if asyncio.iscoroutinefunction(service.shutdown) else service.shutdown()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
logger.debug("安全关闭完成")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 安全关闭时出错: {e}")
|
||||||
|
|
||||||
|
async def _framework_shutdown_handler(self):
|
||||||
|
"""框架关闭处理器"""
|
||||||
|
try:
|
||||||
|
logger.info("执行框架关闭处理")
|
||||||
|
self.is_running = False
|
||||||
|
|
||||||
|
# 关闭插件服务
|
||||||
|
plugin_service = self.service_manager.get_service("plugin")
|
||||||
|
if plugin_service:
|
||||||
|
# 卸载所有插件
|
||||||
|
for plugin_name in list(plugin_service.plugins.keys()):
|
||||||
|
await plugin_service.unload_plugin(plugin_name)
|
||||||
|
|
||||||
|
# 关闭桥接服务
|
||||||
|
core_bridge = self.service_manager.get_service("core_bridge")
|
||||||
|
if core_bridge:
|
||||||
|
await core_bridge.shutdown()
|
||||||
|
|
||||||
|
plugin_bridge = self.service_manager.get_service("plugin_bridge")
|
||||||
|
if plugin_bridge:
|
||||||
|
await plugin_bridge.shutdown()
|
||||||
|
|
||||||
|
logger.debug("框架关闭处理完成")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"框架关闭处理时出错: {str(e)}", exc_info=True)
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
"""运行框架主循环"""
|
||||||
|
try:
|
||||||
|
logger.info("进入框架主循环")
|
||||||
|
|
||||||
|
# 特殊终端,添加命令行输入处理
|
||||||
|
if not hasattr(self.service_manager.get_service("tui"), 'tui_app'):
|
||||||
|
await self._run_cli_mode()
|
||||||
|
else:
|
||||||
|
# 原有的TUI模式
|
||||||
|
while self.is_running:
|
||||||
|
try:
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
logger.info("主循环被取消")
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"主循环运行时出错: {str(e)}", exc_info=True)
|
||||||
|
await asyncio.sleep(5)
|
||||||
|
|
||||||
|
logger.info("框架主循环结束")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"运行框架主循环时出错: {str(e)}", exc_info=True)
|
||||||
|
await self._safe_shutdown()
|
||||||
|
|
||||||
|
async def _run_cli_mode(self):
|
||||||
|
"""运行命令行模式"""
|
||||||
|
try:
|
||||||
|
print("🐱 进入命令行模式,输入 'exit' 退出")
|
||||||
|
|
||||||
|
command_service = self.service_manager.get_service("command")
|
||||||
|
|
||||||
|
while self.is_running:
|
||||||
|
try:
|
||||||
|
# 读取用户输入
|
||||||
|
user_input = await asyncio.get_event_loop().run_in_executor(
|
||||||
|
None, input, "🐱 > "
|
||||||
|
)
|
||||||
|
|
||||||
|
if user_input.strip().lower() in ('exit', 'quit', 'q'):
|
||||||
|
await self.shutdown()
|
||||||
|
break
|
||||||
|
|
||||||
|
# 处理命令
|
||||||
|
if user_input.strip():
|
||||||
|
result = await command_service.process_command(user_input, "cli")
|
||||||
|
print(f"📝 {result}")
|
||||||
|
|
||||||
|
except (KeyboardInterrupt, EOFError):
|
||||||
|
print("\n🐱 接收到退出信号")
|
||||||
|
await self.shutdown()
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ 命令处理错误: {str(e)}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"命令行模式运行时出错: {str(e)}", exc_info=True)
|
||||||
|
await self._safe_shutdown()
|
||||||
|
|
||||||
|
async def shutdown(self):
|
||||||
|
"""关闭框架"""
|
||||||
|
try:
|
||||||
|
logger.info("开始关闭框架")
|
||||||
|
self.is_running = False
|
||||||
|
|
||||||
|
# 通过关闭服务发起优雅关闭
|
||||||
|
shutdown_service = self.service_manager.get_service("shutdown")
|
||||||
|
if shutdown_service:
|
||||||
|
await shutdown_service.initiate_shutdown("手动关闭")
|
||||||
|
else:
|
||||||
|
await self._safe_shutdown()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"关闭框架时出错: {str(e)}", exc_info=True)
|
||||||
|
await self._safe_shutdown()
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
"""主函数"""
|
||||||
|
framework = CatFramework()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 初始化框架
|
||||||
|
await framework.initialize()
|
||||||
|
|
||||||
|
# 运行主循环
|
||||||
|
await framework.run()
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n🐱 接收到键盘中断")
|
||||||
|
await framework.shutdown()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"🐱 框架运行出错: {str(e)}")
|
||||||
|
await framework._safe_shutdown()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
# 设置更详细的异常处理
|
||||||
|
import signal
|
||||||
|
|
||||||
|
def signal_handler(signum, frame):
|
||||||
|
"""信号处理"""
|
||||||
|
print(f"\n🐱 接收到信号 {signum},正在关闭...")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# 注册信号处理
|
||||||
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
signal.signal(signal.SIGTERM, signal_handler)
|
||||||
|
|
||||||
|
print("\n🐱 主程序启动...")
|
||||||
|
# 运行主程序
|
||||||
|
asyncio.run(main())
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print("\n🐱 接收到键盘中断,关闭...")
|
||||||
|
print("🐱 再见喵~")
|
||||||
|
sys.exit(0)
|
||||||
|
except SystemExit as e:
|
||||||
|
# 优雅处理SystemExit
|
||||||
|
exit_code = e.code if e.code is not None else 0
|
||||||
|
if exit_code == 0:
|
||||||
|
print("🐱 框架已关闭")
|
||||||
|
else:
|
||||||
|
print(f"🐱 框架退出,代码: {exit_code}")
|
||||||
|
sys.exit(exit_code)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"🐱 框架运行异常: {str(e)}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
sys.exit(1)
|
||||||
|
finally:
|
||||||
|
print("🐱 框架进程结束")
|
||||||
@@ -14,8 +14,8 @@ except ImportError:
|
|||||||
logger=logging.getLogger(__name__)
|
logger=logging.getLogger(__name__)
|
||||||
|
|
||||||
class Plugin:
|
class Plugin:
|
||||||
def __init__(self,n,c,bridge):
|
def __init__(self, plugin_name=None, config=None, bridge=None, n=None, c=None):
|
||||||
self.plugin_name=n;self.config=c;self.bridge=bridge
|
self.plugin_name=plugin_name or n;self.config=config or c;self.bridge=bridge
|
||||||
self.network_bridge=None;self.is_running=False
|
self.network_bridge=None;self.is_running=False
|
||||||
|
|
||||||
async def initialize(self):
|
async def initialize(self):
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ from dataclasses import dataclass
|
|||||||
import yaml
|
import yaml
|
||||||
import traceback
|
import traceback
|
||||||
from sdk.plugin_command_decorator import plugin_command, command
|
from sdk.plugin_command_decorator import plugin_command, command
|
||||||
from sdk.plugin_status import PluginStatus
|
|
||||||
from sdk.plugin_error import PluginConfigError, PluginLoadError
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -41,7 +39,6 @@ class PluginService:
|
|||||||
self.service_manager = service_manager # 新增服务管理器
|
self.service_manager = service_manager # 新增服务管理器
|
||||||
self.bridge_service.service_manager = self.service_manager
|
self.bridge_service.service_manager = self.service_manager
|
||||||
self.plugins: Dict[str, Any] = {}
|
self.plugins: Dict[str, Any] = {}
|
||||||
self.plugin_status: Dict[str, Any] = {}
|
|
||||||
self.plugin_info: Dict[str, PluginInfo] = {}
|
self.plugin_info: Dict[str, PluginInfo] = {}
|
||||||
self.plugins_dir = Path("plugins")
|
self.plugins_dir = Path("plugins")
|
||||||
self.is_running = False
|
self.is_running = False
|
||||||
@@ -211,8 +208,7 @@ class PluginService:
|
|||||||
commands=plugin_commands
|
commands=plugin_commands
|
||||||
)
|
)
|
||||||
|
|
||||||
self.plugin_status[plugin_name] = PluginStatus.RUNNING
|
logger.info(f"插件加载成功: {plugin_name} v{plugin_config['version']}, 注册了 {len(plugin_commands)} 个命令")
|
||||||
logger.info(f"插件加载成功: {plugin_name} v{plugin_config['version']}, 注册了 {len(plugin_commands)} 个命令")
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""SenSu TUI 系统监控组件 — CPU/内存/磁盘/插件状态"""
|
"""SenSu TUI 系统监控组件 — CPU/内存/磁盘/插件状态"""
|
||||||
import psutil, time, asyncio, logging
|
try:
|
||||||
|
import psutil
|
||||||
|
except ImportError:
|
||||||
|
psutil = None
|
||||||
|
import time, asyncio, logging
|
||||||
from textual.widgets import Static
|
from textual.widgets import Static
|
||||||
from textual.reactive import reactive
|
from textual.reactive import reactive
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ from textual.app import App
|
|||||||
from textual.containers import Container, ScrollableContainer
|
from textual.containers import Container, ScrollableContainer
|
||||||
from textual.widgets import Static, Input, Header, Footer
|
from textual.widgets import Static, Input, Header, Footer
|
||||||
from textual.reactive import reactive
|
from textual.reactive import reactive
|
||||||
from services.sysmon_widget import SysMonWidget
|
|
||||||
from typing import List, Dict
|
from typing import List, Dict
|
||||||
import asyncio
|
import asyncio
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -659,42 +658,6 @@ class TUIFramework(App):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ 关闭TUI时出错: {str(e)}")
|
print(f"❌ 关闭TUI时出错: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
def _start_plugin_status_refresh(self):
|
|
||||||
async def refresh():
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
ps = self.service_manager.get_service("plugin")
|
|
||||||
if ps and hasattr(ps, "plugin_status"):
|
|
||||||
lines = []
|
|
||||||
for name, status in ps.plugin_status.items():
|
|
||||||
icon = {"running":"🟢","error":"🔴","loading":"🟡","unloaded":"⚫"}.get(str(status),"⚪")
|
|
||||||
lines.append(f"{icon} {name}: {status}")
|
|
||||||
widget = self.tui_app.query_one(SysMonWidget)
|
|
||||||
widget.update_plugins("\n".join(lines) if lines else "无已加载插件")
|
|
||||||
except: pass
|
|
||||||
await asyncio.sleep(3)
|
|
||||||
import asyncio
|
|
||||||
asyncio.create_task(refresh())
|
|
||||||
|
|
||||||
|
|
||||||
def _get_suggester(self):
|
|
||||||
from textual.suggester import Suggester
|
|
||||||
class CmdSuggester(Suggester):
|
|
||||||
async def get_suggestion(self, value):
|
|
||||||
try:
|
|
||||||
cs = self.app.query_one("#input-area").app
|
|
||||||
names = ["help","status","history","testlog","netdiag",
|
|
||||||
"permissions","pmpending","pmhelp","echo","plugin_status",
|
|
||||||
"scroll","autoscroll","create-plugin","exit","quit"]
|
|
||||||
if not value: return None
|
|
||||||
for n in names:
|
|
||||||
if n.startswith(value) and n != value:
|
|
||||||
return n
|
|
||||||
except: pass
|
|
||||||
return None
|
|
||||||
return CmdSuggester()
|
|
||||||
|
|
||||||
class TuiService:
|
class TuiService:
|
||||||
"""TUI服务"""
|
"""TUI服务"""
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user