mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
The desktop app can quit while a preview-driven Bash task is still running npm/vite. Bash commands are spawned as detached process groups, so killing only the CLI or shell process can leave descendant dev servers alive and make later app launches look stuck. This gives the CLI enough shutdown budget to run cleanup, lets the native sidecar wait long enough for that server cleanup, kills detached Bash process groups before the existing tree-kill fallback, and closes the native preview WebView instead of hiding it on workbench unmount. Constraint: CLI gracefulShutdown has a 5s failsafe, so outer desktop shutdown windows must not SIGKILL it after 2-3s Rejected: Treat the browser preview as the dev-server owner | it only opens URLs and does not spawn npm/vite Confidence: high Scope-risk: moderate Directive: Do not shorten desktop/server shutdown windows below the CLI cleanup budget without reproducing BashTool background-task teardown Tested: bun test src/utils/ShellCommand.test.ts src/server/__tests__/conversation-service.test.ts Tested: cd desktop && bun run test -- --run src/components/browser/BrowserSurface.test.tsx src/components/workbench/WorkbenchPanel.webview.test.tsx Tested: cd desktop && bun run lint Tested: bun run check:server Tested: SKIP_INSTALL=1 ./desktop/scripts/build-macos-arm64.sh Tested: Opened canonical app and generated DMG app; both showed visible/frontmost window and left no app/sidecar/vite processes after quit Not-tested: Notarized release install path
40 lines
1.2 KiB
TypeScript
40 lines
1.2 KiB
TypeScript
import { afterEach, describe, expect, test } from 'bun:test'
|
|
import { killDetachedProcessGroup } from './ShellCommand.js'
|
|
|
|
describe('killDetachedProcessGroup', () => {
|
|
const originalKill = process.kill
|
|
|
|
afterEach(() => {
|
|
process.kill = originalKill
|
|
})
|
|
|
|
test('targets the process group for POSIX detached shell commands', () => {
|
|
if (process.platform === 'win32') {
|
|
expect(killDetachedProcessGroup(1234)).toBe(false)
|
|
return
|
|
}
|
|
|
|
const calls: Array<{ pid: number, signal: NodeJS.Signals | number | undefined }> = []
|
|
process.kill = ((pid: number, signal?: NodeJS.Signals | number) => {
|
|
calls.push({ pid, signal })
|
|
return true
|
|
}) as typeof process.kill
|
|
|
|
expect(killDetachedProcessGroup(1234)).toBe(true)
|
|
expect(calls).toEqual([{ pid: -1234, signal: 'SIGKILL' }])
|
|
})
|
|
|
|
test('treats a missing process group as an already-clean fallback case', () => {
|
|
if (process.platform === 'win32') {
|
|
expect(killDetachedProcessGroup(1234)).toBe(false)
|
|
return
|
|
}
|
|
|
|
process.kill = (() => {
|
|
throw Object.assign(new Error('missing'), { code: 'ESRCH' })
|
|
}) as typeof process.kill
|
|
|
|
expect(killDetachedProcessGroup(1234)).toBe(false)
|
|
})
|
|
})
|