mirror of
https://github.com/cline/cline.git
synced 2026-10-25 10:26:36 +08:00
fix(vscode): only carry a local ClinePass model into the cloud with a current plan
The local ClinePass provider also serves free models without a subscription, so its model now becomes the cloud default only once the plan check confirms a current plan. Without one, the user falls through to Cline's default model.
This commit is contained in:
@@ -768,15 +768,18 @@ describe("SdkCloudSessionCoordinator ownership", () => {
|
||||
checkClinePassSubscription: vi.fn(() => plan.promise),
|
||||
})
|
||||
|
||||
// A local ClinePass model carries over without waiting for the plan check.
|
||||
expect(coordinator.nextCloudModelId()).toBe("cline-pass/local")
|
||||
// A local ClinePass model only carries over once the plan check confirms a current plan.
|
||||
expect(coordinator.nextCloudModelId()).toBe(CLINE_DEFAULT_MODEL_ID)
|
||||
await vi.waitFor(() => expect(options.postStateToWebview).toHaveBeenCalledTimes(1))
|
||||
apiConfig = { actModeApiProvider: "anthropic" }
|
||||
expect(coordinator.nextCloudModelId()).toBe(CLINE_DEFAULT_MODEL_ID)
|
||||
|
||||
plan.resolve(true)
|
||||
await vi.waitFor(() => expect(coordinator.clinePassSubscribed()).toBe(true))
|
||||
expect(options.postStateToWebview).toHaveBeenCalledTimes(2)
|
||||
expect(coordinator.nextCloudModelId()).toBe("cline-pass/local")
|
||||
apiConfig = { actModeApiProvider: "cline-pass", actModeClinePassModelId: "cline-free/local" }
|
||||
expect(coordinator.nextCloudModelId()).toBe("cline-pass/first")
|
||||
apiConfig = { actModeApiProvider: "anthropic" }
|
||||
expect(coordinator.nextCloudModelId()).toBe("cline-pass/first")
|
||||
apiConfig = { actModeApiProvider: "cline", actModeClineModelId: "usage/model" }
|
||||
expect(coordinator.nextCloudModelId()).toBe("usage/model")
|
||||
@@ -789,6 +792,26 @@ describe("SdkCloudSessionCoordinator ownership", () => {
|
||||
await coordinator.dispose()
|
||||
})
|
||||
|
||||
it("starts a local ClinePass provider user without a current plan on Cline's default model", async () => {
|
||||
let apiConfig: Record<string, unknown> = { actModeApiProvider: "cline-pass", actModeClinePassModelId: "cline-free/local" }
|
||||
const { coordinator } = makeCoordinator({
|
||||
stateManager: { ...makeStateManager(), getApiConfiguration: () => apiConfig } as never,
|
||||
getAccountScope: () => "user-1:",
|
||||
loadCloudModels: async () => [
|
||||
{ id: "cline-pass/local", name: "Local", catalogId: "cline-pass" },
|
||||
{ id: "cline-free/local", name: "Free", catalogId: "cline" },
|
||||
],
|
||||
checkClinePassSubscription: async () => false,
|
||||
})
|
||||
|
||||
coordinator.nextCloudModelId()
|
||||
await vi.waitFor(() => expect(coordinator.clinePassSubscribed()).toBe(false))
|
||||
expect(coordinator.nextCloudModelId()).toBe(CLINE_DEFAULT_MODEL_ID)
|
||||
apiConfig = { actModeApiProvider: "cline-pass", actModeClinePassModelId: "cline-pass/local" }
|
||||
expect(coordinator.nextCloudModelId()).toBe(CLINE_DEFAULT_MODEL_ID)
|
||||
await coordinator.dispose()
|
||||
})
|
||||
|
||||
it("keeps a local ClinePass model out of the cloud when the catalog doesn't offer ClinePass", async () => {
|
||||
const { coordinator } = makeCoordinator({
|
||||
stateManager: {
|
||||
|
||||
@@ -902,12 +902,12 @@ export class SdkCloudSessionCoordinator {
|
||||
|
||||
/**
|
||||
* The Cline model a new sandbox runs: the cloud model picked in Settings,
|
||||
* else the user's Act-mode Cline or ClinePass model when the cloud catalog
|
||||
* offers it, else the first ClinePass model for a subscriber, else Cline's
|
||||
* default model. Until the catalog is known, a local Cline or ClinePass
|
||||
* model is assumed to be offered. Synchronous, and the only source for both
|
||||
* the composer label and the start path, so the sandbox runs the model the
|
||||
* user saw when they submitted.
|
||||
* else the user's Act-mode Cline model, or ClinePass model when the plan
|
||||
* check confirms a current plan, if the cloud catalog offers it; else the
|
||||
* first ClinePass model for a subscriber, else Cline's default model. Until
|
||||
* the catalog is known, a local Cline or ClinePass model is assumed to be
|
||||
* offered. Synchronous, and the only source for both the composer label and
|
||||
* the start path, so the sandbox runs the model the user saw when they submitted.
|
||||
*/
|
||||
nextCloudModelId(): string {
|
||||
const chosen = this.options.stateManager.getGlobalStateKey("cloudModelId")?.trim()
|
||||
@@ -915,15 +915,16 @@ export class SdkCloudSessionCoordinator {
|
||||
return chosen
|
||||
}
|
||||
const apiConfig = this.options.stateManager.getApiConfiguration()
|
||||
const account = this.accountCloudModels()
|
||||
// The local ClinePass provider also serves free models without a subscription,
|
||||
// so only a confirmed plan makes its model a ClinePass model to carry over.
|
||||
const local =
|
||||
apiConfig.actModeApiProvider === "cline"
|
||||
? apiConfig.actModeClineModelId?.trim()
|
||||
: apiConfig.actModeApiProvider === "cline-pass"
|
||||
: apiConfig.actModeApiProvider === "cline-pass" && account?.subscribed
|
||||
? apiConfig.actModeClinePassModelId?.trim()
|
||||
: undefined
|
||||
const account = this.accountCloudModels()
|
||||
const catalog = account?.models
|
||||
// The local ClinePass provider also offers free models, which the cloud does not take.
|
||||
const offered = catalog
|
||||
? catalog.some((model) => model.id === local)
|
||||
: apiConfig.actModeApiProvider === "cline" || !!local?.startsWith("cline-pass/")
|
||||
|
||||
Reference in New Issue
Block a user