mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-21 14:07:20 +08:00
refactor(kilo-gateway): clean up proxy auth naming, error handling, and OpenAPI schema alignment
Rename `proxy` helper to `proxyAuth` for clarity, switch modes endpoint to gracefully handle missing auth instead of returning BadRequest, remove redundant Content-Type header from audio transcriptions response, and fix OpenAPI parameter types: - Change `worktrees` query param from string enum to boolean - Add `^que.*` pattern to network endpoint requestID path params - Register experimental session worktrees in query parameter schemas - Re-enable and update Effect HttpApi route parity tests
This commit is contained in:
@@ -55,7 +55,7 @@ export const kiloGatewayHandlers = HttpApiBuilder.group(InstanceHttpApi, "kilo",
|
||||
return { profile, balance, currentOrgId }
|
||||
})
|
||||
|
||||
const proxy = Effect.fn("KiloGatewayHttpApi.proxyAuth")(function* () {
|
||||
const proxyAuth = Effect.fn("KiloGatewayHttpApi.proxyAuth")(function* () {
|
||||
const info = yield* auth.get("kilo").pipe(Effect.mapError(() => new HttpApiError.Unauthorized({})))
|
||||
return {
|
||||
auth: info,
|
||||
@@ -65,7 +65,7 @@ export const kiloGatewayHandlers = HttpApiBuilder.group(InstanceHttpApi, "kilo",
|
||||
})
|
||||
|
||||
const modes = Effect.fn("KiloGatewayHttpApi.modes")(function* () {
|
||||
const info = yield* auth.get("kilo").pipe(Effect.mapError(() => new HttpApiError.BadRequest({})))
|
||||
const info = yield* auth.get("kilo").pipe(Effect.catch(() => Effect.succeed(undefined)))
|
||||
if (!info || info.type !== "oauth" || !info.access || !info.accountId) return { modes: [] }
|
||||
|
||||
return yield* Effect.promise(() => fetchOrganizationModes(info.access, info.accountId)).pipe(
|
||||
@@ -75,7 +75,7 @@ export const kiloGatewayHandlers = HttpApiBuilder.group(InstanceHttpApi, "kilo",
|
||||
})
|
||||
|
||||
const fim = Effect.fn("KiloGatewayHttpApi.fim")(function* (ctx: { payload: typeof FimBody.Type }) {
|
||||
const info = yield* proxy()
|
||||
const info = yield* proxyAuth()
|
||||
if (!info.auth) return yield* Effect.fail(new HttpApiError.Unauthorized({}))
|
||||
if (!info.token) return yield* Effect.fail(new HttpApiError.Unauthorized({}))
|
||||
|
||||
@@ -136,7 +136,7 @@ export const kiloGatewayHandlers = HttpApiBuilder.group(InstanceHttpApi, "kilo",
|
||||
const audioTranscriptions = Effect.fn("KiloGatewayHttpApi.audioTranscriptions")(function* (ctx: {
|
||||
payload: typeof AudioTranscriptionsBody.Type
|
||||
}) {
|
||||
const info = yield* proxy()
|
||||
const info = yield* proxyAuth()
|
||||
if (!info.auth) return yield* Effect.fail(new HttpApiError.Unauthorized({}))
|
||||
if (!info.token) return yield* Effect.fail(new HttpApiError.Unauthorized({}))
|
||||
|
||||
@@ -160,9 +160,6 @@ export const kiloGatewayHandlers = HttpApiBuilder.group(InstanceHttpApi, "kilo",
|
||||
return HttpServerResponse.raw(text, {
|
||||
status: response.status,
|
||||
contentType: response.headers.get("Content-Type") ?? "application/json",
|
||||
headers: {
|
||||
"Content-Type": response.headers.get("Content-Type") ?? "application/json",
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ const QueryNumberParameters = new Set(["start", "cursor", "limit", "method"])
|
||||
const QueryBooleanParameters = new Set(["roots", "archived"])
|
||||
const QueryParameterSchemas = {
|
||||
"GET /find/file limit": { type: "integer", minimum: 1, maximum: 200 },
|
||||
"GET /experimental/session worktrees": { type: "boolean" }, // kilocode_change
|
||||
"GET /kilo/cloud-sessions cursor": { type: "string" }, // kilocode_change
|
||||
"GET /session/{sessionID}/diff messageID": { type: "string", pattern: "^msg.*" },
|
||||
"GET /session/{sessionID}/message limit": { type: "integer", minimum: 0, maximum: Number.MAX_SAFE_INTEGER },
|
||||
@@ -523,6 +524,8 @@ function pathParameterSchema(route: string, name: string) {
|
||||
if (name === "id" && route.startsWith("POST /experimental/workspace/")) return { type: "string", pattern: "^wrk.*" }
|
||||
if (name === "requestID" && route.startsWith("POST /permission/")) return { type: "string", pattern: "^per.*" }
|
||||
if (name === "requestID" && route.startsWith("POST /question/")) return { type: "string", pattern: "^que.*" }
|
||||
// /network/* reuses QuestionID (prefix "que"), not a separate brand. // kilocode_change
|
||||
if (name === "requestID" && route.startsWith("POST /network/")) return { type: "string", pattern: "^que.*" } // kilocode_change
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,12 @@ import { afterEach, describe, expect, test } from "bun:test"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { ControlPaths } from "../../src/server/routes/instance/httpapi/groups/control"
|
||||
import { ExperimentalPaths } from "../../src/server/routes/instance/httpapi/groups/experimental"
|
||||
import { FilePaths } from "../../src/server/routes/instance/httpapi/groups/file"
|
||||
import { GlobalPaths } from "../../src/server/routes/instance/httpapi/groups/global"
|
||||
import { KiloGatewayPaths } from "../../src/kilocode/server/httpapi/groups/kilo-gateway"
|
||||
import { PublicApi } from "../../src/server/routes/instance/httpapi/public"
|
||||
import { SessionPaths } from "../../src/server/routes/instance/httpapi/groups/session"
|
||||
import { ExperimentalHttpApiServer } from "../../src/server/routes/instance/httpapi/server"
|
||||
import { Server } from "../../src/server/server"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
@@ -222,13 +224,8 @@ describe("HttpApi server", () => {
|
||||
expect(Server.backend()).toEqual({ backend: "effect-httpapi", reason: "env" })
|
||||
})
|
||||
|
||||
// kilocode_change start - skip Effect HttpApi parity tests until Kilo overlay routes are migrated.
|
||||
// These tests verify every Hono route has an Effect HttpApi contract. Kilo-specific routes
|
||||
// (/config/warnings, /indexing/status, /kilo/claw/*, /kilo/cloud-sessions, /experimental/worktree/diff*)
|
||||
// aren't yet wired into PublicApi. The Effect HttpApi bridge is gated behind KILO_EXPERIMENTAL_HTTPAPI
|
||||
// and is not enabled in any production client (VS Code extension, JetBrains, TUI, desktop all use Hono).
|
||||
// Follow-up: migrate Kilo overlay routes onto the Effect HttpApi bridge.
|
||||
test.skip("covers every generated OpenAPI route with Effect HttpApi contracts", async () => {
|
||||
// kilocode_change start - Effect HttpApi route parity.
|
||||
test("covers every generated OpenAPI route with Effect HttpApi contracts", async () => {
|
||||
const honoRoutes = openApiRouteKeys(await Server.openapiHono())
|
||||
const effectRoutes = openApiRouteKeys(effectOpenApi())
|
||||
|
||||
@@ -237,13 +234,14 @@ describe("HttpApi server", () => {
|
||||
"GET /api/session",
|
||||
"GET /api/session/{sessionID}/context",
|
||||
"GET /api/session/{sessionID}/message",
|
||||
"GET /indexing/status",
|
||||
"POST /api/session/{sessionID}/compact",
|
||||
"POST /api/session/{sessionID}/prompt",
|
||||
"POST /api/session/{sessionID}/wait",
|
||||
])
|
||||
})
|
||||
|
||||
test.skip("matches generated OpenAPI route parameters", async () => {
|
||||
test("matches generated OpenAPI route parameters", async () => {
|
||||
const hono = openApiParameters(await Server.openapiHono())
|
||||
const effect = openApiParameters(effectOpenApi())
|
||||
|
||||
@@ -254,7 +252,7 @@ describe("HttpApi server", () => {
|
||||
).toEqual([])
|
||||
})
|
||||
|
||||
test.skip("matches generated OpenAPI request body shape", async () => {
|
||||
test("matches generated OpenAPI request body shape", async () => {
|
||||
const hono = openApiRequestBodies(await Server.openapiHono())
|
||||
const effect = openApiRequestBodies(effectOpenApi())
|
||||
|
||||
@@ -270,17 +268,24 @@ describe("HttpApi server", () => {
|
||||
test("Kilo overlay routes are mirrored in Hono and Effect specs", async () => {
|
||||
const hono = new Set(openApiRouteKeys(await Server.openapiHono()))
|
||||
const effect = new Set(openApiRouteKeys(effectOpenApi()))
|
||||
// The 22 Kilo overlay paths ported to Effect HttpApi. Both backends must serve each.
|
||||
// Kilo overlay paths ported to Effect HttpApi. Both backends must serve each.
|
||||
const kilo = [
|
||||
"POST /permission/allow-everything",
|
||||
"POST /enhance-prompt",
|
||||
"POST /commit-message",
|
||||
`GET ${ExperimentalPaths.worktreeDiff}`,
|
||||
`GET ${ExperimentalPaths.worktreeDiffFile}`,
|
||||
`GET ${ExperimentalPaths.worktreeDiffSummary}`,
|
||||
"GET /network",
|
||||
"POST /network/{requestID}/reply",
|
||||
"POST /network/{requestID}/reject",
|
||||
`GET ${KiloGatewayPaths.modes}`,
|
||||
`POST ${KiloGatewayPaths.fim}`,
|
||||
`POST ${KiloGatewayPaths.audioTranscriptions}`,
|
||||
"POST /remote/enable",
|
||||
"POST /remote/disable",
|
||||
"GET /remote/status",
|
||||
`POST ${SessionPaths.viewed}`,
|
||||
"POST /telemetry/capture",
|
||||
"POST /telemetry/setEnabled",
|
||||
"GET /suggestion",
|
||||
|
||||
@@ -866,7 +866,7 @@ export class Session extends HeyApiClient {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
projectID?: string
|
||||
worktrees?: "true" | "false"
|
||||
worktrees?: boolean
|
||||
roots?: boolean | "true" | "false"
|
||||
start?: number
|
||||
cursor?: number
|
||||
|
||||
@@ -4186,7 +4186,7 @@ export type ExperimentalSessionListData = {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
projectID?: string
|
||||
worktrees?: "true" | "false"
|
||||
worktrees?: boolean
|
||||
roots?: boolean | "true" | "false"
|
||||
start?: number
|
||||
cursor?: number
|
||||
|
||||
@@ -1488,8 +1488,7 @@
|
||||
"name": "worktrees",
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["true", "false"]
|
||||
"type": "boolean"
|
||||
},
|
||||
"required": false
|
||||
},
|
||||
@@ -10564,7 +10563,8 @@
|
||||
"name": "requestID",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"pattern": "^que.*"
|
||||
},
|
||||
"required": true
|
||||
}
|
||||
@@ -10637,7 +10637,8 @@
|
||||
"name": "requestID",
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
"type": "string",
|
||||
"pattern": "^que.*"
|
||||
},
|
||||
"required": true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user