source dump of claude code
at main 54 lines 2.3 kB view raw
1import { feature } from 'bun:bundle' 2import { getFeatureValue_CACHED_MAY_BE_STALE } from '../services/analytics/growthbook.js' 3import { 4 getClaudeAIOAuthTokens, 5 isAnthropicAuthEnabled, 6} from '../utils/auth.js' 7 8/** 9 * Kill-switch check for voice mode. Returns true unless the 10 * `tengu_amber_quartz_disabled` GrowthBook flag is flipped on (emergency 11 * off). Default `false` means a missing/stale disk cache reads as "not 12 * killed" — so fresh installs get voice working immediately without 13 * waiting for GrowthBook init. Use this for deciding whether voice mode 14 * should be *visible* (e.g., command registration, config UI). 15 */ 16export function isVoiceGrowthBookEnabled(): boolean { 17 // Positive ternary pattern — see docs/feature-gating.md. 18 // Negative pattern (if (!feature(...)) return) does not eliminate 19 // inline string literals from external builds. 20 return feature('VOICE_MODE') 21 ? !getFeatureValue_CACHED_MAY_BE_STALE('tengu_amber_quartz_disabled', false) 22 : false 23} 24 25/** 26 * Auth-only check for voice mode. Returns true when the user has a valid 27 * Anthropic OAuth token. Backed by the memoized getClaudeAIOAuthTokens — 28 * first call spawns `security` on macOS (~20-50ms), subsequent calls are 29 * cache hits. The memoize clears on token refresh (~once/hour), so one 30 * cold spawn per refresh is expected. Cheap enough for usage-time checks. 31 */ 32export function hasVoiceAuth(): boolean { 33 // Voice mode requires Anthropic OAuth — it uses the voice_stream 34 // endpoint on claude.ai which is not available with API keys, 35 // Bedrock, Vertex, or Foundry. 36 if (!isAnthropicAuthEnabled()) { 37 return false 38 } 39 // isAnthropicAuthEnabled only checks the auth *provider*, not whether 40 // a token exists. Without this check, the voice UI renders but 41 // connectVoiceStream fails silently when the user isn't logged in. 42 const tokens = getClaudeAIOAuthTokens() 43 return Boolean(tokens?.accessToken) 44} 45 46/** 47 * Full runtime check: auth + GrowthBook kill-switch. Callers: `/voice` 48 * (voice.ts, voice/index.ts), ConfigTool, VoiceModeNotice — command-time 49 * paths where a fresh keychain read is acceptable. For React render 50 * paths use useVoiceEnabled() instead (memoizes the auth half). 51 */ 52export function isVoiceModeEnabled(): boolean { 53 return hasVoiceAuth() && isVoiceGrowthBookEnabled() 54}