names = stringList(reply, "names");
+ final String dir = Json.str(reply, "dir", "");
+ ui.post(new Runnable() {
+ @Override
+ public void run() {
+ if (names.isEmpty()) {
+ toast("电脑上还没有音效(或没连上)");
+ appendLog("音效列表是空的。"
+ + (dir.isEmpty() ? "" : "素材目录:" + dir));
+ return;
+ }
+ showChooser("音效(点一下电脑就响)", names,
+ new Chooser() {
+ @Override
+ public void onPick(int index) {
+ String one = names.get(index);
+ sendCommand(Json.obj("cmd", "sfx_play",
+ "name", one), "音效 " + one);
+ }
+ });
+ }
+ });
+ } catch (final IOException e) {
+ postError("获取音效", e);
+ }
+ }
+ });
+ }
+
/** 选择电脑上的 TTS 音色。 */
private void pickVoice() {
appendLog("正在获取电脑音色…");
diff --git a/docs/AFTER-INSTALL.txt b/docs/AFTER-INSTALL.txt
index f50db49..706ae27 100644
--- a/docs/AFTER-INSTALL.txt
+++ b/docs/AFTER-INSTALL.txt
@@ -3,7 +3,7 @@
========================================================
软件名称:ykxmz
-版本:2.0.0
+版本:2.1.0
作者:小麦子
装好之后看这一页:怎么确认它在跑、怎么改设置、
@@ -197,7 +197,7 @@
八、版本记录
--------------------------------------------------------
- 2.0.0
+ 2.1.0
* 下载改走 Cloudflare 边缘缓存 —— 安装包和 APK 快了几十倍
* 跨网络更稳:隧道**两种协议同时跑**(QUIC + HTTP/2,
合计 10 条连接),一种协议犯病另一种还在服务
@@ -218,6 +218,6 @@
--------------------------------------------------------
作者:小麦子
- 软件:ykxmz 2.0.0
+ 软件:ykxmz 2.1.0
许可条款见《许可协议》(LICENSE.txt)
--------------------------------------------------------
diff --git a/docs/BEFORE-INSTALL.txt b/docs/BEFORE-INSTALL.txt
index 1fb703e..2294540 100644
--- a/docs/BEFORE-INSTALL.txt
+++ b/docs/BEFORE-INSTALL.txt
@@ -3,7 +3,7 @@
========================================================
软件名称:ykxmz
-版本:2.0.0
+版本:2.1.0
作者:小麦子
请你**在安装之前**花三分钟读完这一页。里面有几条很要紧的
@@ -164,6 +164,6 @@ ykxmz 是一套自己搭建的远程管理工具,由三部分组成:
--------------------------------------------------------
作者:小麦子
- 软件:ykxmz 2.0.0
+ 软件:ykxmz 2.1.0
继续安装,即表示你已阅读并同意《许可协议》。
--------------------------------------------------------
diff --git a/docs/LICENSE.txt b/docs/LICENSE.txt
index f9b8018..7a44669 100644
--- a/docs/LICENSE.txt
+++ b/docs/LICENSE.txt
@@ -1,38 +1,48 @@
========================================================
- ykxmz 软件许可协议
+ ykxmz 使用条款与免责声明
========================================================
软件名称:ykxmz(远程管理工具)
-版本:1.7.1
+版本:2.1.0
作者:小麦子
生效日期:2026 年 10 月 5 日
+> **这份文件管的是「使用行为」,代码授权看仓库根目录的 `LICENSE`。**
+>
+> 根目录 `LICENSE` 是 GNU AGPL-3.0:你可以查看、修改、
+> 再发布本软件的**代码**,衍生作品必须以同样协议开源。
+>
+> 这份文件说的是另一件事:**不管你从哪拿到它、改没改过,
+> 都不许拿去做下面第三条那些事。**
+>
+> (早先这版文件写的是"软件许可协议",里面有一条"不得修改"——
+> 那和 AGPL 冲突,所以 2.1.0 起拆开了:代码授权归 LICENSE,
+> 使用约束归这份文件。)
+
--------------------------------------------------------
-第一条 协议的范围
+第一条 条款的范围
--------------------------------------------------------
-本协议是你(以下称"使用者")与作者小麦子之间就 ykxmz 软件
+本条款是你(以下称"使用者")与作者小麦子之间就 ykxmz 软件
(含被控端程序、中转服务器程序、手表端应用及相关文档,
-以下统称"本软件")达成的许可使用协议。
+以下统称"本软件")达成的**使用约定**。
下载、安装、复制或以任何方式使用本软件,即表示你已阅读、
-理解并同意本协议全部条款。如不同意,请不要安装或使用。
+理解并同意本条款全部内容。如不同意,请不要安装或使用。
--------------------------------------------------------
-第二条 授权范围
+第二条 你可以做的
--------------------------------------------------------
-在遵守本协议的前提下,作者授予你一项免费的、非独占的、
-不可转让的使用许可,你可以:
+本软件**免费**使用,作者不收取任何费用。你可以:
1. 在**你自己拥有或已获得明确授权**的设备上安装和使用本软件;
- 2. 复制并分发本软件的完整副本(须保持文件完整、不得修改、
- 不得去除本协议与作者署名);
- 3. 在教室、机房、家庭等场景中,用于管理**你负责管理**的设备。
-
-本软件免费使用,作者不收取任何费用。
+ 2. 在教室、机房、家庭等场景中,用于管理**你负责管理**的设备;
+ 3. 复制并分发本软件 —— 分发时请保留作者署名,不要去掉版权声明;
+ 4. 查看、修改其**源代码**并再发布,具体条件见根目录 `LICENSE`
+ (AGPL-3.0:衍生作品要同样开源,且不能拿去闭源商用)。
--------------------------------------------------------
@@ -58,7 +68,7 @@
**一句话:只能管你自己的设备,或者别人明确同意你管的设备。**
-违反上述任何一条,本协议立即终止,且由此产生的全部法律
+违反上述任何一条,本条款立即终止,且由此产生的全部法律
责任由使用者自行承担,与作者无关。
@@ -124,33 +134,34 @@
--------------------------------------------------------
-第八条 协议终止
+第八条 条款终止
--------------------------------------------------------
-出现下列情形之一,本协议自动终止:
+出现下列情形之一,本条款自动终止:
- 1. 你违反本协议任何条款;
+ 1. 你违反本条款任何内容;
2. 你将本软件用于第三条约定的禁止用途。
-协议终止后,你应当立即停止使用并卸载本软件。第二条授予的
-分发许可同时失效。
+条款终止后,你应当立即停止使用并卸载本软件。第二条中关于
+分发的许可同时失效(代码授权是否终止以根目录 `LICENSE` 为准)。
--------------------------------------------------------
第九条 其他
--------------------------------------------------------
- 1. 本协议适用中华人民共和国法律;
- 2. 因本协议产生的争议,双方应友好协商解决;协商不成的,
+ 1. 本条款适用中华人民共和国法律;
+ 2. 因本条款产生的争议,双方应友好协商解决;协商不成的,
提交作者所在地有管辖权的人民法院;
- 3. 作者保留在后续版本中调整本协议的权利,调整后的协议
- 随新版本一同发布;继续使用新版本即视为接受新协议;
- 4. 本协议部分条款无效,不影响其余条款的效力。
+ 3. 作者保留在后续版本中调整本条款的权利,调整后的条款
+ 随新版本一同发布;继续使用新版本即视为接受新条款;
+ 4. 本条款部分内容无效,不影响其余内容的效力。
--------------------------------------------------------
作者:小麦子
- 软件:ykxmz 1.7.1
+ 软件:ykxmz 2.1.0
- 如果你不同意本协议的任何一条,请不要安装或使用本软件。
+ 如果你不同意本条款的任何一条,请不要安装或使用本软件。
+ 代码授权条款见仓库根目录 LICENSE(GNU AGPL-3.0)。
--------------------------------------------------------
diff --git a/installer/license-combined.txt b/installer/license-combined.txt
index 248a3ff..84fde2b 100644
--- a/installer/license-combined.txt
+++ b/installer/license-combined.txt
@@ -1,38 +1,48 @@
========================================================
- ykxmz 软件许可协议
+ ykxmz 使用条款与免责声明
========================================================
软件名称:ykxmz(远程管理工具)
-版本:2.0.0
+版本:2.1.0
作者:小麦子
生效日期:2026 年 10 月 5 日
+> **这份文件管的是「使用行为」,代码授权看仓库根目录的 `LICENSE`。**
+>
+> 根目录 `LICENSE` 是 GNU AGPL-3.0:你可以查看、修改、
+> 再发布本软件的**代码**,衍生作品必须以同样协议开源。
+>
+> 这份文件说的是另一件事:**不管你从哪拿到它、改没改过,
+> 都不许拿去做下面第三条那些事。**
+>
+> (早先这版文件写的是"软件许可协议",里面有一条"不得修改"——
+> 那和 AGPL 冲突,所以 2.1.0 起拆开了:代码授权归 LICENSE,
+> 使用约束归这份文件。)
+
--------------------------------------------------------
-第一条 协议的范围
+第一条 条款的范围
--------------------------------------------------------
-本协议是你(以下称"使用者")与作者小麦子之间就 ykxmz 软件
+本条款是你(以下称"使用者")与作者小麦子之间就 ykxmz 软件
(含被控端程序、中转服务器程序、手表端应用及相关文档,
-以下统称"本软件")达成的许可使用协议。
+以下统称"本软件")达成的**使用约定**。
下载、安装、复制或以任何方式使用本软件,即表示你已阅读、
-理解并同意本协议全部条款。如不同意,请不要安装或使用。
+理解并同意本条款全部内容。如不同意,请不要安装或使用。
--------------------------------------------------------
-第二条 授权范围
+第二条 你可以做的
--------------------------------------------------------
-在遵守本协议的前提下,作者授予你一项免费的、非独占的、
-不可转让的使用许可,你可以:
+本软件**免费**使用,作者不收取任何费用。你可以:
1. 在**你自己拥有或已获得明确授权**的设备上安装和使用本软件;
- 2. 复制并分发本软件的完整副本(须保持文件完整、不得修改、
- 不得去除本协议与作者署名);
- 3. 在教室、机房、家庭等场景中,用于管理**你负责管理**的设备。
-
-本软件免费使用,作者不收取任何费用。
+ 2. 在教室、机房、家庭等场景中,用于管理**你负责管理**的设备;
+ 3. 复制并分发本软件 —— 分发时请保留作者署名,不要去掉版权声明;
+ 4. 查看、修改其**源代码**并再发布,具体条件见根目录 `LICENSE`
+ (AGPL-3.0:衍生作品要同样开源,且不能拿去闭源商用)。
--------------------------------------------------------
@@ -58,7 +68,7 @@
**一句话:只能管你自己的设备,或者别人明确同意你管的设备。**
-违反上述任何一条,本协议立即终止,且由此产生的全部法律
+违反上述任何一条,本条款立即终止,且由此产生的全部法律
责任由使用者自行承担,与作者无关。
@@ -124,35 +134,36 @@
--------------------------------------------------------
-第八条 协议终止
+第八条 条款终止
--------------------------------------------------------
-出现下列情形之一,本协议自动终止:
+出现下列情形之一,本条款自动终止:
- 1. 你违反本协议任何条款;
+ 1. 你违反本条款任何内容;
2. 你将本软件用于第三条约定的禁止用途。
-协议终止后,你应当立即停止使用并卸载本软件。第二条授予的
-分发许可同时失效。
+条款终止后,你应当立即停止使用并卸载本软件。第二条中关于
+分发的许可同时失效(代码授权是否终止以根目录 `LICENSE` 为准)。
--------------------------------------------------------
第九条 其他
--------------------------------------------------------
- 1. 本协议适用中华人民共和国法律;
- 2. 因本协议产生的争议,双方应友好协商解决;协商不成的,
+ 1. 本条款适用中华人民共和国法律;
+ 2. 因本条款产生的争议,双方应友好协商解决;协商不成的,
提交作者所在地有管辖权的人民法院;
- 3. 作者保留在后续版本中调整本协议的权利,调整后的协议
- 随新版本一同发布;继续使用新版本即视为接受新协议;
- 4. 本协议部分条款无效,不影响其余条款的效力。
+ 3. 作者保留在后续版本中调整本条款的权利,调整后的条款
+ 随新版本一同发布;继续使用新版本即视为接受新条款;
+ 4. 本条款部分内容无效,不影响其余内容的效力。
--------------------------------------------------------
作者:小麦子
- 软件:ykxmz 2.0.0
+ 软件:ykxmz 2.1.0
- 如果你不同意本协议的任何一条,请不要安装或使用本软件。
+ 如果你不同意本条款的任何一条,请不要安装或使用本软件。
+ 代码授权条款见仓库根目录 LICENSE(GNU AGPL-3.0)。
--------------------------------------------------------
======================================================================
@@ -162,7 +173,7 @@
========================================================
软件名称:ykxmz
-版本:2.0.0
+版本:2.1.0
作者:小麦子
请你**在安装之前**花三分钟读完这一页。里面有几条很要紧的
@@ -323,7 +334,7 @@ ykxmz 是一套自己搭建的远程管理工具,由三部分组成:
--------------------------------------------------------
作者:小麦子
- 软件:ykxmz 2.0.0
+ 软件:ykxmz 2.1.0
继续安装,即表示你已阅读并同意《许可协议》。
--------------------------------------------------------
@@ -334,7 +345,7 @@ ykxmz 是一套自己搭建的远程管理工具,由三部分组成:
========================================================
软件名称:ykxmz
-版本:2.0.0
+版本:2.1.0
作者:小麦子
装好之后看这一页:怎么确认它在跑、怎么改设置、
@@ -528,7 +539,7 @@ ykxmz 是一套自己搭建的远程管理工具,由三部分组成:
八、版本记录
--------------------------------------------------------
- 2.0.0
+ 2.1.0
* 下载改走 Cloudflare 边缘缓存 —— 安装包和 APK 快了几十倍
* 跨网络更稳:隧道**两种协议同时跑**(QUIC + HTTP/2,
合计 10 条连接),一种协议犯病另一种还在服务
@@ -549,6 +560,6 @@ ykxmz 是一套自己搭建的远程管理工具,由三部分组成:
--------------------------------------------------------
作者:小麦子
- 软件:ykxmz 2.0.0
+ 软件:ykxmz 2.1.0
许可条款见《许可协议》(LICENSE.txt)
--------------------------------------------------------
diff --git a/installer/ykxmz.iss b/installer/ykxmz.iss
index 136b40b..3b0ac2a 100644
--- a/installer/ykxmz.iss
+++ b/installer/ykxmz.iss
@@ -27,7 +27,7 @@
; ============================================================================
#define MyAppName "ykxmz"
-#define MyAppVersion "2.0.0"
+#define MyAppVersion "2.1.0"
#define MyAppPublisher "小麦子"
#define MyAppExeName "ykxmz.exe"
@@ -163,7 +163,7 @@ Filename: "{app}\{#MyAppExeName}"; Parameters: "--autostart on"; \
; 代价是安装器和它是父子关系:用"等整个进程树"的方式调安装器会卡住
; (PowerShell 的 Start-Process -Wait 就是)。
; 学校批量部署请用 cmd:
-; cmd /c ykxmz-setup-2.0.0.exe /VERYSILENT /CODE=123456
+; cmd /c ykxmz-setup-2.1.0.exe /VERYSILENT /CODE=123456
; cmd 只等安装器本身,不等孙子进程,不会卡。
Filename: "{app}\{#MyAppExeName}"; Parameters: "--silent"; \
Description: "立即启动(后台运行,不显示窗口)"; \
diff --git a/relay/admin.py b/relay/admin.py
index 6af1501..7e9575f 100644
--- a/relay/admin.py
+++ b/relay/admin.py
@@ -595,8 +595,41 @@ if (autoRef) {
"""
+def _rescue_rows(items: list) -> str:
+ """抢救报告列表的 HTML。
+
+ 照 dl_rows 的风格:在服务端直接拼好塞进模板,不用 JS 去拉 ——
+ 这台机器只有 460MB 内存,多个接口就多份开销,而且后台页
+ 本来就只是一次性的展示。
+
+ 没有报告时给一句正面的话("一直没出过问题"),
+ 而不是干巴巴的"暂无数据"—— 没报告是好事。
+ """
+ if not items:
+ return ('还没有报告 —— 说明网站一直没出过'
+ '「自动救不回来」的问题。
')
+ import time as _t
+ import urllib.parse as _up
+ rows = []
+ for it in items:
+ when = _t.strftime("%m-%d %H:%M", _t.localtime(it["mtime"]))
+ size = it["size"] / 1024
+ size_s = ("%.1f KB" % size) if size >= 1 else ("%d B" % it["size"])
+ summary = _esc(it.get("summary") or "(没有结论行)")
+ href = "/rescue/" + _up.quote(it["name"])
+ rows.append(
+ ''
+ '%s'
+ ''
+ '%s'
+ '
'
+ '%s '
+ % (href, when, size_s, summary))
+ return "".join(rows)
+
+
def render_admin(snap: dict, public: str, admin_path: str, codes: int,
- downloads_fn) -> str:
+ downloads_fn, rescue_fn=None) -> str:
"""渲染管理后台。
snap RELAY.snapshot() 的结果
@@ -629,6 +662,13 @@ def render_admin(snap: dict, public: str, admin_path: str, codes: int,
dl_rows = ('还没有上传任何安装包。'
'把文件放到中转的 downloads 目录即可。
')
+ # 抢救报告列表(和下载区同样在页面里直接渲染,不用 JS 拉)
+ try:
+ rescue_items = rescue_fn() if rescue_fn else []
+ except Exception: # noqa: BLE001
+ rescue_items = []
+ rescue_rows = _rescue_rows(rescue_items)
+
return """
@@ -710,6 +750,12 @@ def render_admin(snap: dict, public: str, admin_path: str, codes: int,
官网下载按钮指向这里的文件。
%(dl)s
+