/ cli / transports / transportUtils.ts
transportUtils.ts
 1  import { URL } from 'url'
 2  import { isEnvTruthy } from '../../utils/envUtils.js'
 3  import { HybridTransport } from './HybridTransport.js'
 4  import { SSETransport } from './SSETransport.js'
 5  import type { Transport } from './Transport.js'
 6  import { WebSocketTransport } from './WebSocketTransport.js'
 7  
 8  /**
 9   * Helper function to get the appropriate transport for a URL.
10   *
11   * Transport selection priority:
12   * 1. SSETransport (SSE reads + POST writes) when CLAUDE_CODE_USE_CCR_V2 is set
13   * 2. HybridTransport (WS reads + POST writes) when CLAUDE_CODE_POST_FOR_SESSION_INGRESS_V2 is set
14   * 3. WebSocketTransport (WS reads + WS writes) — default
15   */
16  export function getTransportForUrl(
17    url: URL,
18    headers: Record<string, string> = {},
19    sessionId?: string,
20    refreshHeaders?: () => Record<string, string>,
21  ): Transport {
22    if (isEnvTruthy(process.env.CLAUDE_CODE_USE_CCR_V2)) {
23      // v2: SSE for reads, HTTP POST for writes
24      // --sdk-url is the session URL (.../sessions/{id});
25      // derive the SSE stream URL by appending /worker/events/stream
26      const sseUrl = new URL(url.href)
27      if (sseUrl.protocol === 'wss:') {
28        sseUrl.protocol = 'https:'
29      } else if (sseUrl.protocol === 'ws:') {
30        sseUrl.protocol = 'http:'
31      }
32      sseUrl.pathname =
33        sseUrl.pathname.replace(/\/$/, '') + '/worker/events/stream'
34      return new SSETransport(sseUrl, headers, sessionId, refreshHeaders)
35    }
36  
37    if (url.protocol === 'ws:' || url.protocol === 'wss:') {
38      if (isEnvTruthy(process.env.CLAUDE_CODE_POST_FOR_SESSION_INGRESS_V2)) {
39        return new HybridTransport(url, headers, sessionId, refreshHeaders)
40      }
41      return new WebSocketTransport(url, headers, sessionId, refreshHeaders)
42    } else {
43      throw new Error(`Unsupported protocol: ${url.protocol}`)
44    }
45  }