mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-10-22 22:59:37 +00:00
Some checks failed
CI / semgrep (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
Tests / test (24) (push) Waiting to run
Tests / test (26) (push) Waiting to run
Tests / test-platforms (macos-latest) (push) Waiting to run
Tests / test-platforms (windows-latest) (push) Waiting to run
Tests / desktop (push) Waiting to run
macOS Menubar CI / test (push) Has been cancelled
Windows Menubar CI / check (ubuntu-latest) (push) Has been cancelled
Windows Menubar CI / check (windows-latest) (push) Has been cancelled
The desktop serve client counted every death, watchdog kills included, against a 3-death budget that never reset, so three sporadic kills over an app run disabled serve for good. The budget now resets on any healthy answer: three generations in a row that never answer still disable it. One-shot CLI runs only heartbeated inside a parse, so a run waiting on another process's cold hydration lock (up to 10 min) went silent and was killed at 45s on every poll. Start the keepalive at command entry.
45 lines
1.8 KiB
TypeScript
45 lines
1.8 KiB
TypeScript
import { spawn } from 'node:child_process'
|
|
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
// A one-shot read that waits on another process's cold hydration lock does no
|
|
// parsing yet, and the app watchdogs kill on silence. It must beat anyway.
|
|
describe('one-shot CLI keepalive', () => {
|
|
it('beats while waiting on a foreign hydration lock', async () => {
|
|
const home = await mkdtemp(join(tmpdir(), 'cb-entry-keepalive-home-'))
|
|
const cacheDir = await mkdtemp(join(tmpdir(), 'cb-entry-keepalive-cache-'))
|
|
// This test process is alive and is not the child, so the child waits.
|
|
await writeFile(join(cacheDir, 'hydrating.lock'), JSON.stringify({ pid: process.pid, at: Date.now() }))
|
|
const child = spawn(process.execPath, ['--import', 'tsx', 'src/cli.ts', 'status', '--format', 'menubar-json', '--provider', 'claude'], {
|
|
cwd: process.cwd(),
|
|
env: {
|
|
...process.env,
|
|
HOME: home,
|
|
USERPROFILE: home,
|
|
CLAUDE_CONFIG_DIR: join(home, '.claude'),
|
|
CODEBURN_CACHE_DIR: cacheDir,
|
|
CODEBURN_PROGRESS: '1',
|
|
},
|
|
stdio: ['ignore', 'ignore', 'pipe'],
|
|
})
|
|
try {
|
|
const sawKeepalive = await new Promise<boolean>(resolve => {
|
|
let stderr = ''
|
|
const timer = setTimeout(() => resolve(false), 20_000)
|
|
child.stderr.on('data', chunk => {
|
|
stderr += chunk
|
|
if (stderr.includes('"kind":"keepalive"')) { clearTimeout(timer); resolve(true) }
|
|
})
|
|
child.on('exit', () => { clearTimeout(timer); resolve(false) })
|
|
})
|
|
expect(sawKeepalive).toBe(true)
|
|
} finally {
|
|
child.kill('SIGKILL')
|
|
await rm(home, { recursive: true, force: true })
|
|
await rm(cacheDir, { recursive: true, force: true })
|
|
}
|
|
}, 30_000)
|
|
})
|