Releases: youridol/dsh-launcher
Releases · youridol/dsh-launcher
Release list
v0.5.6
修复
- 开窗探测误判导致弹窗卡"正在打开"30s 超时:
- 根因:
web_ready判定 HTTP 200 才就绪,但实测 dsh web 对带 token 请求返回
303 See Other(重定向到/走会话交换,浏览器跟随后才 200)——裸 HTTP GET
永远到不了 200 → 探测永不通过 → 前端轮询 30s 超时报"HTTP 未返回 200"。 - 修复:就绪判据改为 2xx/3xx(200/301/302/303/307 均视为 HTTP server 完整响应、
路由已挂);404(冷启动路由未挂)/401/5xx/连接失败仍视为未就绪。新增
http_status_is_success纯函数 + 单测覆盖。 - 效果:内嵌与外部浏览器两条路径都能在 dsh 真正可服务后立即打开(不再 30s 卡死)。
- 根因:
- dsh web 访问地址(token URL)明文写入日志与前端日志流:
- 产品决策(所有者明确要求):用户需从日志复制完整带 token 的访问地址在外部
浏览器手动打开(裸 URL 会被 dsh 401 "authentication required" 拒绝)。 - 安全权衡说明:日志仅本机用户可读写(LOCALAPPDATA),不再对 token 打码;
dsh stdout 落盘文件(dsh-web-stdout.log)本就不打码,此改动使统一日志
(launcher 侧)与前端日志面板同样可见完整 token。
- 产品决策(所有者明确要求):用户需从日志复制完整带 token 的访问地址在外部
- 外部浏览器打开按钮:随就绪探测修复,带 token URL 正常打开(此前同样被探测卡住)。
v0.5.5
修复
- 冷启动弹出内嵌窗口命中 404("找不到此 127.0.0.1 页 / HTTP ERROR 404"),须关闭重开:
- 根因:端口监听/TCP 通 ≠ HTTP 路由就绪。冷启动时 dsh 先监听端口、后挂 SPA 路由,
期间带 token 请求返回 404;此前"等 token + 固定 1s"是启发式,token 打印瞬间
HTTP 路由可能仍未挂好 → WebView2 首载命中 404 错误页且不自动恢复。 - 修复:改为真实 HTTP 就绪探测——新增 Rust
core::port::web_ready()(TcpStream
手写最小 HTTP/1.1 GET,请求带 token 完整 URL,读状态行判定 200)+ IPC 命令
probe_web_ready。- 前端
openWithGuide:拿到 token URL 后轮询probeWebReady直到 HTTP 200
(≤30s,700ms 间隔)才开窗; - Rust
open_web_gui_window(快捷方式/autoOpen 路径):同步轮询web_ready
(≤20s)再建窗。
- 前端
- 效果:内嵌窗口只在 dsh web 真正可服务(HTTP 200) 后弹出,冷启动首载即成功,
不再出现 404 需手动重开。
- 根因:端口监听/TCP 通 ≠ HTTP 路由就绪。冷启动时 dsh 先监听端口、后挂 SPA 路由,
v0.5.4
修复
- 启动后内嵌窗口在 dsh 打印新 token 之前过早弹出(弹窗显示"打开内嵌窗口…"后立即出窗,
需手动重开):- 根因:
process.rs start_locked启动新 dsh 时不清除上一轮的 token 缓存
(last-web-url 文件)。冷启动/异常退出后重启,旧缓存残留 → 前端waitForWebUrl
经get_web_url兜底读到旧 token 秒回 → 不等当前 dsh 输出新 token
(dsh web: http://127.0.0.1:3080/?token=…)窗口就弹出 → 旧 token 无效 → 首载失败。 - 修复:
start_lockedspawn 成功后清空内存 web_url + last-web-url 缓存,前端只能
轮询到当前进程输出的新 token 才放行;- 前端
waitForWebUrl增加校验:仅接受含token=的完整 URL 才返回
(裸 URL / 无 token 值一律继续等,杜绝 401 死窗口)。
- 根因:
v0.5.3
变更
- 启动后打开内嵌窗口增加 1 秒就绪缓冲:
- 前端(StatusCard openWithGuide)与 Rust(open_web_gui_window)两条路径统一:
拿到带 token 完整 URL(dsh 打印dsh web: http://127.0.0.1:<port>/?token=…)后
再等 1 秒才创建内嵌窗口。 - 原因:token 刚输出的瞬间 dsh web 服务/前端资源可能仍在预热(首屏资源、插件
serve 未完),立刻开窗会首载失败/白屏,需手动重开一遍;1 秒缓冲后首载即成功。 - 前端 waitForWebUrl 已保证等 token 出现(≤40s),Rust 路径等 token ≤10s,两者
之后统一 +1 秒缓冲,与"完全启动后 1 秒弹出"的用户预期一致。
- 前端(StatusCard openWithGuide)与 Rust(open_web_gui_window)两条路径统一:
v0.5.2
修复
- 内嵌 Web GUI 白屏 + 所有窗口无法关闭(需任务管理器强杀)——主线程死锁:
- 根因:
create_web_gui_window命令在 async 运行时线程执行同步 fn,内部
run_on_main_thread(建窗)+std::sync::mpsc::recv()阻塞等待主线程建窗结果。
当主线程正被 WebView2 的同步窗口消息占用(内嵌 dsh UI 页面加载/交互时常见,
Windows 窗口消息模型下主线程处理 WebView 消息期间不会让出给 run_on_main_thread
排队的闭包)→ 主线程与等待它的命令线程交叉死锁 → 内嵌窗口停在白屏、主窗口
及所有窗口事件循环卡死、无法关闭。 - 修复:命令改为 async fn,把建窗调度到主线程后立即返回(fire-and-forget),
不跨线程阻塞等待;窗口创建仍在主线程完成(Tauri 要求),创建失败由内部日志
记录。前端不再依赖返回的窗口 label(图标在创建时已同步设置)。
- 根因:
- 移除 create_web_gui_window 返回 label 的契约(前端 lib/tauri.ts
createWebGuiWindow
签名不变,仅不再消费返回值)。 - 内嵌窗口偶发创建失败("the underlying handle is not available"):
apply_window_icon
在 build() 后立即取 hwnd 的竞态失败不再阻断窗口创建(恢复 0.4.14 语义:失败仅记警告,
窗口照常打开)。此前?传播会把已建成的窗口整体判失败 → 不开窗。 - 日志全域化(不允许丢失任何日志):
- 新增全局 panic hook:任何线程 panic 落盘
logs/crash.log+ 经 Logger 记录
(release 无控制台时 panic 此前完全不可见 → 静默崩溃无痕)。 - 关键路径
eprintln!(主窗口图标失败、外链打开失败、Tauri 启动失败)改经 Logger
落盘,release 下不再丢失。 - dsh stdout/stderr 已实时落盘
dsh-web-stdout/stderr.log并逐行进日志流;崩溃
尾部不完整行有 flush 兜底,保证 dsh 侧日志不丢。
- 新增全局 panic hook:任何线程 panic 落盘
v0.4.14
修复
- 修复内嵌 Web GUI(deepseek-harness Web UI)无法打开会话中的超链接:
- 根因(三层):
- dsh Web UI 将 markdown 中的 http/https 外链渲染为
target="_blank"锚点(dsh 侧源码行为,本启动器不改 dsh)。 - launcher 注册的 opener 插件(
tauri_plugin_opener::init(),默认open_js_links_on_click: true)向每个 WebView 注入点击拦截脚本:左键点击_blank外链会preventDefault()并改走plugin:opener|open_urlIPC。 - 该 IPC 受 Tauri 2 ACL 管控:原
capabilities/default.json仅授予main窗口opener:default,内嵌dsh-web-gui-*窗口没有任何 capability →open_url被静默拒绝(release 下 "not allowed by ACL"),点击无任何反应。
- dsh Web UI 将 markdown 中的 http/https 外链渲染为
- 修复:
- 新增
src-tauri/capabilities/dsh-web-gui.json:对dsh-web-gui-*窗口放行opener:default,remote.urls覆盖http(s)://127.0.0.1:*与http(s)://localhost:*(dsh Web GUI 的 loopback 服务地址;缺省组件自动补全配符,见 tauri URLPattern 语义)。除 opener 外不授予任何本地文件/系统命令权限。 lib.rs内嵌窗口 builder 增加 Rust 兜底:.on_navigation(|_| true)显式放行一切导航(窗口只作 dsh Web UI 载体);.on_new_window()收到 WebView2 原生新窗口请求(window.open等)时用系统默认浏览器打开目标 URL 并Deny,杜绝进程内游离 WebView2 子窗口。
- 新增
- 效果:点击会话内 http/https 超链接 → 系统默认浏览器(Edge/Chrome 等)打开新标签;ctrl/shift 点击与
window.open类请求一致处理。 - 验证:tsc / vite build / cargo check --all-targets(0 警告)。
- 根因(三层):
v0.4.13
变更
- 全链路审计修复(依据 AUDIT_REPORT §2–§5):
- 进程安全:收养/停止/端口清剿前校验监听进程确为 dsh(命令行特征),不再强杀占用同端口的无关进程。
- 假功能修复:npm registry 镜像真正生效(npm view / npm install -g / pnpm install 注入 --registry);GitHub tag 列表改 semver 数值排序(0.9<0.10、rc.9<rc.10);工具链检测产出
mismatch(Node 22.19+/24+、Git 2.26+、Python 3.10+ 门槛比较)。 - 凭据安全:GitHub Token 落盘 DPAPI 加密(Windows)、不再回传前端明文、git 认证改环境变量注入(不进命令行);dsh web token 不再明文进日志/日志流(日志打码 + 独立缓存文件)。
- 可靠性:子进程/网络操作全部加超时与强杀(查询/下载/安装/UAC 分档 60s~20min,流式命令 30min 看门狗);配置读写加互斥与原子写;Logger/托盘构建失败降级不再 panic;新增后端每 5s 状态对账(收养实例退出收敛/外部启动自动接管);monitor 收尾按 pid 归属复位,防覆盖重启新实例。
- 前端:面板宽度与对侧开关联动重算(reclamp 接线);日志流增量格式化消除 O(n²);贴顶滚动不再打断阅读;进度清理定时器登记、打开流程取消令牌下渗;版本平局比较按 semver 数值化(rc.9<rc.10);开关/token 表单陈旧闭包与明文回显修正。
- 构建/CI/发布:
tsc -p tsconfig.node.json --noEmit纳入(vite.config.ts 首获类型检查,补 @types/node);cargo check --all-targets;补离线集成测试,网络测试#[ignore];release 流程 bump 后显式打 tag、决策前同步远端 master、skip-release 不再自动递增;构建期依赖归入 devDependencies;清理死代码/重复实现与注释/文档漂移(AGENTS.md 悬空引用等)。 - 验证:tsc / vite build / cargo check --all-targets(0 警告)/ cargo test --lib 35 通过 / 6 个离线集成测试通过。
v0.4.12
变更
- 移除版本管理面板通道按钮上的版本数角标:
- 原按钮组(GitHub 通道 / npm 通道)右侧显示通道可用版本总数角标(如 GitHub 10、
npm 15)。该数字为远端拉取到的全部版本数,而列表只展示最新 8 个 → 与所见不一致
易误导。移除角标,按钮仅保留通道名称,干净无歧义。 - 版本 0.4.12。
- 原按钮组(GitHub 通道 / npm 通道)右侧显示通道可用版本总数角标(如 GitHub 10、
v0.4.11
变更
- 优化内嵌打开逻辑:自动拉起 dsh 并等待就绪后才开窗 + 进度小弹窗:
- 此前点击“内嵌打开/外部浏览器”时若 dsh 未运行,直接 toast 报错要求手动先点“启动”
——流程割裂;若 dsh 冷启动中则静默轮询无感知。 - 现在(components/StatusCard.tsx):点击后弹出进度引导弹窗,阶段化展示
(检查状态 → 启动 dsh → 等待就绪(端口监听) → 获取访问地址 → 打开窗口),
每阶段推进度条与文案;dsh 未运行/出错时自动调用 startDsh 拉起(含端口校验、
端口占用由 Rust 端报错),停止中则等待停止完成后自动重启;就绪后拿到带 token 的
完整 URL 才创建内嵌窗口/打开外部浏览器(杜绝 401/拒绝连接死窗口)。 - 弹窗含取消、失败重试按钮(重试走 force 重开流程);用流程序号(useRef)防竞态——
取消/重开后旧流程异步回调不再污染状态;未安装 dsh 时弹窗明确提示先安装。 - 验证:tsc / vite / cargo release / NSIS 构建通过。版本 0.4.11。
- 此前点击“内嵌打开/外部浏览器”时若 dsh 未运行,直接 toast 报错要求手动先点“启动”
v0.4.10
修复
- 卸载 Git / Python 失败:“Start-Process : 系统找不到指定的文件”(真实客户机
192.168.3.122 实测复现 + 验证):- 现象:点卸载 Git / Python → 日志报
Start-Process -FilePath 'C:\Program'
-ArgumentList 'Files\Git\unins00...'(路径在第一个空格处截断)→
“系统找不到指定的文件”。 - 根因:注册表 UninstallString 是整体引号包裹的路径,如
"C:\Program Files\Git\unins000.exe"、
"C:\Users\...\Package Cache\{...}\python-3.14.0-amd64.exe" /uninstall。
core/toolchain.rs 的 find_uninstall_entry 在收集候选时执行
trim_matches('"')提前剥掉首尾引号 → 返回值变成无引号含空格的裸路径
→ split_uninstall_cmd 按“无引号:第一个空白前为 exe”切分 → exe 截断为
C:\Program/C:\Users\Administrator\AppData\Local\Package→
Start-Process 找不到该“文件”。 - 修复:find_uninstall_entry 保留原始 UninstallString(含引号),引号解析统一
交给已正确支持引号路径的 split_uninstall_cmd(对 Git 整体引号、Python 引号+
参数两种真实格式均正确解析)。 - 验证:测试机 192.168.3.122 实测——旧逻辑 exe=C:\Program(不存在)复现报错;
修复后 exe=C:\Program Files\Git\unins000.exe 与完整 Python 缓存路径均解析正确
且文件存在;新增 test_split_uninstall_cmd_real_registry_values 回归测试
(真实注册表值 Git/Python)。版本 0.4.10。
- 现象:点卸载 Git / Python → 日志报