Download src/services/notifier.ts from chenbhao/codev: direct link, hf CLI and curl.
- Browser
- Download file 4.26 kB
-
https://huggingface.co/chenbhao/codev/resolve/main/src/services/notifier.ts
- Command line
-
hf download hf://chenbhao/codev/src/services/notifier.ts
-
curl -L -o notifier.ts https://huggingface.co/chenbhao/codev/resolve/main/src/services/notifier.ts
4.26 kB
| import 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 | |
| } | |
| } | |