Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 64 additions & 8 deletions openless-all/app/crates/openless-core/src/coding_agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -604,14 +604,20 @@ pub fn build_dsh_patch_yaml(patch_path: &Path, prompt: &str) -> Result<String, B
.map_err(|error| BackendError::new(BackendErrorCode::Internal, error.to_string()))?;
let task = serde_json::to_string(prompt)
.map_err(|error| BackendError::new(BackendErrorCode::Internal, error.to_string()))?;
// Not `\` line continuations: they strip the next line's leading spaces, which
// flattened this document into invalid YAML that dsh refused to parse.
Ok(format!(
"# Generated by OpenLess. Applied as the last patch layer for one headless run.\n\
- insert:\n\
- id: dsh-events\n\
name: {path}\n\
- id: headless-runner\n\
config:\n\
task: {task}\n"
concat!(
"# Generated by OpenLess. Applied as the last patch layer for one headless run.\n",
"- insert:\n",
" - id: dsh-events\n",
" name: {path}\n",
"- id: headless-runner\n",
" config:\n",
" task: {task}\n",
),
path = path,
task = task,
))
}

Expand Down Expand Up @@ -1300,9 +1306,11 @@ fn summarize_stderr(stderr: &str) -> Option<String> {
.lines()
.map(str::trim)
.filter(|line| !line.is_empty());
// A crashing Node CLI prints "Error: <cause>" 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)
}
Expand Down Expand Up @@ -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");
Expand Down
Loading