Repository navigation
发布 v0.6.2:统一操作失败提示,并修复 SSH 主机指纹确认框 - #49
Merged
Merged
Conversation
SSH 主机页「测试连接」调用 ssh.exec。目标是新主机(1002)或指纹变化(1001)时,
固件把指纹放进 result->data,同时返回非 OK;HTTP 出口的通用失败分支只输出
{code,error} 并释放 data,页面取不到指纹就跳过确认框,用户只看到「连接失败」,
无法在该入口信任新主机,也无法核对并接受指纹变化。
- ts_webui_api.c:通用失败分支同样输出 result.data;没有 data 的失败响应不变。
同一处顺带加固:code / error 写不进去(内存分配失败)时改发 HTTP 500,
不再发出缺 code 的残缺 JSON(原有漏洞,由下面的分配失败遍历发现)。
固件的指纹校验没有改动:确认必须带回当前连接上的指纹,否则继续拒绝,
校验失败时不发凭据、不执行命令。
- 新增 tests/runtime/test_ssh_fingerprint_wire.py:运行生产的 ssh.exec / ssh.test
处理函数和 HTTP 响应分支,检查线上字节,并存为契约夹具
tests/fixtures/ssh-fingerprint;并在构建失败响应的每个分配点注入失败,
结果只能是完整响应或 HTTP 500。
- 新增 tests/prompts/ssh-fingerprint.browser.test.cjs:把夹具原样回放给真实页面,
中英文检查确认框内容、取消不信任不重试、确认只发送用户看到的指纹、
确认时设备换密钥则不信任且不循环。
- test_keystore.py:原先钉着「其他接口的失败响应不带 data」,按新决定改为断言带上
(只改了这一条断言和模拟字段名,共 2 行);密钥生成原有的失败阶段、清理结果、
序列化失败返回 500 等测试全部保留,并逐一破坏生产代码确认它们仍会报错。
- docs/repair/ssh-fingerprint/RESULT.md:根因、修复与验证记录。
验证(未接触真机):运行时全套 41 项、静态 122 项、浏览器 249 项、发布检查通过;
ESP-IDF 5.5.2 完整编译通过。还原修复后运行时测试断言失败;夹具换成旧格式后
浏览器测试失败。
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
修复「测试连接」确认框之后,指纹变化(1001)和新主机(1002)弹出的是同一个框: 不说指纹变了,也不显示已登记的旧指纹,用户容易当成普通确认点过去。 - 指纹变化时:标题「安全警告:主机指纹不匹配」,说明可能原因(中间人攻击、服务器重装、 IP 被分配给了别的服务器),同时显示已登记指纹和当前指纹,确认按钮为「更新主机密钥」, 默认焦点在「取消」,回车等于取消。 - 新主机的确认框不变。 - 文案沿用已有的 securityPage.* 语言包条目,没有新增翻译。 - index.html 中 app.js 的缓存版本串提升为 0.6.1-lpmu6-keygen2-fp1。 - 浏览器测试补充:标题、按钮文字、两个指纹、默认焦点、回车不接受;去掉本提交后这些断言失败。 验证(未接触真机):静态 122 项、浏览器 249 项、运行时 41 项、发布检查通过。 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
app.js 删除确认没有任何入口的旧函数 50 个(约 900 行):LED 旧控制与滤镜、 旧 SSH 主机管理、旧自动化变量页、旧动作行等;同时删除它们的 27 条 window 导出、因此失去用途的 selectedFilter / allVariables 变量,以及再也打不开的 「主机指纹不匹配」旧弹窗及其处理函数。app.js 减少约 47 KB(gzip 约 8 KB)。 确认方式:语法树可达性分析加全文搜索(含字符串里的函数名、window.xxx、 模板里的 onclick);删除后重新分析无人调用函数为 0;静态 122 项、浏览器 模拟 232 项与删除前一致。handleModalLogMessage 经 window 在运行时被调用, 不在删除范围内。 input-repair 浏览器测试原先直接调用旧矩阵页的 applySelectedFilter, 改为在现行的弹窗滤镜路径上验证同样的零值保留。macOS 27 对照清单与说明 文档同步。 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
样板间 B、C 需要固件多说一点;只加字段、不改任何业务,旧界面照常工作。 快捷操作点击后被拒(automation.rules.trigger): - 引擎里新增 ts_rule_trigger_ex(),除返回值外还报告拒绝原因:引擎未在接收执行请求、 规则被禁用、不允许手动执行、配置未加载、需要恢复、配置正在保存、规则正在执行、 服务状态不允许启动、规则刚被修改。原来这些全被压成同一句 "Failed to trigger rule"。 ts_rule_trigger() 行为不变(内部调用 _ex)。 - API 把原因代号写进 message(如 engine_not_accepting、execution_busy); 忙碌类仍用 BUSY 错误码,旧界面还能说"设备正忙"。规则不存在、其他未知原因的返回不变。 SSH 连接失败(ssh.exec / ssh.test): - 客户端记录连接停在哪一步:域名解析、端口被拒、主机不可达、超时、握手、主机指纹、 认证(ts_ssh_get_fail_stage);错误文字 "SSH connection failed (code=N)" 与所有返回值不变。 - ssh.exec 的连接失败改用原因代号(ssh_dns_failed、ssh_port_refused、ssh_host_unreachable、 ssh_connect_timeout、ssh_handshake_failed、ssh_auth_failed、ssh_connect_failed), data 里带 stage 和客户端原文 detail;ssh.test 保持原来的成功信封,另加 reason 和 stage。 测试(主机上运行生产代码,未接触真机): - 引擎:逐一覆盖每种拒绝原因;未知规则不报原因;ts_rule_trigger / _ex 两个入口。 - SSH 客户端:逐一模拟每个失败阶段,并确认原有返回值与错误文字不变。 - test_failure_wire.py:规则保存/启停的回复和触发被拒经生产的 HTTP 响应链,线上字节存为 tests/fixtures/failure-wire;test_ssh_fingerprint_wire.py 增加每种 SSH 连接失败的线上字节。 夹具与代码不一致时测试失败,浏览器测试用同一批字节回放。 - 逐一破坏(原因搞错、原因丢失、阶段搞错),这些测试都会报错。 - test_stop_protocol 沿用的是 test_engine 的前半截,桩件原文未动,新开关用宏叠加。 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
三个样板间,作为后续铺开到全部页面的范本: - 规则:启用 / 禁用 / 保存。写出规则名而不是内部 id;原因是人话("它正在执行中"), 不再先说"提交的信息不符合要求"再跟内部代号;失败了不再写"已启用";成功说"已启用规则“x”", 而不是 "…: ok";结果未确认单独成句并提示不要重复提交。 - 快捷操作:原来一句"当前不允许启动:请检查启用、手动权限及服务状态"塞了 5 种可能, 现在卡片知道自己为什么不能启动(规则已禁用、不允许手动执行、引用未解析、详情读取失败、 服务运行中/启动中/核验中/停止中/未确认已停止),分别说;点击后被固件拒绝的 9 种也各说各的。 "请等待几秒后再触发其他模型"改为完整句子"刚刚触发过其他快捷操作,请等几秒再试。" - SSH 测试连接:按连接停在哪一步说(解析不了主机名、对方拒绝连接、无法到达、超时、 无法建立 SSH 加密连接、主机拒绝了用户登录);取消确认框是中性提示而不是"连接失败"; 确认时对方换了指纹会明确说没有信任它。 公共部分: - 语言包新增 failure 目录:拼句格式、各操作名称、约 50 个"原因 + 下一步"(中英文)。 - api.js describeFailure():按原因代号取句子;查不到的代号按错误类别诚实地说, 设备英文原文只放进折叠的"详情";缺参数时降级;绝不出现"说明暂时无法显示"。 - app.js 新的失败提示条:停留 8 秒,鼠标悬停或展开详情时暂停,有"详情"折叠(原因代号、接口、 设备原文、阶段、时间)和关闭按钮;不会被随后的普通成功提示冲掉。其他页面的提示不受影响。 - runtimeSaveError 唯一的使用者被替换,已删除。 测试: - failure-catalog:两种语言一一对应;每条文字符合写作规范(不带机器代号、中文里无多余英文、 原因无句末标点、下一步是完整句子);固件会发出的每个原因代号必须有对应说法(从 C 源码读取)。 - failure-messages:提示条行为;每种语言下的标准句子;规则、快捷操作的真实页面流程, 回放固件线上字节;SSH 连接失败 7 种 + 老固件无指纹 + 确认时换指纹。 - 受影响的旧测试同步:快捷操作被阻止、SSH 取消、规则保存的 Node 版测试; 静态检查把 failure 目录整族纳入。 验证(未接触真机):静态 127、浏览器 305、运行时 43 项、发布检查通过;ESP-IDF 5.5.2 完整编译通过。 补充:详情只在有可看的内容时出现(页面自己判断出的拒绝没有接口、设备原文,就没有「详情」链接); × 与「详情」同一行;新网页配旧固件(只发英文原文)时仍按错误类别诚实地说; 写操作没拿到设备回答一律说"结果未确认"。写法与新增方法见 docs/repair/failure-messages/README.md。 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
只加字段、不改业务:失败时 message 换成固定的原因代号(网页按语言翻译), 数字错误码按"最近类别"选,旧网页仍能按类别说话。 - 引擎:启动/暂停被拒时按引擎当时状态报 engine_paused / engine_not_running / engine_unavailable;停止超时 engine_stop_timeout;重载读配置失败 config_file_invalid(原始错误名放 data.detail);内存不足 no_memory - 动作模板:数量满 template_capacity、重复 ID template_exists、LED 滤镜参数 不合法 led_filter_invalid;服务绑定保护沿用 service_delete_protected - 动作测试:没跑成功时报 action_failed / action_timeout,动作自己的输出放 data.detail(折叠详情里看);找不到模板用 NOT_FOUND - 数据源创建:表满 source_capacity;管理器未就绪 engine_unavailable。 原先 INVALID_STATE 被说成"该 ID 已存在",其实 ts_source_register 对已有 ID 是更新而不是拒绝,这句话是错的 结果拼装抽成小函数(fail_with / engine_refusal_token / action_template_write_reply / action_execute_failure / source_register_reply),主机测试用生产代码 + 生产 HTTP 响应链生成线上字节存进 tests/fixtures/failure-wire/,漂移即失败。 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
按"动作+对象失败:原因。下一步。"重写,成功也说清做了什么、对谁: - 引擎四个按钮、规则(手动触发/删除/读取)、动作模板(保存/测试/读取/删除)、 数据源(启用/禁用/删除/创建)及三个列表的读取失败;列表读取失败不再被当成"空列表" - 保存动作模板的必填项校验改为贴在输入框旁并自动定位(空的是变量名还是值分别提示) - 规则编辑器里"没有动作模板"和"动作模板列表读不出来"是两回事,分别说 - 页面自己的脚本出错单独说"页面出了问题"并把脚本报错放进详情,不再冒充设备失败; 写操作的网络中断一律说"结果未确认"(引擎启停等是用 GET 发出的写操作) - 英文必填提示改成通顺的句子;详情里"设备原文"改称"原始信息"(网络错误文字不是设备说的) 顺带修复:数据源的启用/禁用开关一直先去查"同名规则",查不到就报错,请求从未发给设备 (v0.5.2 起;规则才需要版本检查)。现在直接请求 sources.enable/disable,并有回归测试。 测试:新增 tests/prompts/flows.browser.test.cjs,用真实页面 + 生产固件的线上字节逐场景 回放,中英文句子存进 tests/fixtures/failure-texts/automation.json(评审文案看这份); pagekit.cjs 抽出公共的页面脚手架;目录测试改为读取每个调用点真正需要的文案, 并检查目录里没有无人使用的条目。 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
只加字段、不改业务:失败时 message 换成固定的原因代号,数字错误码按"最近类别"。
- 配置包(.tscfg):新增 ts_config_pack_reason(),13 种结果各有自己的代号
(不是为这台设备生成的 / 签名没通过 / 内容损坏 / 已过期 ……),库里的英文原句放进
data.detail;automation.{sources,rules,actions}.{export,import} 六个接口改用它。
导出权限不足 pack_not_allowed、设备证书不可用 device_cert_unavailable;
写文件失败统一 storage_failed(errno 文字放详情)
- 规则配置包:验证和预览可以,真正导入一直返回 "source_read_only: encrypted rule
import requires a recoverable writer"——这是功能没做完,不是"只读来源",现在如实报
rule_import_unavailable(原文放详情)
- 数据源连接测试(REST / WebSocket / Socket.IO 代理):连不上 proxy_connect_failed、
对方回了 HTTP 错误 proxy_http_error(状态码数字 + 回应开头放 data)、WebSocket
连接出错/超时/没收到消息 ws_*、Socket.IO 没按协议回应/等不到事件 sio_*;
原来都是写死的英文句子
主机测试用生产代码 + 生产 HTTP 响应链生成线上字节,存进 tests/fixtures/failure-wire/。
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- 数据源「测试」:REST / WebSocket / Socket.IO 的失败说清是哪个地址、为什么、怎么办;
测试不改任何东西,所以断线说"无法连接到设备"而不是"结果未确认"(write:false)
- 配置包导出 / 验证 / 导入(数据源、规则、动作模板):失败写进对话框结果框,详情放提示;
对象叫法放进 failure.kinds,英文句中小写;"请先选择文件"说明要选的是 .tscfg 配置包
- 变量 / 图片 / 主机的选择列表读取失败不再被当成"空列表";SD 卡未挂载单独一句
- 设备回的数字(如 HTTP 状态码)可以填进原因文案里的 {status}
- 英文表单提示补冠词、改成通顺的句子
测试:新增 41 个场景,中英文句子存进 tests/fixtures/failure-texts/automation.json。
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
只加字段、不改业务:失败时 message 换成固定的原因代号,数字错误码按"最近类别",旧网页仍按类别说话。 - 新增公共函数 ts_api_result_reason(result, 类别, 代号, 详情)(原先只在自动化接口里有)和私有头 ts_api_pack.h(配置包失败的统一回复,自动化与 SSH 两边共用) - 主机指纹:保存/核对失败 known_host_failed;新主机 ssh_host_key_unknown、指纹变化 ssh_host_key_changed(数字码 1002/1001 不变)——页面里其他调用 ssh.exec 的地方也能说清 - ssh.exec / ssh.test / ssh.copyid / ssh.revoke:登录信息不全 ssh_config_invalid、会话创建失败 no_memory、密钥库没有这把密钥 key_not_found、公钥格式错 pubkey_invalid、服务器拒绝部署 ssh_deploy_failed(服务器的 stderr 放详情)、撤销前检查/撤销失败、命令没执行成功 ssh_exec_failed;连接失败沿用上一批的阶段代号 - ssh.exec_stream / ssh.cancel:另一条命令在跑 ssh_exec_busy、没能开始 ssh_start_failed、 没有正在执行的命令 ssh_no_session、取消没送达 ssh_cancel_failed - 主机 / 指令数量满 host_capacity / command_capacity;密钥重名 key_exists;服务不允许启动 service_not_admissible - 配置包导入导出(主机、指令)改用共用回复;写文件失败 storage_failed - 连接失败的阶段映射挪进 SSH 客户端库(ts_ssh_connect_reason),WebSocket 的命令执行错误也用它: ssh_exec_error 消息现在带原因代号 + stage + detail;输出送达不完整 / 结果送达失败改成代号 主机测试按新代号更新(指纹相关的线上字节夹具随之改变)。 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- 安全页:已知主机指纹、主机记录、撤销公钥、删除/导出密钥、部署/撤销密钥的失败都说清 "对谁做什么失败:原因。下一步。";部署/撤销里缺哪项就把光标放到哪项;主机列表读不出来时 说明原因,不再只写"加载失败";撤销成功但没能移除本机记录单独说 - 指令页:保存/删除指令、执行(含 WebSocket 推回来的错误)、取消、启动服务、查看日志; 辅助命令(读日志/查进程)失败不再显示成"无输出";必填项贴在输入框旁;ID 规则一次说全 - 配置包导出(SSH 主机、指令)失败写进对话框;导出句式改成"导出“名称”(种类)失败", 中文里的英文种类(SSH 主机)前后不再粘连 - 行内失败提示现在也有「详情」链接,和提示条一致("点详情可以看到…"在哪里都成立); 浏览器拒绝写剪贴板单独说明;列表过期(找不到这台主机/指令)说明是页面过期 测试:新增 53 个场景,中英文句子存进 tests/fixtures/failure-texts/ssh.json 和 ssh-commands.json; 几个旧测试跟着新句子更新。 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Persist complete versioned rule sets with one selector, exact-byte acceptance records, target-local revisions and restart-pending guards. Validate signing authority, recipient identity and dependencies without publishing imported rules at runtime. Fix datasource toggle routing and add production crypto, migration, fault-recovery, engine/API and bilingual browser coverage.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Classify active/saved union states and block new admission after confirmed pending deletion while preserving existing runtime objects and leases. Keep ordinary rule loading compatible, retain published source identity, and scope strict dependencies to active and saved package versions. Share effective-action/proposed-object validation for direct/reference SSH, internal-first host fallbacks and unsupported Webhook actions. Add the directed A1-A3, B1-B3 and C1-C3 production-path regressions and bilingual pending-delete/error UI checks. Preserve v3 storage, encrypted trust and save-only/reboot activation. Record red/green evidence and unverified device/deployment prerequisites in R1-R3-review.md.
Preserve both reviewed histories, PR README/banner changes and existing ordinary-reference deletion diagnostics. Keep package dependency guards in the checked removal path and retain new-reference validation on saves. Use the same recursive SSH binding gate for nested cold operations and nonblocking edit admission with binding-before-transaction lock order. Adapt production-path fixtures without weakening their assertions. Validated combined runtime suite, exact HTTP fixtures, 33 unit/static checks, 58 bilingual browser cases and isolated ESP-IDF 5.5.2 build. Device acceptance remains unexecuted; see INTEGRATION.md for evidence.
冲突已按“保留上游新逻辑、失败提示走统一目录”解决:删除保护弹窗保留;规则配置包的验证/导入失败、 快捷操作被拒(待删除/未生效)、规则保存失败(模板已不存在)改由 failure 目录出句子。 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- 规则配置包验证/导入失败、结果未确认改由 failure 目录出句子(原因 22 条新增;旧的 rulePack.errors 整张表删除) - 快捷操作:待删除/未生效的规则被拒时说明原因 - 固件:config pack 新结果码(时间未确认、未配置信任、签名者无权限、凭证变化)映射为原因代号;引擎拒绝待删除规则时给出 rule_pending_delete - describeFailure 读取错误自带的 data(上游让 ApiOperationError 带上 data) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- 44 个自动转换留下的“保存失败/设置失败”式提示改成“动作+对象”,并补上“结果未确认”的说法 - 设备原因不再被页面自己抛的 Error 吞掉:OTA 上传/服务器、证书、配置包都带原因代号(pageFailure) - 登录失败只在表单里显示一次;目录读取失败不再套用“上传结果不受影响”;更新检查不再重复“检查更新失败” - 密钥生成弹窗保留失败阶段与清理结果;规则保存缺少动作模板时说出模板名 - 折叠详情里的“接口”统一写成 storage.mkdir 这种形式;错误自带 data 时也显示阶段与原始信息 - 删除 198 条已无人使用的语言包旧键和 1 条已不存在的原因(规则导入已实现) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- failure-catalog:新增“每个调用点都给足句子里的占位值”检查;一次列出全部问题 - flows:新增 system 组(登录、重启、服务、文件、网络、OTA、证书、配置包)和阻断删除弹窗、规则包导入场景;黄金句子重生成 - 上游测试适配:rule-pack/delete-reference 用 describeFailure 取句子;runtime 测试补桩与抽取清单 - ws 适配测试:补 ts_ssh_connect_reason 桩;帧类型按 type 字段判断,不再被原因代号里的 ssh_output 误判 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- 引擎:删除待重启的规则,手动触发给出 TS_RULE_REJECT_PENDING_DELETE(优先于“正在保存/正在执行”),自动评估保持静默 - 线上字节:新增 wire-trigger-rule-pending-delete.json,并让“每个拒绝原因都有代号”的循环覆盖到它 - 页面:触发该规则的句子写入黄金清单;快捷操作卡片在“待删除/还没生效”时各自给出原因 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…-prompts RMinte-AI#47、RMinte-AI#48 在上游是以 squash 方式合并的,分支里保留的是它们原来的提交,直接合并会产生大量假冲突。 这里按正确的共同祖先(RMinte-AI#47 的头提交 bdc987d)做三方合并:我方改动与 RMinte-AI#48 改动没有重叠的文件,结果干净无冲突, 合并后相对 origin/main 的差异就只剩本 PR 自己的改动。 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- version.txt 升到 0.6.2;新增 docs/releases/v0.6.2.md(汇总 RMinte-AI#47、RMinte-AI#48、RMinte-AI#49)和 v0.6.2-validation.md - 合并到上游 main 后由 Actions 自动创建标签 v0.6.2 并发布 Release(check_release_state.py 的 main 路径) - README 版本说明、静态资源 ?v= 标识同步为 0.6.2 - tests/ws_subscriptions/test_f1_f2.c:按帧的 type 字段判断“输出帧”,不再被原因代号里的 ssh_output 字样误判 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
CI 的证书回归按名字从页面脚本里取 certError 并断言设备文字原样显示;迁移后该函数已删除,失败提示改由 failure 目录生成。测试改为:直接运行 api.js 里生产的 describeFailure 与 app.js 的 packBox/failureHtml, 断言句子来自目录、设备给的类 HTML 文字只以转义形式出现在折叠详情里、断线按“结果未确认”处理且只提交一次、按钮恢复。 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- RMinte-AI#47 新增的配置包签发根(CONFIG_TS_CONFIG_PACK_SIGNING_ROOT_SHA256,默认空)还没写进已跟踪的 sdkconfig, 每次构建都会把它补上;这里先提交,保证从提交出来的干净检出构建不会改动已跟踪文件 - tools/make_firmware_manifest.py:发版时把干净构建的 6 个镜像/映射文件复制到 build/ 并生成 firmware-manifest.json, 并检查构建版本与 version.txt、被构建提交一致(HASH 后带 d 的未提交修改构建会被拒绝);--verify 核对 build/ 与清单 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- 从干净检出的提交 774c3a9 用 ESP-IDF 5.5.2 构建;版本 0.6.2+774c3a97.10130158,主固件 2,226,000 字节 - build/firmware-manifest.json 记录版本、被构建提交、源码输入哈希和 6 个文件的 SHA-256(tools/make_firmware_manifest.py 生成,--verify 通过) - 验证记录补充:镜像来源与更新方法、证书回归已在本地跑通 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
合并即发布 v0.6.2
本 PR 同时是 0.6.2 的发布 PR。合并到 main 后,GitHub Actions 会自动构建、通过 CI Gate,然后创建标签
v0.6.2并发布 Release(附TianShanOS.bin、www.bin等),Release 正文取自docs/releases/v0.6.2.md,汇总 #47、#48、#49 三个 PR 的更新。version.txt升到0.6.2,加入中文发布说明和验证记录(docs/releases/v0.6.2-validation.md),README 版本说明和静态资源?v=标识同步为 0.6.2。tools/ci/check_profile.py判定“上游仓库 main 推送”有发布资格(fork 推送、PR 事件没有);tools/ci/check_release_state.py取标签 =v+version.txt,该版本尚无 Release 才发布;tools/check_release.py要求标签、version.txt、编译版本一致,且中文说明不少于 100 字。v0.6.2标签:标签由 Action 打在合并后的提交上。提前打在别的提交上,预检会因“标签与构建提交不一致”拒绝发布,并且不会移动标签。v0.6.2的标签和 Release 目前都不存在。build/下的默认刷机镜像已更新为 0.6.2:从干净检出的提交774c3a97用 ESP-IDF 5.5.2 构建(版本0.6.2+774c3a97.10130158,主固件 2,226,000 字节,3 MiB 分区剩 29%,构建不改动任何已提交文件)。build/firmware-manifest.json记录版本、被构建提交和每个文件的 SHA-256。新增tools/make_firmware_manifest.py生成清单(--verify可核对,带未提交修改的构建会被拒绝)。Release 附件由 Actions 另行构建,字节会和这里不同。与 #47、#48 的关系
#47、#48 已合并(squash)。本分支原本是在 #47 之上做的,直接合并最新 main 会因为 squash 产生大量假冲突,所以按真实的共同祖先(#47 的头提交
bdc987d9)做了三方合并:我方改动与 #48 改动没有重叠的文件,结果干净。合并后 Files changed 只剩本 PR 自己的改动(130 个文件,+7,066 / −2,438,测试占大头)。Commits 标签页里会出现 #47 原来的 9 个提交(上游是 squash 合并,SHA 不同),可以忽略。概述
用户点按钮后操作失败时,WebUI 现在统一用「动作+对象失败:原因。下一步。」说话(英文:
Failed to X “对象”: reason. Next step.)。固件只返回一个固定的原因代号,网页按当前语言把它翻成句子;设备确实说不清原因时,诚实写「设备没有返回具体原因」并指向「终端」页的系统日志,不编造原因。写操作遇到断线或没有应答时,不再冒充“失败”,而是写「…的结果未确认」并提醒“先核对状态、不要重复提交”。另外包含两件独立的事:一个 SSH 缺陷修复(主机指纹确认框弹不出),以及一次无入口旧代码的清理。想先看效果,读
tests/fixtures/failure-texts/*.json(189 个失败场景,中英文各一句,就是用户真正看到的话,当文档读即可)。为什么做
原来的失败提示很难让人定位问题。全量盘点(约 410 条弹出提示、170 条行内提示、790 条接口错误文案)后,典型问题是:
Failed to trigger rule;SSH 连接失败(密码错、端口不通、DNS、超时…)统统落成SSH connection failed (code=…)。capacity、storage_failed…)直接当文案;启停失败时还写“已启用/已禁用”。throw new Error(设备给的文字),设备的真实原因被当成“页面出错”丢掉。改后的句子(节选,均来自上述黄金清单):
/sdcard/b.txt: 删除文件或文件夹的结果未确认。请先刷新核对当前状态,不要重复提交。
/sdcard: 删除文件或文件夹失败:根目录不能删除。请只删除里面的文件或文件夹。
句子怎么拼
failure.format:{动作+对象}:{原因}。{下一步}(英文各有自己的标点)。三部分各有来源:failure.ops.<组>.<操作>.{failed,unknown,blocked,done},每个按钮自己的,带对象名failure.reasons.<代号>.{why,hint};代号由固件返回(或旧固件的英文原文经对照表映射)no_detail。设备的英文原文永远不进句子,只进折叠的「详情」js/api.js的describeFailure({op, params, result|error, token, endpoint, write})按固定顺序选原因:明确代号 → 错误类型(网络/超时/格式)→ 页面自己的错误(page_error)→ 设备代号(目录里有且参数齐才用)→ 旧英文原文对照 → 数字类别兜底 →no_detail。write: true的操作拿不到应答时是“结果未确认”,读取/测试则说“无法连接”。页面自己发现的问题用pageFailure(代号, 参数),设备给的原因必须用requireApiSuccess/ApiOperationError带上,不要throw new Error(设备文字)。失败提示条停留 8 秒、带折叠的「详情」(原因代号、接口、设备原文、阶段、时间);成功提示 3 秒。做法和新增一条提示的步骤见docs/repair/failure-messages/README.md。具体改动
网页(
components/ts_webui/web)reportFailure、页面内文字failureText/failureNote、对话框结果框packBox、批量结果列表。sdkconfig补上 修复自动化删除保护并实现规则配置包导入 #47 新增配置项(配置包签发根,默认空)的默认值,保证从提交出来的检出构建不会改动已跟踪文件;build/默认刷机镜像与清单更新到 0.6.2(见上)。window.X = X导出,并删除 198 条因此无人使用的语言包旧键(只清被这次迁移换掉的那批,v0.6.1 原有的其他无用键没动)。固件(15 个文件,相对 #47 +526/−221;处理函数的业务逻辑不变,只加字段和代号;行为上的改动只有 HTTP 失败响应的序列化(见下面 SSH 一节)和少数失败的数字类别)
ts_api_result_reason(result, 类别, 代号, 详情);配置包失败用ts_api_pack.h的pack_failure();ts_ssh_connect_reason()把 SSH 失败分成 DNS/端口被拒/不可达/超时/握手/主机指纹/认证等阶段。config.pack.verify/apply与上传校验多回reason;TS_RULE_REJECT_PENDING_DELETE(待重启才移除的规则被拒绝执行)。SSH 主机指纹确认框(独立缺陷,见
docs/repair/ssh-fingerprint/RESULT.md)data.fingerprint弹确认框,但 HTTP 通用失败分支丢掉了data,确认框永远弹不出。result.data(没有 data 的响应字节不变)。加固:在构建失败响应的每个分配点注入失败的遍历测试发现,写code时分配失败会发出缺code的残缺 JSON,现改为 HTTP 500(成功分支有同类写法,未改)。7b899a07,可丢弃):指纹变化时的确认框改为明确的安全警告,显示新旧指纹,默认焦点在“取消”。与 #47 的整合:规则包导入原有的 28 句“一个代号一句话”的单独表并入统一目录并改成“原因+下一步”(新增 22 个原因,如
wrong_device、signature_invalid、dependency_missing、overwrite_required…);删除保护的弹窗是 #47 评审过的设计,保留不动;规则“待重启/待删除”时的快捷操作和触发给出各自的原因。用户可见的变化
兼容性
old-firmware-text等)。data(ts_webui_api.c通用失败分支)。按字段名审过:目前有 24 个处理函数可能在失败时带data,字段名里没有密码/私钥/令牌类(其中数据源“测试连接”会带回用户自己请求的远端响应体body)。WebUI 之外的调用方若假设失败响应没有data,需要知道这一点。验证(已合并最新 main,本地在 macOS 上)
npm test --prefix tests/promptsnpm run test:browser --prefix tests/prompts(真实 Chrome,15 个文件,与 CI 同样的单次调用)flows覆盖 189 个失败场景(中英文各一遍),含system组、阻断删除弹窗、规则包导入npm run test:xterm --prefix tests/promptsbash tests/runtime/run_all.sh、run_rule_pack.shtests/ws_subscriptions/:run_operation_adapter.sh、run_host.sh、run_reviewer.sh、run_f1_f2.shssh_output判断“输出帧”,被新的原因代号误判,已改成按帧的type字段判断)tests/fan_monitor、tests/fan_review、tests/fan_decaybash tests/certificate/run_host.sh(本地用 ESP-IDF 自带的 cmake 编译 MbedTLS)25b7808e的 CI 正是在这一步失败:test_ui.cjs按名字从页面脚本里取已被删除的certError;已改为直接运行生产的describeFailure/packBox并断言,类 HTML 的设备文字只以转义形式出现在折叠详情里python3 -m unittest discover -s tests/ci、tests/release/test_release.py、test_web_minify.py774c3a97)0.6.2+774c3a97.10130158,镜像 2,226,000 字节,应用分区剩 29%;tools/make_firmware_manifest.py --verify通过tools/ci/check_web_artifacts.py(--build-dir、--web-root)+ 成品 WebUI 冒烟/缺资源负例www.bin与刷写映射一致,13 个成品资源语法和 gzip 通过额外的检查(本 PR 新增,已在测试里保留):
failure-catalog.test.cjs新增“每个调用点都给足句子里的占位值”检查(故意去掉一个参数会失败)和“目录里没有无人使用的操作/原因”检查;对页面脚本做过“使用了未声明标识符”扫描,相对 main 新增 0 个。没有验证的 / 已知限制
errno)、各提示的实际手感、新的指纹警告弹窗,都只在主机上用生产代码加模拟件验证过。风险与回滚
*.json即可(UPDATE_FIXTURES=1 node --test tests/prompts/flows.browser.test.cjs重新生成)。data(见兼容性);首次发布流程依赖 ci: 完善回归门槛与构建后 Web 验证 #48 刚接入的自动发布路径,这是它第一次用于真实发布。提交一览(本 PR 自己的提交)
e068f1517b899a0785d8474046303b29fed2920508f05c03ca8cbf68a52346df767eb53387b716f975f0ab4e0e5be43be59544353a63ee4a474bec119db1b3ce61df8d5e5f37643925b7808e5ae368b6774c3a976b518bb2当前头提交:
6b518bb2。🤖 Generated with Claude Code