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
+210
View File
@@ -0,0 +1,210 @@
# -*- coding: utf-8 -*-
"""首次运行设置窗口。
流程:
装完第一次启动 → 弹一个窗口,让你自己定一个配对码
(顺便可以勾"以后开机静默运行")
→ 之后**再也不会有任何窗口**,手表输这个码就能连
为什么是"你自己定"而不是"程序里写死":
写死意味着每台装这软件的被控端都是同一个码,而且会打进手表 APK。
自己定的话,这个码只存在你这台机器的 data\\settings.json 里。
"""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\windows"
r"\lanremote")
UI = ROOT / "ui.py"
RUN = ROOT.parent / "run_agent.py"
# ---------------- ui.py:设置窗口 ----------------
ANCHOR = ''' def run(self) -> None:
from .actions import Actions
'''
NEW = ''' # ------------------------------------------------------------------
# 首次运行:设置配对码
# ------------------------------------------------------------------
def setup_needed(self) -> bool:
"""是不是还没设置过配对码。"""
return not bool(self.store.get("setup_done", False))
def open_first_run_setup(self) -> bool:
"""第一次使用时的设置窗口。返回是否设置成功。
这个窗口**只出现一次** —— 设完之后程序再也不弹任何窗口,
手表用这个码连上就完事了。
"""
win = tk.Toplevel(self.root)
win.title("第一次使用 · 设置配对码")
win.configure(bg=BG)
win.resizable(False, False)
result = {"ok": False}
def refuse_close():
# 必须设一个码才能继续,否则手表连不上
self.show_message("请先设置一个配对码") if hasattr(self, "show_message") else None
win.protocol("WM_DELETE_WINDOW", refuse_close)
header = tk.Frame(win, bg=ACCENT, height=6)
header.pack(fill="x")
tk.Label(win, text="设置你的配对码", bg=BG, fg=FG,
font=("Microsoft YaHei UI", 15, "bold")).pack(
anchor="w", padx=18, pady=(16, 4))
tk.Label(win,
text="设一个只有你知道的码,手表上输这个就能连。\\n"
"设完之后程序就不会再弹任何窗口了。",
bg=BG, fg=DIM, font=FONT_SMALL, justify="left").pack(
anchor="w", padx=18, pady=(0, 12))
code_row = tk.Frame(win, bg=BG)
code_row.pack(fill="x", padx=18)
tk.Label(code_row, text="配对码", bg=BG, fg=FG,
font=FONT_SMALL).pack(side="left")
ent = tk.Entry(code_row, bg=CARD, fg=FG, relief="flat",
font=("Consolas", 16, "bold"), width=12,
justify="center", highlightthickness=1,
highlightbackground=BORDER)
ent.insert(0, self.store.pairing_code() or self.store.new_pairing_code())
ent.pack(side="left", padx=(10, 0), ipady=6)
hint = tk.Label(win, text="6-16 位,数字或字母",
bg=BG, fg=DIM, font=FONT_SMALL)
hint.pack(anchor="w", padx=18, pady=(6, 0))
var_auto = tk.BooleanVar(value=True)
tk.Checkbutton(win, text="以后开机自动静默运行(不再显示窗口)",
variable=var_auto, bg=BG, fg=FG,
activebackground=BG, selectcolor=CARD,
font=FONT_SMALL, bd=0, highlightthickness=0).pack(
anchor="w", padx=14, pady=(12, 0))
status = tk.Label(win, text="", bg=BG, fg=DANGER, font=FONT_SMALL)
status.pack(anchor="w", padx=18, pady=(6, 0))
def confirm():
typed = ent.get().strip()
try:
saved = self.store.set_pairing_code(typed)
except ValueError as exc:
status.configure(text=str(exc))
return
self.store.set("setup_done", True)
self.lbl_code.configure(text=saved)
note = ""
if var_auto.get():
try:
from .autostart import is_enabled, enable
if not is_enabled():
enable()
note = ";已设为开机静默运行"
except Exception as exc: # noqa: BLE001
note = f";开机自启没设上({exc})"
self.log(f"配对码已设为 {saved}{note}")
result["ok"] = True
win.grab_release()
win.destroy()
def reroll():
ent.delete(0, "end")
ent.insert(0, self.store.new_pairing_code())
buttons = tk.Frame(win, bg=BG)
buttons.pack(fill="x", padx=18, pady=(4, 18))
self._btn(buttons, "确定,开始使用", confirm, width=15)
self._btn(buttons, "换个随机的", reroll, width=11, last=True)
self.center_window(win)
win.grab_set()
ent.focus_set()
win.bind("<Return>", lambda _e: confirm())
self.root.wait_window(win)
return result["ok"]
def run(self) -> None:
from .actions import Actions
# 第一次使用:先让用户定一个自己的配对码。
# 这一个窗口是整个程序里唯一一次需要你动手的地方。
if self.force_setup or self.setup_needed():
if not self.open_first_run_setup():
self.log("没有设置配对码,退出")
return
'''
# AgentUI.__init__ 要接受 force_setup
INIT_ANCHOR = " def __init__(self, store: Store, start_minimized: bool = False):"
INIT_NEW = (" def __init__(self, store: Store, start_minimized: bool = False,\n"
" force_setup: bool = False):")
INIT_BODY_ANCHOR = " self.start_minimized = start_minimized"
INIT_BODY_NEW = (" self.start_minimized = start_minimized\n"
" self.force_setup = force_setup")
# ---------------- run_agent.py:--setup + 静默模式提示 ----------------
RUN_OLD = ''' parser.add_argument("--status", action="store_true", help="打印状态后退出")'''
RUN_NEW = ''' parser.add_argument("--status", action="store_true", help="打印状态后退出")
parser.add_argument("--setup", action="store_true",
help="强制打开首次设置窗口(重新设配对码)")'''
RUN_UI_OLD = ''' ui = AgentUI(store, start_minimized=args.minimized)'''
RUN_UI_NEW = ''' ui = AgentUI(store, start_minimized=args.minimized,
force_setup=args.setup)'''
RUN_SILENT_OLD = ''' ui = HeadlessUI(store, log_file=log_file)
ui.log(f"{APP_NAME} v{__version__} 静默启动")'''
RUN_SILENT_NEW = ''' ui = HeadlessUI(store, log_file=log_file)
ui.log(f"{APP_NAME} v{__version__} 静默启动")
# 还没设过配对码就静默启动的话,手表没法连(而且用户看不到码)。
# 记到日志里,并提醒怎么处理。
if not store.get("setup_done", False):
ui.log("注意:还没有设置过配对码。请先运行:"
"LanRemote-Agent.exe --setup")
ui.log(f"当前临时配对码是 {store.pairing_code()}")'''
def main() -> int:
ok = True
for path, old, new, tag in (
(UI, ANCHOR, NEW, "ui: 首次设置窗口"),
(UI, INIT_ANCHOR, INIT_NEW, "ui: __init__ 签名"),
(UI, INIT_BODY_ANCHOR, INIT_BODY_NEW, "ui: force_setup 字段"),
(RUN, RUN_OLD, RUN_NEW, "run_agent: --setup"),
(RUN, RUN_UI_OLD, RUN_UI_NEW, "run_agent: 传 force_setup"),
(RUN, RUN_SILENT_OLD, RUN_SILENT_NEW, "run_agent: 静默模式提示"),
):
text = path.read_text(encoding="utf-8")
if old not in text:
print(f" [跳过] {tag} —— 锚点没找到")
ok = False
continue
path.write_text(text.replace(old, new, 1), encoding="utf-8")
print(f" {tag}: OK")
import ast
for label, path in (("ui.py", UI), ("run_agent.py", RUN)):
try:
ast.parse(path.read_text(encoding="utf-8"))
print(f" {label}: 语法 OK")
except SyntaxError as exc:
print(f" {label}: 语法错误 {exc}")
ok = False
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())