mirror of
https://github.com/Kilo-Org/kilocode.git
synced 2026-09-24 16:02:55 +08:00
feat(kilo-gateway): add Effect HttpApi endpoints for kilo gateway operations
Extract shared handler logic from Hono routes into reusable functions in kilo-gateway/server/handlers.ts and wire up new Effect HttpApi endpoints for profile, notifications, organization switching, claw status, and claw chat credentials. - Add handlers.ts with pure business logic (getProfile, getNotifications, setOrganization, getClawStatus, getClawChatCredentials) - Define KiloGatewayApi group with typed schemas for all endpoints - Implement Effect-based handler layer in httpapi/handlers/kilo-gateway.ts - Register kilo gateway routes in instance router and HttpApi composition - Add config/warnings endpoint to config group and handler - Regenerate SDK types and client methods for new endpoints - Update OpenAPI spec with /kilo/* and /config/warnings paths - Export new constants (KILO_CHAT_URL, KILO_EVENT_SERVICE_URL) and handler utilities from kilo-gateway package index
This commit is contained in:
@@ -53,6 +53,17 @@ export { fetchKilocodeNotifications, type KilocodeNotification } from "./api/not
|
||||
// Server Routes (optional - requires hono and OpenCode dependencies)
|
||||
// ============================================================================
|
||||
export { createKiloRoutes } from "./server/routes.js"
|
||||
export {
|
||||
GatewayError,
|
||||
UnauthorizedError,
|
||||
getOrganizationId,
|
||||
getClawChatCredentials,
|
||||
getClawStatus,
|
||||
getNotifications,
|
||||
getProfile,
|
||||
getToken,
|
||||
setOrganization,
|
||||
} from "./server/handlers.js"
|
||||
|
||||
// ============================================================================
|
||||
// Note: TUI exports moved to separate entry point
|
||||
@@ -88,6 +99,8 @@ export {
|
||||
ENV_KILO_API_URL,
|
||||
DEFAULT_KILO_API_URL,
|
||||
KILO_API_BASE,
|
||||
KILO_CHAT_URL,
|
||||
KILO_EVENT_SERVICE_URL,
|
||||
KILO_OPENROUTER_BASE,
|
||||
POLL_INTERVAL_MS,
|
||||
DEFAULT_MODEL,
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
import { fetchBalance, fetchProfile } from "../api/profile.js"
|
||||
import { fetchKilocodeNotifications } from "../api/notifications.js"
|
||||
import { clearModesCache } from "../api/modes.js"
|
||||
import { HEADER_ORGANIZATIONID, KILO_API_BASE, KILO_CHAT_URL, KILO_EVENT_SERVICE_URL } from "../api/constants.js"
|
||||
import type { KilocodeBalance, KilocodeProfile } from "../types.js"
|
||||
|
||||
export type KiloAuth =
|
||||
| { type: "api"; key: string }
|
||||
| { type: "oauth"; access: string; refresh: string; expires: number; accountId?: string }
|
||||
| { type: "wellknown"; key: string; token: string }
|
||||
|
||||
export interface KiloProfileResult {
|
||||
profile: KilocodeProfile
|
||||
balance: KilocodeBalance | null
|
||||
currentOrgId: string | null
|
||||
}
|
||||
|
||||
export interface ClawChatCredentials {
|
||||
token: string
|
||||
expiresAt: string
|
||||
kiloChatUrl: string
|
||||
eventServiceUrl: string
|
||||
}
|
||||
|
||||
export interface AuthStore {
|
||||
get(provider: string): Promise<KiloAuth | undefined>
|
||||
set(provider: string, auth: Extract<KiloAuth, { type: "oauth" }>): Promise<void>
|
||||
}
|
||||
|
||||
export interface OrganizationDeps {
|
||||
auth: AuthStore
|
||||
clear(): void
|
||||
dispose(): Promise<void>
|
||||
}
|
||||
|
||||
export class UnauthorizedError extends Error {}
|
||||
|
||||
export class GatewayError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
readonly status: number,
|
||||
) {
|
||||
super(message)
|
||||
}
|
||||
}
|
||||
|
||||
export function getToken(auth: KiloAuth | undefined) {
|
||||
if (auth?.type === "api") return auth.key
|
||||
if (auth?.type === "oauth") return auth.access
|
||||
return undefined
|
||||
}
|
||||
|
||||
export function getOrganizationId(auth: KiloAuth | undefined) {
|
||||
if (auth?.type === "oauth") return auth.accountId
|
||||
return undefined
|
||||
}
|
||||
|
||||
export async function getProfile(auth: AuthStore): Promise<KiloProfileResult> {
|
||||
const info = await auth.get("kilo")
|
||||
if (!info || info.type !== "oauth") throw new UnauthorizedError("Not authenticated with Kilo Gateway")
|
||||
|
||||
const currentOrgId = info.accountId ?? null
|
||||
const [profile, balance] = await Promise.all([
|
||||
fetchProfile(info.access),
|
||||
fetchBalance(info.access, currentOrgId ?? undefined),
|
||||
])
|
||||
return { profile, balance, currentOrgId }
|
||||
}
|
||||
|
||||
export async function getNotifications(auth: AuthStore) {
|
||||
const info = await auth.get("kilo")
|
||||
const token = getToken(info)
|
||||
if (!token) return []
|
||||
|
||||
return fetchKilocodeNotifications({
|
||||
kilocodeToken: token,
|
||||
kilocodeOrganizationId: getOrganizationId(info),
|
||||
})
|
||||
}
|
||||
|
||||
export async function setOrganization(deps: OrganizationDeps, organizationId: string | null) {
|
||||
const info = await deps.auth.get("kilo")
|
||||
if (!info || info.type !== "oauth") throw new UnauthorizedError("Not authenticated with Kilo Gateway")
|
||||
|
||||
await deps.auth.set("kilo", {
|
||||
type: "oauth",
|
||||
refresh: info.refresh,
|
||||
access: info.access,
|
||||
expires: info.expires,
|
||||
...(organizationId && { accountId: organizationId }),
|
||||
})
|
||||
|
||||
deps.clear()
|
||||
clearModesCache()
|
||||
await deps.dispose()
|
||||
return true
|
||||
}
|
||||
|
||||
export async function getClawStatus(auth: AuthStore) {
|
||||
const info = await auth.get("kilo")
|
||||
const token = getToken(info)
|
||||
if (!token) throw new UnauthorizedError("No valid token found")
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
const org = getOrganizationId(info)
|
||||
if (org) headers[HEADER_ORGANIZATIONID] = org
|
||||
|
||||
const response = await fetch(`${KILO_API_BASE}/api/kiloclaw/status`, { headers })
|
||||
if (!response.ok) throw new GatewayError(await response.text(), response.status)
|
||||
return response.json()
|
||||
}
|
||||
|
||||
export async function getClawChatCredentials(auth: AuthStore): Promise<ClawChatCredentials> {
|
||||
const info = await auth.get("kilo")
|
||||
const token = getToken(info)
|
||||
if (!token) throw new UnauthorizedError("No valid token found")
|
||||
|
||||
const expires = info?.type === "oauth" ? info.expires : Date.now() + 365 * 24 * 60 * 60 * 1000
|
||||
return {
|
||||
token,
|
||||
expiresAt: new Date(expires).toISOString(),
|
||||
kiloChatUrl: KILO_CHAT_URL,
|
||||
eventServiceUrl: KILO_EVENT_SERVICE_URL,
|
||||
}
|
||||
}
|
||||
@@ -5,19 +5,21 @@
|
||||
* This factory function accepts OpenCode dependencies to create Kilo-specific routes
|
||||
*/
|
||||
|
||||
import { fetchProfile, fetchBalance } from "../api/profile.js"
|
||||
import { fetchKilocodeNotifications, KilocodeNotificationSchema } from "../api/notifications.js"
|
||||
import { fetchOrganizationModes, clearModesCache } from "../api/modes.js"
|
||||
import {
|
||||
KILO_API_BASE,
|
||||
KILO_CHAT_URL,
|
||||
KILO_EVENT_SERVICE_URL,
|
||||
HEADER_FEATURE,
|
||||
HEADER_ORGANIZATIONID,
|
||||
} from "../api/constants.js"
|
||||
import { KILO_API_BASE, HEADER_FEATURE, HEADER_ORGANIZATIONID } from "../api/constants.js"
|
||||
import { buildKiloHeaders } from "../headers.js"
|
||||
import type { ImportDeps, DrizzleDb } from "../cloud-sessions.js"
|
||||
import { fetchCloudSession, fetchCloudSessionForImport, importSessionToDb } from "../cloud-sessions.js"
|
||||
import {
|
||||
GatewayError,
|
||||
UnauthorizedError,
|
||||
getClawChatCredentials,
|
||||
getClawStatus,
|
||||
getNotifications,
|
||||
getProfile,
|
||||
setOrganization,
|
||||
} from "./handlers.js"
|
||||
|
||||
// Type definitions for OpenCode dependencies (injected at runtime)
|
||||
type Hono = any
|
||||
@@ -167,24 +169,12 @@ export function createKiloRoutes(deps: KiloRoutesDeps) {
|
||||
},
|
||||
}),
|
||||
async (c: any) => {
|
||||
// Get Kilo auth
|
||||
const auth = await Auth.get("kilo")
|
||||
|
||||
if (!auth || auth.type !== "oauth") {
|
||||
try {
|
||||
return c.json(await getProfile(Auth))
|
||||
} catch (err) {
|
||||
if (!(err instanceof UnauthorizedError)) throw err
|
||||
return c.json({ error: "Not authenticated with Kilo Gateway" }, 401)
|
||||
}
|
||||
|
||||
const token = auth.access
|
||||
const currentOrgId = auth.accountId ?? null
|
||||
|
||||
// Fetch profile and balance in parallel
|
||||
// Pass organizationId to fetchBalance to get team balance when in org context
|
||||
const [profile, balance] = await Promise.all([
|
||||
fetchProfile(token),
|
||||
fetchBalance(token, currentOrgId ?? undefined),
|
||||
])
|
||||
|
||||
return c.json({ profile, balance, currentOrgId })
|
||||
},
|
||||
)
|
||||
.post(
|
||||
@@ -214,27 +204,21 @@ export function createKiloRoutes(deps: KiloRoutesDeps) {
|
||||
async (c: any) => {
|
||||
const { organizationId } = c.req.valid("json")
|
||||
|
||||
// Get current Kilo auth
|
||||
const auth = await Auth.get("kilo")
|
||||
|
||||
if (!auth || auth.type !== "oauth") {
|
||||
try {
|
||||
return c.json(
|
||||
await setOrganization(
|
||||
{
|
||||
auth: Auth,
|
||||
clear: () => ModelCache.clear("kilo"),
|
||||
dispose: () => InstanceStore.disposeAllInstances(),
|
||||
},
|
||||
organizationId,
|
||||
),
|
||||
)
|
||||
} catch (err) {
|
||||
if (!(err instanceof UnauthorizedError)) throw err
|
||||
return c.json({ error: "Not authenticated with Kilo Gateway" }, 401)
|
||||
}
|
||||
|
||||
// Update auth with new organization ID
|
||||
await Auth.set("kilo", {
|
||||
type: "oauth",
|
||||
refresh: auth.refresh,
|
||||
access: auth.access,
|
||||
expires: auth.expires,
|
||||
...(organizationId && { accountId: organizationId }),
|
||||
})
|
||||
|
||||
ModelCache.clear("kilo")
|
||||
clearModesCache()
|
||||
await InstanceStore.disposeAllInstances()
|
||||
|
||||
return c.json(true)
|
||||
},
|
||||
)
|
||||
.get(
|
||||
@@ -481,19 +465,7 @@ export function createKiloRoutes(deps: KiloRoutesDeps) {
|
||||
},
|
||||
}),
|
||||
async (c: any) => {
|
||||
const auth = await Auth.get("kilo")
|
||||
if (!auth) return c.json([])
|
||||
|
||||
const token = auth.type === "api" ? auth.key : auth.type === "oauth" ? auth.access : undefined
|
||||
if (!token) return c.json([])
|
||||
|
||||
const organizationId = auth.type === "oauth" ? auth.accountId : undefined
|
||||
const notifications = await fetchKilocodeNotifications({
|
||||
kilocodeToken: token,
|
||||
kilocodeOrganizationId: organizationId,
|
||||
})
|
||||
|
||||
return c.json(notifications)
|
||||
return c.json(await getNotifications(Auth))
|
||||
},
|
||||
)
|
||||
.get(
|
||||
@@ -642,29 +614,11 @@ export function createKiloRoutes(deps: KiloRoutesDeps) {
|
||||
}),
|
||||
async (c: any) => {
|
||||
try {
|
||||
const auth = await Auth.get("kilo")
|
||||
if (!auth) return c.json({ error: "Not authenticated with Kilo Gateway" }, 401)
|
||||
const token = auth.type === "api" ? auth.key : auth.type === "oauth" ? auth.access : undefined
|
||||
if (!token) return c.json({ error: "No valid token found" }, 401)
|
||||
|
||||
const organizationId = auth.type === "oauth" ? auth.accountId : undefined
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
if (organizationId) {
|
||||
headers[HEADER_ORGANIZATIONID] = organizationId
|
||||
}
|
||||
|
||||
const response = await fetch(`${KILO_API_BASE}/api/kiloclaw/status`, { headers })
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text()
|
||||
return c.json({ error: `KiloClaw request failed: ${response.status} ${text}` }, response.status as any)
|
||||
}
|
||||
|
||||
return c.json(await response.json())
|
||||
return c.json(await getClawStatus(Auth))
|
||||
} catch (err: any) {
|
||||
if (err instanceof GatewayError) {
|
||||
return c.json({ error: `KiloClaw request failed: ${err.status} ${err.message}` }, err.status as any)
|
||||
}
|
||||
console.error("[Kilo Gateway] claw/status: error", err?.message ?? err)
|
||||
return c.json({ error: "Failed to reach KiloClaw" }, 502)
|
||||
}
|
||||
@@ -701,25 +655,12 @@ export function createKiloRoutes(deps: KiloRoutesDeps) {
|
||||
},
|
||||
}),
|
||||
async (c: any) => {
|
||||
const auth = await Auth.get("kilo")
|
||||
if (!auth) return c.json({ error: "Not authenticated with Kilo Gateway" }, 401)
|
||||
const token = auth.type === "api" ? auth.key : auth.type === "oauth" ? auth.access : undefined
|
||||
if (!token) return c.json({ error: "No valid token found" }, 401)
|
||||
|
||||
// For OAuth, expires is a millisecond epoch we already track. For
|
||||
// API tokens we don't have a verified expiry locally — the JWT is
|
||||
// signed by the cloud and validated by kilo-chat/event-service on
|
||||
// every request. Use a far-future placeholder so the client cache
|
||||
// doesn't refetch unnecessarily; on 401 the client clears the
|
||||
// cache and prompts re-auth.
|
||||
const expiresAtMs = auth.type === "oauth" ? auth.expires : Date.now() + 365 * 24 * 60 * 60 * 1000
|
||||
|
||||
return c.json({
|
||||
token,
|
||||
expiresAt: new Date(expiresAtMs).toISOString(),
|
||||
kiloChatUrl: KILO_CHAT_URL,
|
||||
eventServiceUrl: KILO_EVENT_SERVICE_URL,
|
||||
})
|
||||
try {
|
||||
return c.json(await getClawChatCredentials(Auth))
|
||||
} catch (err) {
|
||||
if (!(err instanceof UnauthorizedError)) throw err
|
||||
return c.json({ error: "Not authenticated with Kilo Gateway" }, 401)
|
||||
}
|
||||
},
|
||||
)
|
||||
.get(
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
import { Schema } from "effect"
|
||||
import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
import { Authorization } from "@/server/routes/instance/httpapi/middleware/authorization"
|
||||
import { InstanceContextMiddleware } from "@/server/routes/instance/httpapi/middleware/instance-context"
|
||||
import { WorkspaceRoutingMiddleware } from "@/server/routes/instance/httpapi/middleware/workspace-routing"
|
||||
import { described } from "@/server/routes/instance/httpapi/groups/metadata"
|
||||
|
||||
const root = "/kilo"
|
||||
|
||||
export const Organization = Schema.Struct({
|
||||
id: Schema.String,
|
||||
name: Schema.String,
|
||||
role: Schema.String,
|
||||
})
|
||||
|
||||
export const Profile = Schema.Struct({
|
||||
email: Schema.String,
|
||||
name: Schema.optional(Schema.String),
|
||||
organizations: Schema.optional(Schema.Array(Organization)),
|
||||
})
|
||||
|
||||
export const Balance = Schema.Struct({
|
||||
balance: Schema.Finite,
|
||||
})
|
||||
|
||||
export const ProfileWithBalance = Schema.Struct({
|
||||
profile: Profile,
|
||||
balance: Schema.NullOr(Balance),
|
||||
currentOrgId: Schema.NullOr(Schema.String),
|
||||
})
|
||||
|
||||
export const NotificationAction = Schema.Struct({
|
||||
actionText: Schema.String,
|
||||
actionURL: Schema.String,
|
||||
})
|
||||
|
||||
export const Notification = Schema.Struct({
|
||||
id: Schema.String,
|
||||
title: Schema.String,
|
||||
message: Schema.String,
|
||||
action: Schema.optional(NotificationAction),
|
||||
showIn: Schema.optional(Schema.Array(Schema.String)),
|
||||
suggestModelId: Schema.optional(Schema.String),
|
||||
})
|
||||
|
||||
export const OrganizationBody = Schema.Struct({
|
||||
organizationId: Schema.NullOr(Schema.String),
|
||||
})
|
||||
|
||||
export const ClawStatus = Schema.Struct({
|
||||
status: Schema.NullOr(
|
||||
Schema.Literals([
|
||||
"provisioned",
|
||||
"starting",
|
||||
"restarting",
|
||||
"recovering",
|
||||
"running",
|
||||
"stopped",
|
||||
"destroying",
|
||||
"restoring",
|
||||
]),
|
||||
),
|
||||
sandboxId: Schema.optional(Schema.String),
|
||||
flyRegion: Schema.optional(Schema.String),
|
||||
machineSize: Schema.optional(
|
||||
Schema.Struct({
|
||||
cpus: Schema.Finite,
|
||||
memory_mb: Schema.Finite,
|
||||
}),
|
||||
),
|
||||
openclawVersion: Schema.optional(Schema.NullOr(Schema.String)),
|
||||
lastStartedAt: Schema.optional(Schema.NullOr(Schema.String)),
|
||||
lastStoppedAt: Schema.optional(Schema.NullOr(Schema.String)),
|
||||
channelCount: Schema.optional(Schema.Finite),
|
||||
secretCount: Schema.optional(Schema.Finite),
|
||||
userId: Schema.optional(Schema.String),
|
||||
botName: Schema.optional(Schema.NullOr(Schema.String)),
|
||||
})
|
||||
|
||||
export const ClawChatCredentials = Schema.NullOr(
|
||||
Schema.Struct({
|
||||
token: Schema.String,
|
||||
expiresAt: Schema.String,
|
||||
kiloChatUrl: Schema.String,
|
||||
eventServiceUrl: Schema.String,
|
||||
}),
|
||||
)
|
||||
|
||||
export const KiloGatewayPaths = {
|
||||
profile: `${root}/profile`,
|
||||
notifications: `${root}/notifications`,
|
||||
organization: `${root}/organization`,
|
||||
clawStatus: `${root}/claw/status`,
|
||||
clawChatCredentials: `${root}/claw/chat-credentials`,
|
||||
} as const
|
||||
|
||||
export const KiloGatewayApi = HttpApi.make("kilo")
|
||||
.add(
|
||||
HttpApiGroup.make("kilo")
|
||||
.add(
|
||||
HttpApiEndpoint.get("profile", KiloGatewayPaths.profile, {
|
||||
success: described(ProfileWithBalance, "Profile data"),
|
||||
error: [HttpApiError.BadRequest, HttpApiError.Unauthorized],
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "kilo.profile",
|
||||
summary: "Get Kilo Gateway profile",
|
||||
description: "Fetch user profile and organizations from Kilo Gateway",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("notifications", KiloGatewayPaths.notifications, {
|
||||
success: described(Schema.Array(Notification), "Notifications list"),
|
||||
error: [HttpApiError.BadRequest, HttpApiError.Unauthorized],
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "kilo.notifications",
|
||||
summary: "Get Kilo notifications",
|
||||
description: "Fetch notifications from Kilo Gateway for CLI display",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.post("organization", KiloGatewayPaths.organization, {
|
||||
payload: OrganizationBody,
|
||||
success: described(Schema.Boolean, "Organization updated successfully"),
|
||||
error: [HttpApiError.BadRequest, HttpApiError.Unauthorized],
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "kilo.organization.set",
|
||||
summary: "Update Kilo Gateway organization",
|
||||
description: "Switch to a different Kilo Gateway organization",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("clawStatus", KiloGatewayPaths.clawStatus, {
|
||||
success: described(ClawStatus, "Instance status"),
|
||||
error: [HttpApiError.Unauthorized, HttpApiError.ServiceUnavailable],
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "kilo.claw.status",
|
||||
summary: "Get KiloClaw instance status",
|
||||
description: "Fetch the user's KiloClaw instance status via the KiloClaw worker",
|
||||
}),
|
||||
),
|
||||
HttpApiEndpoint.get("clawChatCredentials", KiloGatewayPaths.clawChatCredentials, {
|
||||
success: described(ClawChatCredentials, "Kilo Chat credentials or null"),
|
||||
error: HttpApiError.Unauthorized,
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "kilo.claw.chatCredentials",
|
||||
summary: "Get KiloClaw chat credentials",
|
||||
description:
|
||||
"Returns the bearer token and endpoint URLs the client uses to talk to the Kilo Chat worker " +
|
||||
"and the Event Service. The bearer is the user's existing long-lived Kilo JWT — kilo-chat and " +
|
||||
"event-service both verify it directly with NEXTAUTH_SECRET, so no separate token mint is needed.",
|
||||
}),
|
||||
),
|
||||
)
|
||||
.annotateMerge(
|
||||
OpenApi.annotations({
|
||||
title: "kilo",
|
||||
description: "Kilo Gateway routes.",
|
||||
}),
|
||||
)
|
||||
.middleware(InstanceContextMiddleware)
|
||||
.middleware(WorkspaceRoutingMiddleware)
|
||||
.middleware(Authorization),
|
||||
)
|
||||
.annotateMerge(
|
||||
OpenApi.annotations({
|
||||
title: "kilo HttpApi",
|
||||
version: "0.0.1",
|
||||
description: "Kilo HttpApi surface.",
|
||||
}),
|
||||
)
|
||||
@@ -0,0 +1,115 @@
|
||||
import { GatewayError, UnauthorizedError, getOrganizationId, getToken } from "@kilocode/kilo-gateway"
|
||||
import {
|
||||
HEADER_ORGANIZATIONID,
|
||||
KILO_API_BASE,
|
||||
KILO_CHAT_URL,
|
||||
KILO_EVENT_SERVICE_URL,
|
||||
clearModesCache,
|
||||
fetchBalance,
|
||||
fetchKilocodeNotifications,
|
||||
fetchProfile,
|
||||
} from "@kilocode/kilo-gateway"
|
||||
import { Effect } from "effect"
|
||||
import { HttpApiBuilder, HttpApiError } from "effect/unstable/httpapi"
|
||||
import { Auth } from "@/auth"
|
||||
import { InstanceStore } from "@/project/instance-store"
|
||||
import { ModelCache } from "@/provider/model-cache"
|
||||
import { InstanceHttpApi } from "@/server/routes/instance/httpapi/api"
|
||||
|
||||
export const kiloGatewayHandlers = HttpApiBuilder.group(InstanceHttpApi, "kilo", (handlers) =>
|
||||
Effect.gen(function* () {
|
||||
const auth = yield* Auth.Service
|
||||
const store = yield* InstanceStore.Service
|
||||
|
||||
const profile = Effect.fn("KiloGatewayHttpApi.profile")(function* () {
|
||||
const info = yield* auth.get("kilo").pipe(Effect.mapError(() => new HttpApiError.BadRequest({})))
|
||||
if (!info || info.type !== "oauth") return yield* Effect.fail(new HttpApiError.Unauthorized({}))
|
||||
|
||||
const currentOrgId = info.accountId ?? null
|
||||
const [profile, balance] = yield* Effect.tryPromise({
|
||||
try: () => Promise.all([fetchProfile(info.access), fetchBalance(info.access, currentOrgId ?? undefined)]),
|
||||
catch: () => new HttpApiError.BadRequest({}),
|
||||
})
|
||||
return { profile, balance, currentOrgId }
|
||||
})
|
||||
|
||||
const notifications = Effect.fn("KiloGatewayHttpApi.notifications")(function* () {
|
||||
const info = yield* auth.get("kilo").pipe(Effect.mapError(() => new HttpApiError.BadRequest({})))
|
||||
const token = getToken(info)
|
||||
if (!token) return []
|
||||
|
||||
return yield* Effect.promise(() =>
|
||||
fetchKilocodeNotifications({
|
||||
kilocodeToken: token,
|
||||
kilocodeOrganizationId: getOrganizationId(info),
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
const organization = Effect.fn("KiloGatewayHttpApi.organization")(function* (ctx) {
|
||||
const info = yield* auth.get("kilo").pipe(Effect.mapError(() => new HttpApiError.Unauthorized({})))
|
||||
if (!info || info.type !== "oauth") return yield* Effect.fail(new HttpApiError.Unauthorized({}))
|
||||
|
||||
yield* auth
|
||||
.set("kilo", {
|
||||
type: "oauth",
|
||||
refresh: info.refresh,
|
||||
access: info.access,
|
||||
expires: info.expires,
|
||||
...(ctx.payload.organizationId && { accountId: ctx.payload.organizationId }),
|
||||
})
|
||||
.pipe(Effect.mapError(() => new HttpApiError.Unauthorized({})))
|
||||
|
||||
ModelCache.clear("kilo")
|
||||
clearModesCache()
|
||||
yield* store.disposeAll().pipe(Effect.mapError(() => new HttpApiError.Unauthorized({})))
|
||||
return true
|
||||
})
|
||||
|
||||
const clawStatus = Effect.fn("KiloGatewayHttpApi.clawStatus")(function* () {
|
||||
const info = yield* auth.get("kilo").pipe(Effect.mapError(() => new HttpApiError.ServiceUnavailable({})))
|
||||
const token = getToken(info)
|
||||
if (!token) return yield* Effect.fail(new HttpApiError.Unauthorized({}))
|
||||
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
const org = getOrganizationId(info)
|
||||
if (org) headers[HEADER_ORGANIZATIONID] = org
|
||||
|
||||
return yield* Effect.tryPromise({
|
||||
try: async () => {
|
||||
const response = await fetch(`${KILO_API_BASE}/api/kiloclaw/status`, { headers })
|
||||
if (!response.ok) throw new GatewayError(await response.text(), response.status)
|
||||
return response.json()
|
||||
},
|
||||
catch: (err) =>
|
||||
err instanceof UnauthorizedError
|
||||
? new HttpApiError.Unauthorized({})
|
||||
: new HttpApiError.ServiceUnavailable({}),
|
||||
})
|
||||
})
|
||||
|
||||
const clawChatCredentials = Effect.fn("KiloGatewayHttpApi.clawChatCredentials")(function* () {
|
||||
const info = yield* auth.get("kilo").pipe(Effect.mapError(() => new HttpApiError.Unauthorized({})))
|
||||
const token = getToken(info)
|
||||
if (!token) return yield* Effect.fail(new HttpApiError.Unauthorized({}))
|
||||
|
||||
const expires = info?.type === "oauth" ? info.expires : Date.now() + 365 * 24 * 60 * 60 * 1000
|
||||
return {
|
||||
token,
|
||||
expiresAt: new Date(expires).toISOString(),
|
||||
kiloChatUrl: KILO_CHAT_URL,
|
||||
eventServiceUrl: KILO_EVENT_SERVICE_URL,
|
||||
}
|
||||
})
|
||||
|
||||
return handlers
|
||||
.handle("profile", profile)
|
||||
.handle("notifications", notifications)
|
||||
.handle("organization", organization)
|
||||
.handle("clawStatus", clawStatus)
|
||||
.handle("clawChatCredentials", clawChatCredentials)
|
||||
}),
|
||||
)
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Context } from "effect"
|
||||
import type { Hono } from "hono"
|
||||
import { IndexingPaths } from "./groups/indexing"
|
||||
import { KiloGatewayPaths } from "./groups/kilo-gateway"
|
||||
import { KilocodePaths } from "./groups/kilocode"
|
||||
import { NetworkPaths } from "./groups/network"
|
||||
import { RemotePaths } from "./groups/remote"
|
||||
@@ -33,6 +34,11 @@ export function register(app: Hono, handler: Handler, context: Context.Context<u
|
||||
app.post(SessionImportPaths.message, (c) => handler(c.req.raw, context))
|
||||
app.post(SessionImportPaths.part, (c) => handler(c.req.raw, context))
|
||||
app.get(IndexingPaths.status, (c) => handler(c.req.raw, context))
|
||||
app.get(KiloGatewayPaths.profile, (c) => handler(c.req.raw, context))
|
||||
app.get(KiloGatewayPaths.notifications, (c) => handler(c.req.raw, context))
|
||||
app.post(KiloGatewayPaths.organization, (c) => handler(c.req.raw, context))
|
||||
app.get(KiloGatewayPaths.clawStatus, (c) => handler(c.req.raw, context))
|
||||
app.get(KiloGatewayPaths.clawChatCredentials, (c) => handler(c.req.raw, context))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Layer } from "effect"
|
||||
import { commitMessageHandlers } from "./handlers/commit-message"
|
||||
import { enhancePromptHandlers } from "./handlers/enhance-prompt"
|
||||
import { indexingHandlers } from "./handlers/indexing"
|
||||
import { kiloGatewayHandlers } from "./handlers/kilo-gateway"
|
||||
import { kilocodeHandlers } from "./handlers/kilocode"
|
||||
import { networkHandlers } from "./handlers/network"
|
||||
import { remoteHandlers } from "./handlers/remote"
|
||||
@@ -14,6 +15,7 @@ export const provide = Layer.provide([
|
||||
commitMessageHandlers,
|
||||
enhancePromptHandlers,
|
||||
indexingHandlers,
|
||||
kiloGatewayHandlers,
|
||||
kilocodeHandlers,
|
||||
networkHandlers,
|
||||
remoteHandlers,
|
||||
|
||||
@@ -24,6 +24,7 @@ import { V2Api } from "./groups/v2"
|
||||
import { CommitMessageApi } from "@/kilocode/server/httpapi/groups/commit-message"
|
||||
import { EnhancePromptApi } from "@/kilocode/server/httpapi/groups/enhance-prompt"
|
||||
import { IndexingApi } from "@/kilocode/server/httpapi/groups/indexing"
|
||||
import { KiloGatewayApi } from "@/kilocode/server/httpapi/groups/kilo-gateway"
|
||||
import { KilocodeApi } from "@/kilocode/server/httpapi/groups/kilocode"
|
||||
import { NetworkApi } from "@/kilocode/server/httpapi/groups/network"
|
||||
import { RemoteApi } from "@/kilocode/server/httpapi/groups/remote"
|
||||
@@ -59,6 +60,7 @@ export const InstanceHttpApi = HttpApi.make("opencode-instance")
|
||||
.addHttpApi(CommitMessageApi)
|
||||
.addHttpApi(EnhancePromptApi)
|
||||
.addHttpApi(IndexingApi)
|
||||
.addHttpApi(KiloGatewayApi)
|
||||
.addHttpApi(KilocodeApi)
|
||||
.addHttpApi(NetworkApi)
|
||||
.addHttpApi(RemoteApi)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Config } from "@/config/config"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import { Schema } from "effect" // kilocode_change
|
||||
import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
|
||||
import { Authorization } from "../middleware/authorization"
|
||||
import { InstanceContextMiddleware } from "../middleware/instance-context"
|
||||
@@ -8,6 +9,14 @@ import { described } from "./metadata"
|
||||
|
||||
const root = "/config"
|
||||
|
||||
// kilocode_change start
|
||||
const Warning = Schema.Struct({
|
||||
path: Schema.String,
|
||||
message: Schema.String,
|
||||
detail: Schema.optional(Schema.String),
|
||||
})
|
||||
// kilocode_change end
|
||||
|
||||
export const ConfigApi = HttpApi.make("config")
|
||||
.add(
|
||||
HttpApiGroup.make("config")
|
||||
@@ -32,6 +41,17 @@ export const ConfigApi = HttpApi.make("config")
|
||||
description: "Update OpenCode configuration settings and preferences.",
|
||||
}),
|
||||
),
|
||||
// kilocode_change start
|
||||
HttpApiEndpoint.get("warnings", `${root}/warnings`, {
|
||||
success: described(Schema.Array(Warning), "Config warnings"),
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "config.warnings",
|
||||
summary: "Get config warnings",
|
||||
description: "Get warnings generated during config loading (e.g., invalid JSON, schema errors).",
|
||||
}),
|
||||
),
|
||||
// kilocode_change end
|
||||
HttpApiEndpoint.get("providers", `${root}/providers`, {
|
||||
success: described(Provider.ConfigProvidersResult, "List of providers"),
|
||||
}).annotateMerge(
|
||||
|
||||
@@ -21,6 +21,12 @@ export const configHandlers = HttpApiBuilder.group(InstanceHttpApi, "config", (h
|
||||
return ctx.payload
|
||||
})
|
||||
|
||||
// kilocode_change start
|
||||
const warnings = Effect.fn("ConfigHttpApi.warnings")(function* () {
|
||||
return yield* configSvc.warnings()
|
||||
})
|
||||
// kilocode_change end
|
||||
|
||||
const providers = Effect.fn("ConfigHttpApi.providers")(function* () {
|
||||
const providers = yield* providerSvc.list()
|
||||
return {
|
||||
@@ -29,6 +35,10 @@ export const configHandlers = HttpApiBuilder.group(InstanceHttpApi, "config", (h
|
||||
}
|
||||
})
|
||||
|
||||
return handlers.handle("get", get).handle("update", update).handle("providers", providers)
|
||||
return handlers
|
||||
.handle("get", get)
|
||||
.handle("update", update)
|
||||
.handle("warnings", warnings)
|
||||
.handle("providers", providers) // kilocode_change
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -59,6 +59,7 @@ export const InstanceRoutes = (upgrade: UpgradeWebSocket, opts?: CorsOptions): H
|
||||
app.post("/permission/:requestID/reply", (c) => handler(c.req.raw, context))
|
||||
app.get("/config", (c) => handler(c.req.raw, context))
|
||||
app.patch("/config", (c) => handler(c.req.raw, context))
|
||||
app.get("/config/warnings", (c) => handler(c.req.raw, context)) // kilocode_change
|
||||
app.get("/config/providers", (c) => handler(c.req.raw, context))
|
||||
app.get(ExperimentalPaths.console, (c) => handler(c.req.raw, context))
|
||||
app.get(ExperimentalPaths.consoleOrgs, (c) => handler(c.req.raw, context))
|
||||
|
||||
@@ -21,6 +21,7 @@ import type {
|
||||
ConfigProvidersResponses,
|
||||
ConfigUpdateErrors,
|
||||
ConfigUpdateResponses,
|
||||
ConfigWarningsResponses,
|
||||
EnhancePromptEnhanceErrors,
|
||||
EnhancePromptEnhanceResponses,
|
||||
EventSubscribeResponses,
|
||||
@@ -61,6 +62,9 @@ import type {
|
||||
GlobalUpgradeResponses,
|
||||
IndexingStatusResponses,
|
||||
InstanceDisposeResponses,
|
||||
KiloClawChatCredentialsResponses,
|
||||
KiloClawStatusErrors,
|
||||
KiloClawStatusResponses,
|
||||
KilocodeHeapSnapshotErrors,
|
||||
KilocodeHeapSnapshotResponses,
|
||||
KilocodeRemoveAgentErrors,
|
||||
@@ -75,6 +79,12 @@ import type {
|
||||
KilocodeSessionImportProjectResponses,
|
||||
KilocodeSessionImportSessionErrors,
|
||||
KilocodeSessionImportSessionResponses,
|
||||
KiloNotificationsErrors,
|
||||
KiloNotificationsResponses,
|
||||
KiloOrganizationSetErrors,
|
||||
KiloOrganizationSetResponses,
|
||||
KiloProfileErrors,
|
||||
KiloProfileResponses,
|
||||
LspStatusResponses,
|
||||
McpAddErrors,
|
||||
McpAddResponses,
|
||||
@@ -665,6 +675,36 @@ export class Config2 extends HeyApiClient {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get config warnings
|
||||
*
|
||||
* Get warnings generated during config loading (e.g., invalid JSON, schema errors).
|
||||
*/
|
||||
public warnings<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "workspace" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<ConfigWarningsResponses, unknown, ThrowOnError>({
|
||||
url: "/config/warnings",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* List config providers
|
||||
*
|
||||
@@ -4884,6 +4924,181 @@ export class Indexing extends HeyApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
export class Organization extends HeyApiClient {
|
||||
/**
|
||||
* Update Kilo Gateway organization
|
||||
*
|
||||
* Switch to a different Kilo Gateway organization
|
||||
*/
|
||||
public set<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
organizationId?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "workspace" },
|
||||
{ in: "body", key: "organizationId" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).post<KiloOrganizationSetResponses, KiloOrganizationSetErrors, ThrowOnError>(
|
||||
{
|
||||
url: "/kilo/organization",
|
||||
...options,
|
||||
...params,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...options?.headers,
|
||||
...params.headers,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class Claw extends HeyApiClient {
|
||||
/**
|
||||
* Get KiloClaw instance status
|
||||
*
|
||||
* Fetch the user's KiloClaw instance status via the KiloClaw worker
|
||||
*/
|
||||
public status<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "workspace" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<KiloClawStatusResponses, KiloClawStatusErrors, ThrowOnError>({
|
||||
url: "/kilo/claw/status",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get KiloClaw chat credentials
|
||||
*
|
||||
* Returns the bearer token and endpoint URLs the client uses to talk to the Kilo Chat worker and the Event Service. The bearer is the user's existing long-lived Kilo JWT — kilo-chat and event-service both verify it directly with NEXTAUTH_SECRET, so no separate token mint is needed.
|
||||
*/
|
||||
public chatCredentials<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "workspace" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<KiloClawChatCredentialsResponses, unknown, ThrowOnError>({
|
||||
url: "/kilo/claw/chat-credentials",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export class Kilo extends HeyApiClient {
|
||||
/**
|
||||
* Get Kilo Gateway profile
|
||||
*
|
||||
* Fetch user profile and organizations from Kilo Gateway
|
||||
*/
|
||||
public profile<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "workspace" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<KiloProfileResponses, KiloProfileErrors, ThrowOnError>({
|
||||
url: "/kilo/profile",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Kilo notifications
|
||||
*
|
||||
* Fetch notifications from Kilo Gateway for CLI display
|
||||
*/
|
||||
public notifications<ThrowOnError extends boolean = false>(
|
||||
parameters?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
},
|
||||
options?: Options<never, ThrowOnError>,
|
||||
) {
|
||||
const params = buildClientParams(
|
||||
[parameters],
|
||||
[
|
||||
{
|
||||
args: [
|
||||
{ in: "query", key: "directory" },
|
||||
{ in: "query", key: "workspace" },
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
return (options?.client ?? this.client).get<KiloNotificationsResponses, KiloNotificationsErrors, ThrowOnError>({
|
||||
url: "/kilo/notifications",
|
||||
...options,
|
||||
...params,
|
||||
})
|
||||
}
|
||||
|
||||
private _organization?: Organization
|
||||
get organization(): Organization {
|
||||
return (this._organization ??= new Organization({ client: this.client }))
|
||||
}
|
||||
|
||||
private _claw?: Claw
|
||||
get claw(): Claw {
|
||||
return (this._claw ??= new Claw({ client: this.client }))
|
||||
}
|
||||
}
|
||||
|
||||
export class Heap extends HeyApiClient {
|
||||
/**
|
||||
* Write heap snapshot
|
||||
@@ -5925,6 +6140,11 @@ export class KiloClient extends HeyApiClient {
|
||||
return (this._indexing ??= new Indexing({ client: this.client }))
|
||||
}
|
||||
|
||||
private _kilo?: Kilo
|
||||
get kilo(): Kilo {
|
||||
return (this._kilo ??= new Kilo({ client: this.client }))
|
||||
}
|
||||
|
||||
private _kilocode?: Kilocode
|
||||
get kilocode(): Kilocode {
|
||||
return (this._kilocode ??= new Kilocode({ client: this.client }))
|
||||
|
||||
@@ -1856,6 +1856,10 @@ export type EffectHttpApiErrorUnauthorized = {
|
||||
_tag: "Unauthorized"
|
||||
}
|
||||
|
||||
export type EffectHttpApiErrorInternalServerError = {
|
||||
_tag: "InternalServerError"
|
||||
}
|
||||
|
||||
export type KilocodeSessionImportResult = {
|
||||
ok: boolean
|
||||
id: string
|
||||
@@ -3794,6 +3798,29 @@ export type ConfigUpdateResponses = {
|
||||
|
||||
export type ConfigUpdateResponse = ConfigUpdateResponses[keyof ConfigUpdateResponses]
|
||||
|
||||
export type ConfigWarningsData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
url: "/config/warnings"
|
||||
}
|
||||
|
||||
export type ConfigWarningsResponses = {
|
||||
/**
|
||||
* Config warnings
|
||||
*/
|
||||
200: Array<{
|
||||
path: string
|
||||
message: string
|
||||
detail?: string
|
||||
}>
|
||||
}
|
||||
|
||||
export type ConfigWarningsResponse = ConfigWarningsResponses[keyof ConfigWarningsResponses]
|
||||
|
||||
export type ConfigProvidersData = {
|
||||
body?: never
|
||||
path?: never
|
||||
@@ -7103,6 +7130,191 @@ export type IndexingStatusResponses = {
|
||||
|
||||
export type IndexingStatusResponse = IndexingStatusResponses[keyof IndexingStatusResponses]
|
||||
|
||||
export type KiloProfileData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
url: "/kilo/profile"
|
||||
}
|
||||
|
||||
export type KiloProfileErrors = {
|
||||
/**
|
||||
* Bad request
|
||||
*/
|
||||
400: BadRequestError
|
||||
}
|
||||
|
||||
export type KiloProfileError = KiloProfileErrors[keyof KiloProfileErrors]
|
||||
|
||||
export type KiloProfileResponses = {
|
||||
/**
|
||||
* Profile data
|
||||
*/
|
||||
200: {
|
||||
profile: {
|
||||
email: string
|
||||
name?: string
|
||||
organizations?: Array<{
|
||||
id: string
|
||||
name: string
|
||||
role: string
|
||||
}>
|
||||
}
|
||||
balance: {
|
||||
balance: number
|
||||
}
|
||||
currentOrgId: string
|
||||
}
|
||||
}
|
||||
|
||||
export type KiloProfileResponse = KiloProfileResponses[keyof KiloProfileResponses]
|
||||
|
||||
export type KiloNotificationsData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
url: "/kilo/notifications"
|
||||
}
|
||||
|
||||
export type KiloNotificationsErrors = {
|
||||
/**
|
||||
* Bad request
|
||||
*/
|
||||
400: BadRequestError
|
||||
}
|
||||
|
||||
export type KiloNotificationsError = KiloNotificationsErrors[keyof KiloNotificationsErrors]
|
||||
|
||||
export type KiloNotificationsResponses = {
|
||||
/**
|
||||
* Notifications list
|
||||
*/
|
||||
200: Array<{
|
||||
id: string
|
||||
title: string
|
||||
message: string
|
||||
action?: {
|
||||
actionText: string
|
||||
actionURL: string
|
||||
}
|
||||
showIn?: Array<string>
|
||||
suggestModelId?: string
|
||||
}>
|
||||
}
|
||||
|
||||
export type KiloNotificationsResponse = KiloNotificationsResponses[keyof KiloNotificationsResponses]
|
||||
|
||||
export type KiloOrganizationSetData = {
|
||||
body?: {
|
||||
organizationId: string
|
||||
}
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
url: "/kilo/organization"
|
||||
}
|
||||
|
||||
export type KiloOrganizationSetErrors = {
|
||||
/**
|
||||
* Bad request
|
||||
*/
|
||||
400: BadRequestError
|
||||
}
|
||||
|
||||
export type KiloOrganizationSetError = KiloOrganizationSetErrors[keyof KiloOrganizationSetErrors]
|
||||
|
||||
export type KiloOrganizationSetResponses = {
|
||||
/**
|
||||
* Organization updated successfully
|
||||
*/
|
||||
200: boolean
|
||||
}
|
||||
|
||||
export type KiloOrganizationSetResponse = KiloOrganizationSetResponses[keyof KiloOrganizationSetResponses]
|
||||
|
||||
export type KiloClawStatusData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
url: "/kilo/claw/status"
|
||||
}
|
||||
|
||||
export type KiloClawStatusErrors = {
|
||||
/**
|
||||
* InternalServerError
|
||||
*/
|
||||
500: EffectHttpApiErrorInternalServerError
|
||||
}
|
||||
|
||||
export type KiloClawStatusError = KiloClawStatusErrors[keyof KiloClawStatusErrors]
|
||||
|
||||
export type KiloClawStatusResponses = {
|
||||
/**
|
||||
* Instance status
|
||||
*/
|
||||
200: {
|
||||
status:
|
||||
| "provisioned"
|
||||
| "starting"
|
||||
| "restarting"
|
||||
| "recovering"
|
||||
| "running"
|
||||
| "stopped"
|
||||
| "destroying"
|
||||
| "restoring"
|
||||
sandboxId?: string
|
||||
flyRegion?: string
|
||||
machineSize?: {
|
||||
cpus: number
|
||||
memory_mb: number
|
||||
}
|
||||
openclawVersion?: string
|
||||
lastStartedAt?: string
|
||||
lastStoppedAt?: string
|
||||
channelCount?: number
|
||||
secretCount?: number
|
||||
userId?: string
|
||||
botName?: string
|
||||
}
|
||||
}
|
||||
|
||||
export type KiloClawStatusResponse = KiloClawStatusResponses[keyof KiloClawStatusResponses]
|
||||
|
||||
export type KiloClawChatCredentialsData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
workspace?: string
|
||||
}
|
||||
url: "/kilo/claw/chat-credentials"
|
||||
}
|
||||
|
||||
export type KiloClawChatCredentialsResponses = {
|
||||
/**
|
||||
* Kilo Chat credentials or null
|
||||
*/
|
||||
200: {
|
||||
token: string
|
||||
expiresAt: string
|
||||
kiloChatUrl: string
|
||||
eventServiceUrl: string
|
||||
}
|
||||
}
|
||||
|
||||
export type KiloClawChatCredentialsResponse = KiloClawChatCredentialsResponses[keyof KiloClawChatCredentialsResponses]
|
||||
|
||||
export type KilocodeHeapSnapshotData = {
|
||||
body?: never
|
||||
path?: never
|
||||
|
||||
@@ -587,6 +587,67 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/config/warnings": {
|
||||
"get": {
|
||||
"tags": ["config"],
|
||||
"operationId": "config.warnings",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "directory",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "workspace",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Config warnings",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"detail": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["path", "message"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"description": "Config warnings"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Get warnings generated during config loading (e.g., invalid JSON, schema errors).",
|
||||
"summary": "Get config warnings",
|
||||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "js",
|
||||
"source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.config.warnings({\n ...\n})"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/config/providers": {
|
||||
"get": {
|
||||
"tags": ["config"],
|
||||
@@ -8802,6 +8863,446 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/kilo/profile": {
|
||||
"get": {
|
||||
"tags": ["kilo"],
|
||||
"operationId": "kilo.profile",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "directory",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "workspace",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Profile data",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"profile": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"organizations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"role": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["id", "name", "role"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["email"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"balance": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"balance": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": ["balance"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"currentOrgId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["profile", "balance", "currentOrgId"],
|
||||
"additionalProperties": false,
|
||||
"description": "Profile data"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/BadRequestError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Fetch user profile and organizations from Kilo Gateway",
|
||||
"summary": "Get Kilo Gateway profile",
|
||||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "js",
|
||||
"source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.kilo.profile({\n ...\n})"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/kilo/notifications": {
|
||||
"get": {
|
||||
"tags": ["kilo"],
|
||||
"operationId": "kilo.notifications",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "directory",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "workspace",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Notifications list",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"action": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"actionText": {
|
||||
"type": "string"
|
||||
},
|
||||
"actionURL": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["actionText", "actionURL"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"showIn": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"suggestModelId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["id", "title", "message"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"description": "Notifications list"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/BadRequestError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Fetch notifications from Kilo Gateway for CLI display",
|
||||
"summary": "Get Kilo notifications",
|
||||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "js",
|
||||
"source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.kilo.notifications({\n ...\n})"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/kilo/organization": {
|
||||
"post": {
|
||||
"tags": ["kilo"],
|
||||
"operationId": "kilo.organization.set",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "directory",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "workspace",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Organization updated successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"description": "Organization updated successfully"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad request",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/BadRequestError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Switch to a different Kilo Gateway organization",
|
||||
"summary": "Update Kilo Gateway organization",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"organizationId": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["organizationId"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "js",
|
||||
"source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.kilo.organization.set({\n ...\n})"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/kilo/claw/status": {
|
||||
"get": {
|
||||
"tags": ["kilo"],
|
||||
"operationId": "kilo.claw.status",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "directory",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "workspace",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Instance status",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"provisioned",
|
||||
"starting",
|
||||
"restarting",
|
||||
"recovering",
|
||||
"running",
|
||||
"stopped",
|
||||
"destroying",
|
||||
"restoring"
|
||||
]
|
||||
},
|
||||
"sandboxId": {
|
||||
"type": "string"
|
||||
},
|
||||
"flyRegion": {
|
||||
"type": "string"
|
||||
},
|
||||
"machineSize": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cpus": {
|
||||
"type": "number"
|
||||
},
|
||||
"memory_mb": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": ["cpus", "memory_mb"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"openclawVersion": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastStartedAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"lastStoppedAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"channelCount": {
|
||||
"type": "number"
|
||||
},
|
||||
"secretCount": {
|
||||
"type": "number"
|
||||
},
|
||||
"userId": {
|
||||
"type": "string"
|
||||
},
|
||||
"botName": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["status"],
|
||||
"additionalProperties": false,
|
||||
"description": "Instance status"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "InternalServerError",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/effect_HttpApiError_InternalServerError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Fetch the user's KiloClaw instance status via the KiloClaw worker",
|
||||
"summary": "Get KiloClaw instance status",
|
||||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "js",
|
||||
"source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.kilo.claw.status({\n ...\n})"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/kilo/claw/chat-credentials": {
|
||||
"get": {
|
||||
"tags": ["kilo"],
|
||||
"operationId": "kilo.claw.chatCredentials",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "directory",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "workspace",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Kilo Chat credentials or null",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"token": {
|
||||
"type": "string"
|
||||
},
|
||||
"expiresAt": {
|
||||
"type": "string"
|
||||
},
|
||||
"kiloChatUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"eventServiceUrl": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["token", "expiresAt", "kiloChatUrl", "eventServiceUrl"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Returns the bearer token and endpoint URLs the client uses to talk to the Kilo Chat worker and the Event Service. The bearer is the user's existing long-lived Kilo JWT — kilo-chat and event-service both verify it directly with NEXTAUTH_SECRET, so no separate token mint is needed.",
|
||||
"summary": "Get KiloClaw chat credentials",
|
||||
"x-codeSamples": [
|
||||
{
|
||||
"lang": "js",
|
||||
"source": "import { createKiloClient } from \"@kilocode/sdk\n\nconst client = createKiloClient()\nawait client.kilo.claw.chatCredentials({\n ...\n})"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/kilocode/heap/snapshot": {
|
||||
"post": {
|
||||
"tags": ["kilocode"],
|
||||
@@ -15890,6 +16391,17 @@
|
||||
"required": ["_tag"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"effect_HttpApiError_InternalServerError": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"_tag": {
|
||||
"type": "string",
|
||||
"enum": ["InternalServerError"]
|
||||
}
|
||||
},
|
||||
"required": ["_tag"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"KilocodeSessionImportResult": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -21051,6 +21563,10 @@
|
||||
"name": "indexing",
|
||||
"description": "Kilo indexing routes."
|
||||
},
|
||||
{
|
||||
"name": "kilo",
|
||||
"description": "Kilo Gateway routes."
|
||||
},
|
||||
{
|
||||
"name": "kilocode",
|
||||
"description": "Kilo-specific routes."
|
||||
|
||||
Reference in New Issue
Block a user