feat(usage): enable coding-plan usage by default for every app

Adding a provider whose base URL matches a coding plan (Kimi, Zhipu,
MiniMax, Volcengine, OpenCode Go, Command Code) now turns on the
token_plan usage query in every app, not only Claude Code. Before, other
apps got it only for OpenCode Go and Codex + Command Code, so a Kimi plan
added to Codex, OpenCode, Pi, Hermes etc. had usage off until enabled by
hand.

The quota endpoints only depend on the API key (Zhipu also on the host),
not on the client protocol or base URL path, and the backend already
resolves usage credentials for all apps. The frontend base URL lookup now
also covers Gemini CLI, Grok Build, Hermes, OpenClaw and MiniMax Code.

ZenMux stays Claude-only: its query GETs the configured base URL itself,
which is untested for other apps' inference URLs. Existing providers are
not changed.
This commit is contained in:
Jason committed 2026-10-13 22:13:16 +08:00
1 parent 3c918cd0a7
commit c859026d48
3 files changed
+68 -27

No files matched your search

+43 -10
View File
@@ -86,11 +86,32 @@ describe("extractBaseUrlForUsageDetection", () => {
).toBe("https://opencode.ai/zen/go/v1");
});
it("returns null for unsupported apps", () => {
it("reads the remaining apps' native base URL fields", () => {
const kimi = "https://api.kimi.com/coding/v1";
expect(
extractBaseUrlForUsageDetection("gemini", {
env: { GOOGLE_GEMINI_BASE_URL: "https://opencode.ai/zen/go" },
env: { GOOGLE_GEMINI_BASE_URL: kimi },
}),
).toBe(kimi);
expect(
extractBaseUrlForUsageDetection("grokbuild", {
config: `[models]\ndefault = "k"\n\n[model.k]\nmodel = "kimi-for-coding"\nbase_url = "${kimi}"\n`,
}),
).toBe(kimi);
expect(extractBaseUrlForUsageDetection("hermes", { base_url: kimi })).toBe(
kimi,
);
expect(extractBaseUrlForUsageDetection("openclaw", { baseUrl: kimi })).toBe(
kimi,
);
expect(
extractBaseUrlForUsageDetection("mcode", { options: { baseURL: kimi } }),
).toBe(kimi);
});
it("returns null for unknown apps", () => {
expect(
extractBaseUrlForUsageDetection("unknown", { baseUrl: "https://x" }),
).toBeNull();
});
});
@@ -154,14 +175,26 @@ describe("injectCodingPlanUsageScript", () => {
expect(injected.meta?.usage_script?.codingPlanProvider).toBe("kimi");
});
it("does not extend other coding plans to non-claude apps", () => {
// 智谱/Kimi 等在其他 app 的自动注入未逐一验证,仅 OpenCode Go 放行
const provider: TestProvider = {
settingsConfig: {
options: { baseURL: "https://open.bigmodel.cn/api/anthropic" },
},
};
expect(inject("opencode", provider).meta?.usage_script).toBeUndefined();
it("injects every coding plan for non-claude apps", () => {
expect(
inject("opencode", {
settingsConfig: {
options: { baseURL: "https://open.bigmodel.cn/api/anthropic" },
},
}).meta?.usage_script,
).toMatchObject({ enabled: true, codingPlanProvider: "zhipu" });
expect(
inject("hermes", {
settingsConfig: { base_url: "https://api.kimi.com/coding/v1" },
}).meta?.usage_script,
).toMatchObject({ enabled: true, codingPlanProvider: "kimi" });
});
it("keeps ZenMux claude-only", () => {
const settingsConfig = { options: { baseURL: "https://zenmux.ai/api/v1" } };
expect(
inject("opencode", { settingsConfig }).meta?.usage_script,
).toBeUndefined();
});
it("never overwrites an existing usage_script", () => {
+20 -14
View File
@@ -10,6 +10,7 @@
import { createUsageScript } from "@/types";
import { TEMPLATE_TYPES } from "@/config/constants";
import { extractCodexBaseUrl } from "@/utils/providerConfigUtils";
import { extractGrokBuildBaseUrl } from "@/utils/grokBuildConfig";
export interface CodingPlanProviderEntry {
/** 与后端 QuotaTier 的 `codingPlanProvider` 取值对齐 */
@@ -121,12 +122,26 @@ export function extractBaseUrlForUsageDetection(
: null,
);
break;
case "gemini":
raw = settingsConfig.env?.GOOGLE_GEMINI_BASE_URL;
break;
case "grokbuild":
raw =
typeof settingsConfig.config === "string"
? extractGrokBuildBaseUrl(settingsConfig.config)
: null;
break;
case "opencode":
case "mcode":
raw = settingsConfig.options?.baseURL;
break;
case "pi":
case "openclaw":
raw = settingsConfig.baseUrl;
break;
case "hermes":
raw = settingsConfig.base_url;
break;
default:
return null;
}
@@ -138,11 +153,10 @@ export function extractBaseUrlForUsageDetection(
* `meta.usage_script` 标记为 token_plan 并启用。
*
* - 仅在 `meta.usage_script` 完全缺失时注入,不覆盖用户/UsageScriptModal 已有配置
* - Claude app 保持既有行为:命中任意 Coding Plan 供应商都注入;
* 其余 app 仅对已验证预设注入:Codex 支持 Command Code,其他非 Claude
* Coding Plan 保持既有行为——
* 五个 app 各有一份 OpenCode Go 预设、凭据形态后端全部支持,而智谱/Kimi
* 等在其他 app 的自动注入未逐一验证过,不随手扩大
* - 所有 app 一视同仁:套餐额度只按 Key(智谱另看 host)查固定端点,与
* 客户端协议、base_url 路径无关;凭据提取与后端 `resolve_usage_credentials` 对齐
* - ZenMux 例外,仍只在 Claude 注入:它的查询直接 GET 用户填的 base_url,
* 其他 app 的推理地址未验证能返回额度
* - code 置空:Rust 端走专用 `coding_plan::get_coding_plan_quota`,不执行 JS 脚本
*/
export function injectCodingPlanUsageScript<
@@ -159,15 +173,7 @@ export function injectCodingPlanUsageScript<
);
const codingPlanProvider = detectCodingPlanProvider(baseUrl);
if (!codingPlanProvider) return provider;
const isCommandCodeForCodex =
appId === "codex" && codingPlanProvider === "command_code";
if (
appId !== "claude" &&
codingPlanProvider !== "opencode_go" &&
!isCommandCodeForCodex
) {
return provider;
}
if (codingPlanProvider === "zenmux" && appId !== "claude") return provider;
return {
...provider,
+5 -3
View File
@@ -34,14 +34,16 @@ base_url = "${CODEX_BASE_URL}"`,
expect(codex.meta?.usage_script?.codingPlanProvider).toBe("command_code");
});
it("does not extend Command Code into OpenCode", () => {
it("injects Command Code into other apps too", () => {
const provider = injectCodingPlanUsageScript("opencode", {
settingsConfig: {
options: { baseURL: CODEX_BASE_URL },
},
}) as { meta?: { usage_script?: unknown } };
}) as { meta?: { usage_script?: { codingPlanProvider?: string } } };
expect(provider.meta?.usage_script).toBeUndefined();
expect(provider.meta?.usage_script?.codingPlanProvider).toBe(
"command_code",
);
});
it("does not overwrite an existing usage script", () => {