============================================================
  deploy.sh 实测
============================================================
  仓库:   /c/Users/shu/Desktop/deepseekagent/LanRemote
  脚本:   /c/Users/shu/Desktop/deepseekagent/LanRemote/relay/deploy.sh

── 1. 语法检查（bash -n）──
    [PASS] deploy.sh 语法正确
    [PASS] deploy-tune.sh 语法正确

── 2. 换行符 / BOM（.sh 必须 LF + 无 BOM）──
    [PASS] deploy.sh 是 LF 且无 BOM
    [PASS] deploy-tune.sh 是 LF 且无 BOM

── 3. --help ──
    [PASS] --help 退出码为 0
    [PASS] --help 有「用法」段
    [PASS] --help 列出了 --prefix
    [PASS] --help 提到了 TUNNEL_TOKEN
    [PASS] --help 没有副作用

── 3b. 非法参数要报错退出 ──
    [PASS] 未知参数退出码为 2
    [PASS] 非法协议退出码为 2

── 4. 搭沙箱（桩命令 + 临时安装根）──
  沙箱:   /tmp/tmp.jb8lwoeXgw
  安装根: /tmp/tmp.jb8lwoeXgw/root
  桩命令: cloudflared curl free getent ip iptables nmcli python3 systemctl 

── 5. 第一次部署（--skip-tunnel，不碰隧道）──
    [PASS] 第一次退出码为 0
    [PASS] relay.py 已部署
    [PASS] admin.py 已部署
    [PASS] landing.py 已部署
    [PASS] showcase.py 已部署
    [PASS] favicon.ico 已部署
    [PASS] deploy-tune.sh 已部署
    [PASS] watchdog.sh 已部署
    [PASS] site_shots 已部署
    [PASS] data 目录已建
    [PASS] downloads 目录已建
    [PASS] lanremote-relay.service 已生成
    [PASS] lanremote-nettune.service 已生成
    [PASS] lanremote-tunnel.service 已生成
    [PASS] 备用 http2 单元已生成
    [PASS] 看门狗 timer 已生成

     生成的 relay unit 里应该有的关键项:
    [PASS] relay unit 含 RELAY_POLL=60
    [PASS] relay unit 含 RELAY_PORT=8080
    [PASS] relay unit 含 python3

     两个隧道单元的内容（默认主 quic+8 / 副本 http2+2）:
    [PASS] 主隧道单元用 quic
    [PASS] 主隧道 8 条连接（实测最优配比）
    [PASS] 副本隧道单元用 http2
    [PASS] 副本隧道 2 条连接（实测最优配比）
    [PASS] 主隧道 metrics 钉死 20241
    [PASS] 副本隧道 metrics 钉死 20242
    [PASS] nettune 只有一个入口脚本
    [PASS] unit 里没有明文 Token
    [PASS] 便捷命令 lanremote-url 已生成
    [PASS] 便捷命令 lanremote-key 已生成
    [PASS] 便捷命令 lanremote-status 已生成

── 6. 幂等性：放几个「不许被冲掉」的东西，再跑第二遍 ──
    [PASS] 第二次退出码为 0
    [PASS] 设备数据没被冲掉
    [PASS] 管理钥匙没被冲掉
    [PASS] 管理密码没被冲掉
    [PASS] 公网地址没被冲掉
    [PASS] relay_state.json 没被改（allowNew 保住了）
    [PASS] allowNew 仍然是 false（没被拨回 true）
    [PASS] 手工 config.yml 没被覆盖
    [PASS] relay.py 内容稳定
    [PASS] 第二遍没有新增 ✗ 错误

── 6b. 没变化时不该重启服务（别打断在线连接）──
    [PASS] 第二遍没有重启中转（无变化）
    [PASS] 第二遍没有重启隧道（无变化）
    [PASS] 第二遍没有多余的 daemon-reload
    [PASS] 输出里明确说了「内容没变、保持运行」

── 7. 凭据模式：给出域名时要能生成 config.yml ──
    [PASS] 凭据模式退出码为 0
    [PASS] config.yml 已生成
    [PASS] config.yml 里的隧道 ID 取自凭据文件名
    [PASS] config.yml 写入了域名
    [PASS] config.yml 有兜底 404 规则（没有它 cloudflared 起不来）
    [PASS] config.yml 指向本地中转端口

── 7c. 默认：两个隧道单元都要启用（协议级故障隔离）──
    [PASS] 默认（不传 --protocol）退出码为 0
    [PASS] 默认启用了主隧道
    [PASS] 默认启用了副本隧道
    [PASS] 默认没有关掉任何一个隧道

── 7d. --protocol quic：只跑主隧道 ──
    [PASS] --protocol quic 退出码为 0
    [PASS] quic 模式启用了主隧道
    [PASS] quic 模式停用了副本隧道
    [PASS] 输出说明了为什么停用副本

── 7e. --protocol http2：只跑副本隧道 ──
    [PASS] --protocol http2 退出码为 0
    [PASS] http2 模式启用了副本隧道
    [PASS] http2 模式停用了主隧道

── 7f. --protocol 的非法值要报错 ──
    [PASS] 非法协议退出码为 2
    [PASS] both 是合法值

── 7b. 再跑一次（不给 --hostname），config.yml 不该被改 ──
    [PASS] 再次运行退出码为 0
    [PASS] 没给 --hostname 时 config.yml 保持不变

── 8. Token 模式 ──
    [PASS] Token 模式退出码为 0
    [PASS] Token 写进了 tunnel.env
    [PASS] tunnel.env 内容正确
    [PASS] unit 里没有明文 Token（只有 EnvironmentFile 引用）
    [PASS] unit 用了 EnvironmentFile 读 Token
    [PASS] unit 的 ExecStart 里用 --token ${TUNNEL_TOKEN}

── 8b. tunnel.env 权限应该是 600 ──
      tunnel.env 权限: 644（Linux 上应为 600）

── 9. 缺文件时要明确报错，不能静默 ──
    [PASS] 文件不全时明确失败（退出码 1）
    [PASS] 报错信息说清了是「程序文件不全」

── 10. 桩命令确实被调用过（证明流程真的走了）──
    [PASS] 调用过 systemctl enable
    [PASS] 调用过 iptables
    [PASS] 第一次安装做了 daemon-reload

── 11. 脚本里不许有写死的域名 ──
    [PASS] deploy.sh 里没有写死的域名
    [PASS] deploy-tune.sh 里没有写死的域名
    [PASS] net-tune.sh 里没有写死的域名
    [PASS] 示例用的是「你的域名」占位符

── 12. 有凭据但没给域名 → 不许乱猜，必须明确提示 ──
     [调试] TMP=/tmp/tmp.jb8lwoeXgw
     [调试] 凭据目录内容: deadbeef-0000-1111-2222-333344445555.json 
    [PASS] 没域名时退出码为 0（中转照装，只是隧道不启）
    [PASS] 没域名时没有生成 config.yml（没有乱猜）
    [PASS] 明确提示了「需要你提供公网域名」
    [PASS] 提示里给了怎么补域名
    [PASS] 提示里给了 Token 模式这个替代办法
    [PASS] 隧道没配好时没有去启动它
    [PASS] 中转仍然正常安装并启用

============================================================
  结果: 通过 98 项，失败 0 项
============================================================
  沙箱目录（想自己看就留着）: /tmp/tmp.jb8lwoeXgw
