source dump of claude code
at main 141 lines 4.2 kB view raw
1import { 2 type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, 3 logEvent, 4} from '../services/analytics/index.js' 5import { logForDebugging } from '../utils/debug.js' 6import { errorMessage } from '../utils/errors.js' 7import { jsonStringify } from '../utils/slowOperations.js' 8 9const DEBUG_MSG_LIMIT = 2000 10 11const SECRET_FIELD_NAMES = [ 12 'session_ingress_token', 13 'environment_secret', 14 'access_token', 15 'secret', 16 'token', 17] 18 19const SECRET_PATTERN = new RegExp( 20 `"(${SECRET_FIELD_NAMES.join('|')})"\\s*:\\s*"([^"]*)"`, 21 'g', 22) 23 24const REDACT_MIN_LENGTH = 16 25 26export function redactSecrets(s: string): string { 27 return s.replace(SECRET_PATTERN, (_match, field: string, value: string) => { 28 if (value.length < REDACT_MIN_LENGTH) { 29 return `"${field}":"[REDACTED]"` 30 } 31 const redacted = `${value.slice(0, 8)}...${value.slice(-4)}` 32 return `"${field}":"${redacted}"` 33 }) 34} 35 36/** Truncate a string for debug logging, collapsing newlines. */ 37export function debugTruncate(s: string): string { 38 const flat = s.replace(/\n/g, '\\n') 39 if (flat.length <= DEBUG_MSG_LIMIT) { 40 return flat 41 } 42 return flat.slice(0, DEBUG_MSG_LIMIT) + `... (${flat.length} chars)` 43} 44 45/** Truncate a JSON-serializable value for debug logging. */ 46export function debugBody(data: unknown): string { 47 const raw = typeof data === 'string' ? data : jsonStringify(data) 48 const s = redactSecrets(raw) 49 if (s.length <= DEBUG_MSG_LIMIT) { 50 return s 51 } 52 return s.slice(0, DEBUG_MSG_LIMIT) + `... (${s.length} chars)` 53} 54 55/** 56 * Extract a descriptive error message from an axios error (or any error). 57 * For HTTP errors, appends the server's response body message if available, 58 * since axios's default message only includes the status code. 59 */ 60export function describeAxiosError(err: unknown): string { 61 const msg = errorMessage(err) 62 if (err && typeof err === 'object' && 'response' in err) { 63 const response = (err as { response?: { data?: unknown } }).response 64 if (response?.data && typeof response.data === 'object') { 65 const data = response.data as Record<string, unknown> 66 const detail = 67 typeof data.message === 'string' 68 ? data.message 69 : typeof data.error === 'object' && 70 data.error && 71 'message' in data.error && 72 typeof (data.error as Record<string, unknown>).message === 73 'string' 74 ? (data.error as Record<string, unknown>).message 75 : undefined 76 if (detail) { 77 return `${msg}: ${detail}` 78 } 79 } 80 } 81 return msg 82} 83 84/** 85 * Extract the HTTP status code from an axios error, if present. 86 * Returns undefined for non-HTTP errors (e.g. network failures). 87 */ 88export function extractHttpStatus(err: unknown): number | undefined { 89 if ( 90 err && 91 typeof err === 'object' && 92 'response' in err && 93 (err as { response?: { status?: unknown } }).response && 94 typeof (err as { response: { status?: unknown } }).response.status === 95 'number' 96 ) { 97 return (err as { response: { status: number } }).response.status 98 } 99 return undefined 100} 101 102/** 103 * Pull a human-readable message out of an API error response body. 104 * Checks `data.message` first, then `data.error.message`. 105 */ 106export function extractErrorDetail(data: unknown): string | undefined { 107 if (!data || typeof data !== 'object') return undefined 108 if ('message' in data && typeof data.message === 'string') { 109 return data.message 110 } 111 if ( 112 'error' in data && 113 data.error !== null && 114 typeof data.error === 'object' && 115 'message' in data.error && 116 typeof data.error.message === 'string' 117 ) { 118 return data.error.message 119 } 120 return undefined 121} 122 123/** 124 * Log a bridge init skip — debug message + `tengu_bridge_repl_skipped` 125 * analytics event. Centralizes the event name and the AnalyticsMetadata 126 * cast so call sites don't each repeat the 5-line boilerplate. 127 */ 128export function logBridgeSkip( 129 reason: string, 130 debugMsg?: string, 131 v2?: boolean, 132): void { 133 if (debugMsg) { 134 logForDebugging(debugMsg) 135 } 136 logEvent('tengu_bridge_repl_skipped', { 137 reason: 138 reason as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, 139 ...(v2 !== undefined && { v2 }), 140 }) 141}