Merge pull request #10786 from Kilo-Org/foul-oval

fix(cli): skip background port scans in VS Code
This commit is contained in:
Marius
2026-06-01 13:29:34 +02:00
committed by GitHub
3 changed files with 131 additions and 1 deletions
@@ -0,0 +1,6 @@
---
"@kilocode/cli": patch
"kilo-code": patch
---
Skip inferred background-process port scanning in VS Code sessions to avoid unnecessary Bun subprocess polling.
@@ -8,6 +8,7 @@ import { SessionID } from "@/session/schema"
import { Shell } from "@/shell/shell"
import { NonNegativeInt, PositiveInt, optionalOmitUndefined, withStatics } from "@/util/schema"
import { zod, ZodOverride } from "@/util/effect-zod"
import { Flag } from "@opencode-ai/core/flag/flag"
import * as Log from "@opencode-ai/core/util/log"
import { spawn, type ChildProcess } from "child_process"
import { Context, Effect, Layer, Schema, Types } from "effect"
@@ -172,7 +173,8 @@ export namespace BackgroundProcess {
return changed
}
const fallback = active.info.ready && active.start.ready?.port ? [active.start.ready.port] : []
const next = Array.from(new Set([...(await Ports.list(pid)), ...fallback])).toSorted((a, b) => a - b)
const ports = Flag.KILO_CLIENT === "cli" ? await Ports.list(pid) : []
const next = Array.from(new Set([...ports, ...fallback])).toSorted((a, b) => a - b)
if (same(active.info.ports, next)) return false
active.info.ports = next
active.info.time.updated = Date.now()
@@ -209,6 +211,7 @@ export namespace BackgroundProcess {
function poll(active: Active) {
if (active.disposed) return
if (Flag.KILO_CLIENT !== "cli") return
if (terminal(active.info.status)) return
if (active.poll) return
active.poll = setTimeout(() => {
@@ -23,6 +23,14 @@ async function script(dir: string, name: string, source: string) {
return `${bin} ${arg}`
}
function port() {
const server = Bun.serve({ hostname: "127.0.0.1", port: 0, fetch: () => new Response() })
const port = server.port
server.stop(true)
if (!port) throw new Error("Failed to reserve port")
return port
}
function update(sessionID: SessionID) {
const state: { off?: () => void; timer?: ReturnType<typeof setTimeout> } = {}
const promise = new Promise<BackgroundProcess.Info>((resolve, reject) => {
@@ -92,6 +100,119 @@ setInterval(() => {}, 1_000)
}),
)
it.instance("reports explicit readiness ports for VS Code clients", () =>
Effect.gen(function* () {
const test = yield* TestInstance
const sessionID = SessionID.descending()
const listen = port()
const command = yield* Effect.promise(() =>
script(
test.directory,
"vscode-ready-port.mjs",
`Bun.serve({ hostname: "127.0.0.1", port: ${listen}, fetch: () => new Response() })
`,
),
)
const client = process.env["KILO_CLIENT"]
process.env["KILO_CLIENT"] = "vscode"
try {
const info = yield* Effect.promise(() =>
BackgroundProcess.start({
sessionID,
command,
cwd: test.directory,
ready: { port: listen, timeout: 5_000 },
}),
)
expect(info.status).toBe("ready")
expect(info.ports).toEqual([listen])
} finally {
yield* Effect.promise(() => BackgroundProcess.stopSession(sessionID))
if (client === undefined) delete process.env["KILO_CLIENT"]
else process.env["KILO_CLIENT"] = client
}
}),
)
it.instance("infers ports for CLI clients", () =>
Effect.gen(function* () {
const test = yield* TestInstance
const sessionID = SessionID.descending()
const listen = port()
const command = yield* Effect.promise(() =>
script(
test.directory,
"cli-port.mjs",
`Bun.serve({ hostname: "127.0.0.1", port: ${listen}, fetch: () => new Response() })
`,
),
)
const client = process.env["KILO_CLIENT"]
process.env["KILO_CLIENT"] = "cli"
try {
const info = yield* Effect.promise(() =>
BackgroundProcess.start({
sessionID,
command,
cwd: test.directory,
}),
)
let found = yield* Effect.promise(() => BackgroundProcess.get(info.id))
if (process.platform !== "win32") {
for (let attempt = 0; attempt < 40 && !found?.ports.includes(listen); attempt++) {
yield* Effect.promise(() => Bun.sleep(250))
found = yield* Effect.promise(() => BackgroundProcess.get(info.id))
}
}
expect(found?.ports).toEqual(process.platform === "win32" ? [] : [listen])
} finally {
yield* Effect.promise(() => BackgroundProcess.stopSession(sessionID))
if (client === undefined) delete process.env["KILO_CLIENT"]
else process.env["KILO_CLIENT"] = client
}
}),
)
it.instance("does not infer ports for VS Code clients", () =>
Effect.gen(function* () {
const test = yield* TestInstance
const sessionID = SessionID.descending()
const listen = port()
const command = yield* Effect.promise(() =>
script(
test.directory,
"vscode-port.mjs",
`Bun.serve({ hostname: "127.0.0.1", port: ${listen}, fetch: () => new Response() })
`,
),
)
const client = process.env["KILO_CLIENT"]
process.env["KILO_CLIENT"] = "vscode"
try {
const info = yield* Effect.promise(() =>
BackgroundProcess.start({
sessionID,
command,
cwd: test.directory,
}),
)
yield* Effect.promise(() => Bun.sleep(2_500))
const found = yield* Effect.promise(() => BackgroundProcess.get(info.id))
expect(found?.ports).toEqual([])
} finally {
yield* Effect.promise(() => BackgroundProcess.stopSession(sessionID))
if (client === undefined) delete process.env["KILO_CLIENT"]
else process.env["KILO_CLIENT"] = client
}
}),
)
it.instance("publishes output updates from process callbacks", () =>
Effect.gen(function* () {
const test = yield* TestInstance