Browse Source

Add Authentication Button on HICAP provider to get API KEY (#9098)

* add auth option to get API-KEY for hicap from hicap dashboard website

* remove default hicap model selection

* change url hicap get api keys, add useEffect when update hicapApiKey

* add changeset
Marco Alejandro Chavez Santos 2 months ago
parent
commit
42ce100

+ 5 - 0
.changeset/two-keys-create.md

@@ -0,0 +1,5 @@
+---
+"cline": minor
+---
+
+Add Generate API Key on Hicap Provider selection

+ 2 - 0
proto/cline/account.proto

@@ -41,6 +41,8 @@ service AccountService {
   rpc openrouterAuthClicked(EmptyRequest) returns (Empty);
 
   rpc requestyAuthClicked(StringRequest) returns (Empty);
+  
+  rpc hicapAuthClicked(EmptyRequest) returns (Empty);
 
   // Returns a link the webview can use to redirect back to the user's IDE.
   rpc getRedirectUrl(EmptyRequest) returns (String);

+ 16 - 0
src/core/controller/account/hicapAuthClicked.ts

@@ -0,0 +1,16 @@
+import { Empty, EmptyRequest } from "@shared/proto/cline/common"
+import { HostProvider } from "@/hosts/host-provider"
+import { openExternal } from "@/utils/env"
+import { Controller } from ".."
+
+/**
+ * Initiates Hicap auth
+ */
+export async function hicapAuthClicked(_: Controller, __: EmptyRequest): Promise<Empty> {
+	const callbackUri = await HostProvider.get().getCallbackUrl()
+	const authUri = `https://dashboard.hicap.ai/setup?application=cline&callback_url=${callbackUri}/hicap`
+
+	await openExternal(authUri)
+
+	return {}
+}

+ 24 - 0
src/core/controller/index.ts

@@ -759,6 +759,30 @@ export class Controller {
 		return undefined
 	}
 
+	// Hicap
+	async handleHicapCallback(code: string) {
+		const apiKey: string = code
+
+		const hicap: ApiProvider = "hicap"
+		const currentMode = this.stateManager.getGlobalSettingsKey("mode")
+
+		// Update API configuration through cache service
+		const currentApiConfiguration = this.stateManager.getApiConfiguration()
+		const updatedConfig = {
+			...currentApiConfiguration,
+			planModeApiProvider: hicap,
+			actModeApiProvider: hicap,
+			hicapApiKey: apiKey,
+		}
+		this.stateManager.setApiConfiguration(updatedConfig)
+
+		await this.postStateToWebview()
+		this.accountService
+		if (this.task) {
+			this.task.api = buildApiHandler({ ...updatedConfig, ulid: this.task.ulid }, currentMode)
+		}
+	}
+
 	// Task history
 
 	async getTaskWithId(id: string): Promise<{

+ 9 - 0
src/services/uri/SharedUriHandler.ts

@@ -104,6 +104,15 @@ export class SharedUriHandler {
 					await visibleWebview.controller.handleMcpOAuthCallback(serverHash, code, state)
 					return true
 				}
+				case "/hicap": {
+					const code = query.get("code")
+					if (code) {
+						await visibleWebview.controller.handleHicapCallback(code)
+						return true
+					}
+					Logger.warn("SharedUriHandler: Missing code parameter for Hicap callback")
+					return false
+				}
 				default:
 					Logger.warn(`SharedUriHandler: Unknown path: ${path}`)
 					return false

+ 2 - 2
webview-ui/src/components/settings/HicapModelPicker.tsx

@@ -33,7 +33,7 @@ const HicapModelPicker: React.FC<HicapModelPickerProps> = ({ isPopup, currentMod
 	const { apiConfiguration, favoritedModelIds, hicapModels, refreshHicapModels } = useExtensionState()
 
 	const modeFields = getModeSpecificFields(apiConfiguration, currentMode)
-	const [searchTerm, setSearchTerm] = useState(modeFields.hicapModelId || "gpt-5")
+	const [searchTerm, setSearchTerm] = useState(modeFields.hicapModelId || "")
 	const [isDropdownVisible, setIsDropdownVisible] = useState(false)
 	const [selectedIndex, setSelectedIndex] = useState(-1)
 	const dropdownRef = useRef<HTMLDivElement>(null)
@@ -60,7 +60,7 @@ const HicapModelPicker: React.FC<HicapModelPickerProps> = ({ isPopup, currentMod
 
 	// Sync external changes when the modelId changes
 	useEffect(() => {
-		const currentModelId = modeFields.hicapModelId || "gpt-5"
+		const currentModelId = modeFields.hicapModelId || ""
 		setSearchTerm(currentModelId)
 	}, [modeFields.hicapModelId])
 

+ 25 - 0
webview-ui/src/components/settings/providers/HicapProvider.tsx

@@ -1,5 +1,9 @@
+import { EmptyRequest } from "@shared/proto/cline/common"
 import { Mode } from "@shared/storage/types"
+import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
+import { useEffect } from "react"
 import { useExtensionState } from "@/context/ExtensionStateContext"
+import { AccountServiceClient } from "@/services/grpc-client"
 import { DebouncedTextField } from "../common/DebouncedTextField"
 import HicapModelPicker from "../HicapModelPicker"
 import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers"
@@ -20,6 +24,12 @@ export const HicapProvider = ({ showModelOptions, isPopup, currentMode }: HicapP
 	const { apiConfiguration, refreshHicapModels } = useExtensionState()
 	const { handleFieldChange } = useApiConfigurationHandlers()
 
+	useEffect(() => {
+		if (apiConfiguration?.hicapApiKey && apiConfiguration?.hicapApiKey.length === 32) {
+			refreshHicapModels()
+		}
+	}, [apiConfiguration?.hicapApiKey])
+
 	return (
 		<div>
 			<div>
@@ -45,6 +55,21 @@ export const HicapProvider = ({ showModelOptions, isPopup, currentMode }: HicapP
 						<span style={{ fontWeight: 500 }}>Hicap API Key</span>
 					</div>
 				</DebouncedTextField>
+
+				{!apiConfiguration?.hicapApiKey && (
+					<VSCodeButton
+						appearance="secondary"
+						onClick={async () => {
+							try {
+								await AccountServiceClient.hicapAuthClicked(EmptyRequest.create())
+							} catch (error) {
+								console.error("Failed to open Hicap auth:", error)
+							}
+						}}
+						style={{ margin: "5px 0 0 0" }}>
+						Generate API Key
+					</VSCodeButton>
+				)}
 			</div>
 
 			{showModelOptions && (