Files
xiaoli-cli/platform_utils.py
T
xiaoli-cli 97cc2bb0f7 feat: 跨平台进程保护机制 v5.3.0
- 新增 process_protection.py 模块
  - 单实例保护: Windows命名Mutex / macOS+Linux fcntl文件锁
  - 进程优先级提升: Windows SetPriorityClass / POSIX os.nice()
  - Windows PPL风格保护: SetErrorMode抑制错误弹窗
  - POSIX信号保护: 忽略SIGHUP/SIGPIPE, 注册SIGTERM优雅关闭
  - 看门狗监控: 后台线程监控父进程状态
  - 关闭钩子系统: 进程退出时自动执行清理
  - /protect 命令查看保护状态
- 更新 platform_utils.py 新增进程工具函数
- 集成到 cli_base.py 启动时自动初始化
- 更新 CHANGELOG.md / README.md 至 v5.3.0
2026-05-09 20:28:17 +08:00

200 lines
5.6 KiB
Python

"""
跨平台工具模块
提供跨平台的兼容性支持
"""
import os
import sys
def get_platform():
"""获取当前平台类型"""
if os.name == 'nt':
return 'windows'
elif sys.platform == 'darwin':
return 'macos'
else:
return 'linux'
def get_hidden_input(prompt="请输入密码: "):
"""跨平台的隐藏输入"""
platform = get_platform()
if platform == 'windows':
try:
import msvcrt
print(f"{prompt} ", end='', flush=True)
password = ""
while True:
char = msvcrt.getch()
if char in [b'\r', b'\n']:
print()
break
elif char == b'\x08':
if len(password) > 0:
password = password[:-1]
print('\b \b', end='', flush=True)
else:
password += char.decode('utf-8')
print('*', end='', flush=True)
return password
except ImportError:
import getpass
return getpass.getpass(prompt)
else:
import getpass
return getpass.getpass(prompt)
def check_key_input():
"""检查是否有键盘输入(跨平台)"""
platform = get_platform()
if platform == 'windows':
try:
import msvcrt
return msvcrt.kbhit() > 0
except ImportError:
return False
else:
# Linux/Mac 的实现较为复杂,暂时返回 False
return False
def get_key():
"""获取按键(跨平台)"""
platform = get_platform()
if platform == 'windows':
try:
import msvcrt
if msvcrt.kbhit():
return msvcrt.getch()
return None
except ImportError:
return None
else:
# Linux/Mac 的实现
import select
import tty
import termios
if select.select([sys.stdin], [], [], 0)[0]:
return sys.stdin.read(1)
return None
def supports_esc_detection():
"""检查是否支持 ESC 键检测"""
platform = get_platform()
return platform == 'windows' # 目前只有 Windows 完全支持 ESC 检测
def get_user_directory():
"""获取用户目录(跨平台)"""
platform = get_platform()
if platform == 'windows':
return os.path.expanduser("~")
elif platform == 'macos':
return os.path.expanduser("~")
else: # Linux
return os.path.expanduser("~")
def get_desktop_directory():
"""获取桌面目录(跨平台)"""
platform = get_platform()
if platform == 'windows':
return os.path.join(os.path.expanduser("~"), "Desktop")
elif platform == 'macos':
return os.path.join(os.path.expanduser("~"), "Desktop")
else: # Linux
desktop = os.path.join(os.path.expanduser("~"), "Desktop")
if os.path.exists(desktop):
return desktop
else:
return os.path.expanduser("~")
def get_config_directory():
"""获取应用配置目录(跨平台)"""
platform = get_platform()
if platform == 'windows':
return os.path.join(os.path.getenv('APPDATA', ''), 'Xiaoli Pro-CLI 3')
elif platform == 'macos':
return os.path.join(os.path.expanduser("~"), 'Library', 'Application Support', 'Xiaoli Pro-CLI 3')
else: # Linux
return os.path.join(os.path.expanduser('~'), '.config', 'xiaoli-pro-cli-3')
def is_process_running(pid):
"""检查指定 PID 的进程是否在运行"""
platform = get_platform()
try:
if platform == 'windows':
import ctypes
kernel32 = ctypes.windll.kernel32
handle = kernel32.OpenProcess(0x1000, False, pid) # PROCESS_QUERY_LIMITED_INFORMATION
if handle:
kernel32.CloseHandle(handle)
return True
return False
else:
os.kill(pid, 0)
return True
except (OSError, ProcessLookupError):
return False
def get_process_priority():
"""获取当前进程优先级"""
platform = get_platform()
try:
if platform == 'windows':
import ctypes
kernel32 = ctypes.windll.kernel32
handle = kernel32.GetCurrentProcess()
return kernel32.GetPriorityClass(handle)
else:
import resource
return resource.getpriority(resource.PRIO_PROCESS, 0)
except Exception:
return None
def set_process_priority(level="above_normal"):
"""
设置进程优先级
Args:
level: "below_normal" | "normal" | "above_normal" | "high"
Returns:
是否设置成功
"""
platform = get_platform()
try:
if platform == 'windows':
import ctypes
kernel32 = ctypes.windll.kernel32
priority_map = {
"below_normal": 0x00004000,
"normal": 0x00000020,
"above_normal": 0x00008000,
"high": 0x00000080,
}
priority = priority_map.get(level, 0x00008000)
handle = kernel32.GetCurrentProcess()
return bool(kernel32.SetPriorityClass(handle, priority))
else:
nice_map = {
"below_normal": 10,
"normal": 0,
"above_normal": -10,
"high": -15,
}
nice_val = nice_map.get(level, -10)
os.nice(nice_val)
return True
except Exception:
return False