mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-10-28 02:42:15 +08:00
fix(vscode): defer unused worktree watchers
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@kilocode/cli": patch
|
||||
"kilo-code": patch
|
||||
---
|
||||
|
||||
Prevent VS Code sessions and Agent Manager worktrees from starting unused file watchers and defer file indexing until search is used.
|
||||
@@ -166,22 +166,33 @@ export const fffLayer = Layer.effect(
|
||||
return real !== undefined && FSUtil.contains(root.path, real)
|
||||
})
|
||||
// kilocode_change end
|
||||
const result = yield* Effect.try({
|
||||
try: () =>
|
||||
Fff.create({
|
||||
basePath: location.directory,
|
||||
aiMode: true,
|
||||
...scanning(location.directory), // kilocode_change - permit broad scanning only at the exact boundary.
|
||||
}),
|
||||
catch: (cause) => cause,
|
||||
}).pipe(Effect.orDie)
|
||||
if (!result.ok) return yield* Effect.die(result.error)
|
||||
yield* Effect.addFinalizer(() => Effect.sync(() => result.value.destroy()).pipe(Effect.ignore))
|
||||
// kilocode_change start - defer FFF until search because other location consumers do not need its native index.
|
||||
const scope = yield* Scope.Scope
|
||||
const load = yield* Effect.cached(
|
||||
Effect.gen(function* () {
|
||||
const result = yield* Effect.try({
|
||||
try: () =>
|
||||
Fff.create({
|
||||
basePath: location.directory,
|
||||
aiMode: true,
|
||||
disableMmapCache: true,
|
||||
disableContentIndexing: true,
|
||||
...scanning(location.directory),
|
||||
}),
|
||||
catch: (cause) => cause,
|
||||
}).pipe(Effect.orDie)
|
||||
if (!result.ok) return yield* Effect.die(result.error)
|
||||
yield* Scope.addFinalizer(scope, Effect.sync(() => result.value.destroy()).pipe(Effect.ignore))
|
||||
return result
|
||||
}),
|
||||
)
|
||||
// kilocode_change end
|
||||
return Service.of({
|
||||
glob: (input) =>
|
||||
// kilocode_change start
|
||||
Effect.gen(function* () {
|
||||
const { root, target } = yield* inspect(input.path)
|
||||
const result = yield* load
|
||||
// kilocode_change end
|
||||
const prefix = input.path?.replaceAll("\\", "/").replace(/\/$/, "")
|
||||
// kilocode_change start
|
||||
@@ -210,6 +221,7 @@ export const fffLayer = Layer.effect(
|
||||
// kilocode_change start
|
||||
Effect.gen(function* () {
|
||||
const { root, target } = yield* inspect(input.path)
|
||||
const result = yield* load
|
||||
// kilocode_change end
|
||||
const prefix = input.path?.replaceAll("\\", "/").replace(/\/$/, "")
|
||||
// kilocode_change start
|
||||
@@ -247,7 +259,8 @@ export const fffLayer = Layer.effect(
|
||||
})
|
||||
}),
|
||||
find: (input) =>
|
||||
Effect.sync(() => {
|
||||
Effect.gen(function* () { // kilocode_change - load the native index only for an actual search.
|
||||
const result = yield* load // kilocode_change
|
||||
const options = { pageIndex: 0, pageSize: input.limit ?? 50 }
|
||||
const items = (() => {
|
||||
if (input.type === "file") {
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { FileFinder, type InitOptions } from "@ff-labs/fff-bun"
|
||||
import "@opencode-ai/core/filesystem"
|
||||
import { Fff } from "@opencode-ai/core/filesystem/fff.bun"
|
||||
import { FSUtil } from "@opencode-ai/core/fs-util"
|
||||
import os from "os"
|
||||
import path from "path"
|
||||
import { Context, Effect, Layer, Scope } from "effect"
|
||||
import { scanning } from "@opencode-ai/core/kilocode/fff"
|
||||
import { Location } from "@opencode-ai/core/location"
|
||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
||||
import { location } from "../fixture/location"
|
||||
import { tmpdir } from "../fixture/tmpdir"
|
||||
|
||||
describe("FFF scanning boundaries", () => {
|
||||
test("enables filesystem-root scanning only at the exact root", () => {
|
||||
@@ -25,3 +34,74 @@ describe("FFF scanning boundaries", () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("FFF lifecycle", () => {
|
||||
test("initializes on the first search and reuses one picker", async () => {
|
||||
if (!Fff.available()) return
|
||||
|
||||
const dir = await tmpdir()
|
||||
const create = FileFinder.create
|
||||
const calls = { create: 0, destroy: 0, opts: undefined as InitOptions | undefined }
|
||||
try {
|
||||
FileFinder.create = (opts) => {
|
||||
calls.create++
|
||||
calls.opts = opts
|
||||
const result = create(opts)
|
||||
if (!result.ok) return result
|
||||
const destroy = result.value.destroy.bind(result.value)
|
||||
result.value.destroy = () => {
|
||||
calls.destroy++
|
||||
destroy()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
await Effect.runPromise(
|
||||
Effect.acquireUseRelease(
|
||||
Scope.make(),
|
||||
(scope) =>
|
||||
Effect.gen(function* () {
|
||||
const { FileSystemSearch } = yield* Effect.promise(() => import("@opencode-ai/core/filesystem/search"))
|
||||
const layer = FileSystemSearch.fffLayer.pipe(
|
||||
Layer.provide(FSUtil.defaultLayer),
|
||||
Layer.provide(
|
||||
Layer.succeed(
|
||||
Location.Service,
|
||||
Location.Service.of(
|
||||
location(
|
||||
{ directory: AbsolutePath.make(dir.path) },
|
||||
{ vcs: { type: "git", store: AbsolutePath.make(path.join(dir.path, ".git")) } },
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
const context = yield* Layer.buildWithScope(layer, scope)
|
||||
const service = Context.get(context, FileSystemSearch.Service)
|
||||
expect(calls.create).toBe(0)
|
||||
|
||||
yield* Effect.all(
|
||||
[
|
||||
service.find({ query: "", type: "file", limit: 1 }),
|
||||
service.find({ query: "", type: "file", limit: 1 }),
|
||||
],
|
||||
{ concurrency: "unbounded" },
|
||||
)
|
||||
expect(calls.create).toBe(1)
|
||||
expect(calls.opts?.disableMmapCache).toBe(true)
|
||||
expect(calls.opts?.disableContentIndexing).toBe(true)
|
||||
|
||||
yield* service.find({ query: "", type: "file", limit: 1 })
|
||||
expect(calls.create).toBe(1)
|
||||
expect(calls.destroy).toBe(0)
|
||||
}),
|
||||
(scope, exit) => Scope.close(scope, exit),
|
||||
),
|
||||
)
|
||||
expect(calls.destroy).toBe(1)
|
||||
} finally {
|
||||
FileFinder.create = create
|
||||
await dir[Symbol.asyncDispose]()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -29,7 +29,12 @@ export function resolveIndexingEnv(folders: readonly WorkspaceFolderLike[] | und
|
||||
}
|
||||
|
||||
export function resolveManagedServerEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
||||
return { ...env, KILO_DISABLE_CHANNEL_DB: "true" }
|
||||
return {
|
||||
...env,
|
||||
KILO_DISABLE_CHANNEL_DB: "true",
|
||||
// VS Code does not consume the backend's file.watcher.updated events.
|
||||
KILO_EXPERIMENTAL_DISABLE_FILEWATCHER: "true",
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerManager {
|
||||
|
||||
@@ -315,10 +315,17 @@ describe("server workspace helpers", () => {
|
||||
expect(resolveIndexingEnv([{ uri: { fsPath: "/repo" } }])).toEqual({})
|
||||
})
|
||||
|
||||
it("uses the shared database for the managed backend while preserving the environment", () => {
|
||||
expect(resolveManagedServerEnv({ PATH: "/usr/bin", KILO_DISABLE_CHANNEL_DB: "false" })).toEqual({
|
||||
it("disables unused managed-backend services while preserving the environment", () => {
|
||||
expect(
|
||||
resolveManagedServerEnv({
|
||||
PATH: "/usr/bin",
|
||||
KILO_DISABLE_CHANNEL_DB: "false",
|
||||
KILO_EXPERIMENTAL_DISABLE_FILEWATCHER: "false",
|
||||
}),
|
||||
).toEqual({
|
||||
PATH: "/usr/bin",
|
||||
KILO_DISABLE_CHANNEL_DB: "true",
|
||||
KILO_EXPERIMENTAL_DISABLE_FILEWATCHER: "true",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user