Compare commits

...
Author SHA1 Message Date
bincxz efa3bf7b63 fix(ai): parse live-shell probe sentinels after bash PS2
Interactive bash prints PS2 on the probe's backslash continuations, so
strict startsWith checks miss `_Q`/`_P:` and silent PTY execs hang until
the command timeout. Strip common prompt prefixes in the parser; keep
the existing short-line probe for OpenWrt.
2026-09-21 15:18:16 +08:00
2 changed files with 14 additions and 4 deletions

No files matched your search

+12 -4
View File
@@ -20,12 +20,20 @@ function buildLiveShellProbe(marker) {
}
// Interactive bash prints PS2 (`> ` by default) on backslash continuations.
// Strip that prefix (and other common prompt leftovers) so `_Q` / `_P:`
// sentinels still parse when the probe is not a single physical line.
function normalizeLiveShellProbeLine(line) {
return String(line).replace(/^[>#$%\s]+/, "");
}
function parseLiveShellProbe(output, marker) {
const lines = String(output).replace(/\r/g, "\n").split("\n");
if (!lines.some((line) => line.startsWith(`${marker}_Q`))) return null;
if (!lines.some((line) => normalizeLiveShellProbeLine(line).startsWith(`${marker}_Q`))) return null;
for (const line of lines) {
if (!line.startsWith(`${marker}_P:`)) continue;
const name = line.slice(marker.length + 3).trim().split("/").pop().replace(/^-/, "");
const normalized = normalizeLiveShellProbeLine(line);
if (!normalized.startsWith(`${marker}_P:`)) continue;
const name = normalized.slice(marker.length + 3).trim().split("/").pop().replace(/^-/, "");
return {
kind: name === "fish" ? "fish"
: /^(?:ba|da|z|k|a)?sh$/.test(name) ? "posix" : null,
@@ -34,4 +42,4 @@ function parseLiveShellProbe(output, marker) {
return { kind: null };
}
module.exports = { buildLiveShellProbe, parseLiveShellProbe };
module.exports = { buildLiveShellProbe, parseLiveShellProbe, normalizeLiveShellProbeLine };
@@ -12,6 +12,8 @@ test('live shell response excludes echoed commands, stale markers and partial li
assert.deepEqual(parseLiveShellProbe(`\r${marker}_P:/usr/bin/fish\r\n${marker}_Q`, marker), { kind: 'fish' });
assert.deepEqual(parseLiveShellProbe(`${marker}_P:-zsh\n${marker}_Q`, marker), { kind: 'posix' });
assert.deepEqual(parseLiveShellProbe(`${marker}_P:\n${marker}_Q`, marker), { kind: null });
assert.deepEqual(parseLiveShellProbe(`> ${marker}_P:bash\n> ${marker}_Q`, marker), { kind: 'posix' });
assert.deepEqual(parseLiveShellProbe(`> ${marker}_P:fish\n> ${marker}_Q`, marker), { kind: 'fish' });
});
test('probe waits for complete reply before choosing the first wrapper', async () => {