feat: show a launch-failure dialog with error detail and log tail (issue #30) - #36
Conversation
Gu-ZT
left a comment
There was a problem hiding this comment.
感谢 PR,功能方向与 issue #30 的诉求吻合(Rust 侧 read_instance_log_tail 实现质量很好:缺失文件返回空、64 KiB 有界读取、from_utf8_lossy 兼容 TUI 原始字节、instance_id 先校验再拼路径)。但当前前端有两个必须修复的问题,本次 request changes:
阻塞项
1. [HIGH] 正常退出(exit code 0)会误弹「实例启动失败」对话框
src/stores/launcher.ts 的 applyStatus 对任何 exited 状态都调用 reportLaunchError,未检查 exit code。#33 合入后这成为可见回归:TUI 实例的正常结束方式就是用户在终端里输入 exit,PTY 子进程以 code 0 退出 → waiter 发 Exited(0) → 弹窗「实例启动失败 / exited unexpectedly (exit code: 0)」。web 实例进程自行以 0 退出同理。
修法:仅在 st.exit_code 非 null 且非 0 时报告(exit_code == null 的语义请一并考虑——waiter 拿不到退出码时传 None,对「启动失败」场景建议 null 也报告,即 st.exit_code !== 0)。
2. [MED] 浏览器预览 mock 的 switch 穿透
src/api/index.ts 中新增的 case 'read_instance_log_tail': return [] as T 插在了 open_instance_log / open_instance_directory 两个 case 标签与它们原本的 return 'C:\\…' as T 之间且没有 break——这两个既有 mock 命令现在返回 [] 而非路径字符串,原 return 成为不可达死代码。把新 case 移到原 return 之后即可。
建议一并修复(不阻塞)
- 连续两次失败时日志尾部不刷新:dialog watch 的是
visible(!!launchError的 computed),对话框已打开时第二次失败不会重新触发 watch,摘要是新错误但日志尾部还是上一次的。改为 watchstore.launchError; - 日志冲刷竞态:waiter 发 Exited 时 stdout/stderr reader 可能还没把最后几行崩溃日志写进文件,对话框打开即读尾部可能缺最后几行。可加短延迟或重读一次;
Home.vuecatch 里的else Message.error分支不可达(onStart 在未选实例时已 early-return)——可顺手删掉;- 两个新文件末尾缺换行符;
read_tail没有单元测试(非阻塞,建议补一个有界读取/缺失文件/尾行丢弃用例)。
其他
- 分支基于 #34 合入前的 main,与当前 main 有一个
src/stores/launcher.ts冲突(externals与launchError字段位置相邻,保留两者即可),rebase 后 CI 才能跑起来; - i18n 双语言已验证齐全一致 ✅;
cargo fmt --check与cargo test --lib(91 passed)在 PR 分支上验证通过 ✅。
修好 1、2 并 rebase 后即可合入。
…sue dsh-plugins#30) Startup failures currently surface only as a transient Message.error toast, forcing users to hunt through logs/<instance_id>.log manually. New behavior: - backend: read_instance_log_tail command returns the last lines of an instance's runtime log (bounded to the final 64 KiB), registered in the invoke handler and the browser-preview mock; - store: an unexpected 'exited' status (distinct from user-initiated 'stopped') records a launchError with the exit code; - frontend: InstanceLaunchErrorDialog shows the failure summary plus the log tail, with Copy error info / Open full log / Close actions; sync start failures from Home.vue route into the same dialog; - i18n: zh-CN and en-US keys for the dialog.
- 仅在退出码非 0(或缺失)时报告启动失败,TUI 终端 exit 等正常退出(code 0)不再误弹对话框 - 修复浏览器预览 mock 的 switch 穿透:open_instance_log/open_instance_directory 恢复返回路径字符串 - 对话框改 watch launchError 本体,二次失败时日志尾部随之刷新;读取前延迟 350ms 并在 800ms 后重读一次,缓解日志冲刷竞态 - 删除 Home.vue 不可达的 else 分支;组件文件补末尾换行 - 新增 read_tail 单元测试:缺失/空文件、尾行截取与末尾换行、64 KiB 有界读取
33ea65a to
ae375f2
Compare
Closes #30
问题 / Problem
启动实例失败时,主页只显示一条短暂的
Message.error提示,用户无法从提示中获得具体原因,必须手动打开logs/<实例id>.log排查——对非技术用户极不友好。改动 / What changed
后端 (Rust)
read_instance_log_tail命令:读取实例运行日志尾部(最多 200 行,读取范围限制在文件末尾 64 KiB),缺失文件返回空而非报错;已注册进 invoke handler 与浏览器预览 mock。前端 (Vue/TS)
launcher.ts):exited事件(非用户主动stopped)现在会记录launchError(含 exit code),不再静默丢弃状态。InstanceLaunchErrorDialog组件:标题「实例启动失败」,展示错误摘要 + 关键日志尾部,底部操作「复制错误信息 / 打开完整日志 / 关闭」。Home.vue:同步启动失败(前置校验 / spawn 失败)同样接入该对话框。App.vue挂载对话框;api/index.ts封装readInstanceLogTail;i18n 中英文案齐全。验证 / Validation(与 CI 相同命令)
cargo fmt --check✅cargo clippy --workspace --all-targets -- -D warnings✅cargo test --workspace✅(91 passed)pnpm build(vue-tsc + vite)✅node ci/check-versions.mjs✅node --test ci/release-notes.test.mjs/bump-version.test.mjs✅