diff --git a/backend/internal/handler/dto/mappers.go b/backend/internal/handler/dto/mappers.go
index f1991c30..18bbec31 100644
--- a/backend/internal/handler/dto/mappers.go
+++ b/backend/internal/handler/dto/mappers.go
@@ -164,6 +164,17 @@ func AccountFromServiceShallow(a *service.Account) *Account {
}
}
+ if scopeLimits := a.GetAntigravityScopeRateLimits(); len(scopeLimits) > 0 {
+ out.ScopeRateLimits = make(map[string]ScopeRateLimitInfo, len(scopeLimits))
+ now := time.Now()
+ for scope, remainingSec := range scopeLimits {
+ out.ScopeRateLimits[scope] = ScopeRateLimitInfo{
+ ResetAt: now.Add(time.Duration(remainingSec) * time.Second),
+ RemainingSec: remainingSec,
+ }
+ }
+ }
+
return out
}
diff --git a/backend/internal/handler/dto/types.go b/backend/internal/handler/dto/types.go
index b425523b..97bd2eca 100644
--- a/backend/internal/handler/dto/types.go
+++ b/backend/internal/handler/dto/types.go
@@ -2,6 +2,11 @@ package dto
import "time"
+type ScopeRateLimitInfo struct {
+ ResetAt time.Time `json:"reset_at"`
+ RemainingSec int64 `json:"remaining_sec"`
+}
+
type User struct {
ID int64 `json:"id"`
Email string `json:"email"`
@@ -97,6 +102,9 @@ type Account struct {
RateLimitResetAt *time.Time `json:"rate_limit_reset_at"`
OverloadUntil *time.Time `json:"overload_until"`
+ // Antigravity scope 级限流状态(从 extra 提取)
+ ScopeRateLimits map[string]ScopeRateLimitInfo `json:"scope_rate_limits,omitempty"`
+
TempUnschedulableUntil *time.Time `json:"temp_unschedulable_until"`
TempUnschedulableReason string `json:"temp_unschedulable_reason"`
diff --git a/frontend/src/components/account/AccountStatusIndicator.vue b/frontend/src/components/account/AccountStatusIndicator.vue
index 7dae33bb..3c8aba97 100644
--- a/frontend/src/components/account/AccountStatusIndicator.vue
+++ b/frontend/src/components/account/AccountStatusIndicator.vue
@@ -62,6 +62,27 @@
+
+
+
+
+
+ {{ formatScopeName(item.scope) }}
+
+
+
+ {{ t('admin.accounts.status.scopeRateLimitedUntil', { scope: formatScopeName(item.scope), time: formatTime(item.reset_at) }) }}
+
+
+
+
+
{
return new Date(props.account.rate_limit_reset_at) > new Date()
})
+// Computed: active scope rate limits (Antigravity)
+const activeScopeRateLimits = computed(() => {
+ const scopeLimits = props.account.scope_rate_limits
+ if (!scopeLimits) return []
+ const now = new Date()
+ return Object.entries(scopeLimits)
+ .filter(([, info]) => new Date(info.reset_at) > now)
+ .map(([scope, info]) => ({ scope, reset_at: info.reset_at }))
+})
+
+const formatScopeName = (scope: string): string => {
+ const names: Record = {
+ claude: 'Claude',
+ gemini_text: 'Gemini',
+ gemini_image: 'Image'
+ }
+ return names[scope] || scope
+}
+
// Computed: is overloaded (529)
const isOverloaded = computed(() => {
if (!props.account.overload_until) return false
diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts
index a3c4c2da..0e4effc9 100644
--- a/frontend/src/i18n/locales/en.ts
+++ b/frontend/src/i18n/locales/en.ts
@@ -1081,6 +1081,7 @@ export default {
limited: 'Limited',
tempUnschedulable: 'Temp Unschedulable',
rateLimitedUntil: 'Rate limited until {time}',
+ scopeRateLimitedUntil: '{scope} rate limited until {time}',
overloadedUntil: 'Overloaded until {time}',
viewTempUnschedDetails: 'View temp unschedulable details'
},
diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts
index dec88217..ae6b2abf 100644
--- a/frontend/src/i18n/locales/zh.ts
+++ b/frontend/src/i18n/locales/zh.ts
@@ -1203,6 +1203,7 @@ export default {
limited: '限流',
tempUnschedulable: '临时不可调度',
rateLimitedUntil: '限流中,重置时间:{time}',
+ scopeRateLimitedUntil: '{scope} 限流中,重置时间:{time}',
overloadedUntil: '负载过重,重置时间:{time}',
viewTempUnschedDetails: '查看临时不可调度详情'
},
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts
index fcd3748f..17377c98 100644
--- a/frontend/src/types/index.ts
+++ b/frontend/src/types/index.ts
@@ -470,6 +470,9 @@ export interface Account {
temp_unschedulable_until: string | null
temp_unschedulable_reason: string | null
+ // Antigravity scope 级限流状态
+ scope_rate_limits?: Record
+
// Session window fields (5-hour window)
session_window_start: string | null
session_window_end: string | null