mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-06 08:18:39 +00:00
feat: add Claude Code provider for local CLI integration
- Add new provider that executes local claude CLI tool - Support streaming responses from CLI JSON output - Add configuration UI for setting CLI path - Include all necessary type definitions and models - Add English translations for new provider This allows users to use Claude models through a locally installed command-line tool instead of API endpoints.
This commit is contained in:
parent
72cb248ef8
commit
0393752e2d
17 changed files with 432 additions and 5 deletions
|
|
@ -3,6 +3,10 @@ import type { Socket } from "net"
|
|||
|
||||
import type { RooCodeSettings } from "./global-settings.js"
|
||||
import type { ProviderSettingsEntry, ProviderSettings } from "./provider-settings.js"
|
||||
|
||||
// ApiHandlerOptions
|
||||
|
||||
export type ApiHandlerOptions = Omit<ProviderSettings, "apiProvider">
|
||||
import type { ClineMessage, TokenUsage } from "./message.js"
|
||||
import type { ToolUsage, ToolName } from "./tool.js"
|
||||
import type { IpcMessage, IpcServerEvents, IsSubtask } from "./ipc.js"
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import { codebaseIndexProviderSchema } from "./codebase-index.js"
|
|||
|
||||
export const providerNames = [
|
||||
"anthropic",
|
||||
"claude-code",
|
||||
"glama",
|
||||
"openrouter",
|
||||
"bedrock",
|
||||
|
|
@ -76,6 +77,10 @@ const anthropicSchema = apiModelIdProviderModelSchema.extend({
|
|||
anthropicUseAuthToken: z.boolean().optional(),
|
||||
})
|
||||
|
||||
const claudeCodeSchema = apiModelIdProviderModelSchema.extend({
|
||||
claudeCodePath: z.string().optional(),
|
||||
})
|
||||
|
||||
const glamaSchema = baseProviderSettingsSchema.extend({
|
||||
glamaModelId: z.string().optional(),
|
||||
glamaApiKey: z.string().optional(),
|
||||
|
|
@ -208,6 +213,7 @@ const defaultSchema = z.object({
|
|||
|
||||
export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProvider", [
|
||||
anthropicSchema.merge(z.object({ apiProvider: z.literal("anthropic") })),
|
||||
claudeCodeSchema.merge(z.object({ apiProvider: z.literal("claude-code") })),
|
||||
glamaSchema.merge(z.object({ apiProvider: z.literal("glama") })),
|
||||
openRouterSchema.merge(z.object({ apiProvider: z.literal("openrouter") })),
|
||||
bedrockSchema.merge(z.object({ apiProvider: z.literal("bedrock") })),
|
||||
|
|
@ -234,6 +240,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
|
|||
export const providerSettingsSchema = z.object({
|
||||
apiProvider: providerNamesSchema.optional(),
|
||||
...anthropicSchema.shape,
|
||||
...claudeCodeSchema.shape,
|
||||
...glamaSchema.shape,
|
||||
...openRouterSchema.shape,
|
||||
...bedrockSchema.shape,
|
||||
|
|
|
|||
13
packages/types/src/providers/claude-code.ts
Normal file
13
packages/types/src/providers/claude-code.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import type { ModelInfo } from "../model.js"
|
||||
import { anthropicModels } from "./anthropic.js"
|
||||
|
||||
// Claude Code
|
||||
export type ClaudeCodeModelId = keyof typeof claudeCodeModels
|
||||
export const claudeCodeDefaultModelId: ClaudeCodeModelId = "claude-sonnet-4-20250514"
|
||||
export const claudeCodeModels = {
|
||||
"claude-sonnet-4-20250514": anthropicModels["claude-sonnet-4-20250514"],
|
||||
"claude-opus-4-20250514": anthropicModels["claude-opus-4-20250514"],
|
||||
"claude-3-7-sonnet-20250219": anthropicModels["claude-3-7-sonnet-20250219"],
|
||||
"claude-3-5-sonnet-20241022": anthropicModels["claude-3-5-sonnet-20241022"],
|
||||
"claude-3-5-haiku-20241022": anthropicModels["claude-3-5-haiku-20241022"],
|
||||
} as const satisfies Record<string, ModelInfo>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
export * from "./anthropic.js"
|
||||
export * from "./bedrock.js"
|
||||
export * from "./chutes.js"
|
||||
export * from "./claude-code.js"
|
||||
export * from "./deepseek.js"
|
||||
export * from "./gemini.js"
|
||||
export * from "./glama.js"
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import {
|
|||
GroqHandler,
|
||||
ChutesHandler,
|
||||
LiteLLMHandler,
|
||||
ClaudeCodeHandler,
|
||||
} from "./providers"
|
||||
|
||||
export interface SingleCompletionHandler {
|
||||
|
|
@ -64,6 +65,8 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
|
|||
switch (apiProvider) {
|
||||
case "anthropic":
|
||||
return new AnthropicHandler(options)
|
||||
case "claude-code":
|
||||
return new ClaudeCodeHandler(options)
|
||||
case "glama":
|
||||
return new GlamaHandler(options)
|
||||
case "openrouter":
|
||||
|
|
|
|||
168
src/api/providers/claude-code.ts
Normal file
168
src/api/providers/claude-code.ts
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
import type { Anthropic } from "@anthropic-ai/sdk"
|
||||
import {
|
||||
type ApiHandlerOptions,
|
||||
claudeCodeDefaultModelId,
|
||||
type ClaudeCodeModelId,
|
||||
claudeCodeModels,
|
||||
} from "@roo-code/types"
|
||||
import { type ApiHandler } from ".."
|
||||
import { ApiStreamUsageChunk, type ApiStream } from "../transform/stream"
|
||||
import { runClaudeCode } from "../../integrations/claude-code/run"
|
||||
import { ClaudeCodeMessage } from "../../integrations/claude-code/types"
|
||||
import { BaseProvider } from "./base-provider"
|
||||
|
||||
export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
|
||||
private options: ApiHandlerOptions
|
||||
|
||||
constructor(options: ApiHandlerOptions) {
|
||||
super()
|
||||
this.options = options
|
||||
}
|
||||
|
||||
override async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
|
||||
const claudeProcess = runClaudeCode({
|
||||
systemPrompt,
|
||||
messages,
|
||||
path: this.options.claudeCodePath,
|
||||
modelId: this.getModel().id,
|
||||
})
|
||||
|
||||
const dataQueue: string[] = []
|
||||
let processError = null
|
||||
let errorOutput = ""
|
||||
let exitCode: number | null = null
|
||||
|
||||
claudeProcess.stdout.on("data", (data) => {
|
||||
const output = data.toString()
|
||||
const lines = output.split("\n").filter((line: string) => line.trim() !== "")
|
||||
|
||||
for (const line of lines) {
|
||||
dataQueue.push(line)
|
||||
}
|
||||
})
|
||||
|
||||
claudeProcess.stderr.on("data", (data) => {
|
||||
errorOutput += data.toString()
|
||||
})
|
||||
|
||||
claudeProcess.on("close", (code) => {
|
||||
exitCode = code
|
||||
})
|
||||
|
||||
claudeProcess.on("error", (error) => {
|
||||
processError = error
|
||||
})
|
||||
|
||||
// Usage is included with assistant messages,
|
||||
// but cost is included in the result chunk
|
||||
let usage: ApiStreamUsageChunk = {
|
||||
type: "usage",
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
cacheReadTokens: 0,
|
||||
cacheWriteTokens: 0,
|
||||
}
|
||||
|
||||
while (exitCode !== 0 || dataQueue.length > 0) {
|
||||
if (dataQueue.length === 0) {
|
||||
await new Promise((resolve) => setImmediate(resolve))
|
||||
}
|
||||
|
||||
if (exitCode !== null && exitCode !== 0) {
|
||||
throw new Error(
|
||||
`Claude Code process exited with code ${exitCode}.${errorOutput ? ` Error output: ${errorOutput.trim()}` : ""}`,
|
||||
)
|
||||
}
|
||||
|
||||
const data = dataQueue.shift()
|
||||
if (!data) {
|
||||
continue
|
||||
}
|
||||
|
||||
const chunk = this.attemptParseChunk(data)
|
||||
|
||||
if (!chunk) {
|
||||
yield {
|
||||
type: "text",
|
||||
text: data || "",
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if (chunk.type === "system" && chunk.subtype === "init") {
|
||||
continue
|
||||
}
|
||||
|
||||
if (chunk.type === "assistant" && "message" in chunk) {
|
||||
const message = chunk.message
|
||||
|
||||
if (message.stop_reason !== null && message.stop_reason !== "tool_use") {
|
||||
const errorMessage =
|
||||
message.content[0]?.text || `Claude Code stopped with reason: ${message.stop_reason}`
|
||||
|
||||
if (errorMessage.includes("Invalid model name")) {
|
||||
throw new Error(
|
||||
errorMessage +
|
||||
`\n\nAPI keys and subscription plans allow different models. Make sure the selected model is included in your plan.`,
|
||||
)
|
||||
}
|
||||
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
for (const content of message.content) {
|
||||
if (content.type === "text") {
|
||||
yield {
|
||||
type: "text",
|
||||
text: content.text,
|
||||
}
|
||||
} else {
|
||||
console.warn("Unsupported content type:", content.type)
|
||||
}
|
||||
}
|
||||
|
||||
usage.inputTokens += message.usage.input_tokens
|
||||
usage.outputTokens += message.usage.output_tokens
|
||||
usage.cacheReadTokens = (usage.cacheReadTokens || 0) + (message.usage.cache_read_input_tokens || 0)
|
||||
usage.cacheWriteTokens =
|
||||
(usage.cacheWriteTokens || 0) + (message.usage.cache_creation_input_tokens || 0)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if (chunk.type === "result" && "result" in chunk) {
|
||||
usage.totalCost = chunk.cost_usd || 0
|
||||
|
||||
yield usage
|
||||
}
|
||||
|
||||
if (processError) {
|
||||
throw processError
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getModel() {
|
||||
const modelId = this.options.apiModelId
|
||||
if (modelId && modelId in claudeCodeModels) {
|
||||
const id = modelId as ClaudeCodeModelId
|
||||
return { id, info: claudeCodeModels[id] }
|
||||
}
|
||||
|
||||
return {
|
||||
id: claudeCodeDefaultModelId,
|
||||
info: claudeCodeModels[claudeCodeDefaultModelId],
|
||||
}
|
||||
}
|
||||
|
||||
// TOOD: Validate instead of parsing
|
||||
private attemptParseChunk(data: string): ClaudeCodeMessage | null {
|
||||
try {
|
||||
return JSON.parse(data)
|
||||
} catch (error) {
|
||||
console.error("Error parsing chunk:", error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ export { AnthropicVertexHandler } from "./anthropic-vertex"
|
|||
export { AnthropicHandler } from "./anthropic"
|
||||
export { AwsBedrockHandler } from "./bedrock"
|
||||
export { ChutesHandler } from "./chutes"
|
||||
export { ClaudeCodeHandler } from "./claude-code"
|
||||
export { DeepSeekHandler } from "./deepseek"
|
||||
export { FakeAIHandler } from "./fake-ai"
|
||||
export { GeminiHandler } from "./gemini"
|
||||
|
|
|
|||
64
src/api/retry.ts
Normal file
64
src/api/retry.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import { Anthropic } from "@anthropic-ai/sdk"
|
||||
import { ApiStream, ApiStreamError } from "./transform/stream"
|
||||
import delay from "delay"
|
||||
|
||||
const RETRIABLE_STATUS_CODES = [408, 429, 500, 502, 503, 504]
|
||||
const MAX_RETRIES = 5
|
||||
const INITIAL_DELAY_MS = 2000
|
||||
|
||||
// `withRetry` is a decorator that adds retry logic to a method that returns an async generator.
|
||||
// It will retry the method if it fails with a retriable error.
|
||||
// It uses exponential backoff with jitter to delay between retries.
|
||||
export function withRetry<T extends (...args: any[]) => ApiStream>(
|
||||
options: {
|
||||
maxRetries?: number
|
||||
baseDelay?: number
|
||||
maxDelay?: number
|
||||
} = {},
|
||||
) {
|
||||
const { maxRetries = MAX_RETRIES, baseDelay = INITIAL_DELAY_MS } = options
|
||||
|
||||
return function (
|
||||
_target: T,
|
||||
_context: ClassMethodDecoratorContext<unknown, T>,
|
||||
): (this: unknown, ...args: Parameters<T>) => ApiStream {
|
||||
const originalMethod = _target
|
||||
|
||||
return async function* (this: unknown, ...args: Parameters<T>): ApiStream {
|
||||
let lastError: Error | undefined
|
||||
for (let i = 0; i < maxRetries; i++) {
|
||||
try {
|
||||
yield* originalMethod.apply(this, args)
|
||||
return
|
||||
} catch (error: any) {
|
||||
lastError = error
|
||||
const isRetriable =
|
||||
error instanceof Anthropic.APIError &&
|
||||
error.status &&
|
||||
RETRIABLE_STATUS_CODES.includes(error.status)
|
||||
|
||||
if (!isRetriable) {
|
||||
throw error
|
||||
}
|
||||
|
||||
const exponentialBackoff = Math.pow(2, i)
|
||||
const jitter = Math.random()
|
||||
const delayMs = Math.min(
|
||||
options.maxDelay || Infinity,
|
||||
baseDelay * exponentialBackoff * (1 + jitter),
|
||||
)
|
||||
|
||||
await delay(delayMs)
|
||||
}
|
||||
}
|
||||
|
||||
const error: ApiStreamError = {
|
||||
type: "error",
|
||||
error: "Retries exhausted",
|
||||
message: lastError!.message,
|
||||
}
|
||||
|
||||
yield error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,12 @@
|
|||
export type ApiStream = AsyncGenerator<ApiStreamChunk>
|
||||
|
||||
export type ApiStreamChunk = ApiStreamTextChunk | ApiStreamUsageChunk | ApiStreamReasoningChunk
|
||||
export type ApiStreamChunk = ApiStreamTextChunk | ApiStreamUsageChunk | ApiStreamReasoningChunk | ApiStreamError
|
||||
|
||||
export interface ApiStreamError {
|
||||
type: "error"
|
||||
error: string
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface ApiStreamTextChunk {
|
||||
type: "text"
|
||||
|
|
|
|||
|
|
@ -103,5 +103,14 @@
|
|||
"organization_mismatch": "You must be authenticated with your organization's Roo Code Cloud account.",
|
||||
"verification_failed": "Unable to verify organization authentication."
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"providers": {
|
||||
"claudeCode": {
|
||||
"pathLabel": "Claude Code Path",
|
||||
"description": "Optional path to your Claude Code CLI. Defaults to 'claude' if not set.",
|
||||
"placeholder": "Default: claude"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
44
src/integrations/claude-code/run.ts
Normal file
44
src/integrations/claude-code/run.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import * as vscode from "vscode"
|
||||
import Anthropic from "@anthropic-ai/sdk"
|
||||
import { execa } from "execa"
|
||||
|
||||
export function runClaudeCode({
|
||||
systemPrompt,
|
||||
messages,
|
||||
path,
|
||||
modelId,
|
||||
}: {
|
||||
systemPrompt: string
|
||||
messages: Anthropic.Messages.MessageParam[]
|
||||
path?: string
|
||||
modelId?: string
|
||||
}) {
|
||||
const claudePath = path || "claude"
|
||||
|
||||
// TODO: Is it worh using sessions? Where do we store the session ID?
|
||||
const args = [
|
||||
"-p",
|
||||
JSON.stringify(messages),
|
||||
"--system-prompt",
|
||||
systemPrompt,
|
||||
"--verbose",
|
||||
"--output-format",
|
||||
"stream-json",
|
||||
// Cline will handle recursive calls
|
||||
"--max-turns",
|
||||
"1",
|
||||
]
|
||||
|
||||
if (modelId) {
|
||||
args.push("--model", modelId)
|
||||
}
|
||||
|
||||
const cwd = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)
|
||||
return execa(claudePath, args, {
|
||||
stdin: "ignore",
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
env: process.env,
|
||||
cwd,
|
||||
})
|
||||
}
|
||||
52
src/integrations/claude-code/types.ts
Normal file
52
src/integrations/claude-code/types.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
type InitMessage = {
|
||||
type: "system"
|
||||
subtype: "init"
|
||||
session_id: string
|
||||
tools: string[]
|
||||
mcp_servers: string[]
|
||||
}
|
||||
|
||||
type ClaudeCodeContent = {
|
||||
type: "text"
|
||||
text: string
|
||||
}
|
||||
|
||||
type AssistantMessage = {
|
||||
type: "assistant"
|
||||
message: {
|
||||
id: string
|
||||
type: "message"
|
||||
role: "assistant"
|
||||
model: string
|
||||
content: ClaudeCodeContent[]
|
||||
stop_reason: null
|
||||
stop_sequence: null
|
||||
usage: {
|
||||
input_tokens: number
|
||||
cache_creation_input_tokens?: number
|
||||
cache_read_input_tokens?: number
|
||||
output_tokens: number
|
||||
service_tier: "standard"
|
||||
}
|
||||
}
|
||||
session_id: string
|
||||
}
|
||||
|
||||
type ErrorMessage = {
|
||||
type: "error"
|
||||
}
|
||||
|
||||
type ResultMessage = {
|
||||
type: "result"
|
||||
subtype: "success"
|
||||
cost_usd: number
|
||||
is_error: boolean
|
||||
duration_ms: number
|
||||
duration_api_ms: number
|
||||
num_turns: number
|
||||
result: string
|
||||
total_cost: number
|
||||
session_id: string
|
||||
}
|
||||
|
||||
export type ClaudeCodeMessage = InitMessage | AssistantMessage | ErrorMessage | ResultMessage
|
||||
|
|
@ -1,8 +1,12 @@
|
|||
import { type ModelInfo, type ProviderSettings, ANTHROPIC_DEFAULT_MAX_TOKENS } from "@roo-code/types"
|
||||
import {
|
||||
type ModelInfo,
|
||||
type ProviderSettings,
|
||||
ANTHROPIC_DEFAULT_MAX_TOKENS,
|
||||
type ApiHandlerOptions,
|
||||
} from "@roo-code/types"
|
||||
|
||||
// ApiHandlerOptions
|
||||
|
||||
export type ApiHandlerOptions = Omit<ProviderSettings, "apiProvider">
|
||||
// Re-export ApiHandlerOptions for backward compatibility
|
||||
export type { ApiHandlerOptions }
|
||||
|
||||
// RouterName
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {
|
|||
litellmDefaultModelId,
|
||||
openAiNativeDefaultModelId,
|
||||
anthropicDefaultModelId,
|
||||
claudeCodeDefaultModelId,
|
||||
geminiDefaultModelId,
|
||||
deepSeekDefaultModelId,
|
||||
mistralDefaultModelId,
|
||||
|
|
@ -36,6 +37,7 @@ import {
|
|||
Anthropic,
|
||||
Bedrock,
|
||||
Chutes,
|
||||
ClaudeCode,
|
||||
DeepSeek,
|
||||
Gemini,
|
||||
Glama,
|
||||
|
|
@ -254,6 +256,7 @@ const ApiOptions = ({
|
|||
requesty: { field: "requestyModelId", default: requestyDefaultModelId },
|
||||
litellm: { field: "litellmModelId", default: litellmDefaultModelId },
|
||||
anthropic: { field: "apiModelId", default: anthropicDefaultModelId },
|
||||
"claude-code": { field: "apiModelId", default: claudeCodeDefaultModelId },
|
||||
"openai-native": { field: "apiModelId", default: openAiNativeDefaultModelId },
|
||||
gemini: { field: "apiModelId", default: geminiDefaultModelId },
|
||||
deepseek: { field: "apiModelId", default: deepSeekDefaultModelId },
|
||||
|
|
@ -383,6 +386,10 @@ const ApiOptions = ({
|
|||
<Anthropic apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
|
||||
)}
|
||||
|
||||
{selectedProvider === "claude-code" && (
|
||||
<ClaudeCode apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
|
||||
)}
|
||||
|
||||
{selectedProvider === "openai-native" && (
|
||||
<OpenAI apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import {
|
|||
type ModelInfo,
|
||||
anthropicModels,
|
||||
bedrockModels,
|
||||
claudeCodeModels,
|
||||
deepSeekModels,
|
||||
geminiModels,
|
||||
mistralModels,
|
||||
|
|
@ -15,6 +16,7 @@ import {
|
|||
|
||||
export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, ModelInfo>>> = {
|
||||
anthropic: anthropicModels,
|
||||
"claude-code": claudeCodeModels,
|
||||
bedrock: bedrockModels,
|
||||
deepseek: deepSeekModels,
|
||||
gemini: geminiModels,
|
||||
|
|
@ -29,6 +31,7 @@ export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, Mod
|
|||
export const PROVIDERS = [
|
||||
{ value: "openrouter", label: "OpenRouter" },
|
||||
{ value: "anthropic", label: "Anthropic" },
|
||||
{ value: "claude-code", label: "Claude Code" },
|
||||
{ value: "gemini", label: "Google Gemini" },
|
||||
{ value: "deepseek", label: "DeepSeek" },
|
||||
{ value: "openai-native", label: "OpenAI" },
|
||||
|
|
|
|||
40
webview-ui/src/components/settings/providers/ClaudeCode.tsx
Normal file
40
webview-ui/src/components/settings/providers/ClaudeCode.tsx
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import React from "react"
|
||||
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
import { type ProviderSettings } from "@roo-code/types"
|
||||
import { useAppTranslation } from "@src/i18n/TranslationContext"
|
||||
|
||||
interface ClaudeCodeProps {
|
||||
apiConfiguration: ProviderSettings
|
||||
setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
|
||||
}
|
||||
|
||||
export const ClaudeCode: React.FC<ClaudeCodeProps> = ({ apiConfiguration, setApiConfigurationField }) => {
|
||||
const { t } = useAppTranslation()
|
||||
|
||||
const handleInputChange = (e: Event | React.FormEvent<HTMLElement>) => {
|
||||
const element = e.target as HTMLInputElement
|
||||
setApiConfigurationField("claudeCodePath", element.value)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<VSCodeTextField
|
||||
value={apiConfiguration?.claudeCodePath || ""}
|
||||
style={{ width: "100%", marginTop: 3 }}
|
||||
type="text"
|
||||
onInput={handleInputChange}
|
||||
placeholder={t("settings:providers.claudeCode.placeholder")}>
|
||||
{t("settings:providers.claudeCode.pathLabel")}
|
||||
</VSCodeTextField>
|
||||
|
||||
<p
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
marginTop: 3,
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
}}>
|
||||
{t("settings:providers.claudeCode.description")}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
export { Anthropic } from "./Anthropic"
|
||||
export { Bedrock } from "./Bedrock"
|
||||
export { Chutes } from "./Chutes"
|
||||
export { ClaudeCode } from "./ClaudeCode"
|
||||
export { DeepSeek } from "./DeepSeek"
|
||||
export { Gemini } from "./Gemini"
|
||||
export { Glama } from "./Glama"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue