d2e21c5006
- InstanceStore: run Instance.provide init inside the ALS context so KilocodeBootstrap (and any forkDetach work it spawns like KiloIndexing.init) can read Instance.directory. Upstream refactor moved init out of the ALS scope; kilo-main's pre-merge Instance.provide wrapped it. Without this, KiloIndexing.init silently fails with "No context found for instance".
- kilocode/agent: thread worktree through planGuard/patchAgents instead of reading Instance.worktree at agent state construction time. Agent state is built inside Effect (no ALS) via InstanceState.make — reading the ALS-backed Instance.worktree there crashed under the new architecture.
- kilocode/agent: drop the "*": "ask" from explore external_directory — defaults already provides it, and redefining here overwrites the tmp/skill allowlist via findLast().
- test/server/httpapi-instance.test.ts: revert the ported Hono-bridge tests. Upstream put the same tests in httpapi-instance.legacy.test.ts (renamed file); the port duplicated them.
- test/server/httpapi-instance.legacy.test.ts: mark the catalog test test.skip with Kilo's original rationale (/agent 500s via the bridge; the bridge is not enabled in any production client).
- test/server/httpapi-ui.test.ts: delete. Tests upstream's proxy-to-app.opencode.ai fallback that Kilo intentionally removed (src/server/routes/ui.ts kilocode_change).
- test/server/httpapi-raw-route-auth.test.ts: basic("opencode", ...) → basic("kilo", ...) to match the Kilo username default.
- test/provider/models.test.ts: skip describe block. Upstream tests assert raw-fixture passthrough but Kilo's ModelsDev.get() filters/injects providers based on Config.get(), which needs an Instance context the test doesn't provide.
90 lines
3.3 KiB
TypeScript
90 lines
3.3 KiB
TypeScript
import { afterEach, describe, expect, test } from "bun:test"
|
|
import { ConfigProvider, Layer } from "effect"
|
|
import { HttpRouter } from "effect/unstable/http"
|
|
import { Flag } from "@opencode-ai/core/flag/flag"
|
|
import { Instance } from "../../src/project/instance"
|
|
import { EventPaths } from "../../src/server/routes/instance/httpapi/event"
|
|
import { PtyPaths } from "../../src/server/routes/instance/httpapi/groups/pty"
|
|
import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server"
|
|
import { PtyID } from "../../src/pty/schema"
|
|
import { resetDatabase } from "../fixture/db"
|
|
import { disposeAllInstances, tmpdir } from "../fixture/fixture"
|
|
import * as Log from "@opencode-ai/core/util/log"
|
|
|
|
void Log.init({ print: false })
|
|
|
|
const originalHttpApi = Flag.KILO_EXPERIMENTAL_HTTPAPI
|
|
|
|
function app(input: { password?: string; username?: string }) {
|
|
Flag.KILO_EXPERIMENTAL_HTTPAPI = true
|
|
const handler = HttpRouter.toWebHandler(
|
|
ExperimentalHttpApiServer.routes.pipe(
|
|
Layer.provide(
|
|
ConfigProvider.layer(
|
|
ConfigProvider.fromUnknown({
|
|
KILO_SERVER_PASSWORD: input.password,
|
|
KILO_SERVER_USERNAME: input.username,
|
|
}),
|
|
),
|
|
),
|
|
),
|
|
{ disableLogger: true },
|
|
).handler
|
|
|
|
return {
|
|
fetch: (request: Request) => handler(request, ExperimentalHttpApiServer.context),
|
|
request(input: string | URL | Request, init?: RequestInit) {
|
|
return this.fetch(input instanceof Request ? input : new Request(new URL(input, "http://localhost"), init))
|
|
},
|
|
}
|
|
}
|
|
|
|
function basic(username: string, password: string) {
|
|
return `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`
|
|
}
|
|
|
|
async function cancelBody(response: Response) {
|
|
await response.body?.cancel().catch(() => {})
|
|
}
|
|
|
|
afterEach(async () => {
|
|
Flag.KILO_EXPERIMENTAL_HTTPAPI = originalHttpApi
|
|
await disposeAllInstances()
|
|
await resetDatabase()
|
|
})
|
|
|
|
describe("HttpApi raw route authorization", () => {
|
|
test("requires configured auth before opening the raw instance event stream", async () => {
|
|
await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } })
|
|
const server = app({ password: "secret" })
|
|
const headers = { "x-kilo-directory": tmp.path }
|
|
|
|
const missing = await server.request(EventPaths.event, { headers })
|
|
await cancelBody(missing)
|
|
expect(missing.status).toBe(401)
|
|
|
|
const authed = await server.request(EventPaths.event, {
|
|
headers: { ...headers, authorization: basic("kilo", "secret") }, // kilocode_change - Kilo username default
|
|
})
|
|
await cancelBody(authed)
|
|
expect(authed.status).toBe(200)
|
|
})
|
|
|
|
test("requires configured auth before resolving the raw PTY websocket route", async () => {
|
|
await using tmp = await tmpdir({ git: true, config: { formatter: false, lsp: false } })
|
|
const server = app({ password: "secret" })
|
|
const route = PtyPaths.connect.replace(":ptyID", PtyID.ascending())
|
|
const headers = { "x-kilo-directory": tmp.path }
|
|
|
|
const missing = await server.request(route, { headers })
|
|
await cancelBody(missing)
|
|
expect(missing.status).toBe(401)
|
|
|
|
const authed = await server.request(route, {
|
|
headers: { ...headers, authorization: basic("kilo", "secret") }, // kilocode_change - Kilo username default
|
|
})
|
|
await cancelBody(authed)
|
|
expect(authed.status).toBe(404)
|
|
})
|
|
})
|