From 1fe6e98823794958394b603ac442021ea9bb09f7 Mon Sep 17 00:00:00 2001 From: xnqycs <1926036758@qq.com> Date: Fri, 16 Oct 2026 03:27:29 +0800 Subject: [PATCH] =?UTF-8?q?fix(coding-agent):=20=E4=BF=AE=E5=A4=8D=20dsh?= =?UTF-8?q?=20=E8=A1=A5=E4=B8=81=20YAML=20=E7=BC=A9=E8=BF=9B=E4=B8=A2?= =?UTF-8?q?=E5=A4=B1=EF=BC=8C=E5=B9=B6=E8=AE=A9=E5=A4=B1=E8=B4=A5=E5=8E=9F?= =?UTF-8?q?=E5=9B=A0=E5=8F=AF=E8=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build_dsh_patch_yaml 用行尾 `\` 续行拼接 YAML,Rust 会吃掉续行后的前导空白, 生成的 openless.patch.yml 没有任何缩进,是非法 YAML。dsh 解析补丁失败后崩溃, Less Computer 选择 dsh 后端时每次都失败。 summarize_stderr 只取以 `dsh:` 开头或含小写 `error:` 的行,而 Node 崩溃输出是 `Error: dsh: failed to parse ...`,匹配不上就回退到最后一行,界面上只剩 `Node.js v24.18.1`,看不到真实原因。 - build_dsh_patch_yaml 改用 concat! 逐行拼接,保留缩进 - summarize_stderr 对 `error:` 的匹配不再区分大小写 - 新增测试:补丁 YAML 逐字输出(含 Windows 反斜杠路径、换行、引号)、 Node 崩溃 stderr 的摘要、无匹配行时回退到最后一行 --- .../crates/openless-core/src/coding_agent.rs | 72 ++++++++++++++++--- 1 file changed, 64 insertions(+), 8 deletions(-) diff --git a/openless-all/app/crates/openless-core/src/coding_agent.rs b/openless-all/app/crates/openless-core/src/coding_agent.rs index b1283e8ff..6227b2018 100644 --- a/openless-all/app/crates/openless-core/src/coding_agent.rs +++ b/openless-all/app/crates/openless-core/src/coding_agent.rs @@ -604,14 +604,20 @@ pub fn build_dsh_patch_yaml(patch_path: &Path, prompt: &str) -> Result Option { .lines() .map(str::trim) .filter(|line| !line.is_empty()); + // A crashing Node CLI prints "Error: " and a stack, then ends with + // "Node.js vX" — taking only the last line would show just the version. lines .clone() - .find(|line| line.starts_with("dsh:") || line.contains("error:")) + .find(|line| line.starts_with("dsh:") || line.to_ascii_lowercase().contains("error:")) .or_else(|| lines.next_back()) .map(str::to_string) } @@ -2386,6 +2394,54 @@ mod tests { } } + #[test] + fn summarize_stderr_picks_the_node_crash_message_not_the_version_footer() { + let stderr = "file:///D:/dsh/lib/index.js:3564\n\ +\t\tthrow new Error(`${binName}: failed to parse ${label} ${file}: ${String(error)}`);\n\ +\t\t ^\n\ +\n\ +Error: dsh: failed to parse overlay C:\\Temp\\openless.patch.yml: YAMLException: bad indentation\n\ + at parsePatchList (file:///D:/dsh/lib/index.js:3564:9)\n\ +\n\ +Node.js v24.18.1\n"; + + assert_eq!( + summarize_stderr(stderr).as_deref(), + Some("Error: dsh: failed to parse overlay C:\\Temp\\openless.patch.yml: YAMLException: bad indentation") + ); + } + + #[test] + fn summarize_stderr_falls_back_to_the_last_line() { + assert_eq!( + summarize_stderr("starting\nsomething odd happened\n").as_deref(), + Some("something odd happened") + ); + assert_eq!(summarize_stderr("\n \n"), None); + } + + #[test] + fn dsh_patch_yaml_keeps_its_indentation() { + let yaml = build_dsh_patch_yaml( + Path::new(r"C:\Users\me\AppData\Local\Temp\dsh-events.mjs"), + "第一行\n\"第二行\"", + ) + .unwrap(); + + assert_eq!( + yaml, + concat!( + "# Generated by OpenLess. Applied as the last patch layer for one headless run.\n", + "- insert:\n", + " - id: dsh-events\n", + " name: \"C:\\\\Users\\\\me\\\\AppData\\\\Local\\\\Temp\\\\dsh-events.mjs\"\n", + "- id: headless-runner\n", + " config:\n", + " task: \"第一行\\n\\\"第二行\\\"\"\n", + ) + ); + } + #[test] fn materialization_plan_substitutes_plain_and_json_paths() { let directory = std::env::temp_dir().join("openless-agent-plan");