source dump of claude code
at main 140 lines 4.7 kB view raw
1import { 2 getSessionIngressToken, 3 setSessionIngressToken, 4} from '../bootstrap/state.js' 5import { 6 CCR_SESSION_INGRESS_TOKEN_PATH, 7 maybePersistTokenForSubprocesses, 8 readTokenFromWellKnownFile, 9} from './authFileDescriptor.js' 10import { logForDebugging } from './debug.js' 11import { errorMessage } from './errors.js' 12import { getFsImplementation } from './fsOperations.js' 13 14/** 15 * Read token via file descriptor, falling back to well-known file. 16 * Uses global state to cache the result since file descriptors can only be read once. 17 */ 18function getTokenFromFileDescriptor(): string | null { 19 // Check if we've already attempted to read the token 20 const cachedToken = getSessionIngressToken() 21 if (cachedToken !== undefined) { 22 return cachedToken 23 } 24 25 const fdEnv = process.env.CLAUDE_CODE_WEBSOCKET_AUTH_FILE_DESCRIPTOR 26 if (!fdEnv) { 27 // No FD env var — either we're not in CCR, or we're a subprocess whose 28 // parent stripped the (useless) FD env var. Try the well-known file. 29 const path = 30 process.env.CLAUDE_SESSION_INGRESS_TOKEN_FILE ?? 31 CCR_SESSION_INGRESS_TOKEN_PATH 32 const fromFile = readTokenFromWellKnownFile(path, 'session ingress token') 33 setSessionIngressToken(fromFile) 34 return fromFile 35 } 36 37 const fd = parseInt(fdEnv, 10) 38 if (Number.isNaN(fd)) { 39 logForDebugging( 40 `CLAUDE_CODE_WEBSOCKET_AUTH_FILE_DESCRIPTOR must be a valid file descriptor number, got: ${fdEnv}`, 41 { level: 'error' }, 42 ) 43 setSessionIngressToken(null) 44 return null 45 } 46 47 try { 48 // Read from the file descriptor 49 // Use /dev/fd on macOS/BSD, /proc/self/fd on Linux 50 const fsOps = getFsImplementation() 51 const fdPath = 52 process.platform === 'darwin' || process.platform === 'freebsd' 53 ? `/dev/fd/${fd}` 54 : `/proc/self/fd/${fd}` 55 56 const token = fsOps.readFileSync(fdPath, { encoding: 'utf8' }).trim() 57 if (!token) { 58 logForDebugging('File descriptor contained empty token', { 59 level: 'error', 60 }) 61 setSessionIngressToken(null) 62 return null 63 } 64 logForDebugging(`Successfully read token from file descriptor ${fd}`) 65 setSessionIngressToken(token) 66 maybePersistTokenForSubprocesses( 67 CCR_SESSION_INGRESS_TOKEN_PATH, 68 token, 69 'session ingress token', 70 ) 71 return token 72 } catch (error) { 73 logForDebugging( 74 `Failed to read token from file descriptor ${fd}: ${errorMessage(error)}`, 75 { level: 'error' }, 76 ) 77 // FD env var was set but read failed — typically a subprocess that 78 // inherited the env var but not the FD (ENXIO). Try the well-known file. 79 const path = 80 process.env.CLAUDE_SESSION_INGRESS_TOKEN_FILE ?? 81 CCR_SESSION_INGRESS_TOKEN_PATH 82 const fromFile = readTokenFromWellKnownFile(path, 'session ingress token') 83 setSessionIngressToken(fromFile) 84 return fromFile 85 } 86} 87 88/** 89 * Get session ingress authentication token. 90 * 91 * Priority order: 92 * 1. Environment variable (CLAUDE_CODE_SESSION_ACCESS_TOKEN) — set at spawn time, 93 * updated in-process via updateSessionIngressAuthToken or 94 * update_environment_variables stdin message from the parent bridge process. 95 * 2. File descriptor (legacy path) — CLAUDE_CODE_WEBSOCKET_AUTH_FILE_DESCRIPTOR, 96 * read once and cached. 97 * 3. Well-known file — CLAUDE_SESSION_INGRESS_TOKEN_FILE env var path, or 98 * /home/claude/.claude/remote/.session_ingress_token. Covers subprocesses 99 * that can't inherit the FD. 100 */ 101export function getSessionIngressAuthToken(): string | null { 102 // 1. Check environment variable 103 const envToken = process.env.CLAUDE_CODE_SESSION_ACCESS_TOKEN 104 if (envToken) { 105 return envToken 106 } 107 108 // 2. Check file descriptor (legacy path), with file fallback 109 return getTokenFromFileDescriptor() 110} 111 112/** 113 * Build auth headers for the current session token. 114 * Session keys (sk-ant-sid) use Cookie auth + X-Organization-Uuid; 115 * JWTs use Bearer auth. 116 */ 117export function getSessionIngressAuthHeaders(): Record<string, string> { 118 const token = getSessionIngressAuthToken() 119 if (!token) return {} 120 if (token.startsWith('sk-ant-sid')) { 121 const headers: Record<string, string> = { 122 Cookie: `sessionKey=${token}`, 123 } 124 const orgUuid = process.env.CLAUDE_CODE_ORGANIZATION_UUID 125 if (orgUuid) { 126 headers['X-Organization-Uuid'] = orgUuid 127 } 128 return headers 129 } 130 return { Authorization: `Bearer ${token}` } 131} 132 133/** 134 * Update the session ingress auth token in-process by setting the env var. 135 * Used by the REPL bridge to inject a fresh token after reconnection 136 * without restarting the process. 137 */ 138export function updateSessionIngressAuthToken(token: string): void { 139 process.env.CLAUDE_CODE_SESSION_ACCESS_TOKEN = token 140}