Compare commits

...

6 Commits

Author SHA1 Message Date
BarreiroT 182fdfd18f Merge branch 'main' into apply-fix-all 2026-02-23 13:09:54 -08:00
BarreiroT 8db9943283 Fix all 2026-02-23 13:07:10 -08:00
BarreiroT 4fd2755714 Revert "Apply biome on all"
This reverts commit 35e1814b10.
2026-02-23 12:57:27 -08:00
BarreiroT c23d2973d8 Fix other issues 2026-02-19 17:55:07 -03:00
BarreiroT b5491997b4 Format all files in CI 2026-02-19 13:08:30 -03:00
BarreiroT 35e1814b10 Apply biome on all 2026-02-19 13:00:16 -03:00
374 changed files with 2218 additions and 2403 deletions
+1 -1
View File
@@ -211,7 +211,7 @@ export class ACPDiffViewProvider extends FileEditProvider {
* content via the ACP connection. Otherwise, it falls back to the
* FileEditProvider's local fs implementation.
*/
protected override async saveDocument(): Promise<Boolean> {
protected override async saveDocument(): Promise<boolean> {
// If we can't write files via ACP, fall back to FileEditProvider
if (!this.canWriteFile()) {
Logger.debug("[ACPDiffViewProvider] Client does not support fs.writeTextFile, falling back to local fs")
+3 -5
View File
@@ -111,7 +111,7 @@ class ACPEnvServiceClient implements EnvServiceClientInterface {
constructor(
_clientCapabilities: acp.ClientCapabilities | undefined,
_sessionIdResolver: SessionIdResolver,
version: string = "1.0.0",
version = "1.0.0",
) {
this.version = version
}
@@ -191,8 +191,6 @@ class ACPEnvServiceClient implements EnvServiceClientInterface {
* Most operations are stubs that will be implemented using ACP extension methods.
*/
class ACPWindowServiceClient implements WindowServiceClientInterface {
constructor(_clientCapabilities: acp.ClientCapabilities | undefined, _sessionIdResolver: SessionIdResolver) {}
async showTextDocument(request: proto.host.ShowTextDocumentRequest): Promise<proto.host.TextEditorInfo> {
// Next phase: Send ACP extension request to open document in the editor.
// This would tell the ACP client to open the specified file.
@@ -402,11 +400,11 @@ export class ACPHostBridgeClientProvider implements HostBridgeClientProvider {
clientCapabilities: acp.ClientCapabilities | undefined,
sessionIdResolver: SessionIdResolver,
cwdResolver: CwdResolver,
version: string = "1.0.0",
version = "1.0.0",
) {
this.workspaceClient = new ACPWorkspaceServiceClient(clientCapabilities, sessionIdResolver, cwdResolver)
this.envClient = new ACPEnvServiceClient(clientCapabilities, sessionIdResolver, version)
this.windowClient = new ACPWindowServiceClient(clientCapabilities, sessionIdResolver)
this.windowClient = new ACPWindowServiceClient()
this.diffClient = new ACPDiffServiceClient()
}
}
+2 -2
View File
@@ -829,7 +829,7 @@ export class ClineAgent implements acp.Agent {
await this.emitSessionUpdate(sessionId, {
sessionUpdate,
content: { type: "text", text: needsNewline ? "\n" + textDelta : textDelta },
content: { type: "text", text: needsNewline ? `\n${textDelta}` : textDelta },
})
}
@@ -1082,7 +1082,7 @@ export class ClineAgent implements acp.Agent {
// Use the permission handler callback pattern
return new Promise<acp.RequestPermissionResponse>((resolve) => {
this.permissionHandler!({ toolCall, options }, resolve)
this.permissionHandler?.({ toolCall, options }, resolve)
})
}
+7 -7
View File
@@ -983,13 +983,13 @@ describe("translateMessage - ask messages", () => {
// Should require permission
expect(result.requiresPermission).toBe(true)
expect(result.permissionRequest).toBeDefined()
expect(result.permissionRequest!.toolCall.toolCallId).toBe(call.toolCallId)
expect(result.permissionRequest?.toolCall.toolCallId).toBe(call.toolCallId)
// Should have standard permission options
expect(result.permissionRequest!.options).toHaveLength(3)
expect(result.permissionRequest!.options.map((o) => o.kind)).toContain("allow_once")
expect(result.permissionRequest!.options.map((o) => o.kind)).toContain("allow_always")
expect(result.permissionRequest!.options.map((o) => o.kind)).toContain("reject_once")
expect(result.permissionRequest?.options).toHaveLength(3)
expect(result.permissionRequest?.options.map((o) => o.kind)).toContain("allow_once")
expect(result.permissionRequest?.options.map((o) => o.kind)).toContain("allow_always")
expect(result.permissionRequest?.options.map((o) => o.kind)).toContain("reject_once")
// Should track pending tool call
expect(sessionState.pendingToolCalls.has(call.toolCallId)).toBe(true)
@@ -1087,8 +1087,8 @@ describe("translateMessage - ask messages", () => {
expect(result.requiresPermission).toBe(true)
// Browser actions have restricted options (no "always allow")
expect(result.permissionRequest!.options).toHaveLength(2)
expect(result.permissionRequest!.options.map((o) => o.kind)).not.toContain("allow_always")
expect(result.permissionRequest?.options).toHaveLength(2)
expect(result.permissionRequest?.options.map((o) => o.kind)).not.toContain("allow_always")
})
})
+2 -2
View File
@@ -233,7 +233,7 @@ function translateSayMessage(
if (message.text) {
updates.push({
sessionUpdate: "agent_message_chunk",
content: { type: "text", text: "\n" + message.text },
content: { type: "text", text: `\n${message.text}` },
})
}
break
@@ -622,7 +622,7 @@ function translateAskMessage(
if (message.text) {
updates.push({
sessionUpdate: "agent_message_chunk",
content: { type: "text", text: "\n" + message.text },
content: { type: "text", text: `\n${message.text}` },
})
}
break
+1 -1
View File
@@ -116,7 +116,7 @@ export function getPermissionOptionsForAskType(askType: ClineAsk): acp.Permissio
* @param askType - The original ClineAsk type that triggered the permission request
* @returns The translated result for Cline's handleWebviewAskResponse
*/
export function handlePermissionResponse(response: acp.RequestPermissionResponse, askType: ClineAsk): PermissionHandlerResult {
export function handlePermissionResponse(response: acp.RequestPermissionResponse, _askType: ClineAsk): PermissionHandlerResult {
// Check if cancelled
if (response.outcome.outcome === "cancelled") {
return {
+1 -1
View File
@@ -194,7 +194,7 @@ const errorTypes = ["api_req_failed", "mistake_limit_reached"]
/**
* Get button configuration based on message type and state
*/
export function getButtonConfig(message: ClineMessage | undefined, isStreaming: boolean = false): ButtonConfig {
export function getButtonConfig(message: ClineMessage | undefined, isStreaming = false): ButtonConfig {
if (!message) {
return BUTTON_CONFIGS.default
}
+38 -39
View File
@@ -145,7 +145,7 @@ export const AskPrompt: React.FC<AskPromptProps> = ({ onRespond }) => {
setTextInput((prev) => prev.slice(0, -1))
} else if (input && !key.ctrl && !key.meta) {
// Check if it's a number for option selection (only when no text typed yet)
const num = parseInt(input, 10)
const num = Number.parseInt(input, 10)
if (textInput === "" && !Number.isNaN(num) && num >= 1 && num <= parts.options.length) {
const selectedOption = parts.options[num - 1]
sendResponse("messageResponse", selectedOption)
@@ -401,43 +401,42 @@ function getCliMessagePrefixIcon(message: ClineMessage): string {
default:
return "❔"
}
} else {
switch (message.say) {
case "task":
return "📋"
case "error":
return "❌"
case "text":
return "💬"
case "reasoning":
return "🧠"
case "completion_result":
return "✅"
case "user_feedback":
return "👤"
case "command":
case "command_output":
return "⚙️"
case "tool":
return "🔧"
case "browser_action":
case "browser_action_launch":
case "browser_action_result":
return "🌐"
case "mcp_server_request_started":
case "mcp_server_response":
return "🔌"
case "api_req_started":
case "api_req_finished":
return "🔄"
case "checkpoint_created":
return "💾"
case "info":
return "️"
case "generate_explanation":
return "📝"
default:
return " "
}
}
switch (message.say) {
case "task":
return "📋"
case "error":
return "❌"
case "text":
return "💬"
case "reasoning":
return "🧠"
case "completion_result":
return "✅"
case "user_feedback":
return "👤"
case "command":
case "command_output":
return "⚙️"
case "tool":
return "🔧"
case "browser_action":
case "browser_action_launch":
case "browser_action_result":
return "🌐"
case "mcp_server_request_started":
case "mcp_server_response":
return "🔌"
case "api_req_started":
case "api_req_finished":
return "🔄"
case "checkpoint_created":
return "💾"
case "info":
return "️"
case "generate_explanation":
return "📝"
default:
return " "
}
}
+2 -2
View File
@@ -323,7 +323,7 @@ export const AuthView: React.FC<AuthViewProps> = ({ controller, onComplete, onEr
setErrorMessage(error instanceof Error ? error.message : String(error))
setStep("error")
}
}, [])
}, [controller])
// Start Cline auth flow
const startClineAuth = useCallback(async () => {
@@ -379,7 +379,7 @@ export const AuthView: React.FC<AuthViewProps> = ({ controller, onComplete, onEr
setStep("apikey")
}
},
[startOcaAuth, startOpenAiCodexAuth],
[startOpenAiCodexAuth],
)
const handleApiKeySubmit = useCallback(
+4 -4
View File
@@ -177,7 +177,7 @@ function getToolMainArg(_toolName: string, args: Record<string, unknown>): strin
// Command - truncate long commands
if (typeof args.command === "string") {
return args.command.length > 120 ? args.command.substring(0, 117) + "..." : args.command
return args.command.length > 120 ? `${args.command.substring(0, 117)}...` : args.command
}
// URL
@@ -221,7 +221,7 @@ const ToolCallText: React.FC<{
*/
function truncate(text: string, maxLength: number): string {
if (text.length <= maxLength) return text
return text.substring(0, maxLength - 3) + "..."
return `${text.substring(0, maxLength - 3)}...`
}
/**
@@ -245,7 +245,7 @@ export const ChatMessage: React.FC<ChatMessageProps> = ({ message, mode, isStrea
// User messages (task, user_feedback)
// If multi-line, extend background to full width for consistent appearance
if (say === "task" || say === "user_feedback") {
const content = "> " + (text || "")
const content = `> ${text || ""}`
const isMultiLine = content.includes("\n") || content.length > terminalWidth
if (isMultiLine) {
@@ -412,7 +412,7 @@ export const ChatMessage: React.FC<ChatMessageProps> = ({ message, mode, isStrea
: "tool: unknown"
let argsLines: string[] = []
if (parsed?.arguments && parsed.arguments.trim() && parsed.arguments !== "{}") {
if (parsed?.arguments?.trim() && parsed.arguments !== "{}") {
let formattedArgs = parsed.arguments
try {
formattedArgs = JSON.stringify(JSON.parse(parsed.arguments), null, 2)
+1 -1
View File
@@ -12,7 +12,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
import { ChatView } from "./ChatView"
// Helper to wait for async state updates
const delay = (ms: number = 60) => new Promise((resolve) => setTimeout(resolve, ms))
const delay = (ms = 60) => new Promise((resolve) => setTimeout(resolve, ms))
// Type for our exit mock function
type ExitMockFn = ReturnType<typeof vi.fn> & (() => void)
+11 -11
View File
@@ -486,7 +486,7 @@ export const ChatView: React.FC<ChatViewProps> = ({
if (taskState.mode && taskState.mode !== mode) {
setMode(taskState.mode as Mode)
}
}, [taskState.mode])
}, [taskState.mode, mode])
const toggleAutoApproveAll = useCallback(() => {
const newValue = !autoApproveAll
@@ -499,7 +499,7 @@ export const ChatView: React.FC<ChatViewProps> = ({
const stateManager = StateManager.get()
const providerKey = mode === "act" ? "actModeApiProvider" : "planModeApiProvider"
return (stateManager.getGlobalSettingsKey(providerKey) as string) || ""
}, [mode, activePanel])
}, [mode])
// Get model ID based on current mode and provider
// Different providers use different state keys (e.g., cline uses actModeOpenRouterModelId)
@@ -510,7 +510,7 @@ export const ChatView: React.FC<ChatViewProps> = ({
const stateManager = StateManager.get()
const modelKey = getProviderModelIdKey(provider as ApiProvider, mode)
return (stateManager.getGlobalSettingsKey(modelKey) as string) || getProviderDefaultModelId(provider as ApiProvider) || ""
}, [mode, provider, activePanel])
}, [mode, provider])
const toggleMode = useCallback(async () => {
const newMode: Mode = mode === "act" ? "plan" : "act"
@@ -549,7 +549,7 @@ export const ChatView: React.FC<ChatViewProps> = ({
if (ctrl) {
ctrl.postStateToWebview()
}
}, [ctrl, clearState, storageKey])
}, [ctrl, clearState, storageKey, setCursorPos, setTextInput])
const refs = useRef({
searchTimeout: null as NodeJS.Timeout | null,
@@ -644,10 +644,10 @@ export const ChatView: React.FC<ChatViewProps> = ({
const messages = taskState.clineMessages || []
// Refresh git diff stats when messages change (after file edits)
const lastMsg = messages[messages.length - 1]
const _lastMsg = messages[messages.length - 1]
useEffect(() => {
setGitDiffStats(getGitDiffStats(workspacePath))
}, [messages.length, lastMsg?.partial, lastMsg?.ts, workspacePath])
}, [workspacePath])
// Filter messages we want to display
const displayMessages = useMemo(() => {
@@ -819,7 +819,7 @@ export const ChatView: React.FC<ChatViewProps> = ({
// Controller may be disposed
}
},
[ctrl, pendingAsk, pastedTexts, storageKey],
[ctrl, pendingAsk, pastedTexts, storageKey, setCursorPos, setTextInput],
)
// Handle cancel/interrupt
@@ -895,7 +895,7 @@ export const ChatView: React.FC<ChatViewProps> = ({
break
}
},
[controller, taskController, sendAskResponse, pendingAsk, handleExit, handleCancel, clearViewAndResetTask],
[sendAskResponse, pendingAsk, handleExit, handleCancel, clearViewAndResetTask, ctrl, setCursorPos, setTextInput],
)
// Handle task submission (new task)
@@ -939,7 +939,7 @@ export const ChatView: React.FC<ChatViewProps> = ({
onError?.()
}
},
[ctrl, onError, pastedTexts, storageKey],
[ctrl, onError, pastedTexts, storageKey, setCursorPos, setTextInput],
)
// Auto-submit initial prompt if provided
@@ -998,7 +998,7 @@ export const ChatView: React.FC<ChatViewProps> = ({
autoSubmit()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []) // Only run once on mount
}, [controller, initialImages, initialPrompt, onError, taskController, taskId]) // Only run once on mount
// Search for files when in mention mode
useEffect(() => {
@@ -1051,7 +1051,7 @@ export const ChatView: React.FC<ChatViewProps> = ({
clearTimeout(r.searchTimeout)
}
}
}, [mentionInfo.inMentionMode, mentionInfo.query, workspacePath])
}, [mentionInfo.inMentionMode, mentionInfo.query, workspacePath, mentionInfo])
// Handle keyboard input
//
+1 -1
View File
@@ -120,7 +120,7 @@ export const CheckpointMenu: React.FC<CheckpointMenuProps> = ({ messages, onSele
// Quick number selection for checkpoints
if (stage === "checkpoint") {
const num = parseInt(input, 10)
const num = Number.parseInt(input, 10)
if (!Number.isNaN(num) && num >= 1 && num <= checkpoints.length) {
setSelectedCheckpoint(num - 1)
setStage("restoreType")
+1 -1
View File
@@ -309,7 +309,7 @@ export const ConfigView: React.FC<ConfigViewProps> = ({
const currentTabIndex = availableTabs.findIndex((t) => t.key === currentTab)
const targetIdx =
input >= "1" && input <= "5"
? Number.parseInt(input) - 1
? Number.parseInt(input, 10) - 1
: key.leftArrow
? (currentTabIndex - 1 + availableTabs.length) % availableTabs.length
: (currentTabIndex + 1) % availableTabs.length
+3 -3
View File
@@ -127,10 +127,10 @@ export function formatValue(value: unknown, maxLen = 50): string {
}
if (typeof value === "object") {
const json = JSON.stringify(value)
return json.length > maxLen ? json.slice(0, maxLen - 3) + "..." : json
return json.length > maxLen ? `${json.slice(0, maxLen - 3)}...` : json
}
const str = String(value)
return str.length > maxLen ? str.slice(0, maxLen - 3) + "..." : str
return str.length > maxLen ? `${str.slice(0, maxLen - 3)}...` : str
}
export function parseValue(input: string, type: ValueType): unknown {
@@ -337,7 +337,7 @@ export const SkillRow: React.FC<{
{skill.description && (
<Box marginLeft={4}>
<Text color="gray">
{skill.description.length > 60 ? skill.description.slice(0, 57) + "..." : skill.description}
{skill.description.length > 60 ? `${skill.description.slice(0, 57)}...` : skill.description}
</Text>
</Box>
)}
+2 -2
View File
@@ -20,11 +20,11 @@ interface FileMentionMenuProps {
/**
* Truncate path from the left if too long, keeping the filename visible
*/
function truncatePath(filePath: string, maxLength: number = 50): string {
function truncatePath(filePath: string, maxLength = 50): string {
if (filePath.length <= maxLength) {
return filePath
}
return "..." + filePath.slice(-(maxLength - 3))
return `...${filePath.slice(-(maxLength - 3))}`
}
export const FileMentionMenu: React.FC<FileMentionMenuProps> = ({
+1 -1
View File
@@ -115,7 +115,7 @@ const Header: React.FC<{
const isCompleted = completedCount === totalCount
const displayText = isCompleted ? "All tasks completed!" : currentTodo?.text || "To-Do list"
const truncatedText = displayText.length > 50 ? displayText.substring(0, 47) + "..." : displayText
const truncatedText = displayText.length > 50 ? `${displayText.substring(0, 47)}...` : displayText
return (
<Box flexDirection="row" gap={1}>
+1 -1
View File
@@ -98,7 +98,7 @@ export const HistoryPanelContent: React.FC<HistoryPanelContentProps> = ({ onClos
// Reset selection when search changes
useEffect(() => {
setSelectedIndex(0)
}, [searchQuery])
}, [])
const handleSelect = useCallback(
async (item: TaskHistoryItem) => {
+4 -4
View File
@@ -40,7 +40,7 @@ interface HistoryViewProps {
/**
* Format separator
*/
function formatSeparator(char: string = "─", width: number = 80): string {
function formatSeparator(char = "─", width = 80): string {
return char.repeat(Math.max(width, 10))
}
@@ -142,7 +142,7 @@ export const HistoryView: React.FC<HistoryViewProps> = ({
return (
<Box flexDirection="column">
<Text bold color="white">
{"📜 Task History (" + totalCount + " total)"}
{`📜 Task History (${totalCount} total)`}
</Text>
<Text color="gray">Use /j/k to navigate, Enter to select</Text>
@@ -161,7 +161,7 @@ export const HistoryView: React.FC<HistoryViewProps> = ({
<Text>No task history available.</Text>
) : (
<Box flexDirection="column">
{showUpIndicator && <Text color="gray">{" ↑ " + startIndex + " more above"}</Text>}
{showUpIndicator && <Text color="gray">{`${startIndex} more above`}</Text>}
{visibleTasks.map((task, index) => {
const actualIndex = startIndex + index
const isSelected = actualIndex === selectedIndex
@@ -197,7 +197,7 @@ export const HistoryView: React.FC<HistoryViewProps> = ({
</Box>
)
})}
{showDownIndicator && <Text color="gray">{" ↓ " + (pageItems.length - endIndex) + " more below"}</Text>}
{showDownIndicator && <Text color="gray">{`${pageItems.length - endIndex} more below`}</Text>}
</Box>
)}
+1 -1
View File
@@ -77,7 +77,7 @@ export const ImportView: React.FC<ImportViewProps> = ({ source, onComplete, onCa
}, [keys, selectedIndex, onComplete])
useInput(
(input, key) => {
(_input, key) => {
if (key.escape) {
if (step === "confirm" && keys.length > 1) {
setStep("select")
+1 -1
View File
@@ -63,7 +63,7 @@ export function SearchableList<T extends SearchableListItem>({
// Reset index when search changes
useEffect(() => {
setIndex(0)
}, [search])
}, [])
useInput(
(input, key) => {
+9 -6
View File
@@ -237,7 +237,7 @@ export const SettingsPanelContent: React.FC<SettingsPanelContentProps> = ({
"not configured",
)
// Refresh trigger to force re-reading model IDs from state
const [modelRefreshKey, setModelRefreshKey] = useState(0)
const [_modelRefreshKey, setModelRefreshKey] = useState(0)
const refreshModelIds = useCallback(() => setModelRefreshKey((k) => k + 1), [])
// OCA auth hook
@@ -273,7 +273,7 @@ export const SettingsPanelContent: React.FC<SettingsPanelContentProps> = ({
actModelId: actKey ? (stateManager.getGlobalSettingsKey(actKey) as string) || "" : "",
planModelId: planKey ? (stateManager.getGlobalSettingsKey(planKey) as string) || "" : "",
}
}, [modelRefreshKey, stateManager])
}, [stateManager])
// Toggle a feature setting
const toggleFeature = useCallback(
@@ -433,7 +433,7 @@ export const SettingsPanelContent: React.FC<SettingsPanelContentProps> = ({
return () => {
cancelled = true
}
}, [isWaitingForClineAuth, controller, fetchAccountInfo])
}, [isWaitingForClineAuth, controller, fetchAccountInfo, refreshModelIds])
// Build items list based on current tab
const items: ListItem[] = useMemo(() => {
@@ -945,7 +945,10 @@ export const SettingsPanelContent: React.FC<SettingsPanelContentProps> = ({
actReasoningEffort,
planReasoningEffort,
rebuildTaskApi,
setReasoningEffortForMode,
setReasoningEffortForMode, // Update telemetry providers to respect the new setting
controller,
handleTabChange,
provider,
])
// Handle completion of the Bedrock custom ARN flow (ARN + base model selected)
@@ -1096,7 +1099,7 @@ export const SettingsPanelContent: React.FC<SettingsPanelContentProps> = ({
setCodexAuthError(error instanceof Error ? error.message : String(error))
setIsWaitingForCodexAuth(false)
}
}, [controller])
}, [controller, refreshModelIds])
const handleProviderSelect = useCallback(
async (providerId: string) => {
@@ -1168,7 +1171,7 @@ export const SettingsPanelContent: React.FC<SettingsPanelContentProps> = ({
setIsPickingProvider(false)
}
},
[stateManager, startCodexAuth, handleClineLogin, startOcaAuth, isOcaAuthenticated, controller, refreshModelIds],
[stateManager, startCodexAuth, handleClineLogin, isOcaAuthenticated, controller, refreshModelIds],
)
// Handle API key submission after provider selection
+1 -1
View File
@@ -248,7 +248,7 @@ const SkillRow: React.FC<{ skill: SkillInfo; isSelected: boolean }> = ({ skill,
{skill.description && (
<Box marginLeft={4}>
<Text color="gray">
{skill.description.length > 60 ? skill.description.slice(0, 57) + "..." : skill.description}
{skill.description.length > 60 ? `${skill.description.slice(0, 57)}...` : skill.description}
</Text>
</Box>
)}
+1 -1
View File
@@ -24,7 +24,7 @@ export const SlashCommandMenu: React.FC<SlashCommandMenuProps> = ({ commands, se
if (maxLength <= 0) return ""
if (text.length <= maxLength) return text
if (maxLength <= 3) return text.slice(0, maxLength)
return text.slice(0, maxLength - 3) + "..."
return `${text.slice(0, maxLength - 3)}...`
}
if (commands.length === 0) {
+2 -2
View File
@@ -50,7 +50,7 @@ function formatNumber(num: number): string {
/**
* Create a progress bar for context window usage
*/
function createContextBar(used: number, total: number, width: number = 8): string {
function createContextBar(used: number, total: number, width = 8): string {
const ratio = Math.min(used / total, 1)
const filled = Math.round(ratio * width)
const empty = width - filled
@@ -76,7 +76,7 @@ export const StatusBar: React.FC<StatusBarProps> = ({
const contextBar = createContextBar(totalTokens, contextWindowSize)
// Format model ID for display (shorten if needed)
const displayModel = modelId.length > 20 ? modelId.substring(0, 17) + "..." : modelId
const displayModel = modelId.length > 20 ? `${modelId.substring(0, 17)}...` : modelId
return (
<Box flexDirection="column">
+1 -1
View File
@@ -92,7 +92,7 @@ export const TaskJsonView: React.FC<TaskJsonViewProps> = ({ taskId: _taskId, ver
outputtedMessages.current.add(message.ts)
}
}, [state.clineMessages, verbose])
}, [state.clineMessages, verbose, getRole])
// Handle task completion
useEffect(() => {
+2 -2
View File
@@ -72,7 +72,7 @@ export const WelcomeView: React.FC<WelcomeViewProps> = ({ onSubmit, onExit, cont
const providerKey = mode === "act" ? "actModeApiProvider" : "planModeApiProvider"
const currentProvider = stateManager.getGlobalSettingsKey(providerKey) as string
return currentProvider || "cline"
}, [controller])
}, [])
// Get model ID based on current mode and provider
// Different providers use different state keys (e.g., cline uses actModeOpenRouterModelId)
@@ -165,7 +165,7 @@ export const WelcomeView: React.FC<WelcomeViewProps> = ({ onSubmit, onExit, cont
clearTimeout(r.searchTimeout)
}
}
}, [mentionInfo.inMentionMode, mentionInfo.query, workspacePath])
}, [mentionInfo.inMentionMode, mentionInfo.query, workspacePath, mentionInfo])
useInput(
(input, key) => {
@@ -3,14 +3,9 @@
* Instead of rendering to a webview, this outputs to the terminal
*/
import type * as vscode from "vscode"
import { WebviewProvider } from "@/core/webview"
export class CliWebviewProvider extends WebviewProvider {
constructor(context: vscode.ExtensionContext) {
super(context)
}
override getWebviewUrl(path: string): string {
// CLI doesn't have webview URLs
return `file://${path}`
+1 -2
View File
@@ -79,7 +79,7 @@ export class CliDiffServiceClient implements DiffServiceClientInterface {
* CLI implementation of EnvService - handles environment operations
*/
export class CliEnvServiceClient implements EnvServiceClientInterface {
private clipboardContent: string = ""
private clipboardContent = ""
private getTelemetrySetting(): proto.host.Setting {
// Read from StateManager - defaults to ENABLED if not set or "unset"
@@ -182,7 +182,6 @@ export class CliWindowServiceClient implements WindowServiceClientInterface {
case proto.host.ShowMessageType.WARNING:
printWarning(message)
break
case proto.host.ShowMessageType.INFORMATION:
default:
printInfo(message)
break
+1 -1
View File
@@ -58,7 +58,7 @@ export const useCompletedAskMessages = () => {
*/
export const useLastCompletedAskMessage = () => {
const { state } = useTaskContext()
const processed = useProcessedMessages()
const _processed = useProcessedMessages()
const getLastCompletedAskMessage = useCallback((): ClineMessage | null => {
if (!state.clineMessages) {
+1 -1
View File
@@ -826,7 +826,7 @@ async function checkAnyProviderConfigured(): Promise<boolean> {
const config = stateManager.getApiConfiguration() as Record<string, unknown>
// Check Cline account (stored as "cline:clineAccountId" in secrets, loaded into config)
if (config["clineApiKey"] || config["cline:clineAccountId"]) return true
if (config.clineApiKey || config["cline:clineAccountId"]) return true
// Check OpenAI Codex OAuth (stored in SECRETS_KEYS, loaded into config)
if (config["openai-codex-oauth-credentials"]) return true
+45 -46
View File
@@ -112,49 +112,48 @@ function getMessageIcon(message: ClineMessage): string {
default:
return "❔"
}
} else {
switch (message.say) {
case "task":
return "📋"
case "error":
return "❌"
case "text":
return "💬"
case "reasoning":
return "🧠"
case "completion_result":
return "✅"
case "user_feedback":
return "👤"
case "command":
case "command_output":
return "⚙️ "
case "tool":
return "🔧"
case "browser_action":
case "browser_action_launch":
case "browser_action_result":
return "🌐"
case "mcp_server_request_started":
case "mcp_server_response":
return "🔌"
case "api_req_started":
case "api_req_finished":
return "🔄"
case "checkpoint_created":
return "💾"
case "info":
return "️ "
default:
return " "
}
}
switch (message.say) {
case "task":
return "📋"
case "error":
return "❌"
case "text":
return "💬"
case "reasoning":
return "🧠"
case "completion_result":
return "✅"
case "user_feedback":
return "👤"
case "command":
case "command_output":
return "⚙️ "
case "tool":
return "🔧"
case "browser_action":
case "browser_action_launch":
case "browser_action_result":
return "🌐"
case "mcp_server_request_started":
case "mcp_server_response":
return "🔌"
case "api_req_started":
case "api_req_finished":
return "🔄"
case "checkpoint_created":
return "💾"
case "info":
return "️ "
default:
return " "
}
}
/**
* Format a ClineMessage for terminal display
*/
export function formatMessage(message: ClineMessage, verbose: boolean = false): string {
export function formatMessage(message: ClineMessage, verbose = false): string {
const icon = getMessageIcon(message)
const timestamp = formatTimestamp(message.ts)
const lines: string[] = []
@@ -244,7 +243,7 @@ function formatSayMessage(message: ClineMessage, prefix: string, verbose: boolea
case "command_output":
const output = message.text || ""
const truncated = output.length > 500 ? output.substring(0, 500) + "..." : output
const truncated = output.length > 500 ? `${output.substring(0, 500)}...` : output
return `${prefix} ${style.dim("Output:")} ${truncated}`
case "tool":
@@ -289,7 +288,7 @@ function formatSayMessage(message: ClineMessage, prefix: string, verbose: boolea
/**
* Display a horizontal separator
*/
export function separator(char: string = "─", width: number = 60): string {
export function separator(char = "─", width = 60): string {
return style.dim(char.repeat(width))
}
@@ -309,7 +308,7 @@ export function taskHeader(taskId: string, task?: string): string {
/**
* Format the current state for display
*/
export function formatState(state: ExtensionState, verbose: boolean = false): string {
export function formatState(state: ExtensionState, verbose = false): string {
const lines: string[] = []
if (state.currentTaskItem) {
@@ -320,7 +319,7 @@ export function formatState(state: ExtensionState, verbose: boolean = false): st
if (state.clineMessages && state.clineMessages.length > 0) {
const messagesToShow = verbose
? state.clineMessages
: state.clineMessages.filter((m) => {
: state.clineMessages.filter((_m) => {
// Filter out noisy messages in non-verbose mode
// if (m.say === "api_req_started" || m.say === "api_req_finished") return false
return true
@@ -344,7 +343,7 @@ export class Spinner {
private frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
private frameIndex = 0
private interval: NodeJS.Timeout | null = null
private message: string = ""
private message = ""
start(message: string) {
this.message = message
@@ -367,7 +366,7 @@ export class Spinner {
if (finalMessage) {
process.stdout.write(`\r${style.success("✓")} ${finalMessage}\n`)
} else {
process.stdout.write("\r" + " ".repeat(this.message.length + 4) + "\r")
process.stdout.write(`\r${" ".repeat(this.message.length + 4)}\r`)
}
}
@@ -392,7 +391,7 @@ export function clearLine() {
/**
* Move cursor up n lines
*/
export function cursorUp(n: number = 1) {
export function cursorUp(n = 1) {
process.stdout.write(`\x1b[${n}A`)
}
@@ -444,7 +443,7 @@ export async function promptUser(question: string): Promise<string> {
})
return new Promise((resolve) => {
rl.question(style.info(question) + " ", (answer: string) => {
rl.question(`${style.info(question)} `, (answer: string) => {
rl.close()
resolve(answer.trim())
})
@@ -466,7 +465,7 @@ export async function promptConfirmation(question: string): Promise<boolean> {
export function setTerminalTitle(title: string): void {
if (process.stdout.isTTY) {
const maxLength = 80
const truncated = title.length > maxLength ? title.slice(0, maxLength) + "..." : title
const truncated = title.length > maxLength ? `${title.slice(0, maxLength)}...` : title
process.stdout.write(`\x1b]0;${truncated}\x07`)
}
}
+3 -3
View File
@@ -183,9 +183,9 @@ export async function listWorkspaceFiles(workspacePath: string, limit = 5000): P
function countGaps(positions: Iterable<number>): number {
let gaps = 0
let prev = -Infinity
let prev = Number.NEGATIVE_INFINITY
for (const pos of positions) {
if (prev !== -Infinity && pos - prev > 1) {
if (prev !== Number.NEGATIVE_INFINITY && pos - prev > 1) {
gaps++
}
prev = pos
@@ -255,5 +255,5 @@ export function insertMention(text: string, atIndex: number, filePath: string):
// Ensure path starts with / for proper mention format
const normalizedPath = filePath.startsWith("/") ? filePath : `/${filePath}`
const mention = normalizedPath.includes(" ") ? `@"${normalizedPath}"` : `@${normalizedPath}`
return text.slice(0, atIndex) + mention + " " + text.slice(end).trimStart()
return `${text.slice(0, atIndex) + mention} ${text.slice(end).trimStart()}`
}
+4 -4
View File
@@ -87,7 +87,7 @@ export async function runPlainTextTask(options: PlainTextTaskOptions): Promise<b
// JSON mode: stream all messages to stdout (existing behavior)
if (jsonOutput) {
process.stdout.write(JSON.stringify(message) + "\n")
process.stdout.write(`${JSON.stringify(message)}\n`)
} else {
handleMessageForPipeMode(message, verbose || false)
}
@@ -119,7 +119,7 @@ export async function runPlainTextTask(options: PlainTextTaskOptions): Promise<b
} catch (error) {
if (jsonOutput) {
process.stdout.write(
JSON.stringify({ type: "error", message: error instanceof Error ? error.message : String(error) }) + "\n",
`${JSON.stringify({ type: "error", message: error instanceof Error ? error.message : String(error) })}\n`,
)
} else {
process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`)
@@ -164,7 +164,7 @@ export async function runPlainTextTask(options: PlainTextTaskOptions): Promise<b
} catch (error) {
const errMsg = error instanceof Error ? error.message : String(error)
if (jsonOutput) {
process.stdout.write(JSON.stringify({ type: "error", message: errMsg }) + "\n")
process.stdout.write(`${JSON.stringify({ type: "error", message: errMsg })}\n`)
} else {
process.stderr.write(`Error: ${errMsg}\n`)
}
@@ -180,7 +180,7 @@ export async function runPlainTextTask(options: PlainTextTaskOptions): Promise<b
.sort(([aTs], [bTs]) => aTs - bTs)
.map(([_, msg]) => msg)
.at(-1)
process.stdout.write(msg + "\n")
process.stdout.write(`${msg}\n`)
}
return !hasError
+11 -11
View File
@@ -76,30 +76,30 @@ export function printSessionSummary(): void {
"├─────────────────────────────────────────────────────────┤",
`${GRAY}Session ID:${RESET} ${stats.sessionId.padEnd(42)}`,
`${GRAY}Session Time:${RESET} ${sessionTimeStr.padEnd(42)}`,
`${GRAY}Tool Calls:${RESET} ${stats.totalToolCalls} ( ${GREEN}${stats.successfulToolCalls}${RESET} ${RED}${stats.failedToolCalls}${RESET} )`.padEnd(
`${`${GRAY}Tool Calls:${RESET} ${stats.totalToolCalls} ( ${GREEN}${stats.successfulToolCalls}${RESET} ${RED}${stats.failedToolCalls}${RESET} )`.padEnd(
70,
) + "│",
`${GRAY}Success Rate:${RESET} ${session.getSuccessRate().toFixed(1)}%`.padEnd(60) + "│",
)}`,
`${`${GRAY}Success Rate:${RESET} ${session.getSuccessRate().toFixed(1)}%`.padEnd(60)}`,
"├─────────────────────────────────────────────────────────┤",
`${BOLD}Performance${RESET}`,
`${GRAY}Wall Time:${RESET} ${formatDuration(wallTimeMs).padEnd(42)}`,
`${GRAY}Agent Active:${RESET} ${formatDuration(agentActiveMs).padEnd(42)}`,
`${GRAY} » API Time:${RESET} ${formatDuration(stats.apiTimeMs)} ${GRAY}(${formatPercent(stats.apiTimeMs, agentActiveMs)})${RESET}`.padEnd(
`${`${GRAY} » API Time:${RESET} ${formatDuration(stats.apiTimeMs)} ${GRAY}(${formatPercent(stats.apiTimeMs, agentActiveMs)})${RESET}`.padEnd(
60,
) + "│",
`${GRAY} » Tool Time:${RESET} ${formatDuration(stats.toolTimeMs)} ${GRAY}(${formatPercent(stats.toolTimeMs, agentActiveMs)})${RESET}`.padEnd(
)}`,
`${`${GRAY} » Tool Time:${RESET} ${formatDuration(stats.toolTimeMs)} ${GRAY}(${formatPercent(stats.toolTimeMs, agentActiveMs)})${RESET}`.padEnd(
60,
) + "│",
)}`,
"├─────────────────────────────────────────────────────────┤",
`${BOLD}Resources${RESET}`,
`${GRAY}Memory (RSS):${RESET} ${formatBytes(stats.resources.rss).padEnd(42)}`,
`${GRAY}Peak Memory:${RESET} ${formatBytes(stats.peakMemoryBytes).padEnd(42)}`,
`${GRAY}Heap Used:${RESET} ${formatBytes(stats.resources.heapUsed)} ${GRAY}/ ${formatBytes(stats.resources.heapTotal)}${RESET}`.padEnd(
`${`${GRAY}Heap Used:${RESET} ${formatBytes(stats.resources.heapUsed)} ${GRAY}/ ${formatBytes(stats.resources.heapTotal)}${RESET}`.padEnd(
60,
) + "│",
`${GRAY}CPU Time:${RESET} ${formatDuration(stats.resources.userCpuMs + stats.resources.systemCpuMs)} ${GRAY}(user: ${formatDuration(stats.resources.userCpuMs)}, sys: ${formatDuration(stats.resources.systemCpuMs)})${RESET}`.padEnd(
)}`,
`${`${GRAY}CPU Time:${RESET} ${formatDuration(stats.resources.userCpuMs + stats.resources.systemCpuMs)} ${GRAY}(user: ${formatDuration(stats.resources.userCpuMs)}, sys: ${formatDuration(stats.resources.systemCpuMs)})${RESET}`.padEnd(
60,
) + "│",
)}`,
"└─────────────────────────────────────────────────────────┘",
"",
]
+1 -1
View File
@@ -22,7 +22,7 @@ export interface VisibleWindow<T> {
* Centers the selected item in the visible window when possible.
* Returns the visible items and the start index for selection tracking.
*/
export function getVisibleWindow<T>(items: T[], selectedIndex: number, maxVisible: number = 5): VisibleWindow<T> {
export function getVisibleWindow<T>(items: T[], selectedIndex: number, maxVisible = 5): VisibleWindow<T> {
if (items.length <= maxVisible) {
return { items, startIndex: 0 }
}
+1 -1
View File
@@ -1,6 +1,6 @@
export function emitTaskStartedMessage(taskId: string, jsonOutput: boolean): void {
if (jsonOutput) {
process.stdout.write(JSON.stringify({ type: "task_started", taskId }) + "\n")
process.stdout.write(`${JSON.stringify({ type: "task_started", taskId })}\n`)
return
}
+1 -1
View File
@@ -10,7 +10,7 @@
export async function waitFor<T>(
condition: () => T | undefined | null,
timeoutMs: number,
pollIntervalMs: number = 100,
pollIntervalMs = 100,
): Promise<T | undefined> {
// Check immediately first
const immediate = condition()
+1 -1
View File
@@ -259,7 +259,7 @@ function parseVersion(version: string): ParsedVersion {
return {
base: nightlyMatch[1].split(".").map(Number),
isNightly: true,
timestamp: parseInt(nightlyMatch[2], 10),
timestamp: Number.parseInt(nightlyMatch[2], 10),
}
}
return {
+7 -9
View File
@@ -14,8 +14,7 @@ td,
th,
span:not(code *),
div:not(code *):not(pre *) {
font-family:
"Geist Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif !important;
font-family: "Geist Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
}
/* Ensure code blocks use Geist Mono */
@@ -25,12 +24,12 @@ pre,
pre code,
code *,
pre * {
font-family: "Geist Mono", "Monaco", "Courier New", monospace !important;
font-family: "Geist Mono", "Monaco", "Courier New", monospace;
}
/* Make h1 titles lighter in font weight */
h1 {
font-weight: 600 !important;
font-weight: 600;
}
/* Keep headings and images at full opacity */
@@ -41,9 +40,8 @@ h4,
h5,
h6,
img {
opacity: 1 !important;
font-family:
"Geist Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif !important;
opacity: 1;
font-family: "Geist Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
}
/* Also apply to any h1 elements within content areas */
@@ -51,7 +49,7 @@ img {
.markdown h1,
article h1,
main h1 {
font-weight: 500 !important;
font-weight: 500;
}
/* JetBrains logo visibility fix for dark mode */
@@ -89,5 +87,5 @@ img[alt="JetBrains logo"]:hover {
/* Reduce list margin-top */
.steps {
margin-top: 5px !important;
margin-top: 5px;
}
+1 -1
View File
@@ -6,7 +6,7 @@ import * as esbuild from "esbuild"
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const production = process.argv.includes("--production") || process.env["IS_DEBUG_BUILD"] === "false"
const production = process.argv.includes("--production") || process.env.IS_DEBUG_BUILD === "false"
const watch = process.argv.includes("--watch")
const standalone = process.argv.includes("--standalone")
const e2eBuild = process.argv.includes("--e2e-build")
+2 -1
View File
@@ -414,8 +414,9 @@
"lint:proto": "bash ./scripts/proto-lint.sh",
"format": "biome format --changed --no-errors-on-unmatched --files-ignore-unknown=true --diagnostic-level=error",
"format:fix": "biome check --changed --no-errors-on-unmatched --files-ignore-unknown=true --write",
"format:all": "biome check --files-ignore-unknown=true --diagnostic-level=error",
"fix:all": "biome check --no-errors-on-unmatched --files-ignore-unknown=true --write --diagnostic-level=error --unsafe",
"ci:check-all": "npx npm-run-all -p check-types lint format",
"ci:check-all": "npx npm-run-all -p check-types lint format:all",
"ci:build": "npm run protos && npm run build:webview && node esbuild.mjs && npm run compile-tests",
"pretest": "npm run compile && npm run compile-tests && npm run compile-standalone && npm run lint",
"test": "npx npm-run-all test:unit test:integration",
+5 -5
View File
@@ -241,7 +241,7 @@ function normalizeProviderName(providerPart) {
function applySpecialCaseMappings(providerApiKeyMap, apiSecretsFields, assignedFields) {
// Special case 1: Bedrock needs AWS fields (if not already assigned)
const awsFields = ["awsAccessKey", "awsSecretKey", "awsRegion"]
const bedrockFields = providerApiKeyMap["bedrock"] || []
const bedrockFields = providerApiKeyMap.bedrock || []
for (const field of awsFields) {
if (apiSecretsFields.fieldNames.includes(field) && !bedrockFields.includes(field)) {
@@ -257,19 +257,19 @@ function applySpecialCaseMappings(providerApiKeyMap, apiSecretsFields, assignedF
}
if (bedrockFields.length > 0) {
providerApiKeyMap["bedrock"] = bedrockFields
providerApiKeyMap.bedrock = bedrockFields
}
// Special case 2: Vertex needs project ID and region
if (providerApiKeyMap["vertex"]) {
if (providerApiKeyMap.vertex) {
// Vertex typically uses application default credentials,
// but requires project ID and region configuration
// These are already captured if they exist in ApiHandlerSecrets
}
// Special case 3: SAP AI Core multi-key authentication
if (providerApiKeyMap["sapaicore"]) {
const sapFields = providerApiKeyMap["sapaicore"]
if (providerApiKeyMap.sapaicore) {
const sapFields = providerApiKeyMap.sapaicore
const requiredSapFields = ["sapAiCoreClientId", "sapAiCoreClientSecret"]
for (const field of requiredSapFields) {
+3 -4
View File
@@ -106,7 +106,7 @@ async function parseServicesWithFiles(protoDir, protoFiles) {
return services
}
function upperFirst(s) {
function _upperFirst(s) {
return s.length ? s[0].toUpperCase() + s.slice(1) : s
}
@@ -273,8 +273,8 @@ async function generateServiceClientsPy(outDir, services) {
:return: iterator of ${aliasPb2}.${respTypeName}
"""
return self._stub.${m.name}(req)`
} else {
return `
}
return `
def ${m.name}(self, req):
"""
Unary RPC.
@@ -282,7 +282,6 @@ async function generateServiceClientsPy(outDir, services) {
:return: ${aliasPb2}.${respTypeName}
"""
return self._stub.${m.name}(req)`
}
})
.join("\n")
+1 -1
View File
@@ -229,7 +229,7 @@ async function main() {
}
// Print summary
console.log("\n" + "=".repeat(50))
console.log(`\n${"=".repeat(50)}`)
console.log("📊 Summary:")
console.log("=".repeat(50))
+4 -6
View File
@@ -125,9 +125,9 @@ function generateExternalClientSetup(serviceName, serviceDefinition) {
return ` ${methodName}(request: ${requestType}): Promise<${responseType}> {
return this.makeRequest((client) => client.${methodName}(request))
}`
} else {
// Generate streaming method
return ` ${methodName}(
}
// Generate streaming method
return ` ${methodName}(
request: ${requestType},
callbacks: StreamingCallbacks<${responseType}>,
): () => void {
@@ -150,7 +150,6 @@ function generateExternalClientSetup(serviceName, serviceDefinition) {
abortController.abort()
}
}\n`
}
})
.join("\n")
@@ -222,9 +221,8 @@ function generateVscodeClientImplementation(serviceName, serviceDefinition) {
const isStreamingResponse = methodDef.responseStream
if (!isStreamingResponse) {
return `${name}ServiceRegistry.registerMethod("${methodName}", ${methodName})`
} else {
return `${name}ServiceRegistry.registerMethod("${methodName}", ${methodName}, { isStreaming: true })`
}
return `${name}ServiceRegistry.registerMethod("${methodName}", ${methodName}, { isStreaming: true })`
})
.join("\n")
+2 -2
View File
@@ -157,8 +157,8 @@ async function generateStandaloneProtobusServiceSetup(protobusServices) {
handlerSetup.push(` server.addService(cline.${name}Service, {`)
for (const [rpcName, rpc] of Object.entries(def.service)) {
imports.push(`import { ${rpcName} } from "@core/controller/${dir}/${rpcName}"`)
const requestType = "cline." + rpc.requestType.type.name
const responseType = "cline." + rpc.responseType.type.name
const requestType = `cline.${rpc.requestType.type.name}`
const responseType = `cline.${rpc.responseType.type.name}`
if (rpc.requestStream) {
throw new Error("Request streaming is not supported")
}
+1 -1
View File
@@ -357,7 +357,7 @@ function replaceMessage(protoContent, messageName, newMessageContent) {
}
// Message doesn't exist, append before the first message or at end
console.warn(`Warning: ${messageName} message not found in proto file, appending`)
return protoContent + "\n\n" + newMessageContent
return `${protoContent}\n\n${newMessageContent}`
}
async function main() {
+1 -1
View File
@@ -26,7 +26,7 @@ const collectSystemInfo = () => {
if (process.platform === "darwin") {
cpuInfo = execSync("sysctl -n machdep.cpu.brand_string").toString().trim()
memoryInfo = execSync("sysctl -n hw.memsize").toString().trim()
memoryInfo = `${Math.round(parseInt(memoryInfo) / 1e9)} GB RAM`
memoryInfo = `${Math.round(Number.parseInt(memoryInfo, 10) / 1e9)} GB RAM`
} else {
// Linux specific commands
cpuInfo = execSync("lscpu").toString().split("\n").slice(0, 5).join("\n")
+2 -2
View File
@@ -70,7 +70,7 @@ function createMockService<T extends grpc.UntypedServiceImplementation>(serviceN
case "getMachineId":
callback(null, {
value: "fake-machine-id-" + os.hostname(),
value: `fake-machine-id-${os.hostname()}`,
})
return
@@ -103,7 +103,7 @@ function createMockService<T extends grpc.UntypedServiceImplementation>(serviceN
case "openDiff":
callback(null, {
diff_id: "fake-diff-" + Date.now(),
diff_id: `fake-diff-${Date.now()}`,
})
return
+2 -2
View File
@@ -204,8 +204,8 @@ async function main() {
const inputPath = args._[0]
const count = Number(args.count)
showServerLogs = Boolean(args["server-logs"])
fix = Boolean(args["fix"])
coverage = Boolean(args["coverage"])
fix = Boolean(args.fix)
coverage = Boolean(args.coverage)
if (!inputPath) {
console.error(
+5 -7
View File
@@ -9,7 +9,7 @@ import { ClineConfigurationError, ClineEndpoint, ClineEnv, Environment } from ".
describe("ClineEndpoint configuration", () => {
let sandbox: sinon.SinonSandbox
let tempDir: string
let originalHomedir: typeof os.homedir
let _originalHomedir: typeof os.homedir
beforeEach(async () => {
sandbox = sinon.createSandbox()
@@ -20,10 +20,8 @@ describe("ClineEndpoint configuration", () => {
await fs.mkdir(path.join(tempDir, ".cline"), { recursive: true })
// Stub os.homedir to return our temp directory
originalHomedir = os.homedir
sandbox
.stub(os, "homedir")
.returns(tempDir)
_originalHomedir = os.homedir
sandbox.stub(os, "homedir").returns(tempDir)
// Reset the singleton state using internal method
;(ClineEndpoint as any)._instance = null
@@ -543,7 +541,7 @@ describe("ClineEndpoint configuration", () => {
describe("bundled endpoints.json behavior", () => {
let bundledDir: string
let setVscodeHostProviderMock: (mock: { extensionFsPath: string; globalStorageFsPath: string }) => void
let _setVscodeHostProviderMock: (mock: { extensionFsPath: string; globalStorageFsPath: string }) => void
beforeEach(async () => {
// Create a separate directory for bundled config
@@ -552,7 +550,7 @@ describe("ClineEndpoint configuration", () => {
// Import HostProvider utilities
const hostProviderModule = await import("../test/host-provider-test-utils")
setVscodeHostProviderMock = hostProviderModule.setVscodeHostProviderMock
_setVscodeHostProviderMock = hostProviderModule.setVscodeHostProviderMock
})
afterEach(async () => {
+1 -1
View File
@@ -36,7 +36,7 @@ class ClineEndpoint {
private onPremiseConfig: EndpointsFileSchema | null = null
private environment: Environment = Environment.production
// Track if config came from bundled file (enterprise distribution)
private isBundled: boolean = false
private isBundled = false
private constructor() {
// Set environment at module load. Use override if provided.
+1 -2
View File
@@ -585,9 +585,8 @@ function reconstructWriteToFileResult(block: any, originalToolName: string, orig
// If no final_file_content found, create a simple success message
if (originalToolName === "write_to_file") {
return `[apply_patch for '${filePath}'] Result:\nThe content was successfully saved to ${filePath}.\n\nThe file has been created/updated with the new content.`
} else {
return `[apply_patch for '${filePath}'] Result:\nThe content was successfully updated in ${filePath}.\n\nThe file has been modified.`
}
return `[apply_patch for '${filePath}'] Result:\nThe content was successfully updated in ${filePath}.\n\nThe file has been modified.`
}
const finalContent = finalContentMatch[2]
@@ -1083,8 +1083,7 @@ describe("AwsBedrockHandler", () => {
// Capture the command passed to executeConverseStream
let capturedCommand: any = null
const originalExecuteConverseStream = handler["executeConverseStream"].bind(handler)
handler["executeConverseStream"] = async function* (command: any, modelInfo: any) {
handler["executeConverseStream"] = async function* (command: any, _modelInfo: any) {
capturedCommand = command
// Yield nothing — we just want to capture the command
}
+1 -2
View File
@@ -97,9 +97,8 @@ export class AnthropicHandler implements ApiHandler {
"anthropic-beta": "context-1m-2025-08-07",
},
}
} else {
return undefined
}
return undefined
})(),
)
} else {
+161 -165
View File
@@ -264,7 +264,7 @@ export class AwsBedrockHandler implements ApiHandler {
if (useProfile) {
AwsBedrockHandler.setEnv("AWS_PROFILE", this.options.awsProfile)
} else {
delete process.env["AWS_PROFILE"]
delete process.env.AWS_PROFILE
AwsBedrockHandler.setEnv("AWS_ACCESS_KEY_ID", this.options.awsAccessKey)
AwsBedrockHandler.setEnv("AWS_SECRET_ACCESS_KEY", this.options.awsSecretKey)
AwsBedrockHandler.setEnv("AWS_SESSION_TOKEN", this.options.awsSessionToken)
@@ -537,7 +537,7 @@ export class AwsBedrockHandler implements ApiHandler {
}
}
combinedContent += message.role === "user" ? "User: " + content + "\n" : "Assistant: " + content + "\n"
combinedContent += message.role === "user" ? `User: ${content}\n` : `Assistant: ${content}\n`
}
// Format according to DeepSeek R1's expected prompt format
@@ -604,182 +604,178 @@ export class AwsBedrockHandler implements ApiHandler {
* Common implementation for both Anthropic and Nova models
*/
private async *executeConverseStream(command: ConverseStreamCommand, modelInfo: ModelInfo): ApiStream {
try {
const client = await this.getBedrockClient()
const response = await client.send(command)
const client = await this.getBedrockClient()
const response = await client.send(command)
if (response.stream) {
// Buffer content by contentBlockIndex to handle multi-block responses correctly
const contentBuffers: Record<number, string> = {}
const blockTypes = new Map<number, "reasoning" | "text">()
const activeToolCalls: Map<number, { toolUseId: string; name: string }> = new Map()
if (response.stream) {
// Buffer content by contentBlockIndex to handle multi-block responses correctly
const contentBuffers: Record<number, string> = {}
const blockTypes = new Map<number, "reasoning" | "text">()
const activeToolCalls: Map<number, { toolUseId: string; name: string }> = new Map()
for await (const chunk of response.stream) {
// Debug logging to see actual response structure
// Logger.log("Bedrock chunk:", JSON.stringify(chunk, null, 2))
for await (const chunk of response.stream) {
// Debug logging to see actual response structure
// Logger.log("Bedrock chunk:", JSON.stringify(chunk, null, 2))
// Handle thinking response in additionalModelResponseFields (LangChain format)
const metadata = chunk.metadata as ExtendedMetadata | undefined
if (metadata?.additionalModelResponseFields?.thinkingResponse) {
const thinkingResponse = metadata.additionalModelResponseFields.thinkingResponse
if (thinkingResponse.reasoning && Array.isArray(thinkingResponse.reasoning)) {
for (const reasoningBlock of thinkingResponse.reasoning) {
if (reasoningBlock.type === "text" && reasoningBlock.text) {
yield {
type: "reasoning",
reasoning: reasoningBlock.text,
}
// Handle thinking response in additionalModelResponseFields (LangChain format)
const metadata = chunk.metadata as ExtendedMetadata | undefined
if (metadata?.additionalModelResponseFields?.thinkingResponse) {
const thinkingResponse = metadata.additionalModelResponseFields.thinkingResponse
if (thinkingResponse.reasoning && Array.isArray(thinkingResponse.reasoning)) {
for (const reasoningBlock of thinkingResponse.reasoning) {
if (reasoningBlock.type === "text" && reasoningBlock.text) {
yield {
type: "reasoning",
reasoning: reasoningBlock.text,
}
}
}
}
}
// Handle metadata events with token usage information
if (chunk.metadata?.usage) {
const inputTokens = chunk.metadata.usage.inputTokens || 0
const outputTokens = chunk.metadata.usage.outputTokens || 0
const cacheReadInputTokens = chunk.metadata.usage.cacheReadInputTokens || 0
const cacheWriteInputTokens = chunk.metadata.usage.cacheWriteInputTokens || 0
// Handle metadata events with token usage information
if (chunk.metadata?.usage) {
const inputTokens = chunk.metadata.usage.inputTokens || 0
const outputTokens = chunk.metadata.usage.outputTokens || 0
const cacheReadInputTokens = chunk.metadata.usage.cacheReadInputTokens || 0
const cacheWriteInputTokens = chunk.metadata.usage.cacheWriteInputTokens || 0
yield {
type: "usage",
yield {
type: "usage",
inputTokens,
outputTokens,
cacheReadTokens: cacheReadInputTokens,
cacheWriteTokens: cacheWriteInputTokens,
totalCost: calculateApiCostOpenAI(
modelInfo,
inputTokens,
outputTokens,
cacheReadTokens: cacheReadInputTokens,
cacheWriteTokens: cacheWriteInputTokens,
totalCost: calculateApiCostOpenAI(
modelInfo,
inputTokens,
outputTokens,
cacheWriteInputTokens,
cacheReadInputTokens,
),
}
cacheWriteInputTokens,
cacheReadInputTokens,
),
}
// Handle content block start - check if Bedrock uses Anthropic SDK format
if (chunk.contentBlockStart) {
const blockStart = chunk.contentBlockStart as ContentBlockStart
const blockIndex = chunk.contentBlockStart.contentBlockIndex
if (blockStart.start?.toolUse) {
const toolUse = blockStart.start.toolUse
if (toolUse.toolUseId && toolUse.name && blockIndex !== undefined) {
activeToolCalls.set(blockIndex, {
toolUseId: toolUse.toolUseId,
name: toolUse.name,
})
}
}
// Check for thinking block in various possible formats
if (
blockStart.start?.type === "thinking" ||
blockStart.contentBlock?.type === "thinking" ||
blockStart.type === "thinking"
) {
if (blockIndex !== undefined) {
blockTypes.set(blockIndex, "reasoning")
// Initialize content if provided
const initialContent =
blockStart.start?.thinking || blockStart.contentBlock?.thinking || blockStart.thinking || ""
if (initialContent) {
yield {
type: "reasoning",
reasoning: initialContent,
}
}
}
}
}
// Handle content block delta - accumulate content by block index
if (chunk.contentBlockDelta) {
const blockIndex = chunk.contentBlockDelta.contentBlockIndex
if (blockIndex !== undefined) {
// Initialize buffer for this block if it doesn't exist
if (!(blockIndex in contentBuffers)) {
contentBuffers[blockIndex] = ""
}
// Check if this is a thinking block
const blockType = blockTypes.get(blockIndex)
const delta = chunk.contentBlockDelta.delta as ContentBlockDelta["delta"]
// Handle thinking delta (Anthropic SDK format)
if (delta?.type === "thinking_delta" || delta?.thinking) {
const thinkingContent = delta.thinking || delta.text || ""
if (thinkingContent) {
yield {
type: "reasoning",
reasoning: thinkingContent,
}
}
} else if (delta?.reasoningContent?.text) {
// Handle reasoning content (Bedrock format)
const reasoningText = delta.reasoningContent.text
if (reasoningText) {
yield {
type: "reasoning",
reasoning: reasoningText,
}
}
} else if (delta?.toolUse?.input !== undefined) {
const toolCall = activeToolCalls.get(blockIndex)
const toolInput = delta.toolUse.input
if (toolCall && typeof toolInput === "string") {
yield {
type: "tool_calls",
tool_call: {
call_id: toolCall.toolUseId,
function: {
id: toolCall.toolUseId,
name: toolCall.name,
arguments: toolInput,
},
},
}
}
} else if (chunk.contentBlockDelta.delta?.text) {
// Handle regular text content
const textContent = chunk.contentBlockDelta.delta.text
contentBuffers[blockIndex] += textContent
// Stream based on block type
if (blockType === "reasoning") {
yield {
type: "reasoning",
reasoning: textContent,
}
} else {
yield {
type: "text",
text: textContent,
}
}
}
}
}
// Handle content block stop - clean up buffers
if (chunk.contentBlockStop) {
const blockIndex = chunk.contentBlockStop.contentBlockIndex
if (blockIndex !== undefined) {
// Clean up buffers and tracking for this block
delete contentBuffers[blockIndex]
blockTypes.delete(blockIndex)
activeToolCalls.delete(blockIndex)
}
}
// Handle errors with unified error handling
yield* this.handleBedrockStreamError(chunk)
}
// Handle content block start - check if Bedrock uses Anthropic SDK format
if (chunk.contentBlockStart) {
const blockStart = chunk.contentBlockStart as ContentBlockStart
const blockIndex = chunk.contentBlockStart.contentBlockIndex
if (blockStart.start?.toolUse) {
const toolUse = blockStart.start.toolUse
if (toolUse.toolUseId && toolUse.name && blockIndex !== undefined) {
activeToolCalls.set(blockIndex, {
toolUseId: toolUse.toolUseId,
name: toolUse.name,
})
}
}
// Check for thinking block in various possible formats
if (
blockStart.start?.type === "thinking" ||
blockStart.contentBlock?.type === "thinking" ||
blockStart.type === "thinking"
) {
if (blockIndex !== undefined) {
blockTypes.set(blockIndex, "reasoning")
// Initialize content if provided
const initialContent =
blockStart.start?.thinking || blockStart.contentBlock?.thinking || blockStart.thinking || ""
if (initialContent) {
yield {
type: "reasoning",
reasoning: initialContent,
}
}
}
}
}
// Handle content block delta - accumulate content by block index
if (chunk.contentBlockDelta) {
const blockIndex = chunk.contentBlockDelta.contentBlockIndex
if (blockIndex !== undefined) {
// Initialize buffer for this block if it doesn't exist
if (!(blockIndex in contentBuffers)) {
contentBuffers[blockIndex] = ""
}
// Check if this is a thinking block
const blockType = blockTypes.get(blockIndex)
const delta = chunk.contentBlockDelta.delta as ContentBlockDelta["delta"]
// Handle thinking delta (Anthropic SDK format)
if (delta?.type === "thinking_delta" || delta?.thinking) {
const thinkingContent = delta.thinking || delta.text || ""
if (thinkingContent) {
yield {
type: "reasoning",
reasoning: thinkingContent,
}
}
} else if (delta?.reasoningContent?.text) {
// Handle reasoning content (Bedrock format)
const reasoningText = delta.reasoningContent.text
if (reasoningText) {
yield {
type: "reasoning",
reasoning: reasoningText,
}
}
} else if (delta?.toolUse?.input !== undefined) {
const toolCall = activeToolCalls.get(blockIndex)
const toolInput = delta.toolUse.input
if (toolCall && typeof toolInput === "string") {
yield {
type: "tool_calls",
tool_call: {
call_id: toolCall.toolUseId,
function: {
id: toolCall.toolUseId,
name: toolCall.name,
arguments: toolInput,
},
},
}
}
} else if (chunk.contentBlockDelta.delta?.text) {
// Handle regular text content
const textContent = chunk.contentBlockDelta.delta.text
contentBuffers[blockIndex] += textContent
// Stream based on block type
if (blockType === "reasoning") {
yield {
type: "reasoning",
reasoning: textContent,
}
} else {
yield {
type: "text",
text: textContent,
}
}
}
}
}
// Handle content block stop - clean up buffers
if (chunk.contentBlockStop) {
const blockIndex = chunk.contentBlockStop.contentBlockIndex
if (blockIndex !== undefined) {
// Clean up buffers and tracking for this block
delete contentBuffers[blockIndex]
blockTypes.delete(blockIndex)
activeToolCalls.delete(blockIndex)
}
}
// Handle errors with unified error handling
yield* this.handleBedrockStreamError(chunk)
}
} catch (error) {
throw error
}
}
@@ -1018,7 +1014,7 @@ export class AwsBedrockHandler implements ApiHandler {
if (item.source.media_type) {
// Extract format from media_type (e.g., "image/jpeg" -> "jpeg")
const formatMatch = item.source.media_type.match(/image\/(\w+)/)
if (formatMatch && formatMatch[1]) {
if (formatMatch?.[1]) {
const extractedFormat = formatMatch[1]
// Ensure format is one of the allowed values
if (["png", "jpeg", "gif", "webp"].includes(extractedFormat)) {
+2 -2
View File
@@ -70,8 +70,8 @@ export class ClaudeCodeHandler implements ApiHandler {
if (message.stop_reason !== null) {
const content = "text" in message.content[0] ? message.content[0] : undefined
const isError = content && content.text.startsWith(`API Error`)
if (isError) {
const isError = content?.text.startsWith(`API Error`)
if (content && isError) {
// Error messages are formatted as: `API Error: <<status code>> <<json>>`
const errorMessageStart = content.text.indexOf("{")
const errorMessage = content.text.slice(errorMessageStart)
+1 -1
View File
@@ -126,7 +126,7 @@ export class ClineHandler implements ApiHandler {
const toolCallProcessor = new ToolCallProcessor()
for await (const chunk of stream) {
Logger.debug("ClineHandler chunk:" + JSON.stringify(chunk))
Logger.debug(`ClineHandler chunk:${JSON.stringify(chunk)}`)
// openrouter returns an error object instead of the openai sdk throwing an error
if ("error" in chunk) {
const error = chunk.error as OpenRouterErrorResponse["error"]
+14 -17
View File
@@ -78,13 +78,11 @@ export class DifyHandler implements ApiHandler {
private baseUrl: string
private apiKey: string
private conversationId: string | null = null
private currentTaskId: string | null = null
private abortController: AbortController | null = null
constructor(options: DifyHandlerOptions) {
this.options = options
this.apiKey = options.difyApiKey || ""
this.baseUrl = options.difyBaseUrl || ""
this.apiKey = this.options.difyApiKey || ""
this.baseUrl = this.options.difyBaseUrl || ""
Logger.log("[DIFY DEBUG] Constructor called with:", {
hasApiKey: !!this.apiKey,
@@ -341,7 +339,7 @@ export class DifyHandler implements ApiHandler {
}
hasYieldedContent = true
}
} catch (e) {
} catch (_e) {
// Not JSON, continue
Logger.log("[DIFY DEBUG] Line is not direct JSON, continuing")
}
@@ -429,7 +427,7 @@ export class DifyHandler implements ApiHandler {
* @param user User identifier (defaults to "cline-user")
* @returns Promise with file upload response
*/
async uploadFile(file: Buffer, filename: string, user: string = "cline-user"): Promise<DifyFileResponse> {
async uploadFile(file: Buffer, filename: string, user = "cline-user"): Promise<DifyFileResponse> {
const formData = new FormData()
formData.append("file", new Blob([new Uint8Array(file)]), filename)
formData.append("user", user)
@@ -454,7 +452,7 @@ export class DifyHandler implements ApiHandler {
* @param user User identifier (defaults to "cline-user")
* @returns Promise that resolves when generation is stopped
*/
async stopGeneration(taskId: string, user: string = "cline-user"): Promise<void> {
async stopGeneration(taskId: string, user = "cline-user"): Promise<void> {
const response = await fetch(`${this.baseUrl}/chat-messages/${taskId}/stop`, {
method: "POST",
headers: this.jsonHeaders(),
@@ -477,9 +475,9 @@ export class DifyHandler implements ApiHandler {
*/
async getConversationHistory(
conversationId: string,
user: string = "cline-user",
user = "cline-user",
firstId?: string,
limit: number = 20,
limit = 20,
): Promise<DifyHistoryResponse> {
const params = new URLSearchParams({ user, limit: limit.toString() })
if (firstId) {
@@ -507,10 +505,10 @@ export class DifyHandler implements ApiHandler {
* @returns Promise with conversations list
*/
async getConversations(
user: string = "cline-user",
user = "cline-user",
lastId?: string,
limit: number = 20,
sortBy: string = "-updated_at",
limit = 20,
sortBy = "-updated_at",
): Promise<DifyConversationsResponse> {
const params = new URLSearchParams({
user,
@@ -539,7 +537,7 @@ export class DifyHandler implements ApiHandler {
* @param user User identifier (defaults to "cline-user")
* @returns Promise that resolves when conversation is deleted
*/
async deleteConversation(conversationId: string, user: string = "cline-user"): Promise<void> {
async deleteConversation(conversationId: string, user = "cline-user"): Promise<void> {
const response = await fetch(`${this.baseUrl}/conversations/${conversationId}`, {
method: "DELETE",
headers: this.jsonHeaders(),
@@ -562,9 +560,9 @@ export class DifyHandler implements ApiHandler {
*/
async renameConversation(
conversationId: string,
user: string = "cline-user",
user = "cline-user",
name?: string,
autoGenerate: boolean = false,
autoGenerate = false,
): Promise<DifyConversationResponse> {
const body: any = { user, auto_generate: autoGenerate }
if (name) {
@@ -597,7 +595,7 @@ export class DifyHandler implements ApiHandler {
messageId: string,
rating: "like" | "dislike",
content?: string,
user: string = "cline-user",
user = "cline-user",
): Promise<void> {
const body: any = { rating, user }
if (content) {
@@ -637,7 +635,6 @@ export class DifyHandler implements ApiHandler {
*/
resetConversation(): void {
this.conversationId = null
this.currentTaskId = null
}
private jsonHeaders() {
@@ -1,10 +1,6 @@
// Mock for @google/genai module to avoid ESM compatibility issues in tests
export class GoogleGenAI {
constructor(_options: any) {
// Mock constructor
}
models = {
generateContentStream: async (_params: any) => {
// Mock implementation that returns an async iterator
+1 -1
View File
@@ -297,7 +297,7 @@ export class GeminiHandler implements ApiHandler {
// https://github.com/googleapis/js-genai/blob/v1.11.0/src/_api_client.ts#L758
const response = this.attemptParse(error.message)
if (response && response.error) {
if (response?.error) {
const responseBody = this.attemptParse(response.error.message)
if (responseBody.error) {
+1 -1
View File
@@ -79,7 +79,7 @@ export class HuaweiCloudMaaSHandler implements ApiHandler {
})
let reasoning: string | null = null
let didOutputUsage: boolean = false
let didOutputUsage = false
let finalUsage: any = null
const toolCallProcessor = new ToolCallProcessor()
+35 -39
View File
@@ -66,53 +66,49 @@ export class HuggingFaceHandler implements ApiHandler {
@withRetry()
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[], tools?: OpenAITool[]): ApiStream {
try {
const client = this.ensureClient()
const model = this.getModel()
const client = this.ensureClient()
const model = this.getModel()
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: "system", content: systemPrompt },
...convertToOpenAiMessages(messages),
]
const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: "system", content: systemPrompt },
...convertToOpenAiMessages(messages),
]
const requestParams = {
model: model.id,
max_tokens: model.info.maxTokens,
messages: openAiMessages,
stream: true,
stream_options: { include_usage: true },
temperature: 0,
...getOpenAIToolParams(tools),
}
const requestParams = {
model: model.id,
max_tokens: model.info.maxTokens,
messages: openAiMessages,
stream: true,
stream_options: { include_usage: true },
temperature: 0,
...getOpenAIToolParams(tools),
}
const toolCallProcessor = new ToolCallProcessor()
const stream = (await client.chat.completions.create(requestParams)) as any
const toolCallProcessor = new ToolCallProcessor()
const stream = (await client.chat.completions.create(requestParams)) as any
let _chunkCount = 0
let _totalContent = ""
let _chunkCount = 0
let _totalContent = ""
for await (const chunk of stream) {
_chunkCount++
const delta = chunk.choices?.[0]?.delta
if (delta?.content) {
_totalContent += delta.content
for await (const chunk of stream) {
_chunkCount++
const delta = chunk.choices?.[0]?.delta
if (delta?.content) {
_totalContent += delta.content
yield {
type: "text",
text: delta.content,
}
}
if (delta?.tool_calls) {
yield* toolCallProcessor.processToolCallDeltas(delta.tool_calls)
}
if (chunk.usage) {
yield* this.yieldUsage(model.info, chunk.usage)
yield {
type: "text",
text: delta.content,
}
}
} catch (error: any) {
throw error
if (delta?.tool_calls) {
yield* toolCallProcessor.processToolCallDeltas(delta.tool_calls)
}
if (chunk.usage) {
yield* this.yieldUsage(model.info, chunk.usage)
}
}
}
+20 -21
View File
@@ -72,26 +72,24 @@ export async function fetchLiteLlmModelsInfo(baseUrl: string, apiKey: string): P
if (response.ok) {
const data: LiteLlmModelInfoResponse = await response.json()
return data
} else {
Logger.error("Failed to fetch LiteLLM model info:", response.statusText)
// Try with Authorization header instead
const retryResponse = await fetch(url, {
method: "GET",
headers: {
accept: "application/json",
Authorization: `Bearer ${apiKey}`,
...buildExternalBasicHeaders(),
},
})
if (retryResponse.ok) {
const data: LiteLlmModelInfoResponse = await retryResponse.json()
return data
} else {
Logger.error("Failed to fetch LiteLLM model info with Authorization header:", retryResponse.statusText)
throw new Error(`Failed to fetch LiteLLM model info: ${retryResponse.statusText}`)
}
}
Logger.error("Failed to fetch LiteLLM model info:", response.statusText)
// Try with Authorization header instead
const retryResponse = await fetch(url, {
method: "GET",
headers: {
accept: "application/json",
Authorization: `Bearer ${apiKey}`,
...buildExternalBasicHeaders(),
},
})
if (retryResponse.ok) {
const data: LiteLlmModelInfoResponse = await retryResponse.json()
return data
}
Logger.error("Failed to fetch LiteLLM model info with Authorization header:", retryResponse.statusText)
throw new Error(`Failed to fetch LiteLLM model info: ${retryResponse.statusText}`)
} catch (error) {
Logger.error("Error fetching LiteLLM model info:", error)
throw error
@@ -102,7 +100,7 @@ export class LiteLlmHandler implements ApiHandler {
private options: LiteLlmHandlerOptions
private client: OpenAI | undefined
private modelInfoCache: LiteLlmModelInfoResponse | undefined
private modelInfoCacheTimestamp: number = 0
private modelInfoCacheTimestamp = 0
private readonly modelInfoCacheTTL = 5 * 60 * 1000 // 5 minutes
constructor(options: LiteLlmHandlerOptions) {
@@ -274,7 +272,8 @@ export class LiteLlmHandler implements ApiHandler {
},
] as any,
}
} else if (Array.isArray(message.content)) {
}
if (Array.isArray(message.content)) {
// Apply cache control to the last content item in the array
return {
...message,
+1 -1
View File
@@ -118,7 +118,7 @@ export class MistralHandler implements ApiHandler {
}
}
} else if (delta?.content) {
let content: string = ""
let content = ""
if (typeof delta.content === "string") {
content = delta.content
} else if (Array.isArray(delta.content)) {
+3 -3
View File
@@ -217,7 +217,7 @@ export class OcaHandler implements ApiHandler {
}
async calculateCost(
modelInfo: ModelInfo,
_modelInfo: ModelInfo,
inputTokens: number,
outputTokens: number,
_cacheWriteTokens?: number,
@@ -231,7 +231,7 @@ export class OcaHandler implements ApiHandler {
@withRetry()
async *createMessage(systemPrompt: string, messages: ClineStorageMessage[], tools?: OpenAITool[]): ApiStream {
if (this.options.ocaModelInfo?.apiFormat == ApiFormat.OPENAI_RESPONSES) {
if (this.options.ocaModelInfo?.apiFormat === ApiFormat.OPENAI_RESPONSES) {
yield* this.createMessageResponsesApi(systemPrompt, messages, tools)
} else if (this.options.ocaModelInfo?.apiFormat == ApiFormat.ANTHROPIC_CHAT) {
yield* this.createMessageMessagesApi(systemPrompt, messages, tools)
@@ -310,7 +310,7 @@ export class OcaHandler implements ApiHandler {
}
if (this.options.ocaModelInfo?.supportsReasoningEffort) {
chatCompletionsParams["reasoning_effort"] = this.options.ocaReasoningEffort || ("medium" as any)
chatCompletionsParams.reasoning_effort = this.options.ocaReasoningEffort || ("medium" as any)
}
const stream = await client.chat.completions.create(chatCompletionsParams)
+1 -1
View File
@@ -86,7 +86,7 @@ export class OllamaHandler implements ApiHandler {
try {
for await (const chunk of stream) {
Logger.debug("[OllamaHandler] Message Chunk" + JSON.stringify(chunk))
Logger.debug(`[OllamaHandler] Message Chunk${JSON.stringify(chunk)}`)
const delta = chunk.message
+1 -2
View File
@@ -175,9 +175,8 @@ export class QwenCodeHandler implements ApiHandler {
client.apiKey = this.credentials.access_token
client.baseURL = this.getBaseUrl(this.credentials)
return await apiCall()
} else {
throw error
}
throw error
}
}
+5 -5
View File
@@ -172,7 +172,7 @@ namespace Bedrock {
if (item.source.media_type) {
// Extract format from media_type (e.g., "image/jpeg" -> "jpeg")
const formatMatch = item.source.media_type.match(/image\/(\w+)/)
if (formatMatch && formatMatch[1]) {
if (formatMatch?.[1]) {
const extractedFormat = formatMatch[1]
// Ensure format is one of the allowed values
if (["png", "jpeg", "gif", "webp"].includes(extractedFormat)) {
@@ -251,7 +251,7 @@ namespace Gemini {
for (const part of partsForThoughts) {
const { thought, text } = part
if (thought && text) {
thoughts += text + "\n"
thoughts += `${text}\n`
}
}
}
@@ -266,7 +266,7 @@ namespace Gemini {
}
// Handle content parts for non-thought text
if (data.candidates && data.candidates[0]?.content?.parts) {
if (data.candidates?.[0]?.content?.parts) {
let nonThoughtText = ""
for (const part of data.candidates[0].content.parts) {
if (part.text && !part.thought) {
@@ -429,7 +429,7 @@ export class SapAiCoreHandler implements ApiHandler {
}
const externalHeaders = buildExternalBasicHeaders()
const tokenUrl = this.options.sapAiCoreTokenUrl!.replace(/\/+$/, "") + "/oauth/token"
const tokenUrl = `${this.options.sapAiCoreTokenUrl?.replace(/\/+$/, "")}/oauth/token`
const response = await axios.post(tokenUrl, payload, {
headers: { ...externalHeaders, "Content-Type": "application/x-www-form-urlencoded" },
...getAxiosSettings(),
@@ -1024,7 +1024,7 @@ export class SapAiCoreHandler implements ApiHandler {
if (data.choices && data.choices.length > 0) {
const choice = data.choices[0]
if (choice.delta && choice.delta.content) {
if (choice.delta?.content) {
yield {
type: "text",
text: choice.delta.content,
+7 -7
View File
@@ -392,7 +392,7 @@ export class VsCodeLmHandler implements ApiHandler, SingleCompletionHandler {
const totalInputTokens: number = await this.calculateTotalInputTokens(vsCodeLmMessages)
// Accumulate the text and count at the end of the stream to reduce token counting overhead.
let accumulatedText: string = ""
let accumulatedText = ""
try {
// Create the response stream with minimal required options
@@ -498,17 +498,17 @@ export class VsCodeLmHandler implements ApiHandler, SingleCompletionHandler {
// Return original error if it's already an Error instance
throw error
} else if (typeof error === "object" && error !== null) {
}
if (typeof error === "object" && error !== null) {
// Handle error-like objects
const errorDetails = JSON.stringify(error, null, 2)
Logger.error("Cline <Language Model API>: Stream error object:", errorDetails)
throw new Error(`Cline <Language Model API>: Response stream error: ${errorDetails}`)
} else {
// Fallback for unknown error types
const errorMessage = String(error)
Logger.error("Cline <Language Model API>: Unknown stream error:", errorMessage)
throw new Error(`Cline <Language Model API>: Response stream error: ${errorMessage}`)
}
// Fallback for unknown error types
const errorMessage = String(error)
Logger.error("Cline <Language Model API>: Unknown stream error:", errorMessage)
throw new Error(`Cline <Language Model API>: Response stream error: ${errorMessage}`)
}
}
+2 -2
View File
@@ -15,7 +15,7 @@ const DEFAULT_OPTIONS: Required<RetryOptions> = {
}
export class RetriableError extends Error {
status: number = 429
status = 429
retryAfter?: number
constructor(message: string, retryAfter?: number, options?: ErrorOptions) {
@@ -56,7 +56,7 @@ export function withRetry(options: RetryOptions = {}) {
let delay: number
if (retryAfter) {
// Handle both delta-seconds and Unix timestamp formats
const retryValue = parseInt(retryAfter, 10)
const retryValue = Number.parseInt(retryAfter, 10)
if (retryValue > Date.now() / 1000) {
// Unix timestamp
delay = retryValue * 1000 - Date.now()
@@ -72,7 +72,7 @@ describe("Tool Call Parsing", () => {
it("should transform OpenAI Responses API tool IDs (fc_ prefix)", () => {
// OpenAI Responses API uses fc_ prefix with 53 char length
const responsesApiId = "fc_" + "x".repeat(50)
const responsesApiId = `fc_${"x".repeat(50)}`
const messages: ClineStorageMessage[] = [
{
role: "assistant",
@@ -127,7 +127,7 @@ describe("Tool Call Parsing", () => {
// Get the transformed tool_call id from assistant message
const assistantMsg = result[0] as OpenAI.Chat.ChatCompletionAssistantMessageParam
const transformedId = assistantMsg.tool_calls![0].id
const transformedId = assistantMsg.tool_calls?.[0].id
// The tool result should have the same transformed id
const toolMsg = result[1] as OpenAI.Chat.ChatCompletionToolMessageParam
@@ -247,7 +247,7 @@ describe("Tool Call Parsing", () => {
result.id.should.equal("chatcmpl-123")
result.role.should.equal("assistant")
result.model.should.equal("gpt-4o")
result.stop_reason!.should.equal("end_turn")
result.stop_reason?.should.equal("end_turn")
result.usage.input_tokens.should.equal(10)
result.usage.output_tokens.should.equal(5)
@@ -288,7 +288,7 @@ describe("Tool Call Parsing", () => {
const result = convertToAnthropicMessage(completion)
result.stop_reason!.should.equal("tool_use")
result.stop_reason?.should.equal("tool_use")
const content = result.content as any[]
content.should.have.length(2) // text block + tool_use block
-1
View File
@@ -389,7 +389,6 @@ export function convertO1ResponseToAnthropicMessage(
return "max_tokens"
case "tool_calls":
return "tool_use"
case "content_filter": // Anthropic doesn't have an exact equivalent
default:
return null
}
+1 -1
View File
@@ -91,7 +91,7 @@ export function convertToOllamaMessages(anthropicMessages: Omit<ClineStorageMess
)
// Process non-tool messages
let content: string = ""
let content = ""
if (nonToolMessages.length > 0) {
content = nonToolMessages
.map((part) => {
+1 -2
View File
@@ -314,7 +314,7 @@ function consolidateReasoningDetails(reasoningDetails: ReasoningDetail[]): Reaso
if (!groupedByIndex.has(index)) {
groupedByIndex.set(index, [])
}
groupedByIndex.get(index)!.push(detail)
groupedByIndex.get(index)?.push(detail)
}
// Consolidate each group
@@ -410,7 +410,6 @@ export function convertToAnthropicMessage(completion: OpenAI.Chat.Completions.Ch
return "max_tokens"
case "tool_calls":
return "tool_use"
case "content_filter": // Anthropic doesn't have an exact equivalent
default:
return null
}
+1 -1
View File
@@ -107,7 +107,7 @@ export async function createOpenRouterStream(
msg.content.push(lastTextPart)
}
// @ts-expect-error-next-line
lastTextPart["cache_control"] = { type: "ephemeral" }
lastTextPart.cache_control = { type: "ephemeral" }
}
})
break
@@ -74,12 +74,12 @@ export class ToolCallProcessor {
}
}
export function getOpenAIToolParams(tools?: OpenAITool[], enableParallelToolCalls: boolean = false) {
export function getOpenAIToolParams(tools?: OpenAITool[], enableParallelToolCalls = false) {
return tools?.length
? {
tools,
tool_choice: tools ? ("auto" as ChatCompletionToolChoiceOption) : undefined,
parallel_tool_calls: enableParallelToolCalls ? true : false,
parallel_tool_calls: !!enableParallelToolCalls,
}
: {
tools: undefined,
@@ -76,7 +76,7 @@ export async function createVercelAIGatewayStream(
msg.content.push(lastTextPart)
}
// @ts-expect-error-next-line
lastTextPart["cache_control"] = { type: "ephemeral" }
lastTextPart.cache_control = { type: "ephemeral" }
}
})
}
+5 -5
View File
@@ -427,12 +427,12 @@ async function constructNewFileContentV1(
// (We artificially add a linebreak since we split on \n at the beginning. In order to not include a trailing linebreak in the final search/result blocks we need to remove it before using them. This allows for partial line matches to be correctly identified.)
// NOTE: search/replace blocks must be arranged in the order they appear in the file due to how we build the content using lastProcessedIndex. We also cannot strip the trailing newline since for non-partial lines it would remove the linebreak from the original content. (If we remove end linebreak from search, then we'd also have to remove it from replace but we can't know if it's a partial line or not since the model may be using the line break to indicate the end of the block rather than as part of the search content.) We require the model to output full lines in order for our fallbacks to work as well.
if (inSearch) {
currentSearchContent += line + "\n"
currentSearchContent += `${line}\n`
} else if (inReplace) {
currentReplaceContent += line + "\n"
currentReplaceContent += `${line}\n`
// Only output replacement lines immediately for in-order replacements
if (searchMatchIndex !== -1 && !pendingOutOfOrderReplacement) {
result += line + "\n"
result += `${line}\n`
}
}
}
@@ -636,10 +636,10 @@ class NewFileContentConstructor {
if (this.isReplacingActive()) {
// Output replacement lines immediately if we know the insertion point
if (this.searchMatchIndex !== -1) {
this.result += line + "\n"
this.result += `${line}\n`
}
} else if (this.isSearchingActive()) {
this.currentSearchContent += line + "\n"
this.currentSearchContent += `${line}\n`
} else {
const appendToPendingNonStandardLines = canWritependingNonStandardLines
if (appendToPendingNonStandardLines) {
@@ -1028,7 +1028,7 @@ export class ContextManager {
i,
EditType.READ_FILE_TOOL,
"",
headerText + "\n" + formatResponse.duplicateFileReadNotice(),
`${headerText}\n${formatResponse.duplicateFileReadNotice()}`,
contentBlockIndex,
])
}
@@ -11,11 +11,11 @@ const mockHostProvider = {
}
describe("RuleContextBuilder", () => {
let hostProviderStub: sinon.SinonStub
let _hostProviderStub: sinon.SinonStub
beforeEach(() => {
// Stub HostProvider to use mock
hostProviderStub = sinon.stub(require("@/hosts/host-provider"), "HostProvider").value(mockHostProvider)
_hostProviderStub = sinon.stub(require("@/hosts/host-provider"), "HostProvider").value(mockHostProvider)
})
afterEach(() => {
@@ -430,10 +430,10 @@ Then do this.`)
const content = await getSkillContent("my-skill", availableSkills)
expect(content).to.not.be.null
expect(content!.name).to.equal("my-skill")
expect(content!.instructions).to.include("These are the detailed instructions")
expect(content!.instructions).to.include("Step 1")
expect(content!.instructions).to.include("Step 2")
expect(content?.name).to.equal("my-skill")
expect(content?.instructions).to.include("These are the detailed instructions")
expect(content?.instructions).to.include("Step 1")
expect(content?.instructions).to.include("Step 2")
})
it("should return null for non-existent skill", async () => {
@@ -464,7 +464,7 @@ description: Test
const availableSkills = getAvailableSkills(allSkills)
const content = await getSkillContent("my-skill", availableSkills)
expect(content!.instructions).to.equal("Instructions with whitespace")
expect(content?.instructions).to.equal("Instructions with whitespace")
})
})
})
@@ -42,7 +42,7 @@ export async function readDirectoryRecursive(
export async function synchronizeRuleToggles(
rulesDirectoryPath: string,
currentToggles: ClineRulesToggles,
allowedFileExtension: string = "",
allowedFileExtension = "",
excludedPaths: string[][] = [],
): Promise<ClineRulesToggles> {
// Create a copy of toggles to modify
@@ -287,7 +287,7 @@ export async function ensureLocalClineDirExists(clinerulePath: string, defaultRu
if (exists && !(await isDirectory(clinerulePath))) {
// logic to convert .clinerules file into directory, and rename the rules file to {defaultRuleFilename}
const content = await fs.readFile(clinerulePath, "utf8")
const tempPath = clinerulePath + ".bak"
const tempPath = `${clinerulePath}.bak`
await fs.rename(clinerulePath, tempPath) // create backup
try {
await fs.mkdir(clinerulePath, { recursive: true })
@@ -9,27 +9,23 @@ import type { Controller } from "../index"
* @returns User credits data response
*/
export async function getUserOrganizations(controller: Controller, _request: EmptyRequest): Promise<UserOrganizationsResponse> {
try {
if (!controller.accountService) {
throw new Error("Account service not available")
}
// Fetch user organizations from the account service
const organizations = await controller.accountService.fetchUserOrganizationsRPC()
return UserOrganizationsResponse.create({
organizations:
organizations?.map((org) =>
UserOrganization.create({
active: org.active,
memberId: org.memberId,
name: org.name,
organizationId: org.organizationId,
roles: org.roles ? [...org.roles] : [],
}),
) || [],
})
} catch (error) {
throw error
if (!controller.accountService) {
throw new Error("Account service not available")
}
// Fetch user organizations from the account service
const organizations = await controller.accountService.fetchUserOrganizationsRPC()
return UserOrganizationsResponse.create({
organizations:
organizations?.map((org) =>
UserOrganization.create({
active: org.active,
memberId: org.memberId,
name: org.name,
organizationId: org.organizationId,
roles: org.roles ? [...org.roles] : [],
}),
) || [],
})
}
@@ -10,15 +10,11 @@ import type { Controller } from "../index"
* @returns Empty response
*/
export async function setUserOrganization(controller: Controller, request: UserOrganizationUpdateRequest): Promise<Empty> {
try {
if (!controller.accountService) {
throw new Error("Account service not available")
}
// Switch to the specified organization using the account service
await controller.accountService.switchAccount(request.organizationId)
await fetchRemoteConfig(controller)
return {}
} catch (error) {
throw error
if (!controller.accountService) {
throw new Error("Account service not available")
}
// Switch to the specified organization using the account service
await controller.accountService.switchAccount(request.organizationId)
await fetchRemoteConfig(controller)
return {}
}
@@ -27,14 +27,13 @@ export async function discoverBrowser(controller: Controller, _request: EmptyReq
message: `Successfully discovered and connected to Chrome at ${discoveredHost}`,
endpoint: result.endpoint || "",
})
} else {
return BrowserConnection.create({
success: false,
message:
"No Chrome instances found. Make sure Chrome is running with remote debugging enabled (--remote-debugging-port=9222).",
endpoint: "",
})
}
return BrowserConnection.create({
success: false,
message:
"No Chrome instances found. Make sure Chrome is running with remote debugging enabled (--remote-debugging-port=9222).",
endpoint: "",
})
} catch (error) {
return BrowserConnection.create({
success: false,
@@ -27,14 +27,13 @@ export async function testBrowserConnection(controller: Controller, request: Str
message: `Auto-discovered and tested connection to Chrome at ${discoveredHost}: ${result.message}`,
endpoint: result.endpoint || "",
})
} else {
return BrowserConnection.create({
success: false,
message:
"No Chrome instances found on the network. Make sure Chrome is running with remote debugging enabled (--remote-debugging-port=9222).",
endpoint: "",
})
}
return BrowserConnection.create({
success: false,
message:
"No Chrome instances found on the network. Make sure Chrome is running with remote debugging enabled (--remote-debugging-port=9222).",
endpoint: "",
})
} catch (error) {
return BrowserConnection.create({
success: false,
+1 -1
View File
@@ -33,7 +33,7 @@ async function getRelativePath(uriString: string): Promise<string> {
throw new Error(`Dropped file ${relativePath} is outside the workspace.`)
}
let result = "/" + relativePath.replace(/\\/g, "/")
let result = `/${relativePath.replace(/\\/g, "/")}`
if (await isDirectory(filePath)) {
result += "/"
}
@@ -27,7 +27,7 @@ export async function openFocusChainFile(controller: Controller, request: String
.reverse()
.find((m) => m.say === "task_progress")
if (lastProgressMessage && lastProgressMessage.text) {
if (lastProgressMessage?.text) {
initialFocusChainContent = extractFocusChainListFromText(lastProgressMessage.text) || undefined
}
}
+1 -5
View File
@@ -116,11 +116,7 @@ async function handleStreamingRequest(
request: GrpcRequest,
): Promise<void> {
// Create a response stream function
const responseStream: StreamingResponseHandler<any> = async (
response: any,
isLast: boolean = false,
sequenceNumber?: number,
) => {
const responseStream: StreamingResponseHandler<any> = async (response: any, isLast = false, sequenceNumber?: number) => {
await postMessageToWebview({
type: "grpc_response",
grpc_response: {
@@ -13,7 +13,7 @@ import { testHooks } from "@/core/controller/grpc-recorder/test-hooks"
*/
export class GrpcRecorderBuilder {
private fileHandler: LogFileHandler | null = null
private enabled: boolean = true
private enabled = true
private filters: GrpcRequestFilter[] = []
private hooks: GrpcPostRecordHook[] = []
@@ -73,7 +73,7 @@ export class GrpcRecorder implements IRecorder {
*
* @param request - The incoming gRPC request.
*/
public recordRequest(request: GrpcRequest, synthetic: boolean = false): void {
public recordRequest(request: GrpcRequest, synthetic = false): void {
if (this.shouldFilter(request)) {
return
}
+1 -1
View File
@@ -691,7 +691,7 @@ export class Controller {
let apiKey: string
try {
const response = await axios.post("https://openrouter.ai/api/v1/auth/keys", { code }, getAxiosSettings())
if (response.data && response.data.key) {
if (response.data?.key) {
apiKey = response.data.key
} else {
throw new Error("Invalid response from OpenRouter API")
+2 -3
View File
@@ -16,10 +16,9 @@ export async function updateMcpTimeout(controller: Controller, request: UpdateMc
const convertedMcpServers = convertMcpServersToProtoMcpServers(mcpServers)
Logger.log("convertedMcpServers", convertedMcpServers)
return McpServers.create({ mcpServers: convertedMcpServers })
} else {
Logger.error("Server name and timeout are required")
throw new Error("Server name and timeout are required")
}
Logger.error("Server name and timeout are required")
throw new Error("Server name and timeout are required")
} catch (error) {
Logger.error(`Failed to update timeout for server ${request.serverName}:`, error)
throw error
@@ -32,7 +32,7 @@ async function getToken(clientId: string, clientSecret: string, tokenUrl: string
client_secret: clientSecret,
})
const url = tokenUrl.replace(/\/+$/, "") + "/oauth/token"
const url = `${tokenUrl.replace(/\/+$/, "")}/oauth/token`
const response = await axios.post(url, payload, {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
...getAxiosSettings(),
@@ -105,7 +105,7 @@ async function fetchAiCoreDeploymentsAndOrchestration(
* @returns SapAiCoreModelsResponse with deployments and orchestration availability
*/
export async function getSapAiCoreModels(
controller: Controller,
_controller: Controller,
request: SapAiCoreModelsRequest,
): Promise<SapAiCoreModelsResponse> {
try {
@@ -74,7 +74,7 @@ async function fetchAndCacheModels(controller: Controller): Promise<Record<strin
throw new Error("Invalid Groq API key format. Groq API keys should start with 'gsk_'")
}
Logger.log("Fetching Groq models with API key:", cleanApiKey.substring(0, 10) + "...")
Logger.log("Fetching Groq models with API key:", `${cleanApiKey.substring(0, 10)}...`)
const response = await axios.get("https://api.groq.com/openai/v1/models", {
headers: {
@@ -56,7 +56,7 @@ export async function refreshHicapModels(controller: Controller, _request: Empty
}
}
await fs.writeFile(hicapModelsFilePath, JSON.stringify(models))
} catch (error) {
} catch (_error) {
// If we failed to fetch models, try to read cached models
/* const cachedModels = await readHicapModels(controller)
if (cachedModels) {
@@ -70,14 +70,14 @@ export async function refreshHicapModels(controller: Controller, _request: Empty
/**
* Reads cached OpenRouter models from disk
*/
async function readHicapModels(controller: Controller): Promise<Record<string, OpenRouterModelInfo> | undefined> {
async function _readHicapModels(controller: Controller): Promise<Record<string, OpenRouterModelInfo> | undefined> {
const hicapModelsFilePath = path.join(await ensureCacheDirectoryExists(controller), GlobalFileNames.hicapModels)
const fileExists = await fileExistsAtPath(hicapModelsFilePath)
if (fileExists) {
try {
const fileContents = await fs.readFile(hicapModelsFilePath, "utf8")
return JSON.parse(fileContents)
} catch (error) {
} catch (_error) {
return undefined
}
}
@@ -181,7 +181,7 @@ export async function refreshOcaModels(controller: Controller, request: StringRe
}
HostProvider.window.showMessage({
type: ShowMessageType.ERROR,
message: `Error refreshing OCA models. ` + userMsg + ` opc-request-id: ${headers["opc-request-id"]}`,
message: `Error refreshing OCA models. ${userMsg} opc-request-id: ${headers["opc-request-id"]}`,
})
return OcaCompatibleModelInfo.create({ error: userMsg })
}
@@ -24,7 +24,7 @@ export async function refreshOpenAiModels(_controller: Controller, request: Open
const config: AxiosRequestConfig = {}
if (request.apiKey) {
config["headers"] = { Authorization: `Bearer ${request.apiKey}` }
config.headers = { Authorization: `Bearer ${request.apiKey}` }
}
const response = await axios.get(`${request.baseUrl}/models`, { ...config, ...getAxiosSettings() })

Some files were not shown because too many files have changed in this diff Show More