Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
486f66c
feat: add provider usage center
lambertjosh Jun 24, 2026
da661d4
refactor: trim auto top-up usage metadata
lambertjosh Jun 26, 2026
e175362
fix(cli): classify MiniMax status three as excluded
lambertjosh Jun 27, 2026
d45830f
Merge remote-tracking branch 'origin/main' into feat/provider-usage-c…
lambertjosh Jul 9, 2026
af017a5
refactor: reuse existing Kilo Pass usage
lambertjosh Jul 9, 2026
ce4bf2c
chore: update kilo-vscode visual regression baselines
kilo-maintainer[bot] Jul 9, 2026
4eab467
chore(vscode): format provider usage cards
lambertjosh Jul 9, 2026
4ec4010
Merge remote-tracking branch 'origin/feat/provider-usage-center' into…
lambertjosh Jul 9, 2026
9f06db3
fix(vscode): make profile content scrollable
lambertjosh Jul 9, 2026
fac8e8d
chore: update kilo-vscode visual regression baselines
kilo-maintainer[bot] Jul 9, 2026
0deccbb
fix(cli): hide unsupported MiniMax video quota
lambertjosh Jul 9, 2026
d138577
fix(vscode): prioritize provider usage in profile
lambertjosh Jul 9, 2026
07667bf
chore: update kilo-vscode visual regression baselines
kilo-maintainer[bot] Jul 9, 2026
9866c50
fix(vscode): prioritize Kilo Pass in plan usage
lambertjosh Jul 9, 2026
0d9386d
chore: update kilo-vscode visual regression baselines
kilo-maintainer[bot] Jul 9, 2026
fd54267
fix(vscode): order profile plans by source
lambertjosh Jul 9, 2026
27f5408
Merge remote-tracking branch 'origin/feat/provider-usage-center' into…
lambertjosh Jul 9, 2026
2433fb9
chore: update kilo-vscode visual regression baselines
kilo-maintainer[bot] Jul 9, 2026
1f26bea
fix(vscode): place personal top-ups under balance
lambertjosh Jul 9, 2026
b1693dd
Merge remote-tracking branch 'origin/feat/provider-usage-center' into…
lambertjosh Jul 9, 2026
980030c
chore: update kilo-vscode visual regression baselines
kilo-maintainer[bot] Jul 9, 2026
d4ee383
fix(cli): hide managed quota after key replacement
lambertjosh Jul 9, 2026
51d5551
Merge remote-tracking branch 'origin/feat/provider-usage-center' into…
lambertjosh Jul 9, 2026
e6707a9
fix(cli): hide managed quota without managed routing
lambertjosh Jul 10, 2026
57cf3c5
fix(vscode): label managed plans as Kilo Gateway
lambertjosh Jul 10, 2026
d2fbeb9
fix(vscode): show Kilo Pass paid and bonus credits
lambertjosh Jul 10, 2026
ad8ade2
chore: update kilo-vscode visual regression baselines
kilo-maintainer[bot] Jul 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/provider-usage-center.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@kilocode/cli": minor
"kilo-code": minor
---

View current provider plan usage, quota windows, and personal top-up status in the CLI and VS Code profile.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions packages/kilo-docs/source-links.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@
<!-- packages/opencode/src/plugin/digitalocean.ts -->
- <https://api.kilo.ai>
<!-- packages/opencode/src/cli/cmd/github.ts -->
- <https://api.minimax.io/v1/token_plan/remains>
<!-- packages/opencode/src/kilocode/provider/minimax/usage.ts -->
- <https://api.minimaxi.com/v1/token_plan/remains>
<!-- packages/opencode/src/kilocode/provider/minimax/usage.ts -->
- <https://api.x.ai/v1>
<!-- packages/opencode/src/plugin/xai.ts -->
- <https://app.kilo.ai>
<!-- packages/opencode/src/kilocode/kilo-commands.tsx -->
<!-- packages/opencode/src/kilocode/provider-usage/cloud.ts -->
- <https://app.kilo.ai/claw>
<!-- packages/kilo-vscode/webview-ui/kiloclaw/components/SetupView.tsx -->
<!-- packages/kilo-vscode/webview-ui/kiloclaw/components/UpgradeView.tsx -->
Expand Down Expand Up @@ -166,6 +171,10 @@
<!-- packages/opencode/src/kilocode/session/index.ts -->
- <https://opncd.ai>
<!-- packages/opencode/src/share/share-next.ts -->
- <https://platform.minimax.io/subscribe/token-plan>
<!-- packages/opencode/src/kilocode/provider/minimax/usage.ts -->
- <https://platform.minimaxi.com/subscribe/token-plan>
<!-- packages/opencode/src/kilocode/provider/minimax/usage.ts -->
- <https://reddit.com/r/kilocode>
<!-- packages/kilo-vscode/webview-ui/src/components/settings/AboutKiloCodeTab.tsx -->
- <https://search.parallel.ai/mcp>
Expand Down
196 changes: 196 additions & 0 deletions packages/kilo-gateway/src/api/trpc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import { z } from "zod"
import { buildKiloHeaders } from "../headers.js"
import { KILO_API_BASE } from "./constants.js"

const timeout = 5_000
const limit = 512 * 1024

const AutoTopUpStateSchema = z.object({
enabled: z.boolean(),
amountCents: z.number().int().nonnegative(),
thresholdCents: z.number().int().nonnegative(),
paymentMethod: z
.object({
type: z.string(),
brand: z.string().nullable(),
last4: z.string().nullable(),
})
.nullable(),
})

const CodingPlanSubscriptionSchema = z.object({
id: z.string(),
planId: z.string(),
planName: z.string(),
providerName: z.string(),
providerId: z.string(),
routeLabel: z.string(),
hasInstalledByokKey: z.boolean(),
status: z.enum(["active", "past_due", "canceled"]),
billingPeriodDays: z.number().int().positive(),
currentPeriodStart: z.string(),
currentPeriodEnd: z.string(),
creditRenewalAt: z.string(),
cancelAtPeriodEnd: z.boolean(),
paymentGraceExpiresAt: z.string().nullable(),
canceledAt: z.string().nullable(),
cancellationReason: z.string().nullable(),
createdAt: z.string(),
costKiloCredits: z.number().nonnegative(),
})

const ByokEntrySchema = z.object({
id: z.string(),
provider_id: z.string(),
management_source: z.enum(["user", "coding_plan"]),
is_enabled: z.boolean(),
})

const NativeNumberSchema = z.number().finite()
const NativeIntegerSchema = z.number().int().safe()
const MiniMaxModelRemainsSchema = z.object({
model_name: z.string(),
current_interval_total_count: NativeIntegerSchema.nonnegative().optional(),
current_interval_usage_count: NativeIntegerSchema.nonnegative().optional(),
start_time: NativeIntegerSchema.nonnegative().optional(),
end_time: NativeIntegerSchema.nonnegative().optional(),
remains_time: NativeIntegerSchema.nonnegative().optional(),
interval_boost_permill: NativeIntegerSchema.nonnegative().optional(),
interval_boost_permille: NativeIntegerSchema.nonnegative().optional(),
current_interval_remaining_percent: NativeNumberSchema.optional(),
current_interval_status: NativeIntegerSchema.optional(),
current_weekly_total_count: NativeIntegerSchema.nonnegative().optional(),
current_weekly_usage_count: NativeIntegerSchema.nonnegative().optional(),
weekly_start_time: NativeIntegerSchema.nonnegative().optional(),
weekly_end_time: NativeIntegerSchema.nonnegative().optional(),
weekly_remains_time: NativeIntegerSchema.nonnegative().optional(),
weekly_boost_permill: NativeIntegerSchema.nonnegative().optional(),
weekly_boost_permille: NativeIntegerSchema.nonnegative().optional(),
current_weekly_remaining_percent: NativeNumberSchema.optional(),
current_weekly_status: NativeIntegerSchema.optional(),
})

export const MiniMaxNativeUsageSchema = z.object({
base_resp: z.object({ status_code: NativeIntegerSchema }),
model_remains: z.array(MiniMaxModelRemainsSchema),
})

const CodingPlanUsageSchema = z.object({
subscriptionId: z.string(),
providerId: z.literal("minimax"),
region: z.literal("global"),
fetchedAt: z.string(),
native: MiniMaxNativeUsageSchema,
})

const envelope = z.object({
result: z.object({ data: z.unknown() }).optional(),
error: z.unknown().optional(),
})

export type AutoTopUpState = z.infer<typeof AutoTopUpStateSchema>
export type CodingPlanSubscription = z.infer<typeof CodingPlanSubscriptionSchema>
export type ByokEntry = z.infer<typeof ByokEntrySchema>
export type CodingPlanUsage = z.infer<typeof CodingPlanUsageSchema>
export type MiniMaxNativeUsage = z.infer<typeof MiniMaxNativeUsageSchema>

async function read(response: Response) {
const declared = Number(response.headers.get("content-length"))
if (Number.isFinite(declared) && declared > limit) {
response.body?.cancel().catch(() => undefined)
throw new CloudTrpcError("protocol", response.status)
}
if (!response.body) {
const body = await response.arrayBuffer()
if (body.byteLength > limit) throw new CloudTrpcError("protocol", response.status)
return new TextDecoder().decode(body)
}

const reader = response.body.getReader()
const chunks: Uint8Array[] = []
let size = 0
while (true) {
const chunk = await reader.read()
if (chunk.done) break
if (!chunk.value) continue
size += chunk.value.byteLength
if (size > limit) {
await reader.cancel().catch(() => undefined)
throw new CloudTrpcError("protocol", response.status)
}
chunks.push(chunk.value)
}
const body = new Uint8Array(size)
let offset = 0
for (const chunk of chunks) {
body.set(chunk, offset)
offset += chunk.byteLength
}
return new TextDecoder().decode(body)
}

export class CloudTrpcError extends Error {
constructor(
readonly kind: "network" | "http" | "protocol" | "procedure" | "schema",
readonly status?: number,
) {
super("Kilo Cloud data is temporarily unavailable.")
this.name = "CloudTrpcError"
}
}

async function query<T>(procedure: string, token: string, schema: z.ZodType<T>, input?: unknown): Promise<T> {
const params = new URLSearchParams()
if (input !== undefined) params.set("input", JSON.stringify(input))
const suffix = params.size ? `?${params.toString()}` : ""
const response = await fetch(`${KILO_API_BASE}/api/trpc/${procedure}${suffix}`, {
method: "GET",
headers: {
Accept: "application/json",
Authorization: `Bearer ${token}`,
...buildKiloHeaders(),
},
redirect: "error",
signal: AbortSignal.timeout(timeout),
}).catch(() => {
throw new CloudTrpcError("network")
})

const body = await read(response).catch((error) => {
if (error instanceof CloudTrpcError) throw error
throw new CloudTrpcError("protocol", response.status)
})

const parsed = (() => {
try {
return envelope.parse(JSON.parse(body))
} catch {
throw new CloudTrpcError("protocol", response.status)
}
})()
if (parsed.error !== undefined) throw new CloudTrpcError("procedure", response.status)
if (!response.ok) throw new CloudTrpcError("http", response.status)
if (!parsed.result) throw new CloudTrpcError("protocol", response.status)

const data = parsed.result.data
const value = typeof data === "object" && data !== null && "json" in data ? (data as { json: unknown }).json : data
const result = schema.safeParse(value)
if (!result.success) throw new CloudTrpcError("schema", response.status)
return result.data
}

export function getAutoTopUpState(token: string) {
return query("user.getAutoTopUpPaymentMethod", token, AutoTopUpStateSchema)
}

export function listCodingPlanSubscriptions(token: string) {
return query("codingPlans.listSubscriptions", token, z.array(CodingPlanSubscriptionSchema))
}

export function listByokEntries(token: string) {
return query("byok.list", token, z.array(ByokEntrySchema), {})
}

export function getCodingPlanUsage(token: string, subscriptionId: string) {
return query("codingPlans.getUsage", token, CodingPlanUsageSchema, { subscriptionId })
}
13 changes: 13 additions & 0 deletions packages/kilo-gateway/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,19 @@ export {
type OrganizationModeConfig,
} from "./api/modes.js"
export { fetchKilocodeNotifications, type KilocodeNotification } from "./api/notifications.js"
export {
CloudTrpcError,
MiniMaxNativeUsageSchema,
getAutoTopUpState,
getCodingPlanUsage,
listByokEntries,
listCodingPlanSubscriptions,
type AutoTopUpState,
type ByokEntry,
type CodingPlanSubscription,
type CodingPlanUsage,
type MiniMaxNativeUsage,
} from "./api/trpc.js"
export { fetchCloudSession, fetchCloudSessionForImport, importSessionToDb } from "./cloud-sessions.js"

// ============================================================================
Expand Down
Loading
Loading