11import { env } from '@codebuff/common/env'
22import { extractApiErrorDetails } from '@codebuff/common/util/error'
3+ import { formatFreebuffHardBlockedPrivacySignals } from '@codebuff/common/util/freebuff-privacy'
34
45import type { ChatMessage } from '../types/chat'
56import type {
@@ -49,17 +50,11 @@ export const isOutOfCreditsError = (error: unknown): boolean => {
4950 * Standardized on statusCode === 403 + error === 'free_mode_unavailable'.
5051 */
5152export const isFreeModeUnavailableError = ( error : unknown ) : boolean => {
52- if (
53- error &&
54- typeof error === 'object' &&
55- 'statusCode' in error &&
56- ( error as { statusCode : unknown } ) . statusCode === 403 &&
57- 'error' in error &&
58- ( error as { error : unknown } ) . error === 'free_mode_unavailable'
59- ) {
60- return true
61- }
62- return false
53+ const details = getCliApiErrorDetails ( error )
54+ return (
55+ details . statusCode === 403 &&
56+ details . errorCode === 'free_mode_unavailable'
57+ )
6358}
6459
6560const getTopLevelApiErrorDetails = (
@@ -68,12 +63,20 @@ const getTopLevelApiErrorDetails = (
6863 statusCode ?: number
6964 errorCode ?: string
7065 message ?: string
66+ countryCode ?: string
67+ countryBlockReason ?: string
68+ ipPrivacySignals ?: string [ ]
7169} => {
7270 if ( ! error || typeof error !== 'object' ) return { }
7371 const statusCode = ( error as { statusCode ?: unknown } ) . statusCode
7472 const status = ( error as { status ?: unknown } ) . status
7573 const errorCode = ( error as { error ?: unknown } ) . error
7674 const message = ( error as { message ?: unknown } ) . message
75+ const countryCode = ( error as { countryCode ?: unknown } ) . countryCode
76+ const countryBlockReason = ( error as { countryBlockReason ?: unknown } )
77+ . countryBlockReason
78+ const ipPrivacySignals = ( error as { ipPrivacySignals ?: unknown } )
79+ . ipPrivacySignals
7780 const resolvedStatusCode =
7881 typeof statusCode === 'number'
7982 ? statusCode
@@ -85,6 +88,14 @@ const getTopLevelApiErrorDetails = (
8588 ...( resolvedStatusCode !== undefined && { statusCode : resolvedStatusCode } ) ,
8689 ...( typeof errorCode === 'string' && { errorCode } ) ,
8790 ...( typeof message === 'string' && message . length > 0 && { message } ) ,
91+ ...( typeof countryCode === 'string' &&
92+ countryCode . length > 0 && { countryCode } ) ,
93+ ...( typeof countryBlockReason === 'string' && { countryBlockReason } ) ,
94+ ...( Array . isArray ( ipPrivacySignals ) && {
95+ ipPrivacySignals : ipPrivacySignals . filter (
96+ ( signal ) : signal is string => typeof signal === 'string' ,
97+ ) ,
98+ } ) ,
8899 }
89100}
90101
@@ -97,6 +108,10 @@ const getCliApiErrorDetails = (error: unknown) => {
97108 errorCode : topLevel . errorCode ?? parsed . errorCode ,
98109 // Prefer responseBody messages over top-level HTTP status text.
99110 message : parsed . message ?? topLevel . message ,
111+ countryCode : topLevel . countryCode ?? parsed . countryCode ,
112+ countryBlockReason :
113+ topLevel . countryBlockReason ?? parsed . countryBlockReason ,
114+ ipPrivacySignals : topLevel . ipPrivacySignals ?? parsed . ipPrivacySignals ,
100115 }
101116}
102117
@@ -119,11 +134,7 @@ export const getCountryBlockFromFreeModeError = (
119134 ipPrivacySignals ?: FreebuffIpPrivacySignal [ ]
120135} | null => {
121136 if ( ! isFreeModeUnavailableError ( error ) ) return null
122- const errorDetails = error as {
123- countryCode ?: unknown
124- countryBlockReason ?: unknown
125- ipPrivacySignals ?: unknown
126- }
137+ const errorDetails = getCliApiErrorDetails ( error )
127138 const countryCode =
128139 typeof errorDetails . countryCode === 'string' &&
129140 errorDetails . countryCode . length > 0
@@ -136,13 +147,23 @@ export const getCountryBlockFromFreeModeError = (
136147 typeof errorDetails . countryBlockReason === 'string'
137148 ? ( errorDetails . countryBlockReason as FreebuffCountryBlockReason )
138149 : undefined ,
139- ipPrivacySignals : Array . isArray ( errorDetails . ipPrivacySignals )
140- ? errorDetails . ipPrivacySignals . filter (
141- ( signal ) : signal is FreebuffIpPrivacySignal =>
142- typeof signal === 'string' ,
143- )
144- : undefined ,
150+ ipPrivacySignals : errorDetails . ipPrivacySignals as
151+ | FreebuffIpPrivacySignal [ ]
152+ | undefined ,
153+ }
154+ }
155+
156+ export const getFreeModeUnavailableErrorMessage = (
157+ error : unknown ,
158+ ) : string => {
159+ const details = getCliApiErrorDetails ( error )
160+ const block = getCountryBlockFromFreeModeError ( error )
161+ if ( block ?. countryBlockReason === 'anonymous_network' ) {
162+ return `${ IS_FREEBUFF ? 'Freebuff' : 'Free mode' } cannot be used from ${ formatFreebuffHardBlockedPrivacySignals (
163+ block . ipPrivacySignals ,
164+ ) } traffic. Please disable it and try again.`
145165 }
166+ return details . message ?? FREE_MODE_UNAVAILABLE_MESSAGE
146167}
147168
148169/**
0 commit comments