mirror of
https://github.com/cline/cline.git
synced 2026-09-04 20:02:30 +08:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b49b460284 | |||
| 516c210678 | |||
| 77e9f0040d | |||
| d53d65468f | |||
| a966632867 | |||
| a7d8959218 | |||
| 761551cda6 | |||
| 4fce90b1de | |||
| 5c21ba321f |
Vendored
+3
@@ -15,6 +15,9 @@
|
||||
"env": {
|
||||
"IS_DEV": "true",
|
||||
"DEV_WORKSPACE_FOLDER": "${workspaceFolder}",
|
||||
// Use the environment variable to determine the backend URL
|
||||
// Options: "production", "staging", "local"
|
||||
// Require the extension to reload to apply changes
|
||||
"CLINE_ENVIRONMENT": "production"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -53,6 +53,7 @@ message UserInfo {
|
||||
optional string display_name = 2;
|
||||
optional string email = 3;
|
||||
optional string photo_url = 4;
|
||||
optional string app_base_url = 5; // Cline app base URL
|
||||
}
|
||||
|
||||
message UserOrganization {
|
||||
|
||||
+63
-41
@@ -1,6 +1,8 @@
|
||||
export type Environment = "production" | "staging" | "local"
|
||||
|
||||
const CLINE_ENVIRONMENT: Environment = (process.env.CLINE_ENVIRONMENT as Environment) || "production"
|
||||
export enum Environment {
|
||||
production = "production",
|
||||
staging = "staging",
|
||||
local = "local",
|
||||
}
|
||||
|
||||
interface EnvironmentConfig {
|
||||
appBaseUrl: string
|
||||
@@ -16,43 +18,63 @@ interface EnvironmentConfig {
|
||||
}
|
||||
}
|
||||
|
||||
const configs: Record<Environment, EnvironmentConfig> = {
|
||||
production: {
|
||||
appBaseUrl: "https://app.cline.bot",
|
||||
apiBaseUrl: "https://api.cline.bot",
|
||||
mcpBaseUrl: "https://api.cline.bot/v1/mcp",
|
||||
firebase: {
|
||||
apiKey: "AIzaSyC5rx59Xt8UgwdU3PCfzUF7vCwmp9-K2vk",
|
||||
authDomain: "cline-prod.firebaseapp.com",
|
||||
projectId: "cline-prod",
|
||||
storageBucket: "cline-prod.firebasestorage.app",
|
||||
messagingSenderId: "941048379330",
|
||||
appId: "1:941048379330:web:45058eedeefc5cdfcc485b",
|
||||
},
|
||||
},
|
||||
staging: {
|
||||
appBaseUrl: "https://staging-app.cline.bot",
|
||||
apiBaseUrl: "https://core-api.staging.int.cline.bot",
|
||||
mcpBaseUrl: "https://api.cline.bot/v1/mcp",
|
||||
firebase: {
|
||||
apiKey: "AIzaSyASSwkwX1kSO8vddjZkE5N19QU9cVQ0CIk",
|
||||
authDomain: "cline-staging.firebaseapp.com",
|
||||
projectId: "cline-staging",
|
||||
storageBucket: "cline-staging.firebasestorage.app",
|
||||
messagingSenderId: "853479478430",
|
||||
appId: "1:853479478430:web:2de0dba1c63c3262d4578f",
|
||||
},
|
||||
},
|
||||
local: {
|
||||
appBaseUrl: "http://localhost:3000",
|
||||
apiBaseUrl: "http://localhost:7777",
|
||||
mcpBaseUrl: "https://api.cline.bot/v1/mcp",
|
||||
firebase: {
|
||||
apiKey: "AIzaSyD8wtkd1I-EICuAg6xgAQpRdwYTvwxZG2w",
|
||||
authDomain: "cline-preview.firebaseapp.com",
|
||||
projectId: "cline-preview",
|
||||
},
|
||||
},
|
||||
function getClineEnv(): Environment {
|
||||
const isE2ETesting = process?.env?.E2E_TEST === "true"
|
||||
const _env = isE2ETesting ? Environment.staging : process?.env?.CLINE_ENVIRONMENT
|
||||
if (_env && Object.values(Environment).includes(_env as Environment)) {
|
||||
return _env as Environment
|
||||
}
|
||||
return Environment.production
|
||||
}
|
||||
|
||||
export const clineEnvConfig = configs[CLINE_ENVIRONMENT]
|
||||
// Config getter function to avoid storing all configs in memory
|
||||
function getEnvironmentConfig(env: Environment): EnvironmentConfig {
|
||||
console.info("Cline environment:", CLINE_ENVIRONMENT)
|
||||
switch (env) {
|
||||
case Environment.staging:
|
||||
return {
|
||||
appBaseUrl: "https://staging-app.cline.bot",
|
||||
apiBaseUrl: "https://core-api.staging.int.cline.bot",
|
||||
mcpBaseUrl: "https://api.cline.bot/v1/mcp",
|
||||
firebase: {
|
||||
apiKey: "AIzaSyASSwkwX1kSO8vddjZkE5N19QU9cVQ0CIk",
|
||||
authDomain: "cline-staging.firebaseapp.com",
|
||||
projectId: "cline-staging",
|
||||
storageBucket: "cline-staging.firebasestorage.app",
|
||||
messagingSenderId: "853479478430",
|
||||
appId: "1:853479478430:web:2de0dba1c63c3262d4578f",
|
||||
},
|
||||
}
|
||||
case Environment.local:
|
||||
return {
|
||||
appBaseUrl: "http://localhost:3000",
|
||||
apiBaseUrl: "http://localhost:7777",
|
||||
mcpBaseUrl: "https://api.cline.bot/v1/mcp",
|
||||
firebase: {
|
||||
apiKey: "AIzaSyD8wtkd1I-EICuAg6xgAQpRdwYTvwxZG2w",
|
||||
authDomain: "cline-preview.firebaseapp.com",
|
||||
projectId: "cline-preview",
|
||||
},
|
||||
}
|
||||
default:
|
||||
return {
|
||||
appBaseUrl: "https://app.cline.bot",
|
||||
apiBaseUrl: "https://api.cline.bot",
|
||||
mcpBaseUrl: "https://api.cline.bot/v1/mcp",
|
||||
firebase: {
|
||||
apiKey: "AIzaSyC5rx59Xt8UgwdU3PCfzUF7vCwmp9-K2vk",
|
||||
authDomain: "cline-prod.firebaseapp.com",
|
||||
projectId: "cline-prod",
|
||||
storageBucket: "cline-prod.firebasestorage.app",
|
||||
messagingSenderId: "941048379330",
|
||||
appId: "1:941048379330:web:45058eedeefc5cdfcc485b",
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get environment once at module load
|
||||
const CLINE_ENVIRONMENT = getClineEnv()
|
||||
const _configCache = getEnvironmentConfig(CLINE_ENVIRONMENT)
|
||||
|
||||
export const clineEnvConfig = _configCache
|
||||
|
||||
@@ -34,7 +34,7 @@ import { sendStateUpdate } from "./state/subscribeToState"
|
||||
import { sendAddToInputEvent } from "./ui/subscribeToAddToInput"
|
||||
import { sendMcpMarketplaceCatalogEvent } from "./mcp/subscribeToMcpMarketplaceCatalog"
|
||||
import { AuthService } from "@/services/auth/AuthService"
|
||||
import { ShowMessageRequest, ShowMessageType } from "@/shared/proto/host/window"
|
||||
import { ShowMessageType } from "@/shared/proto/host/window"
|
||||
import { getHostBridgeProvider } from "@/hosts/host-providers"
|
||||
import { clineEnvConfig } from "@/config"
|
||||
|
||||
@@ -744,6 +744,7 @@ export class Controller {
|
||||
|
||||
return {
|
||||
version: this.context.extension?.packageJSON?.version ?? "",
|
||||
clineAppBaseUrl: clineEnvConfig.appBaseUrl,
|
||||
apiConfiguration,
|
||||
uriScheme: vscode.env.uriScheme,
|
||||
currentTaskItem: this.task?.taskId ? (taskHistory || []).find((item) => item.id === this.task?.taskId) : undefined,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import vscode from "vscode"
|
||||
import { EmptyRequest, String } from "../../shared/proto/common"
|
||||
import { AuthState, UserInfo } from "../../shared/proto/account"
|
||||
import { StreamingResponseHandler, getRequestRegistry } from "@/core/controller/grpc-handler"
|
||||
import { FirebaseAuthProvider } from "./providers/FirebaseAuthProvider"
|
||||
import { Controller } from "@/core/controller"
|
||||
import { storeSecret } from "@/core/storage/state"
|
||||
import { clineEnvConfig } from "@/config"
|
||||
import { Controller } from "@/core/controller"
|
||||
import { getRequestRegistry, type StreamingResponseHandler } from "@/core/controller/grpc-handler"
|
||||
import { storeSecret } from "@/core/storage/state"
|
||||
import { AuthState, UserInfo } from "../../shared/proto/account"
|
||||
import { type EmptyRequest, String } from "../../shared/proto/common"
|
||||
import { FirebaseAuthProvider } from "./providers/FirebaseAuthProvider"
|
||||
import { openExternal } from "@/utils/env"
|
||||
|
||||
const DefaultClineAccountURI = `${clineEnvConfig.appBaseUrl}/auth`
|
||||
@@ -32,6 +32,10 @@ export interface ClineAccountUserInfo {
|
||||
email: string
|
||||
id: string
|
||||
organizations: ClineAccountOrganization[]
|
||||
/**
|
||||
* Cline app base URL, used for webview UI and other client-side operations
|
||||
*/
|
||||
appBaseUrl?: string
|
||||
}
|
||||
|
||||
export interface ClineAccountOrganization {
|
||||
@@ -156,17 +160,20 @@ export class AuthService {
|
||||
let user: any = null
|
||||
if (this._clineAuthInfo && this._authenticated) {
|
||||
const userInfo = this._clineAuthInfo.userInfo
|
||||
this._clineAuthInfo.userInfo.appBaseUrl = clineEnvConfig?.appBaseUrl
|
||||
|
||||
user = UserInfo.create({
|
||||
// TODO: create proto for new user info type
|
||||
uid: userInfo?.id,
|
||||
displayName: userInfo?.displayName,
|
||||
email: userInfo?.email,
|
||||
photoUrl: undefined,
|
||||
appBaseUrl: userInfo?.appBaseUrl,
|
||||
})
|
||||
}
|
||||
|
||||
return AuthState.create({
|
||||
user: user,
|
||||
user,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ export const DEFAULT_PLATFORM = "unknown"
|
||||
export interface ExtensionState {
|
||||
isNewUser: boolean
|
||||
welcomeViewCompleted: boolean
|
||||
clineAppBaseUrl: string
|
||||
apiConfiguration?: ApiConfiguration
|
||||
autoApprovalSettings: AutoApprovalSettings
|
||||
browserSettings: BrowserSettings
|
||||
|
||||
@@ -2,4 +2,5 @@ export interface UserInfo {
|
||||
displayName?: string
|
||||
email?: string
|
||||
photoUrl?: string
|
||||
apiBaseUrl?: string // Base URL for API requests
|
||||
}
|
||||
|
||||
@@ -6,6 +6,10 @@ e2e("Auth - can set up API keys", async ({ page, sidebar }) => {
|
||||
await expect(sidebar.getByRole("button", { name: "Get Started for Free" })).toBeVisible()
|
||||
await expect(sidebar.getByRole("button", { name: "Use your own API key" })).toBeVisible()
|
||||
|
||||
// Check if Sign In button has title attribute set to staging URL
|
||||
await sidebar.getByRole("button", { name: "Get Started for Free" }).hover()
|
||||
await expect(sidebar.getByTitle("https://staging-app.cline.bot")).toBeVisible()
|
||||
|
||||
// Navigate to API key setup
|
||||
await sidebar.getByRole("button", { name: "Use your own API key" }).click()
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { clineEnvConfig } from "@/config"
|
||||
import { useClineAuth } from "@/context/ClineAuthContext"
|
||||
import { useExtensionState } from "@/context/ExtensionStateContext"
|
||||
import { AccountServiceClient } from "@/services/grpc-client"
|
||||
@@ -14,10 +13,10 @@ import {
|
||||
VSCodeOption,
|
||||
VSCodeTag,
|
||||
} from "@vscode/webview-ui-toolkit/react"
|
||||
import { memo, useCallback, useEffect, useRef, useState } from "react"
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"
|
||||
import ClineLogoWhite from "../../assets/ClineLogoWhite"
|
||||
import VSCodeButtonLink from "../common/VSCodeButtonLink"
|
||||
import CreditsHistoryTable from "./CreditsHistoryTable"
|
||||
import VSCodeButtonLink from "../common/VSCodeButtonLink"
|
||||
|
||||
// Custom hook for animated credit display with styled decimals
|
||||
const useAnimatedCredits = (targetValue: number, duration: number = 660) => {
|
||||
@@ -35,7 +34,7 @@ const useAnimatedCredits = (targetValue: number, duration: number = 660) => {
|
||||
const progress = Math.min(elapsed / duration, 1)
|
||||
|
||||
// Easing function (ease-out)
|
||||
const easedProgress = 1 - Math.pow(1 - progress, 3)
|
||||
const easedProgress = 1 - (1 - progress) ** 3
|
||||
const newValue = easedProgress * targetValue
|
||||
|
||||
setCurrentValue(newValue)
|
||||
@@ -112,11 +111,13 @@ const getMainRole = (roles?: string[]) => {
|
||||
return "Member"
|
||||
}
|
||||
|
||||
const CLINE_APP_URL = "https://app.cline.bot"
|
||||
|
||||
export const ClineAccountView = () => {
|
||||
const { clineUser, handleSignIn, handleSignOut } = useClineAuth()
|
||||
const { userInfo, apiConfiguration } = useExtensionState()
|
||||
|
||||
let user = apiConfiguration?.clineAccountId ? clineUser || userInfo : undefined
|
||||
const user = apiConfiguration?.clineAccountId ? clineUser || userInfo : undefined
|
||||
|
||||
const [balance, setBalance] = useState<number | null>(null)
|
||||
const [userOrganizations, setUserOrganizations] = useState<UserOrganization[]>([])
|
||||
@@ -127,9 +128,18 @@ export const ClineAccountView = () => {
|
||||
const [paymentsData, setPaymentsData] = useState<PaymentTransaction[]>([])
|
||||
const intervalRef = useRef<NodeJS.Timeout | null>(null)
|
||||
|
||||
const dashboardAddCreditsURL = activeOrganization
|
||||
? `${clineEnvConfig.appBaseUrl}/dashboard/organization?tab=credits&redirect=true`
|
||||
: `${clineEnvConfig.appBaseUrl}/dashboard/account?tab=credits&redirect=true`
|
||||
const clineUris = useMemo(() => {
|
||||
const base = new URL(clineUser?.appBaseUrl || CLINE_APP_URL)
|
||||
const dashboard = new URL("dashboard", base)
|
||||
const credits = new URL(activeOrganization ? "/organization" : "/account", dashboard)
|
||||
credits.searchParams.set("tab", "credits")
|
||||
credits.searchParams.set("redirect", "true")
|
||||
|
||||
return {
|
||||
dashboard,
|
||||
credits,
|
||||
}
|
||||
}, [clineUser?.appBaseUrl, activeOrganization])
|
||||
|
||||
async function getUserCredits() {
|
||||
setIsLoading(true)
|
||||
@@ -291,10 +301,7 @@ export const ClineAccountView = () => {
|
||||
|
||||
<div className="w-full flex gap-2 flex-col min-[225px]:flex-row">
|
||||
<div className="w-full min-[225px]:w-1/2">
|
||||
<VSCodeButtonLink
|
||||
href={`${clineEnvConfig.appBaseUrl}/dashboard`}
|
||||
appearance="primary"
|
||||
className="w-full">
|
||||
<VSCodeButtonLink href={clineUris.dashboard.href} appearance="primary" className="w-full">
|
||||
Dashboard
|
||||
</VSCodeButtonLink>
|
||||
</div>
|
||||
@@ -332,7 +339,7 @@ export const ClineAccountView = () => {
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<VSCodeButtonLink href={dashboardAddCreditsURL} className="w-full">
|
||||
<VSCodeButtonLink href={clineUris.credits.href} className="w-full">
|
||||
Add Credits
|
||||
</VSCodeButtonLink>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,6 @@ import { TaskServiceClient } from "@/services/grpc-client"
|
||||
import { AskResponseRequest } from "@shared/proto/task"
|
||||
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
|
||||
import React from "react"
|
||||
import { clineEnvConfig } from "@/config"
|
||||
|
||||
interface CreditLimitErrorProps {
|
||||
currentBalance: number
|
||||
@@ -18,7 +17,7 @@ const CreditLimitError: React.FC<CreditLimitErrorProps> = ({
|
||||
totalSpent = 0,
|
||||
totalPromotions = 0,
|
||||
message = "You have run out of credit.",
|
||||
buyCreditsUrl = `${clineEnvConfig.appBaseUrl}/dashboard`,
|
||||
buyCreditsUrl = "https://app.cline.bot/dashboard/account?tab=credits&redirect=true",
|
||||
}) => {
|
||||
// We have to divide because the balance is stored in microcredits
|
||||
return (
|
||||
|
||||
@@ -8,7 +8,7 @@ import { AccountServiceClient, StateServiceClient } from "@/services/grpc-client
|
||||
import { EmptyRequest, BooleanRequest } from "@shared/proto/common"
|
||||
|
||||
const WelcomeView = memo(() => {
|
||||
const { apiConfiguration, chatSettings } = useExtensionState()
|
||||
const { apiConfiguration, chatSettings, clineAppBaseUrl } = useExtensionState()
|
||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
|
||||
const [showApiOptions, setShowApiOptions] = useState(false)
|
||||
|
||||
@@ -54,7 +54,7 @@ const WelcomeView = memo(() => {
|
||||
3.7 Sonnet.
|
||||
</p>
|
||||
|
||||
<VSCodeButton appearance="primary" onClick={handleLogin} className="w-full mt-1">
|
||||
<VSCodeButton appearance="primary" title={clineAppBaseUrl} onClick={handleLogin} className="w-full mt-1">
|
||||
Get Started for Free
|
||||
</VSCodeButton>
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
export type Environment = "production" | "staging" | "local"
|
||||
|
||||
const CLINE_ENVIRONMENT: Environment = (process.env.CLINE_ENVIRONMENT as Environment) || "production"
|
||||
|
||||
interface EnvironmentConfig {
|
||||
appBaseUrl: string
|
||||
}
|
||||
|
||||
const configs: Record<Environment, EnvironmentConfig> = {
|
||||
production: {
|
||||
appBaseUrl: "https://app.cline.bot",
|
||||
},
|
||||
staging: {
|
||||
appBaseUrl: "https://staging-app.cline.bot",
|
||||
},
|
||||
local: {
|
||||
appBaseUrl: "http://localhost:3000",
|
||||
},
|
||||
}
|
||||
|
||||
export const clineEnvConfig = configs[CLINE_ENVIRONMENT]
|
||||
@@ -8,6 +8,7 @@ export interface ClineUser {
|
||||
email?: string
|
||||
displayName?: string
|
||||
photoUrl?: string
|
||||
appBaseUrl?: string
|
||||
}
|
||||
|
||||
export interface ClineAuthContextType {
|
||||
|
||||
@@ -26,8 +26,6 @@ import {
|
||||
requestyDefaultModelInfo,
|
||||
groqDefaultModelId,
|
||||
groqModels,
|
||||
huggingFaceDefaultModelId,
|
||||
huggingFaceModels,
|
||||
} from "../../../src/shared/api"
|
||||
import { McpMarketplaceCatalog, McpServer, McpViewTab } from "../../../src/shared/mcp"
|
||||
import { convertTextMateToHljs } from "../utils/textMateToHljs"
|
||||
@@ -173,6 +171,7 @@ export const ExtensionStateContextProvider: React.FC<{
|
||||
|
||||
const [state, setState] = useState<ExtensionState>({
|
||||
version: "",
|
||||
clineAppBaseUrl: "https://app.cline.bot",
|
||||
clineMessages: [],
|
||||
taskHistory: [],
|
||||
shouldShowAnnouncement: false,
|
||||
|
||||
@@ -67,7 +67,6 @@ export default defineConfig({
|
||||
NODE_ENV: JSON.stringify(process.env.IS_DEV ? "development" : "production"),
|
||||
IS_DEV: JSON.stringify(process.env.IS_DEV),
|
||||
IS_TEST: JSON.stringify(process.env.IS_TEST),
|
||||
CLINE_ENVIRONMENT: JSON.stringify(process.env.CLINE_ENVIRONMENT ?? "production"),
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
|
||||
Reference in New Issue
Block a user