Something went wrong. Try again.
source dump of claude code forked from oppi.li/claude-code
Something went wrong. Try again.
4.3 kB · 156 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157import type { TerminalNotification } from '../ink/useTerminalNotification.js'import { getGlobalConfig } from '../utils/config.js'import { env } from '../utils/env.js'import { execFileNoThrow } from '../utils/execFileNoThrow.js'import { executeNotificationHooks } from '../utils/hooks.js'import { logError } from '../utils/log.js'import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent,} from './analytics/index.js'
export type NotificationOptions = { message: string title?: string notificationType: string}
export async function sendNotification( notif: NotificationOptions, terminal: TerminalNotification,): Promise<void> { const config = getGlobalConfig() const channel = config.preferredNotifChannel
await executeNotificationHooks(notif)
const methodUsed = await sendToChannel(channel, notif, terminal)
logEvent('tengu_notification_method_used', { configured_channel: channel as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, method_used: methodUsed as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, term: env.terminal as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, })}
const DEFAULT_TITLE = 'Claude Code'
async function sendToChannel( channel: string, opts: NotificationOptions, terminal: TerminalNotification,): Promise<string> { const title = opts.title || DEFAULT_TITLE
try { switch (channel) { case 'auto': return sendAuto(opts, terminal) case 'iterm2': terminal.notifyITerm2(opts) return 'iterm2' case 'iterm2_with_bell': terminal.notifyITerm2(opts) terminal.notifyBell() return 'iterm2_with_bell' case 'kitty': terminal.notifyKitty({ ...opts, title, id: generateKittyId() }) return 'kitty' case 'ghostty': terminal.notifyGhostty({ ...opts, title }) return 'ghostty' case 'terminal_bell': terminal.notifyBell() return 'terminal_bell' case 'notifications_disabled': return 'disabled' default: return 'none' } } catch { return 'error' }}
async function sendAuto( opts: NotificationOptions, terminal: TerminalNotification,): Promise<string> { const title = opts.title || DEFAULT_TITLE
switch (env.terminal) { case 'Apple_Terminal': { const bellDisabled = await isAppleTerminalBellDisabled() if (bellDisabled) { terminal.notifyBell() return 'terminal_bell' } return 'no_method_available' } case 'iTerm.app': terminal.notifyITerm2(opts) return 'iterm2' case 'kitty': terminal.notifyKitty({ ...opts, title, id: generateKittyId() }) return 'kitty' case 'ghostty': terminal.notifyGhostty({ ...opts, title }) return 'ghostty' default: return 'no_method_available' }}
function generateKittyId(): number { return Math.floor(Math.random() * 10000)}
async function isAppleTerminalBellDisabled(): Promise<boolean> { try { if (env.terminal !== 'Apple_Terminal') { return false }
const osascriptResult = await execFileNoThrow('osascript', [ '-e', 'tell application "Terminal" to name of current settings of front window', ]) const currentProfile = osascriptResult.stdout.trim()
if (!currentProfile) { return false }
const defaultsOutput = await execFileNoThrow('defaults', [ 'export', 'com.apple.Terminal', '-', ])
if (defaultsOutput.code !== 0) { return false }
// Lazy-load plist (~280KB with xmlbuilder+@xmldom) — only hit on // Apple_Terminal with auto-channel, which is a small fraction of users. const plist = await import('plist') const parsed: Record<string, unknown> = plist.parse(defaultsOutput.stdout) const windowSettings = parsed?.['Window Settings'] as | Record<string, unknown> | undefined const profileSettings = windowSettings?.[currentProfile] as | Record<string, unknown> | undefined
if (!profileSettings) { return false }
return profileSettings.Bell === false } catch (error) { logError(error) return false }}