mirror of
https://github.com/cline/cline.git
synced 2026-09-19 02:05:44 +08:00
* feat(llms): include Cline free models in the cline-pass catalog * feat(vscode): show Subscribed/Free model tabs on the ClinePass provider * feat(cli): show Subscribed/Free sections in the ClinePass model picker * fix(cli): drop redundant browse-all entry from ClinePass picker * fix(cli): show only subscribed models in ClinePass onboarding picker * feat(cli): include free models and quota explainer in ClinePass onboarding picker * fix: shorten ClinePass free section copy * fix(cli): strip redundant free markers from sectioned picker names * fix: drop free from ClinePass free section copy * fix: tighten ClinePass free section copy * refactor: address review feedback on ClinePass free models - single buildFeaturedModelEntries(providerId) dispatcher, builders private - rename isClineProvider to isClineManagedProvider (includes cline-pass) - use isClineManagedProvider in the free-model cost check - themed tab border, pretty names on free model cards - clearer cline-pass cost test name * fix: address ClinePass free-model review blockers - Stop re-sorting the cline-pass live catalog by release date in mergeKnownModels: free models carry OpenRouter release dates, so the sort could put a free model first and make it the fallback default when the bundled default id rotates out of the live clinePass bucket. Preserve the normalize-time order (pass models first) and pin it with an end-to-end resolveProviderConfig test. - Add the browse-all escape to the CLI ClinePass picker when the clinePass bucket is empty (bundled fallback after a fetch failure), so a subscriber isn't left with a free-models-only picker. - Rename ErrorRow's local isClineManagedProvider to isClineUsageBillingProvider: it only matches the cline provider, unlike the shared util of the same name that also matches cline-pass.
144 lines
3.4 KiB
TypeScript
144 lines
3.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
getMainMenuOptions,
|
|
getOAuthProviderLabel,
|
|
shouldUseFeaturedClineModelPicker,
|
|
toModelEntriesFromKnownModels,
|
|
toModelEntry,
|
|
toProviderEntry,
|
|
} from "./model";
|
|
|
|
describe("onboarding model helpers", () => {
|
|
it("hides ClinePass from the main menu unless its feature flag is enabled", () => {
|
|
expect(
|
|
getMainMenuOptions().some((option) => option.value === "cline-pass"),
|
|
).toBe(false);
|
|
expect(
|
|
getMainMenuOptions({ isClinePassEnabled: false }).some(
|
|
(option) => option.value === "cline-pass",
|
|
),
|
|
).toBe(false);
|
|
expect(
|
|
getMainMenuOptions({ isClinePassEnabled: true }).some(
|
|
(option) => option.value === "cline-pass",
|
|
),
|
|
).toBe(true);
|
|
});
|
|
|
|
it("maps provider catalog entries into onboarding provider entries", () => {
|
|
expect(
|
|
toProviderEntry({
|
|
id: "cline",
|
|
name: "Cline",
|
|
apiKey: "",
|
|
oauthAccessTokenPresent: true,
|
|
models: 12,
|
|
defaultModelId: "openai/gpt-5.3-codex",
|
|
}),
|
|
).toEqual({
|
|
id: "cline",
|
|
name: "Cline",
|
|
isOAuth: true,
|
|
isLocalAuth: false,
|
|
hasAuth: true,
|
|
models: 12,
|
|
defaultModelId: "openai/gpt-5.3-codex",
|
|
});
|
|
});
|
|
|
|
it("treats API key providers as authenticated when an API key exists", () => {
|
|
expect(
|
|
toProviderEntry({
|
|
id: "anthropic",
|
|
name: "Anthropic",
|
|
apiKey: "sk-test",
|
|
models: null,
|
|
}),
|
|
).toMatchObject({
|
|
id: "anthropic",
|
|
isOAuth: false,
|
|
isLocalAuth: false,
|
|
hasAuth: true,
|
|
models: null,
|
|
});
|
|
});
|
|
|
|
it("marks the OpenAI Codex CLI provider as local auth", () => {
|
|
expect(
|
|
toProviderEntry({
|
|
id: "openai-codex-cli",
|
|
name: "OpenAI Codex CLI",
|
|
models: null,
|
|
}),
|
|
).toMatchObject({
|
|
id: "openai-codex-cli",
|
|
isOAuth: false,
|
|
isLocalAuth: true,
|
|
});
|
|
});
|
|
|
|
it("maps model names and reasoning support strictly", () => {
|
|
expect(
|
|
toModelEntry({
|
|
id: "anthropic/claude-sonnet-4.6",
|
|
supportsReasoning: false,
|
|
}),
|
|
).toEqual({
|
|
id: "anthropic/claude-sonnet-4.6",
|
|
name: "anthropic/claude-sonnet-4.6",
|
|
supportsReasoning: false,
|
|
});
|
|
|
|
expect(
|
|
toModelEntry({
|
|
id: "openai/gpt-5.3-codex",
|
|
name: "GPT-5.3 Codex",
|
|
supportsReasoning: true,
|
|
}),
|
|
).toEqual({
|
|
id: "openai/gpt-5.3-codex",
|
|
name: "GPT-5.3 Codex",
|
|
supportsReasoning: true,
|
|
});
|
|
});
|
|
|
|
it("maps resolved known models into sorted onboarding model entries", () => {
|
|
expect(
|
|
toModelEntriesFromKnownModels({
|
|
"gpt-5.2": {
|
|
name: "GPT-5.2",
|
|
capabilities: ["tools"],
|
|
},
|
|
"gpt-5.3-codex": {
|
|
name: "GPT-5.3 Codex",
|
|
capabilities: ["tools", "reasoning"],
|
|
},
|
|
}),
|
|
).toEqual([
|
|
{
|
|
id: "gpt-5.2",
|
|
name: "GPT-5.2",
|
|
supportsReasoning: false,
|
|
},
|
|
{
|
|
id: "gpt-5.3-codex",
|
|
name: "GPT-5.3 Codex",
|
|
supportsReasoning: true,
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("formats OAuth provider labels for onboarding status views", () => {
|
|
expect(getOAuthProviderLabel("cline")).toBe("Cline");
|
|
expect(getOAuthProviderLabel("cline-pass")).toBe("ClinePass");
|
|
expect(getOAuthProviderLabel("openai-codex")).toBe("ChatGPT");
|
|
expect(getOAuthProviderLabel("oca")).toBe("oca");
|
|
});
|
|
|
|
it("uses the featured Cline model picker for the Cline and ClinePass providers", () => {
|
|
expect(shouldUseFeaturedClineModelPicker("cline")).toBe(true);
|
|
expect(shouldUseFeaturedClineModelPicker("cline-pass")).toBe(true);
|
|
expect(shouldUseFeaturedClineModelPicker("anthropic")).toBe(false);
|
|
});
|
|
});
|