@@ -3,7 +3,11 @@ import { claudeCodeDefaultModelId, type ClaudeCodeModelId, claudeCodeModels } fr
33import { type ApiHandler } from ".."
44import { ApiStreamUsageChunk , type ApiStream } from "../transform/stream"
55import { runClaudeCode } from "../../integrations/claude-code/run"
6+ < << << << HEAD
67import { filterMessagesForClaudeCode } from "../../integrations/claude-code/message-filter"
8+ = === ===
9+ import { ClaudeCodeMessage } from "../../integrations/claude-code/types"
10+ > >>> >>> 4 fa735de3 ( feat : add Claude Code provider for local CLI integration ( #4864 ) )
711import { BaseProvider } from "./base-provider"
812import { t } from "../../i18n"
913import { ApiHandlerOptions } from "../../shared/api"
@@ -17,16 +21,51 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
1721 }
1822
1923 override async * createMessage ( systemPrompt : string , messages : Anthropic . Messages . MessageParam [ ] ) : ApiStream {
24+ < < < << << HEAD
2025 // Filter out image blocks since Claude Code doesn't support them
2126 const filteredMessages = filterMessagesForClaudeCode ( messages )
2227
2328 const claudeProcess = runClaudeCode ( {
2429 systemPrompt,
2530 messages : filteredMessages ,
31+ = === ===
32+ const claudeProcess = runClaudeCode ( {
33+ systemPrompt,
34+ messages,
35+ > >>> >>> 4 fa735de3 ( feat : add Claude Code provider for local CLI integration ( #4864 ) )
2636 path : this . options . claudeCodePath ,
2737 modelId : this . getModel ( ) . id ,
2838 } )
2939
40+ < << << << HEAD
41+ === === =
42+ const dataQueue : string [ ] = [ ]
43+ let processError = null
44+ let errorOutput = ""
45+ let exitCode : number | null = null
46+
47+ claudeProcess . stdout . on ( "data" , ( data ) => {
48+ const output = data . toString ( )
49+ const lines = output . split ( "\n" ) . filter ( ( line : string ) => line . trim ( ) !== "" )
50+
51+ for ( const line of lines ) {
52+ dataQueue . push ( line )
53+ }
54+ } )
55+
56+ claudeProcess . stderr . on ( "data" , ( data ) => {
57+ errorOutput += data . toString ( )
58+ } )
59+
60+ claudeProcess . on ( "close" , ( code ) => {
61+ exitCode = code
62+ } )
63+
64+ claudeProcess . on ( "error" , ( error ) => {
65+ processError = error
66+ } )
67+
68+ >>> > >>> 4 fa735de3 ( feat : add Claude Code provider for local CLI integration ( #4864 ) )
3069 // Usage is included with assistant messages,
3170 // but cost is included in the result chunk
3271 let usage : ApiStreamUsageChunk = {
@@ -37,27 +76,62 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
3776 cacheWriteTokens : 0 ,
3877 }
3978
79+ << < < < << HEAD
4080 let isPaidUsage = true
4181
4282 for await ( const chunk of claudeProcess ) {
4383 if ( typeof chunk === "string" ) {
4484 yield {
4585 type : "text" ,
4686 text : chunk ,
87+ = === ===
88+ while ( exitCode ! == 0 | | dataQueue . length > 0 ) {
89+ if ( dataQueue . length === 0 ) {
90+ await new Promise ( ( resolve ) => setImmediate ( resolve ) )
91+ }
92+
93+ if ( exitCode !== null && exitCode !== 0 ) {
94+ if ( errorOutput ) {
95+ throw new Error (
96+ t ( "common:errors.claudeCode.processExitedWithError" , {
97+ exitCode,
98+ output : errorOutput . trim ( ) ,
99+ } ) ,
100+ )
101+ }
102+ throw new Error ( t ( "common:errors.claudeCode.processExited" , { exitCode } ) )
103+ }
104+
105+ const data = dataQueue . shift ( )
106+ if ( ! data ) {
107+ continue
108+ }
109+
110+ const chunk = this . attemptParseChunk ( data )
111+
112+ if ( ! chunk ) {
113+ yield {
114+ type : "text" ,
115+ text : data || "" ,
116+ > >>> >>> 4 fa735de3 ( feat : add Claude Code provider for local CLI integration ( #4864 ) )
47117 }
48118
49119 continue
50120 }
51121
52122 if ( chunk . type === "system" && chunk . subtype === "init" ) {
123+ << < < < << HEAD
53124 // Based on my tests, subscription usage sets the `apiKeySource` to "none"
54125 isPaidUsage = chunk . apiKeySource !== "none"
126+ === = ===
127+ >>> > >>> 4 fa735de3 ( feat : add Claude Code provider for local CLI integration ( #4864 ) )
55128 continue
56129 }
57130
58131 if ( chunk . type === "assistant" && "message" in chunk ) {
59132 const message = chunk . message
60133
134+ << < < < << HEAD
61135 if ( message . stop_reason !== null ) {
62136 const content = "text" in message . content [ 0 ] ? message . content [ 0 ] : undefined
63137
@@ -105,6 +179,28 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
105179 case "tool_use" :
106180 console . error ( `tool_use is not supported yet. Received: ${ JSON . stringify ( content ) } ` )
107181 break
182+ = === ===
183+ if ( message . stop_reason !== null && message . stop_reason !== "tool_use" ) {
184+ const errorMessage =
185+ message . content [ 0 ] ?. text ||
186+ t ( "common:errors.claudeCode.stoppedWithReason" , { reason : message . stop_reason } )
187+
188+ if ( errorMessage . includes ( "Invalid model name" ) ) {
189+ throw new Error ( errorMessage + `\n\n${ t ( "common:errors.claudeCode.apiKeyModelPlanMismatch" ) } ` )
190+ }
191+
192+ throw new Error ( errorMessage )
193+ }
194+
195+ for ( const content of message . content ) {
196+ if ( content . type === "text" ) {
197+ yield {
198+ type : "text" ,
199+ text : content . text ,
200+ }
201+ } else {
202+ console . warn ( "Unsupported content type:" , content . type )
203+ >>> > >>> 4 fa735de3 ( feat : add Claude Code provider for local CLI integration ( #4864 ) )
108204 }
109205 }
110206
@@ -118,10 +214,23 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
118214 }
119215
120216 if ( chunk . type === "result " && "result " in chunk ) {
217+ << << << < HEAD
121218 usage . totalCost = isPaidUsage ? chunk . total_cost_usd : 0
122219
123220 yield usage
124221 }
222+ === = ===
223+ // Only use the cost from the CLI if provided
224+ // Don't calculate cost as it may be $0 for subscription users
225+ usage . totalCost = chunk . cost_usd ?? 0
226+
227+ yield usage
228+ }
229+
230+ if ( processError ) {
231+ throw processError
232+ }
233+ >>> >>> > 4 fa735de3 ( feat : add Claude Code provider for local CLI integration ( #4864 ) )
125234 }
126235 }
127236
@@ -138,10 +247,19 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
138247 }
139248 }
140249
250+ << < < < << HEAD
141251 private attemptParse ( str : string ) {
142252 try {
143253 return JSON . parse ( str )
144254 } catch ( err ) {
255+ === = ===
256+ // TODO: Validate instead of parsing
257+ private attemptParseChunk ( data : string ) : ClaudeCodeMessage | null {
258+ try {
259+ return JSON . parse ( data )
260+ } catch ( error ) {
261+ console . error ( "Error parsing chunk:" , error )
262+ >>> > >>> 4 fa735de3 ( feat : add Claude Code provider for local CLI integration ( #4864 ) )
145263 return null
146264 }
147265 }
0 commit comments