mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-01 04:46:43 +08:00
refactor: hot reload kilo console configs
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/cli": patch
|
||||
---
|
||||
|
||||
Show Agent Manager and other Git worktrees in the Kilo Console project view.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@kilocode/cli": patch
|
||||
---
|
||||
|
||||
Keep Kilo Console terminal sessions open when changing diff layout and other console preferences.
|
||||
@@ -79,8 +79,9 @@ export const configConsoleHandlers = HttpApiBuilder.group(InstanceHttpApi, "conf
|
||||
return yield* config.get()
|
||||
}
|
||||
if (body.scope === "global") {
|
||||
const result = yield* config.updateGlobal(patch)
|
||||
if (result.changed) {
|
||||
const hot = Object.keys(patch).every((key) => key === "console")
|
||||
const result = yield* config.updateGlobal(patch, hot ? { dispose: false } : undefined)
|
||||
if (result.changed && !hot) {
|
||||
yield* disposeAllInstancesAndEmitGlobalDisposed({ swallowErrors: true }).pipe(
|
||||
Effect.catchCause(() => Effect.void),
|
||||
)
|
||||
|
||||
@@ -112,10 +112,13 @@ export const experimentalHandlers = HttpApiBuilder.group(InstanceHttpApi, "exper
|
||||
return yield* registry.ids()
|
||||
})
|
||||
|
||||
// kilocode_change start - discover Agent Manager and external git worktrees
|
||||
const worktree = Effect.fn("ExperimentalHttpApi.worktree")(function* () {
|
||||
const ctx = yield* InstanceState.context
|
||||
return yield* project.sandboxes(ctx.project.id)
|
||||
return yield* mapWorktreeError(worktreeSvc.list()).pipe(
|
||||
Effect.map((items) => items.map((item) => item.directory)),
|
||||
)
|
||||
})
|
||||
// kilocode_change end
|
||||
|
||||
const worktreeCreate = Effect.fn("ExperimentalHttpApi.worktreeCreate")(function* (ctx: {
|
||||
payload: Worktree.CreateInput | undefined
|
||||
|
||||
@@ -6,12 +6,14 @@ import { Server } from "../../../src/server/server"
|
||||
import { Config } from "../../../src/config/config"
|
||||
import { KilocodeConfigOverlay } from "../../../src/kilocode/config/overlay"
|
||||
import { Permission } from "../../../src/permission"
|
||||
import { PtyPaths } from "../../../src/server/routes/instance/httpapi/groups/pty"
|
||||
import { resetDatabase } from "../../fixture/db"
|
||||
import { disposeAllInstances, tmpdir } from "../../fixture/fixture"
|
||||
|
||||
void Log.init({ print: false })
|
||||
|
||||
const original = Global.Path.config
|
||||
const terminal = process.platform === "win32" ? test.skip : test.serial
|
||||
|
||||
type Overlay = {
|
||||
fields: Record<string, { source: string; inherited: boolean; overridden: boolean; value?: unknown }>
|
||||
@@ -323,6 +325,35 @@ describe("config overlay routes", () => {
|
||||
)
|
||||
})
|
||||
|
||||
terminal("preserves active terminals after updating global console preferences", async () => {
|
||||
await using global = await tmpdir()
|
||||
await using project = await tmpdir()
|
||||
;(Global.Path as { config: string }).config = global.path
|
||||
const headers = { "x-kilo-directory": project.path }
|
||||
const created = await Server.Default().app.request(PtyPaths.create, {
|
||||
method: "POST",
|
||||
headers: { ...headers, "content-type": "application/json" },
|
||||
body: JSON.stringify({ command: "/usr/bin/env", args: ["sh", "-c", "sleep 30"], title: "console" }),
|
||||
})
|
||||
const info = await json<{ id: string }>(created)
|
||||
|
||||
try {
|
||||
await json(
|
||||
await request(Server.Default().app, undefined, "/config/overlay", {
|
||||
method: "PATCH",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ scope: "global", set: { console: { diff_style: "split" } } }),
|
||||
}),
|
||||
)
|
||||
|
||||
const found = await Server.Default().app.request(PtyPaths.get.replace(":ptyID", info.id), { headers })
|
||||
expect(found.status).toBe(200)
|
||||
expect(await found.json()).toMatchObject({ id: info.id, title: "console", status: "running" })
|
||||
} finally {
|
||||
await Server.Default().app.request(PtyPaths.remove.replace(":ptyID", info.id), { method: "DELETE", headers })
|
||||
}
|
||||
})
|
||||
|
||||
for (const value of [false, true]) {
|
||||
test.serial(
|
||||
`${value ? "httpapi" : "legacy"} global overlay update refreshes existing project instances without a project directory`,
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { $ } from "bun"
|
||||
import { describe, expect } from "bun:test"
|
||||
import path from "path"
|
||||
import { Effect, Layer, Schema } from "effect"
|
||||
import { HttpRouter } from "effect/unstable/http"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { HttpApiApp } from "../../../src/server/routes/instance/httpapi/server"
|
||||
import { ExperimentalPaths } from "../../../src/server/routes/instance/httpapi/groups/experimental"
|
||||
import { resetDatabase } from "../../fixture/db"
|
||||
import { TestInstance } from "../../fixture/fixture"
|
||||
import { testEffect } from "../../lib/effect"
|
||||
|
||||
const state = Layer.effectDiscard(
|
||||
Effect.gen(function* () {
|
||||
const original = Flag.KILO_EXPERIMENTAL_WORKSPACES
|
||||
Flag.KILO_EXPERIMENTAL_WORKSPACES = true
|
||||
yield* Effect.addFinalizer(() =>
|
||||
Effect.promise(async () => {
|
||||
Flag.KILO_EXPERIMENTAL_WORKSPACES = original
|
||||
await resetDatabase()
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
const it = testEffect(state)
|
||||
const run = process.platform === "win32" ? it.instance.skip : it.instance
|
||||
|
||||
type Server = ReturnType<typeof HttpRouter.toWebHandler>
|
||||
|
||||
function serve() {
|
||||
return Effect.acquireRelease(
|
||||
Effect.sync(() => HttpRouter.toWebHandler(HttpApiApp.routes, { disableLogger: true })),
|
||||
(server) => Effect.promise(() => server.dispose()).pipe(Effect.ignore),
|
||||
)
|
||||
}
|
||||
|
||||
function request(server: Server, input: string) {
|
||||
return Effect.promise(() => server.handler(new Request(new URL(input, "http://localhost")), HttpApiApp.context))
|
||||
}
|
||||
|
||||
describe("Kilo Console worktree listing", () => {
|
||||
run(
|
||||
"lists worktrees created by Agent Manager",
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const test = yield* TestInstance
|
||||
const server = yield* serve()
|
||||
const directory = path.join(test.directory, ".kilo", "worktrees", "console-list")
|
||||
yield* Effect.promise(() =>
|
||||
$`git worktree add --quiet -b console-list ${directory} HEAD`.cwd(test.directory).quiet(),
|
||||
)
|
||||
|
||||
const response = yield* request(
|
||||
server,
|
||||
`${ExperimentalPaths.worktree}?directory=${encodeURIComponent(test.directory)}`,
|
||||
)
|
||||
expect(response.status).toBe(200)
|
||||
const worktrees = Schema.decodeUnknownSync(Schema.Array(Schema.String))(
|
||||
yield* Effect.promise(() => response.json()),
|
||||
)
|
||||
|
||||
expect(worktrees).toContain(directory)
|
||||
expect(worktrees).not.toContain(test.directory)
|
||||
}),
|
||||
{ git: true },
|
||||
)
|
||||
})
|
||||
Reference in New Issue
Block a user