fix(gpu-monitor): update to version 1.0.1, resolving CRLF line ending issues in scripts; enhance validation and error reporting for GPU probe execution

This commit is contained in:
Kang committed 2026-10-03 19:10:01 +08:00
1 parent e5b7daad99
commit 2b123a67a3
9 files changed
+175 -20

No files matched your search

+13
View File
@@ -28,6 +28,19 @@ node plugins/examples/gpu-monitor/verify-ui.mjs
更新或切换版本会停用插件并移除自动脚本授权,需重新审核。第二个有效的 `gpu.v1` Monitor 无法同时启用。
`native` 后台是以当前用户权限运行的可信程序,宿主 RPC 权限不构成操作系统沙箱。
当前版本 `1.0.1` 修复了 Windows 构建的脚本 CRLF 换行问题。构建时规范为 LF,Git 属性也固定 LF;
已安装 `1.0.0` 时直接安装新版包并重新审核授权,无需为这次修复重新编译宿主。
执行失败会记录退出码,缺少退出状态会单独提示,不把 stderr 或 GPU 进程列表写入日志。
验证最终包中的实际脚本(需要 Python;Windows 使用已安装的 WSL Ubuntu):
```powershell
python plugins/examples/gpu-monitor/verify-probe.py --wsl Ubuntu
```
Linux 使用 `python3 plugins/examples/gpu-monitor/verify-probe.py`。
测试使用隔离的 `nvidia-smi` 固定样本,并验证 CRLF 失败和无 NVIDIA 工具的分支,不连接真实服务器。
## 行为和恢复
- 立即采集一次,随后按既有 GPU 间隔采集,最短 3 秒;请求不重叠,完成后安排下一轮。
+1 -1
View File
@@ -82,7 +82,7 @@ dependencies = [
[[package]]
name = "nyaterm-plugin-gpu"
version = "1.0.0"
version = "1.0.1"
dependencies = [
"nyaterm-gpu",
"nyaterm-plugin-sdk",
@@ -1,6 +1,6 @@
[package]
name = "nyaterm-plugin-gpu"
version = "1.0.0"
version = "1.0.1"
edition = "2024"
rust-version = "1.94"
@@ -9,8 +9,15 @@ impl Plugin for GpuMonitor {
return Err(RpcError::method_not_found(method));
}
let result = context.host().remote_probe("gpu-overview").await?;
if result.get("exitStatus").and_then(Value::as_u64) != Some(0) {
return Err(RpcError::new(-32000, "GPU probe did not exit successfully"));
match result.get("exitStatus").and_then(Value::as_u64) {
Some(0) => {}
Some(status) => {
return Err(RpcError::new(
-32000,
format!("GPU probe failed (exit status {status})"),
));
}
None => return Err(RpcError::new(-32000, "GPU probe returned no exit status")),
}
let output = result
.get("stdout")
+4
View File
@@ -8,6 +8,10 @@ import tailwindcss from "@tailwindcss/vite";
const root = fileURLToPath(new URL("./", import.meta.url));
const repository = path.resolve(root, "../../..");
// Execute on Linux even when this project was created/checked out on Windows.
const probePath = path.join(root, "assets/probes/gpu.sh");
const probe = readFileSync(probePath, "utf8").replace(/\r\n/g, "\n");
writeFileSync(probePath, probe);
const translations = {};
for (const language of ["en", "zh-CN", "zh-TW", "ko"]) {
const locale = JSON.parse(
+1 -1
View File
@@ -2,7 +2,7 @@
"manifestVersion": 1,
"id": "nyaterm.gpu",
"name": "GPU Monitor",
"version": "1.0.0",
"version": "1.0.1",
"description": "NVIDIA GPU monitoring over an existing NyaTerm SSH session",
"publisher": "NyaTerm",
"engine": ">=1.2.12, <2.0.0",
@@ -0,0 +1,60 @@
"""Execute the exact packaged probe in a POSIX shell with controlled NVIDIA fixtures.
Windows: python plugins/examples/gpu-monitor/verify-probe.py --wsl Ubuntu
Linux: python3 plugins/examples/gpu-monitor/verify-probe.py
"""
import argparse
import json
import subprocess
import zipfile
from pathlib import Path
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--wsl", metavar="DISTRO", help="Use an existing WSL distribution on Windows")
parser.add_argument("--package", type=Path, default=Path(__file__).resolve().parents[3] / "temp/plugins/gpu-monitor.nyap")
args = parser.parse_args()
shell = ["wsl", "-d", args.wsl, "--exec", "/bin/sh"] if args.wsl else ["/bin/sh"]
with zipfile.ZipFile(args.package) as package:
manifest = json.loads(package.read("manifest.json"))
assert manifest["id"] == "nyaterm.gpu"
probe = package.read("assets/probes/gpu.sh")
# Reproduce the old Windows packaging failure, then exercise the actual fixed package.
assert b"\r" not in probe, "Packaged POSIX script contains Windows CRLF line endings"
assert not probe.startswith(b"\xef\xbb\xbf"), "Packaged script contains a UTF-8 BOM"
assert probe.endswith(b"\n")
def execute(script, command):
result = subprocess.run(shell + ["-c", command], input=script, capture_output=True, timeout=20)
return result.returncode, result.stdout, result.stderr
unavailable_command = "PATH=/nonexistent /bin/sh -s"
status, _, _ = execute(probe.replace(b"\n", b"\r\n"), unavailable_command)
assert status != 0, "CRLF regression did not reproduce in the Linux shell"
status, stdout, _ = execute(probe, unavailable_command)
assert status == 0 and stdout == b"GPU_AVAILABLE\t0\n", (status, stdout)
fixture_command = r'''set -eu
fixture=$(mktemp -d)
trap 'rm -rf "$fixture"' EXIT HUP INT TERM
cat > "$fixture/nvidia-smi" <<'NYATERM_NVIDIA_FIXTURE'
#!/bin/sh
case "$*" in
--query-gpu=*)
printf '%s\n' '0, GPU-a, NVIDIA RTX 4090, 550.54, 43, 77, 31, 24564, 12345, 12219, 180.5, 450, 46, P2'
;;
--query-compute-apps=*)
printf '%s\n' 'GPU-a, 4242, 2048, python'
;;
*)
printf '%s\n' '| NVIDIA-SMI 550.54 Driver Version: 550.54 CUDA Version: 12.4 |'
;;
esac
NYATERM_NVIDIA_FIXTURE
chmod +x "$fixture/nvidia-smi"
PATH="$fixture:/usr/bin:/bin" /bin/sh -s
'''
status, stdout, _ = execute(probe, fixture_command)
assert status == 0, f"GPU fixture probe exited with {status}"
for required in [b"GPU_AVAILABLE\t1\n", b"GPU_CUDA_VERSION\t12.4\n", b"GPU_CSV_BEGIN\n", b"NVIDIA RTX 4090", b"GPU_CSV_END\n", b"GPU_PROCESS_CSV_BEGIN\n", b"GPU-a, 4242, 2048, python", b"GPU_PROCESS_CSV_END\n"]:
assert required in stdout, f"Probe did not produce {required!r}"
print(f"GPU probe {manifest['version']} passed: exact package LF bytes, CRLF regression, unavailable and NVIDIA fixtures in a Linux/POSIX shell.")