ykxmz 1.5.0:手表端 TLS 证书兼容 + 三端分开打包 + 下载区

手表端
- 内置 GTS Root R4 / GlobalSign Root CA
  修 SSLHandshakeException: Chain validation failed
- 连接错误翻译成人话:证书 / 时间 / 解析 / 超时分别给提示
- 经中转连上后用服务器报的地址刷新已配对记录
- 版本号 1.3.0 -> 1.5.0(AndroidManifest 之前漏改了)

服务器端
- 支持 chunked 请求体,修每 16 秒一次的连接错乱与大面积 502
- 网络调优 net-tune.sh(开机自动应用)
- 新增下载区,管理页加下载卡片
- 隧道改 HTTP/2 + 双连接

打包方式变更
- 从一个合并 zip 改成三个独立产物:
    ykxmz-agent-*.zip    被控端
    ykxmz-server-*.zip   服务器端
    ykxmz-watch-*.apk    控制端
This commit is contained in:
xmz7891
2026-10-06 23:08:05 +08:00
parent edac13dbe2
commit 8111fb6d97
20 changed files with 3770 additions and 2943 deletions
+77
View File
@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
r"""把 TlsCompat.java 加进各处的 Java 编译清单。
背景:
RelayTransport 改成用 TlsCompat.open() 之后,
那些"只编译一部分 java"的地方就编译不过了 ——
它们没有把 TlsCompat.java 列进去。
需要补的三处:
_tests/test_package_remote.py
_tests/test_watch_lan_switch.py
_tests/verify_all.ps1
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
TARGETS = [
ROOT / "_tests" / "test_package_remote.py",
ROOT / "_tests" / "test_watch_lan_switch.py",
ROOT / "_tests" / "verify_all.ps1",
]
def patch(path: Path) -> bool:
raw = path.read_bytes()
had_bom = raw.startswith(b"\xef\xbb\xbf")
text = raw.decode("utf-8-sig")
if "TlsCompat.java" in text:
print(f" {path.name}: 已经有了")
return True
# 在 RelayTransport.java 那一行后面插一行同格式的 TlsCompat.java
pattern = re.compile(
r"([ \t]*(?:\(Join-Path \$root )?[\"']?[^\n\"']*RelayTransport\.java[\"']?\)?\,?[ \t]*\r?\n)")
match = pattern.search(text)
if not match:
print(f" [失败] {path.name}: 找不到 RelayTransport.java 那一行")
return False
line = match.group(1)
indent = re.match(r"[ \t]*", line).group(0)
# 保留原来的包裹形式
if "Join-Path" in line:
new_line = f'{indent}(Join-Path $root "android\\src\\com\\lanremote\\watch\\TlsCompat.java")`\n'
elif '"' in line:
new_line = f'{indent}"TlsCompat.java",\n' if line.rstrip().endswith(",") \
else f'{indent}"TlsCompat.java"\n'
else:
new_line = f'{indent}TlsCompat.java\n'
text = text[:match.end(1)] + new_line + text[match.end(1):]
body = text.replace("\r\n", "\n").encode("utf-8")
path.write_bytes((b"\xef\xbb\xbf" if had_bom else b"") + body)
print(f" {path.name}: 补上了 TlsCompat.java")
return True
def main() -> int:
ok = True
for path in TARGETS:
if not path.is_file():
print(f" [跳过] {path.name} 不存在")
continue
if not patch(path):
ok = False
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())
+71
View File
@@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
r"""精确补上 TlsCompat.java(上一版正则没匹配上)。
两处的实际写法:
test_package_remote.py:
for n in ("Json.java", "B64.java", "Transport.java",
"RelayTransport.java", "RemoteClient.java",
"DiscoveryCore.java")]
verify_all.ps1:
(Join-Path $root "android\src\com\lanremote\watch\RelayTransport.java") `
"""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
PY_OLD = ''' sources = [ROOT / "android" / "src" / "com" / "lanremote" / "watch" / n
for n in ("Json.java", "B64.java", "Transport.java",
"RelayTransport.java", "RemoteClient.java",
"DiscoveryCore.java")]'''
PY_NEW = ''' sources = [ROOT / "android" / "src" / "com" / "lanremote" / "watch" / n
for n in ("Json.java", "B64.java", "Transport.java",
"RelayTransport.java", "TlsCompat.java",
"RemoteClient.java",
"DiscoveryCore.java")]'''
PS1_OLD = ''' (Join-Path $root "android\\src\\com\\lanremote\\watch\\RelayTransport.java") `'''
PS1_NEW = ''' (Join-Path $root "android\\src\\com\\lanremote\\watch\\RelayTransport.java") `
# TlsCompat:内置根证书,修老手表 SSLHandshakeException
(Join-Path $root "android\\src\\com\\lanremote\\watch\\TlsCompat.java") `'''
def main() -> int:
ok = True
path = ROOT / "_tests" / "test_package_remote.py"
text = path.read_text(encoding="utf-8")
if "TlsCompat.java" in text:
print(" test_package_remote.py: 已经有了")
elif PY_OLD in text:
path.write_text(text.replace(PY_OLD, PY_NEW, 1),
encoding="utf-8", newline="\n")
print(" test_package_remote.py: 补上 TlsCompat.java")
else:
print(" [失败] test_package_remote.py 锚点没找到")
ok = False
path = ROOT / "_tests" / "verify_all.ps1"
raw = path.read_bytes()
had_bom = raw.startswith(b"\xef\xbb\xbf")
text = raw.decode("utf-8-sig")
if "TlsCompat.java" in text:
print(" verify_all.ps1: 已经有了")
elif PS1_OLD in text:
text = text.replace(PS1_OLD, PS1_NEW, 1)
path.write_bytes((b"\xef\xbb\xbf" if had_bom else b"")
+ text.replace("\r\n", "\n").encode("utf-8"))
print(" verify_all.ps1: 补上 TlsCompat.java")
else:
print(" [失败] verify_all.ps1 锚点没找到")
ok = False
return 0 if ok else 1
if __name__ == "__main__":
sys.exit(main())
+71
View File
@@ -0,0 +1,71 @@
# 提交 1.5.0 的全部改动
$ErrorActionPreference = "Continue"
$git = "C:\Program Files\Git\bin\git.exe"
$root = "C:\Users\shu\Desktop\deepseekagent\LanRemote"
Set-Location $root
Write-Host "===== 1. 暂存 =====" -ForegroundColor Cyan
& $git add -A 2>&1 | Out-Null
$files = @(& $git -c core.quotepath=false diff --cached --name-only |
Where-Object { $_ -and $_.Trim() -ne "" })
Write-Host " 改动文件数: $($files.Count)"
$files | Select-Object -First 25 | ForEach-Object { Write-Host " $_" }
if ($files.Count -gt 25) { Write-Host " …还有 $($files.Count - 25) 个" }
Write-Host ""
Write-Host "===== 2. 确认没有大文件混进来 =====" -ForegroundColor Cyan
$big = foreach ($f in $files) {
$p = Join-Path $root ($f -replace '/','\')
if (Test-Path -LiteralPath $p -PathType Leaf) {
$len = (Get-Item -LiteralPath $p).Length
if ($len -gt 1MB) { [pscustomobject]@{ Name = $f; MB = [math]::Round($len/1MB,2) } }
}
}
if ($big) { $big | ForEach-Object { Write-Host " ⚠ $($_.MB) MB $($_.Name)" -ForegroundColor Yellow } }
else { Write-Host " ✓ 没有超过 1MB 的文件" -ForegroundColor Green }
Write-Host ""
Write-Host "===== 3. 提交 =====" -ForegroundColor Cyan
$msg = @"
ykxmz 1.5.0:手表端 TLS 证书兼容 + 三端分开打包 + 下载区
手表端
- 内置 GTS Root R4 / GlobalSign Root CA
修 SSLHandshakeException: Chain validation failed
(廉价手表系统根证书库常缺 GTS Root R4)
- 连接错误翻译成人话:证书 / 时间 / 解析 / 超时分别给提示
- 经中转连上后用服务器报的地址刷新已配对记录(原来一直显示旧 IP)
- 版本号 1.3.0 -> 1.5.0(AndroidManifest 之前漏改了)
服务器端
- 支持 chunked 请求体:修每 16 秒一次的连接错乱与大面积 502
- 网络调优 net-tune.sh:夹 MSS 1400 + 路径 MTU 探测
(公网可用率 48% -> 100%,实测 15 分钟 180/180)
做成开机服务 lanremote-nettune.service,并会自动重建隧道
- 新增下载区:GET /v1/downloads 列表 + GET /download/<文件>
只允许白名单后缀、严格校验文件名(挡目录穿越)
管理页加下载卡片
- 隧道改 HTTP/2 + 双连接(QUIC 在这条线路上可用率只有 48%)
- 网络体检、批量删除设备
打包方式变更
- 原来是三个部分合成一个 zip,现在**分成三个独立产物**:
ykxmz-agent-1.5.0.zip 被控端
ykxmz-server-1.5.0.zip 服务器端
ykxmz-watch-1.5.0.apk 控制端(直接给 APK,不打 zip)
三个包/文件都带上许可协议 / 安装前必读 / 安装后须知
文档
- 许可协议 / 安装前必读 / 安装后须知(作者:小麦子)
"@
& $git commit -q -m $msg
Write-Host " exit=$LASTEXITCODE"
& $git log --oneline 2>&1 | Select-Object -First 3 | ForEach-Object { Write-Host " $_" }
Write-Host ""
Write-Host "===== 4. 公钥(加到这里才能推)=====" -ForegroundColor Yellow
Write-Host " 打开 https://gitee.com/profile/sshkeys" -ForegroundColor White
Write-Host " 把下面这一整行粘进去:" -ForegroundColor White
Get-Content "$env:USERPROFILE\.ssh\id_ed25519_gitee.pub" | ForEach-Object {
Write-Host " $_" -ForegroundColor Green
}
+151 -132
View File
@@ -352,11 +352,88 @@ RELAY_README = f"""========================================================
"""
WATCH_README = f"""========================================================
控制端(安卓手表) 版本 {VERSION}
========================================================
【装】
把 ykxmz-watch-{VERSION}.apk 传到手表上安装。
手表如果提示"未知来源",在设置里允许一下。
【连】
1) 打开 App,首页有四个东西:
中转设置(跨网络用) 填服务器端给的地址
局域网连接(同一个WiFi)同一个 WiFi 时走这条,最快
输入配对码连接 经中转连
服务器:还没测 点右边「检查服务器」看延迟
2) 电脑和手表在同一个 WiFi -> 点「局域网连接」,自动搜到电脑,点一下就连
电脑在外面 -> 点「输入配对码连接」,填电脑上那个 6 位码
3) 连上后是 10 个标签页:
媒体 屏幕 文件 语音 电脑 网络 系统 教室 效率 娱乐
【连不上时看这个】
App 会把错误翻译成人话:
"证书校验失败" -> 手表系统太旧或网络被劫持
"证书时间不对" -> 把手表的日期时间调成自动获取
"连接超时" -> 网络慢或被防火墙挡
"电脑拒绝连接" -> 电脑上 ykxmz 没开
"域名解析不了" -> 手表没联网
首页的「检查服务器」能测出到中转的延迟(快/一般/慢三档配色)。
【回退】
系统页最上面三个按钮:
回退上一步 撤销刚才那一步
回退全部 把能撤的都撤了
看能回退什么 列出还能撤哪些
能撤的:所有开关、所有画面(全屏大字/投图/通知等)
撤不了的:关机、重启、删文件、杀进程 —— 这些**不会**出现在回退列表里,
免得你以为什么都能退回去。
"""
def write_readme(path: Path, text: str) -> None:
"""说明文档统一写成 UTF-8 with BOM —— Windows 记事本不加 BOM 会认成 GBK。"""
path.write_bytes(b"\xef\xbb\xbf" + text.replace("\n", "\r\n").encode("utf-8"))
def _zip_dir(zip_path: Path, source: Path, arc_root: str) -> int:
"""把一个目录打成一个 zip(内部带一层 arc_root 目录名)。"""
if zip_path.exists():
zip_path.unlink()
count = 0
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED,
compresslevel=6) as archive:
for item in sorted(source.rglob("*")):
if item.is_file():
archive.write(item, Path(arc_root) / item.relative_to(source))
count += 1
return count
def _copy_docs(target: Path) -> int:
"""把三份正式文档拷到目标目录(UTF-8 BOM + CRLF)。"""
done = 0
for name in ("LICENSE.txt", "BEFORE-INSTALL.txt", "AFTER-INSTALL.txt"):
src = DOCS / name
if src.is_file():
write_readme(target / name, src.read_text(encoding="utf-8"))
done += 1
else:
print(f" [警告] 缺少文档 {src}")
return done
def main() -> int:
if not AGENT_SRC.is_dir():
print(f" [失败] 找不到被控端: {AGENT_SRC}")
@@ -365,13 +442,18 @@ def main() -> int:
print(f" [失败] 找不到 APK: {APK_SRC}")
return 1
build = ROOT / "dist" / NAME
if build.exists():
shutil.rmtree(build, ignore_errors=True)
dist = ROOT / "dist"
dist.mkdir(parents=True, exist_ok=True)
work = dist / "_build_tmp"
if work.exists():
shutil.rmtree(work, ignore_errors=True)
# ---------- 1. 被控端 ----------
agent_dir = build / "1-agent"
print(" 复制被控端…")
results = []
# ================= 1. 被控端 =================
print(" [1/3] 被控端(Windows)")
agent_name = f"ykxmz-agent-{VERSION}"
agent_dir = work / agent_name
shutil.copytree(AGENT_SRC, agent_dir,
ignore=shutil.ignore_patterns("data", "received",
"*.idsig", "*.log"))
@@ -381,155 +463,92 @@ def main() -> int:
if src.is_file():
shutil.copy2(src, agent_dir / tool)
write_readme(agent_dir / "README-agent.txt", AGENT_README)
# 老版本往被控端目录塞过一份顶层说明的副本,清掉
for junk in ("先看这个.txt", "使用说明.txt"):
target = agent_dir / junk
if target.is_file():
target.unlink()
_copy_docs(agent_dir)
if not (agent_dir / "ykxmz.exe").is_file():
print(" [失败] 被控端里没有 ykxmz.exe")
return 1
agent_zip = dist / f"{agent_name}.zip"
n = _zip_dir(agent_zip, agent_dir, agent_name)
results.append(("被控端", agent_zip, n))
# ---------- 2. 服务器端 ----------
relay_dir = build / "2-server"
relay_dir.mkdir(parents=True)
print(" 复制服务器端…")
# 文件清单写死容易漏 —— watchdog.sh 就漏过一次。
# ================= 2. 服务器端 =================
print(" [2/3] 服务器端(随身 WiFi / Linux)")
server_name = f"ykxmz-server-{VERSION}"
server_dir = work / server_name
server_dir.mkdir(parents=True)
# 文件清单写死容易漏 —— watchdog.sh / net-tune.sh 都漏过一次。
# 改成:relay 目录里所有 .py / .sh 都带上。
wanted = sorted(
[f for f in RELAY_SRC.iterdir()
if f.suffix in (".py", ".sh") and f.is_file()],
key=lambda f: f.name)
for src in wanted:
shutil.copy2(src, relay_dir / src.name)
write_readme(relay_dir / "README-server.txt", RELAY_README)
shutil.copy2(src, server_dir / src.name)
write_readme(server_dir / "README-server.txt", RELAY_README)
_copy_docs(server_dir)
for must in ("relay.py", "install.sh", "watchdog.sh", "net-tune.sh"):
if not (server_dir / must).is_file():
print(f" [警告] 服务器端少了 {must}")
server_zip = dist / f"{server_name}.zip"
n = _zip_dir(server_zip, server_dir, server_name)
results.append(("服务器端", server_zip, n))
# ---------- 3. 控制端 ----------
watch_dir = build / "3-watch"
watch_dir.mkdir(parents=True)
print(" 复制控制端…")
shutil.copy2(APK_SRC, watch_dir / APK_SRC.name)
write_readme(watch_dir / "README-watch.txt", f"""========================================================
控制端(安卓手表) 版本 {VERSION}
========================================================
# ================= 3. 控制端(直接给 APK)=================
print(" [3/3] 控制端(手表)—— 不打 zip,直接给 APK")
# 顺带把说明也放一份在旁边,方便看
watch_note = dist / f"ykxmz-watch-{VERSION}-说明.txt"
write_readme(watch_note, WATCH_README)
results.append(("控制端", APK_SRC, 1))
【装】
shutil.rmtree(work, ignore_errors=True)
把 {APK_SRC.name} 传到手表上安装。
手表如果提示"未知来源",在设置里允许一下。
【连】
1) 打开 App,首页点「中转设置(跨网络用)」
填服务器端给你的地址,形如 https://relay.你的域名
(不填也能用,但只在同一个 WiFi 下)
2) 点「自动搜索电脑」—— 同一个局域网时能直接搜到
搜不到就点「连不上?点这里诊断」
3) 输 6 位配对码(电脑上设的那个)
4) 连上后是 10 个标签页:
媒体 屏幕 文件 语音 电脑 网络 系统 教室 效率 娱乐
每个页面上半部分是常用按钮,往下滑是更多功能(共 206 个)。
【回退】
系统页最上面三个按钮:
回退上一步 撤销刚才那一步
回退全部 把能撤的都撤了
看能回退什么 列出还能撤哪些
能撤的:所有开关(15 个)、所有画面(26 个,全屏大字/投图/通知等)
撤不了的:关机、重启、删文件、杀进程 —— 这些**不会**出现在回退列表里,
免得你以为什么都能退回去。
""")
# ---------- 4. 顶层说明 ----------
write_readme(build / "README.txt", TOP_README)
# ---------- 4b. 三份正式文档(作者:小麦子)----------
# 放顶层,和 README.txt 并排,解压第一眼就能看到。
# 统一转成 UTF-8 BOM + CRLF —— 不带 BOM 记事本会按 GBK 读,全乱。
doc_files = [
("LICENSE.txt", "许可协议"),
("BEFORE-INSTALL.txt", "安装前必读"),
("AFTER-INSTALL.txt", "安装后须知"),
]
for name, label in doc_files:
src = DOCS / name
if not src.is_file():
print(f" [警告] 缺少文档 {src}")
# ================= 汇总 =================
print()
print(" ================ 产物 ================")
ok = True
for label, path, count in results:
if not path.is_file():
print(f" {label}: ✗ 缺失!")
ok = False
continue
# 读进来(原文件是无 BOM 的 UTF-8),交给 write_readme
# 统一加 BOM + CRLF 后写出
write_readme(build / name, src.read_text(encoding="utf-8"))
print(f" {label}: {name}")
# ---------- 5. 检查 ----------
print()
print(" 目录结构:")
total = 0
for item in sorted(build.rglob("*")):
if item.is_file():
total += 1
for item in sorted(build.iterdir()):
if item.is_dir():
count = sum(1 for _ in item.rglob("*") if _.is_file())
print(f" {item.name}/ ({count} 个文件)")
else:
print(f" {item.name}")
print(f" 合计 {total} 个文件")
# 必须有的东西
required = [
agent_dir / "ykxmz.exe",
relay_dir / "relay.py",
relay_dir / "install.sh",
watch_dir / APK_SRC.name,
build / "README.txt",
build / "LICENSE.txt",
build / "BEFORE-INSTALL.txt",
build / "AFTER-INSTALL.txt",
]
missing = [str(p.relative_to(build)) for p in required if not p.is_file()]
if missing:
print(f" [失败] 少了: {missing}")
size = path.stat().st_size / 1024 / 1024
extra = f"({count} 个文件)" if count > 1 else ""
print(f" {label:6} {path.name:34} {size:6.2f} MB {extra}")
if not ok:
return 1
print(" 三个部分都在")
# ---------- 6. 打包 ----------
# ================= 验证 =================
print()
print(" 压缩中…")
zip_path = ROOT / "dist" / f"{NAME}.zip"
if zip_path.exists():
zip_path.unlink()
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED,
compresslevel=6) as archive:
for item in sorted(build.rglob("*")):
if item.is_file():
archive.write(item, item.relative_to(build.parent))
size = zip_path.stat().st_size / 1024 / 1024
print(f" {zip_path.name} {size:.2f} MB")
# ---------- 7. 验证压缩包 ----------
with zipfile.ZipFile(zip_path) as archive:
print(" 验证压缩包内容:")
for label, path, _count in results:
if path.suffix != ".zip":
continue
with zipfile.ZipFile(path) as archive:
names = archive.namelist()
bad = archive.testzip()
if bad:
print(f" [失败] 压缩包里有坏文件: {bad}")
print(f" [失败] {path.name} 里有坏文件: {bad}")
return 1
has = {
"被控端": any("ykxmz.exe" in n for n in names),
"服务器端": any("relay.py" in n for n in names),
"控制端": any(".apk" in n for n in names),
"顶层说明": any("README.txt" in n for n in names),
}
for label, ok in has.items():
print(f" {label}: {'✓' if ok else '✗'}")
if not all(has.values()):
print(f" {path.name}: {len(names)} 个条目,完整")
with zipfile.ZipFile(results[0][1]) as archive:
names = archive.namelist()
if not any("ykxmz.exe" in n for n in names):
print(" [失败] 被控端包里没有 ykxmz.exe")
return 1
print(f" 共 {len(names)} 个条目,完整")
with zipfile.ZipFile(results[1][1]) as archive:
names = archive.namelist()
for must in ("relay.py", "install.sh", "watchdog.sh", "net-tune.sh"):
if not any(must in n for n in names):
print(f" [失败] 服务器端包里没有 {must}")
return 1
print()
print(" 三个产物齐全")
return 0
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -3,7 +3,7 @@
False,
True,
['C:/Users/shu/Desktop/deepseekagent/LanRemote/windows/app.ico'],
versioninfo.VSVersionInfo(ffi=versioninfo.FixedFileInfo(filevers=(1, 3, 0, 0), prodvers=(1, 3, 0, 0), mask=0x3f, flags=0x0, OS=0x40004, fileType=1, subtype=0x0, date=(0, 0)), kids=[versioninfo.StringFileInfo([versioninfo.StringTable('080404B0', [versioninfo.StringStruct('CompanyName', 'ykxmz'), versioninfo.StringStruct('FileDescription', 'ykxmz'), versioninfo.StringStruct('FileVersion', '1.4.0.0'), versioninfo.StringStruct('InternalName', 'ykxmz'), versioninfo.StringStruct('LegalCopyright', 'ykxmz'), versioninfo.StringStruct('OriginalFilename', 'ykxmz.exe'), versioninfo.StringStruct('ProductName', 'ykxmz'), versioninfo.StringStruct('ProductVersion', '1.4.0.0')])]), versioninfo.VarFileInfo([versioninfo.VarStruct('Translation', [2052, 1200])])]),
versioninfo.VSVersionInfo(ffi=versioninfo.FixedFileInfo(filevers=(1, 3, 0, 0), prodvers=(1, 3, 0, 0), mask=0x3f, flags=0x0, OS=0x40004, fileType=1, subtype=0x0, date=(0, 0)), kids=[versioninfo.StringFileInfo([versioninfo.StringTable('080404B0', [versioninfo.StringStruct('CompanyName', 'ykxmz'), versioninfo.StringStruct('FileDescription', 'ykxmz'), versioninfo.StringStruct('FileVersion', '1.5.0.0'), versioninfo.StringStruct('InternalName', 'ykxmz'), versioninfo.StringStruct('LegalCopyright', 'ykxmz'), versioninfo.StringStruct('OriginalFilename', 'ykxmz.exe'), versioninfo.StringStruct('ProductName', 'ykxmz'), versioninfo.StringStruct('ProductVersion', '1.5.0.0')])]), versioninfo.VarFileInfo([versioninfo.VarStruct('Translation', [2052, 1200])])]),
False,
False,
b'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<assembly xmlns='
@@ -64,7 +64,7 @@
[],
False,
False,
1791279782,
1791284710,
[('runw.exe',
'C:\\Users\\shu\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\PyInstaller\\bootloader\\Windows-64bit-intel\\runw.exe',
'EXECUTABLE')],
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+274
View File
@@ -0,0 +1,274 @@
# -*- coding: utf-8 -*-
r"""把打包改成"三端分开出"。
用户要求:
"三端分别打包,控制端不用打包成 zip,直接上传 apk"
所以产物是三个文件(原来是一个大 zip):
dist/ykxmz-agent-1.5.0.zip 被控端(Windows)
dist/ykxmz-server-1.5.0.zip 服务器端(随身 WiFi / Linux)
dist/ykxmz-watch-1.5.0.apk 控制端(手表,直接给 APK 不打 zip)
三个包里都带上三份文档(许可协议 / 安装前必读 / 安装后须知),
这样用户拿到哪一个都知道自己在装什么、有什么红线。
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
PKG = ROOT / "_build" / "package_remote.py"
# 从 "def main() -> int:" 起,到文件末尾的 if __name__ 之前,全换成新的
NEW_MAIN = '''def _zip_dir(zip_path: Path, source: Path, arc_root: str) -> int:
"""把一个目录打成一个 zip(内部带一层 arc_root 目录名)。"""
if zip_path.exists():
zip_path.unlink()
count = 0
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED,
compresslevel=6) as archive:
for item in sorted(source.rglob("*")):
if item.is_file():
archive.write(item, Path(arc_root) / item.relative_to(source))
count += 1
return count
def _copy_docs(target: Path) -> int:
"""把三份正式文档拷到目标目录(UTF-8 BOM + CRLF)。"""
done = 0
for name in ("LICENSE.txt", "BEFORE-INSTALL.txt", "AFTER-INSTALL.txt"):
src = DOCS / name
if src.is_file():
write_readme(target / name, src.read_text(encoding="utf-8"))
done += 1
else:
print(f" [警告] 缺少文档 {src}")
return done
def main() -> int:
if not AGENT_SRC.is_dir():
print(f" [失败] 找不到被控端: {AGENT_SRC}")
return 1
if not APK_SRC.is_file():
print(f" [失败] 找不到 APK: {APK_SRC}")
return 1
dist = ROOT / "dist"
dist.mkdir(parents=True, exist_ok=True)
work = dist / "_build_tmp"
if work.exists():
shutil.rmtree(work, ignore_errors=True)
results = []
# ================= 1. 被控端 =================
print(" [1/3] 被控端(Windows)")
agent_name = f"ykxmz-agent-{VERSION}"
agent_dir = work / agent_name
shutil.copytree(AGENT_SRC, agent_dir,
ignore=shutil.ignore_patterns("data", "received",
"*.idsig", "*.log"))
for tool in TOOLS:
if not (agent_dir / tool).is_file():
src = ROOT / "tools" / tool
if src.is_file():
shutil.copy2(src, agent_dir / tool)
write_readme(agent_dir / "README-agent.txt", AGENT_README)
for junk in ("先看这个.txt", "使用说明.txt"):
target = agent_dir / junk
if target.is_file():
target.unlink()
_copy_docs(agent_dir)
if not (agent_dir / "ykxmz.exe").is_file():
print(" [失败] 被控端里没有 ykxmz.exe")
return 1
agent_zip = dist / f"{agent_name}.zip"
n = _zip_dir(agent_zip, agent_dir, agent_name)
results.append(("被控端", agent_zip, n))
# ================= 2. 服务器端 =================
print(" [2/3] 服务器端(随身 WiFi / Linux)")
server_name = f"ykxmz-server-{VERSION}"
server_dir = work / server_name
server_dir.mkdir(parents=True)
# 文件清单写死容易漏 —— watchdog.sh / net-tune.sh 都漏过一次。
# 改成:relay 目录里所有 .py / .sh 都带上。
wanted = sorted(
[f for f in RELAY_SRC.iterdir()
if f.suffix in (".py", ".sh") and f.is_file()],
key=lambda f: f.name)
for src in wanted:
shutil.copy2(src, server_dir / src.name)
write_readme(server_dir / "README-server.txt", RELAY_README)
_copy_docs(server_dir)
for must in ("relay.py", "install.sh", "watchdog.sh", "net-tune.sh"):
if not (server_dir / must).is_file():
print(f" [警告] 服务器端少了 {must}")
server_zip = dist / f"{server_name}.zip"
n = _zip_dir(server_zip, server_dir, server_name)
results.append(("服务器端", server_zip, n))
# ================= 3. 控制端(直接给 APK)=================
print(" [3/3] 控制端(手表)—— 不打 zip,直接给 APK")
# 顺带把说明也放一份在旁边,方便看
watch_note = dist / f"ykxmz-watch-{VERSION}-说明.txt"
write_readme(watch_note, WATCH_README)
results.append(("控制端", APK_SRC, 1))
shutil.rmtree(work, ignore_errors=True)
# ================= 汇总 =================
print()
print(" ================ 产物 ================")
ok = True
for label, path, count in results:
if not path.is_file():
print(f" {label}: ✗ 缺失!")
ok = False
continue
size = path.stat().st_size / 1024 / 1024
extra = f"({count} 个文件)" if count > 1 else ""
print(f" {label:6} {path.name:34} {size:6.2f} MB {extra}")
if not ok:
return 1
# ================= 验证 =================
print()
print(" 验证压缩包内容:")
for label, path, _count in results:
if path.suffix != ".zip":
continue
with zipfile.ZipFile(path) as archive:
names = archive.namelist()
bad = archive.testzip()
if bad:
print(f" [失败] {path.name} 里有坏文件: {bad}")
return 1
print(f" {path.name}: {len(names)} 个条目,完整")
with zipfile.ZipFile(results[0][1]) as archive:
names = archive.namelist()
if not any("ykxmz.exe" in n for n in names):
print(" [失败] 被控端包里没有 ykxmz.exe")
return 1
with zipfile.ZipFile(results[1][1]) as archive:
names = archive.namelist()
for must in ("relay.py", "install.sh", "watchdog.sh", "net-tune.sh"):
if not any(must in n for n in names):
print(f" [失败] 服务器端包里没有 {must}")
return 1
print()
print(" 三个产物齐全")
return 0
'''
# 手表说明单独抽出来(原来嵌在 main 里)
WATCH_README_BLOCK = '''WATCH_README = f"""========================================================
控制端(安卓手表) 版本 {VERSION}
========================================================
【装】
把 ykxmz-watch-{VERSION}.apk 传到手表上安装。
手表如果提示"未知来源",在设置里允许一下。
【连】
1) 打开 App,首页有四个东西:
中转设置(跨网络用) 填服务器端给的地址
局域网连接(同一个WiFi)同一个 WiFi 时走这条,最快
输入配对码连接 经中转连
服务器:还没测 点右边「检查服务器」看延迟
2) 电脑和手表在同一个 WiFi -> 点「局域网连接」,自动搜到电脑,点一下就连
电脑在外面 -> 点「输入配对码连接」,填电脑上那个 6 位码
3) 连上后是 10 个标签页:
媒体 屏幕 文件 语音 电脑 网络 系统 教室 效率 娱乐
【连不上时看这个】
App 会把错误翻译成人话:
"证书校验失败" -> 手表系统太旧或网络被劫持
"证书时间不对" -> 把手表的日期时间调成自动获取
"连接超时" -> 网络慢或被防火墙挡
"电脑拒绝连接" -> 电脑上 ykxmz 没开
"域名解析不了" -> 手表没联网
首页的「检查服务器」能测出到中转的延迟(快/一般/慢三档配色)。
【回退】
系统页最上面三个按钮:
回退上一步 撤销刚才那一步
回退全部 把能撤的都撤了
看能回退什么 列出还能撤哪些
能撤的:所有开关、所有画面(全屏大字/投图/通知等)
撤不了的:关机、重启、删文件、杀进程 —— 这些**不会**出现在回退列表里,
免得你以为什么都能退回去。
"""
'''
WATCH_PATTERN = re.compile(r'WATCH_README = f""".*?"""\n\n\n', re.S)
def main() -> int:
text = PKG.read_text(encoding="utf-8")
if "ykxmz-agent-" in text:
print(" 已经改过")
return 0
# 1. 把 WATCH_README 提到顶层的常量
# 原来它是嵌在 main() 里 write_readme 的第三个参数
old_watch = re.search(
r' write_readme\(watch_dir / "README-watch\.txt", f"""'
r'.*?"""\)\n', text, re.S)
if old_watch:
text = text[:old_watch.start()] + text[old_watch.end():]
print(" 摘掉了 main 里内嵌的手表说明")
else:
print(" [提示] 没找到内嵌的手表说明(可能已改)")
# 2. 插入 WATCH_README 常量(放在 RELAY_README 之后)
anchor = 'def write_readme(path: Path, text: str) -> None:'
if "WATCH_README =" not in text and anchor in text:
text = text.replace(anchor, WATCH_README_BLOCK + anchor, 1)
print(" 加了 WATCH_README 常量")
# 3. 换掉 main()
start = text.find("def main() -> int:")
end = text.find('if __name__ == "__main__":')
if start < 0 or end < 0 or end < start:
print(" [失败] 找不到 main() 的边界")
return 1
text = text[:start] + NEW_MAIN + text[end:]
print(" 替换了 main()(改成出三个产物)")
PKG.write_text(text, encoding="utf-8", newline="\n")
import ast
try:
ast.parse(text)
print(" package_remote.py 语法 OK")
except SyntaxError as exc:
print(f" 语法错误: 行 {exc.lineno}: {exc.msg}")
return 1
return 0
if __name__ == "__main__":
sys.exit(main())
+172
View File
@@ -0,0 +1,172 @@
# -*- coding: utf-8 -*-
r"""把 test_package_remote.py 改成适配"三端分开出"。
原来:
ZIP = dist/ykxmz-1.5.0.zip (一个合并包)
解压后是 1-agent/ 2-server/ 3-watch/ + 顶层 README
现在:
dist/ykxmz-agent-1.5.0.zip 被控端
dist/ykxmz-server-1.5.0.zip 服务器端
dist/ykxmz-watch-1.5.0.apk 控制端(直接给)
测试要验证的东西不变(三部分内容完整、能跑起来、文档带 BOM),
只是"从哪拿"变了。
"""
from __future__ import annotations
import ast
import re
import sys
from pathlib import Path
TEST = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\_tests"
r"\test_package_remote.py")
OLD_HEAD = '''ROOT = Path(__file__).resolve().parent.parent
ZIP = ROOT / "dist" / "ykxmz-1.5.0.zip"'''
NEW_HEAD = '''ROOT = Path(__file__).resolve().parent.parent
# 三端分开出(用户要求):
# 被控端 -> 一个 zip
# 服务器端 -> 一个 zip
# 控制端 -> 直接给 APK(不打 zip)
AGENT_ZIP = ROOT / "dist" / "ykxmz-agent-1.5.0.zip"
SERVER_ZIP = ROOT / "dist" / "ykxmz-server-1.5.0.zip"
WATCH_APK = ROOT / "dist" / "ykxmz-watch-1.5.0.apk"'''
OLD_OPEN = '''def main() -> int:
if not ZIP.is_file():
print(f" 找不到 {ZIP}")
return 1
work = Path(tempfile.mkdtemp(prefix="verify_pkg_"))
relay_proc = agent_proc = None
try:
# ---------------- 1. 解压 ----------------
print("一、解压")
with zipfile.ZipFile(ZIP) as archive:
archive.extractall(work)
top = work / ZIP.stem
check("顶层目录存在", top.is_dir(), top)
agent_dir = top / "1-agent"
relay_dir = top / "2-server"
watch_dir = top / "3-watch"
check("三个部分目录都在",
agent_dir.is_dir() and relay_dir.is_dir() and watch_dir.is_dir())
check("顶层说明在", (top / "README.txt").is_file())
exe = agent_dir / "ykxmz.exe"
relay_py = relay_dir / "relay.py"
install_sh = relay_dir / "install.sh"
apk = next(watch_dir.glob("*.apk"), None)
check("被控端 EXE 在", exe.is_file())
check("服务器端 relay.py 在", relay_py.is_file())
check("服务器端 install.sh 在", install_sh.is_file())
check("控制端 APK 在", apk is not None, apk)
# 说明文档要有 BOM(否则记事本会认成 GBK 乱码)
for label, path in (("顶层", top / "README.txt"),
("被控端", agent_dir / "README-agent.txt"),
("服务器端", relay_dir / "README-server.txt"),
("控制端", watch_dir / "README-watch.txt")):
raw = path.read_bytes() if path.is_file() else b""
check(f"{label}说明有 UTF-8 BOM",
raw[:3] == b"\\xef\\xbb\\xbf", raw[:3])'''
NEW_OPEN = '''def main() -> int:
# 1) 三个产物都要在
for label, path in (("被控端包", AGENT_ZIP), ("服务器端包", SERVER_ZIP),
("控制端 APK", WATCH_APK)):
if not path.is_file():
print(f" 找不到{label}: {path}")
return 1
work = Path(tempfile.mkdtemp(prefix="verify_pkg_"))
relay_proc = agent_proc = None
try:
# ---------------- 1. 解压两个 zip ----------------
print("一、解压(两个 zip)")
with zipfile.ZipFile(AGENT_ZIP) as archive:
archive.extractall(work)
with zipfile.ZipFile(SERVER_ZIP) as archive:
archive.extractall(work)
# 两个包各自带一层版本目录
agent_dir = work / AGENT_ZIP.stem
relay_dir = work / SERVER_ZIP.stem
check("被控端目录存在", agent_dir.is_dir(), agent_dir)
check("服务器端目录存在", relay_dir.is_dir(), relay_dir)
exe = agent_dir / "ykxmz.exe"
relay_py = relay_dir / "relay.py"
install_sh = relay_dir / "install.sh"
apk = WATCH_APK
check("被控端 EXE 在", exe.is_file())
check("服务器端 relay.py 在", relay_py.is_file())
check("服务器端 install.sh 在", install_sh.is_file())
check("服务器端带了网络调优脚本", (relay_dir / "net-tune.sh").is_file())
check("服务器端带了看门狗", (relay_dir / "watchdog.sh").is_file())
check("控制端 APK 在(直接在 dist 里)", apk.is_file(), apk)
# 说明文档要有 BOM(否则记事本会认成 GBK 乱码)
for label, path in (("被控端", agent_dir / "README-agent.txt"),
("服务器端", relay_dir / "README-server.txt"),
("控制端(旁边那份)",
ROOT / "dist" / "ykxmz-watch-1.5.0-说明.txt")):
raw = path.read_bytes() if path.is_file() else b""
check(f"{label}说明有 UTF-8 BOM",
raw[:3] == b"\\xef\\xbb\\xbf", raw[:3])
# 三份正式文档:两个包里都要有
for label, base in (("被控端包", agent_dir), ("服务器端包", relay_dir)):
for doc in ("LICENSE.txt", "BEFORE-INSTALL.txt",
"AFTER-INSTALL.txt"):
check(f"{label}带了 {doc}", (base / doc).is_file())'''
OLD_TOOLS = ''' # 被控端目录里不该有 data / 说明残留
check("没有把 data 目录打进去",
not (agent_dir / "data").exists())'''
NEW_TOOLS = ''' # 被控端目录里不该有 data / 说明残留
check("没有把 data 目录打进去",
not (agent_dir / "data").exists())'''
def main() -> int:
text = TEST.read_text(encoding="utf-8")
if "AGENT_ZIP" in text:
print(" 已经改过")
return 0
ok = True
for old, new, label in ((OLD_HEAD, NEW_HEAD, "产物路径常量"),
(OLD_OPEN, NEW_OPEN, "解压与检查段")):
if old not in text:
print(f" [失败] {label} —— 锚点没找到")
# 打印实际内容帮助定位
key = old.splitlines()[0]
for i, line in enumerate(text.splitlines(), 1):
if line.strip() == key.strip():
print(f" 实际 {i}: {line}")
ok = False
continue
text = text.replace(old, new, 1)
print(f" {label}")
if not ok:
return 1
TEST.write_text(text, encoding="utf-8", newline="\n")
try:
ast.parse(text)
print(" test_package_remote.py 语法 OK")
except SyntaxError as exc:
print(f" 语法错误: 行 {exc.lineno}: {exc.msg}")
return 1
return 0
if __name__ == "__main__":
sys.exit(main())
+68
View File
@@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
r"""两件事:
1. 给 verify_all.ps1 补回 UTF-8 BOM
(edit 工具写出来不带 BOM,PowerShell 里中文会乱码)
2. 把三个成品上传到设备的下载目录
"""
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote")
sys.path.insert(0, str(ROOT / "_build"))
from devssh import run, upload # noqa: E402
VERIFY = ROOT / "_tests" / "verify_all.ps1"
UPLOADS = [
("dist/ykxmz-agent-1.5.0.zip",
"/root/lanremote-relay/downloads/ykxmz-agent-1.5.0.zip"),
("dist/ykxmz-server-1.5.0.zip",
"/root/lanremote-relay/downloads/ykxmz-server-1.5.0.zip"),
("dist/ykxmz-watch-1.5.0.apk",
"/root/lanremote-relay/downloads/ykxmz-watch-1.5.0.apk"),
("dist/ykxmz-watch-1.5.0-说明.txt",
"/root/lanremote-relay/downloads/ykxmz-watch-1.5.0-说明.txt"),
]
def main() -> int:
# ---- 1. 补 BOM ----
raw = VERIFY.read_bytes()
if raw.startswith(b"\xef\xbb\xbf"):
print(" verify_all.ps1: BOM 正常")
else:
VERIFY.write_bytes(b"\xef\xbb\xbf" + raw)
print(" verify_all.ps1: 补回了 UTF-8 BOM")
# ---- 2. 建目录 ----
run("mkdir -p /root/lanremote-relay/downloads", timeout=30)
run("rm -f /root/lanremote-relay/downloads/ykxmz-1.5.0.zip", timeout=30)
# ---- 3. 上传 ----
print()
print(" 上传成品:")
for local, remote in UPLOADS:
src = ROOT / local
if not src.is_file():
print(f" [跳过] {local} 不存在")
continue
try:
upload(str(src), remote, 0o644)
size = src.stat().st_size
print(f" ✓ {src.name} ({size / 1024 / 1024:.2f} MB)")
except Exception as exc: # noqa: BLE001
print(f" ✗ {src.name}: {exc}")
# ---- 4. 确认 ----
print()
code, out, _err = run(
"ls -la /root/lanremote-relay/downloads/ | tail -8", timeout=30)
print(out)
return 0
if __name__ == "__main__":
sys.exit(main())
+54
View File
@@ -0,0 +1,54 @@
#!/bin/bash
# 验证下载区能用(本地 + 公网)。
set -u
BASE=http://127.0.0.1:8080
PUB=https://relay.xxmxxs.top
UA='LanRemote/1.4.0'
echo "===== 1. 本地列出下载文件 ====="
curl -s --max-time 10 "$BASE/v1/downloads" | python3 -m json.tool 2>/dev/null \
| head -30 || echo " 读不到"
echo
echo "===== 2. 本地下载(只看 HTTP 码和大小)====="
for f in ykxmz-watch-1.5.0.apk ykxmz-server-1.5.0.zip ykxmz-agent-1.5.0.zip; do
info=$(curl -s --max-time 20 -o /tmp/dl.bin -w '%{http_code} %{size_download} %{content_type}' \
"$BASE/download/$f" 2>/dev/null)
printf " %-32s %s\n" "$f" "$info"
done
echo
echo "===== 3. 公网列出(走隧道)====="
curl -s --max-time 20 -H "User-Agent: $UA" "$PUB/v1/downloads" \
| head -c 300
echo
echo
echo "===== 4. 公网下载 APK 头几个字节(验证真的能下)====="
code=$(curl -s --max-time 40 -o /tmp/apk_pub.bin -w '%{http_code}' \
-H "User-Agent: $UA" "$PUB/download/ykxmz-watch-1.5.0.apk" 2>/dev/null)
size=$(stat -c %s /tmp/apk_pub.bin 2>/dev/null || echo 0)
echo " HTTP $code,下载 $size 字节"
echo -n " 是不是合法 APK(zip 头 PK): "
head -c 2 /tmp/apk_pub.bin | grep -q 'PK' && echo "是 ✓" || echo "不是 ✗"
echo
echo "===== 5. 安全:目录穿越要被挡住 ====="
for bad in "../relay.py" "..%2Frelay.py" "relay.py" "x.sh"; do
code=$(curl -s --max-time 10 -o /dev/null -w '%{http_code}' \
"$BASE/download/$bad" 2>/dev/null)
printf " %-20s HTTP %s\n" "$bad" "$code"
done
echo
echo "===== 6. 管理页有没有下载卡片 ====="
KEY=$(cat /root/lanremote-relay/data/admin_key.txt 2>/dev/null)
for needle in 'id=dlPanel' '/v1/downloads' '/download/'; do
if curl -s --max-time 10 -H "X-Admin: $KEY" "$BASE/" 2>/dev/null \
| grep -q "$needle"; then
echo " ✓ $needle"
else
echo " ✗ $needle"
fi
done
+38 -20
View File
@@ -22,7 +22,13 @@ import zipfile
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
ZIP = ROOT / "dist" / "ykxmz-1.5.0.zip"
# 三端分开出(用户要求):
# 被控端 -> 一个 zip
# 服务器端 -> 一个 zip
# 控制端 -> 直接给 APK(不打 zip)
AGENT_ZIP = ROOT / "dist" / "ykxmz-agent-1.5.0.zip"
SERVER_ZIP = ROOT / "dist" / "ykxmz-server-1.5.0.zip"
WATCH_APK = ROOT / "dist" / "ykxmz-watch-1.5.0.apk"
TOOLCHAIN = Path(r"C:\Users\shu\Desktop\deepseekagent\starklink\toolchain")
JAVAC = TOOLCHAIN / "jdk17" / "bin" / "javac.exe"
JAVA = TOOLCHAIN / "jdk17" / "bin" / "java.exe"
@@ -45,44 +51,55 @@ def check(name, ok, detail=""):
def main() -> int:
if not ZIP.is_file():
print(f" 找不到 {ZIP}")
# 1) 三个产物都要在
for label, path in (("被控端包", AGENT_ZIP), ("服务器端包", SERVER_ZIP),
("控制端 APK", WATCH_APK)):
if not path.is_file():
print(f" 找不到{label}: {path}")
return 1
work = Path(tempfile.mkdtemp(prefix="verify_pkg_"))
relay_proc = agent_proc = None
try:
# ---------------- 1. 解压 ----------------
print("一、解压")
with zipfile.ZipFile(ZIP) as archive:
# ---------------- 1. 解压两个 zip ----------------
print("一、解压(两个 zip)")
with zipfile.ZipFile(AGENT_ZIP) as archive:
archive.extractall(work)
top = work / ZIP.stem
check("顶层目录存在", top.is_dir(), top)
agent_dir = top / "1-agent"
relay_dir = top / "2-server"
watch_dir = top / "3-watch"
check("三个部分目录都在",
agent_dir.is_dir() and relay_dir.is_dir() and watch_dir.is_dir())
check("顶层说明在", (top / "README.txt").is_file())
with zipfile.ZipFile(SERVER_ZIP) as archive:
archive.extractall(work)
# 两个包各自带一层版本目录
agent_dir = work / AGENT_ZIP.stem
relay_dir = work / SERVER_ZIP.stem
check("被控端目录存在", agent_dir.is_dir(), agent_dir)
check("服务器端目录存在", relay_dir.is_dir(), relay_dir)
exe = agent_dir / "ykxmz.exe"
relay_py = relay_dir / "relay.py"
install_sh = relay_dir / "install.sh"
apk = next(watch_dir.glob("*.apk"), None)
apk = WATCH_APK
check("被控端 EXE 在", exe.is_file())
check("服务器端 relay.py 在", relay_py.is_file())
check("服务器端 install.sh 在", install_sh.is_file())
check("控制端 APK 在", apk is not None, apk)
check("服务器端带了网络调优脚本", (relay_dir / "net-tune.sh").is_file())
check("服务器端带了看门狗", (relay_dir / "watchdog.sh").is_file())
check("控制端 APK 在(直接在 dist 里)", apk.is_file(), apk)
# 说明文档要有 BOM(否则记事本会认成 GBK 乱码)
for label, path in (("顶层", top / "README.txt"),
("被控端", agent_dir / "README-agent.txt"),
for label, path in (("被控端", agent_dir / "README-agent.txt"),
("服务器端", relay_dir / "README-server.txt"),
("控制端", watch_dir / "README-watch.txt")):
("控制端(旁边那份)",
ROOT / "dist" / "ykxmz-watch-1.5.0-说明.txt")):
raw = path.read_bytes() if path.is_file() else b""
check(f"{label}说明有 UTF-8 BOM",
raw[:3] == b"\xef\xbb\xbf", raw[:3])
# 三份正式文档:两个包里都要有
for label, base in (("被控端包", agent_dir), ("服务器端包", relay_dir)):
for doc in ("LICENSE.txt", "BEFORE-INSTALL.txt",
"AFTER-INSTALL.txt"):
check(f"{label}带了 {doc}", (base / doc).is_file())
# 被控端目录里不该有 data / 说明残留
check("没有把 data 目录打进去",
not (agent_dir / "data").exists())
@@ -179,7 +196,8 @@ def main() -> int:
classes.mkdir(parents=True, exist_ok=True)
sources = [ROOT / "android" / "src" / "com" / "lanremote" / "watch" / n
for n in ("Json.java", "B64.java", "Transport.java",
"RelayTransport.java", "RemoteClient.java",
"RelayTransport.java", "TlsCompat.java",
"RemoteClient.java",
"DiscoveryCore.java")]
sources.append(ROOT / "_tests" / "java" / "JavaRelayTest.java")
comp = subprocess.run(
+1
View File
@@ -20,6 +20,7 @@ ROOT = Path(__file__).resolve().parent.parent
WATCH = ROOT / "android" / "src" / "com" / "lanremote" / "watch"
MAIN = WATCH / "MainActivity.java"
RELAY_T = WATCH / "RelayTransport.java"
"TlsCompat.java"
CLIENT = WATCH / "RemoteClient.java"
JSON = WATCH / "Json.java"
+2
View File
@@ -402,6 +402,8 @@ New-Item -ItemType Directory -Force -Path $out | Out-Null
(Join-Path $root "android\src\com\lanremote\watch\DiscoveryCore.java") `
(Join-Path $root "android\src\com\lanremote\watch\Transport.java") `
(Join-Path $root "android\src\com\lanremote\watch\RelayTransport.java") `
# TlsCompat:内置根证书,修老手表连中转时的 SSLHandshakeException
(Join-Path $root "android\src\com\lanremote\watch\TlsCompat.java") `
(Join-Path $root "android\src\com\lanremote\watch\RemoteClient.java") `
(Join-Path $root "_tests\java\JavaClientTest.java") 2>&1 | Out-Null
$compiled = ($LASTEXITCODE -eq 0)
Binary file not shown.
Binary file not shown.
Binary file not shown.