Merge pull request #13466 from Kilo-Org/investigate-connection-failures-root-cause

fix(vscode): handle fragmented CLI startup output
This commit is contained in:
Marius
2026-08-27 09:32:28 +02:00
committed by GitHub
4 changed files with 68 additions and 7 deletions
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---
Prevent intermittent server connection failures during VS Code startup.
@@ -6,7 +6,7 @@ import * as path from "path"
import * as vscode from "vscode"
import { resolveLocalBwrapEnv, resolveTreeSitterEnv } from "./cli-resources"
import { t } from "./i18n"
import { parseServerPort } from "./server-utils"
import { scanServerPort } from "./server-utils"
export interface ServerInstance {
port: number
@@ -15,6 +15,7 @@ export interface ServerInstance {
}
const STARTUP_TIMEOUT_SECONDS = 30
const STARTUP_OUTPUT_LIMIT = 1024
type WorkspaceFolderLike = { uri: { fsPath: string } }
type ServerExitListener = (code: number | null, signal: NodeJS.Signals | null) => void
@@ -162,13 +163,16 @@ export class ServerManager {
console.log("[Kilo New] ServerManager: 📦 Process spawned with PID:", serverProcess.pid)
let resolved = false
let output = ""
const stderrLines: string[] = []
serverProcess.stdout?.on("data", (data: Buffer) => {
const output = data.toString()
console.log("[Kilo New] ServerManager: 📥 CLI Server stdout:", output)
const chunk = data.toString()
console.log("[Kilo New] ServerManager: 📥 CLI Server stdout:", chunk)
const port = parseServerPort(output)
const state = scanServerPort(output, chunk, STARTUP_OUTPUT_LIMIT)
output = state.output
const port = state.port
if (port !== null && !resolved) {
resolved = true
console.log("[Kilo New] ServerManager: 🎯 Port detected:", port)
@@ -3,8 +3,15 @@
* Matches lines like: "kilo server listening on http://127.0.0.1:12345"
* Returns the port number or null if not found.
*/
export function parseServerPort(output: string): number | null {
const match = output.match(/listening on http:\/\/[\w.]+:(\d+)/)
export function parseServerPort(output: string, complete = false): number | null {
const match = output.match(
complete ? /listening on http:\/\/[\w.]+:(\d+)\r?\n/ : /listening on http:\/\/[\w.]+:(\d+)/,
)
if (!match) return null
return parseInt(match[1]!, 10)
}
export function scanServerPort(output: string, chunk: string, limit: number) {
const text = `${output}${chunk}`
return { output: text.slice(-limit), port: parseServerPort(text, true) }
}
@@ -1,5 +1,5 @@
import { describe, it, expect } from "bun:test"
import { parseServerPort } from "../../src/services/cli-backend/server-utils"
import { parseServerPort, scanServerPort } from "../../src/services/cli-backend/server-utils"
import {
resolveServerCwd,
resolveIndexingEnv,
@@ -61,6 +61,51 @@ describe("parseServerPort", () => {
const output = "listening on http://127.0.0.1:3000 and http://127.0.0.1:4000"
expect(parseServerPort(output)).toBe(3000)
})
it("waits for the complete startup line before resolving a split port", () => {
const first = "kilo server listening on http://127.0.0.1:43"
expect(parseServerPort(first, true)).toBeNull()
expect(parseServerPort(`${first}123\n`, true)).toBe(43123)
})
it("detects a startup announcement split across stdout chunks", () => {
const first = "kilo server listening on http://127.0."
const second = "0.1:43123\n"
expect(parseServerPort(first, true)).toBeNull()
expect(parseServerPort(`${first}${second}`, true)).toBe(43123)
})
it("accepts complete Windows startup lines", () => {
expect(parseServerPort("kilo server listening on http://127.0.0.1:43123\r\n", true)).toBe(43123)
})
})
describe("scanServerPort", () => {
it("detects startup announcements split across stdout chunks", () => {
const first = scanServerPort("", "kilo server listening on http://127.0.", 1024)
const second = scanServerPort(first.output, "0.1:43123\n", 1024)
expect(first.port).toBeNull()
expect(second.port).toBe(43123)
})
it("waits for split port digits before resolving startup", () => {
const first = scanServerPort("", "kilo server listening on http://127.0.0.1:43", 1024)
const second = scanServerPort(first.output, "123\n", 1024)
expect(first.port).toBeNull()
expect(second.port).toBe(43123)
})
it("preserves startup announcements followed by oversized stdout chunks", () => {
const chunk = `kilo server listening on http://127.0.0.1:43123\n${"x".repeat(1024)}`
const state = scanServerPort("", chunk, 1024)
expect(state.port).toBe(43123)
expect(state.output).toHaveLength(1024)
})
})
describe("cli tree-sitter resources", () => {