mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-08-29 03:44:06 +08:00
fix: harden upstream v1.14.42 integration
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"permission": {
|
||||
"edit": {
|
||||
"packages/opencode/migration/*": "ask",
|
||||
"packages/opencode/migration/*": "deny",
|
||||
},
|
||||
},
|
||||
"mcp": {},
|
||||
|
||||
@@ -111,7 +111,8 @@ describe("cross-spawn spawner", () => {
|
||||
ChildProcess.make(process.execPath, ["-e", "process.stdout.write(process.cwd())"], { cwd: tmp.path }),
|
||||
),
|
||||
)
|
||||
expect(out).toBe(tmp.path)
|
||||
const cwd = yield* Effect.promise(() => fs.realpath(tmp.path))
|
||||
expect(out).toBe(cwd)
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Global } from "@opencode-ai/core/global"
|
||||
|
||||
describe("global paths", () => {
|
||||
test("tmp path is under the system temp directory", () => {
|
||||
expect(Global.Path.tmp).toBe(path.join(os.tmpdir(), "opencode"))
|
||||
expect(Global.Path.tmp).toBe(path.join(os.tmpdir(), "kilo")) // kilocode_change
|
||||
expect(Global.make().tmp).toBe(Global.Path.tmp)
|
||||
})
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ exclude = [
|
||||
'^https?://ai-gateway\.vercel\.sh/v1/?$',
|
||||
'^https?://api\.voyageai\.com/v1/embeddings/?$',
|
||||
'^https?://generativelanguage\.googleapis\.com/v1beta/openai/?$',
|
||||
'^https?://search\.parallel\.ai/mcp/?$',
|
||||
# xAI API and OAuth endpoints require request parameters or non-GET methods.
|
||||
'^https?://api\.x\.ai/v1/?$',
|
||||
'^https?://auth\.x\.ai/?$',
|
||||
|
||||
+1
-1
@@ -92,6 +92,6 @@ data class CommandInfo(
|
||||
|
||||
data class SkillInfo(
|
||||
val name: String,
|
||||
val description: String,
|
||||
val description: String?,
|
||||
val location: String,
|
||||
)
|
||||
|
||||
@@ -5,6 +5,6 @@ import kotlinx.serialization.Serializable
|
||||
@Serializable
|
||||
data class SkillDto(
|
||||
val name: String,
|
||||
val description: String,
|
||||
val description: String? = null,
|
||||
val location: String,
|
||||
)
|
||||
|
||||
+62
-28
@@ -5,6 +5,8 @@ const fs = require("fs")
|
||||
const path = require("path")
|
||||
const os = require("os")
|
||||
|
||||
const forwardedSignals = ["SIGINT", "SIGTERM", "SIGHUP"]
|
||||
|
||||
// kilocode_change start - point packaged binaries at co-located tree-sitter WASM resources
|
||||
function configureTreeSitterResources(target) {
|
||||
const wasmDir = path.join(path.dirname(target), "tree-sitter")
|
||||
@@ -14,42 +16,74 @@ function configureTreeSitterResources(target) {
|
||||
}
|
||||
// kilocode_change end
|
||||
|
||||
function run(target) {
|
||||
function run(target, fallback) { // kilocode_change - preserve cached binary fallback
|
||||
configureTreeSitterResources(target) // kilocode_change
|
||||
const result = childProcess.spawnSync(target, process.argv.slice(2), {
|
||||
stdio: "inherit",
|
||||
})
|
||||
if (result.error) {
|
||||
console.error(result.error.message)
|
||||
process.exit(1)
|
||||
// kilocode_change start - fall through if the cached binary cannot be spawned
|
||||
const child = (() => {
|
||||
try {
|
||||
return childProcess.spawn(target, process.argv.slice(2), {
|
||||
stdio: "inherit",
|
||||
})
|
||||
} catch (error) {
|
||||
if (fallback) {
|
||||
run(fallback)
|
||||
return
|
||||
}
|
||||
console.error(error.message)
|
||||
process.exit(1)
|
||||
}
|
||||
})()
|
||||
if (!child) return
|
||||
// kilocode_change end
|
||||
|
||||
const forwarders = {}
|
||||
const clear = () => { // kilocode_change - remove listeners before cached binary fallback
|
||||
for (const signal of forwardedSignals) {
|
||||
process.removeListener(signal, forwarders[signal])
|
||||
}
|
||||
}
|
||||
const code = typeof result.status === "number" ? result.status : 0
|
||||
process.exit(code)
|
||||
|
||||
child.on("error", (error) => {
|
||||
clear() // kilocode_change
|
||||
// kilocode_change start - fall through to findBinary() if cached binary fails
|
||||
if (fallback) {
|
||||
run(fallback)
|
||||
return
|
||||
}
|
||||
// kilocode_change end
|
||||
console.error(error.message)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
for (const signal of forwardedSignals) {
|
||||
forwarders[signal] = () => {
|
||||
try {
|
||||
child.kill(signal)
|
||||
} catch {
|
||||
// The child may have already exited.
|
||||
}
|
||||
}
|
||||
process.on(signal, forwarders[signal])
|
||||
}
|
||||
|
||||
child.on("exit", (code, signal) => {
|
||||
clear() // kilocode_change
|
||||
|
||||
if (signal) {
|
||||
process.kill(process.pid, signal)
|
||||
return
|
||||
}
|
||||
|
||||
process.exit(typeof code === "number" ? code : 0)
|
||||
})
|
||||
}
|
||||
|
||||
const envPath = process.env.KILO_BIN_PATH
|
||||
if (envPath) {
|
||||
run(envPath)
|
||||
}
|
||||
|
||||
const scriptPath = fs.realpathSync(__filename)
|
||||
const scriptDir = path.dirname(scriptPath)
|
||||
|
||||
// kilocode_change start - fall through to findBinary() if cached binary fails
|
||||
const cached = path.join(scriptDir, ".kilo")
|
||||
if (fs.existsSync(cached)) {
|
||||
configureTreeSitterResources(cached)
|
||||
const result = childProcess.spawnSync(cached, process.argv.slice(2), {
|
||||
stdio: "inherit",
|
||||
})
|
||||
if (!result.error) {
|
||||
const code = typeof result.status === "number" ? result.status : 0
|
||||
process.exit(code)
|
||||
}
|
||||
// cached binary failed (e.g. wrong platform/arch, missing dynamic linker),
|
||||
// fall through to findBinary() which has better variant detection
|
||||
}
|
||||
// kilocode_change end
|
||||
|
||||
const platformMap = {
|
||||
darwin: "darwin",
|
||||
@@ -186,7 +220,7 @@ function findBinary(startDir) {
|
||||
}
|
||||
}
|
||||
|
||||
const resolved = findBinary(scriptDir)
|
||||
const resolved = envPath || (fs.existsSync(cached) ? cached : findBinary(scriptDir))
|
||||
if (!resolved) {
|
||||
console.error(
|
||||
"It seems that your package manager failed to install the right version of the Kilo CLI for your platform. You can try manually installing " +
|
||||
@@ -196,4 +230,4 @@ if (!resolved) {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
run(resolved)
|
||||
run(resolved, resolved === cached ? findBinary(scriptDir) : undefined) // kilocode_change - preserve cached binary fallback
|
||||
|
||||
@@ -9,6 +9,7 @@ import { isReviewCommand, parseReviewCommand } from "@/kilocode/review/command"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { Effect } from "effect"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { EffectBridge } from "@/effect/bridge"
|
||||
|
||||
export type ReviewTelemetry = {
|
||||
mode: "review"
|
||||
@@ -127,7 +128,7 @@ export namespace KiloSessionProcessor {
|
||||
return Effect.gen(function* () {
|
||||
const msg = SessionNetwork.message(input.error)
|
||||
|
||||
const { id, promise } = yield* Effect.promise(() =>
|
||||
const { id, promise } = yield* EffectBridge.fromPromise(() =>
|
||||
SessionNetwork.ask({
|
||||
sessionID: input.sessionID,
|
||||
message: msg,
|
||||
|
||||
@@ -38,30 +38,37 @@ function eventData(data: unknown): Sse.Event {
|
||||
}
|
||||
|
||||
function eventResponse(bus: Bus.Interface) {
|
||||
const events = bus.subscribeAll().pipe(Stream.takeUntil((event) => event.type === Bus.InstanceDisposed.type))
|
||||
const heartbeat = Stream.tick("10 seconds").pipe(
|
||||
Stream.drop(1),
|
||||
Stream.map(() => ({ id: Bus.createID(), type: "server.heartbeat", properties: {} })),
|
||||
)
|
||||
return Effect.gen(function* () {
|
||||
const context = yield* Effect.context()
|
||||
|
||||
log.info("event connected")
|
||||
return HttpServerResponse.stream(
|
||||
Stream.make({ id: Bus.createID(), type: "server.connected", properties: {} }).pipe(
|
||||
Stream.concat(events.pipe(Stream.merge(heartbeat, { haltStrategy: "left" }))),
|
||||
Stream.map(eventData),
|
||||
Stream.pipeThroughChannel(Sse.encode()),
|
||||
Stream.encodeText,
|
||||
Stream.ensuring(Effect.sync(() => log.info("event disconnected"))),
|
||||
),
|
||||
{
|
||||
contentType: "text/event-stream",
|
||||
headers: {
|
||||
"Cache-Control": "no-cache, no-transform",
|
||||
"X-Accel-Buffering": "no",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
const events = bus.subscribeAll().pipe(
|
||||
Stream.provideContext(context),
|
||||
Stream.takeUntil((event) => event.type === Bus.InstanceDisposed.type),
|
||||
)
|
||||
const heartbeat = Stream.tick("10 seconds").pipe(
|
||||
Stream.drop(1),
|
||||
Stream.map(() => ({ id: Bus.createID(), type: "server.heartbeat", properties: {} })),
|
||||
)
|
||||
|
||||
log.info("event connected")
|
||||
return HttpServerResponse.stream(
|
||||
Stream.make({ id: Bus.createID(), type: "server.connected", properties: {} }).pipe(
|
||||
Stream.concat(events.pipe(Stream.merge(heartbeat, { haltStrategy: "left" }))),
|
||||
Stream.map(eventData),
|
||||
Stream.pipeThroughChannel(Sse.encode()),
|
||||
Stream.encodeText,
|
||||
Stream.ensuring(Effect.sync(() => log.info("event disconnected"))),
|
||||
),
|
||||
{
|
||||
contentType: "text/event-stream",
|
||||
headers: {
|
||||
"Cache-Control": "no-cache, no-transform",
|
||||
"X-Accel-Buffering": "no",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export const eventHandlers = HttpApiBuilder.group(EventApi, "event", (handlers) =>
|
||||
@@ -70,7 +77,7 @@ export const eventHandlers = HttpApiBuilder.group(EventApi, "event", (handlers)
|
||||
return handlers.handleRaw(
|
||||
"subscribe",
|
||||
Effect.fn("EventHttpApi.subscribe")(function* () {
|
||||
return eventResponse(bus)
|
||||
return yield* eventResponse(bus)
|
||||
}),
|
||||
)
|
||||
}),
|
||||
|
||||
@@ -21,27 +21,36 @@ describe("bin/kilo tree-sitter resources", () => {
|
||||
return { bin, log, wasm, wrapper: join(dir, "kilo") }
|
||||
}
|
||||
|
||||
async function run(root: string, bin: string | undefined, log: string, wrapper?: string) {
|
||||
async function run(root: string, bin: string | undefined, log: string, wrapper?: string, failCached?: boolean) {
|
||||
const capture = `
|
||||
const { EventEmitter } = require("events")
|
||||
const kiloFs = require("fs")
|
||||
const kiloChild = require("child_process")
|
||||
const log = process.argv[1]
|
||||
const wrapper = process.argv[2]
|
||||
const failCached = process.argv[3] === "true"
|
||||
const realpathSync = kiloFs.realpathSync
|
||||
kiloFs.realpathSync = (file) => wrapper && file === __filename ? wrapper : realpathSync(file)
|
||||
kiloChild.spawnSync = () => {
|
||||
kiloFs.realpathSync = (file) => file === __filename ? wrapper || process.cwd() : realpathSync(file)
|
||||
kiloChild.spawn = (target) => {
|
||||
if (failCached && target.endsWith(".kilo")) throw new Error("cached binary failed")
|
||||
kiloFs.writeFileSync(log, process.env.KILO_TREE_SITTER_WASM_DIR || "")
|
||||
return { status: 0 }
|
||||
const child = new EventEmitter()
|
||||
child.kill = () => {}
|
||||
process.nextTick(() => child.emit("exit", 0))
|
||||
return child
|
||||
}
|
||||
`
|
||||
const source = (await Bun.file(script).text()).replace(/^#!.*\n/, "")
|
||||
return Bun.spawnSync(["node", "--input-type=commonjs", "--eval", capture + source, log, wrapper ?? ""], {
|
||||
cwd: root,
|
||||
env: {
|
||||
PATH: process.env.PATH ?? "",
|
||||
...(bin ? { KILO_BIN_PATH: bin } : {}),
|
||||
return Bun.spawnSync(
|
||||
["node", "--input-type=commonjs", "--eval", capture + source, log, wrapper ?? "", String(failCached)],
|
||||
{
|
||||
cwd: root,
|
||||
env: {
|
||||
PATH: process.env.PATH ?? "",
|
||||
...(bin ? { KILO_BIN_PATH: bin } : {}),
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
test("exports co-located tree-sitter WASM dir for optional package binary", async () => {
|
||||
@@ -69,4 +78,18 @@ kiloChild.spawnSync = () => {
|
||||
await rm(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test("falls back to the optional package when the cached binary cannot spawn", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "kilo-bin-tree-sitter-"))
|
||||
try {
|
||||
const cached = await setup(root, false)
|
||||
const item = await setup(root, true)
|
||||
const proc = await run(root, undefined, item.log, cached.wrapper, true)
|
||||
|
||||
expect(proc.exitCode).toBe(0)
|
||||
expect(await Bun.file(item.log).text()).toBe(cached.wasm)
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user