mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-28 19:11:03 +08:00
fix(pty): harden cross-platform smoke checks
This commit is contained in:
@@ -17,7 +17,8 @@ export async function smoke() {
|
||||
const exited = Promise.withResolvers<number>()
|
||||
const data = proc.onData((chunk) => {
|
||||
state.output += chunk
|
||||
if (state.output.includes("KILO_PTY_READY")) output.resolve()
|
||||
const lines = state.output.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "").split(/\r?\n/)
|
||||
if (lines.some((line) => line.trim() === "KILO_PTY_READY")) output.resolve()
|
||||
})
|
||||
const exit = proc.onExit((event) => {
|
||||
state.exited = true
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { spawn } from "child_process"
|
||||
import { readdir, readFile } from "node:fs/promises"
|
||||
import { setTimeout as sleep } from "node:timers/promises"
|
||||
import type { Proc } from "../../pty/pty"
|
||||
import { Log } from "../../util/log"
|
||||
@@ -87,6 +88,15 @@ function signal(proc: Process, pids: number[], value: "SIGTERM" | "SIGKILL", inp
|
||||
}
|
||||
|
||||
async function tree(file: string = "ps", args: string[] = ["-axo", "pid=,ppid="]) {
|
||||
if (process.platform === "linux") {
|
||||
try {
|
||||
const rows = await procTree()
|
||||
if (rows.length > 0) return rows
|
||||
} catch (err) {
|
||||
log.debug("failed to read Linux process tree", { err })
|
||||
}
|
||||
}
|
||||
|
||||
return await new Promise<Array<{ pid: number; parent: number }>>((resolve, reject) => {
|
||||
try {
|
||||
const child = spawn(file, args, {
|
||||
@@ -116,6 +126,21 @@ async function tree(file: string = "ps", args: string[] = ["-axo", "pid=,ppid="]
|
||||
})
|
||||
}
|
||||
|
||||
async function procTree() {
|
||||
const entries = await readdir("/proc", { withFileTypes: true })
|
||||
const rows = await Promise.all(
|
||||
entries
|
||||
.filter((entry) => entry.isDirectory() && /^\d+$/.test(entry.name))
|
||||
.map(async (entry) => {
|
||||
const stat = await readFile(`/proc/${entry.name}/stat`, "utf8")
|
||||
const match = stat.match(/^\d+ \(.*\) [A-Z] (\d+)/)
|
||||
if (!match) return
|
||||
return { pid: Number(entry.name), parent: Number(match[1]) }
|
||||
}),
|
||||
)
|
||||
return rows.filter((row): row is { pid: number; parent: number } => row !== undefined)
|
||||
}
|
||||
|
||||
async function taskkill(file: string, args: string[], opts: { stdio: "ignore"; windowsHide: true; timeout: number }) {
|
||||
return await new Promise<boolean>((resolve) => {
|
||||
try {
|
||||
|
||||
@@ -21,6 +21,26 @@ const layer = AppNodeBuilder.build(LayerNode.group([Pty.node, EventV2.node]), [
|
||||
])
|
||||
const it = testEffect(layer)
|
||||
|
||||
async function alive(pid: number) {
|
||||
if (process.platform !== "win32") {
|
||||
try {
|
||||
process.kill(pid, 0)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const proc = Bun.spawn(["tasklist", "/FI", `PID eq ${pid}`, "/FO", "CSV", "/NH"], {
|
||||
stdout: "pipe",
|
||||
stderr: "ignore",
|
||||
windowsHide: true,
|
||||
})
|
||||
const output = await new Response(proc.stdout).text()
|
||||
await proc.exited
|
||||
return output.includes(`"${pid}"`)
|
||||
}
|
||||
|
||||
const attach = Effect.fn("PtyPlatformTest.attach")(function* (id: Pty.Info["id"]) {
|
||||
const pty = yield* Pty.Service
|
||||
const output = yield* Queue.unbounded<string>()
|
||||
@@ -70,12 +90,7 @@ describe("cross-platform PTY", () => {
|
||||
expect(pid).toBeGreaterThan(0)
|
||||
|
||||
yield* pty.remove(info.id)
|
||||
if (process.platform === "win32") {
|
||||
const result = yield* Effect.promise(() => Bun.$`taskkill /pid ${pid} /f /t`.quiet().nothrow())
|
||||
expect(result.exitCode).toBe(128)
|
||||
} else {
|
||||
expect(() => process.kill(pid, 0)).toThrow()
|
||||
}
|
||||
expect(yield* Effect.promise(() => alive(pid))).toBe(false)
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user