Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions docs-site/src/content/docs/fr/guides/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,14 @@ CLI Command Code. Le catalogue, propre au compte, provient du point de terminais
après la connexion. Les requêtes de chat utilisent la clé Bearer configurée. Créez des clés dans
[Command Code Studio](https://commandcode.ai/studio/).

**Quota Command Code.** Le tableau de bord et `ocx account refresh` sondent les fenêtres
`/alpha/billing/credits` de Command Code (5 heures et hebdomadaire) sur l'hôte canonique
`https://api.commandcode.ai`. Le préréglage OAuth (`command-code`) utilise le jeton porteur du compte
enregistré ; le préréglage à clé d'API fournisseur (`commandcode`) utilise la clé active configurée. Une
URL de base modifiée ressemblant à l'original n'est jamais sondée. Les crédits mensuels, achetés et
gratuits restants sont affichés sous forme de fenêtre en USD lorsque Command Code signale également les
dépenses de la période.

**Découverte SambaNova Cloud.** Le préréglage lit la liste publique `/v1/models` de SambaNova Cloud depuis
l'hôte API fixe, préserve les identifiants natifs du fournisseur et limite la découverte à 128 KiB et 128
lignes brutes. Le catalogue n'étant pas authentifié, le parcours de connexion de la CLI signale que la clé ne
Expand Down
7 changes: 7 additions & 0 deletions docs-site/src/content/docs/guides/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,13 @@ account-scoped and comes from the authenticated discovery endpoint after login.
configured Bearer key. Create keys at
[Command Code Studio](https://commandcode.ai/studio/).

**Command Code quota.** The dashboard and `ocx account refresh` probe Command Code's
`/alpha/billing/credits` windows (5-hour and weekly) on the canonical
`https://api.commandcode.ai` host. The OAuth preset (`command-code`) uses the stored
account bearer; the Provider-API key preset (`commandcode`) uses the active configured
key. A user-edited lookalike base URL is never probed. Remaining monthly, purchased, and
free credits are shown as a USD window when Command Code also reports period spend.

**SambaNova Cloud discovery.** The preset reads SambaNova Cloud's public `/v1/models` list from the fixed API
host, preserves provider-native ids, and caps discovery at 128 KiB and 128 raw rows. Because the
catalog is unauthenticated, the CLI login flow reports the key as unverifiable instead of treating
Expand Down
2 changes: 2 additions & 0 deletions docs-site/src/content/docs/ja/guides/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,8 @@ CLI 資格情報の取り込みも可能)。モデルカタログはアカウン
discovery エンドポイントから取得します。チャットリクエストは設定済みの bearer キーを使います。
キーは [Command Code Studio](https://commandcode.ai/studio/) で作成します。

**Command Code の quota:** ダッシュボードと `ocx account refresh` は、正規ホスト `https://api.commandcode.ai` 上の `/alpha/billing/credits` ウィンドウ(5時間と週次)を照会します。OAuth プリセット (`command-code`) は保存済みアカウント bearer を使い、Provider-API キープリセット (`commandcode`) は設定済みの有効キーを使います。ユーザーが編集した類似ホストは照会しません。期間支出が返る場合は、残りの monthly / purchased / free credits を USD ウィンドウとして表示します。

**SambaNova Cloud の discovery:** preset は固定 API ホスト上の SambaNova Cloud の公開 `/v1/models` 一覧を読み、
プロバイダー固有の ID を保持し、discovery を 128 KiB と raw 128 行に制限します。カタログは認証不要のため、
CLI の login flow は公開レスポンスをキーの有効性の証拠にせず、キーを検証不能として報告します。chat リクエストは
Expand Down
2 changes: 2 additions & 0 deletions docs-site/src/content/docs/ko/guides/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,8 @@ CLI 사용자는 `~/.commandcode/auth.json`의 로컬 CLI 자격 증명을 가
계정 단위이며 로그인 후 인증된 discovery 엔드포인트에서 가져옵니다. 채팅 요청은 설정된 bearer
키를 사용합니다. 키는 [Command Code Studio](https://commandcode.ai/studio/)에서 생성합니다.

**Command Code 할당량:** 대시보드와 `ocx account refresh`는 정규 호스트 `https://api.commandcode.ai`에서 `/alpha/billing/credits` 창(5시간 및 주간)을 조회합니다. OAuth 프리셋(`command-code`)은 저장된 계정 bearer를 사용하고, Provider-API 키 프리셋(`commandcode`)은 현재 설정된 활성 키를 사용합니다. 사용자가 바꾼 유사 base URL은 조회하지 않습니다. Command Code가 기간 사용량을 함께 반환하면 남은 monthly / purchased / free credits가 USD 창으로 표시됩니다.

**SambaNova Cloud 검색:** 프리셋은 고정 API 호스트의 SambaNova Cloud 공개 `/v1/models` 목록을 읽고, 프로바이더
네이티브 ID를 보존하며 discovery를 128 KiB와 raw 행 128개로 제한합니다. 카탈로그에는 인증이 필요하지 않으므로
CLI 로그인 흐름은 공개 응답을 키 유효성의 증거로 사용하지 않고 키를 검증할 수 없는 것으로 보고합니다. chat 요청은
Expand Down
2 changes: 2 additions & 0 deletions docs-site/src/content/docs/ru/guides/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ Service token Nscale создаётся в [Nscale Console](https://console.nsca
аутентифицированного discovery endpoint после входа. Запросы чата используют настроенный bearer-ключ.
Ключи создаются в [Command Code Studio](https://commandcode.ai/studio/).

**Квота Command Code.** Дашборд и `ocx account refresh` опрашивают окна `/alpha/billing/credits` (5 часов и неделя) на каноническом хосте `https://api.commandcode.ai`. OAuth-пресет (`command-code`) использует сохранённый bearer аккаунта; пресет Provider-API ключа (`commandcode`) — активный настроенный ключ. Пользовательски изменённый похожий base URL не опрашивается. Если Command Code также сообщает расход за период, оставшиеся monthly / purchased / free credits показываются как USD-окно.

**Discovery для SambaNova Cloud.** Пресет читает общедоступный список SambaNova Cloud `/v1/models` на
фиксированном API-хосте, сохраняет нативные id провайдера и ограничивает discovery размером 128 KiB
и 128 исходными строками. Каталог не требует аутентификации, поэтому процедура входа CLI сообщает, что
Expand Down
3 changes: 2 additions & 1 deletion docs-site/src/content/docs/tr/guides/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,8 @@ doğrulamalı keşif uç noktasından gelir. Sohbet istekleri yapılandırılmı
anahtarını kullanır. [Command Code Studio](https://commandcode.ai/studio/)
üzerinden anahtarlar oluşturun.

**Command Code kotası.** Pano ve `ocx account refresh`, kanonik `https://api.commandcode.ai` ana bilgisayarında `/alpha/billing/credits` pencerelerini (5 saat ve haftalık) sorgular. OAuth önayarı (`command-code`) kayıtlı hesap bearer'ını kullanır; Provider-API anahtar önayarı (`commandcode`) etkin yapılandırılmış anahtarı kullanır. Kullanıcının değiştirdiği benzer bir temel URL asla sorgulanmaz. Command Code dönem harcamasını da bildirirse kalan monthly / purchased / free credits USD penceresi olarak gösterilir.

**SambaNova Cloud keşfi.** Önayar, sabit API ana bilgisayarından SambaNova
Cloud'un genel `/v1/models` listesini okur, sağlayıcı yerel kimliklerini korur
ve keşfi 128 KiB ve 128 ham satırla sınırlar. Katalog kimlik doğrulamasız
Expand Down Expand Up @@ -662,4 +664,3 @@ Canlı araştırmaya sahip sağlayıcılar: OpenAI/Codex, Anthropic, xAI, Cursor
Kimi, Google Antigravity, OpenRouter, DeepSeek, ClinePass, Z.AI, MiniMax,
Moonshot, Venice, Synthetic, DeepInfra, Neuralwatt ve a6api destekli herhangi
bir özel sağlayıcı.

2 changes: 2 additions & 0 deletions docs-site/src/content/docs/zh-cn/guides/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ inference key 可从 [Vultr Console](https://my.vultr.com) 的订阅概览复制
`~/.commandcode/auth.json` 导入本地 CLI 凭据);模型目录按账户隔离,并在登录后从经过认证的发现
端点获取。聊天请求使用已配置的 bearer 密钥。密钥可在 [Command Code Studio](https://commandcode.ai/studio/) 创建。

**Command Code 配额:**仪表盘和 `ocx account refresh` 会在规范主机 `https://api.commandcode.ai` 上探测 `/alpha/billing/credits` 窗口(5 小时和每周)。OAuth 预设 (`command-code`) 使用已保存的账户 bearer;Provider-API 密钥预设 (`commandcode`) 使用当前配置的有效密钥。用户改写后的仿冒 base URL 不会被探测。当 Command Code 同时返回周期消耗时,剩余的 monthly / purchased / free credits 会显示为 USD 窗口。

**SambaNova Cloud 发现:**该预设从固定 API 主机读取 SambaNova Cloud 的公开 `/v1/models` 列表,保留提供商原生
模型 id,并将发现限制为 128 KiB 和 128 条原始记录。该目录无需鉴权,因此 CLI 登录流程不会把公开响应
当作密钥有效性的证明,而会将密钥报告为无法验证。chat 请求仍使用已配置的 Bearer 密钥;由于 SambaNova
Expand Down
2 changes: 2 additions & 0 deletions docs-site/src/content/docs/zh-tw/guides/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,8 @@ Hyperbolic 另外的 image、audio 與 GPU endpoint 不在範圍內。可在
endpoint 取得。Chat request 使用設定的 Bearer key。可在
[Command Code Studio](https://commandcode.ai/studio/) 建立 key。

**Command Code 配額。** 儀表板與 `ocx account refresh` 會在正規主機 `https://api.commandcode.ai` 探測 `/alpha/billing/credits` 視窗(5 小時與每週)。OAuth preset (`command-code`) 使用已儲存的帳號 bearer;Provider-API key preset (`commandcode`) 使用目前設定的有效 key。使用者改寫過的仿冒 base URL 不會被探測。當 Command Code 同時回報週期消耗時,剩餘的 monthly / purchased / free credits 會顯示為 USD 視窗。

**SambaNova Cloud 探索。** preset 從固定 API host 讀取 SambaNova Cloud 公開的 `/v1/models` 列表,保留
provider-native id,並把 discovery 限制在 128 KiB/128 個 raw row。因 catalog 不需要認證,CLI login
流程會把 key 回報為 unverifiable,而不會把公開 response 當成有效 key 的證明。Chat request 仍使用
Expand Down
159 changes: 159 additions & 0 deletions src/providers/quota.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ const REQUEST_TIMEOUT_MS = 8_000;
export const QUOTA_RESPONSE_MAX_BYTES = 512 * 1024;
const KIMI_CODE_BASE_URL = "https://api.kimi.com/coding/v1";
const KIMI_CODE_USAGE_URL = `${KIMI_CODE_BASE_URL}/usages`;
const COMMAND_CODE_BASE_URL = "https://api.commandcode.ai";
const COMMAND_CODE_WHOAMI_URL = `${COMMAND_CODE_BASE_URL}/alpha/whoami`;
const COMMAND_CODE_CREDITS_URL = `${COMMAND_CODE_BASE_URL}/alpha/billing/credits`;
const COMMAND_CODE_SUBSCRIPTIONS_URL = `${COMMAND_CODE_BASE_URL}/alpha/billing/subscriptions`;
const COMMAND_CODE_USAGE_URL = `${COMMAND_CODE_BASE_URL}/alpha/usage/summary`;
const A6API_BASE_URL = "https://api.a6api.com";
const OPENCODE_GO_BASE_URL = "https://opencode.ai/zen/go/v1";
const OPENCODE_GO_USAGE_URL = `${OPENCODE_GO_BASE_URL}/usage`;
Expand Down Expand Up @@ -1510,6 +1515,12 @@ function isCanonicalKimiCodeBaseUrl(baseUrl: string): boolean {
return normalizedBaseUrl(baseUrl) === KIMI_CODE_BASE_URL;
}

function isCanonicalCommandCodeBaseUrl(baseUrl: string): boolean {
const normalized = normalizedBaseUrl(baseUrl);
// OAuth preset points at the API root; the Provider-API preset at /provider/v1.
return normalized === COMMAND_CODE_BASE_URL || normalized === `${COMMAND_CODE_BASE_URL}/provider/v1`;
}

/** Prefer the nested `data` shell when the outer object is only an envelope. */
function unwrapKimiQuotaPayload(value: unknown): Record<string, unknown> | null {
const body = asRecord(value);
Expand Down Expand Up @@ -1631,6 +1642,145 @@ async function fetchKimiQuota(provider: string, config: OcxProviderConfig): Prom
return quota ? report(provider, "kimi:usages", quota) : null;
}

/**
* Command Code rolling window: `{ cap, used, resetAt }` off /alpha/billing/credits,
* normalized to a percent with an optional reset timestamp.
*/
function parseCommandCodeWindow(value: unknown): { percent: number; resetAt?: number } | null {
const row = asRecord(value);
if (!row) return null;
const cap = toFiniteNumber(row.cap);
const used = toFiniteNumber(row.used);
if (cap === undefined || used === undefined || cap <= 0 || used < 0) return null;
const percent = normalizePercent((used / cap) * 100);
if (percent === undefined) return null;
const resetAt = quotaResetAt(row);
return { percent, ...(resetAt !== undefined ? { resetAt } : {}) };
}

/** Soft-fail GET returning a parsed record, or null when unavailable. */
async function fetchCommandCodeJson(url: string, bearer: string): Promise<Record<string, unknown> | null> {
try {
const response = await fetch(url, {
headers: { Accept: "application/json", Authorization: `Bearer ${bearer}` },
redirect: "error",
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
});
if (!response.ok) return null;
return asRecord(await readQuotaJson(response));
} catch {
return null;
}
}

/**
* Soft-fail period spend (used) against the remaining credit pools → creditsUsd.
* Period scoping: `since=<currentPeriodStart>` keeps spend aligned with the
* pools' billing cycle, and `currentPeriodEnd` becomes expiresAt.
*/
async function fetchCommandCodeSpend(
bearer: string,
credits: Record<string, unknown> | null,
orgQuery: string,
): Promise<ProviderQuotaCreditsUsd | undefined> {
if (!credits) return undefined;
const subscriptionBody = await fetchCommandCodeJson(`${COMMAND_CODE_SUBSCRIPTIONS_URL}${orgQuery}`, bearer);
const subscription = asRecord(subscriptionBody?.data) ?? subscriptionBody;
const periodStart = typeof subscription?.currentPeriodStart === "string" ? subscription.currentPeriodStart.trim() : "";
// Unscoped /usage/summary is lifetime spend; mixing it with current-cycle
// remaining pools produces a wrong percent. Omit creditsUsd until a period exists.
if (!periodStart) return undefined;
const sinceQuery = `${orgQuery ? "&" : "?"}since=${encodeURIComponent(periodStart)}`;
const expiresAt = normalizeResetAt(subscription?.currentPeriodEnd);
const summaryBody = await fetchCommandCodeJson(`${COMMAND_CODE_USAGE_URL}${orgQuery}${sinceQuery}`, bearer);
const summary = asRecord(summaryBody?.data) ?? summaryBody;
const used = toFiniteNumber(summary?.totalCost) ?? toFiniteNumber(summary?.totalMonthlyCredits);
if (used === undefined || used < 0) return undefined;
const pools = [credits.monthlyCredits, credits.purchasedCredits, credits.freeCredits]
.map(value => toFiniteNumber(value))
.filter((value): value is number => value !== undefined);
// Field presence is what separates a real balance from absent data: an exhausted
// all-zero account still reports remaining=0, while no remaining-credit field at
// all means there is nothing to meter.
if (pools.length === 0) return undefined;
const remaining = pools.reduce((sum, value) => sum + Math.max(0, value ?? 0), 0);
const limit = used + remaining;
const percent = normalizePercent(limit > 0 ? (used / limit) * 100 : 0);
// Purchased credits roll over past the subscription period end, so an expiry is
// only truthful when the aggregate contains no non-expiring purchased pool.
const purchased = toFiniteNumber(credits.purchasedCredits) ?? 0;
return percent === undefined
? undefined
: {
used,
limit,
remaining,
percent,
...(expiresAt !== undefined && purchased <= 0 ? { expiresAt } : {}),
};
}

/** OAuth access token or ACTIVE Provider-API key for the Command Code quota probe. */
async function resolveCommandCodeQuotaBearer(config: OcxProviderConfig): Promise<string | null> {
if (config.authMode === "oauth") {
try {
return await getValidAccessToken("command-code");
} catch {
return null;
}
}
// ACTIVE key only: a quota bar for a different account than the one routing
// requests is a wrong meter, not a helpful one.
return resolveEnvValue(config.apiKey)?.trim() || null;
}

/**
* Command Code `GET /alpha/billing/credits` — the same Bearer surface the CLI's
* usage view uses (windowLimits.fiveHour / windowLimits.weekly), plus soft
* whoami (team orgId scoping) and subscription-scoped spend for creditsUsd.
*/
async function fetchCommandCodeQuota(provider: string, config: OcxProviderConfig): Promise<ProviderQuotaProbeResult> {
// Never release credentials to a user-edited or lookalike provider host.
if (!isCanonicalCommandCodeBaseUrl(config.baseUrl)) return null;
const bearer = await resolveCommandCodeQuotaBearer(config);
if (!bearer) return null;
const whoamiBody = await fetchCommandCodeJson(COMMAND_CODE_WHOAMI_URL, bearer);
const whoami = asRecord(whoamiBody?.data) ?? whoamiBody;
const org = asRecord(whoami?.org);
const orgId = typeof org?.id === "string" && org.id.trim() ? org.id.trim() : null;
const orgQuery = orgId ? `?orgId=${encodeURIComponent(orgId)}` : "";
const response = await fetch(`${COMMAND_CODE_CREDITS_URL}${orgQuery}`, {
headers: { Accept: "application/json", Authorization: `Bearer ${bearer}` },
redirect: "error",
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
});
if (!response.ok) {
return response.status >= 400 && response.status < 500 && response.status !== 408 && response.status !== 429
? TERMINAL_QUOTA_FAILURE
: null;
}
const raw = asRecord(await readQuotaJson(response));
const body = asRecord(raw?.data) ?? raw;
const credits = asRecord(body?.credits);
const limits = asRecord(body?.windowLimits);
if (!credits && !limits) return null;
const fiveHour = parseCommandCodeWindow(limits?.fiveHour);
const weekly = parseCommandCodeWindow(limits?.weekly);
const creditsUsd = await fetchCommandCodeSpend(bearer, credits, orgQuery);
return report(provider, "command-code:credits", {
...(fiveHour ? {
fiveHourPercent: fiveHour.percent,
...(fiveHour.resetAt !== undefined ? { fiveHourResetAt: fiveHour.resetAt } : {}),
} : {}),
...(weekly ? {
weeklyPercent: weekly.percent,
...(weekly.resetAt !== undefined ? { weeklyResetAt: weekly.resetAt } : {}),
} : {}),
...(creditsUsd ? { creditsUsd } : {}),
updatedAt: Date.now(),
});
}

/** Cursor included usage via api2.cursor.sh (Bearer from OAuth) — unofficial, may change. */
async function fetchCursorQuota(provider: string): Promise<ProviderQuotaReport | null> {
let accessToken: string;
Expand Down Expand Up @@ -1919,6 +2069,15 @@ async function maybeFetchProviderQuota(
if (provider.authMode === "key" && isCanonicalKimiCodeBaseUrl(provider.baseUrl)) {
return fetchKimiQuota(name, provider);
}
// OAuth account login or Provider-API key only; forward/local modes carry no
// credential of ours on the canonical host.
if (provider.authMode === "oauth" && name === "command-code") {
return fetchCommandCodeQuota(name, provider);
}
if ((provider.authMode ?? "key") === "key" && name === "commandcode"
&& isCanonicalCommandCodeBaseUrl(provider.baseUrl)) {
return fetchCommandCodeQuota(name, provider);
}
if ((provider.authMode ?? "key") === "key" && name === "opencode-go") {
return fetchOpenCodeGoQuota(name, provider);
}
Expand Down
Loading
Loading