fix(persistent-bash): wait for complete status markers
This commit is contained in:
@@ -93,16 +93,18 @@ function stripPrompt(text: string): string {
|
|||||||
function commandOutput(
|
function commandOutput(
|
||||||
snapshot: RetainedOutput,
|
snapshot: RetainedOutput,
|
||||||
marker: CommandMarkers,
|
marker: CommandMarkers,
|
||||||
): CapturedOutput {
|
): CapturedOutput | undefined {
|
||||||
const text = snapshot.text
|
const text = snapshot.text
|
||||||
const end = text.lastIndexOf(marker.end)
|
const end = text.lastIndexOf(marker.end)
|
||||||
const exitCode = Number.parseInt(text.slice(end + marker.end.length), 10)
|
if (end < 0) return undefined
|
||||||
|
const status = /^(\d+)\r?\n/.exec(text.slice(end + marker.end.length))?.[1]
|
||||||
|
if (status === undefined) return undefined
|
||||||
const startMarker = text.lastIndexOf(marker.start, end)
|
const startMarker = text.lastIndexOf(marker.start, end)
|
||||||
const start = startMarker < 0 ? 0 : startMarker + marker.start.length
|
const start = startMarker < 0 ? 0 : startMarker + marker.start.length
|
||||||
return {
|
return {
|
||||||
text: stripPrompt(text.slice(start, end).replace(/^\r?\n/, '')),
|
text: stripPrompt(text.slice(start, end).replace(/^\r?\n/, '')),
|
||||||
incomplete: startMarker < 0,
|
incomplete: startMarker < 0,
|
||||||
exitCode,
|
exitCode: Number(status),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,7 +319,7 @@ async function executeCommand(
|
|||||||
}
|
}
|
||||||
if (latest.text.includes(marker.end)) {
|
if (latest.text.includes(marker.end)) {
|
||||||
const complete = commandOutput(retainedScrollback(ctx, owner, id, latest), marker)
|
const complete = commandOutput(retainedScrollback(ctx, owner, id, latest), marker)
|
||||||
return renderCaptured(complete, config.maxOutputChars)
|
if (complete !== undefined) return renderCaptured(complete, config.maxOutputChars)
|
||||||
}
|
}
|
||||||
if (result.sessionStatus.kind === 'exited') {
|
if (result.sessionStatus.kind === 'exited') {
|
||||||
const snapshot = retainedScrollback(ctx, owner, id, latest)
|
const snapshot = retainedScrollback(ctx, owner, id, latest)
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ type StubMode =
|
|||||||
| 'idle-then-normal'
|
| 'idle-then-normal'
|
||||||
| 'large'
|
| 'large'
|
||||||
| 'nonzero'
|
| 'nonzero'
|
||||||
|
| 'torn-status'
|
||||||
|
| 'finish-torn-status'
|
||||||
| 'end-only'
|
| 'end-only'
|
||||||
| 'init-exit'
|
| 'init-exit'
|
||||||
| 'init-timeout'
|
| 'init-timeout'
|
||||||
@@ -160,6 +162,17 @@ class StubPtySession implements PtyBackendSession {
|
|||||||
this.pendingText = ''
|
this.pendingText = ''
|
||||||
const start = /__DSH_PERSISTENT_BASH_START_[^_]+(?:-[^_]+)*__/.exec(sent)?.[0]
|
const start = /__DSH_PERSISTENT_BASH_START_[^_]+(?:-[^_]+)*__/.exec(sent)?.[0]
|
||||||
const end = /__DSH_PERSISTENT_BASH_END_[^:]+:/.exec(sent)?.[0]
|
const end = /__DSH_PERSISTENT_BASH_END_[^:]+:/.exec(sent)?.[0]
|
||||||
|
if (this.mode === 'torn-status') {
|
||||||
|
const output = `${start ?? ''}\nhello from stub\n${end ?? ''}`
|
||||||
|
this.scrollback += output
|
||||||
|
this.mode = 'finish-torn-status'
|
||||||
|
return this.operation(Promise.resolve(this.result(output, 'inferred_idle')))
|
||||||
|
}
|
||||||
|
if (this.mode === 'finish-torn-status') {
|
||||||
|
const output = `7\n${this.motd}`
|
||||||
|
this.scrollback += output
|
||||||
|
return this.operation(Promise.resolve(this.result(output, 'stdin_read')))
|
||||||
|
}
|
||||||
if (this.mode === 'end-only') {
|
if (this.mode === 'end-only') {
|
||||||
const output = `recovered output\n${end ?? ''}0\n${this.motd}`
|
const output = `recovered output\n${end ?? ''}0\n${this.motd}`
|
||||||
this.scrollback += output
|
this.scrollback += output
|
||||||
@@ -345,6 +358,15 @@ describe('tool-bash-persistent', () => {
|
|||||||
expect(stub.sessions[2]?.closed).toEqual(['external cleanup'])
|
expect(stub.sessions[2]?.closed).toEqual(['external cleanup'])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('waits for status digits after a torn completion marker', async () => {
|
||||||
|
const { ctx, owner, stub } = await setup({ backendType: 'stub', maxOutputChars: 1_000 })
|
||||||
|
await call(ctx, owner, 'warm up')
|
||||||
|
stub.sessions[0]!.mode = 'torn-status'
|
||||||
|
stub.sessions[0]!.scrollback = ''
|
||||||
|
|
||||||
|
expect(text(await call(ctx, owner, 'torn status'))).toBe('hello from stub\n[exit code: 7]')
|
||||||
|
})
|
||||||
|
|
||||||
it('marks a short missing-prefix result and tolerates exhausted scrollback pages', async () => {
|
it('marks a short missing-prefix result and tolerates exhausted scrollback pages', async () => {
|
||||||
const { ctx, owner, stub } = await setup({ backendType: 'stub', maxOutputChars: 1_000 })
|
const { ctx, owner, stub } = await setup({ backendType: 'stub', maxOutputChars: 1_000 })
|
||||||
await call(ctx, owner, 'warm up')
|
await call(ctx, owner, 'warm up')
|
||||||
|
|||||||
Reference in New Issue
Block a user