Skip to content
Merged
Show file tree
Hide file tree
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
6 changes: 6 additions & 0 deletions packages/dsh-loopx-plugin/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,9 +377,15 @@ export async function resolveLoopXCommand(
maxOutputBytes: 16 * 1024,
},
)
if (options.signal?.aborted) {
throw new LoopXCliError('aborted', 'operation was cancelled', false)
}
const version = result.exitCode === 0 ? versionText(result.stdout) : undefined
if (version) return { ...candidate, version }
} catch (error: unknown) {
if (options.signal?.aborted) {
throw new LoopXCliError('aborted', 'operation was cancelled', false)
}
if (error instanceof LoopXCliError && error.kind === 'aborted') throw error
}
}
Expand Down
17 changes: 17 additions & 0 deletions packages/dsh-loopx-plugin/tests/cli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,23 @@ const command: LoopXCommand = {
}

describe('runJsonCommand', () => {
it.each(['valid-version', 'nonzero-exit', 'rejected-probe'])(
'honours cancellation at the %s probe completion boundary', async outcome => {
const controller = new AbortController()
let probes = 0
await expect(resolveLoopXCommand({
signal: controller.signal, env: { LOOPX_BIN: 'fixture-loopx' },
runner: async () => {
probes += 1
controller.abort()
if (outcome === 'rejected-probe') throw new Error('probe closed during cancellation')
return { exitCode: outcome === 'valid-version' ? 0 : 1, stdout: 'loopx 1.2.4\n', stderr: '' }
},
})).rejects.toMatchObject({ kind: 'aborted', retryable: false })
expect(probes).toBe(1)
},
)

it.each(['0.5.4', '1.2.3', '1.2.4rc1', '1.2.4.dev1', 'smoke', '1.2.4unknown'])(
'rejects unavailable or pre-fix CLI version %s', async version => {
await expect(resolveLoopXCommand({
Expand Down
25 changes: 25 additions & 0 deletions packages/dsh-loopx-plugin/tests/driver.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,31 @@ function makeDriver(fixture: RunnerFixture): LoopXContinuationDriver {
}

describe('same-session LoopX driver', () => {
it('does not report a cancelled maintenance probe as a missing CLI', async () => {
const host = fakeAgent()
const maintenance = new AbortController()
const warnings: string[] = []
let probes = 0
vi.spyOn(host.agent, 'runMaintenance').mockImplementation(task => task(maintenance.signal))
const driver = new LoopXContinuationDriver({
isLiveAgent: () => true,
runner: async () => {
probes += 1
maintenance.abort()
return { exitCode: 1, stdout: '', stderr: '' }
},
warn: message => warnings.push(message),
})
try {
observeActivated(driver, host)
await waitFor(() => probes > 0)
await new Promise<void>(resolve => setTimeout(resolve, 0))
expect(warnings).toEqual([])
expect(host.nextTurn).toHaveLength(0)
expect(probes).toBe(1)
} finally { await driver.dispose() }
})

it('retries admission after attach-time maintenance becomes free without an idle event', async () => {
const fixture = runnerFixture()
const host = fakeAgent()
Expand Down
Loading