From 843ef29a07d65f4bde93b2c189bb4a4b6ea2ec83 Mon Sep 17 00:00:00 2001 From: Saoud Rizwan <7799382+saoudrizwan@users.noreply.github.com> Date: Tue, 27 Aug 2024 21:38:01 -0400 Subject: [PATCH] Remove Kodu provider --- CHANGELOG.md | 1 - package-lock.json | 81 +------- package.json | 6 +- src/ClaudeDev.ts | 4 +- src/api/index.ts | 3 - src/api/kodu.ts | 186 ------------------ src/extension.ts | 20 +- src/providers/ClaudeDevProvider.ts | 94 +++------ src/shared/ExtensionMessage.ts | 10 +- src/shared/WebviewMessage.ts | 3 - src/shared/api.ts | 11 +- src/shared/kodu.ts | 21 -- webview-ui/src/App.tsx | 82 ++++---- webview-ui/src/components/Announcement.tsx | 19 -- webview-ui/src/components/ApiOptions.tsx | 90 +-------- webview-ui/src/components/ChatRow.tsx | 4 +- webview-ui/src/components/ChatView.tsx | 7 - webview-ui/src/components/KoduPromo.tsx | 71 ------- webview-ui/src/components/TaskHeader.tsx | 12 +- webview-ui/src/components/WelcomeView.tsx | 45 +---- .../src/context/ExtensionStateContext.tsx | 9 - webview-ui/src/utils/validate.ts | 5 - 22 files changed, 95 insertions(+), 689 deletions(-) delete mode 100644 src/api/kodu.ts delete mode 100644 src/shared/kodu.ts delete mode 100644 webview-ui/src/components/KoduPromo.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 42fe1ab..3cf5947 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,6 @@ All notable changes to the "claude-dev" extension will be documented in this fil ## [1.4.0] -- Add Kodu provider with credit tracking (+ $20 free credits!) - Add "Always allow read-only operations" setting to let Claude read files and view directories without needing approval (off by default) - Implement sliding window context management to keep tasks going past 200k tokens diff --git a/package-lock.json b/package-lock.json index f15f2dd..25862a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,17 @@ { "name": "claude-dev", - "version": "1.4.12", + "version": "1.4.15", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "claude-dev", - "version": "1.4.12", + "version": "1.4.15", "license": "MIT", "dependencies": { "@anthropic-ai/bedrock-sdk": "^0.10.2", "@anthropic-ai/sdk": "^0.26.0", "@anthropic-ai/tokenizer": "^0.0.4", - "@kodu-ai/cloud-api": "^1.0.1", "@types/clone-deep": "^4.0.4", "@vscode/codicons": "^0.0.36", "axios": "^1.7.4", @@ -29,8 +28,7 @@ "serialize-error": "^11.0.3", "tree-kill": "^1.2.2", "tree-sitter-wasms": "^0.1.11", - "web-tree-sitter": "^0.22.6", - "zod": "^3.23.8" + "web-tree-sitter": "^0.22.6" }, "devDependencies": { "@types/diff": "^5.2.1", @@ -2790,16 +2788,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@kodu-ai/cloud-api": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@kodu-ai/cloud-api/-/cloud-api-1.0.1.tgz", - "integrity": "sha512-CnmZP4Gm72ReqSO8qZgRYgbuGbo6A7RBbG/kNjMSS9KsDmtXCQu8OIEj/ZBVdaW+vm32t633r7q4nLKv3SaUHQ==", - "dependencies": { - "@trpc/client": "^11.0.0-rc.485", - "@trpc/server": "^11.0.0-rc.485", - "superjson": "^2.2.1" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -4512,25 +4500,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" }, - "node_modules/@trpc/client": { - "version": "11.0.0-rc.485", - "resolved": "https://registry.npmjs.org/@trpc/client/-/client-11.0.0-rc.485.tgz", - "integrity": "sha512-Ld1gQjdYyrku0rjP/4QMg/SdsKgujr0P5XNoWkCyPRjdw3PuJbZFebauQPRC17cbbqGcpJrR+T3vnkhjMb1sgw==", - "funding": [ - "https://trpc.io/sponsor" - ], - "peerDependencies": { - "@trpc/server": "11.0.0-rc.485+1c1d824cd" - } - }, - "node_modules/@trpc/server": { - "version": "11.0.0-rc.485", - "resolved": "https://registry.npmjs.org/@trpc/server/-/server-11.0.0-rc.485.tgz", - "integrity": "sha512-U9SK9jbqCjR8S9wGSe4UBu2e0fqxhQWriZiDb5BLzdxXzls4Jv+XhAkI65yBzlcTbt6VqXegZDAXB3IARPhUCg==", - "funding": [ - "https://trpc.io/sponsor" - ] - }, "node_modules/@types/clone-deep": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/clone-deep/-/clone-deep-4.0.4.tgz", @@ -5468,20 +5437,6 @@ "dev": true, "license": "MIT" }, - "node_modules/copy-anything": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", - "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", - "dependencies": { - "is-what": "^4.1.8" - }, - "engines": { - "node": ">=12.13" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -7306,17 +7261,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-what": { - "version": "4.1.16", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", - "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", - "engines": { - "node": ">=12.13" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -9471,17 +9415,6 @@ "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==" }, - "node_modules/superjson": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.1.tgz", - "integrity": "sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==", - "dependencies": { - "copy-anything": "^3.0.2" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/supports-color": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", @@ -10254,14 +10187,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } } } } diff --git a/package.json b/package.json index a749d1d..53af5ea 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "claude-dev", "displayName": "Claude Dev", "description": "Autonomous coding agent right in your IDE, capable of creating/editing files, executing commands, and more with your permission every step of the way.", - "version": "1.4.15", + "version": "1.4.16", "icon": "icon.png", "engines": { "vscode": "^1.84.0" @@ -135,7 +135,6 @@ "@anthropic-ai/bedrock-sdk": "^0.10.2", "@anthropic-ai/sdk": "^0.26.0", "@anthropic-ai/tokenizer": "^0.0.4", - "@kodu-ai/cloud-api": "^1.0.1", "@types/clone-deep": "^4.0.4", "@vscode/codicons": "^0.0.36", "axios": "^1.7.4", @@ -152,7 +151,6 @@ "serialize-error": "^11.0.3", "tree-kill": "^1.2.2", "tree-sitter-wasms": "^0.1.11", - "web-tree-sitter": "^0.22.6", - "zod": "^3.23.8" + "web-tree-sitter": "^0.22.6" } } diff --git a/src/ClaudeDev.ts b/src/ClaudeDev.ts index 6905523..5fa3b7d 100644 --- a/src/ClaudeDev.ts +++ b/src/ClaudeDev.ts @@ -1261,8 +1261,8 @@ ${this.customInstructions.trim()} ) const { message, userCredits } = await this.api.createMessage(systemPrompt, adjustedMessages, tools) if (userCredits !== undefined) { - console.log("Updating kodu credits", userCredits) - this.providerRef.deref()?.updateKoduCredits(userCredits) + console.log("Updating credits", userCredits) + // TODO: update credits } return message } catch (error) { diff --git a/src/api/index.ts b/src/api/index.ts index c287000..4c725ae 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -3,7 +3,6 @@ import { ApiConfiguration, ApiModelId, ModelInfo } from "../shared/api" import { AnthropicHandler } from "./anthropic" import { AwsBedrockHandler } from "./bedrock" import { OpenRouterHandler } from "./openrouter" -import { KoduHandler } from "./kodu" export interface ApiHandlerMessageResponse { message: Anthropic.Messages.Message @@ -38,8 +37,6 @@ export function buildApiHandler(configuration: ApiConfiguration): ApiHandler { return new OpenRouterHandler(options) case "bedrock": return new AwsBedrockHandler(options) - case "kodu": - return new KoduHandler(options) default: return new AnthropicHandler(options) } diff --git a/src/api/kodu.ts b/src/api/kodu.ts deleted file mode 100644 index 93b0bf3..0000000 --- a/src/api/kodu.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { Anthropic } from "@anthropic-ai/sdk" -import axios from "axios" -import { ApiHandler, ApiHandlerMessageResponse, withoutImageData } from "." -import { ApiHandlerOptions, koduDefaultModelId, KoduModelId, koduModels, ModelInfo } from "../shared/api" -import { getKoduCreditsUrl, getKoduInferenceUrl } from "../shared/kodu" - -export async function fetchKoduCredits({ apiKey }: { apiKey: string }) { - const response = await axios.get(getKoduCreditsUrl(), { - headers: { - "x-api-key": apiKey, - }, - }) - return (response.data.credits as number) || 0 -} - -export class KoduHandler implements ApiHandler { - private options: ApiHandlerOptions - - constructor(options: ApiHandlerOptions) { - this.options = options - } - - async createMessage( - systemPrompt: string, - messages: Anthropic.Messages.MessageParam[], - tools: Anthropic.Messages.Tool[] - ): Promise { - const modelId = this.getModel().id - let requestBody: Anthropic.Beta.PromptCaching.Messages.MessageCreateParamsNonStreaming - switch (modelId) { - case "claude-3-5-sonnet-20240620": - case "claude-3-opus-20240229": - case "claude-3-haiku-20240307": - const userMsgIndices = messages.reduce( - (acc, msg, index) => (msg.role === "user" ? [...acc, index] : acc), - [] as number[] - ) - const lastUserMsgIndex = userMsgIndices[userMsgIndices.length - 1] ?? -1 - const secondLastMsgUserIndex = userMsgIndices[userMsgIndices.length - 2] ?? -1 - requestBody = { - model: modelId, - max_tokens: this.getModel().info.maxTokens, - system: [{ text: systemPrompt, type: "text", cache_control: { type: "ephemeral" } }], - messages: messages.map((message, index) => { - if (index === lastUserMsgIndex || index === secondLastMsgUserIndex) { - return { - ...message, - content: - typeof message.content === "string" - ? [ - { - type: "text", - text: message.content, - cache_control: { type: "ephemeral" }, - }, - ] - : message.content.map((content, contentIndex) => - contentIndex === message.content.length - 1 - ? { ...content, cache_control: { type: "ephemeral" } } - : content - ), - } - } - return message - }), - tools, - tool_choice: { type: "auto" }, - } - break - default: - requestBody = { - model: modelId, - max_tokens: this.getModel().info.maxTokens, - system: [{ text: systemPrompt, type: "text" }], - messages, - tools, - tool_choice: { type: "auto" }, - } - } - - // const response = await axios.post(getKoduInferenceUrl(), requestBody, { - // headers: { - // "x-api-key": this.options.koduApiKey, - // }, - // }) - // const message = response.data - // const userCredits = response.headers["user-credits"] - // return { message, userCredits: userCredits !== undefined ? parseFloat(userCredits) : undefined } - // const thing = { - // method: "POST", - // headers: { - // "Content-Type": "application/json", - // "x-api-key": this.options.koduApiKey || "", - // }, - // body: JSON.stringify(requestBody), - // } - - const response = await axios.post(getKoduInferenceUrl(), requestBody, { - headers: { - "Content-Type": "application/json", - "x-api-key": this.options.koduApiKey || "", - }, - responseType: "stream", - }) - - if (response.data) { - const reader = response.data - const decoder = new TextDecoder("utf-8") - let finalResponse: any = null - let buffer = "" - - for await (const chunk of reader) { - buffer += decoder.decode(chunk, { stream: true }) - const lines = buffer.split("\n\n") - buffer = lines.pop() || "" - - for (const line of lines) { - if (line.startsWith("data: ")) { - const eventData = JSON.parse(line.slice(6)) - - console.log("eventData", eventData) - - if (eventData.code === 0) { - console.log("Health check received") - } else if (eventData.code === 1) { - finalResponse = eventData.body - console.log("finalResponse", finalResponse) - break - } else if (eventData.code === -1) { - throw new Error(`Error in SSE stream: ${JSON.stringify(eventData.json)}`) - } - } - } - - if (finalResponse) { - break - } - } - - if (!finalResponse) { - throw new Error("No final response received from the SSE stream") - } - - const message: { - anthropic: Anthropic.Messages.Message - internal: { - userCredits: number - } - } = finalResponse - console.log("message", message) - return { - message: message.anthropic, - userCredits: message.internal?.userCredits, - } - } else { - throw new Error("No response data received") - } - } - - createUserReadableRequest( - userContent: Array< - | Anthropic.TextBlockParam - | Anthropic.ImageBlockParam - | Anthropic.ToolUseBlockParam - | Anthropic.ToolResultBlockParam - > - ): any { - return { - model: this.getModel().id, - max_tokens: this.getModel().info.maxTokens, - system: "(see SYSTEM_PROMPT in src/ClaudeDev.ts)", - messages: [{ conversation_history: "..." }, { role: "user", content: withoutImageData(userContent) }], - tools: "(see tools in src/ClaudeDev.ts)", - tool_choice: { type: "auto" }, - } - } - - getModel(): { id: KoduModelId; info: ModelInfo } { - const modelId = this.options.apiModelId - if (modelId && modelId in koduModels) { - const id = modelId as KoduModelId - return { id, info: koduModels[id] } - } - return { id: koduDefaultModelId, info: koduModels[koduDefaultModelId] } - } -} diff --git a/src/extension.ts b/src/extension.ts index ecb2273..6b966f8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -109,16 +109,16 @@ export function activate(context: vscode.ExtensionContext) { vscode.workspace.registerTextDocumentContentProvider("claude-dev-diff", diffContentProvider) ) - // URI Handler - const handleUri = async (uri: vscode.Uri) => { - const query = new URLSearchParams(uri.query.replace(/\+/g, "%2B")) - const token = query.get("token") - const email = query.get("email") - if (token) { - await sidebarProvider.saveKoduApiKey(token, email || undefined) - } - } - context.subscriptions.push(vscode.window.registerUriHandler({ handleUri })) + // // URI Handler + // const handleUri = async (uri: vscode.Uri) => { + // const query = new URLSearchParams(uri.query.replace(/\+/g, "%2B")) + // const token = query.get("token") + // const email = query.get("email") + // if (token) { + // await sidebarProvider.saveKoduApiKey(token, email || undefined) + // } + // } + // context.subscriptions.push(vscode.window.registerUriHandler({ handleUri })) } // This method is called when your extension is deactivated diff --git a/src/providers/ClaudeDevProvider.ts b/src/providers/ClaudeDevProvider.ts index 96a3cae..fa25416 100644 --- a/src/providers/ClaudeDevProvider.ts +++ b/src/providers/ClaudeDevProvider.ts @@ -8,7 +8,6 @@ import { downloadTask, getNonce, getUri, selectImages } from "../utils" import * as path from "path" import fs from "fs/promises" import { HistoryItem } from "../shared/HistoryItem" -import { fetchKoduCredits } from "../api/kodu" /* https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts @@ -16,19 +15,16 @@ https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default https://github.com/KumarVariable/vscode-extension-sidebar-html/blob/master/src/customSidebarViewProvider.ts */ -type SecretKey = "apiKey" | "openRouterApiKey" | "awsAccessKey" | "awsSecretKey" | "koduApiKey" +type SecretKey = "apiKey" | "openRouterApiKey" | "awsAccessKey" | "awsSecretKey" type GlobalStateKey = | "apiProvider" | "apiModelId" | "awsRegion" - | "koduEmail" - | "koduCredits" | "maxRequestsPerTask" | "lastShownAnnouncementId" | "customInstructions" | "alwaysAllowReadOnly" | "taskHistory" - | "shouldShowKoduPromo" export class ClaudeDevProvider implements vscode.WebviewViewProvider { public static readonly sideBarId = "claude-dev.SidebarProvider" // used in package.json as the view's id. This value cannot be changed due to how vscode caches views based on their id, and updating the id would break existing instances of the extension. @@ -40,6 +36,28 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { constructor(readonly context: vscode.ExtensionContext, private readonly outputChannel: vscode.OutputChannel) { this.outputChannel.appendLine("ClaudeDevProvider instantiated") + this.revertKodu() + } + + async revertKodu() { + const apiProvider = await this.getGlobalState("apiProvider") + if (apiProvider === "kodu") { + // switch back to previous provider + const anthropicKey = await this.getSecret("apiKey") + if (anthropicKey) { + await this.updateGlobalState("apiProvider", "anthropic" as ApiProvider) + } else { + const openRouterApiKey = await this.getSecret("openRouterApiKey") + if (openRouterApiKey) { + await this.updateGlobalState("apiProvider", "openrouter" as ApiProvider) + } else { + const awsAccessKey = await this.getSecret("awsAccessKey") + if (awsAccessKey) { + await this.updateGlobalState("apiProvider", "bedrock" as ApiProvider) + } + } + } + } } /* @@ -359,25 +377,6 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { case "exportTaskWithId": this.exportTaskWithId(message.text!) break - case "didClickKoduSignOut": - await this.signOutKodu() - break - case "fetchKoduCredits": - const koduApiKey = await this.getSecret("koduApiKey") - if (koduApiKey) { - const credits = await fetchKoduCredits({ apiKey: koduApiKey }) - await this.updateGlobalState("koduCredits", credits) - await this.postMessageToWebview({ - type: "action", - action: "koduCreditsFetched", - state: await this.getStateToPostToWebview(), - }) - } - break - case "didDismissKoduPromo": - await this.updateGlobalState("shouldShowKoduPromo", false) - await this.postStateToWebview() - break case "resetState": await this.resetState() break @@ -390,27 +389,6 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { ) } - // Kodu - - async saveKoduApiKey(apiKey: string, email?: string) { - await this.storeSecret("koduApiKey", apiKey) - await this.updateGlobalState("koduEmail", email) - await this.updateGlobalState("apiProvider", "kodu") - await this.updateGlobalState("shouldShowKoduPromo", false) - await this.postStateToWebview() - await this.postMessageToWebview({ type: "action", action: "koduAuthenticated" }) - this.claudeDev?.updateApi({ apiProvider: "kodu", koduApiKey: apiKey }) - } - - async signOutKodu() { - await this.storeSecret("koduApiKey", undefined) - await this.updateGlobalState("koduEmail", undefined) - await this.updateGlobalState("koduCredits", undefined) - await this.updateGlobalState("apiProvider", "kodu") - this.claudeDev?.updateApi({ apiProvider: "kodu", koduApiKey: undefined }) - await this.postStateToWebview() - } - // Task history async getTaskWithId(id: string): Promise<{ @@ -510,8 +488,6 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { customInstructions, alwaysAllowReadOnly, taskHistory, - koduCredits, - shouldShowKoduPromo, } = await this.getState() return { version: this.context.extension?.packageJSON?.version ?? "", @@ -524,8 +500,6 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { claudeMessages: this.claudeDev?.claudeMessages || [], taskHistory: (taskHistory || []).filter((item) => item.ts && item.task).sort((a, b) => b.ts - a.ts), shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId, - koduCredits, - shouldShowKoduPromo, } } @@ -624,15 +598,11 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { awsAccessKey, awsSecretKey, awsRegion, - koduApiKey, - koduEmail, - koduCredits, maxRequestsPerTask, lastShownAnnouncementId, customInstructions, alwaysAllowReadOnly, taskHistory, - shouldShowKoduPromo, ] = await Promise.all([ this.getGlobalState("apiProvider") as Promise, this.getGlobalState("apiModelId") as Promise, @@ -641,15 +611,11 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { this.getSecret("awsAccessKey") as Promise, this.getSecret("awsSecretKey") as Promise, this.getGlobalState("awsRegion") as Promise, - this.getSecret("koduApiKey") as Promise, - this.getGlobalState("koduEmail") as Promise, - this.getGlobalState("koduCredits") as Promise, this.getGlobalState("maxRequestsPerTask") as Promise, this.getGlobalState("lastShownAnnouncementId") as Promise, this.getGlobalState("customInstructions") as Promise, this.getGlobalState("alwaysAllowReadOnly") as Promise, this.getGlobalState("taskHistory") as Promise, - this.getGlobalState("shouldShowKoduPromo") as Promise, ]) let apiProvider: ApiProvider @@ -661,8 +627,8 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { if (apiKey) { apiProvider = "anthropic" } else { - // New users should default to kodu - apiProvider = "kodu" + // New users should default to anthropic + apiProvider = "anthropic" } } @@ -675,16 +641,12 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { awsAccessKey, awsSecretKey, awsRegion, - koduApiKey, - koduEmail, }, maxRequestsPerTask, lastShownAnnouncementId, customInstructions, alwaysAllowReadOnly: alwaysAllowReadOnly ?? false, taskHistory, - koduCredits, - shouldShowKoduPromo: shouldShowKoduPromo ?? true, } } @@ -700,10 +662,6 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { return history } - async updateKoduCredits(credits: number) { - await this.updateGlobalState("koduCredits", credits) - } - // global private async updateGlobalState(key: GlobalStateKey, value: any) { @@ -755,7 +713,7 @@ export class ClaudeDevProvider implements vscode.WebviewViewProvider { for (const key of this.context.globalState.keys()) { await this.context.globalState.update(key, undefined) } - const secretKeys: SecretKey[] = ["apiKey", "openRouterApiKey", "awsAccessKey", "awsSecretKey", "koduApiKey"] + const secretKeys: SecretKey[] = ["apiKey", "openRouterApiKey", "awsAccessKey", "awsSecretKey"] for (const key of secretKeys) { await this.storeSecret(key, undefined) } diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 50decea..0fa22d7 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -7,13 +7,7 @@ import { HistoryItem } from "./HistoryItem" export interface ExtensionMessage { type: "action" | "state" | "selectedImages" text?: string - action?: - | "chatButtonTapped" - | "settingsButtonTapped" - | "historyButtonTapped" - | "didBecomeVisible" - | "koduAuthenticated" - | "koduCreditsFetched" + action?: "chatButtonTapped" | "settingsButtonTapped" | "historyButtonTapped" | "didBecomeVisible" state?: ExtensionState images?: string[] } @@ -29,8 +23,6 @@ export interface ExtensionState { claudeMessages: ClaudeMessage[] taskHistory: HistoryItem[] shouldShowAnnouncement: boolean - koduCredits?: number - shouldShowKoduPromo: boolean } export interface ClaudeMessage { diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index a6b56d4..0d404d3 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -16,9 +16,6 @@ export interface WebviewMessage { | "showTaskWithId" | "deleteTaskWithId" | "exportTaskWithId" - | "didClickKoduSignOut" - | "fetchKoduCredits" - | "didDismissKoduPromo" | "resetState" text?: string askResponse?: ClaudeAskResponse diff --git a/src/shared/api.ts b/src/shared/api.ts index 7113935..b4fb300 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -1,4 +1,4 @@ -export type ApiProvider = "anthropic" | "openrouter" | "bedrock" | "kodu" +export type ApiProvider = "anthropic" | "openrouter" | "bedrock" export interface ApiHandlerOptions { apiModelId?: ApiModelId @@ -7,8 +7,6 @@ export interface ApiHandlerOptions { awsAccessKey?: string awsSecretKey?: string awsRegion?: string - koduApiKey?: string - koduEmail?: string } export type ApiConfiguration = ApiHandlerOptions & { @@ -252,10 +250,3 @@ export const openRouterModels = { // outputPrice: 1.5, // }, } as const satisfies Record - -// Kodu -export type KoduModelId = keyof typeof koduModels -export const koduDefaultModelId: KoduModelId = "claude-3-5-sonnet-20240620" -export const koduModels = { - ...anthropicModels, -} as const satisfies Record diff --git a/src/shared/kodu.ts b/src/shared/kodu.ts deleted file mode 100644 index 19c0181..0000000 --- a/src/shared/kodu.ts +++ /dev/null @@ -1,21 +0,0 @@ -const KODU_BASE_URL = "https://kodu.ai" - -export function getKoduSignInUrl(uriScheme?: string) { - return `${KODU_BASE_URL}/auth/login?redirectTo=${uriScheme}://saoudrizwan.claude-dev&ext=1` -} - -export function getKoduAddCreditsUrl(uriScheme?: string) { - return `${KODU_BASE_URL}/cloud/plan` -} - -export function getKoduCreditsUrl() { - return `${KODU_BASE_URL}/api/credits` -} - -export function getKoduInferenceUrl() { - return `${KODU_BASE_URL}/api/inference-stream` -} - -export function getKoduHomepageUrl() { - return `${KODU_BASE_URL}` -} diff --git a/webview-ui/src/App.tsx b/webview-ui/src/App.tsx index 31d8af3..f501fef 100644 --- a/webview-ui/src/App.tsx +++ b/webview-ui/src/App.tsx @@ -15,54 +15,40 @@ const AppContent = () => { const [showHistory, setShowHistory] = useState(false) const [showWelcome, setShowWelcome] = useState(false) const [showAnnouncement, setShowAnnouncement] = useState(false) - const [didAuthKoduFromWelcome, setDidAuthKoduFromWelcome] = useState(false) - const handleMessage = useCallback( - (e: MessageEvent) => { - const message: ExtensionMessage = e.data - switch (message.type) { - case "state": - const hasKey = - message.state!.apiConfiguration?.apiKey !== undefined || - message.state!.apiConfiguration?.openRouterApiKey !== undefined || - message.state!.apiConfiguration?.awsAccessKey !== undefined || - message.state!.apiConfiguration?.koduApiKey !== undefined - setShowWelcome(!hasKey) - if (!hasKey) { - setDidAuthKoduFromWelcome(false) - } - // don't update showAnnouncement to false if shouldShowAnnouncement is false - if (message.state!.shouldShowAnnouncement) { - setShowAnnouncement(true) - } - break - case "action": - switch (message.action!) { - case "settingsButtonTapped": - setShowSettings(true) - setShowHistory(false) - break - case "historyButtonTapped": - setShowSettings(false) - setShowHistory(true) - break - case "chatButtonTapped": - setShowSettings(false) - setShowHistory(false) - break - case "koduAuthenticated": - if (!didAuthKoduFromWelcome) { - setShowSettings(true) - setShowHistory(false) - } - break - } - break - } - // (react-use takes care of not registering the same listener multiple times even if this callback is updated.) - }, - [didAuthKoduFromWelcome] - ) + const handleMessage = useCallback((e: MessageEvent) => { + const message: ExtensionMessage = e.data + switch (message.type) { + case "state": + const hasKey = + message.state!.apiConfiguration?.apiKey !== undefined || + message.state!.apiConfiguration?.openRouterApiKey !== undefined || + message.state!.apiConfiguration?.awsAccessKey !== undefined + setShowWelcome(!hasKey) + // don't update showAnnouncement to false if shouldShowAnnouncement is false + if (message.state!.shouldShowAnnouncement) { + setShowAnnouncement(true) + } + break + case "action": + switch (message.action!) { + case "settingsButtonTapped": + setShowSettings(true) + setShowHistory(false) + break + case "historyButtonTapped": + setShowSettings(false) + setShowHistory(true) + break + case "chatButtonTapped": + setShowSettings(false) + setShowHistory(false) + break + } + break + } + // (react-use takes care of not registering the same listener multiple times even if this callback is updated.) + }, []) useEvent("message", handleMessage) @@ -73,7 +59,7 @@ const AppContent = () => { return ( <> {showWelcome ? ( - + ) : ( <> {showSettings && setShowSettings(false)} />} diff --git a/webview-ui/src/components/Announcement.tsx b/webview-ui/src/components/Announcement.tsx index 26f1e07..a699242 100644 --- a/webview-ui/src/components/Announcement.tsx +++ b/webview-ui/src/components/Announcement.tsx @@ -1,7 +1,5 @@ import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react" import { ApiConfiguration } from "../../../src/shared/api" -import { getKoduSignInUrl } from "../../../src/shared/kodu" -import VSCodeButtonLink from "./VSCodeButtonLink" interface AnnouncementProps { version: string @@ -33,23 +31,6 @@ const Announcement = ({ version, hideAnnouncement, apiConfiguration, vscodeUriSc
    -
  • - Excited to announce that we've partnered with Anthropic and are offering $20 free credits to - help users get the most out of Claude Dev with increased rate limits and prompt caching! Stay tuned - for some exciting updates like easier billing, voice mode and one click deployment! - {apiConfiguration?.koduApiKey === undefined && ( - - Claim $20 Credits on Kodu - - )} -
  • Added "Always allow read-only operations" setting to let Claude read files and view directories without needing to approve (off by default). diff --git a/webview-ui/src/components/ApiOptions.tsx b/webview-ui/src/components/ApiOptions.tsx index c54517d..f925169 100644 --- a/webview-ui/src/components/ApiOptions.tsx +++ b/webview-ui/src/components/ApiOptions.tsx @@ -1,6 +1,5 @@ import { VSCodeDropdown, VSCodeLink, VSCodeOption, VSCodeTextField } from "@vscode/webview-ui-toolkit/react" -import React, { useCallback, useEffect, useMemo, useState } from "react" -import { useEvent } from "react-use" +import React, { useMemo } from "react" import { ApiConfiguration, ApiModelId, @@ -9,26 +8,18 @@ import { anthropicModels, bedrockDefaultModelId, bedrockModels, - koduDefaultModelId, - koduModels, openRouterDefaultModelId, openRouterModels, } from "../../../src/shared/api" -import { ExtensionMessage } from "../../../src/shared/ExtensionMessage" -import { getKoduAddCreditsUrl, getKoduHomepageUrl, getKoduSignInUrl } from "../../../src/shared/kodu" -import { vscode } from "../utils/vscode" -import VSCodeButtonLink from "./VSCodeButtonLink" import { useExtensionState } from "../context/ExtensionStateContext" interface ApiOptionsProps { showModelOptions: boolean apiErrorMessage?: string - setDidAuthKodu?: React.Dispatch> } -const ApiOptions: React.FC = ({ showModelOptions, apiErrorMessage, setDidAuthKodu }) => { - const { apiConfiguration, setApiConfiguration, koduCredits, uriScheme } = useExtensionState() - const [, setDidFetchKoduCredits] = useState(false) +const ApiOptions: React.FC = ({ showModelOptions, apiErrorMessage }) => { + const { apiConfiguration, setApiConfiguration } = useExtensionState() const handleInputChange = (field: keyof ApiConfiguration) => (event: any) => { setApiConfiguration({ ...apiConfiguration, [field]: event.target.value }) } @@ -69,27 +60,6 @@ const ApiOptions: React.FC = ({ showModelOptions, apiErrorMessa ) } - useEffect(() => { - if (selectedProvider === "kodu" && apiConfiguration?.koduApiKey && koduCredits === undefined) { - setDidFetchKoduCredits(false) - vscode.postMessage({ type: "fetchKoduCredits" }) - } - }, [selectedProvider, apiConfiguration?.koduApiKey, koduCredits]) - - const handleMessage = useCallback((e: MessageEvent) => { - const message: ExtensionMessage = e.data - switch (message.type) { - case "action": - switch (message.action) { - case "koduCreditsFetched": - setDidFetchKoduCredits(true) - break - } - break - } - }, []) - useEvent("message", handleMessage) - return (
    @@ -97,7 +67,6 @@ const ApiOptions: React.FC = ({ showModelOptions, apiErrorMessa API Provider - Kodu Anthropic AWS Bedrock OpenRouter @@ -154,56 +123,6 @@ const ApiOptions: React.FC = ({ showModelOptions, apiErrorMessa
    )} - {selectedProvider === "kodu" && ( -
    - {apiConfiguration?.koduApiKey !== undefined ? ( - <> -
    - - Signed in as {apiConfiguration?.koduEmail || "Unknown"} - {" "} - vscode.postMessage({ type: "didClickKoduSignOut" })}> - (sign out?) - -
    -
    - Credits remaining:{" "} - - {formatPrice(koduCredits || 0)} - -
    - - Add Credits - - - ) : ( -
    - setDidAuthKodu?.(true)}> - Sign in to Kodu - -
    - )} -

    - Kodu is recommended for its high rate limits and access to the latest features like prompt - caching. - - Learn more about Kodu here. - -

    -
    - )} - {selectedProvider === "bedrock" && (
    = ({ showModelOptions, apiErrorMessa {selectedProvider === "anthropic" && createDropdown(anthropicModels)} {selectedProvider === "openrouter" && createDropdown(openRouterModels)} {selectedProvider === "bedrock" && createDropdown(bedrockModels)} - {selectedProvider === "kodu" && createDropdown(koduModels)}
    @@ -393,8 +311,6 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration) { return getProviderData(openRouterModels, openRouterDefaultModelId) case "bedrock": return getProviderData(bedrockModels, bedrockDefaultModelId) - case "kodu": - return getProviderData(koduModels, koduDefaultModelId) default: return getProviderData(anthropicModels, anthropicDefaultModelId) } diff --git a/webview-ui/src/components/ChatRow.tsx b/webview-ui/src/components/ChatRow.tsx index aeb09e9..c440f4d 100644 --- a/webview-ui/src/components/ChatRow.tsx +++ b/webview-ui/src/components/ChatRow.tsx @@ -281,7 +281,7 @@ const ChatRow: React.FC = ({

    {apiRequestFailedMessage}

    - {apiProvider === "kodu" && ( + {/* {apiProvider === "kodu" && (
    = ({ .
    - )} + )} */} )} diff --git a/webview-ui/src/components/ChatView.tsx b/webview-ui/src/components/ChatView.tsx index c8803f0..e62c636 100644 --- a/webview-ui/src/components/ChatView.tsx +++ b/webview-ui/src/components/ChatView.tsx @@ -14,7 +14,6 @@ import { vscode } from "../utils/vscode" import Announcement from "./Announcement" import ChatRow from "./ChatRow" import HistoryPreview from "./HistoryPreview" -import KoduPromo from "./KoduPromo" import TaskHeader from "./TaskHeader" import Thumbnails from "./Thumbnails" @@ -44,8 +43,6 @@ const ChatView = ({ themeName: vscodeThemeName, apiConfiguration, uriScheme, - shouldShowKoduPromo, - koduCredits, } = useExtensionState() //const task = messages.length > 0 ? (messages[0].say === "task" ? messages[0] : undefined) : undefined) : undefined @@ -483,7 +480,6 @@ const ChatView = ({ totalCost={apiMetrics.totalCost} onClose={handleTaskCloseButtonClick} isHidden={isHidden} - koduCredits={koduCredits} vscodeUriScheme={uriScheme} apiProvider={apiConfiguration?.apiProvider} /> @@ -497,9 +493,6 @@ const ChatView = ({ vscodeUriScheme={uriScheme} /> )} - {apiConfiguration?.koduApiKey === undefined && !showAnnouncement && shouldShowKoduPromo && ( - - )}
    0 ? undefined : 1 }}>

    What can I do for you?

    diff --git a/webview-ui/src/components/KoduPromo.tsx b/webview-ui/src/components/KoduPromo.tsx deleted file mode 100644 index 81141ca..0000000 --- a/webview-ui/src/components/KoduPromo.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import React from "react" -import { getKoduSignInUrl } from "../../../src/shared/kodu" -import { vscode } from "../utils/vscode" -import { useExtensionState } from "../context/ExtensionStateContext" - -interface KoduPromoProps { - style?: React.CSSProperties -} - -const KoduPromo: React.FC = ({ style }) => { - const { uriScheme } = useExtensionState() - - function onClose() { - vscode.postMessage({ type: "didDismissKoduPromo" }) - } - - return ( -

    -
    - - - Claim $20 free credits from Kodu - - -
    -
    - ) -} - -export default KoduPromo diff --git a/webview-ui/src/components/TaskHeader.tsx b/webview-ui/src/components/TaskHeader.tsx index 89d5bc1..4480336 100644 --- a/webview-ui/src/components/TaskHeader.tsx +++ b/webview-ui/src/components/TaskHeader.tsx @@ -1,12 +1,10 @@ -import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react" +import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" import React, { useEffect, useRef, useState } from "react" import { useWindowSize } from "react-use" +import { ApiProvider } from "../../../src/shared/api" import { ClaudeMessage } from "../../../src/shared/ExtensionMessage" import { vscode } from "../utils/vscode" import Thumbnails from "./Thumbnails" -import { formatPrice } from "./ApiOptions" -import { getKoduAddCreditsUrl } from "../../../src/shared/kodu" -import { ApiProvider } from "../../../src/shared/api" interface TaskHeaderProps { task: ClaudeMessage @@ -18,7 +16,6 @@ interface TaskHeaderProps { totalCost: number onClose: () => void isHidden: boolean - koduCredits?: number vscodeUriScheme?: string apiProvider?: ApiProvider } @@ -33,7 +30,6 @@ const TaskHeader: React.FC = ({ totalCost, onClose, isHidden, - koduCredits, vscodeUriScheme, apiProvider, }) => { @@ -258,7 +254,7 @@ const TaskHeader: React.FC = ({
    - {apiProvider === "kodu" && ( + {/* {apiProvider === "kodu" && (
    = ({ )}
    - )} + )} */} ) } diff --git a/webview-ui/src/components/WelcomeView.tsx b/webview-ui/src/components/WelcomeView.tsx index 212f69b..cb20f58 100644 --- a/webview-ui/src/components/WelcomeView.tsx +++ b/webview-ui/src/components/WelcomeView.tsx @@ -1,17 +1,14 @@ import { VSCodeButton, VSCodeLink } from "@vscode/webview-ui-toolkit/react" import React, { useEffect, useState } from "react" -import { getKoduSignInUrl } from "../../../src/shared/kodu" import { useExtensionState } from "../context/ExtensionStateContext" import { validateApiConfiguration } from "../utils/validate" import { vscode } from "../utils/vscode" import ApiOptions from "./ApiOptions" -interface WelcomeViewProps { - setDidAuthKoduFromWelcome: React.Dispatch> -} +interface WelcomeViewProps {} -const WelcomeView: React.FC = ({ setDidAuthKoduFromWelcome }) => { - const { apiConfiguration, uriScheme } = useExtensionState() +const WelcomeView: React.FC = () => { + const { apiConfiguration } = useExtensionState() const [apiErrorMessage, setApiErrorMessage] = useState(undefined) @@ -41,39 +38,11 @@ const WelcomeView: React.FC = ({ setDidAuthKoduFromWelcome }) To get started, this extension needs an API provider for Claude 3.5 Sonnet. -
    - - - Explore Claude's capabilities with $20 free credits from{" "} - - Kodu - - -
    -
    - - {apiConfiguration?.apiProvider !== "kodu" && ( - - Let's go! - - )} + + + Let's go! +
    ) diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 2f2882c..60fda79 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -20,7 +20,6 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode claudeMessages: [], taskHistory: [], shouldShowAnnouncement: false, - shouldShowKoduPromo: false, }) const [didHydrateState, setDidHydrateState] = useState(false) @@ -30,14 +29,6 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setState(message.state) setDidHydrateState(true) } - if (message.type === "action" && message.action) { - switch (message.action) { - case "koduCreditsFetched": - // special case where we only want to update one part of state in case user is in the middle of modifying settings - setState((prevState) => ({ ...prevState, koduCredits: message.state?.koduCredits })) - break - } - } }, []) useEvent("message", handleMessage) diff --git a/webview-ui/src/utils/validate.ts b/webview-ui/src/utils/validate.ts index a7ca9ab..288ebd1 100644 --- a/webview-ui/src/utils/validate.ts +++ b/webview-ui/src/utils/validate.ts @@ -18,11 +18,6 @@ export function validateApiConfiguration(apiConfiguration?: ApiConfiguration): s return "You must provide a valid API key or choose a different provider." } break - case "kodu": - if (!apiConfiguration.koduApiKey) { - return "You must sign in to Kodu to use it as an API provider." - } - break } } return undefined