ykxmz 1.5.0:修复手表端 TLS 证书校验 + MTU 修复网络稳定性

被控端
- 修复 MTU 问题:夹 MSS + 路径 MTU 探测(公网可用率 48% -> 100%)
- 复用 HTTP 连接(免掉每次新建 TLS 的 30% 握手失败)
- 每分钟回头找局域网,自动切回快 400 倍的路径
- 画面心跳帧:静止时省流量,客户端仍知道流是活的
- 锁屏时给出可操作的提示

手表端
- 内置 GTS Root R4 / GlobalSign Root CA,修 SSLHandshakeException
- 连接错误翻译成人话(证书/时间/解析/超时分别提示)
- 经中转连上后用服务器报的地址刷新已配对记录
- 新增局域网连接页面、服务器延迟检测

服务器端
- 支持 chunked 请求体(修每 16 秒一次的连接错乱与大面积 502)
- 网络调优脚本 net-tune.sh(开机自动应用)
- 管理页:批量删除、网络体检、下载区
- 隧道改 HTTP/2 + 双连接

文档
- 许可协议 / 安装前必读 / 安装后须知(作者:小麦子)
This commit is contained in:
xmz7891
2026-10-06 19:01:05 +08:00
commit b62ac865ea
3195 changed files with 203292 additions and 0 deletions
+179
View File
@@ -0,0 +1,179 @@
# -*- coding: utf-8 -*-
"""收尾三个测试问题。
1. test_image_modes.py
主线程直接调 Tk(winfo_screenwidth / update)会报
"main thread is not in main loop"。所有 Tk 访问都要走 ui_call。
2. test_selfcheck.py
被控端现在跑静默模式,日志写在 data\\silent.log,不再是 运行日志.txt。
3. test_pair_once_flow.py
feature_list 拿不到内容,先打印真实回复再看。
"""
from __future__ import annotations
import sys
from pathlib import Path
TESTS = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\_tests")
# ---------------- 1. test_image_modes ----------------
IMAGE = TESTS / "test_image_modes.py"
GEO_OLD = '''def wait_geometry(win, root, timeout: float = 4.0):
"""轮询直到窗口几何可用(同时泵事件)。"""
deadline = time.time() + timeout
while time.time() < deadline:
try:
root.update()
except tk.TclError:
return None
rect = geometry_of(win)
if rect:
return rect
time.sleep(0.04)
return None'''
GEO_NEW = '''def ui_read(agent, fn, timeout: float = 4.0):
"""所有 Tk 调用都要走界面线程,否则报 main thread is not in main loop。"""
try:
return agent.ui_call(fn, timeout=timeout)
except Exception: # noqa: BLE001
return None
def wait_geometry(win, agent, timeout: float = 4.0):
"""轮询直到窗口几何可用。"""
if win is None:
return None
deadline = time.time() + timeout
while time.time() < deadline:
rect = ui_read(agent, lambda: _read_geometry(win), timeout=3.0)
if rect:
return rect
time.sleep(0.05)
return None
def _read_geometry(win):
try:
if not win.winfo_exists():
return None
win.update_idletasks()
left = win.winfo_rootx()
top = win.winfo_rooty()
width = win.winfo_width()
height = win.winfo_height()
if width <= 1 or height <= 1:
return None
return (left, top, width, height)
except tk.TclError:
return None'''
SCREEN_OLD = ''' screen_w = agent.root.winfo_screenwidth()
screen_h = agent.root.winfo_screenheight()'''
SCREEN_NEW = ''' screen_w, screen_h = agent.screen_size() # 不直接碰 Tk'''
ROOT_OLD = ''' actions = Actions(agent, store)
root = agent.root'''
ROOT_NEW = ''' actions = Actions(agent, store)'''
# 所有 wait_geometry(win, root) -> wait_geometry(win, agent)
CALL_OLD = "wait_geometry(actions._image_window, root)"
CALL_NEW = "wait_geometry(actions._image_window, agent)"
def patch_image() -> bool:
text = IMAGE.read_text(encoding="utf-8")
ok = True
for old, new, tag in ((GEO_OLD, GEO_NEW, "wait_geometry 走界面线程"),
(SCREEN_OLD, SCREEN_NEW, "屏幕尺寸不碰 Tk"),
(ROOT_OLD, ROOT_NEW, "去掉 root 变量")):
if old not in text:
print(f" [跳过] image / {tag} —— 锚点没找到")
ok = False
continue
text = text.replace(old, new, 1)
print(f" image / {tag}: OK")
count = text.count(CALL_OLD)
text = text.replace(CALL_OLD, CALL_NEW)
print(f" image / wait_geometry 调用点改了 {count} 处")
IMAGE.write_text(text, encoding="utf-8")
return ok
# ---------------- 2. test_selfcheck ----------------
SELF = TESTS / "test_selfcheck.py"
LOG_OLD = ''' log_path = data_dir / "运行日志.txt"
log_text = log_path.read_text(encoding="utf-8") if log_path.is_file() else ""'''
LOG_NEW = ''' # 被控端现在跑静默模式,日志在 silent.log;旧版是 运行日志.txt
log_text = ""
for name in ("silent.log", "运行日志.txt"):
log_path = data_dir / name
if log_path.is_file():
try:
log_text += log_path.read_text(encoding="utf-8",
errors="replace")
except OSError:
pass'''
def patch_selfcheck() -> bool:
text = SELF.read_text(encoding="utf-8")
if LOG_OLD not in text:
print(" [跳过] selfcheck / 日志路径 —— 锚点没找到")
return False
SELF.write_text(text.replace(LOG_OLD, LOG_NEW, 1), encoding="utf-8")
print(" selfcheck / 日志路径: OK")
return True
# ---------------- 3. test_pair_once_flow 加调试 ----------------
FLOW = TESTS / "test_pair_once_flow.py"
FLOW_OLD = ''' reply = talk.call({"cmd": "feature_list"}, timeout=25)
features = reply.get("features") or []
check("已配对设备能拿到功能列表", reply.get("ok") is True and len(features) > 100,
f"{len(features)} 个")'''
FLOW_NEW = ''' reply = talk.call({"cmd": "feature_list"}, timeout=25)
features = reply.get("features") or []
if not features or reply.get("ok") is not True:
print(f" (feature_list 原始回复: {str(reply)[:300]})")
check("已配对设备能拿到功能列表", reply.get("ok") is True and len(features) > 100,
f"{len(features)} 个, ok={reply.get('ok')}, "
f"err={reply.get('error')}")'''
def patch_flow() -> bool:
text = FLOW.read_text(encoding="utf-8")
if FLOW_OLD not in text:
print(" [跳过] flow / 调试输出 —— 锚点没找到")
return False
FLOW.write_text(text.replace(FLOW_OLD, FLOW_NEW, 1), encoding="utf-8")
print(" flow / 调试输出: OK")
return True
def main() -> int:
ok = patch_image()
print()
ok = patch_selfcheck() and ok
print()
ok = patch_flow() and ok
import ast
for path in (IMAGE, SELF, FLOW):
try:
ast.parse(path.read_text(encoding="utf-8"))
except SyntaxError as exc:
print(f" {path.name}: 语法错误 {exc}")
ok = False
print(" 语法检查完成")
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())