From f1dc656aca99513bd40c3ffc26359f497a389ec7 Mon Sep 17 00:00:00 2001 From: Catie Date: Wed, 23 Sep 2026 15:11:05 +0800 Subject: [PATCH] feat: admin-configurable Antigravity client version and model catalog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Antigravity client version (used in the spoofed User-Agent) and the routable model catalog were both hardcoded, so updating either needed a redeploy. Add an AntigravitySettings block (clientVersion + models) persisted in app settings and edited from a new Settings → Antigravity tab; empty fields fall back to the baked-in defaults. The per-account User-Agent now interpolates the configured version over the stable platform pool. Add a "从上游拉取模型" action that calls the real upstream fetchAvailableModels (POST /v1internal:fetchAvailableModels with {project}) using one of the pool's own accounts, so admins discover the live model list with their own credential and merge it into the editable catalog before saving. Rebuild the frontend bundle (index-D9XDLNLM.js). Co-Authored-By: Claude Opus 4.8 (1M context) --- cmd/capi/antigravity.go | 153 +++++++++++++++++++++++++++------- cmd/capi/antigravity_test.go | 70 ++++++++++++---- cmd/capi/main.go | 150 ++++++++++++++++++++++++++++++++- dist/assets/index-BBl9ztAh.js | 43 ---------- dist/assets/index-D9XDLNLM.js | 47 +++++++++++ dist/index.html | 2 +- src/App.tsx | 102 ++++++++++++++++++++++- 7 files changed, 475 insertions(+), 92 deletions(-) delete mode 100644 dist/assets/index-BBl9ztAh.js create mode 100644 dist/assets/index-D9XDLNLM.js diff --git a/cmd/capi/antigravity.go b/cmd/capi/antigravity.go index 8e138de..9e81fd4 100644 --- a/cmd/capi/antigravity.go +++ b/cmd/capi/antigravity.go @@ -9,6 +9,7 @@ import ( "io" "net/http" "net/url" + "sort" "strconv" "strings" "sync" @@ -44,8 +45,13 @@ const ( antigravityGeneratePath = "/v1internal:generateContent" antigravityStreamPath = "/v1internal:streamGenerateContent" antigravityLoadCodeAssistPath = "/v1internal:loadCodeAssist" + antigravityFetchModelsPath = "/v1internal:fetchAvailableModels" - antigravityUserAgent = "antigravity/hub/2.9.1 darwin/arm64" + // antigravityDefaultClientVersion is the hub client version baked into the + // User-Agent. It is only a default — admins can override it from settings + // (AntigravitySettings.ClientVersion) so the UA can track upstream client + // bumps without a redeploy. + antigravityDefaultClientVersion = "2.9.1" // A valid Google access token is reused until it is within this window of // expiry, then refreshed under the shared openAIRefreshMu lock. @@ -73,9 +79,10 @@ var antigravityOAuthClientSecret = func() string { // loadCodeAssist is only called once per account per process. var antigravityProjectCache sync.Map -// antigravityModelIDs is the set of model IDs Antigravity exposes upstream. The -// exposed ID is forwarded verbatim as the top-level "model" field; there is no -// rename table. Admins add these to a channel's model list to route to them. +// antigravityModelIDs is the default set of model IDs Antigravity exposes +// upstream. It is only a fallback: once an admin saves a model list (typically +// pulled live via fetchAvailableModels), antigravityModelsLocked returns that +// instead, so the catalog updates without a redeploy. func antigravityModelIDs() []string { return []string{ "claude-opus-4-6-thinking", @@ -93,6 +100,22 @@ func antigravityModelIDs() []string { } } +// antigravityModelsLocked returns the admin-configured model catalog, or the +// baked-in default when none is configured. Callers must hold s.mu. +func (s *Server) antigravityModelsLocked() []string { + if configured := mergeStrings(nil, s.state.Settings.Antigravity.Models); len(configured) > 0 { + return configured + } + return antigravityModelIDs() +} + +// antigravityModels is the locking wrapper around antigravityModelsLocked. +func (s *Server) antigravityModels() []string { + s.mu.Lock() + defer s.mu.Unlock() + return s.antigravityModelsLocked() +} + func isAntigravitySource(source string) bool { switch strings.ToLower(strings.TrimSpace(source)) { case "antigravity", "agy", "google-antigravity": @@ -126,7 +149,7 @@ func (s *Server) ensureAntigravityChannelLocked(channel *Channel) bool { channel.Provider = "antigravity" changed = true } - for _, modelID := range antigravityModelIDs() { + for _, modelID := range s.antigravityModelsLocked() { if s.ensureImportedModelLocked(modelID) { changed = true } @@ -217,7 +240,7 @@ func (s *Server) refreshAntigravityAccount(refreshToken string) (OpenAIRefreshRe } request.Header.Set("Content-Type", "application/x-www-form-urlencoded") request.Header.Set("Accept", "application/json") - request.Header.Set("User-Agent", antigravityUserAgent) + request.Header.Set("User-Agent", s.antigravityUserAgentCanonical()) response, err := s.httpClient.Do(request) if err != nil { @@ -289,7 +312,7 @@ func (s *Server) exchangeAntigravityOAuthCode(code, verifier string) (OpenAIRefr } request.Header.Set("Content-Type", "application/x-www-form-urlencoded") request.Header.Set("Accept", "application/json") - request.Header.Set("User-Agent", antigravityUserAgent) + request.Header.Set("User-Agent", s.antigravityUserAgentCanonical()) response, err := s.httpClient.Do(request) if err != nil { @@ -375,26 +398,24 @@ func (s *Server) resolveAntigravityProject(account OpenAIAccount, accessToken st return project, nil } -// antigravityUserAgents is the pool of realistic Antigravity desktop-client -// User-Agent strings. Real clients report their host platform, so spreading -// pooled accounts across the platform axis (while keeping the known-good client -// version) avoids every account fingerprinting as one identical client from a -// single server IP. Only the os/arch suffix varies — inventing version numbers -// would risk looking less real, not more. -var antigravityUserAgents = []string{ - "antigravity/hub/2.9.1 darwin/arm64", - "antigravity/hub/2.9.1 darwin/x64", - "antigravity/hub/2.9.1 win32/x64", - "antigravity/hub/2.9.1 linux/x64", +// antigravityPlatforms is the set of realistic host platforms an Antigravity +// desktop client reports. Spreading pooled accounts across this axis (while the +// client version stays whatever settings configure) avoids every account +// fingerprinting as one identical client from a single server IP. +var antigravityPlatforms = []string{ + "darwin/arm64", + "darwin/x64", + "win32/x64", + "linux/x64", } -// antigravityUserAgentFor returns a stable User-Agent for an account: the same -// account always reports the same client (mirroring a real single-device user), -// while different accounts spread deterministically across the pool. A stable -// per-account UA is deliberately chosen over per-request rotation — a single -// account flipping platforms every call looks more bot-like, not less. Falls -// back to the canonical UA when the account has no identifier yet. -func antigravityUserAgentFor(account OpenAIAccount) string { +// antigravityPlatformFor returns a stable platform for an account: the same +// account always reports the same platform (mirroring a real single-device +// user), while different accounts spread deterministically across the pool. A +// stable per-account platform is deliberately chosen over per-request rotation +// — a single account flipping platforms every call looks more bot-like, not +// less. Falls back to the first platform when the account has no identifier. +func antigravityPlatformFor(account OpenAIAccount) string { id := strings.TrimSpace(account.ID) if id == "" { id = strings.TrimSpace(account.AccountID) @@ -403,17 +424,93 @@ func antigravityUserAgentFor(account OpenAIAccount) string { id = strings.TrimSpace(account.Email) } if id == "" { - return antigravityUserAgent + return antigravityPlatforms[0] } hasher := fnv.New32a() _, _ = hasher.Write([]byte(id)) - return antigravityUserAgents[hasher.Sum32()%uint32(len(antigravityUserAgents))] + return antigravityPlatforms[hasher.Sum32()%uint32(len(antigravityPlatforms))] +} + +// antigravityUserAgentFor builds the client UA for an account from the configured +// version and the account's stable platform. +func antigravityUserAgentFor(version string, account OpenAIAccount) string { + if strings.TrimSpace(version) == "" { + version = antigravityDefaultClientVersion + } + return "antigravity/hub/" + version + " " + antigravityPlatformFor(account) +} + +// antigravityClientVersion returns the admin-configured client version, or the +// baked-in default when unset. +func (s *Server) antigravityClientVersion() string { + s.mu.Lock() + version := strings.TrimSpace(s.state.Settings.Antigravity.ClientVersion) + s.mu.Unlock() + if version == "" { + return antigravityDefaultClientVersion + } + return version +} + +// antigravityUserAgentCanonical is the account-agnostic UA used on the OAuth +// token endpoints (refresh/exchange), where per-account platform variety adds +// nothing. It still tracks the configured version. +func (s *Server) antigravityUserAgentCanonical() string { + return antigravityUserAgentFor(s.antigravityClientVersion(), OpenAIAccount{}) } func (s *Server) setAntigravityHeaders(request *http.Request, account OpenAIAccount, accessToken string) { request.Header.Set("Content-Type", "application/json") request.Header.Set("Authorization", "Bearer "+accessToken) - request.Header.Set("User-Agent", antigravityUserAgentFor(account)) + request.Header.Set("User-Agent", antigravityUserAgentFor(s.antigravityClientVersion(), account)) +} + +// fetchAntigravityModels asks the upstream which models the account can use, via +// the same fetchAvailableModels call the real client makes. The available model +// IDs are the keys of the top-level "models" map in the response. +func (s *Server) fetchAntigravityModels(account OpenAIAccount, accessToken string) ([]string, error) { + project, err := s.resolveAntigravityProject(account, accessToken) + if err != nil { + return nil, err + } + payload, err := json.Marshal(map[string]string{"project": project}) + if err != nil { + return nil, err + } + request, err := http.NewRequest(http.MethodPost, antigravityBaseURLProd+antigravityFetchModelsPath, bytes.NewReader(payload)) + if err != nil { + return nil, err + } + s.setAntigravityHeaders(request, account, accessToken) + + response, err := s.httpClient.Do(request) + if err != nil { + return nil, err + } + defer response.Body.Close() + + content, err := io.ReadAll(io.LimitReader(response.Body, 1<<20)) + if err != nil { + return nil, err + } + if response.StatusCode < 200 || response.StatusCode >= 300 { + return nil, fmt.Errorf("fetchAvailableModels failed: %s", truncateString(strings.TrimSpace(string(content)), 300)) + } + + var parsed struct { + Models map[string]json.RawMessage `json:"models"` + } + if err := json.Unmarshal(content, &parsed); err != nil { + return nil, fmt.Errorf("fetchAvailableModels 响应不是有效 JSON") + } + ids := make([]string, 0, len(parsed.Models)) + for id := range parsed.Models { + if trimmed := strings.TrimSpace(id); trimmed != "" { + ids = append(ids, trimmed) + } + } + sort.Strings(ids) + return ids, nil } // buildAntigravityPayload converts an OpenAI chat request into the Antigravity diff --git a/cmd/capi/antigravity_test.go b/cmd/capi/antigravity_test.go index 5fc3601..5d8bfae 100644 --- a/cmd/capi/antigravity_test.go +++ b/cmd/capi/antigravity_test.go @@ -215,25 +215,20 @@ func TestAntigravityAuthorizeURL(t *testing.T) { } func TestAntigravityUserAgentForIsStableAndSpread(t *testing.T) { - // Every UA in the pool must be a well-formed antigravity client string. + // The same account always reports the same platform (a real single-device + // user), and the chosen platform is always drawn from the pool. poolSet := map[string]bool{} - for _, ua := range antigravityUserAgents { - if !strings.HasPrefix(ua, "antigravity/hub/") { - t.Fatalf("unexpected UA format in pool: %q", ua) - } - poolSet[ua] = true + for _, platform := range antigravityPlatforms { + poolSet[platform] = true } - - // The same account always reports the same UA (a real single-device user), - // and the chosen UA is always drawn from the pool. account := OpenAIAccount{ID: "oaiacc_abc"} - first := antigravityUserAgentFor(account) + first := antigravityPlatformFor(account) if !poolSet[first] { - t.Fatalf("UA %q not from pool", first) + t.Fatalf("platform %q not from pool", first) } for i := 0; i < 5; i++ { - if got := antigravityUserAgentFor(account); got != first { - t.Fatalf("UA not stable for same account: %q vs %q", got, first) + if got := antigravityPlatformFor(account); got != first { + t.Fatalf("platform not stable for same account: %q vs %q", got, first) } } @@ -241,14 +236,53 @@ func TestAntigravityUserAgentForIsStableAndSpread(t *testing.T) { // so the pool does not fingerprint as a single identical client. seen := map[string]bool{} for i := 0; i < 200; i++ { - seen[antigravityUserAgentFor(OpenAIAccount{ID: "oaiacc_" + strconv.Itoa(i)})] = true + seen[antigravityPlatformFor(OpenAIAccount{ID: "oaiacc_" + strconv.Itoa(i)})] = true } if len(seen) < 2 { - t.Fatalf("UA selection did not spread across the pool: %v", seen) + t.Fatalf("platform selection did not spread across the pool: %v", seen) + } + + // The UA carries the configured version and a pool platform; an empty version + // falls back to the default, and an identifierless account uses platform[0]. + ua := antigravityUserAgentFor("3.0.0", account) + if !strings.HasPrefix(ua, "antigravity/hub/3.0.0 ") { + t.Fatalf("UA should carry the configured version, got %q", ua) + } + if got := antigravityUserAgentFor("", OpenAIAccount{}); got != "antigravity/hub/"+antigravityDefaultClientVersion+" "+antigravityPlatforms[0] { + t.Fatalf("empty version + empty account should yield the canonical default UA, got %q", got) + } +} + +func TestAntigravityVersionValid(t *testing.T) { + for _, ok := range []string{"2.9.1", "2.10.0", "3.0.0-beta", "3.0.0+build.2", "12"} { + if !antigravityVersionValid(ok) { + t.Fatalf("version %q should be valid", ok) + } } + for _, bad := range []string{"2.9.1 darwin", "", "2.9.1;rm -rf", "\n2.9", "版本"} { + if antigravityVersionValid(bad) { + t.Fatalf("version %q should be rejected", bad) + } + } +} - // No identifier at all falls back to the canonical UA rather than panicking. - if got := antigravityUserAgentFor(OpenAIAccount{}); got != antigravityUserAgent { - t.Fatalf("empty account should fall back to canonical UA, got %q", got) +func TestPickAntigravityProbeAccount(t *testing.T) { + channel := Channel{OpenAIAccounts: []OpenAIAccount{ + {ID: "a", Status: "invalid", AccessToken: "x"}, + {ID: "b", Status: "healthy", RefreshToken: "r"}, + {ID: "c", Status: "unchecked"}, // no credential + }} + // Prefers a healthy account with a credential. + if account, ok := pickAntigravityProbeAccount(channel, ""); !ok || account.ID != "b" { + t.Fatalf("expected healthy account b, got %q ok=%v", account.ID, ok) + } + // Honors an explicit account selection even if not healthy. + if account, ok := pickAntigravityProbeAccount(channel, "a"); !ok || account.ID != "a" { + t.Fatalf("expected explicitly selected account a, got %q ok=%v", account.ID, ok) + } + // A pool with no usable credentials yields nothing. + empty := Channel{OpenAIAccounts: []OpenAIAccount{{ID: "c", Status: "unchecked"}}} + if _, ok := pickAntigravityProbeAccount(empty, ""); ok { + t.Fatalf("expected no probe account when none carry a credential") } } diff --git a/cmd/capi/main.go b/cmd/capi/main.go index 6638275..99c3e3b 100644 --- a/cmd/capi/main.go +++ b/cmd/capi/main.go @@ -91,6 +91,17 @@ type AppSettings struct { Auth AuthSettings `json:"auth,omitempty"` CheckIn CheckInSettings `json:"checkIn,omitempty"` Maintenance MaintenanceSettings `json:"maintenance,omitempty"` + Antigravity AntigravitySettings `json:"antigravity,omitempty"` +} + +// AntigravitySettings holds admin-tunable Antigravity knobs so the client +// version (used in the spoofed User-Agent) and the routable model catalog can +// be updated from the UI without a redeploy. Empty fields fall back to the +// baked-in defaults. +type AntigravitySettings struct { + Managed bool `json:"managed,omitempty"` + ClientVersion string `json:"clientVersion,omitempty"` + Models []string `json:"models,omitempty"` } type MaintenanceSettings struct { @@ -959,6 +970,7 @@ func (s *Server) registerRoutes(router *gin.Engine) { admin.POST("/channels/:id/openai-oauth/complete", s.completeOpenAIOAuth) admin.POST("/channels/:id/antigravity-oauth/start", s.startAntigravityOAuth) admin.POST("/channels/:id/antigravity-oauth/complete", s.completeAntigravityOAuth) + admin.POST("/channels/:id/antigravity/available-models", s.probeAntigravityModels) admin.POST("/channels/:id/openai-accounts/check", s.checkOpenAIAccounts) admin.POST("/channels/:id/openai-accounts/deduplicate", s.deduplicateOpenAIAccounts) admin.DELETE("/channels/:id/openai-accounts/:accountId", s.deleteOpenAIAccount) @@ -982,6 +994,8 @@ func (s *Server) registerRoutes(router *gin.Engine) { admin.PATCH("/settings/check-in", s.updateCheckInSettings) admin.GET("/settings/maintenance", s.getMaintenanceSettings) admin.PATCH("/settings/maintenance", s.updateMaintenanceSettings) + admin.GET("/settings/antigravity", s.getAntigravitySettings) + admin.PATCH("/settings/antigravity", s.updateAntigravitySettings) admin.GET("/backup", s.exportBackup) admin.POST("/restore", s.restoreBackup) @@ -1579,6 +1593,140 @@ func (s *Server) updateMaintenanceSettings(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"maintenance": s.maintenanceSettingsLocked()}) } +// antigravitySettingsLocked returns the stored Antigravity settings with empty +// fields resolved to their effective defaults, for display. Callers hold s.mu. +func (s *Server) antigravitySettingsLocked() AntigravitySettings { + settings := s.state.Settings.Antigravity + if strings.TrimSpace(settings.ClientVersion) == "" { + settings.ClientVersion = antigravityDefaultClientVersion + } + settings.Models = s.antigravityModelsLocked() + return settings +} + +func (s *Server) getAntigravitySettings(c *gin.Context) { + s.mu.Lock() + defer s.mu.Unlock() + c.JSON(http.StatusOK, gin.H{"antigravity": s.antigravitySettingsLocked()}) +} + +func (s *Server) updateAntigravitySettings(c *gin.Context) { + var body struct { + ClientVersion string `json:"clientVersion"` + Models []string `json:"models"` + } + if err := c.ShouldBindJSON(&body); err != nil { + validationError(c, "无效的 Antigravity 设置") + return + } + version := strings.TrimSpace(body.ClientVersion) + if version != "" && !antigravityVersionValid(version) { + validationError(c, "客户端版本格式不正确,只能包含数字、点和短横线") + return + } + models := mergeStrings(nil, body.Models) + + s.mu.Lock() + defer s.mu.Unlock() + s.state.Settings.Antigravity = AntigravitySettings{ + Managed: true, + ClientVersion: version, + Models: models, + } + // Register the effective catalog so routing resolves the models immediately. + for _, modelID := range s.antigravityModelsLocked() { + s.ensureImportedModelLocked(modelID) + } + s.saveStateLocked() + c.JSON(http.StatusOK, gin.H{"antigravity": s.antigravitySettingsLocked()}) +} + +// antigravityVersionValid accepts dotted versions with optional pre-release or +// build suffixes (e.g. 2.9.1, 2.10.0-beta, 3.0.0+build.2) and rejects anything +// that could smuggle spaces or control characters into the User-Agent. +func antigravityVersionValid(version string) bool { + if len(version) > 32 { + return false + } + hasAlnum := false + for _, r := range version { + switch { + case r >= '0' && r <= '9', r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z': + hasAlnum = true + case r == '.' || r == '-' || r == '+' || r == '_': + default: + return false + } + } + return hasAlnum +} + +// pickAntigravityProbeAccount selects an account from the channel to probe the +// upstream model catalog with: a specific one when accountID is set, otherwise a +// healthy account, otherwise any account that carries a usable credential. +func pickAntigravityProbeAccount(channel Channel, accountID string) (OpenAIAccount, bool) { + var fallback OpenAIAccount + haveFallback := false + for _, account := range channel.OpenAIAccounts { + if accountID != "" && account.ID != accountID { + continue + } + if strings.TrimSpace(account.AccessToken) == "" && strings.TrimSpace(account.RefreshToken) == "" && strings.TrimSpace(account.SessionToken) == "" { + continue + } + if accountID != "" || account.Status == "healthy" { + return account, true + } + if !haveFallback { + fallback = account + haveFallback = true + } + } + return fallback, haveFallback +} + +// probeAntigravityModels pulls the live model catalog from upstream using one of +// the channel's own accounts (fetchAvailableModels). The admin then reviews the +// result and saves it via updateAntigravitySettings. +func (s *Server) probeAntigravityModels(c *gin.Context) { + accountIDFilter := strings.TrimSpace(c.Query("accountId")) + + s.mu.Lock() + channel := s.findChannel(c.Param("id")) + if channel == nil { + s.mu.Unlock() + c.JSON(http.StatusNotFound, gin.H{"error": gin.H{"message": "Channel not found"}}) + return + } + if !isAntigravityChannel(*channel) { + s.mu.Unlock() + validationError(c, "该渠道不是 Antigravity 账号池") + return + } + account, ok := pickAntigravityProbeAccount(*channel, accountIDFilter) + s.mu.Unlock() + if !ok { + validationError(c, "该账号池没有可用账号,请先导入或授权一个 Antigravity 账号") + return + } + + accessToken, err := s.resolveOpenAIAccountAccessToken(account) + if err != nil { + s.openAIError(c, http.StatusBadGateway, "antigravity_token_unavailable", err.Error(), "api_error", nil) + return + } + models, err := s.fetchAntigravityModels(account, accessToken) + if err != nil { + s.openAIError(c, http.StatusBadGateway, "antigravity_models_unavailable", err.Error(), "api_error", nil) + return + } + if len(models) == 0 { + c.JSON(http.StatusBadGateway, gin.H{"error": gin.H{"message": "上游未返回可用模型"}}) + return + } + c.JSON(http.StatusOK, gin.H{"models": models, "account": publicOpenAIAccount(account)}) +} + func (s *Server) exportBackup(c *gin.Context) { s.mu.Lock() envelope := BackupEnvelope{ @@ -7078,7 +7226,7 @@ func chatCompletionUsageFromCodex(usage gin.H, messages []ChatMessage) gin.H { func (s *Server) fetchUpstreamModelIDs(channel Channel, upstreamKey string) ([]string, error) { if isAntigravityChannel(channel) { - return antigravityModelIDs(), nil + return s.antigravityModels(), nil } if isCodexChannel(channel) { return codexChannelModelIDs(), nil diff --git a/dist/assets/index-BBl9ztAh.js b/dist/assets/index-BBl9ztAh.js deleted file mode 100644 index 972843f..0000000 --- a/dist/assets/index-BBl9ztAh.js +++ /dev/null @@ -1,43 +0,0 @@ -(function(){const f=document.createElement("link").relList;if(f&&f.supports&&f.supports("modulepreload"))return;for(const C of document.querySelectorAll('link[rel="modulepreload"]'))r(C);new MutationObserver(C=>{for(const T of C)if(T.type==="childList")for(const k of T.addedNodes)k.tagName==="LINK"&&k.rel==="modulepreload"&&r(k)}).observe(document,{childList:!0,subtree:!0});function p(C){const T={};return C.integrity&&(T.integrity=C.integrity),C.referrerPolicy&&(T.referrerPolicy=C.referrerPolicy),C.crossOrigin==="use-credentials"?T.credentials="include":C.crossOrigin==="anonymous"?T.credentials="omit":T.credentials="same-origin",T}function r(C){if(C.ep)return;C.ep=!0;const T=p(C);fetch(C.href,T)}})();function q0(u){return u&&u.__esModule&&Object.prototype.hasOwnProperty.call(u,"default")?u.default:u}var Qc={exports:{}},ss={};var tm;function G0(){if(tm)return ss;tm=1;var u=Symbol.for("react.transitional.element"),f=Symbol.for("react.fragment");function p(r,C,T){var k=null;if(T!==void 0&&(k=""+T),C.key!==void 0&&(k=""+C.key),"key"in C){T={};for(var W in C)W!=="key"&&(T[W]=C[W])}else T=C;return C=T.ref,{$$typeof:u,type:r,key:k,ref:C!==void 0?C:null,props:T}}return ss.Fragment=f,ss.jsx=p,ss.jsxs=p,ss}var lm;function k0(){return lm||(lm=1,Qc.exports=G0()),Qc.exports}var n=k0(),Xc={exports:{}},Ae={};var am;function Y0(){if(am)return Ae;am=1;var u=Symbol.for("react.transitional.element"),f=Symbol.for("react.portal"),p=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),C=Symbol.for("react.profiler"),T=Symbol.for("react.consumer"),k=Symbol.for("react.context"),W=Symbol.for("react.forward_ref"),N=Symbol.for("react.suspense"),y=Symbol.for("react.memo"),G=Symbol.for("react.lazy"),_=Symbol.for("react.activity"),V=Symbol.iterator;function P(m){return m===null||typeof m!="object"?null:(m=V&&m[V]||m["@@iterator"],typeof m=="function"?m:null)}var fe={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},ee=Object.assign,me={};function ne(m,h,M){this.props=m,this.context=h,this.refs=me,this.updater=M||fe}ne.prototype.isReactComponent={},ne.prototype.setState=function(m,h){if(typeof m!="object"&&typeof m!="function"&&m!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,m,h,"setState")},ne.prototype.forceUpdate=function(m){this.updater.enqueueForceUpdate(this,m,"forceUpdate")};function se(){}se.prototype=ne.prototype;function Y(m,h,M){this.props=m,this.context=h,this.refs=me,this.updater=M||fe}var xe=Y.prototype=new se;xe.constructor=Y,ee(xe,ne.prototype),xe.isPureReactComponent=!0;var te=Array.isArray;function ye(){}var O={H:null,A:null,T:null,S:null},ae=Object.prototype.hasOwnProperty;function ve(m,h,M){var Q=M.ref;return{$$typeof:u,type:m,key:h,ref:Q!==void 0?Q:null,props:M}}function Ce(m,h){return ve(m.type,h,m.props)}function Me(m){return typeof m=="object"&&m!==null&&m.$$typeof===u}function ue(m){var h={"=":"=0",":":"=2"};return"$"+m.replace(/[=:]/g,function(M){return h[M]})}var Z=/\/+/g;function de(m,h){return typeof m=="object"&&m!==null&&m.key!=null?ue(""+m.key):h.toString(36)}function $(m){switch(m.status){case"fulfilled":return m.value;case"rejected":throw m.reason;default:switch(typeof m.status=="string"?m.then(ye,ye):(m.status="pending",m.then(function(h){m.status==="pending"&&(m.status="fulfilled",m.value=h)},function(h){m.status==="pending"&&(m.status="rejected",m.reason=h)})),m.status){case"fulfilled":return m.value;case"rejected":throw m.reason}}throw m}function A(m,h,M,Q,le){var Ne=typeof m;(Ne==="undefined"||Ne==="boolean")&&(m=null);var z=!1;if(m===null)z=!0;else switch(Ne){case"bigint":case"string":case"number":z=!0;break;case"object":switch(m.$$typeof){case u:case f:z=!0;break;case G:return z=m._init,A(z(m._payload),h,M,Q,le)}}if(z)return le=le(m),z=Q===""?"."+de(m,0):Q,te(le)?(M="",z!=null&&(M=z.replace(Z,"$&/")+"/"),A(le,h,M,"",function(jt){return jt})):le!=null&&(Me(le)&&(le=Ce(le,M+(le.key==null||m&&m.key===le.key?"":(""+le.key).replace(Z,"$&/")+"/")+z)),h.push(le)),1;z=0;var be=Q===""?".":Q+":";if(te(m))for(var ce=0;ce>>1,ie=A[X];if(0>>1;XC(M,H))QC(le,M)?(A[X]=le,A[Q]=H,X=Q):(A[X]=M,A[h]=H,X=h);else if(QC(le,H))A[X]=le,A[Q]=H,X=Q;else break e}}return L}function C(A,L){var H=A.sortIndex-L.sortIndex;return H!==0?H:A.id-L.id}if(u.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var T=performance;u.unstable_now=function(){return T.now()}}else{var k=Date,W=k.now();u.unstable_now=function(){return k.now()-W}}var N=[],y=[],G=1,_=null,V=3,P=!1,fe=!1,ee=!1,me=!1,ne=typeof setTimeout=="function"?setTimeout:null,se=typeof clearTimeout=="function"?clearTimeout:null,Y=typeof setImmediate<"u"?setImmediate:null;function xe(A){for(var L=p(y);L!==null;){if(L.callback===null)r(y);else if(L.startTime<=A)r(y),L.sortIndex=L.expirationTime,f(N,L);else break;L=p(y)}}function te(A){if(ee=!1,xe(A),!fe)if(p(N)!==null)fe=!0,ye||(ye=!0,ue());else{var L=p(y);L!==null&&$(te,L.startTime-A)}}var ye=!1,O=-1,ae=5,ve=-1;function Ce(){return me?!0:!(u.unstable_now()-veA&&Ce());){var X=_.callback;if(typeof X=="function"){_.callback=null,V=_.priorityLevel;var ie=X(_.expirationTime<=A);if(A=u.unstable_now(),typeof ie=="function"){_.callback=ie,xe(A),L=!0;break t}_===p(N)&&r(N),xe(A)}else r(N);_=p(N)}if(_!==null)L=!0;else{var m=p(y);m!==null&&$(te,m.startTime-A),L=!1}}break e}finally{_=null,V=H,P=!1}L=void 0}}finally{L?ue():ye=!1}}}var ue;if(typeof Y=="function")ue=function(){Y(Me)};else if(typeof MessageChannel<"u"){var Z=new MessageChannel,de=Z.port2;Z.port1.onmessage=Me,ue=function(){de.postMessage(null)}}else ue=function(){ne(Me,0)};function $(A,L){O=ne(function(){A(u.unstable_now())},L)}u.unstable_IdlePriority=5,u.unstable_ImmediatePriority=1,u.unstable_LowPriority=4,u.unstable_NormalPriority=3,u.unstable_Profiling=null,u.unstable_UserBlockingPriority=2,u.unstable_cancelCallback=function(A){A.callback=null},u.unstable_forceFrameRate=function(A){0>A||125X?(A.sortIndex=H,f(y,A),p(N)===null&&A===p(y)&&(ee?(se(O),O=-1):ee=!0,$(te,H-X))):(A.sortIndex=ie,f(N,A),fe||P||(fe=!0,ye||(ye=!0,ue()))),A},u.unstable_shouldYield=Ce,u.unstable_wrapCallback=function(A){var L=V;return function(){var H=V;V=L;try{return A.apply(this,arguments)}finally{V=H}}}})(Vc)),Vc}var im;function X0(){return im||(im=1,Zc.exports=Q0()),Zc.exports}var Jc={exports:{}},yt={};var um;function K0(){if(um)return yt;um=1;var u=eo();function f(N){var y="https://react.dev/errors/"+N;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(u)}catch(f){console.error(f)}}return u(),Jc.exports=K0(),Jc.exports}var om;function V0(){if(om)return is;om=1;var u=X0(),f=eo(),p=Z0();function r(e){var t="https://react.dev/errors/"+e;if(1ie||(e.current=X[ie],X[ie]=null,ie--)}function M(e,t){ie++,X[ie]=e.current,e.current=t}var Q=m(null),le=m(null),Ne=m(null),z=m(null);function be(e,t){switch(M(Ne,t),M(le,e),M(Q,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Af(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Af(t),e=Cf(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}h(Q),M(Q,e)}function ce(){h(Q),h(le),h(Ne)}function jt(e){e.memoizedState!==null&&M(z,e);var t=Q.current,l=Cf(t,e.type);t!==l&&(M(le,e),M(Q,l))}function I(e){le.current===e&&(h(Q),h(le)),z.current===e&&(h(z),ts._currentValue=H)}var Ve,ot;function Je(e){if(Ve===void 0)try{throw Error()}catch(l){var t=l.stack.trim().match(/\n( *(at )?)/);Ve=t&&t[1]||"",ot=-1)":-1s||d[a]!==j[s]){var D=` -`+d[a].replace(" at new "," at ");return e.displayName&&D.includes("")&&(D=D.replace("",e.displayName)),D}while(1<=a&&0<=s);break}}}finally{gt=!1,Error.prepareStackTrace=l}return(l=e?e.displayName||e.name:"")?Je(l):""}function ll(e,t){switch(e.tag){case 26:case 27:case 5:return Je(e.type);case 16:return Je("Lazy");case 13:return e.child!==t&&t!==null?Je("Suspense Fallback"):Je("Suspense");case 19:return Je("SuspenseList");case 0:case 15:return Dt(e.type,!1);case 11:return Dt(e.type.render,!1);case 1:return Dt(e.type,!0);case 31:return Je("Activity");default:return""}}function al(e){try{var t="",l=null;do t+=ll(e,l),l=e,e=e.return;while(e);return t}catch(a){return` -Error generating stack: `+a.message+` -`+a.stack}}var U=Object.prototype.hasOwnProperty,B=u.unstable_scheduleCallback,Be=u.unstable_cancelCallback,We=u.unstable_shouldYield,at=u.unstable_requestPaint,Pe=u.unstable_now,zi=u.unstable_getCurrentPriorityLevel,cs=u.unstable_ImmediatePriority,os=u.unstable_UserBlockingPriority,ua=u.unstable_NormalPriority,Ea=u.unstable_LowPriority,hn=u.unstable_IdlePriority,Oi=u.log,R=u.unstable_setDisableYieldValue,K=null,J=null;function F(e){if(typeof Oi=="function"&&R(e),J&&typeof J.setStrictMode=="function")try{J.setStrictMode(K,e)}catch{}}var he=Math.clz32?Math.clz32:Rt,$e=Math.log,Ut=Math.LN2;function Rt(e){return e>>>=0,e===0?32:31-($e(e)/Ut|0)|0}var nl=256,rl=262144,Ma=4194304;function vl(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function Hl(e,t,l){var a=e.pendingLanes;if(a===0)return 0;var s=0,i=e.suspendedLanes,c=e.pingedLanes;e=e.warmLanes;var o=a&134217727;return o!==0?(a=o&~i,a!==0?s=vl(a):(c&=o,c!==0?s=vl(c):l||(l=o&~e,l!==0&&(s=vl(l))))):(o=a&~i,o!==0?s=vl(o):c!==0?s=vl(c):l||(l=a&~e,l!==0&&(s=vl(l)))),s===0?0:t!==0&&t!==s&&(t&i)===0&&(i=s&-s,l=t&-t,i>=l||i===32&&(l&4194048)!==0)?t:s}function ca(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function Cm(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function ao(){var e=Ma;return Ma<<=1,(Ma&62914560)===0&&(Ma=4194304),e}function _i(e){for(var t=[],l=0;31>l;l++)t.push(e);return t}function pn(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function Tm(e,t,l,a,s,i){var c=e.pendingLanes;e.pendingLanes=l,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=l,e.entangledLanes&=l,e.errorRecoveryDisabledLanes&=l,e.shellSuspendCounter=0;var o=e.entanglements,d=e.expirationTimes,j=e.hiddenUpdates;for(l=c&~l;0"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var Dm=/[\n"\\]/g;function Xt(e){return e.replace(Dm,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function Bi(e,t,l,a,s,i,c,o){e.name="",c!=null&&typeof c!="function"&&typeof c!="symbol"&&typeof c!="boolean"?e.type=c:e.removeAttribute("type"),t!=null?c==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+Qt(t)):e.value!==""+Qt(t)&&(e.value=""+Qt(t)):c!=="submit"&&c!=="reset"||e.removeAttribute("value"),t!=null?qi(e,c,Qt(t)):l!=null?qi(e,c,Qt(l)):a!=null&&e.removeAttribute("value"),s==null&&i!=null&&(e.defaultChecked=!!i),s!=null&&(e.checked=s&&typeof s!="function"&&typeof s!="symbol"),o!=null&&typeof o!="function"&&typeof o!="symbol"&&typeof o!="boolean"?e.name=""+Qt(o):e.removeAttribute("name")}function yo(e,t,l,a,s,i,c,o){if(i!=null&&typeof i!="function"&&typeof i!="symbol"&&typeof i!="boolean"&&(e.type=i),t!=null||l!=null){if(!(i!=="submit"&&i!=="reset"||t!=null)){Hi(e);return}l=l!=null?""+Qt(l):"",t=t!=null?""+Qt(t):l,o||t===e.value||(e.value=t),e.defaultValue=t}a=a??s,a=typeof a!="function"&&typeof a!="symbol"&&!!a,e.checked=o?e.checked:!!a,e.defaultChecked=!!a,c!=null&&typeof c!="function"&&typeof c!="symbol"&&typeof c!="boolean"&&(e.name=c),Hi(e)}function qi(e,t,l){t==="number"&&fs(e.ownerDocument)===e||e.defaultValue===""+l||(e.defaultValue=""+l)}function Ra(e,t,l,a){if(e=e.options,t){t={};for(var s=0;s"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Qi=!1;if(bl)try{var bn={};Object.defineProperty(bn,"passive",{get:function(){Qi=!0}}),window.addEventListener("test",bn,bn),window.removeEventListener("test",bn,bn)}catch{Qi=!1}var ql=null,Xi=null,hs=null;function Ao(){if(hs)return hs;var e,t=Xi,l=t.length,a,s="value"in ql?ql.value:ql.textContent,i=s.length;for(e=0;e=Sn),Oo=" ",_o=!1;function Do(e,t){switch(e){case"keyup":return ih.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Uo(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var qa=!1;function ch(e,t){switch(e){case"compositionend":return Uo(t);case"keypress":return t.which!==32?null:(_o=!0,Oo);case"textInput":return e=t.data,e===Oo&&_o?null:e;default:return null}}function oh(e,t){if(qa)return e==="compositionend"||!$i&&Do(e,t)?(e=Ao(),hs=Xi=ql=null,qa=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:l,offset:t-e};e=a}e:{for(;l;){if(l.nextSibling){l=l.nextSibling;break e}l=l.parentNode}l=void 0}l=Yo(l)}}function Qo(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Qo(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Xo(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=fs(e.document);t instanceof e.HTMLIFrameElement;){try{var l=typeof t.contentWindow.location.href=="string"}catch{l=!1}if(l)e=t.contentWindow;else break;t=fs(e.document)}return t}function Ii(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}var yh=bl&&"documentMode"in document&&11>=document.documentMode,Ga=null,Pi=null,Tn=null,eu=!1;function Ko(e,t,l){var a=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;eu||Ga==null||Ga!==fs(a)||(a=Ga,"selectionStart"in a&&Ii(a)?a={start:a.selectionStart,end:a.selectionEnd}:(a=(a.ownerDocument&&a.ownerDocument.defaultView||window).getSelection(),a={anchorNode:a.anchorNode,anchorOffset:a.anchorOffset,focusNode:a.focusNode,focusOffset:a.focusOffset}),Tn&&Cn(Tn,a)||(Tn=a,a=ui(Pi,"onSelect"),0>=c,s-=c,dl=1<<32-he(t)+s|l<Ee?(De=re,re=null):De=re.sibling;var we=S(g,re,x[Ee],w);if(we===null){re===null&&(re=De);break}e&&re&&we.alternate===null&&t(g,re),v=i(we,v,Ee),Re===null?ge=we:Re.sibling=we,Re=we,re=De}if(Ee===x.length)return l(g,re),Ue&&jl(g,Ee),ge;if(re===null){for(;EeEe?(De=re,re=null):De=re.sibling;var ia=S(g,re,we.value,w);if(ia===null){re===null&&(re=De);break}e&&re&&ia.alternate===null&&t(g,re),v=i(ia,v,Ee),Re===null?ge=ia:Re.sibling=ia,Re=ia,re=De}if(we.done)return l(g,re),Ue&&jl(g,Ee),ge;if(re===null){for(;!we.done;Ee++,we=x.next())we=q(g,we.value,w),we!==null&&(v=i(we,v,Ee),Re===null?ge=we:Re.sibling=we,Re=we);return Ue&&jl(g,Ee),ge}for(re=a(re);!we.done;Ee++,we=x.next())we=E(re,g,Ee,we.value,w),we!==null&&(e&&we.alternate!==null&&re.delete(we.key===null?Ee:we.key),v=i(we,v,Ee),Re===null?ge=we:Re.sibling=we,Re=we);return e&&re.forEach(function(B0){return t(g,B0)}),Ue&&jl(g,Ee),ge}function Le(g,v,x,w){if(typeof x=="object"&&x!==null&&x.type===ee&&x.key===null&&(x=x.props.children),typeof x=="object"&&x!==null){switch(x.$$typeof){case P:e:{for(var ge=x.key;v!==null;){if(v.key===ge){if(ge=x.type,ge===ee){if(v.tag===7){l(g,v.sibling),w=s(v,x.props.children),w.return=g,g=w;break e}}else if(v.elementType===ge||typeof ge=="object"&&ge!==null&&ge.$$typeof===ae&&ba(ge)===v.type){l(g,v.sibling),w=s(v,x.props),Dn(w,x),w.return=g,g=w;break e}l(g,v);break}else t(g,v);v=v.sibling}x.type===ee?(w=ha(x.props.children,g.mode,w,x.key),w.return=g,g=w):(w=As(x.type,x.key,x.props,null,g.mode,w),Dn(w,x),w.return=g,g=w)}return c(g);case fe:e:{for(ge=x.key;v!==null;){if(v.key===ge)if(v.tag===4&&v.stateNode.containerInfo===x.containerInfo&&v.stateNode.implementation===x.implementation){l(g,v.sibling),w=s(v,x.children||[]),w.return=g,g=w;break e}else{l(g,v);break}else t(g,v);v=v.sibling}w=uu(x,g.mode,w),w.return=g,g=w}return c(g);case ae:return x=ba(x),Le(g,v,x,w)}if($(x))return oe(g,v,x,w);if(ue(x)){if(ge=ue(x),typeof ge!="function")throw Error(r(150));return x=ge.call(x),Se(g,v,x,w)}if(typeof x.then=="function")return Le(g,v,_s(x),w);if(x.$$typeof===Y)return Le(g,v,Es(g,x),w);Ds(g,x)}return typeof x=="string"&&x!==""||typeof x=="number"||typeof x=="bigint"?(x=""+x,v!==null&&v.tag===6?(l(g,v.sibling),w=s(v,x),w.return=g,g=w):(l(g,v),w=iu(x,g.mode,w),w.return=g,g=w),c(g)):l(g,v)}return function(g,v,x,w){try{_n=0;var ge=Le(g,v,x,w);return Wa=null,ge}catch(re){if(re===$a||re===zs)throw re;var Re=Ht(29,re,null,g.mode);return Re.lanes=w,Re.return=g,Re}}}var ja=hr(!0),pr=hr(!1),Ql=!1;function bu(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function xu(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function Xl(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function Kl(e,t,l){var a=e.updateQueue;if(a===null)return null;if(a=a.shared,(He&2)!==0){var s=a.pending;return s===null?t.next=t:(t.next=s.next,s.next=t),a.pending=t,t=Ns(e),Io(e,null,l),t}return Ss(e,a,t,l),Ns(e)}function Un(e,t,l){if(t=t.updateQueue,t!==null&&(t=t.shared,(l&4194048)!==0)){var a=t.lanes;a&=e.pendingLanes,l|=a,t.lanes=l,so(e,l)}}function ju(e,t){var l=e.updateQueue,a=e.alternate;if(a!==null&&(a=a.updateQueue,l===a)){var s=null,i=null;if(l=l.firstBaseUpdate,l!==null){do{var c={lane:l.lane,tag:l.tag,payload:l.payload,callback:null,next:null};i===null?s=i=c:i=i.next=c,l=l.next}while(l!==null);i===null?s=i=t:i=i.next=t}else s=i=t;l={baseState:a.baseState,firstBaseUpdate:s,lastBaseUpdate:i,shared:a.shared,callbacks:a.callbacks},e.updateQueue=l;return}e=l.lastBaseUpdate,e===null?l.firstBaseUpdate=t:e.next=t,l.lastBaseUpdate=t}var Su=!1;function Rn(){if(Su){var e=Ja;if(e!==null)throw e}}function wn(e,t,l,a){Su=!1;var s=e.updateQueue;Ql=!1;var i=s.firstBaseUpdate,c=s.lastBaseUpdate,o=s.shared.pending;if(o!==null){s.shared.pending=null;var d=o,j=d.next;d.next=null,c===null?i=j:c.next=j,c=d;var D=e.alternate;D!==null&&(D=D.updateQueue,o=D.lastBaseUpdate,o!==c&&(o===null?D.firstBaseUpdate=j:o.next=j,D.lastBaseUpdate=d))}if(i!==null){var q=s.baseState;c=0,D=j=d=null,o=i;do{var S=o.lane&-536870913,E=S!==o.lane;if(E?(_e&S)===S:(a&S)===S){S!==0&&S===Va&&(Su=!0),D!==null&&(D=D.next={lane:0,tag:o.tag,payload:o.payload,callback:null,next:null});e:{var oe=e,Se=o;S=t;var Le=l;switch(Se.tag){case 1:if(oe=Se.payload,typeof oe=="function"){q=oe.call(Le,q,S);break e}q=oe;break e;case 3:oe.flags=oe.flags&-65537|128;case 0:if(oe=Se.payload,S=typeof oe=="function"?oe.call(Le,q,S):oe,S==null)break e;q=_({},q,S);break e;case 2:Ql=!0}}S=o.callback,S!==null&&(e.flags|=64,E&&(e.flags|=8192),E=s.callbacks,E===null?s.callbacks=[S]:E.push(S))}else E={lane:S,tag:o.tag,payload:o.payload,callback:o.callback,next:null},D===null?(j=D=E,d=q):D=D.next=E,c|=S;if(o=o.next,o===null){if(o=s.shared.pending,o===null)break;E=o,o=E.next,E.next=null,s.lastBaseUpdate=E,s.shared.pending=null}}while(!0);D===null&&(d=q),s.baseState=d,s.firstBaseUpdate=j,s.lastBaseUpdate=D,i===null&&(s.shared.lanes=0),Wl|=c,e.lanes=c,e.memoizedState=q}}function vr(e,t){if(typeof e!="function")throw Error(r(191,e));e.call(t)}function yr(e,t){var l=e.callbacks;if(l!==null)for(e.callbacks=null,e=0;ei?i:8;var c=A.T,o={};A.T=o,Yu(e,!1,t,l);try{var d=s(),j=A.S;if(j!==null&&j(o,d),d!==null&&typeof d=="object"&&typeof d.then=="function"){var D=Th(d,a);qn(e,t,D,Yt(e))}else qn(e,t,a,Yt(e))}catch(q){qn(e,t,{then:function(){},status:"rejected",reason:q},Yt())}finally{L.p=i,c!==null&&o.types!==null&&(c.types=o.types),A.T=c}}function Dh(){}function Gu(e,t,l,a){if(e.tag!==5)throw Error(r(476));var s=$r(e).queue;Jr(e,s,t,H,l===null?Dh:function(){return Wr(e),l(a)})}function $r(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:H,baseState:H,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Cl,lastRenderedState:H},next:null};var l={};return t.next={memoizedState:l,baseState:l,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Cl,lastRenderedState:l},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function Wr(e){var t=$r(e);t.next===null&&(t=e.alternate.memoizedState),qn(e,t.next.queue,{},Yt())}function ku(){return ht(ts)}function Fr(){return tt().memoizedState}function Ir(){return tt().memoizedState}function Uh(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var l=Yt();e=Xl(l);var a=Kl(t,e,l);a!==null&&(Mt(a,t,l),Un(a,t,l)),t={cache:pu()},e.payload=t;return}t=t.return}}function Rh(e,t,l){var a=Yt();l={lane:a,revertLane:0,gesture:null,action:l,hasEagerState:!1,eagerState:null,next:null},Ls(e)?ed(t,l):(l=nu(e,t,l,a),l!==null&&(Mt(l,e,a),td(l,t,a)))}function Pr(e,t,l){var a=Yt();qn(e,t,l,a)}function qn(e,t,l,a){var s={lane:a,revertLane:0,gesture:null,action:l,hasEagerState:!1,eagerState:null,next:null};if(Ls(e))ed(t,s);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var c=t.lastRenderedState,o=i(c,l);if(s.hasEagerState=!0,s.eagerState=o,wt(o,c))return Ss(e,t,s,0),Qe===null&&js(),!1}catch{}if(l=nu(e,t,s,a),l!==null)return Mt(l,e,a),td(l,t,a),!0}return!1}function Yu(e,t,l,a){if(a={lane:2,revertLane:bc(),gesture:null,action:a,hasEagerState:!1,eagerState:null,next:null},Ls(e)){if(t)throw Error(r(479))}else t=nu(e,l,a,2),t!==null&&Mt(t,e,2)}function Ls(e){var t=e.alternate;return e===Te||t!==null&&t===Te}function ed(e,t){Ia=ws=!0;var l=e.pending;l===null?t.next=t:(t.next=l.next,l.next=t),e.pending=t}function td(e,t,l){if((l&4194048)!==0){var a=t.lanes;a&=e.pendingLanes,l|=a,t.lanes=l,so(e,l)}}var Gn={readContext:ht,use:qs,useCallback:Fe,useContext:Fe,useEffect:Fe,useImperativeHandle:Fe,useLayoutEffect:Fe,useInsertionEffect:Fe,useMemo:Fe,useReducer:Fe,useRef:Fe,useState:Fe,useDebugValue:Fe,useDeferredValue:Fe,useTransition:Fe,useSyncExternalStore:Fe,useId:Fe,useHostTransitionStatus:Fe,useFormState:Fe,useActionState:Fe,useOptimistic:Fe,useMemoCache:Fe,useCacheRefresh:Fe};Gn.useEffectEvent=Fe;var ld={readContext:ht,use:qs,useCallback:function(e,t){return bt().memoizedState=[e,t===void 0?null:t],e},useContext:ht,useEffect:Gr,useImperativeHandle:function(e,t,l){l=l!=null?l.concat([e]):null,ks(4194308,4,Qr.bind(null,t,e),l)},useLayoutEffect:function(e,t){return ks(4194308,4,e,t)},useInsertionEffect:function(e,t){ks(4,2,e,t)},useMemo:function(e,t){var l=bt();t=t===void 0?null:t;var a=e();if(Sa){F(!0);try{e()}finally{F(!1)}}return l.memoizedState=[a,t],a},useReducer:function(e,t,l){var a=bt();if(l!==void 0){var s=l(t);if(Sa){F(!0);try{l(t)}finally{F(!1)}}}else s=t;return a.memoizedState=a.baseState=s,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:s},a.queue=e,e=e.dispatch=Rh.bind(null,Te,e),[a.memoizedState,e]},useRef:function(e){var t=bt();return e={current:e},t.memoizedState=e},useState:function(e){e=Ru(e);var t=e.queue,l=Pr.bind(null,Te,t);return t.dispatch=l,[e.memoizedState,l]},useDebugValue:Bu,useDeferredValue:function(e,t){var l=bt();return qu(l,e,t)},useTransition:function(){var e=Ru(!1);return e=Jr.bind(null,Te,e.queue,!0,!1),bt().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,l){var a=Te,s=bt();if(Ue){if(l===void 0)throw Error(r(407));l=l()}else{if(l=t(),Qe===null)throw Error(r(349));(_e&127)!==0||Nr(a,t,l)}s.memoizedState=l;var i={value:l,getSnapshot:t};return s.queue=i,Gr(Cr.bind(null,a,i,e),[e]),a.flags|=2048,en(9,{destroy:void 0},Ar.bind(null,a,i,l,t),null),l},useId:function(){var e=bt(),t=Qe.identifierPrefix;if(Ue){var l=fl,a=dl;l=(a&~(1<<32-he(a)-1)).toString(32)+l,t="_"+t+"R_"+l,l=Hs++,0<\/script>",i=i.removeChild(i.firstChild);break;case"select":i=typeof a.is=="string"?c.createElement("select",{is:a.is}):c.createElement("select"),a.multiple?i.multiple=!0:a.size&&(i.size=a.size);break;default:i=typeof a.is=="string"?c.createElement(s,{is:a.is}):c.createElement(s)}}i[ft]=t,i[St]=a;e:for(c=t.child;c!==null;){if(c.tag===5||c.tag===6)i.appendChild(c.stateNode);else if(c.tag!==4&&c.tag!==27&&c.child!==null){c.child.return=c,c=c.child;continue}if(c===t)break e;for(;c.sibling===null;){if(c.return===null||c.return===t)break e;c=c.return}c.sibling.return=c.return,c=c.sibling}t.stateNode=i;e:switch(vt(i,s,a),s){case"button":case"input":case"select":case"textarea":a=!!a.autoFocus;break e;case"img":a=!0;break e;default:a=!1}a&&El(t)}}return Ke(t),tc(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,l),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==a&&El(t);else{if(typeof a!="string"&&t.stateNode===null)throw Error(r(166));if(e=Ne.current,Ka(t)){if(e=t.stateNode,l=t.memoizedProps,a=null,s=mt,s!==null)switch(s.tag){case 27:case 5:a=s.memoizedProps}e[ft]=t,e=!!(e.nodeValue===l||a!==null&&a.suppressHydrationWarning===!0||Sf(e.nodeValue,l)),e||Yl(t,!0)}else e=ci(e).createTextNode(a),e[ft]=t,t.stateNode=e}return Ke(t),null;case 31:if(l=t.memoizedState,e===null||e.memoizedState!==null){if(a=Ka(t),l!==null){if(e===null){if(!a)throw Error(r(318));if(e=t.memoizedState,e=e!==null?e.dehydrated:null,!e)throw Error(r(557));e[ft]=t}else pa(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;Ke(t),e=!1}else l=du(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=l),e=!0;if(!e)return t.flags&256?(qt(t),t):(qt(t),null);if((t.flags&128)!==0)throw Error(r(558))}return Ke(t),null;case 13:if(a=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(s=Ka(t),a!==null&&a.dehydrated!==null){if(e===null){if(!s)throw Error(r(318));if(s=t.memoizedState,s=s!==null?s.dehydrated:null,!s)throw Error(r(317));s[ft]=t}else pa(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;Ke(t),s=!1}else s=du(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=s),s=!0;if(!s)return t.flags&256?(qt(t),t):(qt(t),null)}return qt(t),(t.flags&128)!==0?(t.lanes=l,t):(l=a!==null,e=e!==null&&e.memoizedState!==null,l&&(a=t.child,s=null,a.alternate!==null&&a.alternate.memoizedState!==null&&a.alternate.memoizedState.cachePool!==null&&(s=a.alternate.memoizedState.cachePool.pool),i=null,a.memoizedState!==null&&a.memoizedState.cachePool!==null&&(i=a.memoizedState.cachePool.pool),i!==s&&(a.flags|=2048)),l!==e&&l&&(t.child.flags|=8192),Vs(t,t.updateQueue),Ke(t),null);case 4:return ce(),e===null&&Nc(t.stateNode.containerInfo),Ke(t),null;case 10:return Nl(t.type),Ke(t),null;case 19:if(h(et),a=t.memoizedState,a===null)return Ke(t),null;if(s=(t.flags&128)!==0,i=a.rendering,i===null)if(s)Yn(a,!1);else{if(Ie!==0||e!==null&&(e.flags&128)!==0)for(e=t.child;e!==null;){if(i=Rs(e),i!==null){for(t.flags|=128,Yn(a,!1),e=i.updateQueue,t.updateQueue=e,Vs(t,e),t.subtreeFlags=0,e=l,l=t.child;l!==null;)Po(l,e),l=l.sibling;return M(et,et.current&1|2),Ue&&jl(t,a.treeForkCount),t.child}e=e.sibling}a.tail!==null&&Pe()>Is&&(t.flags|=128,s=!0,Yn(a,!1),t.lanes=4194304)}else{if(!s)if(e=Rs(i),e!==null){if(t.flags|=128,s=!0,e=e.updateQueue,t.updateQueue=e,Vs(t,e),Yn(a,!0),a.tail===null&&a.tailMode==="hidden"&&!i.alternate&&!Ue)return Ke(t),null}else 2*Pe()-a.renderingStartTime>Is&&l!==536870912&&(t.flags|=128,s=!0,Yn(a,!1),t.lanes=4194304);a.isBackwards?(i.sibling=t.child,t.child=i):(e=a.last,e!==null?e.sibling=i:t.child=i,a.last=i)}return a.tail!==null?(e=a.tail,a.rendering=e,a.tail=e.sibling,a.renderingStartTime=Pe(),e.sibling=null,l=et.current,M(et,s?l&1|2:l&1),Ue&&jl(t,a.treeForkCount),e):(Ke(t),null);case 22:case 23:return qt(t),Au(),a=t.memoizedState!==null,e!==null?e.memoizedState!==null!==a&&(t.flags|=8192):a&&(t.flags|=8192),a?(l&536870912)!==0&&(t.flags&128)===0&&(Ke(t),t.subtreeFlags&6&&(t.flags|=8192)):Ke(t),l=t.updateQueue,l!==null&&Vs(t,l.retryQueue),l=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(l=e.memoizedState.cachePool.pool),a=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(a=t.memoizedState.cachePool.pool),a!==l&&(t.flags|=2048),e!==null&&h(ga),null;case 24:return l=null,e!==null&&(l=e.memoizedState.cache),t.memoizedState.cache!==l&&(t.flags|=2048),Nl(nt),Ke(t),null;case 25:return null;case 30:return null}throw Error(r(156,t.tag))}function Gh(e,t){switch(ou(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Nl(nt),ce(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return I(t),null;case 31:if(t.memoizedState!==null){if(qt(t),t.alternate===null)throw Error(r(340));pa()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(qt(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(r(340));pa()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return h(et),null;case 4:return ce(),null;case 10:return Nl(t.type),null;case 22:case 23:return qt(t),Au(),e!==null&&h(ga),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return Nl(nt),null;case 25:return null;default:return null}}function Td(e,t){switch(ou(t),t.tag){case 3:Nl(nt),ce();break;case 26:case 27:case 5:I(t);break;case 4:ce();break;case 31:t.memoizedState!==null&&qt(t);break;case 13:qt(t);break;case 19:h(et);break;case 10:Nl(t.type);break;case 22:case 23:qt(t),Au(),e!==null&&h(ga);break;case 24:Nl(nt)}}function Ln(e,t){try{var l=t.updateQueue,a=l!==null?l.lastEffect:null;if(a!==null){var s=a.next;l=s;do{if((l.tag&e)===e){a=void 0;var i=l.create,c=l.inst;a=i(),c.destroy=a}l=l.next}while(l!==s)}}catch(o){Ge(t,t.return,o)}}function Jl(e,t,l){try{var a=t.updateQueue,s=a!==null?a.lastEffect:null;if(s!==null){var i=s.next;a=i;do{if((a.tag&e)===e){var c=a.inst,o=c.destroy;if(o!==void 0){c.destroy=void 0,s=t;var d=l,j=o;try{j()}catch(D){Ge(s,d,D)}}}a=a.next}while(a!==i)}}catch(D){Ge(t,t.return,D)}}function Ed(e){var t=e.updateQueue;if(t!==null){var l=e.stateNode;try{yr(t,l)}catch(a){Ge(e,e.return,a)}}}function Md(e,t,l){l.props=Na(e.type,e.memoizedProps),l.state=e.memoizedState;try{l.componentWillUnmount()}catch(a){Ge(e,t,a)}}function Qn(e,t){try{var l=e.ref;if(l!==null){switch(e.tag){case 26:case 27:case 5:var a=e.stateNode;break;case 30:a=e.stateNode;break;default:a=e.stateNode}typeof l=="function"?e.refCleanup=l(a):l.current=a}}catch(s){Ge(e,t,s)}}function ml(e,t){var l=e.ref,a=e.refCleanup;if(l!==null)if(typeof a=="function")try{a()}catch(s){Ge(e,t,s)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof l=="function")try{l(null)}catch(s){Ge(e,t,s)}else l.current=null}function zd(e){var t=e.type,l=e.memoizedProps,a=e.stateNode;try{e:switch(t){case"button":case"input":case"select":case"textarea":l.autoFocus&&a.focus();break e;case"img":l.src?a.src=l.src:l.srcSet&&(a.srcset=l.srcSet)}}catch(s){Ge(e,e.return,s)}}function lc(e,t,l){try{var a=e.stateNode;u0(a,e.type,l,t),a[St]=t}catch(s){Ge(e,e.return,s)}}function Od(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&ta(e.type)||e.tag===4}function ac(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Od(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&ta(e.type)||e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function nc(e,t,l){var a=e.tag;if(a===5||a===6)e=e.stateNode,t?(l.nodeType===9?l.body:l.nodeName==="HTML"?l.ownerDocument.body:l).insertBefore(e,t):(t=l.nodeType===9?l.body:l.nodeName==="HTML"?l.ownerDocument.body:l,t.appendChild(e),l=l._reactRootContainer,l!=null||t.onclick!==null||(t.onclick=gl));else if(a!==4&&(a===27&&ta(e.type)&&(l=e.stateNode,t=null),e=e.child,e!==null))for(nc(e,t,l),e=e.sibling;e!==null;)nc(e,t,l),e=e.sibling}function Js(e,t,l){var a=e.tag;if(a===5||a===6)e=e.stateNode,t?l.insertBefore(e,t):l.appendChild(e);else if(a!==4&&(a===27&&ta(e.type)&&(l=e.stateNode),e=e.child,e!==null))for(Js(e,t,l),e=e.sibling;e!==null;)Js(e,t,l),e=e.sibling}function _d(e){var t=e.stateNode,l=e.memoizedProps;try{for(var a=e.type,s=t.attributes;s.length;)t.removeAttributeNode(s[0]);vt(t,a,l),t[ft]=e,t[St]=l}catch(i){Ge(e,e.return,i)}}var Ml=!1,ut=!1,sc=!1,Dd=typeof WeakSet=="function"?WeakSet:Set,dt=null;function kh(e,t){if(e=e.containerInfo,Tc=pi,e=Xo(e),Ii(e)){if("selectionStart"in e)var l={start:e.selectionStart,end:e.selectionEnd};else e:{l=(l=e.ownerDocument)&&l.defaultView||window;var a=l.getSelection&&l.getSelection();if(a&&a.rangeCount!==0){l=a.anchorNode;var s=a.anchorOffset,i=a.focusNode;a=a.focusOffset;try{l.nodeType,i.nodeType}catch{l=null;break e}var c=0,o=-1,d=-1,j=0,D=0,q=e,S=null;t:for(;;){for(var E;q!==l||s!==0&&q.nodeType!==3||(o=c+s),q!==i||a!==0&&q.nodeType!==3||(d=c+a),q.nodeType===3&&(c+=q.nodeValue.length),(E=q.firstChild)!==null;)S=q,q=E;for(;;){if(q===e)break t;if(S===l&&++j===s&&(o=c),S===i&&++D===a&&(d=c),(E=q.nextSibling)!==null)break;q=S,S=q.parentNode}q=E}l=o===-1||d===-1?null:{start:o,end:d}}else l=null}l=l||{start:0,end:0}}else l=null;for(Ec={focusedElem:e,selectionRange:l},pi=!1,dt=t;dt!==null;)if(t=dt,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,dt=e;else for(;dt!==null;){switch(t=dt,i=t.alternate,e=t.flags,t.tag){case 0:if((e&4)!==0&&(e=t.updateQueue,e=e!==null?e.events:null,e!==null))for(l=0;l title"))),vt(i,a,l),i[ft]=e,rt(i),a=i;break e;case"link":var c=Gf("link","href",s).get(a+(l.href||""));if(c){for(var o=0;oLe&&(c=Le,Le=Se,Se=c);var g=Lo(o,Se),v=Lo(o,Le);if(g&&v&&(E.rangeCount!==1||E.anchorNode!==g.node||E.anchorOffset!==g.offset||E.focusNode!==v.node||E.focusOffset!==v.offset)){var x=q.createRange();x.setStart(g.node,g.offset),E.removeAllRanges(),Se>Le?(E.addRange(x),E.extend(v.node,v.offset)):(x.setEnd(v.node,v.offset),E.addRange(x))}}}}for(q=[],E=o;E=E.parentNode;)E.nodeType===1&&q.push({element:E,left:E.scrollLeft,top:E.scrollTop});for(typeof o.focus=="function"&&o.focus(),o=0;ol?32:l,A.T=null,l=fc,fc=null;var i=Il,c=Ul;if(ct=0,sn=Il=null,Ul=0,(He&6)!==0)throw Error(r(331));var o=He;if(He|=4,Qd(i.current),kd(i,i.current,c,l),He=o,$n(0,!1),J&&typeof J.onPostCommitFiberRoot=="function")try{J.onPostCommitFiberRoot(K,i)}catch{}return!0}finally{L.p=s,A.T=a,cf(e,t)}}function rf(e,t,l){t=Zt(l,t),t=Ku(e.stateNode,t,2),e=Kl(e,t,2),e!==null&&(pn(e,2),hl(e))}function Ge(e,t,l){if(e.tag===3)rf(e,e,l);else for(;t!==null;){if(t.tag===3){rf(t,e,l);break}else if(t.tag===1){var a=t.stateNode;if(typeof t.type.getDerivedStateFromError=="function"||typeof a.componentDidCatch=="function"&&(Fl===null||!Fl.has(a))){e=Zt(l,e),l=rd(2),a=Kl(t,l,2),a!==null&&(dd(l,a,t,e),pn(a,2),hl(a));break}}t=t.return}}function vc(e,t,l){var a=e.pingCache;if(a===null){a=e.pingCache=new Qh;var s=new Set;a.set(t,s)}else s=a.get(t),s===void 0&&(s=new Set,a.set(t,s));s.has(l)||(cc=!0,s.add(l),e=Jh.bind(null,e,t,l),t.then(e,e))}function Jh(e,t,l){var a=e.pingCache;a!==null&&a.delete(t),e.pingedLanes|=e.suspendedLanes&l,e.warmLanes&=~l,Qe===e&&(_e&l)===l&&(Ie===4||Ie===3&&(_e&62914560)===_e&&300>Pe()-Fs?(He&2)===0&&un(e,0):oc|=l,nn===_e&&(nn=0)),hl(e)}function df(e,t){t===0&&(t=ao()),e=ma(e,t),e!==null&&(pn(e,t),hl(e))}function $h(e){var t=e.memoizedState,l=0;t!==null&&(l=t.retryLane),df(e,l)}function Wh(e,t){var l=0;switch(e.tag){case 31:case 13:var a=e.stateNode,s=e.memoizedState;s!==null&&(l=s.retryLane);break;case 19:a=e.stateNode;break;case 22:a=e.stateNode._retryCache;break;default:throw Error(r(314))}a!==null&&a.delete(t),df(e,l)}function Fh(e,t){return B(e,t)}var ni=null,on=null,yc=!1,si=!1,gc=!1,ea=0;function hl(e){e!==on&&e.next===null&&(on===null?ni=on=e:on=on.next=e),si=!0,yc||(yc=!0,Ph())}function $n(e,t){if(!gc&&si){gc=!0;do for(var l=!1,a=ni;a!==null;){if(e!==0){var s=a.pendingLanes;if(s===0)var i=0;else{var c=a.suspendedLanes,o=a.pingedLanes;i=(1<<31-he(42|e)+1)-1,i&=s&~(c&~o),i=i&201326741?i&201326741|1:i?i|2:0}i!==0&&(l=!0,pf(a,i))}else i=_e,i=Hl(a,a===Qe?i:0,a.cancelPendingCommit!==null||a.timeoutHandle!==-1),(i&3)===0||ca(a,i)||(l=!0,pf(a,i));a=a.next}while(l);gc=!1}}function Ih(){ff()}function ff(){si=yc=!1;var e=0;ea!==0&&o0()&&(e=ea);for(var t=Pe(),l=null,a=ni;a!==null;){var s=a.next,i=mf(a,t);i===0?(a.next=null,l===null?ni=s:l.next=s,s===null&&(on=l)):(l=a,(e!==0||(i&3)!==0)&&(si=!0)),a=s}ct!==0&&ct!==5||$n(e),ea!==0&&(ea=0)}function mf(e,t){for(var l=e.suspendedLanes,a=e.pingedLanes,s=e.expirationTimes,i=e.pendingLanes&-62914561;0o)break;var D=d.transferSize,q=d.initiatorType;D&&Nf(q)&&(d=d.responseEnd,c+=D*(d"u"?null:document;function wf(e,t,l){var a=rn;if(a&&typeof t=="string"&&t){var s=Xt(t);s='link[rel="'+e+'"][href="'+s+'"]',typeof l=="string"&&(s+='[crossorigin="'+l+'"]'),Rf.has(s)||(Rf.add(s),e={rel:e,crossOrigin:l,href:t},a.querySelector(s)===null&&(t=a.createElement("link"),vt(t,"link",e),rt(t),a.head.appendChild(t)))}}function g0(e){Rl.D(e),wf("dns-prefetch",e,null)}function b0(e,t){Rl.C(e,t),wf("preconnect",e,t)}function x0(e,t,l){Rl.L(e,t,l);var a=rn;if(a&&e&&t){var s='link[rel="preload"][as="'+Xt(t)+'"]';t==="image"&&l&&l.imageSrcSet?(s+='[imagesrcset="'+Xt(l.imageSrcSet)+'"]',typeof l.imageSizes=="string"&&(s+='[imagesizes="'+Xt(l.imageSizes)+'"]')):s+='[href="'+Xt(e)+'"]';var i=s;switch(t){case"style":i=dn(e);break;case"script":i=fn(e)}It.has(i)||(e=_({rel:"preload",href:t==="image"&&l&&l.imageSrcSet?void 0:e,as:t},l),It.set(i,e),a.querySelector(s)!==null||t==="style"&&a.querySelector(Pn(i))||t==="script"&&a.querySelector(es(i))||(t=a.createElement("link"),vt(t,"link",e),rt(t),a.head.appendChild(t)))}}function j0(e,t){Rl.m(e,t);var l=rn;if(l&&e){var a=t&&typeof t.as=="string"?t.as:"script",s='link[rel="modulepreload"][as="'+Xt(a)+'"][href="'+Xt(e)+'"]',i=s;switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":i=fn(e)}if(!It.has(i)&&(e=_({rel:"modulepreload",href:e},t),It.set(i,e),l.querySelector(s)===null)){switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(l.querySelector(es(i)))return}a=l.createElement("link"),vt(a,"link",e),rt(a),l.head.appendChild(a)}}}function S0(e,t,l){Rl.S(e,t,l);var a=rn;if(a&&e){var s=Da(a).hoistableStyles,i=dn(e);t=t||"default";var c=s.get(i);if(!c){var o={loading:0,preload:null};if(c=a.querySelector(Pn(i)))o.loading=5;else{e=_({rel:"stylesheet",href:e,"data-precedence":t},l),(l=It.get(i))&&Rc(e,l);var d=c=a.createElement("link");rt(d),vt(d,"link",e),d._p=new Promise(function(j,D){d.onload=j,d.onerror=D}),d.addEventListener("load",function(){o.loading|=1}),d.addEventListener("error",function(){o.loading|=2}),o.loading|=4,ri(c,t,a)}c={type:"stylesheet",instance:c,count:1,state:o},s.set(i,c)}}}function N0(e,t){Rl.X(e,t);var l=rn;if(l&&e){var a=Da(l).hoistableScripts,s=fn(e),i=a.get(s);i||(i=l.querySelector(es(s)),i||(e=_({src:e,async:!0},t),(t=It.get(s))&&wc(e,t),i=l.createElement("script"),rt(i),vt(i,"link",e),l.head.appendChild(i)),i={type:"script",instance:i,count:1,state:null},a.set(s,i))}}function A0(e,t){Rl.M(e,t);var l=rn;if(l&&e){var a=Da(l).hoistableScripts,s=fn(e),i=a.get(s);i||(i=l.querySelector(es(s)),i||(e=_({src:e,async:!0,type:"module"},t),(t=It.get(s))&&wc(e,t),i=l.createElement("script"),rt(i),vt(i,"link",e),l.head.appendChild(i)),i={type:"script",instance:i,count:1,state:null},a.set(s,i))}}function Hf(e,t,l,a){var s=(s=Ne.current)?oi(s):null;if(!s)throw Error(r(446));switch(e){case"meta":case"title":return null;case"style":return typeof l.precedence=="string"&&typeof l.href=="string"?(t=dn(l.href),l=Da(s).hoistableStyles,a=l.get(t),a||(a={type:"style",instance:null,count:0,state:null},l.set(t,a)),a):{type:"void",instance:null,count:0,state:null};case"link":if(l.rel==="stylesheet"&&typeof l.href=="string"&&typeof l.precedence=="string"){e=dn(l.href);var i=Da(s).hoistableStyles,c=i.get(e);if(c||(s=s.ownerDocument||s,c={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},i.set(e,c),(i=s.querySelector(Pn(e)))&&!i._p&&(c.instance=i,c.state.loading=5),It.has(e)||(l={rel:"preload",as:"style",href:l.href,crossOrigin:l.crossOrigin,integrity:l.integrity,media:l.media,hrefLang:l.hrefLang,referrerPolicy:l.referrerPolicy},It.set(e,l),i||C0(s,e,l,c.state))),t&&a===null)throw Error(r(528,""));return c}if(t&&a!==null)throw Error(r(529,""));return null;case"script":return t=l.async,l=l.src,typeof l=="string"&&t&&typeof t!="function"&&typeof t!="symbol"?(t=fn(l),l=Da(s).hoistableScripts,a=l.get(t),a||(a={type:"script",instance:null,count:0,state:null},l.set(t,a)),a):{type:"void",instance:null,count:0,state:null};default:throw Error(r(444,e))}}function dn(e){return'href="'+Xt(e)+'"'}function Pn(e){return'link[rel="stylesheet"]['+e+"]"}function Bf(e){return _({},e,{"data-precedence":e.precedence,precedence:null})}function C0(e,t,l,a){e.querySelector('link[rel="preload"][as="style"]['+t+"]")?a.loading=1:(t=e.createElement("link"),a.preload=t,t.addEventListener("load",function(){return a.loading|=1}),t.addEventListener("error",function(){return a.loading|=2}),vt(t,"link",l),rt(t),e.head.appendChild(t))}function fn(e){return'[src="'+Xt(e)+'"]'}function es(e){return"script[async]"+e}function qf(e,t,l){if(t.count++,t.instance===null)switch(t.type){case"style":var a=e.querySelector('style[data-href~="'+Xt(l.href)+'"]');if(a)return t.instance=a,rt(a),a;var s=_({},l,{"data-href":l.href,"data-precedence":l.precedence,href:null,precedence:null});return a=(e.ownerDocument||e).createElement("style"),rt(a),vt(a,"style",s),ri(a,l.precedence,e),t.instance=a;case"stylesheet":s=dn(l.href);var i=e.querySelector(Pn(s));if(i)return t.state.loading|=4,t.instance=i,rt(i),i;a=Bf(l),(s=It.get(s))&&Rc(a,s),i=(e.ownerDocument||e).createElement("link"),rt(i);var c=i;return c._p=new Promise(function(o,d){c.onload=o,c.onerror=d}),vt(i,"link",a),t.state.loading|=4,ri(i,l.precedence,e),t.instance=i;case"script":return i=fn(l.src),(s=e.querySelector(es(i)))?(t.instance=s,rt(s),s):(a=l,(s=It.get(i))&&(a=_({},l),wc(a,s)),e=e.ownerDocument||e,s=e.createElement("script"),rt(s),vt(s,"link",a),e.head.appendChild(s),t.instance=s);case"void":return null;default:throw Error(r(443,t.type))}else t.type==="stylesheet"&&(t.state.loading&4)===0&&(a=t.instance,t.state.loading|=4,ri(a,l.precedence,e));return t.instance}function ri(e,t,l){for(var a=l.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),s=a.length?a[a.length-1]:null,i=s,c=0;c title"):null)}function T0(e,t,l){if(l===1||t.itemProp!=null)return!1;switch(e){case"meta":case"title":return!0;case"style":if(typeof t.precedence!="string"||typeof t.href!="string"||t.href==="")break;return!0;case"link":if(typeof t.rel!="string"||typeof t.href!="string"||t.href===""||t.onLoad||t.onError)break;return t.rel==="stylesheet"?(e=t.disabled,typeof t.precedence=="string"&&e==null):!0;case"script":if(t.async&&typeof t.async!="function"&&typeof t.async!="symbol"&&!t.onLoad&&!t.onError&&t.src&&typeof t.src=="string")return!0}return!1}function Yf(e){return!(e.type==="stylesheet"&&(e.state.loading&3)===0)}function E0(e,t,l,a){if(l.type==="stylesheet"&&(typeof a.media!="string"||matchMedia(a.media).matches!==!1)&&(l.state.loading&4)===0){if(l.instance===null){var s=dn(a.href),i=t.querySelector(Pn(s));if(i){t=i._p,t!==null&&typeof t=="object"&&typeof t.then=="function"&&(e.count++,e=fi.bind(e),t.then(e,e)),l.state.loading|=4,l.instance=i,rt(i);return}i=t.ownerDocument||t,a=Bf(a),(s=It.get(s))&&Rc(a,s),i=i.createElement("link"),rt(i);var c=i;c._p=new Promise(function(o,d){c.onload=o,c.onerror=d}),vt(i,"link",a),l.instance=i}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(l,t),(t=l.state.preload)&&(l.state.loading&3)===0&&(e.count++,l=fi.bind(e),t.addEventListener("load",l),t.addEventListener("error",l))}}var Hc=0;function M0(e,t){return e.stylesheets&&e.count===0&&hi(e,e.stylesheets),0Hc?50:800)+t);return e.unsuspend=l,function(){e.unsuspend=null,clearTimeout(a),clearTimeout(s)}}:null}function fi(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)hi(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var mi=null;function hi(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,mi=new Map,t.forEach(z0,e),mi=null,fi.call(e))}function z0(e,t){if(!(t.state.loading&4)){var l=mi.get(e);if(l)var a=l.get(null);else{l=new Map,mi.set(e,l);for(var s=e.querySelectorAll("link[data-precedence],style[data-precedence]"),i=0;i"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(u)}catch(f){console.error(f)}}return u(),Kc.exports=V0(),Kc.exports}var $0=J0();function je(u){return Array.isArray(u)?u:[]}function ym(u){const f=new Set;return u.map(p=>p.trim()).filter(p=>{const r=p.toLowerCase();return!p||f.has(r)?!1:(f.add(r),!0)})}function Pt(u){const f=Ni.some(p=>p.value===u.streamMode)?u.streamMode:"auto";return{...u,streamMode:f,models:je(u.models),allowedGroupIds:je(u.allowedGroupIds),openaiAccounts:je(u.openaiAccounts),kiroAccounts:je(u.kiroAccounts)}}function us(u){return{...u,aliases:je(u.aliases)}}function W0(u){return{...u,apiKeys:je(u.apiKeys),logs:je(u.logs)}}class Ci extends Error{status;payload;constructor(f,p,r){super(p),this.status=f,this.payload=r}}const F0={home:"M3 10.5 12 3l9 7.5V21a1 1 0 0 1-1 1h-5v-7H9v7H4a1 1 0 0 1-1-1z",users:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75",key:"M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.78 7.78 5.5 5.5 0 0 1 7.78-7.78ZM14 8l7-7M21 8h-5V3",models:"M12 2 4 6v12l8 4 8-4V6zM4 6l8 4 8-4M12 10v12",image:"M21 19V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2ZM8.5 11a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5ZM21 16l-5-5L5 21",route:"M4 19a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM20 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM7 16h3a4 4 0 0 0 4-4V8h3",logs:"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8zM14 2v6h6M8 13h8M8 17h6",orbit:"M12 7a5 5 0 1 0 0 10 5 5 0 0 0 0-10ZM3 12a9 3 0 1 0 18 0 9 3 0 1 0-18 0Z",settings:"M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7ZM19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.6 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 8.92 4a1.65 1.65 0 0 0 1-1.51V2a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9c.14.31.39.57.71.71.23.1.49.18.8.2H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z",search:"M21 21l-4.35-4.35M10.5 18a7.5 7.5 0 1 1 0-15 7.5 7.5 0 0 1 0 15Z",copy:"M8 8h11a1 1 0 0 1 1 1v11a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1ZM4 16H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h11a1 1 0 0 1 1 1v1",ban:"M4.93 4.93 19.07 19.07M22 12A10 10 0 1 1 2 12a10 10 0 0 1 20 0Z",check:"M20 6 9 17l-5-5",moon:"M21 12.8A8.5 8.5 0 1 1 11.2 3 6.5 6.5 0 0 0 21 12.8Z",sun:"M12 4V2M12 22v-2M4.93 4.93 3.52 3.52M20.48 20.48l-1.41-1.41M4 12H2M22 12h-2M4.93 19.07l-1.41 1.41M20.48 3.52l-1.41 1.41M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z",plus:"M12 5v14M5 12h14"};function zt({name:u}){return n.jsx("svg",{viewBox:"0 0 24 24","aria-hidden":"true",className:"icon",children:n.jsx("path",{d:F0[u]})})}async function pe(u,f){const p=new Headers(f?.headers);p.set("Content-Type","application/json");const r=window.sessionStorage.getItem("capi-admin-token");r&&p.set("Authorization",`Bearer ${r}`);const C=await fetch(u,{credentials:"include",...f,headers:p});if(!C.ok){const T=await C.json().catch(()=>null);throw new Ci(C.status,T?.error?.message||`Request failed: ${C.status}`,T)}return C.json()}async function I0(u,f){const p=new Headers,r=window.sessionStorage.getItem("capi-admin-token");r&&p.set("Authorization",`Bearer ${r}`);const C=await fetch(u,{credentials:"include",method:"POST",headers:p,body:f});if(!C.ok){const T=await C.json().catch(()=>null);throw new Ci(C.status,T?.error?.message||`Request failed: ${C.status}`,T)}return C.json()}const dm=[{id:"overview",label:"概览",icon:"home"},{id:"users",label:"用户",icon:"users"},{id:"groups",label:"分组",icon:"users"},{id:"keys",label:"密钥",icon:"key"},{id:"models",label:"模型",icon:"models"},{id:"drawing",label:"绘图",icon:"image"},{id:"antigravity",label:"Antigravity",icon:"orbit"},{id:"channels",label:"渠道",icon:"route"},{id:"logs",label:"日志",icon:"logs"},{id:"settings",label:"设置",icon:"settings"}],to=[{value:"kiro",label:"Kiro / Amazon Q"},{value:"codex",label:"Codex / ChatGPT OAuth"},{value:"antigravity",label:"Antigravity / Google"},{value:"cpa",label:"CPA / CLIProxyAPI"},{value:"openai",label:"OpenAI"},{value:"anthropic",label:"Anthropic / Claude"},{value:"google",label:"Google Gemini"},{value:"deepseek",label:"DeepSeek"},{value:"openrouter",label:"OpenRouter"},{value:"groq",label:"Groq"},{value:"siliconflow",label:"SiliconFlow"},{value:"moonshot",label:"Moonshot"},{value:"compatible",label:"OpenAI 兼容接口"}],gm="https://api.openai.com/v1",Ti="https://chatgpt.com/backend-api",bm="http://localhost:8317/v1",xm="https://codewhisperer.us-east-1.amazonaws.com",jm="gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4, gpt-image-2, gpt-image-1",P0="gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4, gpt-image-2",ep="gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4, claude-sonnet-4, gemini-3.1-pro",tp="claude-sonnet-4.5, claude-sonnet-4, claude-haiku-4.5, claude-opus-4.5",Sm="https://daily-cloudcode-pa.googleapis.com",lp="claude-opus-4-6-thinking, claude-sonnet-4-6, gemini-3.8-flash-high, gemini-3.7-flash-high, gemini-3.6-flash-high, gemini-3-flash, gemini-pro-agent, gemini-3.1-pro-low, gpt-oss-120b-medium, gemini-3.1-flash-lite, gemini-3.5-flash-lite, gemini-3.1-flash-image",Fc=[{provider:"kiro",label:"Kiro / Amazon Q",name:"Kiro 账号池",baseUrl:xm,models:tp.split(",").map(u=>u.trim())},{provider:"openai",label:"OpenAI",name:"OpenAI 主线路",baseUrl:gm,models:P0.split(",").map(u=>u.trim())},{provider:"codex",label:"Codex 账号池",name:"Codex 账号池",baseUrl:Ti,models:jm.split(",").map(u=>u.trim())},{provider:"antigravity",label:"Antigravity 账号池",name:"Antigravity 账号池",baseUrl:Sm,models:lp.split(",").map(u=>u.trim())},{provider:"cpa",label:"CPA / CLIProxyAPI",name:"CPA 本地代理",baseUrl:bm,models:ep.split(",").map(u=>u.trim())},{provider:"compatible",label:"OpenAI 兼容接口",name:"兼容渠道",baseUrl:"",models:[]},{provider:"openrouter",label:"OpenRouter",name:"OpenRouter",baseUrl:"https://openrouter.ai/api/v1",models:[]},{provider:"google",label:"Google Gemini",name:"Gemini",baseUrl:"",models:[]},{provider:"anthropic",label:"Anthropic / Claude",name:"Claude",baseUrl:"",models:[]}];function Ta(u){return Fc.find(f=>f.provider===u)||Fc[2]}function Ic(u){const f=Ta(u);return{name:f.name,provider:f.provider,baseUrl:f.baseUrl,models:[...f.models],streamMode:"auto"}}function ap(u){const f=u?.trim().toLowerCase();return f&&({free:"Free",plus:"Plus",pro:"Pro",team:"Team",enterprise:"Enterprise"}[f]||u)||"套餐未知"}function fm(u){return u?{upstream_token_invalidated:"Token 已失效",upstream_invalid_api_key:"API Key 无效",upstream_account_error:"账号错误",upstream_accounts_unavailable:"账号池不可用",upstream_error:"上游错误"}[u]||u:""}function Ei(u){return u==="openai"?gm:u==="codex"?Ti:u==="antigravity"?Sm:u==="cpa"||u==="cliproxyapi"?bm:u==="kiro"?xm:""}function Pc(u){return ym(u.split(/[\s,;,;]+/))}function np(u){return ym(u.split(/[\n,;,;]+/).map(f=>f.trim()))}function Nm(u){const f=np(u);return f.length===0?{}:f.length===1?{upstreamApiKey:f[0]}:{upstreamApiKeys:f}}const Ni=[{value:"auto",label:"自动",description:"按请求参数处理"},{value:"real",label:"真流",description:"直连上游 SSE"},{value:"fake",label:"假流",description:"非流转 SSE"},{value:"disabled",label:"禁用流",description:"流式请求跳过"}];function tl(u){if(!u)return"未使用";const f=new Date(u);return Number.isNaN(f.getTime())?"-":new Intl.DateTimeFormat("zh-CN",{month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}).format(f)}function mm(u){if(!u)return"";const f=new Date(u);if(Number.isNaN(f.getTime()))return"";const p=f.getTimezoneOffset()*6e4;return new Date(f.getTime()-p).toISOString().slice(0,16)}function sp(u){if(!u)return"";const f=new Date(u);return Number.isNaN(f.getTime())?"":f.toISOString()}function ip(u){if(!u)return"-";const f=new Date(u);return Number.isNaN(f.getTime())?"-":new Intl.DateTimeFormat("zh-CN",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"}).format(f)}function Ot(u){return{active:"正常",disabled:"禁用",limited:"受限",overdue:"欠费",healthy:"正常",standby:"备用",available:"Available",success:"成功",failed:"失败"}[u]||u}function xt(u,f=2){const p=Number(u||0);return new Intl.NumberFormat("zh-CN",{minimumFractionDigits:f,maximumFractionDigits:f}).format(p)}function pl(u){return new Intl.NumberFormat("zh-CN").format(Math.max(0,Math.round(Number(u||0))))}function lo(u){return u.model?u.model:u.errorCode==="invalid_api_key"?"密钥无效":u.errorCode==="model_not_available"?"模型不可用":u.errorCode==="insufficient_quota"?"额度不足":u.errorCode==="rate_limit_exceeded"?"请求过快":u.errorCode||"请求失败"}function up(u){return u.model||(u.errorCode?`错误:${u.errorCode}`:"-")}function cp(u){return u.channel||(u.apiKeyPrefix?`Key ${u.apiKeyPrefix}`:"-")}function op(u){const f=je(u);if(f.length===0)return"未绑定模型";const p=f.slice(0,2).join(", ");return f.length>2?`${p} · 其余 ${f.length-2} 个`:p}function cl(u){return u==="cliproxyapi"||u==="cli-proxy-api"?"CPA / CLIProxyAPI":to.find(f=>f.value===u)?.label||u||"Custom"}function $c(u){const f=`${u.vendor} ${u.id} ${u.name}`.toLowerCase();return f.includes("cliproxyapi")||/\bcpa\b/.test(f)?"cpa":f.includes("openai")||/\bgpt[-_/]/.test(f)||f.includes("o1-")||f.includes("o3-")?"openai":f.includes("anthropic")||f.includes("claude")?"anthropic":f.includes("google")||f.includes("gemini")||f.includes("gcli-")?"google":f.includes("deepseek")?"deepseek":f.includes("openrouter")?"openrouter":f.includes("groq")?"groq":f.includes("siliconflow")?"siliconflow":f.includes("moonshot")||f.includes("kimi")?"moonshot":u.vendor&&u.vendor.toLowerCase()!=="custom"?u.vendor.toLowerCase():"compatible"}function hm({provider:u}){if(u==="deepseek")return n.jsx("span",{className:"provider-icon provider-icon-deepseek","aria-hidden":"true",children:n.jsxs("svg",{viewBox:"0 0 32 32",role:"img",children:[n.jsx("rect",{x:"1",y:"1",width:"30",height:"30",rx:"8"}),n.jsx("path",{transform:"translate(2.4 4.4)",d:"M26.517 3.395c-.282-.138-.403.125-.568.258-.057.044-.105.1-.152.152-.413.44-.895.73-1.524.695-.92-.052-1.705.237-2.4.941-.147-.868-.638-1.386-1.384-1.718-.39-.173-.786-.346-1.06-.721-.19-.268-.243-.566-.338-.86-.061-.176-.121-.357-.325-.388-.222-.034-.309.151-.396.307-.347.635-.481 1.334-.468 2.042.03 1.594.703 2.863 2.04 3.765.152.104.191.207.143.359-.091.31-.2.613-.295.924-.06.198-.151.242-.364.155-.734-.306-1.367-.76-1.927-1.308-.951-.92-1.81-1.934-2.882-2.729-.252-.185-.504-.358-.764-.522-1.094-1.062.143-1.935.43-2.038.3-.108.104-.48-.864-.475-.968.004-1.853.328-2.982.76-.165.065-.339.112-.516.151-1.024-.194-2.088-.237-3.199-.112-2.092.233-3.763 1.222-4.991 2.91C.254 7.972-.093 10.278.332 12.682c.446 2.535 1.74 4.633 3.728 6.274 2.062 1.7 4.436 2.534 7.145 2.375 1.645-.095 3.476-.316 5.542-2.064.521.259 1.068.363 1.975.44.699.065 1.371-.034 1.892-.142.816-.173.76-.929.465-1.067-2.392-1.114-1.866-.661-2.344-1.027 1.215-1.438 3.071-3.993 3.644-7.473.056-.384.128-.925.12-1.236-.005-.19.038-.263.255-.285.6-.069 1.18-.233 1.715-.527 1.55-.846 2.175-2.237 2.322-3.903.022-.255-.005-.518-.274-.652ZM13.014 18.395c-2.318-1.823-3.442-2.423-3.906-2.397-.434.026-.356.523-.26.847.1.32.23.54.412.82.126.186.213.462-.126.67-.746.461-2.044-.156-2.105-.186-1.51-.89-2.773-2.064-3.664-3.67-.86-1.545-1.358-3.204-1.44-4.974-.022-.427.104-.578.529-.656.56-.103 1.137-.125 1.697-.043 2.366.346 4.379 1.403 6.068 3.079.963.954 1.692 2.094 2.443 3.208.799 1.183 1.658 2.31 2.752 3.234.387.324.695.57.99.751-.89.1-2.374.121-3.39-.683Zm1.111-7.146c0-.19.152-.341.343-.341.043 0 .082.009.117.021.048.018.092.044.126.083.061.06.096.146.096.237a.341.341 0 0 1-.343.341.34.34 0 0 1-.339-.341Zm3.451 1.77c-.222.09-.443.168-.656.177-.33.017-.69-.117-.885-.281-.304-.255-.521-.397-.612-.842-.039-.19-.017-.483.017-.652.078-.362-.009-.595-.265-.807-.208-.172-.473-.22-.764-.22-.108 0-.208-.048-.282-.086-.121-.061-.221-.212-.126-.398.031-.06.178-.207.213-.233.395-.225.85-.151 1.272.018.39.16.686.453 1.111.867.434.501.512.639.759 1.015.196.294.373.596.495.942.073.215-.022.392-.277.5Z"})]})});if(u==="openai")return n.jsx("span",{className:"provider-icon provider-icon-openai","aria-hidden":"true",children:n.jsxs("svg",{viewBox:"0 0 32 32",role:"img",children:[n.jsx("rect",{x:"1",y:"1",width:"30",height:"30",rx:"8"}),n.jsx("g",{transform:"translate(7 7.5) scale(0.065)",fill:"currentColor",stroke:"none",children:n.jsx("path",{d:"M267.06 111.34a71.78 71.78 0 0 0-6.17-58.91c-14.5-25.15-43.55-38.09-71.9-32.03A71.78 71.78 0 0 0 135.1.5C106 .43 80.21 19.16 71.29 46.85a71.79 71.79 0 0 0-47.98 34.8c-14.6 25.1-11.28 56.75 8.22 78.3a71.78 71.78 0 0 0 6.16 58.9c14.5 25.16 43.56 38.1 71.91 32.04a71.76 71.76 0 0 0 53.89 24.02c29.12.02 54.92-18.72 63.84-46.44a71.79 71.79 0 0 0 47.98-34.8c14.58-25.1 11.25-56.72-8.24-78.27zm-107.9 150.77a53.15 53.15 0 0 1-34.15-12.35c.43-.24 1.2-.66 1.7-.96l56.68-32.73a9.22 9.22 0 0 0 4.66-8.06v-79.9l23.95 13.83a.85.85 0 0 1 .47.66v66.16a53.42 53.42 0 0 1-53.3 53.35zM44.6 213.16a53.13 53.13 0 0 1-6.36-35.75c.42.25 1.15.7 1.68 1l56.68 32.73a9.24 9.24 0 0 0 9.31 0l69.2-39.95v27.66a.87.87 0 0 1-.34.74l-57.29 33.07a53.42 53.42 0 0 1-72.88-19.5zM29.7 90.05a53.1 53.1 0 0 1 27.76-23.36c0 .49-.03 1.36-.03 1.96v65.46a9.22 9.22 0 0 0 4.65 8.05l69.2 39.95-23.95 13.83a.86.86 0 0 1-.81.07L49.2 162.9A53.42 53.42 0 0 1 29.7 90.05zm196.8 45.8L157.3 95.9l23.95-13.82a.86.86 0 0 1 .81-.07l57.3 33.08a53.37 53.37 0 0 1-8.24 96.29v-65.46a9.2 9.2 0 0 0-4.62-8.06zm23.84-35.89c-.42-.26-1.15-.7-1.68-1.01l-56.68-32.73a9.25 9.25 0 0 0-9.31 0l-69.2 39.95V78.5a.87.87 0 0 1 .35-.74l57.28-33.05a53.35 53.35 0 0 1 79.24 55.25zM100.11 149.24l-23.96-13.83a.85.85 0 0 1-.46-.66V68.6a53.37 53.37 0 0 1 87.52-40.95c-.42.24-1.19.66-1.7.96l-56.68 32.73a9.22 9.22 0 0 0-4.66 8.06l-.04 79.85zm13.01-28.05L144 103.3l30.88 17.83v35.68L144 174.63l-30.88-17.82v-35.62z"})})]})});const f=u==="codex"?"C":u==="antigravity"?"G":u==="cpa"||u==="cliproxyapi"?"CPA":u==="anthropic"?"A":u==="google"?"✦":u==="openrouter"?"↗":u==="groq"?"G":u==="siliconflow"?"S":u==="moonshot"?"M":"◇";return n.jsx("span",{className:`provider-icon provider-icon-${u}`,"aria-hidden":"true",children:n.jsxs("svg",{viewBox:"0 0 32 32",role:"img",children:[n.jsx("rect",{x:"1",y:"1",width:"30",height:"30",rx:"8"}),n.jsx("text",{x:"16",y:"21",textAnchor:"middle",children:f})]})})}async function Ai(u){if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(u);return}const f=document.createElement("textarea");f.value=u,f.setAttribute("readonly","true"),f.style.position="fixed",f.style.opacity="0",document.body.appendChild(f),f.select(),document.execCommand("copy"),document.body.removeChild(f)}function wl(){return window.location.origin}function rp(){return`${wl()}/api/auth/discord/callback`}function dp(){return`${wl()}/`}function Wc(u){return{...u,redirectUri:u.redirectUri&&!u.redirectUri.includes("localhost")?u.redirectUri:rp(),authSuccessUrl:u.authSuccessUrl&&!u.authSuccessUrl.includes("localhost")?u.authSuccessUrl:dp(),blockedGuildIds:je(u.blockedGuildIds),sessionTtlHours:u.sessionTtlHours||168}}function Mi(u){return u==="email"||u==="discord"?u:"username"}function fp(u){const p=(u.split("@")[0]||"user").toLowerCase().replace(/[^a-z0-9_-]+/g,"-").replace(/^[-_]+|[-_]+$/g,"");return(p.length>=3?p:"user").slice(0,24)}function mp(){const[u,f]=b.useState("home"),[p,r]=b.useState("login"),[C,T]=b.useState(null),[k,W]=b.useState("overview"),[N,y]=b.useState(()=>{const R=window.localStorage.getItem("capi-theme");return R==="light"||R==="dark"?R:window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}),[G,_]=b.useState("comfortable"),[V,P]=b.useState(null),[fe,ee]=b.useState([]),[me,ne]=b.useState([]),[se,Y]=b.useState([]),[xe,te]=b.useState([]),[ye,O]=b.useState([]),[ae,ve]=b.useState(""),[Ce,Me]=b.useState(""),[ue,Z]=b.useState(null),[de,$]=b.useState(""),[A,L]=b.useState(""),[H,X]=b.useState(!1),ie=b.useMemo(()=>{const R=ae.trim().toLowerCase();return R?fe.filter(K=>`${K.id} ${K.name} ${K.email}`.toLowerCase().includes(R)):fe},[ae,fe]);async function m(){const R=new Date().getTimezoneOffset(),[K,J,F,he,$e,Ut]=await Promise.all([pe(`/api/overview?timezoneOffset=${R}`),pe("/api/users"),pe("/api/channels"),pe("/api/models"),pe("/api/logs"),pe("/api/groups")]),Rt=je(J.users),nl=je(F.channels).map(Pt),rl=je(he.models).map(us),Ma=je($e.logs),vl=je(Ut.groups);P(K),ee(Rt),ne(nl),Y(vl),te(rl),O(Ma),Me(Hl=>Hl&&Rt.some(ca=>ca.id===Hl)?Hl:Rt[0]?.id||""),Rt.length===0&&Z(null),X(!0)}async function h(R){const K=await pe(`/api/users/${R}`);Z(W0(K))}async function M(R,K){const J=await pe(`/api/users/${R}`,{method:"PATCH",body:JSON.stringify(K)});ee(F=>F.map(he=>he.id===R?J.user:he)),Z(F=>F?.user.id===R?{...F,user:J.user}:F),$("已更新用户"),window.setTimeout(()=>$(""),1800)}async function Q(R,K,J={}){const F=await pe("/api/users/bulk",{method:"POST",body:JSON.stringify({userIds:R,action:K,...J})}),he=new Map(je(F.users).map($e=>[$e.id,$e]));ee($e=>$e.map(Ut=>he.get(Ut.id)||Ut)),Z($e=>{if(!$e)return $e;const Ut=he.get($e.user.id);return Ut?{...$e,user:Ut}:$e}),$(`已处理 ${F.updated} 个用户`),window.setTimeout(()=>$(""),1800)}async function le(R){const K=await pe(`/api/users/${R}/api-keys`,{method:"POST",body:JSON.stringify({name:"Console Key"})});ue?.user.id===R&&Z({...ue,apiKeys:[...ue.apiKeys,K.apiKey]}),L(K.secret),await Ai(K.secret),$("新 Key 已创建并复制,请立即保存"),window.setTimeout(()=>$(""),2400)}async function Ne(R){window.confirm("删除这个 Key?删除后使用它的请求会立即失效。")&&(await pe(`/api/api-keys/${R}`,{method:"DELETE"}),Z(K=>K&&{...K,apiKeys:K.apiKeys.filter(J=>J.id!==R)}),$("Key 已删除"),window.setTimeout(()=>$(""),1800))}async function z(R,K){const J=await pe(`/api/api-keys/${R}`,{method:"PATCH",body:JSON.stringify(K)});Z(F=>F&&{...F,apiKeys:F.apiKeys.map(he=>he.id===R?J.apiKey:he)}),$("Key 已更新"),window.setTimeout(()=>$(""),1800)}async function be(R,K){const J=await pe(`/api/channels/${R}`,{method:"PATCH",body:JSON.stringify(K)});ne(F=>F.map(he=>he.id===R?Pt(J.channel):he)),Je(J.removedModels),$("渠道已更新"),window.setTimeout(()=>$(""),1800)}async function ce(R){const K=me.find(F=>F.id===R);if(!window.confirm(`删除渠道「${K?.name||R}」?`))return;const J=await pe(`/api/channels/${R}`,{method:"DELETE"});ne(F=>F.filter(he=>he.id!==R)),Je(J.removedModels),$("渠道已删除"),window.setTimeout(()=>$(""),1800)}async function jt(R){const K=await pe("/api/groups",{method:"POST",body:JSON.stringify(R)});Y(J=>[...J,K.group]),$("分组已创建"),window.setTimeout(()=>$(""),1800)}async function I(R,K){const J=await pe(`/api/groups/${R}`,{method:"PATCH",body:JSON.stringify(K)});Y(F=>F.map(he=>he.id===R?J.group:he)),$("分组已更新"),window.setTimeout(()=>$(""),1800)}async function Ve(R){const K=se.find(J=>J.id===R);window.confirm(`删除分组「${K?.name||R}」?删除后渠道的可见范围会移除该分组。`)&&(await pe(`/api/groups/${R}`,{method:"DELETE"}),Y(J=>J.filter(F=>F.id!==R)),ne(J=>J.map(F=>({...F,allowedGroupIds:F.allowedGroupIds.filter(he=>he!==R)}))),$("分组已删除"),window.setTimeout(()=>$(""),1800))}async function ot(R,K){const J=je(K).map(Rt=>Rt.trim()).filter(Boolean),F=await pe(`/api/channels/${R}/sync-models`,{method:"POST",body:JSON.stringify(J.length?{models:J}:{})}),he=Pt(F.channel),$e=je(F.addedModels).map(us),Ut=je(F.models);ne(Rt=>Rt.map(nl=>nl.id===R?he:nl)),$e.length>0&&te(Rt=>{const nl=new Set(Rt.map(rl=>rl.id.toLowerCase()));return[...Rt,...$e.filter(rl=>!nl.has(rl.id.toLowerCase()))]}),Je(F.removedModels),$(J.length?`已保存 ${Ut.length} 个模型`:Ut.length?`已拉取 ${Ut.length} 个模型`:"上游没有返回模型"),window.setTimeout(()=>$(""),2200)}function Je(R){const K=new Set(je(R).map(J=>J.toLowerCase()));K.size!==0&&(te(J=>J.filter(F=>!K.has(F.id.toLowerCase()))),Z(J=>J&&{...J,apiKeys:J.apiKeys.map(F=>({...F,allowedModels:F.allowedModels.filter(he=>!K.has(he.toLowerCase()))}))}))}async function gt(R){try{const K=await pe(`/api/channels/${R}/check`,{method:"POST",body:JSON.stringify({})});ne(J=>J.map(F=>F.id===R?Pt(K.channel):F)),$(K.ok?`渠道可用,检测到 ${je(K.models).length} 个模型`:"渠道检测失败")}catch(K){const J=K instanceof Ci?K.payload?.channel:null;J&&ne(F=>F.map(he=>he.id===R?Pt(J):he)),$(K instanceof Error?K.message:"渠道检测失败")}window.setTimeout(()=>$(""),2400)}async function Dt(R=Ic("codex")){const K=await pe("/api/channels",{method:"POST",body:JSON.stringify(R)});ne(J=>[...J,Pt(K.channel)]),$("渠道已创建,可继续拉取模型或检测渠道"),window.setTimeout(()=>$(""),2400)}async function ll(R,K){const J=new FormData;J.append("file",K);const F=await I0(`/api/channels/${encodeURIComponent(R)}/import-openai-accounts`,J);ne(he=>he.map($e=>$e.id===F.channel.id?Pt(F.channel):$e)),$(`新增 ${F.created??F.imported} 个账号${F.updated?`,更新 ${F.updated} 个已有账号`:""}${F.skipped?`,跳过 ${F.skipped} 个`:""}`),window.setTimeout(()=>$(""),2600)}async function al(R,K=!1,J=""){const F=await pe(`/api/channels/${encodeURIComponent(R)}/openai-accounts/check`,{method:"POST",body:JSON.stringify({onlyInvalid:K,accountId:J})});ne(he=>he.map($e=>$e.id===F.channel.id?Pt(F.channel):$e)),$(`${K?"无效账号复检":"账号测活"}完成:${F.healthy}/${F.checked} 可用${F.failed?`,无效 ${F.failed}`:""}`),window.setTimeout(()=>$(""),3e3)}async function U(R){const K=await pe(`/api/channels/${encodeURIComponent(R)}/openai-accounts/deduplicate`,{method:"POST",body:JSON.stringify({})});ne(J=>J.map(F=>F.id===K.channel.id?Pt(K.channel):F)),$(K.removed?`已合并 ${K.removed} 个重复账号`:"未发现可识别的重复账号"),window.setTimeout(()=>$(""),2600)}async function B(R,K){const J=await pe(`/api/channels/${encodeURIComponent(R)}/openai-accounts/${encodeURIComponent(K)}`,{method:"DELETE"});ne(F=>F.map(he=>he.id===J.channel.id?Pt(J.channel):he)),$("账号已删除"),window.setTimeout(()=>$(""),1800)}async function Be(R){return pe(`/api/channels/${encodeURIComponent(R)}/openai-oauth/start`,{method:"POST",body:JSON.stringify({})})}async function We(R,K){const J=await pe(`/api/channels/${encodeURIComponent(R)}/openai-oauth/complete`,{method:"POST",body:JSON.stringify(K)});return ne(F=>F.map(he=>he.id===J.channel.id?Pt(J.channel):he)),$("已通过 OAuth 添加账号"),window.setTimeout(()=>$(""),2400),J}async function at(R){return pe(`/api/channels/${encodeURIComponent(R)}/antigravity-oauth/start`,{method:"POST",body:JSON.stringify({})})}async function Pe(R,K){const J=await pe(`/api/channels/${encodeURIComponent(R)}/antigravity-oauth/complete`,{method:"POST",body:JSON.stringify(K)});return ne(F=>F.map(he=>he.id===J.channel.id?Pt(J.channel):he)),$("已通过 Google 授权添加账号"),window.setTimeout(()=>$(""),2400),J}async function zi(R){const K=await pe("/api/models",{method:"POST",body:JSON.stringify(R)});te(J=>[...J,us(K.model)]),$("模型已添加"),window.setTimeout(()=>$(""),1800)}async function cs(R,K){const J=await pe(`/api/models/${encodeURIComponent(R)}`,{method:"PATCH",body:JSON.stringify(K)});te(F=>F.map(he=>he.id===R?us(J.model):he)),$("模型已更新"),window.setTimeout(()=>$(""),1600)}async function os(R){window.confirm(`删除模型 ${R}?渠道和 Key 中的引用也会一起清理。`)&&(await pe(`/api/models/${encodeURIComponent(R)}`,{method:"DELETE"}),te(K=>K.filter(J=>J.id!==R)),ne(K=>K.map(J=>({...J,models:J.models.filter(F=>F!==R)}))),Z(K=>K&&{...K,apiKeys:K.apiKeys.map(J=>({...J,allowedModels:J.allowedModels.filter(F=>F!==R)}))}),$("模型已删除"),window.setTimeout(()=>$(""),1800))}async function ua(R,K="已复制"){await Ai(R),$(K),window.setTimeout(()=>$(""),1600)}function Ea(R,K){if(R instanceof Ci&&R.status===401){X(!1),r("login"),f("auth");return}$(K)}async function hn(){try{const R=await pe("/api/auth/status");if(T(R),!R.initialized){r("setup"),f("auth");return}if(!R.authenticated){r("login"),f("auth");return}f(R.session?.role==="admin"?"console":"account")}catch(R){Ea(R,"认证状态加载失败")}}async function Oi(){await pe("/api/auth/logout",{method:"POST"}),window.sessionStorage.removeItem("capi-admin-token"),T(null),f("home")}return b.useEffect(()=>{let R=!1;return pe("/api/auth/status").then(K=>{R||(T(K),K.authenticated&&K.session&&f(K.session.role==="admin"?"console":"account"))}).catch(()=>{}),()=>{R=!0}},[]),b.useEffect(()=>{u==="console"&&(X(!1),m().catch(R=>Ea(R,"加载数据失败")))},[u]),b.useEffect(()=>{u!=="console"||!H||h(Ce).catch(R=>Ea(R,"加载用户详情失败"))},[Ce,u,H]),b.useEffect(()=>{window.localStorage.setItem("capi-theme",N)},[N]),b.useEffect(()=>{window.scrollTo({top:0,left:0})},[u,k]),u==="home"?n.jsx(xp,{theme:N,setTheme:y,enterConsole:hn}):u==="auth"?n.jsx(hp,{theme:N,mode:p,status:C,setTheme:y,setMode:r,goHome:()=>f("home"),onAuthenticated:R=>{T(K=>K?{...K,authenticated:!0,initialized:!0,session:R}:null),f(R.role==="admin"?"console":"account")}}):u==="account"?n.jsx(bp,{theme:N,setTheme:y,goHome:()=>f("home"),openLogin:hn}):n.jsxs("div",{className:"app-shell","data-theme":N,"data-density":G,children:[n.jsxs("aside",{className:"sidebar",children:[n.jsxs("div",{className:"ios-window-dots","aria-hidden":"true",children:[n.jsx("span",{}),n.jsx("span",{}),n.jsx("span",{})]}),n.jsxs("div",{className:"brand",children:[n.jsx("div",{className:"brand-mark",children:"C"}),n.jsxs("div",{children:[n.jsx("strong",{children:"CAPI"}),n.jsx("span",{children:"聚合网关"})]})]}),n.jsx("nav",{children:dm.map(R=>n.jsxs("button",{className:k===R.id?"nav-item active":"nav-item",onClick:()=>W(R.id),children:[n.jsx(zt,{name:R.icon}),n.jsx("span",{className:"nav-label",children:R.label})]},R.id))}),n.jsxs("div",{className:"sidebar-footer",children:[n.jsx("span",{children:"Gateway"}),n.jsxs("strong",{children:[n.jsx("span",{className:"pulse-dot"}),"Online"]})]})]}),n.jsxs("main",{className:"content",children:[n.jsxs("header",{className:"topbar",children:[n.jsxs("div",{children:[n.jsx("p",{className:"eyebrow",children:"Admin Console"}),n.jsx("h1",{children:dm.find(R=>R.id===k)?.label})]}),n.jsxs("div",{className:"topbar-actions",children:[n.jsx(Vp,{value:G,options:[{value:"comfortable",label:"舒适"},{value:"compact",label:"紧凑"}],onChange:R=>_(R)}),n.jsxs("button",{className:"theme-toggle","aria-label":"切换暗色模式",onClick:()=>y(N==="dark"?"light":"dark"),children:[n.jsx(zt,{name:N==="dark"?"sun":"moon"}),n.jsx("span",{children:N==="dark"?"浅色":"暗色"})]}),n.jsx("button",{className:"primary-button",onClick:()=>m().catch(R=>Ea(R,"刷新失败")),children:"刷新"}),n.jsx("button",{className:"secondary-button home-link",onClick:()=>f("home"),children:"首页"}),n.jsx("button",{className:"secondary-button",onClick:Oi,children:"退出"})]})]}),k==="overview"&&n.jsx(jp,{overview:V,channels:me,logs:ye,onNavigate:R=>{W(R),R==="channels"&&me.length===0&&$("渠道页可以创建第一个上游"),R==="logs"&&ye.length===0&&$("暂无异常日志"),window.setTimeout(()=>$(""),1800)}}),k==="users"&&n.jsx(Np,{users:ie,query:ae,selectedUser:ue,onQuery:ve,onSelect:Me,onUpdate:M,onBulkUpdate:Q,onCreateKey:le,groups:se,onOpenRegistration:()=>{W("settings"),$("在账号与注册里开放注册,用户即可自助创建账号"),window.setTimeout(()=>$(""),2400)}}),k==="groups"&&n.jsx(Kp,{groups:se,onCreate:jt,onUpdate:I,onDelete:Ve}),k==="keys"&&n.jsx(Ap,{selectedUser:ue,onCreateKey:le,onUpdateKey:z,onDeleteKey:Ne}),k==="models"&&n.jsx(Tp,{models:xe,onCopy:ua,onCreate:zi,onUpdate:cs,onDelete:os}),k==="drawing"&&n.jsx(vm,{variant:"drawing",channels:me,onCreate:Dt,onImport:ll,onCheckAccounts:al,onDeduplicateAccounts:U,onDeleteAccount:B,onUpdate:be,onStartOAuth:Be,onCompleteOAuth:We}),k==="antigravity"&&n.jsx(vm,{variant:"antigravity",channels:me,onCreate:Dt,onImport:ll,onCheckAccounts:al,onDeduplicateAccounts:U,onDeleteAccount:B,onUpdate:be,onStartOAuth:at,onCompleteOAuth:Pe}),k==="channels"&&n.jsx(kp,{channels:me,groups:se,onUpdate:be,onCreate:Dt,onImport:ll,onDelete:ce,onSyncModels:ot,onCheck:gt}),k==="logs"&&n.jsx(Lp,{logs:ye,onCopy:ua}),k==="settings"&&n.jsx(Xp,{models:xe,channels:me,groups:se})]}),A&&n.jsx("div",{className:"secret-dialog-backdrop",role:"presentation",children:n.jsxs("section",{className:"secret-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"secret-dialog-title",children:[n.jsxs("div",{children:[n.jsx("p",{className:"eyebrow",children:"One-time secret"}),n.jsx("h2",{id:"secret-dialog-title",children:"完整 API Key"})]}),n.jsx("p",{children:"完整密钥只显示这一次。列表中的星号内容只是识别前缀,不能用于 API 调用。"}),n.jsx("code",{children:A}),n.jsxs("div",{className:"secret-dialog-actions",children:[n.jsx("button",{className:"secondary-button",onClick:()=>{Ai(A),$("完整 Key 已复制"),window.setTimeout(()=>$(""),1800)},children:"复制"}),n.jsx("button",{className:"primary-button",onClick:()=>L(""),children:"完成"})]})]})}),de&&n.jsx("div",{className:"toast",children:de})]})}function hp({theme:u,mode:f,status:p,setTheme:r,setMode:C,goHome:T,onAuthenticated:k}){const[W,N]=b.useState(""),[y,G]=b.useState(""),[_,V]=b.useState(""),[P,fe]=b.useState(""),[ee,me]=b.useState(""),[ne,se]=b.useState(""),[Y,xe]=b.useState(!1),[te,ye]=b.useState("username"),[O,ae]=b.useState("0"),[ve,Ce]=b.useState(""),[Me,ue]=b.useState(!1),Z=f==="setup",de=f==="register",$=Mi(p?.registrationMode),A=de&&$==="email",L=de&&$==="discord";async function H(){if(!L){if((Z||de)&&ee!==ne){Ce("两次输入的密码不一致");return}ue(!0),Ce("");try{const X=Z?"/api/auth/setup":de?"/api/auth/register":"/api/auth/login",ie=A?fp(_):W,m=f==="login"?{identifier:W,password:ee}:{username:ie,password:ee,displayName:y,email:_,discordUserId:Z?P:"",registrationEnabled:Z?Y:void 0,registrationMode:Z?te:void 0,defaultBalance:Z?Number(O||0):void 0},h=await pe(X,{method:"POST",body:JSON.stringify(m)});k(h.session)}catch(X){Ce(X instanceof Error?X.message:"操作失败")}finally{ue(!1)}}}return n.jsxs("main",{className:"auth-page","data-theme":u,children:[n.jsxs("header",{className:"auth-topbar",children:[n.jsxs("button",{className:"auth-brand",onClick:T,children:[n.jsx("span",{className:"brand-mark",children:"C"}),n.jsx("strong",{children:"CAPI"})]}),n.jsxs("button",{className:"theme-toggle","aria-label":"切换暗色模式",onClick:()=>r(u==="dark"?"light":"dark"),children:[n.jsx(zt,{name:u==="dark"?"sun":"moon"}),n.jsx("span",{children:u==="dark"?"浅色":"暗色"})]})]}),n.jsxs("section",{className:"auth-stage",children:[n.jsxs("div",{className:"auth-intro",children:[n.jsx("span",{children:Z?"First Run":"Welcome Back"}),n.jsx("h1",{children:Z?"初始化 CAPI":de?"创建账号":"登录"}),n.jsx("p",{children:Z?"创建第一个管理员账号,完成后即可进入控制台。":"使用你的 CAPI 账号继续。"})]}),n.jsxs("form",{className:"auth-form",onSubmit:X=>{X.preventDefault(),H()},children:[L?n.jsxs("div",{className:"auth-discord-register",children:[n.jsx("strong",{children:"使用 Discord 创建账号"}),n.jsx("span",{children:"继续后会按站点设置校验服务器和身份组。"}),p?.discordEnabled?n.jsx("a",{className:"discord-login-button",href:"/api/auth/discord/start",children:"继续使用 Discord"}):n.jsx("div",{className:"auth-message",children:"管理员还没有启用 Discord 登录"})]}):n.jsxs(n.Fragment,{children:[f!=="login"&&n.jsxs("label",{children:[n.jsx("span",{children:"显示名称"}),n.jsx("input",{value:y,onChange:X=>G(X.target.value),autoComplete:"name",placeholder:"CAPI"})]}),!A&&n.jsxs("label",{children:[n.jsx("span",{children:f==="login"?"账号或邮箱":"账号"}),n.jsx("input",{value:W,onChange:X=>N(X.target.value),autoComplete:"username",placeholder:f==="login"?"输入账号或邮箱":"3-32 位字母、数字、_ 或 -"})]}),(Z||A)&&n.jsxs("label",{children:[n.jsx("span",{children:A?"邮箱":"邮箱(可选)"}),n.jsx("input",{type:"email",value:_,onChange:X=>V(X.target.value),autoComplete:"email",placeholder:"name@example.com"})]}),Z&&n.jsxs(n.Fragment,{children:[n.jsxs("label",{children:[n.jsx("span",{children:"Discord 用户 ID(可选)"}),n.jsx("input",{inputMode:"numeric",autoComplete:"off",value:P,onChange:X=>fe(X.target.value),placeholder:"绑定管理员 Discord 账号"})]}),n.jsxs("div",{className:"setup-options",children:[n.jsxs("div",{className:"setting",children:[n.jsx("span",{children:"开放注册"}),n.jsx("button",{type:"button",className:Y?"ios-switch is-on":"ios-switch","aria-pressed":Y,onClick:()=>xe(X=>!X),children:n.jsx("span",{})})]}),n.jsxs("label",{children:[n.jsx("span",{children:"注册方式"}),n.jsxs("select",{value:te,onChange:X=>ye(Mi(X.target.value)),children:[n.jsx("option",{value:"username",children:"账号密码"}),n.jsx("option",{value:"email",children:"邮箱"}),n.jsx("option",{value:"discord",children:"Discord"})]})]}),n.jsxs("label",{children:[n.jsx("span",{children:"新用户初始额度"}),n.jsx("input",{type:"number",min:"0",step:"0.01",value:O,onChange:X=>ae(X.target.value)})]})]})]}),n.jsxs("label",{children:[n.jsx("span",{children:"密码"}),n.jsx("input",{type:"password",value:ee,onChange:X=>me(X.target.value),autoComplete:f==="login"?"current-password":"new-password",placeholder:"至少 8 个字符"})]}),f!=="login"&&n.jsxs("label",{children:[n.jsx("span",{children:"确认密码"}),n.jsx("input",{type:"password",value:ne,onChange:X=>se(X.target.value),autoComplete:"new-password",placeholder:"再次输入密码"})]}),n.jsx("div",{className:"auth-message",role:"status",children:ve}),n.jsx("button",{className:"primary-button auth-submit",type:"submit",disabled:Me,children:Me?"请稍候":Z?"创建管理员":de?"注册":"登录"})]}),!Z&&!L&&p?.discordEnabled&&n.jsx("a",{className:"discord-login-button",href:"/api/auth/discord/start",children:"使用 Discord 登录"}),!Z&&n.jsx("div",{className:"auth-switch",children:f==="login"&&p?.registrationEnabled?n.jsx("button",{type:"button",onClick:()=>C("register"),children:"创建账号"}):n.jsx("button",{type:"button",onClick:()=>C("login"),children:"返回登录"})})]})]})]})}const pp=[1,1.5,2,2.5,3,4,5,6,8,10];function vp(u){if(!(u>0))return 1;const f=Math.floor(Math.log10(u)),p=Math.pow(10,f),r=u/p;return(pp.find(T=>r<=T)??10)*p}const pm=88;function yp(u){const f=u.split("-");return f.length===3?`${f[1]}/${f[2]}`:u}function gp(){const[u,f]=b.useState(14),[p,r]=b.useState(null),[C,T]=b.useState(!0),[k,W]=b.useState(!1),[N,y]=b.useState("");b.useEffect(()=>{let Y=!1;return T(!0),pe(`/api/account/usage?days=${u}&timezoneOffset=${new Date().getTimezoneOffset()}`).then(xe=>{Y||(r({...xe.usage,daily:je(xe.usage?.daily),models:je(xe.usage?.models)}),W(!1))}).catch(()=>{Y||W(!0)}).finally(()=>{Y||T(!1)}),()=>{Y=!0}},[u]);const G=p?.daily??[],_=p?.models??[],V=vp(Math.max(0,...G.map(Y=>Y.cost))),P=Math.max(0,..._.map(Y=>Y.cost)),fe=G.reduce((Y,xe)=>!Y||xe.cost>Y.cost?xe:Y,null),ee=Math.max(1,Math.ceil(G.length/5)),me=G.find(Y=>Y.day===N)||null,ne=me?G.indexOf(me):-1,se=p?p.today.cost-p.yesterday.cost:0;return n.jsxs("section",{className:"account-section usage-section",children:[n.jsx("div",{className:"account-section-title",children:n.jsxs("div",{children:[n.jsx("p",{className:"eyebrow",children:"Usage"}),n.jsx("h2",{children:"用量概览"})]})}),!p&&C&&n.jsx("p",{className:"usage-placeholder",children:"正在统计用量…"}),!p&&!C&&k&&n.jsx("p",{className:"usage-placeholder",children:"用量加载失败,请稍后重试"}),p&&n.jsxs("div",{className:C?"usage-body is-refreshing":"usage-body",children:[n.jsxs("div",{className:"usage-kpi",children:[n.jsxs("div",{className:"usage-tile",children:[n.jsx("span",{children:"今日消费"}),n.jsx("strong",{children:xt(p.today.cost)}),n.jsxs("small",{children:["较昨日 ",se>=0?"+":"−",xt(Math.abs(se))]})]}),n.jsxs("div",{className:"usage-tile",children:[n.jsx("span",{children:"本月消费"}),n.jsx("strong",{children:xt(p.month.cost)}),n.jsxs("small",{children:[pl(p.month.requests)," 次请求"]})]}),n.jsxs("div",{className:"usage-tile",children:[n.jsx("span",{children:"今日请求"}),n.jsx("strong",{children:pl(p.today.requests)}),n.jsxs("small",{children:["成功率 ",p.today.successRate,"%"]})]}),n.jsxs("div",{className:"usage-tile",children:[n.jsx("span",{children:"累计消费"}),n.jsx("strong",{children:xt(p.total.cost)}),n.jsxs("small",{children:[pl(p.total.requests)," 次请求"]})]})]}),n.jsx("div",{className:"usage-range",role:"group","aria-label":"统计范围",children:[7,14,30].map(Y=>n.jsxs("button",{type:"button",className:u===Y?"selected":"","aria-pressed":u===Y,onClick:()=>f(Y),children:["近 ",Y," 天"]},Y))}),n.jsxs("div",{className:"usage-block",children:[n.jsxs("div",{className:"usage-block-head",children:[n.jsx("strong",{children:"每日消费"}),n.jsxs("span",{children:["近 ",p.rangeDays," 天"]})]}),G.length===0||V<=0?n.jsx("p",{className:"usage-placeholder",children:"这段时间还没有消费记录"}):n.jsxs(n.Fragment,{children:[n.jsxs("div",{className:"usage-plot",children:[n.jsx("div",{className:"usage-grid","aria-hidden":"true",children:[1,.5,0].map(Y=>n.jsx("div",{className:"usage-gridline",style:{bottom:`${Y*pm}%`},children:n.jsx("span",{children:xt(V*Y)})},Y))}),n.jsxs("div",{className:"usage-columns",children:[G.map((Y,xe)=>{const te=V>0?Y.cost/V*pm:0,ye=Y.cost>0,O=ye&&fe!==null&&Y.day===fe.day,ae=xe%ee===0||xe===G.length-1;return n.jsxs("div",{className:"usage-column",children:[n.jsxs("button",{type:"button",className:N===Y.day?"usage-column-hit is-hovered":"usage-column-hit",onMouseEnter:()=>y(Y.day),onMouseLeave:()=>y(""),onFocus:()=>y(Y.day),onBlur:()=>y(""),"aria-label":`${Y.day} 消费 ${xt(Y.cost)},${Y.requests} 次请求`,children:[ye&&n.jsx("span",{className:"usage-column-bar",style:{height:`${te}%`}}),O&&n.jsx("span",{className:"usage-column-peak",style:{bottom:`calc(${te}% + 6px)`},children:xt(Y.cost)})]}),n.jsx("span",{className:"usage-column-tick",children:ae?yp(Y.day):""})]},Y.day)}),me&&n.jsxs("div",{className:"usage-tooltip",style:{left:`${(ne+.5)/G.length*100}%`},role:"status",children:[n.jsx("strong",{children:xt(me.cost)}),n.jsx("span",{children:me.day}),n.jsxs("span",{children:[me.requests," 次请求"]})]})]})]}),n.jsxs("details",{className:"usage-table-toggle",children:[n.jsx("summary",{children:"查看数据表"}),n.jsxs("div",{className:"usage-table",children:[n.jsxs("div",{className:"usage-table-head",children:[n.jsx("span",{children:"日期"}),n.jsx("span",{children:"请求"}),n.jsx("span",{children:"消费"})]}),G.slice().reverse().map(Y=>n.jsxs("div",{className:"usage-table-row",children:[n.jsx("span",{children:Y.day}),n.jsx("span",{children:Y.requests}),n.jsx("span",{children:xt(Y.cost)})]},Y.day))]})]})]})]}),n.jsxs("div",{className:"usage-block",children:[n.jsxs("div",{className:"usage-block-head",children:[n.jsx("strong",{children:"按模型拆分"}),n.jsxs("span",{children:["近 ",p.rangeDays," 天消费"]})]}),_.length===0?n.jsx("p",{className:"usage-placeholder",children:"这段时间还没有调用记录"}):n.jsx("div",{className:"usage-models",children:_.map(Y=>n.jsxs("div",{className:"usage-model-row",children:[n.jsx("span",{className:"usage-model-name",title:Y.model,children:Y.model}),n.jsx("span",{className:"usage-model-track",children:n.jsx("span",{className:"usage-model-bar",style:{width:`${P>0?Y.cost/P*100:0}%`}})}),n.jsx("span",{className:"usage-model-value",children:xt(Y.cost)})]},Y.model))})]})]})]})}function bp({theme:u,setTheme:f,goHome:p,openLogin:r}){const[C,T]=b.useState(null),[k,W]=b.useState([]),[N,y]=b.useState(null),[G,_]=b.useState(!1),[V,P]=b.useState(""),[fe,ee]=b.useState(""),[me,ne]=b.useState("");async function se(){try{const[O,ae,ve]=await Promise.all([pe("/api/account/me"),pe("/api/catalog/models"),pe("/api/account/check-in")]);T({...O,apiKeys:je(O.apiKeys)}),W(je(ae.models).map(us)),y(ve.checkIn)}catch{r()}}b.useEffect(()=>{se()},[]);async function Y(){if(!(!N?.enabled||N.claimed||G)){_(!0),P("");try{const O=await pe("/api/account/check-in",{method:"POST",body:JSON.stringify({})});T(ae=>ae&&{...ae,user:O.user}),y(O.checkIn),P(`签到成功,获得 ${O.reward.toFixed(2)} 额度`)}catch(O){P(O instanceof Error?O.message:"签到失败,请稍后重试")}finally{_(!1)}}}async function xe(){try{const O=await pe("/api/account/api-keys",{method:"POST",body:JSON.stringify({name:"My API Key"})});ee(O.secret),ne("新密钥只显示这一次"),await se()}catch(O){ne(O instanceof Error?O.message:"创建密钥失败")}}async function te(O){if(window.confirm("删除这个 API Key?使用它的请求会立即失效。"))try{await pe(`/api/account/api-keys/${O}`,{method:"DELETE"}),T(ae=>ae&&{...ae,apiKeys:ae.apiKeys.filter(ve=>ve.id!==O)}),ne("密钥已删除")}catch(ae){ne(ae instanceof Error?ae.message:"删除密钥失败")}}async function ye(){await pe("/api/auth/logout",{method:"POST"}),p()}return n.jsxs("main",{className:"account-page","data-theme":u,children:[n.jsxs("header",{className:"account-topbar",children:[n.jsxs("button",{className:"auth-brand",onClick:p,children:[n.jsx("span",{className:"brand-mark",children:"C"}),n.jsx("strong",{children:"CAPI"})]}),n.jsxs("div",{className:"account-actions",children:[n.jsx("button",{className:"theme-toggle","aria-label":"切换暗色模式",onClick:()=>f(u==="dark"?"light":"dark"),children:n.jsx(zt,{name:u==="dark"?"sun":"moon"})}),n.jsx("button",{className:"secondary-button",onClick:ye,children:"退出"})]})]}),n.jsxs("section",{className:"account-content",children:[n.jsxs("div",{className:"account-heading",children:[n.jsxs("div",{children:[n.jsx("p",{className:"eyebrow",children:"My CAPI"}),n.jsx("h1",{children:C?.user.name||"账户"})]}),n.jsxs("div",{className:"account-balance",children:[n.jsx("span",{children:"余额"}),n.jsx("strong",{children:C?C.user.balance.toFixed(2):"-"})]})]}),n.jsx(gp,{}),n.jsxs("section",{className:"account-section check-in-section",children:[n.jsxs("div",{className:"account-section-title",children:[n.jsxs("div",{children:[n.jsx("p",{className:"eyebrow",children:"Daily Reward"}),n.jsx("h2",{children:"每日签到"})]}),n.jsx("button",{className:"primary-button",disabled:!N?.enabled||!!N?.claimed||G,onClick:Y,children:G?"领取中":N?.claimed?"今日已签到":N?.enabled?"签到领额度":"暂未开放"})]}),n.jsxs("div",{className:"check-in-summary",children:[n.jsxs("div",{children:[n.jsx("span",{children:"今日状态"}),n.jsx("strong",{children:N?.claimed?`已领取 ${N.reward.toFixed(2)}`:N?.enabled?"等待签到":"活动关闭"})]}),n.jsxs("div",{children:[n.jsx("span",{children:"随机奖励"}),n.jsx("strong",{children:N?`${N.minReward.toFixed(2)} - ${N.maxReward.toFixed(2)}`:"-"})]}),n.jsxs("div",{children:[n.jsx("span",{children:"结算日期"}),n.jsx("strong",{children:N?.day||"北京时间"})]})]}),n.jsx("p",{className:"check-in-note",children:"每天按北京时间 00:00 刷新,奖励领取后直接计入账户余额。"}),V&&n.jsx("p",{className:"account-message check-in-message",role:"status",children:V})]}),n.jsxs("section",{className:"account-section",children:[n.jsxs("div",{className:"account-section-title",children:[n.jsxs("div",{children:[n.jsx("p",{className:"eyebrow",children:"API Keys"}),n.jsx("h2",{children:"API 密钥"})]}),n.jsx("button",{className:"primary-button",onClick:xe,children:"创建密钥"})]}),fe&&n.jsx("code",{className:"one-time-secret",children:fe}),me&&n.jsx("p",{className:"account-message",children:me}),n.jsxs("div",{className:"account-key-list",children:[C?.apiKeys?.map(O=>n.jsxs("div",{className:"account-key-item",children:[n.jsx("span",{className:"account-key-mark","aria-hidden":"true",children:n.jsx(zt,{name:"key"})}),n.jsxs("div",{className:"account-key-info",children:[n.jsx("strong",{children:O.name}),n.jsxs("code",{children:[O.prefix,"…"]})]}),n.jsx(_t,{tone:O.status,children:Ot(O.status)}),n.jsx("button",{className:"icon-button","aria-label":`删除 ${O.name}`,title:"删除密钥",onClick:()=>te(O.id),children:n.jsx(zt,{name:"ban"})})]},O.id)),je(C?.apiKeys).length===0&&n.jsx("div",{className:"empty",children:"还没有 API 密钥"})]})]}),n.jsxs("section",{className:"account-section",children:[n.jsx("div",{className:"account-section-title",children:n.jsxs("div",{children:[n.jsx("p",{className:"eyebrow",children:"Models"}),n.jsx("h2",{children:"可用模型"})]})}),n.jsxs("div",{className:"account-model-grid",children:[k.map(O=>n.jsxs("article",{children:[n.jsx("span",{children:O.vendor}),n.jsx("strong",{children:O.name}),n.jsx("p",{children:O.description}),n.jsx("code",{children:O.id})]},O.id)),k.length===0&&n.jsx("div",{className:"account-model-empty",children:"暂无可用模型,管理员配置渠道后将在此展示"})]})]})]})]})}function xp({theme:u,setTheme:f,enterConsole:p}){return n.jsxs("main",{className:"public-home","data-theme":u,children:[n.jsxs("header",{className:"home-topbar",children:[n.jsxs("div",{className:"home-brand",children:[n.jsx("div",{className:"brand-mark",children:"C"}),n.jsxs("div",{children:[n.jsx("strong",{children:"CAPI"}),n.jsx("span",{children:"AI 聚合网关"})]})]}),n.jsxs("div",{className:"home-actions",children:[n.jsxs("button",{className:"theme-toggle","aria-label":"切换暗色模式",onClick:()=>f(u==="dark"?"light":"dark"),children:[n.jsx(zt,{name:u==="dark"?"sun":"moon"}),n.jsx("span",{children:u==="dark"?"浅色":"暗色"})]}),n.jsx("button",{className:"primary-button",onClick:p,children:"控制台"})]})]}),n.jsxs("section",{className:"home-hero",children:[n.jsxs("div",{className:"home-copy",children:[n.jsx("span",{className:"home-kicker",children:"兼容 OpenAI 格式的网关"}),n.jsxs("h1",{children:["CAPI",n.jsx("span",{children:"轻量 AI 聚合网关"})]}),n.jsx("p",{children:"面向个人用户和团队的模型接入层。把 API Key、额度、模型渠道和调用日志放在一个清爽控制台里,保持轻量,也便于排障。"}),n.jsx("div",{className:"home-cta",children:n.jsx("button",{className:"primary-button",onClick:p,children:"进入控制台"})}),n.jsxs("div",{className:"integration-row","aria-label":"网关能力概览",children:[n.jsx("span",{children:"网关能力"}),n.jsxs("div",{children:[n.jsx("span",{children:"OpenAI 兼容接口"}),n.jsx("span",{children:"额度控制"}),n.jsx("span",{children:"调用审计"})]})]})]}),n.jsxs("div",{className:"gateway-terminal","aria-label":"CAPI 终端请求示意",children:[n.jsxs("div",{className:"terminal-titlebar",children:[n.jsxs("div",{className:"terminal-dots","aria-hidden":"true",children:[n.jsx("span",{}),n.jsx("span",{}),n.jsx("span",{})]}),n.jsx("strong",{children:"CAPI Terminal"}),n.jsxs("div",{className:"terminal-status",children:[n.jsx("span",{className:"pulse-dot"}),n.jsx("strong",{children:"Online"})]})]}),n.jsxs("div",{className:"terminal-endpoint",children:[n.jsx("span",{children:"POST"}),n.jsx("strong",{children:"/v1/chat/completions"})]}),n.jsxs("div",{className:"terminal-body",children:[n.jsxs("div",{className:"terminal-block",children:[n.jsx("span",{children:"REQUEST"}),n.jsx("pre",{children:`curl https://api.capi.local/v1/chat/completions \\ - -H "Authorization: Bearer cat_..." \\ - -d '{ - "model": "capi-fast", - "messages": [{ "role": "user", "content": "ping" }] - }'`})]}),n.jsxs("div",{className:"terminal-route",children:[n.jsxs("div",{children:[n.jsx("span",{children:"auth"}),n.jsx("strong",{children:"pass"})]}),n.jsxs("div",{children:[n.jsx("span",{children:"quota"}),n.jsx("strong",{children:"ok"})]}),n.jsxs("div",{children:[n.jsx("span",{children:"route"}),n.jsx("strong",{children:"capi-fast"})]}),n.jsxs("div",{children:[n.jsx("span",{children:"latency"}),n.jsx("strong",{children:"186ms"})]})]}),n.jsxs("div",{className:"terminal-block response",children:[n.jsx("span",{children:"RESPONSE"}),n.jsxs("pre",{children:[`{ - "status": 200, - "model": "capi-fast", - "usage": { "total_tokens": 27 }, - "message": "request routed" -}`,n.jsx("span",{className:"terminal-caret","aria-hidden":"true"})]})]})]})]})]})]})}function jp({overview:u,channels:f,logs:p,onNavigate:r}){const C=p.filter(T=>T.status!=="success");return n.jsxs("section",{className:"page-stack",children:[n.jsxs("div",{className:"hero-strip",children:[n.jsxs("div",{children:[n.jsx("span",{children:"Live Gateway"}),n.jsxs("strong",{children:["CAPI 网关正在服务 ",u?.activeUsers??"-"," 个活跃用户"]}),n.jsx("p",{children:"请求进入 CAPI 后,会按额度、模型和渠道状态自动选择最合适的上游。"})]}),n.jsxs("div",{className:"live-island",children:[n.jsx("div",{className:"pulse-dot"}),n.jsx("span",{children:"在线"})]})]}),n.jsxs("div",{className:"quick-actions","aria-label":"快捷操作",children:[n.jsx(Si,{icon:"key",label:"创建 Key",onClick:()=>r("keys")}),n.jsx(Si,{icon:"route",label:"配置渠道",onClick:()=>r("channels")}),n.jsx(Si,{icon:"users",label:"调整额度",onClick:()=>r("users")}),n.jsx(Si,{icon:"logs",label:"查看异常",onClick:()=>r("logs")})]}),n.jsxs("div",{className:"metrics-grid",children:[n.jsx(ol,{label:"活跃用户",value:u?pl(u.activeUsers):"-"}),n.jsx(ol,{label:"今日请求",value:u?pl(u.requestsToday):"-"}),n.jsx(ol,{label:"今日输入",value:u?pl(u.todayInputTokens):"-"}),n.jsx(ol,{label:"今日输出",value:u?pl(u.todayOutputTokens):"-"}),n.jsx(ol,{label:"今日扣费",value:u?xt(u.todayCost,4):"-"}),n.jsx(ol,{label:"账户余额",value:u?xt(u.totalBalance):"-"}),n.jsx(ol,{label:"成功率",value:u?`${u.successRate}%`:"-"})]}),n.jsx(Sp,{}),n.jsxs("div",{className:"split-grid",children:[n.jsx(lt,{title:"渠道状态",children:f.length?f.map(T=>n.jsxs("div",{className:"list-row overview-channel-row",children:[n.jsxs("div",{children:[n.jsx("strong",{children:T.name}),n.jsx("span",{title:je(T.models).join(", "),children:op(T.models)})]}),n.jsx(_t,{tone:T.status,children:Ot(T.status)})]},T.id)):n.jsx(Lt,{text:"暂无渠道"})}),n.jsx(lt,{title:"最近请求",children:p.length?p.slice(0,4).map(T=>n.jsxs("div",{className:"list-row",children:[n.jsxs("div",{children:[n.jsx("strong",{children:lo(T)}),n.jsxs("span",{children:[T.id," · ",T.errorCode||tl(T.createdAt)]})]}),n.jsx(_t,{tone:T.status,children:Ot(T.status)})]},T.id)):n.jsx(Lt,{text:C.length?"暂无最近请求":"暂无请求"})})]})]})}function Si({icon:u,label:f,onClick:p}){return n.jsxs("button",{className:"quick-action",onClick:p,children:[n.jsx(zt,{name:u}),n.jsx("span",{children:f})]})}function Sp(){const u=[{label:"认证",detail:"校验 API Key"},{label:"额度",detail:"检查余额"},{label:"路由",detail:"选择渠道"},{label:"响应",detail:"返回结果"}];return n.jsxs("section",{className:"flow-panel","aria-label":"网关流转",children:[n.jsxs("div",{className:"flow-copy",children:[n.jsx("span",{children:"Request Flow"}),n.jsx("strong",{children:"请求处理流程"})]}),n.jsx("div",{className:"flow-steps",children:u.map((f,p)=>n.jsxs("div",{className:"flow-step",children:[n.jsx("div",{className:"flow-index",children:p+1}),n.jsx("strong",{children:f.label}),n.jsx("span",{children:f.detail})]},f.label))})]})}function Np({users:u,query:f,selectedUser:p,onQuery:r,onSelect:C,onUpdate:T,onBulkUpdate:k,onCreateKey:W,groups:N,onOpenRegistration:y}){const[_,V]=b.useState(1),[P,fe]=b.useState("all"),[ee,me]=b.useState(new Set),[ne,se]=b.useState("10"),[Y,xe]=b.useState(""),[te,ye]=b.useState(""),[O,ae]=b.useState(!1),[ve,Ce]=b.useState("10"),[Me,ue]=b.useState(""),[Z,de]=b.useState(""),[$,A]=b.useState(!1),L=P==="all"?u:u.filter(z=>z.status===P),H=L.filter(z=>z.role!=="admin"),X=Math.max(1,Math.ceil(L.length/25)),ie=Math.min(_,X),m=L.slice((ie-1)*25,ie*25),h=H.length>0&&H.every(z=>ee.has(z.id));b.useEffect(()=>{V(1)},[f,P]),b.useEffect(()=>{const z=new Set(u.map(be=>be.id));me(be=>new Set([...be].filter(ce=>z.has(ce))))},[u]),b.useEffect(()=>{Ce("10"),ue(""),de("")},[p?.user.id]);function M(z){me(be=>{const ce=new Set(be);return ce.has(z)?ce.delete(z):ce.add(z),ce})}function Q(){me(z=>{const be=new Set(z);return h?H.forEach(ce=>be.delete(ce.id)):H.forEach(ce=>be.add(ce.id)),be})}async function le(z,be){if(ee.size!==0){ae(!0);try{await k([...ee],z,be),me(new Set),xe("")}finally{ae(!1)}}}async function Ne(z){if(!p)return;const be=Math.abs(Number(ve));if(!Number.isFinite(be)||be<=0){de("请输入大于 0 的金额");return}A(!0),de("");try{await k([p.user.id],"adjust_balance",{amount:Number((be*z).toFixed(4)),reason:Me.trim()||(z>0?"管理员增加额度":"管理员扣减额度")}),de(z>0?"额度已增加":"额度已扣减"),ue("")}catch(ce){de(ce instanceof Error?ce.message:"额度调整失败")}finally{A(!1)}}return n.jsxs("section",{className:"users-layout",children:[n.jsxs(lt,{title:"用户管理",children:[n.jsxs("div",{className:"panel-toolbar",children:[n.jsxs("div",{className:"search-box",children:[n.jsx(zt,{name:"search"}),n.jsx("input",{value:f,onChange:z=>r(z.target.value),placeholder:"搜索 ID、姓名或邮箱"})]}),n.jsx("button",{className:"icon-button",title:"开放注册",onClick:y,children:n.jsx(zt,{name:"plus"})})]}),n.jsxs("div",{className:"user-summary-strip",children:[n.jsxs("span",{children:[n.jsx("strong",{children:u.length})," 匹配用户"]}),n.jsxs("span",{children:[n.jsx("strong",{children:u.filter(z=>z.status==="active").length})," 正常"]}),n.jsxs("span",{children:[n.jsx("strong",{children:u.filter(z=>z.status==="disabled").length})," 禁用"]}),n.jsxs("span",{children:[n.jsx("strong",{children:u.reduce((z,be)=>z+be.requestsToday,0)})," 今日请求"]})]}),n.jsx("div",{className:"user-filter-row",role:"group","aria-label":"用户状态筛选",children:[{value:"all",label:"全部"},{value:"active",label:"正常"},{value:"limited",label:"受限"},{value:"disabled",label:"禁用"}].map(z=>n.jsx("button",{type:"button",className:P===z.value?"selected":"",onClick:()=>fe(z.value),children:z.label},z.value))}),n.jsx("button",{type:"button",className:"secondary-button mobile-bulk-select",onClick:Q,children:h?"取消全选":`全选结果(${H.length})`}),ee.size>0&&n.jsxs("div",{className:"bulk-action-bar",children:[n.jsxs("strong",{children:["已选 ",ee.size," 人"]}),n.jsx("input",{type:"number",step:"0.01",value:ne,onChange:z=>se(z.target.value),"aria-label":"额度调整值"}),n.jsx("input",{value:Y,onChange:z=>xe(z.target.value),placeholder:"原因,例如:活动赠送","aria-label":"调整原因"}),n.jsx("button",{type:"button",className:"secondary-button",disabled:O||!Number(ne),onClick:()=>le("adjust_balance",{amount:Number(ne),reason:Y}),children:"调整额度"}),n.jsx("button",{type:"button",className:"secondary-button",disabled:O,onClick:()=>le("set_status",{value:"active"}),children:"启用"}),n.jsx("button",{type:"button",className:"danger-button",disabled:O,onClick:()=>le("set_status",{value:"disabled"}),children:"禁用"}),n.jsxs("select",{className:"bulk-group-select",value:te,disabled:O,"aria-label":"批量设置分组",onChange:z=>ye(z.target.value),children:[n.jsx("option",{value:"",children:"未分组"}),N.map(z=>n.jsx("option",{value:z.id,children:z.name},z.id))]}),n.jsx("button",{type:"button",className:"secondary-button",disabled:O,onClick:()=>le("set_group",{value:te}),children:"设为分组"})]}),n.jsxs("div",{className:"table",children:[n.jsxs("div",{className:"table-head users-table",children:[n.jsx("input",{type:"checkbox",checked:h,onChange:Q,"aria-label":"选择当前筛选结果"}),n.jsx("span",{children:"用户"}),n.jsx("span",{children:"状态"}),n.jsx("span",{children:"余额"}),n.jsx("span",{children:"今日"})]}),m.map(z=>n.jsxs("div",{className:p?.user.id===z.id?"table-row users-table selected":"table-row users-table",role:"button",tabIndex:0,onClick:()=>C(z.id),onKeyDown:be=>{(be.key==="Enter"||be.key===" ")&&C(z.id)},children:[n.jsx("input",{type:"checkbox",checked:ee.has(z.id),disabled:z.role==="admin",onChange:()=>M(z.id),onClick:be=>be.stopPropagation(),"aria-label":z.role==="admin"?`${z.name} 是管理员,不参与批量操作`:`选择 ${z.name}`}),n.jsxs("span",{children:[n.jsx("strong",{children:z.name}),n.jsxs("small",{children:[z.id," · ",z.email||"未绑定邮箱"]})]}),n.jsx(_t,{tone:z.status,children:Ot(z.status)}),n.jsx("span",{children:xt(z.balance)}),n.jsx("span",{children:z.requestsToday})]},z.id)),L.length===0&&n.jsx(Lt,{text:"暂无匹配用户"})]}),L.length>25&&n.jsxs("div",{className:"pagination-bar",children:[n.jsx("button",{className:"secondary-button",disabled:ie<=1,onClick:()=>V(z=>Math.max(1,z-1)),children:"上一页"}),n.jsxs("span",{children:[ie," / ",X]}),n.jsx("button",{className:"secondary-button",disabled:ie>=X,onClick:()=>V(z=>Math.min(X,z+1)),children:"下一页"})]})]}),n.jsx(lt,{title:"用户详情",children:p?n.jsxs("div",{className:"detail-stack",children:[n.jsxs("div",{className:"user-hero",children:[n.jsx("div",{className:"avatar",children:p.user.name.slice(0,1)}),n.jsxs("div",{children:[n.jsx("h2",{children:p.user.name}),n.jsx("p",{children:p.user.email})]}),n.jsx(_t,{tone:p.user.status,children:Ot(p.user.status)})]}),n.jsxs("div",{className:"settings-group",children:[n.jsx(el,{label:"角色",value:p.user.role==="admin"?"管理员":"用户"}),n.jsx(el,{label:"余额",value:xt(p.user.balance)}),n.jsx(el,{label:"总请求",value:String(p.user.totalRequests)}),n.jsx(el,{label:"最后登录",value:tl(p.user.lastLoginAt)}),n.jsx(el,{label:"API 调用",value:p.user.status==="disabled"?"关闭":"允许",switchOn:p.user.status!=="disabled"})]}),n.jsxs("div",{className:"group-assign-row",children:[n.jsxs("label",{children:[n.jsx("span",{children:"所属分组"}),n.jsxs("select",{value:p.user.groupId||"",onChange:z=>T(p.user.id,{groupId:z.target.value}),children:[n.jsx("option",{value:"",children:"未分组"}),N.map(z=>n.jsx("option",{value:z.id,children:z.name},z.id))]})]}),n.jsx("small",{children:"分组决定该用户可路由到哪些渠道;未分组用户只能使用未限制分组的渠道。"})]}),n.jsxs("div",{className:"balance-adjuster",children:[n.jsxs("div",{className:"balance-adjuster-title",children:[n.jsx("strong",{children:"调整余额"}),n.jsxs("span",{children:["当前 ",xt(p.user.balance)]})]}),n.jsxs("div",{className:"balance-adjuster-fields",children:[n.jsxs("label",{children:[n.jsx("span",{children:"金额"}),n.jsx("input",{type:"number",min:"0.0001",step:"0.01",value:ve,onChange:z=>Ce(z.target.value)})]}),n.jsxs("label",{children:[n.jsx("span",{children:"备注"}),n.jsx("input",{value:Me,onChange:z=>ue(z.target.value),placeholder:"可选,会记录到流水"})]})]}),n.jsxs("div",{className:"balance-adjuster-actions",children:[n.jsx("button",{className:"secondary-button",disabled:$,onClick:()=>Ne(1),children:"增加"}),n.jsx("button",{className:"danger-button",disabled:$,onClick:()=>Ne(-1),children:"扣减"}),n.jsx("span",{role:"status",children:Z})]})]}),n.jsxs("div",{className:"action-row",children:[n.jsx("button",{className:"secondary-button",onClick:()=>W(p.user.id),children:"创建 Key"}),n.jsx("button",{className:"secondary-button",disabled:p.user.role==="admin",onClick:()=>T(p.user.id,{status:p.user.status==="disabled"?"active":"disabled"}),children:p.user.role==="admin"?"管理员保护":p.user.status==="disabled"?"解封":"禁用"})]}),n.jsxs("div",{children:[n.jsx("h3",{children:"API Key"}),p.apiKeys.map(z=>n.jsxs("div",{className:"list-row",children:[n.jsxs("div",{children:[n.jsx("strong",{children:z.name}),n.jsxs("span",{children:[z.prefix,"*** · ",z.requestCount," 次"]})]}),n.jsx(_t,{tone:z.status,children:Ot(z.status)})]},z.id)),p.apiKeys.length===0&&n.jsx(Lt,{text:"暂无 API Key"})]})]}):n.jsx(Lt,{text:"请选择一个用户"})})]})}function Ap({selectedUser:u,onCreateKey:f,onUpdateKey:p,onDeleteKey:r}){return n.jsx(lt,{title:"密钥管理",children:u?n.jsxs(n.Fragment,{children:[n.jsxs("div",{className:"panel-toolbar",children:[n.jsxs("span",{className:"muted-inline",children:[u.user.name," · 完整 Key 只在创建时显示,丢失请重新创建"]}),n.jsx("button",{className:"primary-button",onClick:()=>f(u.user.id),children:"创建 Key"})]}),u.apiKeys.length?u.apiKeys.map(C=>n.jsx(Cp,{apiKey:C,onSave:p,onDelete:r},C.id)):n.jsx(Lt,{text:"暂无密钥"})]}):n.jsx(Lt,{text:"请选择一个用户"})})}function Cp({apiKey:u,onSave:f,onDelete:p}){const[r,C]=b.useState(u.name),[T,k]=b.useState(je(u.allowedModels).join(", ")),[W,N]=b.useState(mm(u.expiresAt||"")),[y,G]=b.useState(String(u.rateLimitPerMinute||"")),[_,V]=b.useState(!1),P=je(u.allowedModels).length?je(u.allowedModels).join(", "):"全部模型";b.useEffect(()=>{C(u.name),k(je(u.allowedModels).join(", ")),N(mm(u.expiresAt||"")),G(String(u.rateLimitPerMinute||""))},[u]);async function fe(){V(!0);try{await f(u.id,{name:r.trim()||"API Key",allowedModels:Pc(T),expiresAt:sp(W),rateLimitPerMinute:Number(y||0)})}finally{V(!1)}}return n.jsxs("details",{className:"key-editor key-editor-collapsible",children:[n.jsxs("summary",{className:"key-editor-head",children:[n.jsxs("div",{children:[n.jsx("strong",{children:u.name}),n.jsxs("span",{children:[u.prefix,"*** · ",P," · 最后使用 ",tl(u.lastUsedAt)]})]}),n.jsxs("div",{className:"row-actions",children:[n.jsx(_t,{tone:u.status,children:Ot(u.status)}),n.jsx("span",{className:"key-expand-hint",children:"管理"})]})]}),n.jsxs("div",{className:"key-editor-body",children:[n.jsxs("div",{className:"key-editor-grid",children:[n.jsxs("label",{children:["名称",n.jsx("input",{value:r,onChange:ee=>C(ee.target.value)})]}),n.jsxs("label",{children:["允许模型",n.jsx("input",{value:T,onChange:ee=>k(ee.target.value),placeholder:"留空表示全部模型,多个用逗号分隔"})]}),n.jsxs("label",{children:["过期时间",n.jsx("input",{type:"datetime-local",value:W,onChange:ee=>N(ee.target.value)})]}),n.jsxs("label",{children:["每分钟限制",n.jsx("input",{type:"number",min:"0",value:y,onChange:ee=>G(ee.target.value),placeholder:"0 使用全局限制"})]})]}),n.jsxs("div",{className:"key-editor-actions",children:[n.jsx("button",{className:"secondary-button",onClick:()=>f(u.id,{status:u.status==="active"?"disabled":"active"}),children:u.status==="active"?"停用密钥":"启用密钥"}),n.jsx("button",{className:"danger-button",onClick:()=>p(u.id),children:"删除密钥"}),n.jsx("button",{className:"primary-button",disabled:_,onClick:fe,children:_?"保存中":"保存设置"})]})]})]})}function Tp({models:u,onCopy:f,onCreate:p,onUpdate:r,onDelete:C}){const T=u.filter(H=>H.recommended),[k,W]=b.useState(""),[N,y]=b.useState("all"),[G,_]=b.useState("all"),[V,P]=b.useState(1),[fe,ee]=b.useState(""),[me,ne]=b.useState(""),[se,Y]=b.useState(""),[xe,te]=b.useState(""),[ye,O]=b.useState(""),[ae,ve]=b.useState(""),Ce=60,Me=b.useMemo(()=>{const H=new Map;return u.forEach(X=>{const ie=$c(X);H.set(ie,(H.get(ie)||0)+1)}),Array.from(H.entries()).sort((X,ie)=>ie[1]-X[1]||cl(X[0]).localeCompare(cl(ie[0]))).map(([X,ie])=>({provider:X,count:ie}))},[u]),ue=b.useMemo(()=>{const H=k.trim().toLowerCase();return u.filter(X=>N!=="all"&&$c(X)!==N||G!=="all"&&X.status!==G?!1:H?[X.id,X.name,X.vendor,X.category,X.description,...je(X.aliases)].join(" ").toLowerCase().includes(H):!0)},[u,k,N,G]),Z=b.useMemo(()=>{const H=new Map;ue.forEach(M=>{const Q=$c(M);H.set(Q,[...H.get(Q)||[],M])});const X=[];let ie=[],m=0;const h=Array.from(H.entries()).sort((M,Q)=>cl(M[0]).localeCompare(cl(Q[0])));for(const M of h)ie.length>0&&m+M[1].length>Ce&&(X.push(ie),ie=[],m=0),ie.push(M),m+=M[1].length;return ie.length>0&&X.push(ie),X},[ue]),de=Math.max(1,Z.length),$=Math.min(V,de),A=Z[$-1]||[];b.useEffect(()=>{P(1)},[k,N,G,u.length]);async function L(H){H.preventDefault();const X=fe.trim();if(X){ve("");try{await p({id:X,name:me.trim()||X,vendor:se.trim()||"Custom",aliases:xe.split(",").map(ie=>ie.trim()).filter(Boolean),category:"通用",description:ye.trim(),price:"自定义",context:"未配置上下文"}),ee(""),ne(""),Y(""),te(""),O("")}catch(ie){ve(ie instanceof Error?ie.message:"模型添加失败")}}}return n.jsxs("section",{className:"models-page",children:[n.jsx("div",{className:"model-hero",children:n.jsxs("div",{children:[n.jsx("span",{children:"Model Catalog"}),n.jsx("strong",{children:"Models"})]})}),n.jsx(lt,{title:"新增模型",children:n.jsxs("form",{className:"model-create-form",onSubmit:L,children:[n.jsx("input",{value:fe,onChange:H=>ee(H.target.value),placeholder:"模型 ID,例如 openai/gpt-4.1"}),n.jsx("input",{value:me,onChange:H=>ne(H.target.value),placeholder:"显示名称(可选)"}),n.jsx("input",{value:se,onChange:H=>Y(H.target.value),placeholder:"供应商(可选)"}),n.jsx("input",{value:xe,onChange:H=>te(H.target.value),placeholder:"代称,多个用逗号分隔(可选)"}),n.jsx("input",{className:"model-create-wide",value:ye,onChange:H=>O(H.target.value),placeholder:"描述(可选)"}),n.jsx("button",{className:"primary-button",type:"submit",children:"新增模型"}),n.jsx("div",{className:"model-create-message",role:"status",children:ae})]})}),T.length>0&&n.jsx(lt,{title:"推荐模型",children:n.jsx("div",{className:"model-grid",children:T.map(H=>n.jsx(Mp,{model:H,featured:!0,onCopy:f,onUpdate:r},H.id))})}),n.jsxs(lt,{title:"全部模型",children:[n.jsxs("div",{className:"panel-toolbar model-list-toolbar",children:[n.jsx("input",{value:k,onChange:H=>W(H.target.value),placeholder:"搜索模型 ID、名称、供应商或代称"}),n.jsx("div",{className:"model-filter-actions",children:[{value:"all",label:"全部"},{value:"available",label:"可用"},{value:"disabled",label:"禁用"}].map(H=>n.jsx("button",{type:"button",className:G===H.value?"selected":"",onClick:()=>_(H.value),children:H.label},H.value))})]}),n.jsxs("div",{className:"model-provider-filter","aria-label":"按供应商筛选模型",children:[n.jsxs("button",{type:"button",className:N==="all"?"selected":"",onClick:()=>y("all"),children:[n.jsx("span",{className:"provider-icon provider-icon-all","aria-hidden":"true",children:"All"}),n.jsx("strong",{children:"全部"}),n.jsx("small",{children:u.length})]}),Me.map(H=>n.jsxs("button",{type:"button",className:N===H.provider?"selected":"",onClick:()=>y(H.provider),children:[n.jsx(hm,{provider:H.provider}),n.jsx("strong",{children:cl(H.provider)}),n.jsx("small",{children:H.count})]},H.provider))]}),n.jsxs("div",{className:"model-list-summary",children:[n.jsx("span",{children:N==="all"?"全部供应商":cl(N)}),n.jsx("strong",{children:ue.length}),n.jsx("span",{children:"个模型"})]}),ue.length>0?n.jsxs(n.Fragment,{children:[n.jsx("div",{className:"model-provider-groups",children:A.map(([H,X])=>n.jsxs("section",{className:"model-provider-group",children:[n.jsxs("header",{children:[n.jsx(hm,{provider:H}),n.jsxs("div",{children:[n.jsx("strong",{children:cl(H)}),n.jsxs("span",{children:[X.length," 个模型"]})]})]}),n.jsx("div",{className:"model-compact-grid",children:X.map(ie=>n.jsx(Ep,{model:ie,onCopy:f,onUpdate:r,onDelete:C},ie.id))})]},H))}),de>1&&n.jsxs("div",{className:"pager",children:[n.jsx("button",{className:"secondary-button compact-button",onClick:()=>P(H=>Math.max(1,H-1)),disabled:$<=1,children:"上一页"}),n.jsxs("span",{children:[$," / ",de]}),n.jsx("button",{className:"secondary-button compact-button",onClick:()=>P(H=>Math.min(de,H+1)),disabled:$>=de,children:"下一页"})]})]}):n.jsx(Lt,{text:u.length?"没有匹配的模型":"暂无模型,请先添加你要开放给用户调用的模型 ID"})]})]})}function Ep({model:u,onCopy:f,onUpdate:p,onDelete:r}){const C=je(u.aliases).slice(0,3),T=u.status==="disabled";return n.jsxs("article",{className:"model-compact-row",children:[n.jsxs("div",{className:"model-compact-main",children:[n.jsx("strong",{children:u.name}),n.jsx("small",{children:u.id}),C.length>0&&n.jsx("span",{children:C.map(k=>n.jsx("em",{children:k},k))})]}),n.jsxs("div",{className:"model-row-actions",children:[n.jsx(_t,{tone:u.status,children:Ot(u.status)}),u.recommended&&n.jsx("span",{className:"model-recommended-mark",children:"推荐"}),n.jsx("button",{className:"icon-button",title:"复制模型 ID",onClick:()=>f(u.id,"模型 ID 已复制"),children:n.jsx(zt,{name:"copy"})}),n.jsxs("details",{className:"model-row-menu",children:[n.jsx("summary",{"aria-label":"模型操作",children:"•••"}),n.jsxs("div",{children:[n.jsx("button",{type:"button",onClick:()=>p(u.id,{recommended:!u.recommended}),children:u.recommended?"取消推荐":"设为推荐"}),n.jsx("button",{type:"button",onClick:()=>p(u.id,{status:T?"available":"disabled"}),children:T?"启用模型":"停用模型"}),n.jsx("button",{className:"is-danger",type:"button",onClick:()=>r(u.id),children:"删除模型"})]})]})]})]})}function Mp({model:u,featured:f=!1,onCopy:p,onUpdate:r}){return n.jsxs("article",{className:f?"model-card featured":"model-card",children:[n.jsxs("div",{className:"model-card-head",children:[n.jsxs("div",{children:[n.jsx("strong",{children:u.name}),n.jsxs("span",{children:[u.vendor," · ",u.category]})]}),n.jsx(_t,{tone:u.status,children:Ot(u.status)})]}),n.jsx("p",{children:u.description}),n.jsx("div",{className:"alias-row",children:je(u.aliases).map(C=>n.jsx("span",{children:C},C))}),n.jsxs("div",{className:"model-meta",children:[n.jsxs("span",{children:["价格:",u.price]}),n.jsx("span",{children:u.context})]}),n.jsxs("div",{className:"model-id",children:[n.jsx("code",{children:u.id}),n.jsx("button",{className:"icon-button",title:"复制模型 ID",onClick:()=>p(u.id,"模型 ID 已复制"),children:n.jsx(zt,{name:"copy"})})]}),r&&n.jsxs("div",{className:"model-card-actions",children:[n.jsx("button",{className:"secondary-button compact-button",type:"button",onClick:()=>r(u.id,{recommended:!1}),children:"取消推荐"}),n.jsx("button",{className:"secondary-button compact-button",type:"button",onClick:()=>r(u.id,{status:u.status==="disabled"?"available":"disabled"}),children:u.status==="disabled"?"启用":"停用"})]})]})}function vm({channels:u,onCreate:f,onImport:p,onCheckAccounts:r,onDeduplicateAccounts:C,onDeleteAccount:T,onUpdate:k,onStartOAuth:W,onCompleteOAuth:N,variant:y="drawing"}){const G=y==="antigravity",_=u.filter(h=>G?h.provider==="antigravity":h.provider!=="antigravity"&&(h.provider==="codex"||h.provider==="openai"||je(h.models).some(M=>M.includes("image")))),[V,P]=b.useState(""),[fe,ee]=b.useState({}),[me,ne]=b.useState({}),[se,Y]=b.useState({}),[xe,te]=b.useState({}),[ye,O]=b.useState(""),[ae,ve]=b.useState(""),[Ce,Me]=b.useState(""),[ue,Z]=b.useState(""),de=24,$=48;async function A(h,M){P(`import:${h}`);try{await p(h,M)}finally{P("")}}async function L(h,M=!1,Q=""){P(Q?`check-account:${Q}`:`${M?"retry":"check"}:${h}`);try{await r(h,M,Q)}finally{P("")}}async function H(h){P(`dedupe:${h}`);try{await C(h)}finally{P("")}}function X(h){const M=je(h.openaiAccounts).map(ce=>[ce.email||ce.name||ce.accountId||ce.id,ce.accountId||"",ce.status||"unchecked",ce.credentialMode||"access-token",ce.planType||"",ce.expiresAt||"",ce.lastCheckedAt||"",ce.lastUsedAt||"",String(ce.requestCount||0),ce.lastErrorCode||"",ce.lastError||""]),Q=ce=>`"${ce.replace(/"/g,'""')}"`,le=[["账号","Account ID","状态","凭据方式","套餐","到期时间","最近检测","最近调用","调用次数","错误码","最后错误"],...M].map(ce=>ce.map(Q).join(",")).join(`\r -`),Ne=new Blob(["\uFEFF"+le],{type:"text/csv;charset=utf-8"}),z=URL.createObjectURL(Ne),be=document.createElement("a");be.href=z,be.download=`${h.name||"account-pool"}-health-report.csv`,be.click(),URL.revokeObjectURL(z)}async function ie(h){P(`status:${h.id}`);try{await k(h.id,{status:h.status==="disabled"?"healthy":"disabled",baseUrl:h.baseUrl||Ei(h.provider)||Ti,provider:h.provider||"codex",models:je(h.models).length?h.models:jm.split(",").map(M=>M.trim())})}finally{P("")}}async function m(h,M){const Q=M.email||M.name||M.accountId||M.id;if(window.confirm(`删除账号「${Q}」?`)){P(`delete-account:${M.id}`);try{await T(h.id,M.id)}finally{P("")}}}return n.jsxs(lt,{title:G?"Antigravity 账号池":"账号池",children:[n.jsxs("div",{className:"panel-toolbar",children:[n.jsx("span",{className:"muted-inline",children:G?"导入 Google OAuth JSON 或 refresh token 即可入池。":"账号有两种来源,任选其一即可。"}),G?n.jsx("button",{className:"primary-button",onClick:()=>f(Ic("antigravity")),children:"新增 Antigravity 账号池"}):n.jsx("button",{className:"primary-button",onClick:()=>f(Ic("codex")),children:"新增账号池渠道"})]}),n.jsx("div",{className:"source-guide",children:G?n.jsxs(n.Fragment,{children:[n.jsxs("div",{className:"source-guide-item",children:[n.jsx("strong",{children:"Antigravity 导入(推荐)"}),n.jsx("span",{children:"粘贴 Google OAuth 凭据 JSON 或 refresh token,调用前自动换取 accessToken。"})]}),n.jsxs("div",{className:"source-guide-item",children:[n.jsx("strong",{children:"批量导入"}),n.jsx("span",{children:"支持 JSON、ZIP、TXT;TXT 可使用 JSONL 或每行一个 refresh token。"})]})]}):n.jsxs(n.Fragment,{children:[n.jsxs("div",{className:"source-guide-item",children:[n.jsx("strong",{children:"网页会话(推荐)"}),n.jsx("span",{children:"支持完整 auth/session JSON 或浏览器 Session Cookie,并在调用前重新获取 accessToken。"})]}),n.jsxs("div",{className:"source-guide-item",children:[n.jsx("strong",{children:"批量导入"}),n.jsx("span",{children:"支持 JSON、ZIP、TXT;TXT 可使用 JSONL 或每行一个 access token。"})]})]})}),n.jsxs("div",{className:"channels-stack",children:[_.map(h=>{const M=je(h.openaiAccounts),Q=M.filter(B=>B.status==="healthy").length,le=M.filter(B=>B.status==="invalid").length,Ne=M.filter(B=>!!B.lastErrorCode).length,z=Math.max(0,M.length-Q-le),be=M.filter(B=>B.credentialMode==="refreshable").length,ce=M.filter(B=>B.credentialMode==="browser-session").length,jt=Math.max(0,M.length-be-ce),I=me[h.id]||"all",Ve=(se[h.id]||"").trim().toLowerCase(),ot=M.filter(B=>{const Be=I==="all"||I==="attention"&&Gp(B)||I==="invalid"&&B.status==="invalid"||I==="error"&&!!B.lastErrorCode||I==="refreshable"&&B.credentialMode==="refreshable",We=`${B.email||""} ${B.name||""} ${B.accountId||""} ${B.userId||""} ${B.lastError||""}`.toLowerCase();return Be&&(!Ve||We.includes(Ve))}),Je=xe[h.id]||"pool",gt=[...ot].sort((B,Be)=>{if(Je==="pool")return 0;const We=Je==="expiry"?B.expiresAt||"9999-12-31":B.lastUsedAt||"",at=Je==="expiry"?Be.expiresAt||"9999-12-31":Be.lastUsedAt||"";return Je==="recent"?at.localeCompare(We):We.localeCompare(at)}),Dt=Math.min(gt.length,fe[h.id]||de),ll=gt.slice(0,Dt),al=Math.max(0,gt.length-ll.length),U=al===0;return n.jsxs("div",{className:"channel-card",children:[n.jsxs("div",{className:"channel-card-head",children:[n.jsxs("div",{children:[n.jsx("strong",{children:h.name}),n.jsx("span",{children:h.baseUrl||Ei(h.provider)||Ti}),n.jsxs("small",{children:["账号 ",M.length," 个,可用 ",Q,",无效 ",le,",未验证 ",z]}),n.jsxs("small",{children:["可续期 ",be," · 网页会话 ",ce," · 仅 Token ",jt]}),n.jsxs("small",{children:["自动检测 ",h.lastCheckedAt?tl(h.lastCheckedAt):"等待首次检测"]})]}),n.jsxs("div",{className:"channel-card-head-actions",children:[n.jsx(_t,{tone:h.status,children:Ot(h.status)}),n.jsx("button",{className:"primary-button compact-button",onClick:()=>Z(h.id),disabled:V!=="",children:"添加账号"}),n.jsx("button",{className:"secondary-button compact-button",onClick:()=>L(h.id),disabled:V!==""||M.length===0,children:V===`check:${h.id}`?"检测中":"批量检测"}),le>0&&n.jsx("button",{className:"secondary-button compact-button",onClick:()=>L(h.id,!0),disabled:V!=="",children:V===`retry:${h.id}`?"复检中":`复检无效 ${le}`}),M.length>1&&n.jsx("button",{className:"secondary-button compact-button",onClick:()=>H(h.id),disabled:V!=="",children:V===`dedupe:${h.id}`?"去重中":"账号去重"}),M.length>0&&n.jsx("button",{className:"secondary-button compact-button",onClick:()=>X(h),disabled:V!=="",children:"导出报告"}),n.jsx("button",{className:"secondary-button compact-button",onClick:()=>ie(h),disabled:V!=="",children:h.status==="disabled"?"启用渠道":"停用渠道"})]})]}),n.jsxs("div",{className:"account-filter-bar",role:"group","aria-label":"账号筛选",children:[n.jsx("input",{value:se[h.id]||"",onChange:B=>Y(Be=>({...Be,[h.id]:B.target.value})),placeholder:"搜索账号或错误","aria-label":"搜索账号"}),n.jsxs("select",{value:Je,onChange:B=>te(Be=>({...Be,[h.id]:B.target.value})),"aria-label":"账号排序",children:[n.jsx("option",{value:"pool",children:"账号池顺序"}),n.jsx("option",{value:"oldest",children:"最久未用优先"}),n.jsx("option",{value:"recent",children:"最近使用优先"}),n.jsx("option",{value:"expiry",children:"最早到期优先"})]}),[["all",`全部 ${M.length}`],["attention","需关注"],["invalid",`无效 ${le}`],["error",`有错误 ${Ne}`],["refreshable",`可续期 ${be}`]].map(([B,Be])=>n.jsx("button",{type:"button",className:I===B?"selected":"",onClick:()=>ne(We=>({...We,[h.id]:B})),children:Be},B))]}),n.jsxs("div",{className:"metrics-grid",children:[n.jsx(ol,{label:"账号总数",value:M.length}),n.jsx(ol,{label:"可用账号",value:Q}),n.jsx(ol,{label:"无效账号",value:le}),n.jsx(ol,{label:"未验证账号",value:z})]}),n.jsx("div",{className:"drawing-channel-models",children:je(h.models).length?je(h.models).map(B=>n.jsx("span",{children:B},B)):n.jsx("span",{children:"未绑定绘图模型"})}),M.length>0&&n.jsxs("div",{className:"account-pool-list",children:[ll.map(B=>n.jsxs("div",{className:"account-pool-row",children:[n.jsxs("div",{className:"account-pool-main",children:[n.jsxs("div",{children:[n.jsxs("div",{className:"account-pool-title",children:[n.jsx("strong",{title:B.email||B.name||B.accountId||B.id,children:B.email||B.name||B.accountId||B.id}),n.jsx("span",{className:`source-tag source-tag-${B.source==="web-login"?"web":"manual"}`,children:B.source==="web-login"?"网页登录":B.source==="web-oauth"?"网页 OAuth":B.source==="oauth"?"Codex OAuth":B.source==="antigravity"||B.source==="agy"||B.source==="google-antigravity"?"Antigravity":"导入"})]}),n.jsx("span",{children:B.lastError?`${fm(B.lastErrorCode)}${fm(B.lastErrorCode)?" · ":""}${B.lastError}`:B.lastCheckedAt?`上次检测 ${tl(B.lastCheckedAt)}`:"未检测"}),n.jsxs("span",{children:[B.credentialMode==="refreshable"?"可自动续期":B.credentialMode==="browser-session"?"依赖网页会话":"仅 access token",B.expiresAt?` · 到期 ${tl(B.expiresAt)}`:""]}),n.jsxs("span",{children:["套餐 ",ap(B.planType)]}),B.lastUsedAt&&n.jsxs("span",{children:["最近调用 ",tl(B.lastUsedAt)," · ",B.requestCount||0," 次"]})]}),n.jsx(wp,{limits:B.quotaLimits})]}),n.jsxs("div",{className:"account-pool-meta",children:[n.jsx(_t,{tone:B.status==="healthy"?"healthy":B.status==="invalid"?"disabled":"standby",children:B.status==="healthy"?"可用":B.status==="invalid"?"无效":"未验证"}),n.jsx("button",{type:"button",className:"secondary-button compact-button",disabled:V!=="",onClick:()=>L(h.id,!1,B.id),children:V===`check-account:${B.id}`?"检测中":"检测"}),n.jsx("button",{type:"button",className:"danger-button compact-button",disabled:V!=="",onClick:()=>m(h,B),children:V===`delete-account:${B.id}`?"删除中":"删除"})]})]},B.id)),ot.length===0&&n.jsx(Lt,{text:"没有匹配的账号"}),gt.length>de&&n.jsxs("div",{className:"account-pool-more",children:[n.jsx("span",{className:"muted-inline",children:U?`已显示全部 ${gt.length} 个账号`:`已显示 ${ll.length} 个,还有 ${al} 个`}),n.jsxs("div",{className:"account-pool-more-actions",children:[!U&&n.jsxs("button",{type:"button",className:"secondary-button compact-button",onClick:()=>ee(B=>({...B,[h.id]:Math.min(gt.length,Dt+$)})),children:["再显示 ",Math.min($,al)," 个"]}),!U&&n.jsx("button",{type:"button",className:"secondary-button compact-button",onClick:()=>ee(B=>({...B,[h.id]:gt.length})),children:"全部显示"}),Dt>de&&n.jsx("button",{type:"button",className:"secondary-button compact-button",onClick:()=>ee(B=>({...B,[h.id]:de})),children:"收起"})]})]})]})]},h.id)}),_.length===0&&n.jsx(Lt,{text:G?"暂无 Antigravity 渠道,先新增一个 Antigravity 账号池":"暂无绘图渠道,先新增一个 OpenAI 账号池渠道"})]}),ye&&n.jsx(Rp,{channelId:ye,variant:G?"antigravity":"openai",onStart:W,onComplete:N,onClose:()=>O("")}),ue&&n.jsx(zp,{busy:V!=="",variant:y,onAuthSession:()=>{ve(ue),Z("")},onAntigravity:()=>{Me(ue),Z("")},onOAuth:()=>{O(ue),Z("")},onImport:async h=>{await A(ue,h),Z("")},onClose:()=>Z("")}),ae&&n.jsx(_p,{onImport:async h=>{await A(ae,new File([JSON.stringify(Op(h))],"authsession.json",{type:"application/json"}))},onClose:()=>ve("")}),Ce&&n.jsx(Up,{onImport:async h=>{await A(Ce,new File([JSON.stringify(Dp(h))],"antigravity.json",{type:"application/json"}))},onClose:()=>Me("")})]})}function zp({busy:u,variant:f="drawing",onAuthSession:p,onAntigravity:r,onOAuth:C,onImport:T,onClose:k}){const W=f==="antigravity";return n.jsx("div",{className:"modal-backdrop",onClick:k,children:n.jsxs("div",{className:"modal-card account-add-modal",onClick:N=>N.stopPropagation(),children:[n.jsxs("div",{className:"modal-head",children:[n.jsxs("div",{children:[n.jsx("strong",{children:"添加账号"}),n.jsx("span",{children:"选择一种账号接入方式"})]}),n.jsx("button",{type:"button",className:"icon-button",onClick:k,children:"×"})]}),n.jsxs("div",{className:"account-add-options",children:[W?n.jsxs(n.Fragment,{children:[n.jsxs("button",{type:"button",className:"account-add-option recommended",onClick:C,disabled:u,children:[n.jsx("span",{className:"account-add-icon",children:"G"}),n.jsx("strong",{children:"本地授权登录"}),n.jsx("small",{children:"用 Google 账号登录授权,自动获取 refresh token"})]}),n.jsxs("button",{type:"button",className:"account-add-option",onClick:r,disabled:u,children:[n.jsx("span",{className:"account-add-icon",children:"T"}),n.jsx("strong",{children:"导入 Antigravity 凭据"}),n.jsx("small",{children:"已有 Google OAuth JSON 或 refresh token 时粘贴"})]})]}):n.jsxs("button",{type:"button",className:"account-add-option recommended",onClick:p,disabled:u,children:[n.jsx("span",{className:"account-add-icon",children:"A"}),n.jsx("strong",{children:"导入网页会话"}),n.jsx("small",{children:"粘贴完整 authsession JSON,保留 sessionToken"})]}),n.jsxs("label",{className:`account-add-option${u?" disabled":""}`,children:[n.jsx("span",{className:"account-add-icon",children:"J"}),n.jsx("strong",{children:u?"导入中":"导入 JSON / ZIP / TXT"}),n.jsx("small",{children:"批量导入已有账号文件"}),n.jsx("input",{type:"file",accept:"application/json,application/zip,text/plain,.json,.zip,.txt",disabled:u,onChange:N=>{const y=N.target.files?.[0];y&&T(y),N.target.value=""}})]})]}),n.jsx("div",{className:"modal-actions",children:n.jsx("button",{type:"button",className:"secondary-button",onClick:k,children:"取消"})})]})})}function Op(u){const f=u.trim(),p=f.match(/(?:^|[;\s])(__Secure-(?:next-auth|authjs)\.session-token)=([^;\s]+)/);if(p)return{sessionToken:p[2],source:"web-login"};try{const r=JSON.parse(f),C=r.tokens&&typeof r.tokens=="object"?r.tokens:{},T=G=>typeof r[G]=="string"?r[G]:typeof C[G]=="string"?C[G]:"",k=T("accessToken")||T("access_token"),W=T("refreshToken")||T("refresh_token"),N=T("sessionToken")||T("session_token"),y=r.user&&typeof r.user=="object"?r.user:{};if(k||W||N)return{accessToken:k||void 0,refreshToken:W||void 0,sessionToken:N||void 0,email:typeof y.email=="string"?y.email:void 0,name:typeof y.name=="string"?y.name:void 0,source:"web-login"}}catch{}return{sessionToken:f,source:"web-login"}}function _p({onImport:u,onClose:f}){const[p,r]=b.useState(""),[C,T]=b.useState(!1),[k,W]=b.useState(""),[N,y]=b.useState("");async function G(){if(!p.trim()){W("请粘贴 authsession");return}T(!0),W(""),y("");try{await u(p.trim()),r(""),y("已导入,继续粘贴下一条即可")}catch(_){W(_ instanceof Error?_.message:"导入失败")}finally{T(!1)}}return n.jsx("div",{className:"modal-backdrop",onClick:f,children:n.jsxs("div",{className:"modal-card",onClick:_=>_.stopPropagation(),children:[n.jsxs("div",{className:"modal-head",children:[n.jsx("strong",{children:"添加网页会话"}),n.jsx("button",{type:"button",className:"icon-button",onClick:f,children:"×"})]}),n.jsxs("p",{className:"muted-inline",children:["可直接粘贴 chatgpt.com/api/auth/session 的完整 JSON;也支持浏览器 ",n.jsx("code",{children:"__Secure-next-auth.session-token"})," 的值或完整 Cookie 字符串。"]}),n.jsxs("label",{className:"authsession-field",children:[n.jsx("span",{children:"authsession"}),n.jsx("textarea",{autoFocus:!0,value:p,onChange:_=>r(_.target.value),placeholder:"eyJhbGci..."})]}),k&&n.jsx("div",{className:"form-error",children:k}),N&&n.jsx("div",{className:"form-success",children:N}),n.jsxs("div",{className:"modal-actions",children:[n.jsx("button",{type:"button",className:"secondary-button",onClick:f,children:"取消"}),n.jsx("button",{type:"button",className:"primary-button",disabled:C,onClick:G,children:C?"导入中":"导入账号"})]})]})})}function Dp(u){const f=u.trim();try{const p=JSON.parse(f),r=p.tokens&&typeof p.tokens=="object"?p.tokens:{},C=(...y)=>{for(const G of y){if(typeof p[G]=="string"&&p[G])return p[G];if(typeof r[G]=="string"&&r[G])return r[G]}return""},T=C("access_token","accessToken"),k=C("refresh_token","refreshToken"),W=C("email"),N=C("expired","expires_at","expiresAt","expiry");if(T||k)return{accessToken:T||void 0,refreshToken:k||void 0,email:W||void 0,expired:N||void 0,source:"antigravity"}}catch{}return{refreshToken:f,source:"antigravity"}}function Up({onImport:u,onClose:f}){const[p,r]=b.useState(""),[C,T]=b.useState(!1),[k,W]=b.useState(""),[N,y]=b.useState("");async function G(){if(!p.trim()){W("请粘贴 Antigravity OAuth JSON 或 refresh token");return}T(!0),W(""),y("");try{await u(p.trim()),r(""),y("已导入,继续粘贴下一条即可")}catch(_){W(_ instanceof Error?_.message:"导入失败")}finally{T(!1)}}return n.jsx("div",{className:"modal-backdrop",onClick:f,children:n.jsxs("div",{className:"modal-card",onClick:_=>_.stopPropagation(),children:[n.jsxs("div",{className:"modal-head",children:[n.jsx("strong",{children:"导入 Antigravity 账号"}),n.jsx("button",{type:"button",className:"icon-button",onClick:f,children:"×"})]}),n.jsxs("p",{className:"muted-inline",children:["粘贴 Antigravity 的 Google OAuth 凭证 JSON(含 ",n.jsx("code",{children:"access_token"})," / ",n.jsx("code",{children:"refresh_token"}),"),或直接粘贴 refresh token。导入后会自动刷新并加入账号池。"]}),n.jsxs("label",{className:"authsession-field",children:[n.jsx("span",{children:"OAuth JSON / refresh token"}),n.jsx("textarea",{autoFocus:!0,value:p,onChange:_=>r(_.target.value),placeholder:'{"access_token":"ya29...","refresh_token":"1//..."}'})]}),k&&n.jsx("div",{className:"form-error",children:k}),N&&n.jsx("div",{className:"form-success",children:N}),n.jsxs("div",{className:"modal-actions",children:[n.jsx("button",{type:"button",className:"secondary-button",onClick:f,children:"取消"}),n.jsx("button",{type:"button",className:"primary-button",disabled:C,onClick:G,children:C?"导入中":"导入账号"})]})]})})}function Rp({channelId:u,variant:f="openai",onStart:p,onComplete:r,onClose:C}){const T=f==="antigravity",[k,W]=b.useState(""),[N,y]=b.useState(""),[G,_]=b.useState(""),[V,P]=b.useState(!1),[fe,ee]=b.useState("");async function me(){P(!0),ee("");try{const se=await p(u);W(se.authorizeUrl),y(se.state),window.open(se.authorizeUrl,"_blank","noopener")}catch(se){ee(se instanceof Error?se.message:"发起授权失败")}finally{P(!1)}}async function ne(){if(!G.trim()){ee("请粘贴授权完成后浏览器跳转的回调地址");return}P(!0),ee("");try{await r(u,{callbackUrl:G.trim(),state:N}),C()}catch(se){ee(se instanceof Error?se.message:"完成授权失败")}finally{P(!1)}}return n.jsx("div",{className:"modal-backdrop",onClick:C,children:n.jsxs("div",{className:"modal-card",onClick:se=>se.stopPropagation(),children:[n.jsxs("div",{className:"modal-head",children:[n.jsx("strong",{children:T?"本地授权添加 Antigravity 账号":"OAuth 授权添加网页账号"}),n.jsx("button",{type:"button",className:"icon-button",onClick:C,children:"×"})]}),n.jsx("p",{className:"muted-inline",children:T?"用 Google 账号登录授权 Antigravity 客户端,服务端换取 refresh_token 后加入账号池,凭据只保存在本服务。":"使用 ChatGPT 网页兼容的 OAuth 客户端获取 refresh_token,只调用网页 backend-api,不会走 Codex 接口。"}),n.jsxs("ol",{className:"oauth-steps",children:[n.jsxs("li",{children:[n.jsx("button",{type:"button",className:"primary-button",onClick:me,disabled:V,children:k?"重新生成授权链接":"① 生成授权链接并打开"}),k&&n.jsxs("div",{className:"oauth-link",children:[n.jsx("input",{readOnly:!0,value:k,onFocus:se=>se.target.select()}),n.jsx("span",{className:"muted-inline",children:T?"若未自动打开,复制到浏览器手动访问,用要添加的 Google 账号登录授权。":"若未自动打开,复制到浏览器手动访问,用要添加的 ChatGPT 账号登录授权。"})]})]}),n.jsxs("li",{children:[n.jsx("label",{children:"② 粘贴授权后浏览器跳转的完整回调地址"}),n.jsx("input",{value:G,placeholder:T?"http://localhost:8788/oauth2callback?code=...&state=...":"https://platform.openai.com/auth/callback?code=...&state=...",onChange:se=>_(se.target.value),disabled:!k||V})]})]}),fe&&n.jsx("p",{className:"form-error",children:fe}),n.jsxs("div",{className:"modal-actions",children:[n.jsx("button",{type:"button",className:"secondary-button",onClick:C,disabled:V,children:"取消"}),n.jsx("button",{type:"button",className:"primary-button",onClick:ne,disabled:!k||V,children:V?"处理中":"完成授权"})]})]})})}function wp({limits:u}){const f=je(u).filter(p=>p.label||p.name).slice(0,3);return f.length?n.jsx("div",{className:"quota-bars",children:f.map(p=>{const r=Hp(p);return n.jsxs("div",{className:"quota-bar",children:[n.jsxs("div",{className:"quota-bar-label",children:[n.jsx("strong",{children:p.label||p.name}),n.jsx("span",{children:Bp(p,r)})]}),n.jsx("div",{className:"quota-bar-track",children:n.jsx("span",{style:{width:`${r}%`}})})]},`${p.label||p.name}-${p.resetAt||""}`)})}):null}function Hp(u){return typeof u.percentRemaining=="number"&&Number.isFinite(u.percentRemaining)?Math.max(0,Math.min(100,u.percentRemaining)):typeof u.remaining=="number"&&typeof u.limit=="number"&&u.limit>0?Math.max(0,Math.min(100,u.remaining/u.limit*100)):typeof u.used=="number"&&typeof u.limit=="number"&&u.limit>0?Math.max(0,Math.min(100,(u.limit-u.used)/u.limit*100)):0}function Bp(u,f){const p=u.resetAt?` · ${tl(u.resetAt)}`:"";return typeof u.remaining=="number"?`${Math.round(f)}% 剩余${p}`:`${Math.round(f)}%${p}`}function qp(u){const f=je(u.models).join(" ").toLowerCase(),p=["对话"];u.streamMode!=="disabled"&&p.push("流式"),/(image|dall-e|gpt-image)/.test(f)&&p.push("图片"),(u.openaiAccountCount??u.openaiAccounts?.length??0)>0&&p.push("账号池");const r=u.upstreamKeyCount??0;return r>1&&p.push(`${r} Key 轮询`),p}function Gp(u){if(u.status==="invalid"||u.credentialMode==="browser-session")return!0;if(!u.expiresAt)return!1;const f=new Date(u.expiresAt).getTime();return Number.isFinite(f)&&f-Date.now()<=1440*60*1e3}function kp({channels:u,groups:f,onUpdate:p,onCreate:r,onImport:C,onDelete:T,onSyncModels:k,onCheck:W}){const N=Ta("openai"),[y,G]=b.useState(!1),[_,V]=b.useState(N.provider),[P,fe]=b.useState(N.name),[ee,me]=b.useState(N.baseUrl),[ne,se]=b.useState(N.models.join(", ")),[Y,xe]=b.useState(""),[te,ye]=b.useState(""),[O,ae]=b.useState(!1),[ve,Ce]=b.useState(!1);function Me(Z){const de=Ta(Z);V(de.provider),fe(de.name),me(de.baseUrl),se(de.models.join(", ")),ye("")}async function ue(Z){Z.preventDefault(),ae(!0),ye("");try{await r({name:P.trim()||Ta(_).name,provider:_,baseUrl:ee.trim(),...Nm(Y),models:Pc(ne),streamMode:"auto"}),G(!1),xe(""),Me(_)}catch(de){ye(de instanceof Error?de.message:"渠道创建失败")}finally{ae(!1)}}return n.jsxs(lt,{title:"渠道",children:[n.jsxs("div",{className:"channel-page-intro",children:[n.jsxs("div",{children:[n.jsx("strong",{children:"管理上游服务"}),n.jsx("span",{children:"每个渠道对应一个 API 上游。先添加渠道,再检测连通性并同步可用模型。"})]}),n.jsxs("div",{className:"channel-page-summary",children:[n.jsxs("span",{children:[n.jsx("b",{children:u.length})," 个渠道"]}),n.jsxs("span",{children:[n.jsx("b",{children:u.filter(Z=>Z.status!=="disabled").length})," 个已启用"]})]})]}),n.jsxs("div",{className:"panel-toolbar channel-toolbar",children:[n.jsx("span",{className:"muted-inline",children:"列表显示当前状态;点击任一渠道可修改连接、模型和计费设置。"}),n.jsx("button",{className:"primary-button",onClick:()=>G(Z=>!Z),children:y?"取消新增":"+ 新增渠道"})]}),y&&n.jsxs("form",{className:"channel-card channel-create-form",onSubmit:ue,children:[n.jsxs("label",{className:"channel-select-field",children:[n.jsx("span",{children:"选择渠道类型"}),n.jsxs("details",{className:"channel-choice-menu",children:[n.jsxs("summary",{children:[n.jsx("span",{children:Ta(_).label}),n.jsx("small",{children:"选择后自动填入建议配置"})]}),n.jsx("div",{role:"listbox","aria-label":"选择渠道类型",children:Fc.map(Z=>n.jsxs("button",{type:"button",className:_===Z.provider?"selected":"",onClick:de=>{Me(Z.provider),de.currentTarget.closest("details")?.removeAttribute("open")},children:[n.jsx("strong",{children:Z.label}),n.jsx("span",{children:"使用推荐的名称和地址"})]},Z.provider))})]})]}),n.jsxs("div",{className:"channel-form-grid",children:[n.jsxs("label",{children:[n.jsx("span",{children:"渠道名称"}),n.jsx("input",{value:P,onChange:Z=>fe(Z.target.value),placeholder:"例如 OpenAI 主线路"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"供应商"}),n.jsx("input",{value:cl(_),readOnly:!0})]}),n.jsxs("label",{className:"channel-form-wide",children:[n.jsx("span",{children:"Base URL"}),n.jsx("input",{value:ee,onChange:Z=>me(Z.target.value),placeholder:"https://provider.example/v1"})]}),n.jsxs("label",{className:"channel-form-wide",children:[n.jsx("span",{children:"上游 Key"}),n.jsx("textarea",{className:"channel-key-input",value:Y,onChange:Z=>xe(Z.target.value),placeholder:_==="codex"?"账号池导入后使用":_==="cpa"?"填写 CPA 的 API key,多个每行一个":"每行一个 Key;填多个会自动轮询分发",autoComplete:"off",rows:3})]}),n.jsxs("div",{className:"channel-form-wide channel-model-field",children:[n.jsxs("div",{className:"field-label-row",children:[n.jsx("span",{children:"模型"}),n.jsx("button",{type:"button",className:"model-pull-button",onClick:()=>{if(_!=="codex"&&!ee.trim()){ye("请先填写 Base URL 再获取模型");return}ye(""),Ce(!0)},children:"从上游获取模型"})]}),n.jsx("textarea",{value:ne,onChange:Z=>se(Z.target.value),placeholder:"多个模型用逗号分隔,或点上方『从上游获取模型』拉取后多选"})]})]}),n.jsxs("div",{className:"channel-card-actions",children:[n.jsx("span",{className:"model-create-message",role:"status",children:te}),n.jsx("button",{className:"secondary-button",type:"button",onClick:()=>Me(_),disabled:O,children:"填入模板"}),n.jsx("button",{className:"primary-button",type:"submit",disabled:O,children:O?"创建中":"创建渠道"})]})]}),y&&ve&&n.jsx(Am,{subtitle:`${P.trim()||Ta(_).name} · 勾选需要接入的模型`,current:Pc(ne),loadModels:async()=>{const Z=await pe("/api/channel-model-preview",{method:"POST",body:JSON.stringify({provider:_,baseUrl:ee.trim(),upstreamApiKey:Y})});return je(Z.models)},onConfirm:async Z=>{se(Z.join(", "))},onClose:()=>Ce(!1)}),n.jsxs("div",{className:"channels-stack",children:[u.map(Z=>n.jsx(Yp,{channel:Z,groups:f,onUpdate:p,onImport:C,onDelete:T,onSyncModels:k,onCheck:W},Z.id)),u.length===0&&n.jsx(Lt,{text:"暂无渠道,先在后端添加渠道接口或导入配置"})]})]})}function Yp({channel:u,groups:f,onUpdate:p,onImport:r,onDelete:C,onSyncModels:T,onCheck:k}){const[W,N]=b.useState(u.name),[y,G]=b.useState(u.provider),[_,V]=b.useState(u.streamMode||"auto"),[P,fe]=b.useState(u.baseUrl),[ee,me]=b.useState(je(u.allowedGroupIds)),[ne,se]=b.useState(je(u.models).join(", ")),[Y,xe]=b.useState("saved"),[te,ye]=b.useState(String(u.inputPricePer1K||0)),[O,ae]=b.useState(String(u.outputPricePer1K||0)),[ve,Ce]=b.useState(!!u.webEndpoint),[Me,ue]=b.useState(""),[Z,de]=b.useState(""),[$,A]=b.useState(!1),L=u.openaiAccountCount??u.openaiAccounts?.length??0,H=je(u.models).length,X=qp(u),ie=u.status==="disabled",m={saved:"已保存",template:"模板",manual:"手动",synced:"上游同步"}[Y],h=Ni.find(I=>I.value===_)||Ni[0],M={auto:"自动处理(推荐)",real:"强制流式",fake:"兼容流式",disabled:"关闭流式"};b.useEffect(()=>{N(u.name),G(u.provider),V(u.streamMode||"auto"),fe(u.baseUrl),me(je(u.allowedGroupIds)),se(je(u.models).join(", ")),xe("saved"),ye(String(u.inputPricePer1K||0)),ae(String(u.outputPricePer1K||0)),Ce(!!u.webEndpoint),ue("")},[u.id,u.name,u.provider,u.streamMode,u.baseUrl,u.models,u.inputPricePer1K,u.outputPricePer1K,u.webEndpoint]);async function Q(){const I=le();de("save");try{await p(u.id,I),ue("")}finally{de("")}}function le(){const I=Ei(y),Ve=!/^https?:\/\//i.test(P.trim())&&I?I:P,ot={name:W.trim()||u.name,provider:y,streamMode:_,baseUrl:Ve,inputPricePer1K:Number(te)||0,outputPricePer1K:Number(O)||0,webEndpoint:ve,models:ne.split(",").map(Je=>Je.trim()).filter(Boolean),allowedGroupIds:ee};return Object.assign(ot,Nm(Me)),ot}async function Ne(){const I=u.status==="disabled"?"healthy":"disabled";de("status");try{await p(u.id,{...le(),status:I}),ue("")}finally{de("")}}async function z(){de("sync");try{await p(u.id,le()),ue(""),A(!0)}finally{de("")}}function be(){const I=Ta(y);se(I.models.join(", ")),xe("template"),I.baseUrl&&!/^https?:\/\//i.test(P.trim())&&fe(I.baseUrl)}async function ce(){de("check");try{await Q(),await k(u.id)}finally{de("")}}async function jt(I){de("import");try{await r(u.id,I)}finally{de("")}}return n.jsxs(n.Fragment,{children:[n.jsxs("details",{className:"channel-card channel-card-collapsible",children:[n.jsxs("summary",{className:"channel-card-head channel-list-row",children:[n.jsxs("div",{className:"channel-identity",children:[n.jsx("strong",{children:u.name}),n.jsx("span",{children:cl(y)}),n.jsx("small",{children:u.baseUrl||"尚未配置上游地址"}),n.jsx("div",{className:"channel-capability-tags",children:X.map(I=>n.jsx("span",{children:I},I))})]}),n.jsxs("div",{className:"channel-list-meta",children:[n.jsxs("span",{children:[n.jsx("b",{children:H})," 个模型"]}),L>0&&n.jsxs("span",{children:[n.jsx("b",{children:L})," 个账号"]})]}),n.jsxs("div",{className:"channel-check-result",children:[n.jsx("span",{children:"连通性"}),n.jsx("b",{className:u.lastError?"is-error":u.lastCheckedAt?"is-ok":"",children:u.lastError?"检测失败":u.lastCheckedAt?`已检测 ${tl(u.lastCheckedAt)}`:"尚未检测"})]}),n.jsxs("div",{className:"channel-list-status",children:[n.jsx(_t,{tone:u.status,children:ie?"已停用":Ot(u.status)}),n.jsx("span",{className:"channel-expand-hint",children:"配置"})]})]}),n.jsxs("div",{className:"channel-editor-controls",children:[n.jsxs("label",{className:"channel-select-field",children:[n.jsx("span",{children:"供应商"}),n.jsxs("details",{className:"channel-choice-menu",children:[n.jsxs("summary",{children:[n.jsx("span",{children:cl(y)}),n.jsx("small",{children:"修改上游协议类型"})]}),n.jsx("div",{role:"listbox","aria-label":"供应商",children:to.map(I=>n.jsxs("button",{type:"button",className:y===I.value?"selected":"",onClick:Ve=>{G(I.value);const ot=Ei(I.value);ot&&!/^https?:\/\//i.test(P.trim())&&fe(ot),Ve.currentTarget.closest("details")?.removeAttribute("open")},children:[n.jsx("strong",{children:I.label}),n.jsx("span",{children:I.value==="compatible"?"适用于兼容 OpenAI 格式的服务":"选择对应的上游协议"})]},I.value))})]})]}),n.jsxs("label",{className:"channel-select-field",children:[n.jsx("span",{children:"响应方式"}),n.jsxs("details",{className:"channel-choice-menu",children:[n.jsxs("summary",{children:[n.jsx("span",{children:M[h.value]}),n.jsx("small",{children:h.description})]}),n.jsx("div",{role:"listbox","aria-label":"响应方式",children:Ni.map(I=>n.jsxs("button",{type:"button",className:_===I.value?"selected":"",onClick:Ve=>{V(I.value),Ve.currentTarget.closest("details")?.removeAttribute("open")},children:[n.jsx("strong",{children:M[I.value]}),n.jsx("span",{children:I.description})]},I.value))})]})]})]}),(y==="codex"||L>0)&&n.jsxs("div",{className:"setting",children:[n.jsxs("div",{children:[n.jsx("span",{children:"网页对话接口"}),n.jsx("small",{children:"开启后走 ChatGPT 网页对话接口,把 Plus 订阅账号包装成 API"})]}),n.jsx("div",{className:"setting-value",children:n.jsx("button",{type:"button",className:ve?"ios-switch is-on":"ios-switch","aria-label":ve?"关闭网页对话接口":"开启网页对话接口","aria-pressed":ve,onClick:()=>Ce(I=>!I),children:n.jsx("span",{})})})]}),n.jsxs("div",{className:"channel-form-grid",children:[n.jsxs("label",{children:[n.jsx("span",{children:"渠道名称"}),n.jsx("input",{value:W,onChange:I=>N(I.target.value),placeholder:"例如 Gemini 主线路"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"供应商"}),n.jsx("input",{value:cl(y),readOnly:!0})]}),n.jsxs("label",{className:"channel-form-wide",children:[n.jsx("span",{children:"Base URL"}),n.jsx("input",{value:P,onChange:I=>fe(I.target.value),placeholder:"https://provider.example/v1"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"优先级"}),n.jsx("input",{value:u.priority,readOnly:!0})]}),n.jsxs("div",{className:"channel-form-wide channel-model-field",children:[n.jsxs("div",{className:"field-label-row",children:[n.jsx("span",{children:"模型"}),n.jsxs("small",{children:["来源:",m]})]}),n.jsx("textarea",{value:ne,onChange:I=>{se(I.target.value),xe("manual")},placeholder:"优先拉取上游模型,也可以手动补充,多个用逗号分隔"}),n.jsxs("div",{className:"channel-model-actions",children:[n.jsx("button",{type:"button",className:"secondary-button compact-button",onClick:be,disabled:Z!=="",children:"填入模板"}),n.jsx("button",{type:"button",className:"secondary-button compact-button",onClick:z,disabled:Z!=="",children:Z==="sync"?"拉取中":"获取上游模型"})]})]}),n.jsxs("label",{className:"channel-form-wide",children:[n.jsxs("span",{children:["上游 Key",(u.upstreamKeyCount??0)>0?` (已配置 ${u.upstreamKeyCount} 个)`:u.upstreamKeySet?" (已配置)":""]}),n.jsx("textarea",{className:"channel-key-input",value:Me,onChange:I=>ue(I.target.value),placeholder:y==="codex"?"Codex 账号池不需要上游 Key":y==="cpa"?"填写 CPA 的 API key,多个每行一个":"每行一个 Key;填多个会自动轮询分发;留空不修改",autoComplete:"off",rows:3})]}),n.jsxs("label",{children:[n.jsx("span",{children:"输入单价 / 1K Token"}),n.jsx("input",{type:"number",min:"0",step:"0.0001",value:te,onChange:I=>ye(I.target.value)})]}),n.jsxs("label",{children:[n.jsx("span",{children:"输出单价 / 1K Token"}),n.jsx("input",{type:"number",min:"0",step:"0.0001",value:O,onChange:I=>ae(I.target.value)})]}),n.jsx("span",{className:"channel-billing-note",children:"定价可先留空;接入是否可用优先看渠道检测和模型同步结果。"})]}),n.jsxs("div",{className:"channel-group-field",children:[n.jsxs("div",{className:"field-label-row",children:[n.jsx("span",{children:"可见分组"}),n.jsx("small",{children:ee.length?`已选择 ${ee.length} 个分组`:"全部用户可用"})]}),f.length===0?n.jsx("p",{className:"channel-group-empty",children:"还没有用户分组。去「分组」页创建后,可在这里把渠道限制为只对特定分组开放。"}):n.jsx("div",{className:"channel-group-options",children:f.map(I=>{const Ve=ee.includes(I.id);return n.jsxs("button",{type:"button",className:Ve?"selected":"","aria-pressed":Ve,onClick:()=>me(ot=>Ve?ot.filter(Je=>Je!==I.id):[...ot,I.id]),children:[n.jsx("span",{children:I.name}),I.description&&n.jsx("small",{children:I.description})]},I.id)})})]}),n.jsxs("div",{className:"channel-card-actions",children:[n.jsx("button",{className:"secondary-button",onClick:ce,disabled:Z!=="",children:Z==="check"?"检测中":"检测渠道"}),n.jsx("button",{className:"primary-button",onClick:Q,disabled:Z!=="",children:Z==="save"?"保存中":"保存"}),n.jsxs("details",{className:"channel-more-actions",children:[n.jsx("summary",{children:"更多操作"}),n.jsxs("div",{children:[n.jsxs("label",{className:"secondary-button",children:[Z==="import"?"导入中":"导入账号 JSON",n.jsx("input",{type:"file",accept:"application/json,application/zip,text/plain,.json,.zip,.txt",disabled:Z!=="",onChange:I=>{const Ve=I.target.files?.[0];Ve&&jt(Ve),I.target.value=""}})]}),n.jsx("button",{className:"secondary-button",onClick:Ne,disabled:Z!=="",children:u.status==="disabled"?"启用渠道":"停用渠道"}),n.jsx("button",{className:"danger-button",onClick:()=>C(u.id),disabled:Z!=="",children:"删除渠道"})]})]})]})]}),$&&n.jsx(Am,{subtitle:`${u.name} · 勾选需要接入的模型`,current:ne.split(",").map(I=>I.trim()).filter(Boolean),loadModels:async()=>{const I=await pe(`/api/channels/${u.id}/upstream-models`,{method:"POST",body:JSON.stringify({})});return je(I.models)},onConfirm:async I=>{await T(u.id,I)},onClose:()=>A(!1)})]})}function Am({subtitle:u,current:f,loadModels:p,onConfirm:r,onClose:C}){const[T,k]=b.useState(!0),[W,N]=b.useState(""),[y,G]=b.useState([]),[_,V]=b.useState(new Set),[P,fe]=b.useState(""),[ee,me]=b.useState(!1);b.useEffect(()=>{let O=!1;return(async()=>{k(!0),N("");try{const ae=await p();if(O)return;const ve=new Set,Ce=je(ae).map(ue=>ue.trim()).filter(ue=>{if(!ue)return!1;const Z=ue.toLowerCase();return ve.has(Z)?!1:(ve.add(Z),!0)}),Me=new Set(f.map(ue=>ue.toLowerCase()));G(Ce),V(new Set(Ce.filter(ue=>Me.has(ue.toLowerCase()))))}catch(ae){O||N(ae instanceof Error?ae.message:"获取上游模型失败")}finally{O||k(!1)}})(),()=>{O=!0}},[]);const ne=P.trim().toLowerCase(),se=ne?y.filter(O=>O.toLowerCase().includes(ne)):y,Y=se.length>0&&se.every(O=>_.has(O));function xe(O){V(ae=>{const ve=new Set(ae);return ve.has(O)?ve.delete(O):ve.add(O),ve})}function te(){V(O=>{const ae=new Set(O);return Y?se.forEach(ve=>ae.delete(ve)):se.forEach(ve=>ae.add(ve)),ae})}async function ye(){const O=new Set(y.map(ue=>ue.toLowerCase())),ae=f.filter(ue=>!O.has(ue.toLowerCase())),ve=y.filter(ue=>_.has(ue)),Ce=new Set,Me=[...ae,...ve].filter(ue=>{const Z=ue.toLowerCase();return Ce.has(Z)?!1:(Ce.add(Z),!0)});me(!0);try{await r(Me),C()}catch{me(!1)}}return n.jsx("div",{className:"modal-backdrop",onClick:C,children:n.jsxs("div",{className:"modal-card model-picker-modal",onClick:O=>O.stopPropagation(),children:[n.jsxs("div",{className:"modal-head",children:[n.jsxs("div",{children:[n.jsx("strong",{children:"选择上游模型"}),n.jsx("span",{children:u})]}),n.jsx("button",{type:"button",className:"icon-button",onClick:C,children:"×"})]}),T?n.jsx("div",{className:"model-picker-status",children:"正在获取上游模型…"}):W?n.jsx("div",{className:"model-picker-status model-picker-error",children:W}):n.jsxs(n.Fragment,{children:[n.jsxs("div",{className:"model-picker-toolbar",children:[n.jsx("input",{className:"model-picker-search",value:P,onChange:O=>fe(O.target.value),placeholder:"搜索模型名称",autoFocus:!0}),n.jsx("button",{type:"button",className:"secondary-button compact-button",onClick:te,disabled:se.length===0,children:Y?"取消全选":"全选"})]}),n.jsxs("div",{className:"model-picker-count",children:["共 ",y.length," 个 · 已选 ",_.size," 个",ne?` · 匹配 ${se.length} 个`:""]}),n.jsxs("div",{className:"model-picker-list",children:[se.map(O=>{const ae=_.has(O),ve=f.some(Ce=>Ce.toLowerCase()===O.toLowerCase());return n.jsxs("label",{className:`model-picker-row${ae?" checked":""}`,children:[n.jsx("input",{type:"checkbox",checked:ae,onChange:()=>xe(O)}),n.jsx("span",{className:"model-picker-name",children:O}),ve&&n.jsx("span",{className:"model-picker-tag",children:"已接入"})]},O)}),se.length===0&&n.jsx("div",{className:"model-picker-status",children:"没有匹配的模型"})]})]}),n.jsxs("div",{className:"modal-actions",children:[n.jsx("button",{type:"button",className:"secondary-button",onClick:C,children:"取消"}),n.jsx("button",{type:"button",className:"primary-button",disabled:T||!!W||ee,onClick:ye,children:ee?"保存中":`导入所选 (${_.size})`})]})]})})}function Lp({logs:u,onCopy:f}){const[p,r]=b.useState(u),[C,T]=b.useState(u.length),[k,W]=b.useState(1),[N,y]=b.useState(""),[G,_]=b.useState("all"),[V,P]=b.useState(null),[fe,ee]=b.useState(!1),[me,ne]=b.useState(!1),se=25,Y=Math.max(1,Math.ceil(C/se));b.useEffect(()=>{W(1)},[N,G]),b.useEffect(()=>{k>Y&&W(Y)},[k,Y]),b.useEffect(()=>{const te=new AbortController,ye=window.setTimeout(async()=>{ee(!0);try{const O=new URLSearchParams({page:String(k),pageSize:String(se),status:G,q:N.trim()}),ae=await pe(`/api/logs?${O}`,{signal:te.signal}),ve=je(ae.logs);r(ve),T(ae.total||0),P(Ce=>Ce&&ve.some(Me=>Me.id===Ce.id)?Ce:null)}catch(O){O instanceof DOMException&&O.name==="AbortError"||(r([]),T(0))}finally{te.signal.aborted||ee(!1)}},N?250:0);return()=>{window.clearTimeout(ye),te.abort()}},[k,N,G]);async function xe(te){if(V?.id===te.id){P(null);return}P(te),ne(!0);try{const ye=await pe(`/api/logs/${encodeURIComponent(te.id)}`);P(ye.log)}catch{P(te)}finally{ne(!1)}}return n.jsxs(lt,{title:"调用日志",children:[n.jsxs("div",{className:"logs-toolbar",children:[n.jsxs("div",{className:"search-box",children:[n.jsx(zt,{name:"search"}),n.jsx("input",{value:N,onChange:te=>y(te.target.value),placeholder:"搜索请求 ID、用户、Key、模型、渠道或错误码"})]}),n.jsx("div",{className:"log-status-filter",role:"group","aria-label":"日志状态筛选",children:[{value:"all",label:"全部"},{value:"success",label:"成功"},{value:"failed",label:"失败"}].map(te=>n.jsx("button",{type:"button",className:G===te.value?"selected":"",onClick:()=>_(te.value),children:te.label},te.value))}),n.jsx("span",{className:"muted-inline",children:fe?"加载中":`共 ${C} 条`})]}),n.jsxs("div",{className:V?"logs-layout has-detail":"logs-layout",children:[n.jsxs("div",{className:"table",children:[n.jsxs("div",{className:"table-head logs-table",children:[n.jsx("span",{children:"请求"}),n.jsx("span",{children:"模型"}),n.jsx("span",{children:"渠道"}),n.jsx("span",{children:"状态"})]}),p.map(te=>n.jsx("div",{className:"log-entry",children:n.jsxs("div",{className:V?.id===te.id?"table-row logs-table selected":"table-row logs-table",role:"button",tabIndex:0,onClick:()=>xe(te),onKeyDown:ye=>{(ye.key==="Enter"||ye.key===" ")&&xe(te)},children:[n.jsxs("span",{children:[n.jsx("strong",{children:lo(te)}),n.jsxs("small",{children:[te.id," · ",tl(te.createdAt)," · ",te.latencyMs,"ms"]})]}),n.jsx("span",{children:up(te)}),n.jsx("span",{children:cp(te)}),n.jsx(_t,{tone:te.status,children:Ot(te.status)})]})},te.id)),!fe&&p.length===0&&n.jsx(Lt,{text:N||G!=="all"?"没有匹配的日志":"暂无调用日志"})]}),V&&n.jsx(Qp,{log:V,loading:me,onCopy:f})]}),Y>1&&n.jsxs("div",{className:"pagination-bar",children:[n.jsx("button",{className:"secondary-button",disabled:k<=1||fe,onClick:()=>W(te=>Math.max(1,te-1)),children:"上一页"}),n.jsxs("span",{children:[k," / ",Y]}),n.jsx("button",{className:"secondary-button",disabled:k>=Y||fe,onClick:()=>W(te=>Math.min(Y,te+1)),children:"下一页"})]})]})}function Qp({log:u,loading:f,onCopy:p}){if(!u)return n.jsx("aside",{className:"log-inspector empty-inspector",children:n.jsx("span",{children:"选择一条日志查看详情"})});const r=Number(u.inputTokens||0),C=Number(u.outputTokens||0),T=typeof u.attempts=="number"?Math.max(0,u.attempts):null,k=[["请求 ID",u.id],["状态",Ot(u.status)],["时间",ip(u.createdAt)],["用户 ID",u.userId||"未识别"],["API Key",u.apiKeyPrefix?`${u.apiKeyPrefix}***`:"未识别"],["模型",u.model||"未提供"],["渠道",u.channel||"未选择"],["实际账号",u.account||"未记录"],["响应耗时",`${u.latencyMs} ms`],["尝试次数",T===null?"未记录":String(T)],["是否重试",T===null?"未记录":T>1?"是":"否"],["输入 Tokens",pl(r)],["输出 Tokens",pl(C)],["总 Tokens",pl(r+C)],["扣费",u.cost.toFixed(4)],["错误码",u.errorCode||"无"]];return n.jsxs("aside",{className:"log-inspector",children:[n.jsxs("header",{children:[n.jsxs("div",{children:[n.jsx("span",{children:f?"加载中":"日志详情"}),n.jsx("strong",{children:lo(u)})]}),n.jsx(_t,{tone:u.status,children:Ot(u.status)})]}),n.jsx("div",{className:"log-detail",children:k.map(([W,N])=>n.jsxs("div",{children:[n.jsx("span",{children:W}),n.jsx("strong",{title:N,children:N})]},W))}),n.jsxs("div",{className:"log-actions",children:[n.jsx("button",{type:"button",className:"secondary-button compact-button",onClick:()=>p(u.id,"请求 ID 已复制"),children:"复制请求 ID"}),u.errorCode&&n.jsx("button",{type:"button",className:"secondary-button compact-button",onClick:()=>p(u.errorCode||"","错误码已复制"),children:"复制错误码"})]})]})}function Xp({models:u,channels:f,groups:p}){const[r,C]=b.useState(null),[T,k]=b.useState(null),[W,N]=b.useState("username"),[y,G]=b.useState("0"),[_,V]=b.useState(""),[P,fe]=b.useState({enabled:!0,minReward:.1,maxReward:1}),[ee,me]=b.useState({logRetentionDays:30,maxLogs:1e4,maxQuotaEntries:2e4}),[ne,se]=b.useState(null),[Y,xe]=b.useState(null),[te,ye]=b.useState(""),[O,ae]=b.useState(""),[ve,Ce]=b.useState(""),[Me,ue]=b.useState(""),[Z,de]=b.useState(""),[$,A]=b.useState(""),[L,H]=b.useState(""),[X,ie]=b.useState(""),[m,h]=b.useState(""),[M,Q]=b.useState(!1),[le,Ne]=b.useState("system"),z=u.find(U=>U.recommended&&U.status==="available")?.id||u.find(U=>U.status==="available")?.id||"未配置",be=f.filter(U=>U.status!=="disabled").length,ce=[{value:"system",label:"系统",description:"运行概况"},{value:"cli",label:"CLI 接入",description:"一键配置"},{value:"auth",label:"注册",description:"开放方式"},{value:"check-in",label:"签到",description:"奖励范围"},{value:"admin",label:"管理员",description:"账号绑定"},{value:"discord",label:"Discord",description:"登录限制"},{value:"maintenance",label:"维护",description:"日志保留"},{value:"backup",label:"备份",description:"导出恢复"}],jt=ce.find(U=>U.value===le)||ce[0];b.useEffect(()=>{Promise.all([pe("/api/settings/discord"),pe("/api/settings/auth"),pe("/api/settings/check-in"),pe("/api/account/me"),pe("/api/settings/maintenance"),pe("/api/health")]).then(([U,B,Be,We,at,Pe])=>{C(Wc(U.discord)),ie(je(U.discord.blockedGuildIds).join(` -`)),k(B.auth.registrationEnabled),N(Mi(B.auth.registrationMode)),G(String(B.auth.defaultBalance||0)),V(B.auth.defaultGroupId||""),fe(Be.checkIn),xe(We.account),ye(We.account?.username||""),ae(We.user.name||""),Ce(We.account?.email||""),ue(We.account?.discordUserId||""),me(at.maintenance),se(Pe)}).catch(()=>h("设置加载失败"))},[]);async function I(U=T,B=W,Be=Number(y),We=_){if(U!==null)try{const at=await pe("/api/settings/auth",{method:"PATCH",body:JSON.stringify({registrationEnabled:U,registrationMode:B,defaultBalance:Be,defaultGroupId:We})});k(at.auth.registrationEnabled),N(Mi(at.auth.registrationMode)),G(String(at.auth.defaultBalance||0)),V(at.auth.defaultGroupId||""),h(at.auth.registrationEnabled?"注册设置已保存":"已关闭用户注册")}catch(at){h(at instanceof Error?at.message:"注册设置保存失败")}}async function Ve(){T!==null&&I(!T,W)}async function ot(){Q(!0),h("");try{const U=await pe("/api/settings/check-in",{method:"PATCH",body:JSON.stringify(P)});fe(U.checkIn),h(U.checkIn.enabled?"签到奖励设置已保存":"已关闭每日签到")}catch(U){h(U instanceof Error?U.message:"签到设置保存失败")}finally{Q(!1)}}async function Je(){if(r){Q(!0),h("");try{const U=Wc(r),B=X.split(/[\s,]+/).map(We=>We.trim()).filter(Boolean),Be=await pe("/api/settings/discord",{method:"PATCH",body:JSON.stringify({...U,blockedGuildIds:B,clientSecret:L})});C(Wc(Be.discord)),ie(je(Be.discord.blockedGuildIds).join(` -`)),H(""),h("Discord 配置已保存")}catch(U){h(U instanceof Error?U.message:"保存失败,请检查填写内容")}finally{Q(!1)}}}async function gt(){try{const U=await pe("/api/account/profile",{method:"PATCH",body:JSON.stringify({username:te,displayName:O,email:ve,discordUserId:Me,currentPassword:Z,newPassword:$})});xe(U.account),ye(U.account.username),Ce(U.account.email||""),ue(U.account.discordUserId||""),de(""),A(""),h("管理员账号已保存")}catch(U){h(U instanceof Error?U.message:"账号设置保存失败")}}async function Dt(){Q(!0),h("");try{const U=await pe("/api/settings/maintenance",{method:"PATCH",body:JSON.stringify(ee)});me(U.maintenance),h("维护设置已保存,历史数据已按新规则清理")}catch(U){h(U instanceof Error?U.message:"维护设置保存失败")}finally{Q(!1)}}async function ll(){Q(!0),h("");try{const U=await fetch("/api/backup",{credentials:"include"});if(!U.ok)throw new Error("备份导出失败");const B=await U.blob(),We=(U.headers.get("Content-Disposition")||"").match(/filename="([^"]+)"/)?.[1]||"capi-backup.json",at=URL.createObjectURL(B),Pe=document.createElement("a");Pe.href=at,Pe.download=We,Pe.click(),URL.revokeObjectURL(at),h("备份已导出,请妥善保管")}catch(U){h(U instanceof Error?U.message:"备份导出失败")}finally{Q(!1)}}async function al(U){if(window.confirm("恢复会覆盖当前全部数据,并退出现有登录会话。确定继续?")){Q(!0),h("");try{const B=await fetch("/api/restore",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:U}),Be=await B.json().catch(()=>null);if(!B.ok)throw new Error(Be?.error?.message||"备份恢复失败");h(`已恢复 ${Be.users} 个用户、${Be.channels} 条渠道和 ${Be.models} 个模型,请重新登录`)}catch(B){h(B instanceof Error?B.message:"备份恢复失败")}finally{Q(!1)}}}return n.jsxs("div",{className:"settings-layout",children:[n.jsx("div",{className:"settings-tabs",children:ce.map(U=>n.jsxs("button",{type:"button",className:le===U.value?"selected":"",onClick:()=>Ne(U.value),children:[n.jsx("strong",{children:U.label}),n.jsx("small",{children:U.description})]},U.value))}),n.jsxs("div",{className:"settings-tab-note",children:[n.jsx("strong",{children:jt.label}),n.jsx("span",{children:jt.description})]}),le==="system"&&n.jsx(lt,{title:"系统设置",children:n.jsxs("div",{className:"settings-group",children:[n.jsx(el,{label:"接口兼容",value:"OpenAI API"}),n.jsx(el,{label:"当前默认模型",value:z}),n.jsx(el,{label:"已配置渠道",value:`${f.length} 个,${be} 个启用`}),n.jsx(el,{label:"可选供应商",value:`${to.length} 种`}),n.jsx(el,{label:"运行版本",value:`${ne?.version||"未知"} · ${ne?.commit||"未知"}`}),n.jsx(el,{label:"构建时间",value:ne?.buildTime?tl(ne.buildTime):"本地构建"}),n.jsx(el,{label:"账号自动检测",value:"每 15 分钟自动检测一次"})]})}),le==="cli"&&n.jsxs(lt,{title:"CLI 工具接入",children:[n.jsx("p",{className:"cli-intro",children:"CAPI 兼容 OpenAI 和 Anthropic 协议,常见 AI 命令行工具可直接接入。"}),n.jsxs("div",{className:"cli-credentials",children:[n.jsxs("div",{className:"cli-credential",children:[n.jsx("span",{children:"Base URL"}),n.jsx("code",{children:wl()}),n.jsx("button",{type:"button",className:"copy-button","aria-label":"复制",onClick:()=>{Ai(wl()),h("已复制 Base URL")},children:n.jsx(zt,{name:"copy"})})]}),n.jsxs("div",{className:"cli-credential",children:[n.jsx("span",{children:"API Key"}),n.jsx("code",{children:"cat_你的_api_key"})]})]}),n.jsxs("div",{className:"cli-tools",children:[n.jsxs("details",{className:"cli-tool",open:!0,children:[n.jsxs("summary",{children:[n.jsx("strong",{children:"Claude Code"}),n.jsx("span",{children:"Anthropic Messages 协议"})]}),n.jsx("pre",{children:`export ANTHROPIC_BASE_URL="${wl()}" -export ANTHROPIC_AUTH_TOKEN="cat_你的_api_key" -export ANTHROPIC_MODEL="${z}" -claude`})]}),n.jsxs("details",{className:"cli-tool",children:[n.jsxs("summary",{children:[n.jsx("strong",{children:"Codex CLI"}),n.jsx("span",{children:"OpenAI Chat 协议"})]}),n.jsxs("p",{children:["编辑 ",n.jsx("code",{children:"~/.codex/config.toml"}),":"]}),n.jsx("pre",{children:`model = "${z}" -model_provider = "capi" - -[model_providers.capi] -name = "CAPI" -base_url = "${wl()}/v1" -env_key = "CAPI_KEY" -wire_api = "chat"`}),n.jsx("p",{children:"然后设置环境变量并运行:"}),n.jsx("pre",{children:`export CAPI_KEY="cat_你的_api_key" -codex`})]}),n.jsxs("details",{className:"cli-tool",children:[n.jsxs("summary",{children:[n.jsx("strong",{children:"Aider"}),n.jsx("span",{children:"OpenAI 兼容"})]}),n.jsx("pre",{children:`export OPENAI_API_BASE="${wl()}" -export OPENAI_API_KEY="cat_你的_api_key" -aider --model openai/${z}`})]}),n.jsxs("details",{className:"cli-tool",children:[n.jsxs("summary",{children:[n.jsx("strong",{children:"Cline / Roo Code / Kilo Code"}),n.jsx("span",{children:"VS Code 插件"})]}),n.jsxs("p",{children:["在插件设置中选择 ",n.jsx("strong",{children:"OpenAI Compatible"}),":"]}),n.jsx("pre",{children:`Base URL: ${wl()}/v1 -API Key: cat_你的_api_key -Model ID: ${z}`})]}),n.jsxs("details",{className:"cli-tool",children:[n.jsxs("summary",{children:[n.jsx("strong",{children:"通用 OpenAI SDK"}),n.jsx("span",{children:"Python / Node.js"})]}),n.jsx("pre",{children:`export OPENAI_BASE_URL="${wl()}" -export OPENAI_API_KEY="cat_你的_api_key"`}),n.jsx("pre",{children:`from openai import OpenAI -client = OpenAI() -response = client.chat.completions.create( - model="${z}", - messages=[{"role": "user", "content": "hello"}] -)`})]})]}),m&&n.jsx("p",{className:"cli-message",role:"status",children:m})]}),le==="maintenance"&&n.jsx(lt,{title:"维护设置",children:n.jsxs("div",{className:"settings-group",children:[n.jsxs("div",{className:"setting",children:[n.jsx("span",{children:"日志保留天数"}),n.jsx("div",{className:"setting-value maintenance-control",children:n.jsx("input",{type:"number",min:"1",max:"3650",value:ee.logRetentionDays,onChange:U=>me(B=>({...B,logRetentionDays:Number(U.target.value)}))})})]}),n.jsxs("div",{className:"setting",children:[n.jsx("span",{children:"日志最大条数"}),n.jsx("div",{className:"setting-value maintenance-control",children:n.jsx("input",{type:"number",min:"100",max:"1000000",step:"100",value:ee.maxLogs,onChange:U=>me(B=>({...B,maxLogs:Number(U.target.value)}))})})]}),n.jsxs("div",{className:"setting",children:[n.jsx("span",{children:"额度流水最大条数"}),n.jsx("div",{className:"setting-value maintenance-control",children:n.jsx("input",{type:"number",min:"100",max:"2000000",step:"100",value:ee.maxQuotaEntries,onChange:U=>me(B=>({...B,maxQuotaEntries:Number(U.target.value)}))})})]}),n.jsxs("div",{className:"settings-save-row",children:[n.jsx("span",{role:"status",children:m}),n.jsx("button",{type:"button",className:"primary-button",disabled:M,onClick:Dt,children:M?"保存中":"保存维护设置"})]})]})}),le==="backup"&&n.jsx(lt,{title:"备份与恢复",children:n.jsx("div",{className:"settings-group",children:n.jsxs("div",{className:"setting",children:[n.jsxs("span",{children:["备份与恢复",n.jsx("small",{children:"包含账号哈希和加密后的上游密钥,恢复时需要相同的 SECRET_KEY"})]}),n.jsxs("div",{className:"setting-value backup-actions",children:[n.jsx("button",{type:"button",className:"secondary-button",disabled:M,onClick:ll,children:"导出备份"}),n.jsxs("label",{className:"secondary-button",children:["恢复备份",n.jsx("input",{type:"file",accept:"application/json,.json",disabled:M,onChange:U=>{const B=U.target.files?.[0];B&&al(B),U.target.value=""}})]})]})]})})}),le==="auth"&&n.jsx(lt,{title:"账号与注册",children:n.jsxs("div",{className:"settings-group",children:[n.jsxs("div",{className:"setting",children:[n.jsx("span",{children:"开放用户注册"}),n.jsxs("div",{className:"setting-value",children:[n.jsx("strong",{children:T?"启用":"关闭"}),n.jsx("button",{type:"button",className:T?"ios-switch is-on":"ios-switch","aria-label":T?"关闭用户注册":"开放用户注册","aria-pressed":!!T,onClick:Ve,children:n.jsx("span",{})})]})]}),n.jsxs("div",{className:"setting",children:[n.jsx("span",{children:"注册方式"}),n.jsx("div",{className:"registration-mode-control",role:"group","aria-label":"注册方式",children:[{value:"username",label:"账号密码"},{value:"email",label:"邮箱"},{value:"discord",label:"Discord"}].map(U=>n.jsx("button",{type:"button",className:W===U.value?"selected":"",onClick:()=>I(T??!0,U.value),children:U.label},U.value))})]}),n.jsxs("div",{className:"setting",children:[n.jsxs("span",{children:["新用户初始额度",n.jsx("small",{children:"注册完成后自动发放,仅影响之后的新用户"})]}),n.jsxs("div",{className:"setting-value auth-default-balance",children:[n.jsx("input",{type:"number",min:"0",step:"0.01",value:y,onChange:U=>G(U.target.value),"aria-label":"新用户初始额度"}),n.jsx("button",{type:"button",className:"secondary-button",onClick:()=>I(T,W,Number(y)),children:"保存"})]})]}),n.jsxs("div",{className:"setting",children:[n.jsxs("span",{children:["默认注册分组",n.jsx("small",{children:"新注册用户自动归入该分组,决定他们能用哪些渠道"})]}),n.jsxs("div",{className:"setting-value auth-default-balance",children:[n.jsxs("select",{value:_,onChange:U=>V(U.target.value),"aria-label":"默认注册分组",children:[p.map(U=>n.jsx("option",{value:U.id,children:U.name},U.id)),p.length===0&&n.jsx("option",{value:"",children:"暂无分组"})]}),n.jsx("button",{type:"button",className:"secondary-button",disabled:p.length===0,onClick:()=>I(T,W,Number(y),_),children:"保存"})]})]})]})}),le==="check-in"&&n.jsx(lt,{title:"每日签到奖励",children:n.jsxs("div",{className:"settings-group",children:[n.jsxs("div",{className:"setting",children:[n.jsxs("span",{children:["开放每日签到",n.jsx("small",{children:"用户每天可领取一次随机额度,按北京时间刷新"})]}),n.jsxs("div",{className:"setting-value",children:[n.jsx("strong",{children:P.enabled?"启用":"关闭"}),n.jsx("button",{type:"button",className:P.enabled?"ios-switch is-on":"ios-switch","aria-label":P.enabled?"关闭每日签到":"开放每日签到","aria-pressed":P.enabled,onClick:()=>fe(U=>({...U,enabled:!U.enabled})),children:n.jsx("span",{})})]})]}),n.jsxs("div",{className:"setting check-in-settings-row",children:[n.jsxs("span",{children:["随机奖励范围",n.jsx("small",{children:"领取金额精确到 0.01,直接计入用户余额和额度流水"})]}),n.jsxs("div",{className:"check-in-reward-inputs",children:[n.jsxs("label",{children:[n.jsx("span",{children:"最低"}),n.jsx("input",{type:"number",min:"0.01",max:"1000000",step:"0.01",value:P.minReward,onChange:U=>fe(B=>({...B,minReward:Number(U.target.value)}))})]}),n.jsxs("label",{children:[n.jsx("span",{children:"最高"}),n.jsx("input",{type:"number",min:"0.01",max:"1000000",step:"0.01",value:P.maxReward,onChange:U=>fe(B=>({...B,maxReward:Number(U.target.value)}))})]})]})]}),n.jsxs("div",{className:"settings-save-row",children:[n.jsx("span",{role:"status",children:m}),n.jsx("button",{type:"button",className:"primary-button",disabled:M,onClick:ot,children:M?"保存中":"保存签到设置"})]})]})}),le==="admin"&&n.jsx(lt,{title:"管理员账号",children:n.jsxs("form",{className:"discord-settings",onSubmit:U=>{U.preventDefault(),gt()},children:[n.jsxs("div",{className:"settings-form-grid",children:[n.jsxs("label",{children:[n.jsx("span",{children:"登录账号"}),n.jsx("input",{value:te,onChange:U=>ye(U.target.value),autoComplete:"username"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"显示名称"}),n.jsx("input",{value:O,onChange:U=>ae(U.target.value),autoComplete:"name"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"邮箱"}),n.jsx("input",{type:"email",value:ve,onChange:U=>Ce(U.target.value),autoComplete:"email"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"Discord 用户 ID(可选)"}),n.jsx("input",{inputMode:"numeric",autoComplete:"off",value:Me,onChange:U=>ue(U.target.value),placeholder:Y?.discordUserId?"已绑定":"输入管理员的 Discord 用户 ID"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"当前密码"}),n.jsx("input",{type:"password",value:Z,onChange:U=>de(U.target.value),autoComplete:"current-password",placeholder:"修改密码时填写"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"新密码"}),n.jsx("input",{type:"password",value:$,onChange:U=>A(U.target.value),autoComplete:"new-password",placeholder:"留空表示不修改"})]})]}),n.jsxs("div",{className:"settings-save-row",children:[n.jsx("span",{role:"status",children:m}),n.jsx("button",{className:"primary-button",type:"submit",children:"保存账号"})]})]})}),le==="discord"&&n.jsx(lt,{title:"Discord 登录",children:r?n.jsxs("form",{className:"discord-settings",autoComplete:"off",onSubmit:U=>{U.preventDefault(),Je()},children:[n.jsxs("div",{className:"discord-toggle-row",children:[n.jsxs("div",{children:[n.jsx("strong",{children:"Discord 登录"}),n.jsx("span",{children:r.enabled?"已启用":"未启用"})]}),n.jsx("button",{type:"button",className:r.enabled?"ios-switch is-on":"ios-switch","aria-label":r.enabled?"停用 Discord 登录":"启用 Discord 登录","aria-pressed":r.enabled,onClick:()=>C({...r,enabled:!r.enabled}),children:n.jsx("span",{})})]}),n.jsxs("div",{className:"settings-form-grid",children:[n.jsxs("label",{children:[n.jsx("span",{children:"Client ID"}),n.jsx("input",{inputMode:"numeric",autoComplete:"off",value:r.clientId,onChange:U=>C({...r,clientId:U.target.value}),placeholder:"100000000000000001"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"Client Secret"}),n.jsx("input",{type:"password",autoComplete:"new-password",value:L,onChange:U=>H(U.target.value),placeholder:r.clientSecretSet?"已设置,留空表示不修改":"粘贴 Discord Client Secret"})]}),n.jsxs("label",{className:"settings-form-wide",children:[n.jsx("span",{children:"回调地址"}),n.jsx("input",{type:"url",value:r.redirectUri,onChange:U=>C({...r,redirectUri:U.target.value}),placeholder:"https://你的域名/api/auth/discord/callback"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"服务器 ID"}),n.jsx("input",{inputMode:"numeric",value:r.allowedGuildId,onChange:U=>C({...r,allowedGuildId:U.target.value}),placeholder:"允许登录的服务器 ID"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"身份组 ID"}),n.jsx("input",{inputMode:"numeric",value:r.allowedRoleId,onChange:U=>C({...r,allowedRoleId:U.target.value}),placeholder:"允许登录的身份组 ID"})]}),n.jsxs("label",{className:"settings-form-wide",children:[n.jsx("span",{children:"拉黑服务器 ID"}),n.jsx("textarea",{value:X,onChange:U=>ie(U.target.value),placeholder:"每行一个服务器 ID;命中的用户禁止注册 / 登录",rows:3}),n.jsx("small",{children:"用户若加入了这些 Discord 服务器中的任意一个,将无法注册或登录(优先于上面的允许规则)。"})]}),n.jsxs("label",{className:"settings-form-wide",children:[n.jsx("span",{children:"登录成功跳转地址"}),n.jsx("input",{type:"url",value:r.authSuccessUrl,onChange:U=>C({...r,authSuccessUrl:U.target.value}),placeholder:"https://你的域名/"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"登录有效期(小时)"}),n.jsx("input",{type:"number",min:"1",max:"8760",value:r.sessionTtlHours,onChange:U=>C({...r,sessionTtlHours:Number(U.target.value)})})]})]}),n.jsxs("div",{className:"settings-save-row",children:[n.jsx("span",{role:"status",children:m}),n.jsx("button",{className:"primary-button",type:"submit",disabled:M,children:M?"保存中":"保存配置"})]})]}):n.jsx("div",{className:"empty",children:"正在读取配置"})})]})}function ol({label:u,value:f}){return n.jsxs("div",{className:"metric",children:[n.jsx("span",{children:u}),n.jsx("strong",{children:f})]})}function Kp({groups:u,onCreate:f,onUpdate:p,onDelete:r}){const[C,T]=b.useState(""),[k,W]=b.useState(""),[N,y]=b.useState(!1);async function G(_){if(_.preventDefault(),!!C.trim()){y(!0);try{await f({name:C.trim(),description:k.trim()}),T(""),W("")}finally{y(!1)}}}return n.jsxs("section",{className:"models-page",children:[n.jsxs(lt,{title:"用户分组",children:[n.jsxs("form",{className:"channel-create-form",onSubmit:G,children:[n.jsxs("div",{className:"channel-form-grid",children:[n.jsxs("label",{children:[n.jsx("span",{children:"分组名称"}),n.jsx("input",{value:C,onChange:_=>T(_.target.value),placeholder:"例如 尊享用户 / 试用用户"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"说明"}),n.jsx("input",{value:k,onChange:_=>W(_.target.value),placeholder:"可选"})]})]}),n.jsx("div",{className:"channel-card-actions",children:n.jsx("button",{className:"primary-button",type:"submit",disabled:N||!C.trim(),children:N?"创建中":"创建分组"})})]}),n.jsx("p",{className:"muted-inline",children:"分组用于控制渠道对用户的可见范围:把用户归入分组,并在渠道上勾选「可见分组」即可限制访问。未分组的用户只能使用未限制分组的渠道。"})]}),n.jsx(lt,{title:"全部分组",children:u.length===0?n.jsx(Lt,{text:"还没有分组"}):n.jsx("div",{className:"channels-stack",children:u.map(_=>n.jsx(Zp,{group:_,onUpdate:p,onDelete:r},_.id))})})]})}function Zp({group:u,onUpdate:f,onDelete:p}){const[r,C]=b.useState(!1),[T,k]=b.useState(u.name),[W,N]=b.useState(u.description),[y,G]=b.useState(!1);async function _(){G(!0);try{await f(u.id,{name:T.trim(),description:W.trim()}),C(!1)}finally{G(!1)}}return n.jsx("div",{className:"channel-card",children:n.jsxs("div",{className:"channel-card-head",children:[n.jsx("div",{children:r?n.jsxs("div",{className:"group-edit-fields",children:[n.jsx("input",{value:T,onChange:V=>k(V.target.value),placeholder:"分组名称"}),n.jsx("input",{value:W,onChange:V=>N(V.target.value),placeholder:"说明"})]}):n.jsxs(n.Fragment,{children:[n.jsx("strong",{children:u.name}),u.description&&n.jsx("span",{children:u.description}),n.jsxs("small",{children:["创建于 ",tl(u.createdAt)]})]})}),n.jsx("div",{className:"channel-card-head-actions",children:r?n.jsxs(n.Fragment,{children:[n.jsx("button",{className:"primary-button compact-button",onClick:_,disabled:y||!T.trim(),children:y?"保存中":"保存"}),n.jsx("button",{className:"secondary-button compact-button",onClick:()=>{k(u.name),N(u.description),C(!1)},children:"取消"})]}):n.jsxs(n.Fragment,{children:[n.jsx("button",{className:"secondary-button compact-button",onClick:()=>C(!0),children:"重命名"}),n.jsx("button",{className:"danger-button compact-button",onClick:()=>p(u.id),children:"删除"})]})})]})})}function lt({title:u,children:f}){return n.jsxs("section",{className:"panel",children:[n.jsx("div",{className:"panel-title",children:n.jsx("h2",{children:u})}),f]})}function _t({tone:u,children:f}){return n.jsx("span",{className:`badge tone-${u}`,children:f})}function el({label:u,value:f,switchOn:p}){return n.jsxs("div",{className:"setting",children:[n.jsx("span",{children:u}),n.jsxs("div",{className:"setting-value",children:[n.jsx("strong",{children:f}),typeof p=="boolean"&&n.jsx("div",{className:p?"ios-switch is-on":"ios-switch","aria-hidden":"true",children:n.jsx("span",{})})]})]})}function Vp({value:u,options:f,onChange:p}){return n.jsx("div",{className:"segmented-control",children:f.map(r=>n.jsx("button",{className:u===r.value?"selected":"",onClick:()=>p(r.value),children:r.label},r.value))})}function Lt({text:u}){return n.jsx("div",{className:"empty",children:u})}$0.createRoot(document.getElementById("root")).render(n.jsx(L0.StrictMode,{children:n.jsx(mp,{})})); diff --git a/dist/assets/index-D9XDLNLM.js b/dist/assets/index-D9XDLNLM.js new file mode 100644 index 0000000..7a1b6e6 --- /dev/null +++ b/dist/assets/index-D9XDLNLM.js @@ -0,0 +1,47 @@ +(function(){const f=document.createElement("link").relList;if(f&&f.supports&&f.supports("modulepreload"))return;for(const C of document.querySelectorAll('link[rel="modulepreload"]'))r(C);new MutationObserver(C=>{for(const E of C)if(E.type==="childList")for(const G of E.addedNodes)G.tagName==="LINK"&&G.rel==="modulepreload"&&r(G)}).observe(document,{childList:!0,subtree:!0});function h(C){const E={};return C.integrity&&(E.integrity=C.integrity),C.referrerPolicy&&(E.referrerPolicy=C.referrerPolicy),C.crossOrigin==="use-credentials"?E.credentials="include":C.crossOrigin==="anonymous"?E.credentials="omit":E.credentials="same-origin",E}function r(C){if(C.ep)return;C.ep=!0;const E=h(C);fetch(C.href,E)}})();function q0(u){return u&&u.__esModule&&Object.prototype.hasOwnProperty.call(u,"default")?u.default:u}var Qc={exports:{}},os={};var tm;function G0(){if(tm)return os;tm=1;var u=Symbol.for("react.transitional.element"),f=Symbol.for("react.fragment");function h(r,C,E){var G=null;if(E!==void 0&&(G=""+E),C.key!==void 0&&(G=""+C.key),"key"in C){E={};for(var F in C)F!=="key"&&(E[F]=C[F])}else E=C;return C=E.ref,{$$typeof:u,type:r,key:G,ref:C!==void 0?C:null,props:E}}return os.Fragment=f,os.jsx=h,os.jsxs=h,os}var lm;function k0(){return lm||(lm=1,Qc.exports=G0()),Qc.exports}var n=k0(),Xc={exports:{}},Ce={};var am;function Y0(){if(am)return Ce;am=1;var u=Symbol.for("react.transitional.element"),f=Symbol.for("react.portal"),h=Symbol.for("react.fragment"),r=Symbol.for("react.strict_mode"),C=Symbol.for("react.profiler"),E=Symbol.for("react.consumer"),G=Symbol.for("react.context"),F=Symbol.for("react.forward_ref"),N=Symbol.for("react.suspense"),y=Symbol.for("react.memo"),q=Symbol.for("react.lazy"),D=Symbol.for("react.activity"),V=Symbol.iterator;function ee(m){return m===null||typeof m!="object"?null:(m=V&&m[V]||m["@@iterator"],typeof m=="function"?m:null)}var he={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},te=Object.assign,pe={};function ie(m,v,U){this.props=m,this.context=v,this.refs=pe,this.updater=U||he}ie.prototype.isReactComponent={},ie.prototype.setState=function(m,v){if(typeof m!="object"&&typeof m!="function"&&m!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,m,v,"setState")},ie.prototype.forceUpdate=function(m){this.updater.enqueueForceUpdate(this,m,"forceUpdate")};function se(){}se.prototype=ie.prototype;function k(m,v,U){this.props=m,this.context=v,this.refs=pe,this.updater=U||he}var xe=k.prototype=new se;xe.constructor=k,te(xe,ie.prototype),xe.isPureReactComponent=!0;var le=Array.isArray;function je(){}var O={H:null,A:null,T:null,S:null},ne=Object.prototype.hasOwnProperty;function ge(m,v,U){var Z=U.ref;return{$$typeof:u,type:m,key:v,ref:Z!==void 0?Z:null,props:U}}function Te(m,v){return ge(m.type,v,m.props)}function ze(m){return typeof m=="object"&&m!==null&&m.$$typeof===u}function ue(m){var v={"=":"=0",":":"=2"};return"$"+m.replace(/[=:]/g,function(U){return v[U]})}var X=/\/+/g;function fe(m,v){return typeof m=="object"&&m!==null&&m.key!=null?ue(""+m.key):v.toString(36)}function W(m){switch(m.status){case"fulfilled":return m.value;case"rejected":throw m.reason;default:switch(typeof m.status=="string"?m.then(je,je):(m.status="pending",m.then(function(v){m.status==="pending"&&(m.status="fulfilled",m.value=v)},function(v){m.status==="pending"&&(m.status="rejected",m.reason=v)})),m.status){case"fulfilled":return m.value;case"rejected":throw m.reason}}throw m}function A(m,v,U,Z,oe){var Se=typeof m;(Se==="undefined"||Se==="boolean")&&(m=null);var _=!1;if(m===null)_=!0;else switch(Se){case"bigint":case"string":case"number":_=!0;break;case"object":switch(m.$$typeof){case u:case f:_=!0;break;case q:return _=m._init,A(_(m._payload),v,U,Z,oe)}}if(_)return oe=oe(m),_=Z===""?"."+fe(m,0):Z,le(oe)?(U="",_!=null&&(U=_.replace(X,"$&/")+"/"),A(oe,v,U,"",function(Ze){return Ze})):oe!=null&&(ze(oe)&&(oe=Te(oe,U+(oe.key==null||m&&m.key===oe.key?"":(""+oe.key).replace(X,"$&/")+"/")+_)),v.push(oe)),1;_=0;var P=Z===""?".":Z+":";if(le(m))for(var ae=0;ae>>1,ce=A[Q];if(0>>1;QC(U,H))ZC(oe,U)?(A[Q]=oe,A[Z]=H,Q=Z):(A[Q]=U,A[v]=H,Q=v);else if(ZC(oe,H))A[Q]=oe,A[Z]=H,Q=Z;else break e}}return L}function C(A,L){var H=A.sortIndex-L.sortIndex;return H!==0?H:A.id-L.id}if(u.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var E=performance;u.unstable_now=function(){return E.now()}}else{var G=Date,F=G.now();u.unstable_now=function(){return G.now()-F}}var N=[],y=[],q=1,D=null,V=3,ee=!1,he=!1,te=!1,pe=!1,ie=typeof setTimeout=="function"?setTimeout:null,se=typeof clearTimeout=="function"?clearTimeout:null,k=typeof setImmediate<"u"?setImmediate:null;function xe(A){for(var L=h(y);L!==null;){if(L.callback===null)r(y);else if(L.startTime<=A)r(y),L.sortIndex=L.expirationTime,f(N,L);else break;L=h(y)}}function le(A){if(te=!1,xe(A),!he)if(h(N)!==null)he=!0,je||(je=!0,ue());else{var L=h(y);L!==null&&W(le,L.startTime-A)}}var je=!1,O=-1,ne=5,ge=-1;function Te(){return pe?!0:!(u.unstable_now()-geA&&Te());){var Q=D.callback;if(typeof Q=="function"){D.callback=null,V=D.priorityLevel;var ce=Q(D.expirationTime<=A);if(A=u.unstable_now(),typeof ce=="function"){D.callback=ce,xe(A),L=!0;break t}D===h(N)&&r(N),xe(A)}else r(N);D=h(N)}if(D!==null)L=!0;else{var m=h(y);m!==null&&W(le,m.startTime-A),L=!1}}break e}finally{D=null,V=H,ee=!1}L=void 0}}finally{L?ue():je=!1}}}var ue;if(typeof k=="function")ue=function(){k(ze)};else if(typeof MessageChannel<"u"){var X=new MessageChannel,fe=X.port2;X.port1.onmessage=ze,ue=function(){fe.postMessage(null)}}else ue=function(){ie(ze,0)};function W(A,L){O=ie(function(){A(u.unstable_now())},L)}u.unstable_IdlePriority=5,u.unstable_ImmediatePriority=1,u.unstable_LowPriority=4,u.unstable_NormalPriority=3,u.unstable_Profiling=null,u.unstable_UserBlockingPriority=2,u.unstable_cancelCallback=function(A){A.callback=null},u.unstable_forceFrameRate=function(A){0>A||125Q?(A.sortIndex=H,f(y,A),h(N)===null&&A===h(y)&&(te?(se(O),O=-1):te=!0,W(le,H-Q))):(A.sortIndex=ce,f(N,A),he||ee||(he=!0,je||(je=!0,ue()))),A},u.unstable_shouldYield=Te,u.unstable_wrapCallback=function(A){var L=V;return function(){var H=V;V=L;try{return A.apply(this,arguments)}finally{V=H}}}})(Vc)),Vc}var im;function X0(){return im||(im=1,Zc.exports=Q0()),Zc.exports}var Jc={exports:{}},xt={};var um;function K0(){if(um)return xt;um=1;var u=eo();function f(N){var y="https://react.dev/errors/"+N;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(u)}catch(f){console.error(f)}}return u(),Jc.exports=K0(),Jc.exports}var om;function V0(){if(om)return rs;om=1;var u=X0(),f=eo(),h=Z0();function r(e){var t="https://react.dev/errors/"+e;if(1ce||(e.current=Q[ce],Q[ce]=null,ce--)}function U(e,t){ce++,Q[ce]=e.current,e.current=t}var Z=m(null),oe=m(null),Se=m(null),_=m(null);function P(e,t){switch(U(Se,t),U(oe,e),U(Z,null),t.nodeType){case 9:case 11:e=(e=t.documentElement)&&(e=e.namespaceURI)?Af(e):0;break;default:if(e=t.tagName,t=t.namespaceURI)t=Af(t),e=Cf(t,e);else switch(e){case"svg":e=1;break;case"math":e=2;break;default:e=0}}v(Z),U(Z,e)}function ae(){v(Z),v(oe),v(Se)}function Ze(e){e.memoizedState!==null&&U(_,e);var t=Z.current,l=Cf(t,e.type);t!==l&&(U(oe,e),U(Z,l))}function $(e){oe.current===e&&(v(Z),v(oe)),_.current===e&&(v(_),ss._currentValue=H)}var Je,$e;function We(e){if(Je===void 0)try{throw Error()}catch(l){var t=l.stack.trim().match(/\n( *(at )?)/);Je=t&&t[1]||"",$e=-1)":-1s||d[a]!==j[s]){var R=` +`+d[a].replace(" at new "," at ");return e.displayName&&R.includes("")&&(R=R.replace("",e.displayName)),R}while(1<=a&&0<=s);break}}}finally{ft=!1,Error.prepareStackTrace=l}return(l=e?e.displayName||e.name:"")?We(l):""}function At(e,t){switch(e.tag){case 26:case 27:case 5:return We(e.type);case 16:return We("Lazy");case 13:return e.child!==t&&t!==null?We("Suspense Fallback"):We("Suspense");case 19:return We("SuspenseList");case 0:case 15:return Nt(e.type,!1);case 11:return Nt(e.type.render,!1);case 1:return Nt(e.type,!0);case 31:return We("Activity");default:return""}}function il(e){try{var t="",l=null;do t+=At(e,l),l=e,e=e.return;while(e);return t}catch(a){return` +Error generating stack: `+a.message+` +`+a.stack}}var Kt=Object.prototype.hasOwnProperty,J=u.unstable_scheduleCallback,ot=u.unstable_cancelCallback,Zt=u.unstable_shouldYield,bl=u.unstable_requestPaint,mt=u.unstable_now,gn=u.unstable_getCurrentPriorityLevel,Oa=u.unstable_ImmediatePriority,_a=u.unstable_UserBlockingPriority,z=u.unstable_NormalPriority,Ae=u.unstable_LowPriority,Fe=u.unstable_IdlePriority,ht=u.log,T=u.unstable_setDisableYieldValue,Y=null,K=null;function I(e){if(typeof ht=="function"&&T(e),K&&typeof K.setStrictMode=="function")try{K.setStrictMode(Y,e)}catch{}}var ve=Math.clz32?Math.clz32:Ht,Ie=Math.log,wt=Math.LN2;function Ht(e){return e>>>=0,e===0?32:31-(Ie(e)/wt|0)|0}var ul=256,ml=262144,Da=4194304;function xl(e){var t=e&42;if(t!==0)return t;switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return e&261888;case 262144:case 524288:case 1048576:case 2097152:return e&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return e&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return e}}function kl(e,t,l){var a=e.pendingLanes;if(a===0)return 0;var s=0,i=e.suspendedLanes,c=e.pingedLanes;e=e.warmLanes;var o=a&134217727;return o!==0?(a=o&~i,a!==0?s=xl(a):(c&=o,c!==0?s=xl(c):l||(l=o&~e,l!==0&&(s=xl(l))))):(o=a&~i,o!==0?s=xl(o):c!==0?s=xl(c):l||(l=a&~e,l!==0&&(s=xl(l)))),s===0?0:t!==0&&t!==s&&(t&i)===0&&(i=s&-s,l=t&-t,i>=l||i===32&&(l&4194048)!==0)?t:s}function da(e,t){return(e.pendingLanes&~(e.suspendedLanes&~e.pingedLanes)&t)===0}function Cm(e,t){switch(e){case 1:case 2:case 4:case 8:case 64:return t+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return t+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function ao(){var e=Da;return Da<<=1,(Da&62914560)===0&&(Da=4194304),e}function _i(e){for(var t=[],l=0;31>l;l++)t.push(e);return t}function bn(e,t){e.pendingLanes|=t,t!==268435456&&(e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0)}function Tm(e,t,l,a,s,i){var c=e.pendingLanes;e.pendingLanes=l,e.suspendedLanes=0,e.pingedLanes=0,e.warmLanes=0,e.expiredLanes&=l,e.entangledLanes&=l,e.errorRecoveryDisabledLanes&=l,e.shellSuspendCounter=0;var o=e.entanglements,d=e.expirationTimes,j=e.hiddenUpdates;for(l=c&~l;0"u")return null;try{return e.activeElement||e.body}catch{return e.body}}var Dm=/[\n"\\]/g;function Jt(e){return e.replace(Dm,function(t){return"\\"+t.charCodeAt(0).toString(16)+" "})}function Bi(e,t,l,a,s,i,c,o){e.name="",c!=null&&typeof c!="function"&&typeof c!="symbol"&&typeof c!="boolean"?e.type=c:e.removeAttribute("type"),t!=null?c==="number"?(t===0&&e.value===""||e.value!=t)&&(e.value=""+Vt(t)):e.value!==""+Vt(t)&&(e.value=""+Vt(t)):c!=="submit"&&c!=="reset"||e.removeAttribute("value"),t!=null?qi(e,c,Vt(t)):l!=null?qi(e,c,Vt(l)):a!=null&&e.removeAttribute("value"),s==null&&i!=null&&(e.defaultChecked=!!i),s!=null&&(e.checked=s&&typeof s!="function"&&typeof s!="symbol"),o!=null&&typeof o!="function"&&typeof o!="symbol"&&typeof o!="boolean"?e.name=""+Vt(o):e.removeAttribute("name")}function yo(e,t,l,a,s,i,c,o){if(i!=null&&typeof i!="function"&&typeof i!="symbol"&&typeof i!="boolean"&&(e.type=i),t!=null||l!=null){if(!(i!=="submit"&&i!=="reset"||t!=null)){Hi(e);return}l=l!=null?""+Vt(l):"",t=t!=null?""+Vt(t):l,o||t===e.value||(e.value=t),e.defaultValue=t}a=a??s,a=typeof a!="function"&&typeof a!="symbol"&&!!a,e.checked=o?e.checked:!!a,e.defaultChecked=!!a,c!=null&&typeof c!="function"&&typeof c!="symbol"&&typeof c!="boolean"&&(e.name=c),Hi(e)}function qi(e,t,l){t==="number"&&hs(e.ownerDocument)===e||e.defaultValue===""+l||(e.defaultValue=""+l)}function qa(e,t,l,a){if(e=e.options,t){t={};for(var s=0;s"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Qi=!1;if(Nl)try{var Nn={};Object.defineProperty(Nn,"passive",{get:function(){Qi=!0}}),window.addEventListener("test",Nn,Nn),window.removeEventListener("test",Nn,Nn)}catch{Qi=!1}var Ll=null,Xi=null,vs=null;function Ao(){if(vs)return vs;var e,t=Xi,l=t.length,a,s="value"in Ll?Ll.value:Ll.textContent,i=s.length;for(e=0;e=Tn),Oo=" ",_o=!1;function Do(e,t){switch(e){case"keyup":return ih.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function Uo(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var La=!1;function ch(e,t){switch(e){case"compositionend":return Uo(t);case"keypress":return t.which!==32?null:(_o=!0,Oo);case"textInput":return e=t.data,e===Oo&&_o?null:e;default:return null}}function oh(e,t){if(La)return e==="compositionend"||!$i&&Do(e,t)?(e=Ao(),vs=Xi=Ll=null,La=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:l,offset:t-e};e=a}e:{for(;l;){if(l.nextSibling){l=l.nextSibling;break e}l=l.parentNode}l=void 0}l=Yo(l)}}function Qo(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?Qo(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function Xo(e){e=e!=null&&e.ownerDocument!=null&&e.ownerDocument.defaultView!=null?e.ownerDocument.defaultView:window;for(var t=hs(e.document);t instanceof e.HTMLIFrameElement;){try{var l=typeof t.contentWindow.location.href=="string"}catch{l=!1}if(l)e=t.contentWindow;else break;t=hs(e.document)}return t}function Ii(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}var yh=Nl&&"documentMode"in document&&11>=document.documentMode,Qa=null,Pi=null,On=null,eu=!1;function Ko(e,t,l){var a=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;eu||Qa==null||Qa!==hs(a)||(a=Qa,"selectionStart"in a&&Ii(a)?a={start:a.selectionStart,end:a.selectionEnd}:(a=(a.ownerDocument&&a.ownerDocument.defaultView||window).getSelection(),a={anchorNode:a.anchorNode,anchorOffset:a.anchorOffset,focusNode:a.focusNode,focusOffset:a.focusOffset}),On&&zn(On,a)||(On=a,a=oi(Pi,"onSelect"),0>=c,s-=c,hl=1<<32-ve(t)+s|l<Me?(Ue=de,de=null):Ue=de.sibling;var He=S(g,de,x[Me],w);if(He===null){de===null&&(de=Ue);break}e&&de&&He.alternate===null&&t(g,de),p=i(He,p,Me),we===null?be=He:we.sibling=He,we=He,de=Ue}if(Me===x.length)return l(g,de),Re&&Cl(g,Me),be;if(de===null){for(;MeMe?(Ue=de,de=null):Ue=de.sibling;var ra=S(g,de,He.value,w);if(ra===null){de===null&&(de=Ue);break}e&&de&&ra.alternate===null&&t(g,de),p=i(ra,p,Me),we===null?be=ra:we.sibling=ra,we=ra,de=Ue}if(He.done)return l(g,de),Re&&Cl(g,Me),be;if(de===null){for(;!He.done;Me++,He=x.next())He=B(g,He.value,w),He!==null&&(p=i(He,p,Me),we===null?be=He:we.sibling=He,we=He);return Re&&Cl(g,Me),be}for(de=a(de);!He.done;Me++,He=x.next())He=M(de,g,Me,He.value,w),He!==null&&(e&&He.alternate!==null&&de.delete(He.key===null?Me:He.key),p=i(He,p,Me),we===null?be=He:we.sibling=He,we=He);return e&&de.forEach(function(B0){return t(g,B0)}),Re&&Cl(g,Me),be}function Le(g,p,x,w){if(typeof x=="object"&&x!==null&&x.type===te&&x.key===null&&(x=x.props.children),typeof x=="object"&&x!==null){switch(x.$$typeof){case ee:e:{for(var be=x.key;p!==null;){if(p.key===be){if(be=x.type,be===te){if(p.tag===7){l(g,p.sibling),w=s(p,x.props.children),w.return=g,g=w;break e}}else if(p.elementType===be||typeof be=="object"&&be!==null&&be.$$typeof===ne&&Sa(be)===p.type){l(g,p.sibling),w=s(p,x.props),Hn(w,x),w.return=g,g=w;break e}l(g,p);break}else t(g,p);p=p.sibling}x.type===te?(w=ya(x.props.children,g.mode,w,x.key),w.return=g,g=w):(w=Ts(x.type,x.key,x.props,null,g.mode,w),Hn(w,x),w.return=g,g=w)}return c(g);case he:e:{for(be=x.key;p!==null;){if(p.key===be)if(p.tag===4&&p.stateNode.containerInfo===x.containerInfo&&p.stateNode.implementation===x.implementation){l(g,p.sibling),w=s(p,x.children||[]),w.return=g,g=w;break e}else{l(g,p);break}else t(g,p);p=p.sibling}w=uu(x,g.mode,w),w.return=g,g=w}return c(g);case ne:return x=Sa(x),Le(g,p,x,w)}if(W(x))return re(g,p,x,w);if(ue(x)){if(be=ue(x),typeof be!="function")throw Error(r(150));return x=be.call(x),Ne(g,p,x,w)}if(typeof x.then=="function")return Le(g,p,Us(x),w);if(x.$$typeof===k)return Le(g,p,zs(g,x),w);Rs(g,x)}return typeof x=="string"&&x!==""||typeof x=="number"||typeof x=="bigint"?(x=""+x,p!==null&&p.tag===6?(l(g,p.sibling),w=s(p,x),w.return=g,g=w):(l(g,p),w=iu(x,g.mode,w),w.return=g,g=w),c(g)):l(g,p)}return function(g,p,x,w){try{wn=0;var be=Le(g,p,x,w);return en=null,be}catch(de){if(de===Pa||de===_s)throw de;var we=qt(29,de,null,g.mode);return we.lanes=w,we.return=g,we}}}var Aa=hr(!0),pr=hr(!1),Vl=!1;function bu(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function xu(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,callbacks:null})}function Jl(e){return{lane:e,tag:0,payload:null,callback:null,next:null}}function $l(e,t,l){var a=e.updateQueue;if(a===null)return null;if(a=a.shared,(Be&2)!==0){var s=a.pending;return s===null?t.next=t:(t.next=s.next,s.next=t),a.pending=t,t=Cs(e),Io(e,null,l),t}return As(e,a,t,l),Cs(e)}function Bn(e,t,l){if(t=t.updateQueue,t!==null&&(t=t.shared,(l&4194048)!==0)){var a=t.lanes;a&=e.pendingLanes,l|=a,t.lanes=l,so(e,l)}}function ju(e,t){var l=e.updateQueue,a=e.alternate;if(a!==null&&(a=a.updateQueue,l===a)){var s=null,i=null;if(l=l.firstBaseUpdate,l!==null){do{var c={lane:l.lane,tag:l.tag,payload:l.payload,callback:null,next:null};i===null?s=i=c:i=i.next=c,l=l.next}while(l!==null);i===null?s=i=t:i=i.next=t}else s=i=t;l={baseState:a.baseState,firstBaseUpdate:s,lastBaseUpdate:i,shared:a.shared,callbacks:a.callbacks},e.updateQueue=l;return}e=l.lastBaseUpdate,e===null?l.firstBaseUpdate=t:e.next=t,l.lastBaseUpdate=t}var Su=!1;function qn(){if(Su){var e=Ia;if(e!==null)throw e}}function Gn(e,t,l,a){Su=!1;var s=e.updateQueue;Vl=!1;var i=s.firstBaseUpdate,c=s.lastBaseUpdate,o=s.shared.pending;if(o!==null){s.shared.pending=null;var d=o,j=d.next;d.next=null,c===null?i=j:c.next=j,c=d;var R=e.alternate;R!==null&&(R=R.updateQueue,o=R.lastBaseUpdate,o!==c&&(o===null?R.firstBaseUpdate=j:o.next=j,R.lastBaseUpdate=d))}if(i!==null){var B=s.baseState;c=0,R=j=d=null,o=i;do{var S=o.lane&-536870913,M=S!==o.lane;if(M?(De&S)===S:(a&S)===S){S!==0&&S===Fa&&(Su=!0),R!==null&&(R=R.next={lane:0,tag:o.tag,payload:o.payload,callback:null,next:null});e:{var re=e,Ne=o;S=t;var Le=l;switch(Ne.tag){case 1:if(re=Ne.payload,typeof re=="function"){B=re.call(Le,B,S);break e}B=re;break e;case 3:re.flags=re.flags&-65537|128;case 0:if(re=Ne.payload,S=typeof re=="function"?re.call(Le,B,S):re,S==null)break e;B=D({},B,S);break e;case 2:Vl=!0}}S=o.callback,S!==null&&(e.flags|=64,M&&(e.flags|=8192),M=s.callbacks,M===null?s.callbacks=[S]:M.push(S))}else M={lane:S,tag:o.tag,payload:o.payload,callback:o.callback,next:null},R===null?(j=R=M,d=B):R=R.next=M,c|=S;if(o=o.next,o===null){if(o=s.shared.pending,o===null)break;M=o,o=M.next,M.next=null,s.lastBaseUpdate=M,s.shared.pending=null}}while(!0);R===null&&(d=B),s.baseState=d,s.firstBaseUpdate=j,s.lastBaseUpdate=R,i===null&&(s.shared.lanes=0),ea|=c,e.lanes=c,e.memoizedState=B}}function vr(e,t){if(typeof e!="function")throw Error(r(191,e));e.call(t)}function yr(e,t){var l=e.callbacks;if(l!==null)for(e.callbacks=null,e=0;ei?i:8;var c=A.T,o={};A.T=o,Yu(e,!1,t,l);try{var d=s(),j=A.S;if(j!==null&&j(o,d),d!==null&&typeof d=="object"&&typeof d.then=="function"){var R=Th(d,a);Ln(e,t,R,Qt(e))}else Ln(e,t,a,Qt(e))}catch(B){Ln(e,t,{then:function(){},status:"rejected",reason:B},Qt())}finally{L.p=i,c!==null&&o.types!==null&&(c.types=o.types),A.T=c}}function Dh(){}function Gu(e,t,l,a){if(e.tag!==5)throw Error(r(476));var s=$r(e).queue;Jr(e,s,t,H,l===null?Dh:function(){return Wr(e),l(a)})}function $r(e){var t=e.memoizedState;if(t!==null)return t;t={memoizedState:H,baseState:H,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:zl,lastRenderedState:H},next:null};var l={};return t.next={memoizedState:l,baseState:l,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:zl,lastRenderedState:l},next:null},e.memoizedState=t,e=e.alternate,e!==null&&(e.memoizedState=t),t}function Wr(e){var t=$r(e);t.next===null&&(t=e.alternate.memoizedState),Ln(e,t.next.queue,{},Qt())}function ku(){return yt(ss)}function Fr(){return at().memoizedState}function Ir(){return at().memoizedState}function Uh(e){for(var t=e.return;t!==null;){switch(t.tag){case 24:case 3:var l=Qt();e=Jl(l);var a=$l(t,e,l);a!==null&&(_t(a,t,l),Bn(a,t,l)),t={cache:pu()},e.payload=t;return}t=t.return}}function Rh(e,t,l){var a=Qt();l={lane:a,revertLane:0,gesture:null,action:l,hasEagerState:!1,eagerState:null,next:null},Xs(e)?ed(t,l):(l=nu(e,t,l,a),l!==null&&(_t(l,e,a),td(l,t,a)))}function Pr(e,t,l){var a=Qt();Ln(e,t,l,a)}function Ln(e,t,l,a){var s={lane:a,revertLane:0,gesture:null,action:l,hasEagerState:!1,eagerState:null,next:null};if(Xs(e))ed(t,s);else{var i=e.alternate;if(e.lanes===0&&(i===null||i.lanes===0)&&(i=t.lastRenderedReducer,i!==null))try{var c=t.lastRenderedState,o=i(c,l);if(s.hasEagerState=!0,s.eagerState=o,Bt(o,c))return As(e,t,s,0),Qe===null&&Ns(),!1}catch{}if(l=nu(e,t,s,a),l!==null)return _t(l,e,a),td(l,t,a),!0}return!1}function Yu(e,t,l,a){if(a={lane:2,revertLane:bc(),gesture:null,action:a,hasEagerState:!1,eagerState:null,next:null},Xs(e)){if(t)throw Error(r(479))}else t=nu(e,l,a,2),t!==null&&_t(t,e,2)}function Xs(e){var t=e.alternate;return e===Ee||t!==null&&t===Ee}function ed(e,t){ln=Bs=!0;var l=e.pending;l===null?t.next=t:(t.next=l.next,l.next=t),e.pending=t}function td(e,t,l){if((l&4194048)!==0){var a=t.lanes;a&=e.pendingLanes,l|=a,t.lanes=l,so(e,l)}}var Qn={readContext:yt,use:ks,useCallback:Pe,useContext:Pe,useEffect:Pe,useImperativeHandle:Pe,useLayoutEffect:Pe,useInsertionEffect:Pe,useMemo:Pe,useReducer:Pe,useRef:Pe,useState:Pe,useDebugValue:Pe,useDeferredValue:Pe,useTransition:Pe,useSyncExternalStore:Pe,useId:Pe,useHostTransitionStatus:Pe,useFormState:Pe,useActionState:Pe,useOptimistic:Pe,useMemoCache:Pe,useCacheRefresh:Pe};Qn.useEffectEvent=Pe;var ld={readContext:yt,use:ks,useCallback:function(e,t){return jt().memoizedState=[e,t===void 0?null:t],e},useContext:yt,useEffect:Gr,useImperativeHandle:function(e,t,l){l=l!=null?l.concat([e]):null,Ls(4194308,4,Qr.bind(null,t,e),l)},useLayoutEffect:function(e,t){return Ls(4194308,4,e,t)},useInsertionEffect:function(e,t){Ls(4,2,e,t)},useMemo:function(e,t){var l=jt();t=t===void 0?null:t;var a=e();if(Ca){I(!0);try{e()}finally{I(!1)}}return l.memoizedState=[a,t],a},useReducer:function(e,t,l){var a=jt();if(l!==void 0){var s=l(t);if(Ca){I(!0);try{l(t)}finally{I(!1)}}}else s=t;return a.memoizedState=a.baseState=s,e={pending:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:s},a.queue=e,e=e.dispatch=Rh.bind(null,Ee,e),[a.memoizedState,e]},useRef:function(e){var t=jt();return e={current:e},t.memoizedState=e},useState:function(e){e=Ru(e);var t=e.queue,l=Pr.bind(null,Ee,t);return t.dispatch=l,[e.memoizedState,l]},useDebugValue:Bu,useDeferredValue:function(e,t){var l=jt();return qu(l,e,t)},useTransition:function(){var e=Ru(!1);return e=Jr.bind(null,Ee,e.queue,!0,!1),jt().memoizedState=e,[!1,e]},useSyncExternalStore:function(e,t,l){var a=Ee,s=jt();if(Re){if(l===void 0)throw Error(r(407));l=l()}else{if(l=t(),Qe===null)throw Error(r(349));(De&127)!==0||Nr(a,t,l)}s.memoizedState=l;var i={value:l,getSnapshot:t};return s.queue=i,Gr(Cr.bind(null,a,i,e),[e]),a.flags|=2048,nn(9,{destroy:void 0},Ar.bind(null,a,i,l,t),null),l},useId:function(){var e=jt(),t=Qe.identifierPrefix;if(Re){var l=pl,a=hl;l=(a&~(1<<32-ve(a)-1)).toString(32)+l,t="_"+t+"R_"+l,l=qs++,0<\/script>",i=i.removeChild(i.firstChild);break;case"select":i=typeof a.is=="string"?c.createElement("select",{is:a.is}):c.createElement("select"),a.multiple?i.multiple=!0:a.size&&(i.size=a.size);break;default:i=typeof a.is=="string"?c.createElement(s,{is:a.is}):c.createElement(s)}}i[pt]=t,i[Ct]=a;e:for(c=t.child;c!==null;){if(c.tag===5||c.tag===6)i.appendChild(c.stateNode);else if(c.tag!==4&&c.tag!==27&&c.child!==null){c.child.return=c,c=c.child;continue}if(c===t)break e;for(;c.sibling===null;){if(c.return===null||c.return===t)break e;c=c.return}c.sibling.return=c.return,c=c.sibling}t.stateNode=i;e:switch(bt(i,s,a),s){case"button":case"input":case"select":case"textarea":a=!!a.autoFocus;break e;case"img":a=!0;break e;default:a=!1}a&&_l(t)}}return Ke(t),tc(t,t.type,e===null?null:e.memoizedProps,t.pendingProps,l),null;case 6:if(e&&t.stateNode!=null)e.memoizedProps!==a&&_l(t);else{if(typeof a!="string"&&t.stateNode===null)throw Error(r(166));if(e=Se.current,$a(t)){if(e=t.stateNode,l=t.memoizedProps,a=null,s=vt,s!==null)switch(s.tag){case 27:case 5:a=s.memoizedProps}e[pt]=t,e=!!(e.nodeValue===l||a!==null&&a.suppressHydrationWarning===!0||Sf(e.nodeValue,l)),e||Kl(t,!0)}else e=ri(e).createTextNode(a),e[pt]=t,t.stateNode=e}return Ke(t),null;case 31:if(l=t.memoizedState,e===null||e.memoizedState!==null){if(a=$a(t),l!==null){if(e===null){if(!a)throw Error(r(318));if(e=t.memoizedState,e=e!==null?e.dehydrated:null,!e)throw Error(r(557));e[pt]=t}else ga(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;Ke(t),e=!1}else l=du(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=l),e=!0;if(!e)return t.flags&256?(kt(t),t):(kt(t),null);if((t.flags&128)!==0)throw Error(r(558))}return Ke(t),null;case 13:if(a=t.memoizedState,e===null||e.memoizedState!==null&&e.memoizedState.dehydrated!==null){if(s=$a(t),a!==null&&a.dehydrated!==null){if(e===null){if(!s)throw Error(r(318));if(s=t.memoizedState,s=s!==null?s.dehydrated:null,!s)throw Error(r(317));s[pt]=t}else ga(),(t.flags&128)===0&&(t.memoizedState=null),t.flags|=4;Ke(t),s=!1}else s=du(),e!==null&&e.memoizedState!==null&&(e.memoizedState.hydrationErrors=s),s=!0;if(!s)return t.flags&256?(kt(t),t):(kt(t),null)}return kt(t),(t.flags&128)!==0?(t.lanes=l,t):(l=a!==null,e=e!==null&&e.memoizedState!==null,l&&(a=t.child,s=null,a.alternate!==null&&a.alternate.memoizedState!==null&&a.alternate.memoizedState.cachePool!==null&&(s=a.alternate.memoizedState.cachePool.pool),i=null,a.memoizedState!==null&&a.memoizedState.cachePool!==null&&(i=a.memoizedState.cachePool.pool),i!==s&&(a.flags|=2048)),l!==e&&l&&(t.child.flags|=8192),$s(t,t.updateQueue),Ke(t),null);case 4:return ae(),e===null&&Nc(t.stateNode.containerInfo),Ke(t),null;case 10:return El(t.type),Ke(t),null;case 19:if(v(lt),a=t.memoizedState,a===null)return Ke(t),null;if(s=(t.flags&128)!==0,i=a.rendering,i===null)if(s)Kn(a,!1);else{if(et!==0||e!==null&&(e.flags&128)!==0)for(e=t.child;e!==null;){if(i=Hs(e),i!==null){for(t.flags|=128,Kn(a,!1),e=i.updateQueue,t.updateQueue=e,$s(t,e),t.subtreeFlags=0,e=l,l=t.child;l!==null;)Po(l,e),l=l.sibling;return U(lt,lt.current&1|2),Re&&Cl(t,a.treeForkCount),t.child}e=e.sibling}a.tail!==null&&mt()>ei&&(t.flags|=128,s=!0,Kn(a,!1),t.lanes=4194304)}else{if(!s)if(e=Hs(i),e!==null){if(t.flags|=128,s=!0,e=e.updateQueue,t.updateQueue=e,$s(t,e),Kn(a,!0),a.tail===null&&a.tailMode==="hidden"&&!i.alternate&&!Re)return Ke(t),null}else 2*mt()-a.renderingStartTime>ei&&l!==536870912&&(t.flags|=128,s=!0,Kn(a,!1),t.lanes=4194304);a.isBackwards?(i.sibling=t.child,t.child=i):(e=a.last,e!==null?e.sibling=i:t.child=i,a.last=i)}return a.tail!==null?(e=a.tail,a.rendering=e,a.tail=e.sibling,a.renderingStartTime=mt(),e.sibling=null,l=lt.current,U(lt,s?l&1|2:l&1),Re&&Cl(t,a.treeForkCount),e):(Ke(t),null);case 22:case 23:return kt(t),Au(),a=t.memoizedState!==null,e!==null?e.memoizedState!==null!==a&&(t.flags|=8192):a&&(t.flags|=8192),a?(l&536870912)!==0&&(t.flags&128)===0&&(Ke(t),t.subtreeFlags&6&&(t.flags|=8192)):Ke(t),l=t.updateQueue,l!==null&&$s(t,l.retryQueue),l=null,e!==null&&e.memoizedState!==null&&e.memoizedState.cachePool!==null&&(l=e.memoizedState.cachePool.pool),a=null,t.memoizedState!==null&&t.memoizedState.cachePool!==null&&(a=t.memoizedState.cachePool.pool),a!==l&&(t.flags|=2048),e!==null&&v(ja),null;case 24:return l=null,e!==null&&(l=e.memoizedState.cache),t.memoizedState.cache!==l&&(t.flags|=2048),El(nt),Ke(t),null;case 25:return null;case 30:return null}throw Error(r(156,t.tag))}function Gh(e,t){switch(ou(t),t.tag){case 1:return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return El(nt),ae(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 26:case 27:case 5:return $(t),null;case 31:if(t.memoizedState!==null){if(kt(t),t.alternate===null)throw Error(r(340));ga()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 13:if(kt(t),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(r(340));ga()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return v(lt),null;case 4:return ae(),null;case 10:return El(t.type),null;case 22:case 23:return kt(t),Au(),e!==null&&v(ja),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 24:return El(nt),null;case 25:return null;default:return null}}function Td(e,t){switch(ou(t),t.tag){case 3:El(nt),ae();break;case 26:case 27:case 5:$(t);break;case 4:ae();break;case 31:t.memoizedState!==null&&kt(t);break;case 13:kt(t);break;case 19:v(lt);break;case 10:El(t.type);break;case 22:case 23:kt(t),Au(),e!==null&&v(ja);break;case 24:El(nt)}}function Zn(e,t){try{var l=t.updateQueue,a=l!==null?l.lastEffect:null;if(a!==null){var s=a.next;l=s;do{if((l.tag&e)===e){a=void 0;var i=l.create,c=l.inst;a=i(),c.destroy=a}l=l.next}while(l!==s)}}catch(o){Ge(t,t.return,o)}}function Il(e,t,l){try{var a=t.updateQueue,s=a!==null?a.lastEffect:null;if(s!==null){var i=s.next;a=i;do{if((a.tag&e)===e){var c=a.inst,o=c.destroy;if(o!==void 0){c.destroy=void 0,s=t;var d=l,j=o;try{j()}catch(R){Ge(s,d,R)}}}a=a.next}while(a!==i)}}catch(R){Ge(t,t.return,R)}}function Ed(e){var t=e.updateQueue;if(t!==null){var l=e.stateNode;try{yr(t,l)}catch(a){Ge(e,e.return,a)}}}function Md(e,t,l){l.props=Ta(e.type,e.memoizedProps),l.state=e.memoizedState;try{l.componentWillUnmount()}catch(a){Ge(e,t,a)}}function Vn(e,t){try{var l=e.ref;if(l!==null){switch(e.tag){case 26:case 27:case 5:var a=e.stateNode;break;case 30:a=e.stateNode;break;default:a=e.stateNode}typeof l=="function"?e.refCleanup=l(a):l.current=a}}catch(s){Ge(e,t,s)}}function vl(e,t){var l=e.ref,a=e.refCleanup;if(l!==null)if(typeof a=="function")try{a()}catch(s){Ge(e,t,s)}finally{e.refCleanup=null,e=e.alternate,e!=null&&(e.refCleanup=null)}else if(typeof l=="function")try{l(null)}catch(s){Ge(e,t,s)}else l.current=null}function zd(e){var t=e.type,l=e.memoizedProps,a=e.stateNode;try{e:switch(t){case"button":case"input":case"select":case"textarea":l.autoFocus&&a.focus();break e;case"img":l.src?a.src=l.src:l.srcSet&&(a.srcset=l.srcSet)}}catch(s){Ge(e,e.return,s)}}function lc(e,t,l){try{var a=e.stateNode;u0(a,e.type,l,t),a[Ct]=t}catch(s){Ge(e,e.return,s)}}function Od(e){return e.tag===5||e.tag===3||e.tag===26||e.tag===27&&sa(e.type)||e.tag===4}function ac(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||Od(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.tag===27&&sa(e.type)||e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function nc(e,t,l){var a=e.tag;if(a===5||a===6)e=e.stateNode,t?(l.nodeType===9?l.body:l.nodeName==="HTML"?l.ownerDocument.body:l).insertBefore(e,t):(t=l.nodeType===9?l.body:l.nodeName==="HTML"?l.ownerDocument.body:l,t.appendChild(e),l=l._reactRootContainer,l!=null||t.onclick!==null||(t.onclick=Sl));else if(a!==4&&(a===27&&sa(e.type)&&(l=e.stateNode,t=null),e=e.child,e!==null))for(nc(e,t,l),e=e.sibling;e!==null;)nc(e,t,l),e=e.sibling}function Ws(e,t,l){var a=e.tag;if(a===5||a===6)e=e.stateNode,t?l.insertBefore(e,t):l.appendChild(e);else if(a!==4&&(a===27&&sa(e.type)&&(l=e.stateNode),e=e.child,e!==null))for(Ws(e,t,l),e=e.sibling;e!==null;)Ws(e,t,l),e=e.sibling}function _d(e){var t=e.stateNode,l=e.memoizedProps;try{for(var a=e.type,s=t.attributes;s.length;)t.removeAttributeNode(s[0]);bt(t,a,l),t[pt]=e,t[Ct]=l}catch(i){Ge(e,e.return,i)}}var Dl=!1,ut=!1,sc=!1,Dd=typeof WeakSet=="function"?WeakSet:Set,dt=null;function kh(e,t){if(e=e.containerInfo,Tc=yi,e=Xo(e),Ii(e)){if("selectionStart"in e)var l={start:e.selectionStart,end:e.selectionEnd};else e:{l=(l=e.ownerDocument)&&l.defaultView||window;var a=l.getSelection&&l.getSelection();if(a&&a.rangeCount!==0){l=a.anchorNode;var s=a.anchorOffset,i=a.focusNode;a=a.focusOffset;try{l.nodeType,i.nodeType}catch{l=null;break e}var c=0,o=-1,d=-1,j=0,R=0,B=e,S=null;t:for(;;){for(var M;B!==l||s!==0&&B.nodeType!==3||(o=c+s),B!==i||a!==0&&B.nodeType!==3||(d=c+a),B.nodeType===3&&(c+=B.nodeValue.length),(M=B.firstChild)!==null;)S=B,B=M;for(;;){if(B===e)break t;if(S===l&&++j===s&&(o=c),S===i&&++R===a&&(d=c),(M=B.nextSibling)!==null)break;B=S,S=B.parentNode}B=M}l=o===-1||d===-1?null:{start:o,end:d}}else l=null}l=l||{start:0,end:0}}else l=null;for(Ec={focusedElem:e,selectionRange:l},yi=!1,dt=t;dt!==null;)if(t=dt,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,dt=e;else for(;dt!==null;){switch(t=dt,i=t.alternate,e=t.flags,t.tag){case 0:if((e&4)!==0&&(e=t.updateQueue,e=e!==null?e.events:null,e!==null))for(l=0;l title"))),bt(i,a,l),i[pt]=e,rt(i),a=i;break e;case"link":var c=Gf("link","href",s).get(a+(l.href||""));if(c){for(var o=0;oLe&&(c=Le,Le=Ne,Ne=c);var g=Lo(o,Ne),p=Lo(o,Le);if(g&&p&&(M.rangeCount!==1||M.anchorNode!==g.node||M.anchorOffset!==g.offset||M.focusNode!==p.node||M.focusOffset!==p.offset)){var x=B.createRange();x.setStart(g.node,g.offset),M.removeAllRanges(),Ne>Le?(M.addRange(x),M.extend(p.node,p.offset)):(x.setEnd(p.node,p.offset),M.addRange(x))}}}}for(B=[],M=o;M=M.parentNode;)M.nodeType===1&&B.push({element:M,left:M.scrollLeft,top:M.scrollTop});for(typeof o.focus=="function"&&o.focus(),o=0;ol?32:l,A.T=null,l=fc,fc=null;var i=la,c=Bl;if(ct=0,rn=la=null,Bl=0,(Be&6)!==0)throw Error(r(331));var o=Be;if(Be|=4,Qd(i.current),kd(i,i.current,c,l),Be=o,Pn(0,!1),K&&typeof K.onPostCommitFiberRoot=="function")try{K.onPostCommitFiberRoot(Y,i)}catch{}return!0}finally{L.p=s,A.T=a,cf(e,t)}}function rf(e,t,l){t=Wt(l,t),t=Ku(e.stateNode,t,2),e=$l(e,t,2),e!==null&&(bn(e,2),yl(e))}function Ge(e,t,l){if(e.tag===3)rf(e,e,l);else for(;t!==null;){if(t.tag===3){rf(t,e,l);break}else if(t.tag===1){var a=t.stateNode;if(typeof t.type.getDerivedStateFromError=="function"||typeof a.componentDidCatch=="function"&&(ta===null||!ta.has(a))){e=Wt(l,e),l=rd(2),a=$l(t,l,2),a!==null&&(dd(l,a,t,e),bn(a,2),yl(a));break}}t=t.return}}function vc(e,t,l){var a=e.pingCache;if(a===null){a=e.pingCache=new Qh;var s=new Set;a.set(t,s)}else s=a.get(t),s===void 0&&(s=new Set,a.set(t,s));s.has(l)||(cc=!0,s.add(l),e=Jh.bind(null,e,t,l),t.then(e,e))}function Jh(e,t,l){var a=e.pingCache;a!==null&&a.delete(t),e.pingedLanes|=e.suspendedLanes&l,e.warmLanes&=~l,Qe===e&&(De&l)===l&&(et===4||et===3&&(De&62914560)===De&&300>mt()-Ps?(Be&2)===0&&dn(e,0):oc|=l,on===De&&(on=0)),yl(e)}function df(e,t){t===0&&(t=ao()),e=va(e,t),e!==null&&(bn(e,t),yl(e))}function $h(e){var t=e.memoizedState,l=0;t!==null&&(l=t.retryLane),df(e,l)}function Wh(e,t){var l=0;switch(e.tag){case 31:case 13:var a=e.stateNode,s=e.memoizedState;s!==null&&(l=s.retryLane);break;case 19:a=e.stateNode;break;case 22:a=e.stateNode._retryCache;break;default:throw Error(r(314))}a!==null&&a.delete(t),df(e,l)}function Fh(e,t){return J(e,t)}var ii=null,mn=null,yc=!1,ui=!1,gc=!1,na=0;function yl(e){e!==mn&&e.next===null&&(mn===null?ii=mn=e:mn=mn.next=e),ui=!0,yc||(yc=!0,Ph())}function Pn(e,t){if(!gc&&ui){gc=!0;do for(var l=!1,a=ii;a!==null;){if(e!==0){var s=a.pendingLanes;if(s===0)var i=0;else{var c=a.suspendedLanes,o=a.pingedLanes;i=(1<<31-ve(42|e)+1)-1,i&=s&~(c&~o),i=i&201326741?i&201326741|1:i?i|2:0}i!==0&&(l=!0,pf(a,i))}else i=De,i=kl(a,a===Qe?i:0,a.cancelPendingCommit!==null||a.timeoutHandle!==-1),(i&3)===0||da(a,i)||(l=!0,pf(a,i));a=a.next}while(l);gc=!1}}function Ih(){ff()}function ff(){ui=yc=!1;var e=0;na!==0&&o0()&&(e=na);for(var t=mt(),l=null,a=ii;a!==null;){var s=a.next,i=mf(a,t);i===0?(a.next=null,l===null?ii=s:l.next=s,s===null&&(mn=l)):(l=a,(e!==0||(i&3)!==0)&&(ui=!0)),a=s}ct!==0&&ct!==5||Pn(e),na!==0&&(na=0)}function mf(e,t){for(var l=e.suspendedLanes,a=e.pingedLanes,s=e.expirationTimes,i=e.pendingLanes&-62914561;0o)break;var R=d.transferSize,B=d.initiatorType;R&&Nf(B)&&(d=d.responseEnd,c+=R*(d"u"?null:document;function wf(e,t,l){var a=hn;if(a&&typeof t=="string"&&t){var s=Jt(t);s='link[rel="'+e+'"][href="'+s+'"]',typeof l=="string"&&(s+='[crossorigin="'+l+'"]'),Rf.has(s)||(Rf.add(s),e={rel:e,crossOrigin:l,href:t},a.querySelector(s)===null&&(t=a.createElement("link"),bt(t,"link",e),rt(t),a.head.appendChild(t)))}}function g0(e){ql.D(e),wf("dns-prefetch",e,null)}function b0(e,t){ql.C(e,t),wf("preconnect",e,t)}function x0(e,t,l){ql.L(e,t,l);var a=hn;if(a&&e&&t){var s='link[rel="preload"][as="'+Jt(t)+'"]';t==="image"&&l&&l.imageSrcSet?(s+='[imagesrcset="'+Jt(l.imageSrcSet)+'"]',typeof l.imageSizes=="string"&&(s+='[imagesizes="'+Jt(l.imageSizes)+'"]')):s+='[href="'+Jt(e)+'"]';var i=s;switch(t){case"style":i=pn(e);break;case"script":i=vn(e)}ll.has(i)||(e=D({rel:"preload",href:t==="image"&&l&&l.imageSrcSet?void 0:e,as:t},l),ll.set(i,e),a.querySelector(s)!==null||t==="style"&&a.querySelector(as(i))||t==="script"&&a.querySelector(ns(i))||(t=a.createElement("link"),bt(t,"link",e),rt(t),a.head.appendChild(t)))}}function j0(e,t){ql.m(e,t);var l=hn;if(l&&e){var a=t&&typeof t.as=="string"?t.as:"script",s='link[rel="modulepreload"][as="'+Jt(a)+'"][href="'+Jt(e)+'"]',i=s;switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":i=vn(e)}if(!ll.has(i)&&(e=D({rel:"modulepreload",href:e},t),ll.set(i,e),l.querySelector(s)===null)){switch(a){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(l.querySelector(ns(i)))return}a=l.createElement("link"),bt(a,"link",e),rt(a),l.head.appendChild(a)}}}function S0(e,t,l){ql.S(e,t,l);var a=hn;if(a&&e){var s=Ha(a).hoistableStyles,i=pn(e);t=t||"default";var c=s.get(i);if(!c){var o={loading:0,preload:null};if(c=a.querySelector(as(i)))o.loading=5;else{e=D({rel:"stylesheet",href:e,"data-precedence":t},l),(l=ll.get(i))&&Rc(e,l);var d=c=a.createElement("link");rt(d),bt(d,"link",e),d._p=new Promise(function(j,R){d.onload=j,d.onerror=R}),d.addEventListener("load",function(){o.loading|=1}),d.addEventListener("error",function(){o.loading|=2}),o.loading|=4,fi(c,t,a)}c={type:"stylesheet",instance:c,count:1,state:o},s.set(i,c)}}}function N0(e,t){ql.X(e,t);var l=hn;if(l&&e){var a=Ha(l).hoistableScripts,s=vn(e),i=a.get(s);i||(i=l.querySelector(ns(s)),i||(e=D({src:e,async:!0},t),(t=ll.get(s))&&wc(e,t),i=l.createElement("script"),rt(i),bt(i,"link",e),l.head.appendChild(i)),i={type:"script",instance:i,count:1,state:null},a.set(s,i))}}function A0(e,t){ql.M(e,t);var l=hn;if(l&&e){var a=Ha(l).hoistableScripts,s=vn(e),i=a.get(s);i||(i=l.querySelector(ns(s)),i||(e=D({src:e,async:!0,type:"module"},t),(t=ll.get(s))&&wc(e,t),i=l.createElement("script"),rt(i),bt(i,"link",e),l.head.appendChild(i)),i={type:"script",instance:i,count:1,state:null},a.set(s,i))}}function Hf(e,t,l,a){var s=(s=Se.current)?di(s):null;if(!s)throw Error(r(446));switch(e){case"meta":case"title":return null;case"style":return typeof l.precedence=="string"&&typeof l.href=="string"?(t=pn(l.href),l=Ha(s).hoistableStyles,a=l.get(t),a||(a={type:"style",instance:null,count:0,state:null},l.set(t,a)),a):{type:"void",instance:null,count:0,state:null};case"link":if(l.rel==="stylesheet"&&typeof l.href=="string"&&typeof l.precedence=="string"){e=pn(l.href);var i=Ha(s).hoistableStyles,c=i.get(e);if(c||(s=s.ownerDocument||s,c={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},i.set(e,c),(i=s.querySelector(as(e)))&&!i._p&&(c.instance=i,c.state.loading=5),ll.has(e)||(l={rel:"preload",as:"style",href:l.href,crossOrigin:l.crossOrigin,integrity:l.integrity,media:l.media,hrefLang:l.hrefLang,referrerPolicy:l.referrerPolicy},ll.set(e,l),i||C0(s,e,l,c.state))),t&&a===null)throw Error(r(528,""));return c}if(t&&a!==null)throw Error(r(529,""));return null;case"script":return t=l.async,l=l.src,typeof l=="string"&&t&&typeof t!="function"&&typeof t!="symbol"?(t=vn(l),l=Ha(s).hoistableScripts,a=l.get(t),a||(a={type:"script",instance:null,count:0,state:null},l.set(t,a)),a):{type:"void",instance:null,count:0,state:null};default:throw Error(r(444,e))}}function pn(e){return'href="'+Jt(e)+'"'}function as(e){return'link[rel="stylesheet"]['+e+"]"}function Bf(e){return D({},e,{"data-precedence":e.precedence,precedence:null})}function C0(e,t,l,a){e.querySelector('link[rel="preload"][as="style"]['+t+"]")?a.loading=1:(t=e.createElement("link"),a.preload=t,t.addEventListener("load",function(){return a.loading|=1}),t.addEventListener("error",function(){return a.loading|=2}),bt(t,"link",l),rt(t),e.head.appendChild(t))}function vn(e){return'[src="'+Jt(e)+'"]'}function ns(e){return"script[async]"+e}function qf(e,t,l){if(t.count++,t.instance===null)switch(t.type){case"style":var a=e.querySelector('style[data-href~="'+Jt(l.href)+'"]');if(a)return t.instance=a,rt(a),a;var s=D({},l,{"data-href":l.href,"data-precedence":l.precedence,href:null,precedence:null});return a=(e.ownerDocument||e).createElement("style"),rt(a),bt(a,"style",s),fi(a,l.precedence,e),t.instance=a;case"stylesheet":s=pn(l.href);var i=e.querySelector(as(s));if(i)return t.state.loading|=4,t.instance=i,rt(i),i;a=Bf(l),(s=ll.get(s))&&Rc(a,s),i=(e.ownerDocument||e).createElement("link"),rt(i);var c=i;return c._p=new Promise(function(o,d){c.onload=o,c.onerror=d}),bt(i,"link",a),t.state.loading|=4,fi(i,l.precedence,e),t.instance=i;case"script":return i=vn(l.src),(s=e.querySelector(ns(i)))?(t.instance=s,rt(s),s):(a=l,(s=ll.get(i))&&(a=D({},l),wc(a,s)),e=e.ownerDocument||e,s=e.createElement("script"),rt(s),bt(s,"link",a),e.head.appendChild(s),t.instance=s);case"void":return null;default:throw Error(r(443,t.type))}else t.type==="stylesheet"&&(t.state.loading&4)===0&&(a=t.instance,t.state.loading|=4,fi(a,l.precedence,e));return t.instance}function fi(e,t,l){for(var a=l.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),s=a.length?a[a.length-1]:null,i=s,c=0;c title"):null)}function T0(e,t,l){if(l===1||t.itemProp!=null)return!1;switch(e){case"meta":case"title":return!0;case"style":if(typeof t.precedence!="string"||typeof t.href!="string"||t.href==="")break;return!0;case"link":if(typeof t.rel!="string"||typeof t.href!="string"||t.href===""||t.onLoad||t.onError)break;return t.rel==="stylesheet"?(e=t.disabled,typeof t.precedence=="string"&&e==null):!0;case"script":if(t.async&&typeof t.async!="function"&&typeof t.async!="symbol"&&!t.onLoad&&!t.onError&&t.src&&typeof t.src=="string")return!0}return!1}function Yf(e){return!(e.type==="stylesheet"&&(e.state.loading&3)===0)}function E0(e,t,l,a){if(l.type==="stylesheet"&&(typeof a.media!="string"||matchMedia(a.media).matches!==!1)&&(l.state.loading&4)===0){if(l.instance===null){var s=pn(a.href),i=t.querySelector(as(s));if(i){t=i._p,t!==null&&typeof t=="object"&&typeof t.then=="function"&&(e.count++,e=hi.bind(e),t.then(e,e)),l.state.loading|=4,l.instance=i,rt(i);return}i=t.ownerDocument||t,a=Bf(a),(s=ll.get(s))&&Rc(a,s),i=i.createElement("link"),rt(i);var c=i;c._p=new Promise(function(o,d){c.onload=o,c.onerror=d}),bt(i,"link",a),l.instance=i}e.stylesheets===null&&(e.stylesheets=new Map),e.stylesheets.set(l,t),(t=l.state.preload)&&(l.state.loading&3)===0&&(e.count++,l=hi.bind(e),t.addEventListener("load",l),t.addEventListener("error",l))}}var Hc=0;function M0(e,t){return e.stylesheets&&e.count===0&&vi(e,e.stylesheets),0Hc?50:800)+t);return e.unsuspend=l,function(){e.unsuspend=null,clearTimeout(a),clearTimeout(s)}}:null}function hi(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)vi(this,this.stylesheets);else if(this.unsuspend){var e=this.unsuspend;this.unsuspend=null,e()}}}var pi=null;function vi(e,t){e.stylesheets=null,e.unsuspend!==null&&(e.count++,pi=new Map,t.forEach(z0,e),pi=null,hi.call(e))}function z0(e,t){if(!(t.state.loading&4)){var l=pi.get(e);if(l)var a=l.get(null);else{l=new Map,pi.set(e,l);for(var s=e.querySelectorAll("link[data-precedence],style[data-precedence]"),i=0;i"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(u)}catch(f){console.error(f)}}return u(),Kc.exports=V0(),Kc.exports}var $0=J0();function ye(u){return Array.isArray(u)?u:[]}function ym(u){const f=new Set;return u.map(h=>h.trim()).filter(h=>{const r=h.toLowerCase();return!h||f.has(r)?!1:(f.add(r),!0)})}function al(u){const f=Ci.some(h=>h.value===u.streamMode)?u.streamMode:"auto";return{...u,streamMode:f,models:ye(u.models),allowedGroupIds:ye(u.allowedGroupIds),openaiAccounts:ye(u.openaiAccounts),kiroAccounts:ye(u.kiroAccounts)}}function ds(u){return{...u,aliases:ye(u.aliases)}}function W0(u){return{...u,apiKeys:ye(u.apiKeys),logs:ye(u.logs)}}class Ei extends Error{status;payload;constructor(f,h,r){super(h),this.status=f,this.payload=r}}const F0={home:"M3 10.5 12 3l9 7.5V21a1 1 0 0 1-1 1h-5v-7H9v7H4a1 1 0 0 1-1-1z",users:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8M22 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75",key:"M21 2l-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.78 7.78 5.5 5.5 0 0 1 7.78-7.78ZM14 8l7-7M21 8h-5V3",models:"M12 2 4 6v12l8 4 8-4V6zM4 6l8 4 8-4M12 10v12",image:"M21 19V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2ZM8.5 11a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5ZM21 16l-5-5L5 21",route:"M4 19a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM20 11a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM7 16h3a4 4 0 0 0 4-4V8h3",logs:"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8zM14 2v6h6M8 13h8M8 17h6",orbit:"M12 7a5 5 0 1 0 0 10 5 5 0 0 0 0-10ZM3 12a9 3 0 1 0 18 0 9 3 0 1 0-18 0Z",settings:"M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7ZM19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.6 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 8.92 4a1.65 1.65 0 0 0 1-1.51V2a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9c.14.31.39.57.71.71.23.1.49.18.8.2H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z",search:"M21 21l-4.35-4.35M10.5 18a7.5 7.5 0 1 1 0-15 7.5 7.5 0 0 1 0 15Z",copy:"M8 8h11a1 1 0 0 1 1 1v11a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1ZM4 16H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h11a1 1 0 0 1 1 1v1",ban:"M4.93 4.93 19.07 19.07M22 12A10 10 0 1 1 2 12a10 10 0 0 1 20 0Z",check:"M20 6 9 17l-5-5",moon:"M21 12.8A8.5 8.5 0 1 1 11.2 3 6.5 6.5 0 0 0 21 12.8Z",sun:"M12 4V2M12 22v-2M4.93 4.93 3.52 3.52M20.48 20.48l-1.41-1.41M4 12H2M22 12h-2M4.93 19.07l-1.41 1.41M20.48 3.52l-1.41 1.41M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z",plus:"M12 5v14M5 12h14"};function Dt({name:u}){return n.jsx("svg",{viewBox:"0 0 24 24","aria-hidden":"true",className:"icon",children:n.jsx("path",{d:F0[u]})})}async function me(u,f){const h=new Headers(f?.headers);h.set("Content-Type","application/json");const r=window.sessionStorage.getItem("capi-admin-token");r&&h.set("Authorization",`Bearer ${r}`);const C=await fetch(u,{credentials:"include",...f,headers:h});if(!C.ok){const E=await C.json().catch(()=>null);throw new Ei(C.status,E?.error?.message||`Request failed: ${C.status}`,E)}return C.json()}async function I0(u,f){const h=new Headers,r=window.sessionStorage.getItem("capi-admin-token");r&&h.set("Authorization",`Bearer ${r}`);const C=await fetch(u,{credentials:"include",method:"POST",headers:h,body:f});if(!C.ok){const E=await C.json().catch(()=>null);throw new Ei(C.status,E?.error?.message||`Request failed: ${C.status}`,E)}return C.json()}const dm=[{id:"overview",label:"概览",icon:"home"},{id:"users",label:"用户",icon:"users"},{id:"groups",label:"分组",icon:"users"},{id:"keys",label:"密钥",icon:"key"},{id:"models",label:"模型",icon:"models"},{id:"drawing",label:"绘图",icon:"image"},{id:"antigravity",label:"Antigravity",icon:"orbit"},{id:"channels",label:"渠道",icon:"route"},{id:"logs",label:"日志",icon:"logs"},{id:"settings",label:"设置",icon:"settings"}],to=[{value:"kiro",label:"Kiro / Amazon Q"},{value:"codex",label:"Codex / ChatGPT OAuth"},{value:"antigravity",label:"Antigravity / Google"},{value:"cpa",label:"CPA / CLIProxyAPI"},{value:"openai",label:"OpenAI"},{value:"anthropic",label:"Anthropic / Claude"},{value:"google",label:"Google Gemini"},{value:"deepseek",label:"DeepSeek"},{value:"openrouter",label:"OpenRouter"},{value:"groq",label:"Groq"},{value:"siliconflow",label:"SiliconFlow"},{value:"moonshot",label:"Moonshot"},{value:"compatible",label:"OpenAI 兼容接口"}],gm="https://api.openai.com/v1",Mi="https://chatgpt.com/backend-api",bm="http://localhost:8317/v1",xm="https://codewhisperer.us-east-1.amazonaws.com",jm="gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4, gpt-image-2, gpt-image-1",P0="gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4, gpt-image-2",ep="gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, gpt-5.5, gpt-5.4, claude-sonnet-4, gemini-3.1-pro",tp="claude-sonnet-4.5, claude-sonnet-4, claude-haiku-4.5, claude-opus-4.5",Sm="https://daily-cloudcode-pa.googleapis.com",lp="claude-opus-4-6-thinking, claude-sonnet-4-6, gemini-3.8-flash-high, gemini-3.7-flash-high, gemini-3.6-flash-high, gemini-3-flash, gemini-pro-agent, gemini-3.1-pro-low, gpt-oss-120b-medium, gemini-3.1-flash-lite, gemini-3.5-flash-lite, gemini-3.1-flash-image",Fc=[{provider:"kiro",label:"Kiro / Amazon Q",name:"Kiro 账号池",baseUrl:xm,models:tp.split(",").map(u=>u.trim())},{provider:"openai",label:"OpenAI",name:"OpenAI 主线路",baseUrl:gm,models:P0.split(",").map(u=>u.trim())},{provider:"codex",label:"Codex 账号池",name:"Codex 账号池",baseUrl:Mi,models:jm.split(",").map(u=>u.trim())},{provider:"antigravity",label:"Antigravity 账号池",name:"Antigravity 账号池",baseUrl:Sm,models:lp.split(",").map(u=>u.trim())},{provider:"cpa",label:"CPA / CLIProxyAPI",name:"CPA 本地代理",baseUrl:bm,models:ep.split(",").map(u=>u.trim())},{provider:"compatible",label:"OpenAI 兼容接口",name:"兼容渠道",baseUrl:"",models:[]},{provider:"openrouter",label:"OpenRouter",name:"OpenRouter",baseUrl:"https://openrouter.ai/api/v1",models:[]},{provider:"google",label:"Google Gemini",name:"Gemini",baseUrl:"",models:[]},{provider:"anthropic",label:"Anthropic / Claude",name:"Claude",baseUrl:"",models:[]}];function za(u){return Fc.find(f=>f.provider===u)||Fc[2]}function Ic(u){const f=za(u);return{name:f.name,provider:f.provider,baseUrl:f.baseUrl,models:[...f.models],streamMode:"auto"}}function ap(u){const f=u?.trim().toLowerCase();return f&&({free:"Free",plus:"Plus",pro:"Pro",team:"Team",enterprise:"Enterprise"}[f]||u)||"套餐未知"}function fm(u){return u?{upstream_token_invalidated:"Token 已失效",upstream_invalid_api_key:"API Key 无效",upstream_account_error:"账号错误",upstream_accounts_unavailable:"账号池不可用",upstream_error:"上游错误"}[u]||u:""}function zi(u){return u==="openai"?gm:u==="codex"?Mi:u==="antigravity"?Sm:u==="cpa"||u==="cliproxyapi"?bm:u==="kiro"?xm:""}function Pc(u){return ym(u.split(/[\s,;,;]+/))}function np(u){return ym(u.split(/[\n,;,;]+/).map(f=>f.trim()))}function Nm(u){const f=np(u);return f.length===0?{}:f.length===1?{upstreamApiKey:f[0]}:{upstreamApiKeys:f}}const Ci=[{value:"auto",label:"自动",description:"按请求参数处理"},{value:"real",label:"真流",description:"直连上游 SSE"},{value:"fake",label:"假流",description:"非流转 SSE"},{value:"disabled",label:"禁用流",description:"流式请求跳过"}];function sl(u){if(!u)return"未使用";const f=new Date(u);return Number.isNaN(f.getTime())?"-":new Intl.DateTimeFormat("zh-CN",{month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}).format(f)}function mm(u){if(!u)return"";const f=new Date(u);if(Number.isNaN(f.getTime()))return"";const h=f.getTimezoneOffset()*6e4;return new Date(f.getTime()-h).toISOString().slice(0,16)}function sp(u){if(!u)return"";const f=new Date(u);return Number.isNaN(f.getTime())?"":f.toISOString()}function ip(u){if(!u)return"-";const f=new Date(u);return Number.isNaN(f.getTime())?"-":new Intl.DateTimeFormat("zh-CN",{year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit"}).format(f)}function Ut(u){return{active:"正常",disabled:"禁用",limited:"受限",overdue:"欠费",healthy:"正常",standby:"备用",available:"Available",success:"成功",failed:"失败"}[u]||u}function St(u,f=2){const h=Number(u||0);return new Intl.NumberFormat("zh-CN",{minimumFractionDigits:f,maximumFractionDigits:f}).format(h)}function gl(u){return new Intl.NumberFormat("zh-CN").format(Math.max(0,Math.round(Number(u||0))))}function lo(u){return u.model?u.model:u.errorCode==="invalid_api_key"?"密钥无效":u.errorCode==="model_not_available"?"模型不可用":u.errorCode==="insufficient_quota"?"额度不足":u.errorCode==="rate_limit_exceeded"?"请求过快":u.errorCode||"请求失败"}function up(u){return u.model||(u.errorCode?`错误:${u.errorCode}`:"-")}function cp(u){return u.channel||(u.apiKeyPrefix?`Key ${u.apiKeyPrefix}`:"-")}function op(u){const f=ye(u);if(f.length===0)return"未绑定模型";const h=f.slice(0,2).join(", ");return f.length>2?`${h} · 其余 ${f.length-2} 个`:h}function dl(u){return u==="cliproxyapi"||u==="cli-proxy-api"?"CPA / CLIProxyAPI":to.find(f=>f.value===u)?.label||u||"Custom"}function $c(u){const f=`${u.vendor} ${u.id} ${u.name}`.toLowerCase();return f.includes("cliproxyapi")||/\bcpa\b/.test(f)?"cpa":f.includes("openai")||/\bgpt[-_/]/.test(f)||f.includes("o1-")||f.includes("o3-")?"openai":f.includes("anthropic")||f.includes("claude")?"anthropic":f.includes("google")||f.includes("gemini")||f.includes("gcli-")?"google":f.includes("deepseek")?"deepseek":f.includes("openrouter")?"openrouter":f.includes("groq")?"groq":f.includes("siliconflow")?"siliconflow":f.includes("moonshot")||f.includes("kimi")?"moonshot":u.vendor&&u.vendor.toLowerCase()!=="custom"?u.vendor.toLowerCase():"compatible"}function hm({provider:u}){if(u==="deepseek")return n.jsx("span",{className:"provider-icon provider-icon-deepseek","aria-hidden":"true",children:n.jsxs("svg",{viewBox:"0 0 32 32",role:"img",children:[n.jsx("rect",{x:"1",y:"1",width:"30",height:"30",rx:"8"}),n.jsx("path",{transform:"translate(2.4 4.4)",d:"M26.517 3.395c-.282-.138-.403.125-.568.258-.057.044-.105.1-.152.152-.413.44-.895.73-1.524.695-.92-.052-1.705.237-2.4.941-.147-.868-.638-1.386-1.384-1.718-.39-.173-.786-.346-1.06-.721-.19-.268-.243-.566-.338-.86-.061-.176-.121-.357-.325-.388-.222-.034-.309.151-.396.307-.347.635-.481 1.334-.468 2.042.03 1.594.703 2.863 2.04 3.765.152.104.191.207.143.359-.091.31-.2.613-.295.924-.06.198-.151.242-.364.155-.734-.306-1.367-.76-1.927-1.308-.951-.92-1.81-1.934-2.882-2.729-.252-.185-.504-.358-.764-.522-1.094-1.062.143-1.935.43-2.038.3-.108.104-.48-.864-.475-.968.004-1.853.328-2.982.76-.165.065-.339.112-.516.151-1.024-.194-2.088-.237-3.199-.112-2.092.233-3.763 1.222-4.991 2.91C.254 7.972-.093 10.278.332 12.682c.446 2.535 1.74 4.633 3.728 6.274 2.062 1.7 4.436 2.534 7.145 2.375 1.645-.095 3.476-.316 5.542-2.064.521.259 1.068.363 1.975.44.699.065 1.371-.034 1.892-.142.816-.173.76-.929.465-1.067-2.392-1.114-1.866-.661-2.344-1.027 1.215-1.438 3.071-3.993 3.644-7.473.056-.384.128-.925.12-1.236-.005-.19.038-.263.255-.285.6-.069 1.18-.233 1.715-.527 1.55-.846 2.175-2.237 2.322-3.903.022-.255-.005-.518-.274-.652ZM13.014 18.395c-2.318-1.823-3.442-2.423-3.906-2.397-.434.026-.356.523-.26.847.1.32.23.54.412.82.126.186.213.462-.126.67-.746.461-2.044-.156-2.105-.186-1.51-.89-2.773-2.064-3.664-3.67-.86-1.545-1.358-3.204-1.44-4.974-.022-.427.104-.578.529-.656.56-.103 1.137-.125 1.697-.043 2.366.346 4.379 1.403 6.068 3.079.963.954 1.692 2.094 2.443 3.208.799 1.183 1.658 2.31 2.752 3.234.387.324.695.57.99.751-.89.1-2.374.121-3.39-.683Zm1.111-7.146c0-.19.152-.341.343-.341.043 0 .082.009.117.021.048.018.092.044.126.083.061.06.096.146.096.237a.341.341 0 0 1-.343.341.34.34 0 0 1-.339-.341Zm3.451 1.77c-.222.09-.443.168-.656.177-.33.017-.69-.117-.885-.281-.304-.255-.521-.397-.612-.842-.039-.19-.017-.483.017-.652.078-.362-.009-.595-.265-.807-.208-.172-.473-.22-.764-.22-.108 0-.208-.048-.282-.086-.121-.061-.221-.212-.126-.398.031-.06.178-.207.213-.233.395-.225.85-.151 1.272.018.39.16.686.453 1.111.867.434.501.512.639.759 1.015.196.294.373.596.495.942.073.215-.022.392-.277.5Z"})]})});if(u==="openai")return n.jsx("span",{className:"provider-icon provider-icon-openai","aria-hidden":"true",children:n.jsxs("svg",{viewBox:"0 0 32 32",role:"img",children:[n.jsx("rect",{x:"1",y:"1",width:"30",height:"30",rx:"8"}),n.jsx("g",{transform:"translate(7 7.5) scale(0.065)",fill:"currentColor",stroke:"none",children:n.jsx("path",{d:"M267.06 111.34a71.78 71.78 0 0 0-6.17-58.91c-14.5-25.15-43.55-38.09-71.9-32.03A71.78 71.78 0 0 0 135.1.5C106 .43 80.21 19.16 71.29 46.85a71.79 71.79 0 0 0-47.98 34.8c-14.6 25.1-11.28 56.75 8.22 78.3a71.78 71.78 0 0 0 6.16 58.9c14.5 25.16 43.56 38.1 71.91 32.04a71.76 71.76 0 0 0 53.89 24.02c29.12.02 54.92-18.72 63.84-46.44a71.79 71.79 0 0 0 47.98-34.8c14.58-25.1 11.25-56.72-8.24-78.27zm-107.9 150.77a53.15 53.15 0 0 1-34.15-12.35c.43-.24 1.2-.66 1.7-.96l56.68-32.73a9.22 9.22 0 0 0 4.66-8.06v-79.9l23.95 13.83a.85.85 0 0 1 .47.66v66.16a53.42 53.42 0 0 1-53.3 53.35zM44.6 213.16a53.13 53.13 0 0 1-6.36-35.75c.42.25 1.15.7 1.68 1l56.68 32.73a9.24 9.24 0 0 0 9.31 0l69.2-39.95v27.66a.87.87 0 0 1-.34.74l-57.29 33.07a53.42 53.42 0 0 1-72.88-19.5zM29.7 90.05a53.1 53.1 0 0 1 27.76-23.36c0 .49-.03 1.36-.03 1.96v65.46a9.22 9.22 0 0 0 4.65 8.05l69.2 39.95-23.95 13.83a.86.86 0 0 1-.81.07L49.2 162.9A53.42 53.42 0 0 1 29.7 90.05zm196.8 45.8L157.3 95.9l23.95-13.82a.86.86 0 0 1 .81-.07l57.3 33.08a53.37 53.37 0 0 1-8.24 96.29v-65.46a9.2 9.2 0 0 0-4.62-8.06zm23.84-35.89c-.42-.26-1.15-.7-1.68-1.01l-56.68-32.73a9.25 9.25 0 0 0-9.31 0l-69.2 39.95V78.5a.87.87 0 0 1 .35-.74l57.28-33.05a53.35 53.35 0 0 1 79.24 55.25zM100.11 149.24l-23.96-13.83a.85.85 0 0 1-.46-.66V68.6a53.37 53.37 0 0 1 87.52-40.95c-.42.24-1.19.66-1.7.96l-56.68 32.73a9.22 9.22 0 0 0-4.66 8.06l-.04 79.85zm13.01-28.05L144 103.3l30.88 17.83v35.68L144 174.63l-30.88-17.82v-35.62z"})})]})});const f=u==="codex"?"C":u==="antigravity"?"G":u==="cpa"||u==="cliproxyapi"?"CPA":u==="anthropic"?"A":u==="google"?"✦":u==="openrouter"?"↗":u==="groq"?"G":u==="siliconflow"?"S":u==="moonshot"?"M":"◇";return n.jsx("span",{className:`provider-icon provider-icon-${u}`,"aria-hidden":"true",children:n.jsxs("svg",{viewBox:"0 0 32 32",role:"img",children:[n.jsx("rect",{x:"1",y:"1",width:"30",height:"30",rx:"8"}),n.jsx("text",{x:"16",y:"21",textAnchor:"middle",children:f})]})})}async function Ti(u){if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(u);return}const f=document.createElement("textarea");f.value=u,f.setAttribute("readonly","true"),f.style.position="fixed",f.style.opacity="0",document.body.appendChild(f),f.select(),document.execCommand("copy"),document.body.removeChild(f)}function Gl(){return window.location.origin}function rp(){return`${Gl()}/api/auth/discord/callback`}function dp(){return`${Gl()}/`}function Wc(u){return{...u,redirectUri:u.redirectUri&&!u.redirectUri.includes("localhost")?u.redirectUri:rp(),authSuccessUrl:u.authSuccessUrl&&!u.authSuccessUrl.includes("localhost")?u.authSuccessUrl:dp(),blockedGuildIds:ye(u.blockedGuildIds),sessionTtlHours:u.sessionTtlHours||168}}function Oi(u){return u==="email"||u==="discord"?u:"username"}function fp(u){const h=(u.split("@")[0]||"user").toLowerCase().replace(/[^a-z0-9_-]+/g,"-").replace(/^[-_]+|[-_]+$/g,"");return(h.length>=3?h:"user").slice(0,24)}function mp(){const[u,f]=b.useState("home"),[h,r]=b.useState("login"),[C,E]=b.useState(null),[G,F]=b.useState("overview"),[N,y]=b.useState(()=>{const T=window.localStorage.getItem("capi-theme");return T==="light"||T==="dark"?T:window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}),[q,D]=b.useState("comfortable"),[V,ee]=b.useState(null),[he,te]=b.useState([]),[pe,ie]=b.useState([]),[se,k]=b.useState([]),[xe,le]=b.useState([]),[je,O]=b.useState([]),[ne,ge]=b.useState(""),[Te,ze]=b.useState(""),[ue,X]=b.useState(null),[fe,W]=b.useState(""),[A,L]=b.useState(""),[H,Q]=b.useState(!1),ce=b.useMemo(()=>{const T=ne.trim().toLowerCase();return T?he.filter(Y=>`${Y.id} ${Y.name} ${Y.email}`.toLowerCase().includes(T)):he},[ne,he]);async function m(){const T=new Date().getTimezoneOffset(),[Y,K,I,ve,Ie,wt]=await Promise.all([me(`/api/overview?timezoneOffset=${T}`),me("/api/users"),me("/api/channels"),me("/api/models"),me("/api/logs"),me("/api/groups")]),Ht=ye(K.users),ul=ye(I.channels).map(al),ml=ye(ve.models).map(ds),Da=ye(Ie.logs),xl=ye(wt.groups);ee(Y),te(Ht),ie(ul),k(xl),le(ml),O(Da),ze(kl=>kl&&Ht.some(da=>da.id===kl)?kl:Ht[0]?.id||""),Ht.length===0&&X(null),Q(!0)}async function v(T){const Y=await me(`/api/users/${T}`);X(W0(Y))}async function U(T,Y){const K=await me(`/api/users/${T}`,{method:"PATCH",body:JSON.stringify(Y)});te(I=>I.map(ve=>ve.id===T?K.user:ve)),X(I=>I?.user.id===T?{...I,user:K.user}:I),W("已更新用户"),window.setTimeout(()=>W(""),1800)}async function Z(T,Y,K={}){const I=await me("/api/users/bulk",{method:"POST",body:JSON.stringify({userIds:T,action:Y,...K})}),ve=new Map(ye(I.users).map(Ie=>[Ie.id,Ie]));te(Ie=>Ie.map(wt=>ve.get(wt.id)||wt)),X(Ie=>{if(!Ie)return Ie;const wt=ve.get(Ie.user.id);return wt?{...Ie,user:wt}:Ie}),W(`已处理 ${I.updated} 个用户`),window.setTimeout(()=>W(""),1800)}async function oe(T){const Y=await me(`/api/users/${T}/api-keys`,{method:"POST",body:JSON.stringify({name:"Console Key"})});ue?.user.id===T&&X({...ue,apiKeys:[...ue.apiKeys,Y.apiKey]}),L(Y.secret),await Ti(Y.secret),W("新 Key 已创建并复制,请立即保存"),window.setTimeout(()=>W(""),2400)}async function Se(T){window.confirm("删除这个 Key?删除后使用它的请求会立即失效。")&&(await me(`/api/api-keys/${T}`,{method:"DELETE"}),X(Y=>Y&&{...Y,apiKeys:Y.apiKeys.filter(K=>K.id!==T)}),W("Key 已删除"),window.setTimeout(()=>W(""),1800))}async function _(T,Y){const K=await me(`/api/api-keys/${T}`,{method:"PATCH",body:JSON.stringify(Y)});X(I=>I&&{...I,apiKeys:I.apiKeys.map(ve=>ve.id===T?K.apiKey:ve)}),W("Key 已更新"),window.setTimeout(()=>W(""),1800)}async function P(T,Y){const K=await me(`/api/channels/${T}`,{method:"PATCH",body:JSON.stringify(Y)});ie(I=>I.map(ve=>ve.id===T?al(K.channel):ve)),We(K.removedModels),W("渠道已更新"),window.setTimeout(()=>W(""),1800)}async function ae(T){const Y=pe.find(I=>I.id===T);if(!window.confirm(`删除渠道「${Y?.name||T}」?`))return;const K=await me(`/api/channels/${T}`,{method:"DELETE"});ie(I=>I.filter(ve=>ve.id!==T)),We(K.removedModels),W("渠道已删除"),window.setTimeout(()=>W(""),1800)}async function Ze(T){const Y=await me("/api/groups",{method:"POST",body:JSON.stringify(T)});k(K=>[...K,Y.group]),W("分组已创建"),window.setTimeout(()=>W(""),1800)}async function $(T,Y){const K=await me(`/api/groups/${T}`,{method:"PATCH",body:JSON.stringify(Y)});k(I=>I.map(ve=>ve.id===T?K.group:ve)),W("分组已更新"),window.setTimeout(()=>W(""),1800)}async function Je(T){const Y=se.find(K=>K.id===T);window.confirm(`删除分组「${Y?.name||T}」?删除后渠道的可见范围会移除该分组。`)&&(await me(`/api/groups/${T}`,{method:"DELETE"}),k(K=>K.filter(I=>I.id!==T)),ie(K=>K.map(I=>({...I,allowedGroupIds:I.allowedGroupIds.filter(ve=>ve!==T)}))),W("分组已删除"),window.setTimeout(()=>W(""),1800))}async function $e(T,Y){const K=ye(Y).map(Ht=>Ht.trim()).filter(Boolean),I=await me(`/api/channels/${T}/sync-models`,{method:"POST",body:JSON.stringify(K.length?{models:K}:{})}),ve=al(I.channel),Ie=ye(I.addedModels).map(ds),wt=ye(I.models);ie(Ht=>Ht.map(ul=>ul.id===T?ve:ul)),Ie.length>0&&le(Ht=>{const ul=new Set(Ht.map(ml=>ml.id.toLowerCase()));return[...Ht,...Ie.filter(ml=>!ul.has(ml.id.toLowerCase()))]}),We(I.removedModels),W(K.length?`已保存 ${wt.length} 个模型`:wt.length?`已拉取 ${wt.length} 个模型`:"上游没有返回模型"),window.setTimeout(()=>W(""),2200)}function We(T){const Y=new Set(ye(T).map(K=>K.toLowerCase()));Y.size!==0&&(le(K=>K.filter(I=>!Y.has(I.id.toLowerCase()))),X(K=>K&&{...K,apiKeys:K.apiKeys.map(I=>({...I,allowedModels:I.allowedModels.filter(ve=>!Y.has(ve.toLowerCase()))}))}))}async function ft(T){try{const Y=await me(`/api/channels/${T}/check`,{method:"POST",body:JSON.stringify({})});ie(K=>K.map(I=>I.id===T?al(Y.channel):I)),W(Y.ok?`渠道可用,检测到 ${ye(Y.models).length} 个模型`:"渠道检测失败")}catch(Y){const K=Y instanceof Ei?Y.payload?.channel:null;K&&ie(I=>I.map(ve=>ve.id===T?al(K):ve)),W(Y instanceof Error?Y.message:"渠道检测失败")}window.setTimeout(()=>W(""),2400)}async function Nt(T=Ic("codex")){const Y=await me("/api/channels",{method:"POST",body:JSON.stringify(T)});ie(K=>[...K,al(Y.channel)]),W("渠道已创建,可继续拉取模型或检测渠道"),window.setTimeout(()=>W(""),2400)}async function At(T,Y){const K=new FormData;K.append("file",Y);const I=await I0(`/api/channels/${encodeURIComponent(T)}/import-openai-accounts`,K);ie(ve=>ve.map(Ie=>Ie.id===I.channel.id?al(I.channel):Ie)),W(`新增 ${I.created??I.imported} 个账号${I.updated?`,更新 ${I.updated} 个已有账号`:""}${I.skipped?`,跳过 ${I.skipped} 个`:""}`),window.setTimeout(()=>W(""),2600)}async function il(T,Y=!1,K=""){const I=await me(`/api/channels/${encodeURIComponent(T)}/openai-accounts/check`,{method:"POST",body:JSON.stringify({onlyInvalid:Y,accountId:K})});ie(ve=>ve.map(Ie=>Ie.id===I.channel.id?al(I.channel):Ie)),W(`${Y?"无效账号复检":"账号测活"}完成:${I.healthy}/${I.checked} 可用${I.failed?`,无效 ${I.failed}`:""}`),window.setTimeout(()=>W(""),3e3)}async function Kt(T){const Y=await me(`/api/channels/${encodeURIComponent(T)}/openai-accounts/deduplicate`,{method:"POST",body:JSON.stringify({})});ie(K=>K.map(I=>I.id===Y.channel.id?al(Y.channel):I)),W(Y.removed?`已合并 ${Y.removed} 个重复账号`:"未发现可识别的重复账号"),window.setTimeout(()=>W(""),2600)}async function J(T,Y){const K=await me(`/api/channels/${encodeURIComponent(T)}/openai-accounts/${encodeURIComponent(Y)}`,{method:"DELETE"});ie(I=>I.map(ve=>ve.id===K.channel.id?al(K.channel):ve)),W("账号已删除"),window.setTimeout(()=>W(""),1800)}async function ot(T){return me(`/api/channels/${encodeURIComponent(T)}/openai-oauth/start`,{method:"POST",body:JSON.stringify({})})}async function Zt(T,Y){const K=await me(`/api/channels/${encodeURIComponent(T)}/openai-oauth/complete`,{method:"POST",body:JSON.stringify(Y)});return ie(I=>I.map(ve=>ve.id===K.channel.id?al(K.channel):ve)),W("已通过 OAuth 添加账号"),window.setTimeout(()=>W(""),2400),K}async function bl(T){return me(`/api/channels/${encodeURIComponent(T)}/antigravity-oauth/start`,{method:"POST",body:JSON.stringify({})})}async function mt(T,Y){const K=await me(`/api/channels/${encodeURIComponent(T)}/antigravity-oauth/complete`,{method:"POST",body:JSON.stringify(Y)});return ie(I=>I.map(ve=>ve.id===K.channel.id?al(K.channel):ve)),W("已通过 Google 授权添加账号"),window.setTimeout(()=>W(""),2400),K}async function gn(T){const Y=await me("/api/models",{method:"POST",body:JSON.stringify(T)});le(K=>[...K,ds(Y.model)]),W("模型已添加"),window.setTimeout(()=>W(""),1800)}async function Oa(T,Y){const K=await me(`/api/models/${encodeURIComponent(T)}`,{method:"PATCH",body:JSON.stringify(Y)});le(I=>I.map(ve=>ve.id===T?ds(K.model):ve)),W("模型已更新"),window.setTimeout(()=>W(""),1600)}async function _a(T){window.confirm(`删除模型 ${T}?渠道和 Key 中的引用也会一起清理。`)&&(await me(`/api/models/${encodeURIComponent(T)}`,{method:"DELETE"}),le(Y=>Y.filter(K=>K.id!==T)),ie(Y=>Y.map(K=>({...K,models:K.models.filter(I=>I!==T)}))),X(Y=>Y&&{...Y,apiKeys:Y.apiKeys.map(K=>({...K,allowedModels:K.allowedModels.filter(I=>I!==T)}))}),W("模型已删除"),window.setTimeout(()=>W(""),1800))}async function z(T,Y="已复制"){await Ti(T),W(Y),window.setTimeout(()=>W(""),1600)}function Ae(T,Y){if(T instanceof Ei&&T.status===401){Q(!1),r("login"),f("auth");return}W(Y)}async function Fe(){try{const T=await me("/api/auth/status");if(E(T),!T.initialized){r("setup"),f("auth");return}if(!T.authenticated){r("login"),f("auth");return}f(T.session?.role==="admin"?"console":"account")}catch(T){Ae(T,"认证状态加载失败")}}async function ht(){await me("/api/auth/logout",{method:"POST"}),window.sessionStorage.removeItem("capi-admin-token"),E(null),f("home")}return b.useEffect(()=>{let T=!1;return me("/api/auth/status").then(Y=>{T||(E(Y),Y.authenticated&&Y.session&&f(Y.session.role==="admin"?"console":"account"))}).catch(()=>{}),()=>{T=!0}},[]),b.useEffect(()=>{u==="console"&&(Q(!1),m().catch(T=>Ae(T,"加载数据失败")))},[u]),b.useEffect(()=>{u!=="console"||!H||v(Te).catch(T=>Ae(T,"加载用户详情失败"))},[Te,u,H]),b.useEffect(()=>{window.localStorage.setItem("capi-theme",N)},[N]),b.useEffect(()=>{window.scrollTo({top:0,left:0})},[u,G]),u==="home"?n.jsx(xp,{theme:N,setTheme:y,enterConsole:Fe}):u==="auth"?n.jsx(hp,{theme:N,mode:h,status:C,setTheme:y,setMode:r,goHome:()=>f("home"),onAuthenticated:T=>{E(Y=>Y?{...Y,authenticated:!0,initialized:!0,session:T}:null),f(T.role==="admin"?"console":"account")}}):u==="account"?n.jsx(bp,{theme:N,setTheme:y,goHome:()=>f("home"),openLogin:Fe}):n.jsxs("div",{className:"app-shell","data-theme":N,"data-density":q,children:[n.jsxs("aside",{className:"sidebar",children:[n.jsxs("div",{className:"ios-window-dots","aria-hidden":"true",children:[n.jsx("span",{}),n.jsx("span",{}),n.jsx("span",{})]}),n.jsxs("div",{className:"brand",children:[n.jsx("div",{className:"brand-mark",children:"C"}),n.jsxs("div",{children:[n.jsx("strong",{children:"CAPI"}),n.jsx("span",{children:"聚合网关"})]})]}),n.jsx("nav",{children:dm.map(T=>n.jsxs("button",{className:G===T.id?"nav-item active":"nav-item",onClick:()=>F(T.id),children:[n.jsx(Dt,{name:T.icon}),n.jsx("span",{className:"nav-label",children:T.label})]},T.id))}),n.jsxs("div",{className:"sidebar-footer",children:[n.jsx("span",{children:"Gateway"}),n.jsxs("strong",{children:[n.jsx("span",{className:"pulse-dot"}),"Online"]})]})]}),n.jsxs("main",{className:"content",children:[n.jsxs("header",{className:"topbar",children:[n.jsxs("div",{children:[n.jsx("p",{className:"eyebrow",children:"Admin Console"}),n.jsx("h1",{children:dm.find(T=>T.id===G)?.label})]}),n.jsxs("div",{className:"topbar-actions",children:[n.jsx(Vp,{value:q,options:[{value:"comfortable",label:"舒适"},{value:"compact",label:"紧凑"}],onChange:T=>D(T)}),n.jsxs("button",{className:"theme-toggle","aria-label":"切换暗色模式",onClick:()=>y(N==="dark"?"light":"dark"),children:[n.jsx(Dt,{name:N==="dark"?"sun":"moon"}),n.jsx("span",{children:N==="dark"?"浅色":"暗色"})]}),n.jsx("button",{className:"primary-button",onClick:()=>m().catch(T=>Ae(T,"刷新失败")),children:"刷新"}),n.jsx("button",{className:"secondary-button home-link",onClick:()=>f("home"),children:"首页"}),n.jsx("button",{className:"secondary-button",onClick:ht,children:"退出"})]})]}),G==="overview"&&n.jsx(jp,{overview:V,channels:pe,logs:je,onNavigate:T=>{F(T),T==="channels"&&pe.length===0&&W("渠道页可以创建第一个上游"),T==="logs"&&je.length===0&&W("暂无异常日志"),window.setTimeout(()=>W(""),1800)}}),G==="users"&&n.jsx(Np,{users:ce,query:ne,selectedUser:ue,onQuery:ge,onSelect:ze,onUpdate:U,onBulkUpdate:Z,onCreateKey:oe,groups:se,onOpenRegistration:()=>{F("settings"),W("在账号与注册里开放注册,用户即可自助创建账号"),window.setTimeout(()=>W(""),2400)}}),G==="groups"&&n.jsx(Kp,{groups:se,onCreate:Ze,onUpdate:$,onDelete:Je}),G==="keys"&&n.jsx(Ap,{selectedUser:ue,onCreateKey:oe,onUpdateKey:_,onDeleteKey:Se}),G==="models"&&n.jsx(Tp,{models:xe,onCopy:z,onCreate:gn,onUpdate:Oa,onDelete:_a}),G==="drawing"&&n.jsx(vm,{variant:"drawing",channels:pe,onCreate:Nt,onImport:At,onCheckAccounts:il,onDeduplicateAccounts:Kt,onDeleteAccount:J,onUpdate:P,onStartOAuth:ot,onCompleteOAuth:Zt}),G==="antigravity"&&n.jsx(vm,{variant:"antigravity",channels:pe,onCreate:Nt,onImport:At,onCheckAccounts:il,onDeduplicateAccounts:Kt,onDeleteAccount:J,onUpdate:P,onStartOAuth:bl,onCompleteOAuth:mt}),G==="channels"&&n.jsx(kp,{channels:pe,groups:se,onUpdate:P,onCreate:Nt,onImport:At,onDelete:ae,onSyncModels:$e,onCheck:ft}),G==="logs"&&n.jsx(Lp,{logs:je,onCopy:z}),G==="settings"&&n.jsx(Xp,{models:xe,channels:pe,groups:se})]}),A&&n.jsx("div",{className:"secret-dialog-backdrop",role:"presentation",children:n.jsxs("section",{className:"secret-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"secret-dialog-title",children:[n.jsxs("div",{children:[n.jsx("p",{className:"eyebrow",children:"One-time secret"}),n.jsx("h2",{id:"secret-dialog-title",children:"完整 API Key"})]}),n.jsx("p",{children:"完整密钥只显示这一次。列表中的星号内容只是识别前缀,不能用于 API 调用。"}),n.jsx("code",{children:A}),n.jsxs("div",{className:"secret-dialog-actions",children:[n.jsx("button",{className:"secondary-button",onClick:()=>{Ti(A),W("完整 Key 已复制"),window.setTimeout(()=>W(""),1800)},children:"复制"}),n.jsx("button",{className:"primary-button",onClick:()=>L(""),children:"完成"})]})]})}),fe&&n.jsx("div",{className:"toast",children:fe})]})}function hp({theme:u,mode:f,status:h,setTheme:r,setMode:C,goHome:E,onAuthenticated:G}){const[F,N]=b.useState(""),[y,q]=b.useState(""),[D,V]=b.useState(""),[ee,he]=b.useState(""),[te,pe]=b.useState(""),[ie,se]=b.useState(""),[k,xe]=b.useState(!1),[le,je]=b.useState("username"),[O,ne]=b.useState("0"),[ge,Te]=b.useState(""),[ze,ue]=b.useState(!1),X=f==="setup",fe=f==="register",W=Oi(h?.registrationMode),A=fe&&W==="email",L=fe&&W==="discord";async function H(){if(!L){if((X||fe)&&te!==ie){Te("两次输入的密码不一致");return}ue(!0),Te("");try{const Q=X?"/api/auth/setup":fe?"/api/auth/register":"/api/auth/login",ce=A?fp(D):F,m=f==="login"?{identifier:F,password:te}:{username:ce,password:te,displayName:y,email:D,discordUserId:X?ee:"",registrationEnabled:X?k:void 0,registrationMode:X?le:void 0,defaultBalance:X?Number(O||0):void 0},v=await me(Q,{method:"POST",body:JSON.stringify(m)});G(v.session)}catch(Q){Te(Q instanceof Error?Q.message:"操作失败")}finally{ue(!1)}}}return n.jsxs("main",{className:"auth-page","data-theme":u,children:[n.jsxs("header",{className:"auth-topbar",children:[n.jsxs("button",{className:"auth-brand",onClick:E,children:[n.jsx("span",{className:"brand-mark",children:"C"}),n.jsx("strong",{children:"CAPI"})]}),n.jsxs("button",{className:"theme-toggle","aria-label":"切换暗色模式",onClick:()=>r(u==="dark"?"light":"dark"),children:[n.jsx(Dt,{name:u==="dark"?"sun":"moon"}),n.jsx("span",{children:u==="dark"?"浅色":"暗色"})]})]}),n.jsxs("section",{className:"auth-stage",children:[n.jsxs("div",{className:"auth-intro",children:[n.jsx("span",{children:X?"First Run":"Welcome Back"}),n.jsx("h1",{children:X?"初始化 CAPI":fe?"创建账号":"登录"}),n.jsx("p",{children:X?"创建第一个管理员账号,完成后即可进入控制台。":"使用你的 CAPI 账号继续。"})]}),n.jsxs("form",{className:"auth-form",onSubmit:Q=>{Q.preventDefault(),H()},children:[L?n.jsxs("div",{className:"auth-discord-register",children:[n.jsx("strong",{children:"使用 Discord 创建账号"}),n.jsx("span",{children:"继续后会按站点设置校验服务器和身份组。"}),h?.discordEnabled?n.jsx("a",{className:"discord-login-button",href:"/api/auth/discord/start",children:"继续使用 Discord"}):n.jsx("div",{className:"auth-message",children:"管理员还没有启用 Discord 登录"})]}):n.jsxs(n.Fragment,{children:[f!=="login"&&n.jsxs("label",{children:[n.jsx("span",{children:"显示名称"}),n.jsx("input",{value:y,onChange:Q=>q(Q.target.value),autoComplete:"name",placeholder:"CAPI"})]}),!A&&n.jsxs("label",{children:[n.jsx("span",{children:f==="login"?"账号或邮箱":"账号"}),n.jsx("input",{value:F,onChange:Q=>N(Q.target.value),autoComplete:"username",placeholder:f==="login"?"输入账号或邮箱":"3-32 位字母、数字、_ 或 -"})]}),(X||A)&&n.jsxs("label",{children:[n.jsx("span",{children:A?"邮箱":"邮箱(可选)"}),n.jsx("input",{type:"email",value:D,onChange:Q=>V(Q.target.value),autoComplete:"email",placeholder:"name@example.com"})]}),X&&n.jsxs(n.Fragment,{children:[n.jsxs("label",{children:[n.jsx("span",{children:"Discord 用户 ID(可选)"}),n.jsx("input",{inputMode:"numeric",autoComplete:"off",value:ee,onChange:Q=>he(Q.target.value),placeholder:"绑定管理员 Discord 账号"})]}),n.jsxs("div",{className:"setup-options",children:[n.jsxs("div",{className:"setting",children:[n.jsx("span",{children:"开放注册"}),n.jsx("button",{type:"button",className:k?"ios-switch is-on":"ios-switch","aria-pressed":k,onClick:()=>xe(Q=>!Q),children:n.jsx("span",{})})]}),n.jsxs("label",{children:[n.jsx("span",{children:"注册方式"}),n.jsxs("select",{value:le,onChange:Q=>je(Oi(Q.target.value)),children:[n.jsx("option",{value:"username",children:"账号密码"}),n.jsx("option",{value:"email",children:"邮箱"}),n.jsx("option",{value:"discord",children:"Discord"})]})]}),n.jsxs("label",{children:[n.jsx("span",{children:"新用户初始额度"}),n.jsx("input",{type:"number",min:"0",step:"0.01",value:O,onChange:Q=>ne(Q.target.value)})]})]})]}),n.jsxs("label",{children:[n.jsx("span",{children:"密码"}),n.jsx("input",{type:"password",value:te,onChange:Q=>pe(Q.target.value),autoComplete:f==="login"?"current-password":"new-password",placeholder:"至少 8 个字符"})]}),f!=="login"&&n.jsxs("label",{children:[n.jsx("span",{children:"确认密码"}),n.jsx("input",{type:"password",value:ie,onChange:Q=>se(Q.target.value),autoComplete:"new-password",placeholder:"再次输入密码"})]}),n.jsx("div",{className:"auth-message",role:"status",children:ge}),n.jsx("button",{className:"primary-button auth-submit",type:"submit",disabled:ze,children:ze?"请稍候":X?"创建管理员":fe?"注册":"登录"})]}),!X&&!L&&h?.discordEnabled&&n.jsx("a",{className:"discord-login-button",href:"/api/auth/discord/start",children:"使用 Discord 登录"}),!X&&n.jsx("div",{className:"auth-switch",children:f==="login"&&h?.registrationEnabled?n.jsx("button",{type:"button",onClick:()=>C("register"),children:"创建账号"}):n.jsx("button",{type:"button",onClick:()=>C("login"),children:"返回登录"})})]})]})]})}const pp=[1,1.5,2,2.5,3,4,5,6,8,10];function vp(u){if(!(u>0))return 1;const f=Math.floor(Math.log10(u)),h=Math.pow(10,f),r=u/h;return(pp.find(E=>r<=E)??10)*h}const pm=88;function yp(u){const f=u.split("-");return f.length===3?`${f[1]}/${f[2]}`:u}function gp(){const[u,f]=b.useState(14),[h,r]=b.useState(null),[C,E]=b.useState(!0),[G,F]=b.useState(!1),[N,y]=b.useState("");b.useEffect(()=>{let k=!1;return E(!0),me(`/api/account/usage?days=${u}&timezoneOffset=${new Date().getTimezoneOffset()}`).then(xe=>{k||(r({...xe.usage,daily:ye(xe.usage?.daily),models:ye(xe.usage?.models)}),F(!1))}).catch(()=>{k||F(!0)}).finally(()=>{k||E(!1)}),()=>{k=!0}},[u]);const q=h?.daily??[],D=h?.models??[],V=vp(Math.max(0,...q.map(k=>k.cost))),ee=Math.max(0,...D.map(k=>k.cost)),he=q.reduce((k,xe)=>!k||xe.cost>k.cost?xe:k,null),te=Math.max(1,Math.ceil(q.length/5)),pe=q.find(k=>k.day===N)||null,ie=pe?q.indexOf(pe):-1,se=h?h.today.cost-h.yesterday.cost:0;return n.jsxs("section",{className:"account-section usage-section",children:[n.jsx("div",{className:"account-section-title",children:n.jsxs("div",{children:[n.jsx("p",{className:"eyebrow",children:"Usage"}),n.jsx("h2",{children:"用量概览"})]})}),!h&&C&&n.jsx("p",{className:"usage-placeholder",children:"正在统计用量…"}),!h&&!C&&G&&n.jsx("p",{className:"usage-placeholder",children:"用量加载失败,请稍后重试"}),h&&n.jsxs("div",{className:C?"usage-body is-refreshing":"usage-body",children:[n.jsxs("div",{className:"usage-kpi",children:[n.jsxs("div",{className:"usage-tile",children:[n.jsx("span",{children:"今日消费"}),n.jsx("strong",{children:St(h.today.cost)}),n.jsxs("small",{children:["较昨日 ",se>=0?"+":"−",St(Math.abs(se))]})]}),n.jsxs("div",{className:"usage-tile",children:[n.jsx("span",{children:"本月消费"}),n.jsx("strong",{children:St(h.month.cost)}),n.jsxs("small",{children:[gl(h.month.requests)," 次请求"]})]}),n.jsxs("div",{className:"usage-tile",children:[n.jsx("span",{children:"今日请求"}),n.jsx("strong",{children:gl(h.today.requests)}),n.jsxs("small",{children:["成功率 ",h.today.successRate,"%"]})]}),n.jsxs("div",{className:"usage-tile",children:[n.jsx("span",{children:"累计消费"}),n.jsx("strong",{children:St(h.total.cost)}),n.jsxs("small",{children:[gl(h.total.requests)," 次请求"]})]})]}),n.jsx("div",{className:"usage-range",role:"group","aria-label":"统计范围",children:[7,14,30].map(k=>n.jsxs("button",{type:"button",className:u===k?"selected":"","aria-pressed":u===k,onClick:()=>f(k),children:["近 ",k," 天"]},k))}),n.jsxs("div",{className:"usage-block",children:[n.jsxs("div",{className:"usage-block-head",children:[n.jsx("strong",{children:"每日消费"}),n.jsxs("span",{children:["近 ",h.rangeDays," 天"]})]}),q.length===0||V<=0?n.jsx("p",{className:"usage-placeholder",children:"这段时间还没有消费记录"}):n.jsxs(n.Fragment,{children:[n.jsxs("div",{className:"usage-plot",children:[n.jsx("div",{className:"usage-grid","aria-hidden":"true",children:[1,.5,0].map(k=>n.jsx("div",{className:"usage-gridline",style:{bottom:`${k*pm}%`},children:n.jsx("span",{children:St(V*k)})},k))}),n.jsxs("div",{className:"usage-columns",children:[q.map((k,xe)=>{const le=V>0?k.cost/V*pm:0,je=k.cost>0,O=je&&he!==null&&k.day===he.day,ne=xe%te===0||xe===q.length-1;return n.jsxs("div",{className:"usage-column",children:[n.jsxs("button",{type:"button",className:N===k.day?"usage-column-hit is-hovered":"usage-column-hit",onMouseEnter:()=>y(k.day),onMouseLeave:()=>y(""),onFocus:()=>y(k.day),onBlur:()=>y(""),"aria-label":`${k.day} 消费 ${St(k.cost)},${k.requests} 次请求`,children:[je&&n.jsx("span",{className:"usage-column-bar",style:{height:`${le}%`}}),O&&n.jsx("span",{className:"usage-column-peak",style:{bottom:`calc(${le}% + 6px)`},children:St(k.cost)})]}),n.jsx("span",{className:"usage-column-tick",children:ne?yp(k.day):""})]},k.day)}),pe&&n.jsxs("div",{className:"usage-tooltip",style:{left:`${(ie+.5)/q.length*100}%`},role:"status",children:[n.jsx("strong",{children:St(pe.cost)}),n.jsx("span",{children:pe.day}),n.jsxs("span",{children:[pe.requests," 次请求"]})]})]})]}),n.jsxs("details",{className:"usage-table-toggle",children:[n.jsx("summary",{children:"查看数据表"}),n.jsxs("div",{className:"usage-table",children:[n.jsxs("div",{className:"usage-table-head",children:[n.jsx("span",{children:"日期"}),n.jsx("span",{children:"请求"}),n.jsx("span",{children:"消费"})]}),q.slice().reverse().map(k=>n.jsxs("div",{className:"usage-table-row",children:[n.jsx("span",{children:k.day}),n.jsx("span",{children:k.requests}),n.jsx("span",{children:St(k.cost)})]},k.day))]})]})]})]}),n.jsxs("div",{className:"usage-block",children:[n.jsxs("div",{className:"usage-block-head",children:[n.jsx("strong",{children:"按模型拆分"}),n.jsxs("span",{children:["近 ",h.rangeDays," 天消费"]})]}),D.length===0?n.jsx("p",{className:"usage-placeholder",children:"这段时间还没有调用记录"}):n.jsx("div",{className:"usage-models",children:D.map(k=>n.jsxs("div",{className:"usage-model-row",children:[n.jsx("span",{className:"usage-model-name",title:k.model,children:k.model}),n.jsx("span",{className:"usage-model-track",children:n.jsx("span",{className:"usage-model-bar",style:{width:`${ee>0?k.cost/ee*100:0}%`}})}),n.jsx("span",{className:"usage-model-value",children:St(k.cost)})]},k.model))})]})]})]})}function bp({theme:u,setTheme:f,goHome:h,openLogin:r}){const[C,E]=b.useState(null),[G,F]=b.useState([]),[N,y]=b.useState(null),[q,D]=b.useState(!1),[V,ee]=b.useState(""),[he,te]=b.useState(""),[pe,ie]=b.useState("");async function se(){try{const[O,ne,ge]=await Promise.all([me("/api/account/me"),me("/api/catalog/models"),me("/api/account/check-in")]);E({...O,apiKeys:ye(O.apiKeys)}),F(ye(ne.models).map(ds)),y(ge.checkIn)}catch{r()}}b.useEffect(()=>{se()},[]);async function k(){if(!(!N?.enabled||N.claimed||q)){D(!0),ee("");try{const O=await me("/api/account/check-in",{method:"POST",body:JSON.stringify({})});E(ne=>ne&&{...ne,user:O.user}),y(O.checkIn),ee(`签到成功,获得 ${O.reward.toFixed(2)} 额度`)}catch(O){ee(O instanceof Error?O.message:"签到失败,请稍后重试")}finally{D(!1)}}}async function xe(){try{const O=await me("/api/account/api-keys",{method:"POST",body:JSON.stringify({name:"My API Key"})});te(O.secret),ie("新密钥只显示这一次"),await se()}catch(O){ie(O instanceof Error?O.message:"创建密钥失败")}}async function le(O){if(window.confirm("删除这个 API Key?使用它的请求会立即失效。"))try{await me(`/api/account/api-keys/${O}`,{method:"DELETE"}),E(ne=>ne&&{...ne,apiKeys:ne.apiKeys.filter(ge=>ge.id!==O)}),ie("密钥已删除")}catch(ne){ie(ne instanceof Error?ne.message:"删除密钥失败")}}async function je(){await me("/api/auth/logout",{method:"POST"}),h()}return n.jsxs("main",{className:"account-page","data-theme":u,children:[n.jsxs("header",{className:"account-topbar",children:[n.jsxs("button",{className:"auth-brand",onClick:h,children:[n.jsx("span",{className:"brand-mark",children:"C"}),n.jsx("strong",{children:"CAPI"})]}),n.jsxs("div",{className:"account-actions",children:[n.jsx("button",{className:"theme-toggle","aria-label":"切换暗色模式",onClick:()=>f(u==="dark"?"light":"dark"),children:n.jsx(Dt,{name:u==="dark"?"sun":"moon"})}),n.jsx("button",{className:"secondary-button",onClick:je,children:"退出"})]})]}),n.jsxs("section",{className:"account-content",children:[n.jsxs("div",{className:"account-heading",children:[n.jsxs("div",{children:[n.jsx("p",{className:"eyebrow",children:"My CAPI"}),n.jsx("h1",{children:C?.user.name||"账户"})]}),n.jsxs("div",{className:"account-balance",children:[n.jsx("span",{children:"余额"}),n.jsx("strong",{children:C?C.user.balance.toFixed(2):"-"})]})]}),n.jsx(gp,{}),n.jsxs("section",{className:"account-section check-in-section",children:[n.jsxs("div",{className:"account-section-title",children:[n.jsxs("div",{children:[n.jsx("p",{className:"eyebrow",children:"Daily Reward"}),n.jsx("h2",{children:"每日签到"})]}),n.jsx("button",{className:"primary-button",disabled:!N?.enabled||!!N?.claimed||q,onClick:k,children:q?"领取中":N?.claimed?"今日已签到":N?.enabled?"签到领额度":"暂未开放"})]}),n.jsxs("div",{className:"check-in-summary",children:[n.jsxs("div",{children:[n.jsx("span",{children:"今日状态"}),n.jsx("strong",{children:N?.claimed?`已领取 ${N.reward.toFixed(2)}`:N?.enabled?"等待签到":"活动关闭"})]}),n.jsxs("div",{children:[n.jsx("span",{children:"随机奖励"}),n.jsx("strong",{children:N?`${N.minReward.toFixed(2)} - ${N.maxReward.toFixed(2)}`:"-"})]}),n.jsxs("div",{children:[n.jsx("span",{children:"结算日期"}),n.jsx("strong",{children:N?.day||"北京时间"})]})]}),n.jsx("p",{className:"check-in-note",children:"每天按北京时间 00:00 刷新,奖励领取后直接计入账户余额。"}),V&&n.jsx("p",{className:"account-message check-in-message",role:"status",children:V})]}),n.jsxs("section",{className:"account-section",children:[n.jsxs("div",{className:"account-section-title",children:[n.jsxs("div",{children:[n.jsx("p",{className:"eyebrow",children:"API Keys"}),n.jsx("h2",{children:"API 密钥"})]}),n.jsx("button",{className:"primary-button",onClick:xe,children:"创建密钥"})]}),he&&n.jsx("code",{className:"one-time-secret",children:he}),pe&&n.jsx("p",{className:"account-message",children:pe}),n.jsxs("div",{className:"account-key-list",children:[C?.apiKeys?.map(O=>n.jsxs("div",{className:"account-key-item",children:[n.jsx("span",{className:"account-key-mark","aria-hidden":"true",children:n.jsx(Dt,{name:"key"})}),n.jsxs("div",{className:"account-key-info",children:[n.jsx("strong",{children:O.name}),n.jsxs("code",{children:[O.prefix,"…"]})]}),n.jsx(Rt,{tone:O.status,children:Ut(O.status)}),n.jsx("button",{className:"icon-button","aria-label":`删除 ${O.name}`,title:"删除密钥",onClick:()=>le(O.id),children:n.jsx(Dt,{name:"ban"})})]},O.id)),ye(C?.apiKeys).length===0&&n.jsx("div",{className:"empty",children:"还没有 API 密钥"})]})]}),n.jsxs("section",{className:"account-section",children:[n.jsx("div",{className:"account-section-title",children:n.jsxs("div",{children:[n.jsx("p",{className:"eyebrow",children:"Models"}),n.jsx("h2",{children:"可用模型"})]})}),n.jsxs("div",{className:"account-model-grid",children:[G.map(O=>n.jsxs("article",{children:[n.jsx("span",{children:O.vendor}),n.jsx("strong",{children:O.name}),n.jsx("p",{children:O.description}),n.jsx("code",{children:O.id})]},O.id)),G.length===0&&n.jsx("div",{className:"account-model-empty",children:"暂无可用模型,管理员配置渠道后将在此展示"})]})]})]})]})}function xp({theme:u,setTheme:f,enterConsole:h}){return n.jsxs("main",{className:"public-home","data-theme":u,children:[n.jsxs("header",{className:"home-topbar",children:[n.jsxs("div",{className:"home-brand",children:[n.jsx("div",{className:"brand-mark",children:"C"}),n.jsxs("div",{children:[n.jsx("strong",{children:"CAPI"}),n.jsx("span",{children:"AI 聚合网关"})]})]}),n.jsxs("div",{className:"home-actions",children:[n.jsxs("button",{className:"theme-toggle","aria-label":"切换暗色模式",onClick:()=>f(u==="dark"?"light":"dark"),children:[n.jsx(Dt,{name:u==="dark"?"sun":"moon"}),n.jsx("span",{children:u==="dark"?"浅色":"暗色"})]}),n.jsx("button",{className:"primary-button",onClick:h,children:"控制台"})]})]}),n.jsxs("section",{className:"home-hero",children:[n.jsxs("div",{className:"home-copy",children:[n.jsx("span",{className:"home-kicker",children:"兼容 OpenAI 格式的网关"}),n.jsxs("h1",{children:["CAPI",n.jsx("span",{children:"轻量 AI 聚合网关"})]}),n.jsx("p",{children:"面向个人用户和团队的模型接入层。把 API Key、额度、模型渠道和调用日志放在一个清爽控制台里,保持轻量,也便于排障。"}),n.jsx("div",{className:"home-cta",children:n.jsx("button",{className:"primary-button",onClick:h,children:"进入控制台"})}),n.jsxs("div",{className:"integration-row","aria-label":"网关能力概览",children:[n.jsx("span",{children:"网关能力"}),n.jsxs("div",{children:[n.jsx("span",{children:"OpenAI 兼容接口"}),n.jsx("span",{children:"额度控制"}),n.jsx("span",{children:"调用审计"})]})]})]}),n.jsxs("div",{className:"gateway-terminal","aria-label":"CAPI 终端请求示意",children:[n.jsxs("div",{className:"terminal-titlebar",children:[n.jsxs("div",{className:"terminal-dots","aria-hidden":"true",children:[n.jsx("span",{}),n.jsx("span",{}),n.jsx("span",{})]}),n.jsx("strong",{children:"CAPI Terminal"}),n.jsxs("div",{className:"terminal-status",children:[n.jsx("span",{className:"pulse-dot"}),n.jsx("strong",{children:"Online"})]})]}),n.jsxs("div",{className:"terminal-endpoint",children:[n.jsx("span",{children:"POST"}),n.jsx("strong",{children:"/v1/chat/completions"})]}),n.jsxs("div",{className:"terminal-body",children:[n.jsxs("div",{className:"terminal-block",children:[n.jsx("span",{children:"REQUEST"}),n.jsx("pre",{children:`curl https://api.capi.local/v1/chat/completions \\ + -H "Authorization: Bearer cat_..." \\ + -d '{ + "model": "capi-fast", + "messages": [{ "role": "user", "content": "ping" }] + }'`})]}),n.jsxs("div",{className:"terminal-route",children:[n.jsxs("div",{children:[n.jsx("span",{children:"auth"}),n.jsx("strong",{children:"pass"})]}),n.jsxs("div",{children:[n.jsx("span",{children:"quota"}),n.jsx("strong",{children:"ok"})]}),n.jsxs("div",{children:[n.jsx("span",{children:"route"}),n.jsx("strong",{children:"capi-fast"})]}),n.jsxs("div",{children:[n.jsx("span",{children:"latency"}),n.jsx("strong",{children:"186ms"})]})]}),n.jsxs("div",{className:"terminal-block response",children:[n.jsx("span",{children:"RESPONSE"}),n.jsxs("pre",{children:[`{ + "status": 200, + "model": "capi-fast", + "usage": { "total_tokens": 27 }, + "message": "request routed" +}`,n.jsx("span",{className:"terminal-caret","aria-hidden":"true"})]})]})]})]})]})]})}function jp({overview:u,channels:f,logs:h,onNavigate:r}){const C=h.filter(E=>E.status!=="success");return n.jsxs("section",{className:"page-stack",children:[n.jsxs("div",{className:"hero-strip",children:[n.jsxs("div",{children:[n.jsx("span",{children:"Live Gateway"}),n.jsxs("strong",{children:["CAPI 网关正在服务 ",u?.activeUsers??"-"," 个活跃用户"]}),n.jsx("p",{children:"请求进入 CAPI 后,会按额度、模型和渠道状态自动选择最合适的上游。"})]}),n.jsxs("div",{className:"live-island",children:[n.jsx("div",{className:"pulse-dot"}),n.jsx("span",{children:"在线"})]})]}),n.jsxs("div",{className:"quick-actions","aria-label":"快捷操作",children:[n.jsx(Ai,{icon:"key",label:"创建 Key",onClick:()=>r("keys")}),n.jsx(Ai,{icon:"route",label:"配置渠道",onClick:()=>r("channels")}),n.jsx(Ai,{icon:"users",label:"调整额度",onClick:()=>r("users")}),n.jsx(Ai,{icon:"logs",label:"查看异常",onClick:()=>r("logs")})]}),n.jsxs("div",{className:"metrics-grid",children:[n.jsx(fl,{label:"活跃用户",value:u?gl(u.activeUsers):"-"}),n.jsx(fl,{label:"今日请求",value:u?gl(u.requestsToday):"-"}),n.jsx(fl,{label:"今日输入",value:u?gl(u.todayInputTokens):"-"}),n.jsx(fl,{label:"今日输出",value:u?gl(u.todayOutputTokens):"-"}),n.jsx(fl,{label:"今日扣费",value:u?St(u.todayCost,4):"-"}),n.jsx(fl,{label:"账户余额",value:u?St(u.totalBalance):"-"}),n.jsx(fl,{label:"成功率",value:u?`${u.successRate}%`:"-"})]}),n.jsx(Sp,{}),n.jsxs("div",{className:"split-grid",children:[n.jsx(tt,{title:"渠道状态",children:f.length?f.map(E=>n.jsxs("div",{className:"list-row overview-channel-row",children:[n.jsxs("div",{children:[n.jsx("strong",{children:E.name}),n.jsx("span",{title:ye(E.models).join(", "),children:op(E.models)})]}),n.jsx(Rt,{tone:E.status,children:Ut(E.status)})]},E.id)):n.jsx(Xt,{text:"暂无渠道"})}),n.jsx(tt,{title:"最近请求",children:h.length?h.slice(0,4).map(E=>n.jsxs("div",{className:"list-row",children:[n.jsxs("div",{children:[n.jsx("strong",{children:lo(E)}),n.jsxs("span",{children:[E.id," · ",E.errorCode||sl(E.createdAt)]})]}),n.jsx(Rt,{tone:E.status,children:Ut(E.status)})]},E.id)):n.jsx(Xt,{text:C.length?"暂无最近请求":"暂无请求"})})]})]})}function Ai({icon:u,label:f,onClick:h}){return n.jsxs("button",{className:"quick-action",onClick:h,children:[n.jsx(Dt,{name:u}),n.jsx("span",{children:f})]})}function Sp(){const u=[{label:"认证",detail:"校验 API Key"},{label:"额度",detail:"检查余额"},{label:"路由",detail:"选择渠道"},{label:"响应",detail:"返回结果"}];return n.jsxs("section",{className:"flow-panel","aria-label":"网关流转",children:[n.jsxs("div",{className:"flow-copy",children:[n.jsx("span",{children:"Request Flow"}),n.jsx("strong",{children:"请求处理流程"})]}),n.jsx("div",{className:"flow-steps",children:u.map((f,h)=>n.jsxs("div",{className:"flow-step",children:[n.jsx("div",{className:"flow-index",children:h+1}),n.jsx("strong",{children:f.label}),n.jsx("span",{children:f.detail})]},f.label))})]})}function Np({users:u,query:f,selectedUser:h,onQuery:r,onSelect:C,onUpdate:E,onBulkUpdate:G,onCreateKey:F,groups:N,onOpenRegistration:y}){const[D,V]=b.useState(1),[ee,he]=b.useState("all"),[te,pe]=b.useState(new Set),[ie,se]=b.useState("10"),[k,xe]=b.useState(""),[le,je]=b.useState(""),[O,ne]=b.useState(!1),[ge,Te]=b.useState("10"),[ze,ue]=b.useState(""),[X,fe]=b.useState(""),[W,A]=b.useState(!1),L=ee==="all"?u:u.filter(_=>_.status===ee),H=L.filter(_=>_.role!=="admin"),Q=Math.max(1,Math.ceil(L.length/25)),ce=Math.min(D,Q),m=L.slice((ce-1)*25,ce*25),v=H.length>0&&H.every(_=>te.has(_.id));b.useEffect(()=>{V(1)},[f,ee]),b.useEffect(()=>{const _=new Set(u.map(P=>P.id));pe(P=>new Set([...P].filter(ae=>_.has(ae))))},[u]),b.useEffect(()=>{Te("10"),ue(""),fe("")},[h?.user.id]);function U(_){pe(P=>{const ae=new Set(P);return ae.has(_)?ae.delete(_):ae.add(_),ae})}function Z(){pe(_=>{const P=new Set(_);return v?H.forEach(ae=>P.delete(ae.id)):H.forEach(ae=>P.add(ae.id)),P})}async function oe(_,P){if(te.size!==0){ne(!0);try{await G([...te],_,P),pe(new Set),xe("")}finally{ne(!1)}}}async function Se(_){if(!h)return;const P=Math.abs(Number(ge));if(!Number.isFinite(P)||P<=0){fe("请输入大于 0 的金额");return}A(!0),fe("");try{await G([h.user.id],"adjust_balance",{amount:Number((P*_).toFixed(4)),reason:ze.trim()||(_>0?"管理员增加额度":"管理员扣减额度")}),fe(_>0?"额度已增加":"额度已扣减"),ue("")}catch(ae){fe(ae instanceof Error?ae.message:"额度调整失败")}finally{A(!1)}}return n.jsxs("section",{className:"users-layout",children:[n.jsxs(tt,{title:"用户管理",children:[n.jsxs("div",{className:"panel-toolbar",children:[n.jsxs("div",{className:"search-box",children:[n.jsx(Dt,{name:"search"}),n.jsx("input",{value:f,onChange:_=>r(_.target.value),placeholder:"搜索 ID、姓名或邮箱"})]}),n.jsx("button",{className:"icon-button",title:"开放注册",onClick:y,children:n.jsx(Dt,{name:"plus"})})]}),n.jsxs("div",{className:"user-summary-strip",children:[n.jsxs("span",{children:[n.jsx("strong",{children:u.length})," 匹配用户"]}),n.jsxs("span",{children:[n.jsx("strong",{children:u.filter(_=>_.status==="active").length})," 正常"]}),n.jsxs("span",{children:[n.jsx("strong",{children:u.filter(_=>_.status==="disabled").length})," 禁用"]}),n.jsxs("span",{children:[n.jsx("strong",{children:u.reduce((_,P)=>_+P.requestsToday,0)})," 今日请求"]})]}),n.jsx("div",{className:"user-filter-row",role:"group","aria-label":"用户状态筛选",children:[{value:"all",label:"全部"},{value:"active",label:"正常"},{value:"limited",label:"受限"},{value:"disabled",label:"禁用"}].map(_=>n.jsx("button",{type:"button",className:ee===_.value?"selected":"",onClick:()=>he(_.value),children:_.label},_.value))}),n.jsx("button",{type:"button",className:"secondary-button mobile-bulk-select",onClick:Z,children:v?"取消全选":`全选结果(${H.length})`}),te.size>0&&n.jsxs("div",{className:"bulk-action-bar",children:[n.jsxs("strong",{children:["已选 ",te.size," 人"]}),n.jsx("input",{type:"number",step:"0.01",value:ie,onChange:_=>se(_.target.value),"aria-label":"额度调整值"}),n.jsx("input",{value:k,onChange:_=>xe(_.target.value),placeholder:"原因,例如:活动赠送","aria-label":"调整原因"}),n.jsx("button",{type:"button",className:"secondary-button",disabled:O||!Number(ie),onClick:()=>oe("adjust_balance",{amount:Number(ie),reason:k}),children:"调整额度"}),n.jsx("button",{type:"button",className:"secondary-button",disabled:O,onClick:()=>oe("set_status",{value:"active"}),children:"启用"}),n.jsx("button",{type:"button",className:"danger-button",disabled:O,onClick:()=>oe("set_status",{value:"disabled"}),children:"禁用"}),n.jsxs("select",{className:"bulk-group-select",value:le,disabled:O,"aria-label":"批量设置分组",onChange:_=>je(_.target.value),children:[n.jsx("option",{value:"",children:"未分组"}),N.map(_=>n.jsx("option",{value:_.id,children:_.name},_.id))]}),n.jsx("button",{type:"button",className:"secondary-button",disabled:O,onClick:()=>oe("set_group",{value:le}),children:"设为分组"})]}),n.jsxs("div",{className:"table",children:[n.jsxs("div",{className:"table-head users-table",children:[n.jsx("input",{type:"checkbox",checked:v,onChange:Z,"aria-label":"选择当前筛选结果"}),n.jsx("span",{children:"用户"}),n.jsx("span",{children:"状态"}),n.jsx("span",{children:"余额"}),n.jsx("span",{children:"今日"})]}),m.map(_=>n.jsxs("div",{className:h?.user.id===_.id?"table-row users-table selected":"table-row users-table",role:"button",tabIndex:0,onClick:()=>C(_.id),onKeyDown:P=>{(P.key==="Enter"||P.key===" ")&&C(_.id)},children:[n.jsx("input",{type:"checkbox",checked:te.has(_.id),disabled:_.role==="admin",onChange:()=>U(_.id),onClick:P=>P.stopPropagation(),"aria-label":_.role==="admin"?`${_.name} 是管理员,不参与批量操作`:`选择 ${_.name}`}),n.jsxs("span",{children:[n.jsx("strong",{children:_.name}),n.jsxs("small",{children:[_.id," · ",_.email||"未绑定邮箱"]})]}),n.jsx(Rt,{tone:_.status,children:Ut(_.status)}),n.jsx("span",{children:St(_.balance)}),n.jsx("span",{children:_.requestsToday})]},_.id)),L.length===0&&n.jsx(Xt,{text:"暂无匹配用户"})]}),L.length>25&&n.jsxs("div",{className:"pagination-bar",children:[n.jsx("button",{className:"secondary-button",disabled:ce<=1,onClick:()=>V(_=>Math.max(1,_-1)),children:"上一页"}),n.jsxs("span",{children:[ce," / ",Q]}),n.jsx("button",{className:"secondary-button",disabled:ce>=Q,onClick:()=>V(_=>Math.min(Q,_+1)),children:"下一页"})]})]}),n.jsx(tt,{title:"用户详情",children:h?n.jsxs("div",{className:"detail-stack",children:[n.jsxs("div",{className:"user-hero",children:[n.jsx("div",{className:"avatar",children:h.user.name.slice(0,1)}),n.jsxs("div",{children:[n.jsx("h2",{children:h.user.name}),n.jsx("p",{children:h.user.email})]}),n.jsx(Rt,{tone:h.user.status,children:Ut(h.user.status)})]}),n.jsxs("div",{className:"settings-group",children:[n.jsx(nl,{label:"角色",value:h.user.role==="admin"?"管理员":"用户"}),n.jsx(nl,{label:"余额",value:St(h.user.balance)}),n.jsx(nl,{label:"总请求",value:String(h.user.totalRequests)}),n.jsx(nl,{label:"最后登录",value:sl(h.user.lastLoginAt)}),n.jsx(nl,{label:"API 调用",value:h.user.status==="disabled"?"关闭":"允许",switchOn:h.user.status!=="disabled"})]}),n.jsxs("div",{className:"group-assign-row",children:[n.jsxs("label",{children:[n.jsx("span",{children:"所属分组"}),n.jsxs("select",{value:h.user.groupId||"",onChange:_=>E(h.user.id,{groupId:_.target.value}),children:[n.jsx("option",{value:"",children:"未分组"}),N.map(_=>n.jsx("option",{value:_.id,children:_.name},_.id))]})]}),n.jsx("small",{children:"分组决定该用户可路由到哪些渠道;未分组用户只能使用未限制分组的渠道。"})]}),n.jsxs("div",{className:"balance-adjuster",children:[n.jsxs("div",{className:"balance-adjuster-title",children:[n.jsx("strong",{children:"调整余额"}),n.jsxs("span",{children:["当前 ",St(h.user.balance)]})]}),n.jsxs("div",{className:"balance-adjuster-fields",children:[n.jsxs("label",{children:[n.jsx("span",{children:"金额"}),n.jsx("input",{type:"number",min:"0.0001",step:"0.01",value:ge,onChange:_=>Te(_.target.value)})]}),n.jsxs("label",{children:[n.jsx("span",{children:"备注"}),n.jsx("input",{value:ze,onChange:_=>ue(_.target.value),placeholder:"可选,会记录到流水"})]})]}),n.jsxs("div",{className:"balance-adjuster-actions",children:[n.jsx("button",{className:"secondary-button",disabled:W,onClick:()=>Se(1),children:"增加"}),n.jsx("button",{className:"danger-button",disabled:W,onClick:()=>Se(-1),children:"扣减"}),n.jsx("span",{role:"status",children:X})]})]}),n.jsxs("div",{className:"action-row",children:[n.jsx("button",{className:"secondary-button",onClick:()=>F(h.user.id),children:"创建 Key"}),n.jsx("button",{className:"secondary-button",disabled:h.user.role==="admin",onClick:()=>E(h.user.id,{status:h.user.status==="disabled"?"active":"disabled"}),children:h.user.role==="admin"?"管理员保护":h.user.status==="disabled"?"解封":"禁用"})]}),n.jsxs("div",{children:[n.jsx("h3",{children:"API Key"}),h.apiKeys.map(_=>n.jsxs("div",{className:"list-row",children:[n.jsxs("div",{children:[n.jsx("strong",{children:_.name}),n.jsxs("span",{children:[_.prefix,"*** · ",_.requestCount," 次"]})]}),n.jsx(Rt,{tone:_.status,children:Ut(_.status)})]},_.id)),h.apiKeys.length===0&&n.jsx(Xt,{text:"暂无 API Key"})]})]}):n.jsx(Xt,{text:"请选择一个用户"})})]})}function Ap({selectedUser:u,onCreateKey:f,onUpdateKey:h,onDeleteKey:r}){return n.jsx(tt,{title:"密钥管理",children:u?n.jsxs(n.Fragment,{children:[n.jsxs("div",{className:"panel-toolbar",children:[n.jsxs("span",{className:"muted-inline",children:[u.user.name," · 完整 Key 只在创建时显示,丢失请重新创建"]}),n.jsx("button",{className:"primary-button",onClick:()=>f(u.user.id),children:"创建 Key"})]}),u.apiKeys.length?u.apiKeys.map(C=>n.jsx(Cp,{apiKey:C,onSave:h,onDelete:r},C.id)):n.jsx(Xt,{text:"暂无密钥"})]}):n.jsx(Xt,{text:"请选择一个用户"})})}function Cp({apiKey:u,onSave:f,onDelete:h}){const[r,C]=b.useState(u.name),[E,G]=b.useState(ye(u.allowedModels).join(", ")),[F,N]=b.useState(mm(u.expiresAt||"")),[y,q]=b.useState(String(u.rateLimitPerMinute||"")),[D,V]=b.useState(!1),ee=ye(u.allowedModels).length?ye(u.allowedModels).join(", "):"全部模型";b.useEffect(()=>{C(u.name),G(ye(u.allowedModels).join(", ")),N(mm(u.expiresAt||"")),q(String(u.rateLimitPerMinute||""))},[u]);async function he(){V(!0);try{await f(u.id,{name:r.trim()||"API Key",allowedModels:Pc(E),expiresAt:sp(F),rateLimitPerMinute:Number(y||0)})}finally{V(!1)}}return n.jsxs("details",{className:"key-editor key-editor-collapsible",children:[n.jsxs("summary",{className:"key-editor-head",children:[n.jsxs("div",{children:[n.jsx("strong",{children:u.name}),n.jsxs("span",{children:[u.prefix,"*** · ",ee," · 最后使用 ",sl(u.lastUsedAt)]})]}),n.jsxs("div",{className:"row-actions",children:[n.jsx(Rt,{tone:u.status,children:Ut(u.status)}),n.jsx("span",{className:"key-expand-hint",children:"管理"})]})]}),n.jsxs("div",{className:"key-editor-body",children:[n.jsxs("div",{className:"key-editor-grid",children:[n.jsxs("label",{children:["名称",n.jsx("input",{value:r,onChange:te=>C(te.target.value)})]}),n.jsxs("label",{children:["允许模型",n.jsx("input",{value:E,onChange:te=>G(te.target.value),placeholder:"留空表示全部模型,多个用逗号分隔"})]}),n.jsxs("label",{children:["过期时间",n.jsx("input",{type:"datetime-local",value:F,onChange:te=>N(te.target.value)})]}),n.jsxs("label",{children:["每分钟限制",n.jsx("input",{type:"number",min:"0",value:y,onChange:te=>q(te.target.value),placeholder:"0 使用全局限制"})]})]}),n.jsxs("div",{className:"key-editor-actions",children:[n.jsx("button",{className:"secondary-button",onClick:()=>f(u.id,{status:u.status==="active"?"disabled":"active"}),children:u.status==="active"?"停用密钥":"启用密钥"}),n.jsx("button",{className:"danger-button",onClick:()=>h(u.id),children:"删除密钥"}),n.jsx("button",{className:"primary-button",disabled:D,onClick:he,children:D?"保存中":"保存设置"})]})]})]})}function Tp({models:u,onCopy:f,onCreate:h,onUpdate:r,onDelete:C}){const E=u.filter(H=>H.recommended),[G,F]=b.useState(""),[N,y]=b.useState("all"),[q,D]=b.useState("all"),[V,ee]=b.useState(1),[he,te]=b.useState(""),[pe,ie]=b.useState(""),[se,k]=b.useState(""),[xe,le]=b.useState(""),[je,O]=b.useState(""),[ne,ge]=b.useState(""),Te=60,ze=b.useMemo(()=>{const H=new Map;return u.forEach(Q=>{const ce=$c(Q);H.set(ce,(H.get(ce)||0)+1)}),Array.from(H.entries()).sort((Q,ce)=>ce[1]-Q[1]||dl(Q[0]).localeCompare(dl(ce[0]))).map(([Q,ce])=>({provider:Q,count:ce}))},[u]),ue=b.useMemo(()=>{const H=G.trim().toLowerCase();return u.filter(Q=>N!=="all"&&$c(Q)!==N||q!=="all"&&Q.status!==q?!1:H?[Q.id,Q.name,Q.vendor,Q.category,Q.description,...ye(Q.aliases)].join(" ").toLowerCase().includes(H):!0)},[u,G,N,q]),X=b.useMemo(()=>{const H=new Map;ue.forEach(U=>{const Z=$c(U);H.set(Z,[...H.get(Z)||[],U])});const Q=[];let ce=[],m=0;const v=Array.from(H.entries()).sort((U,Z)=>dl(U[0]).localeCompare(dl(Z[0])));for(const U of v)ce.length>0&&m+U[1].length>Te&&(Q.push(ce),ce=[],m=0),ce.push(U),m+=U[1].length;return ce.length>0&&Q.push(ce),Q},[ue]),fe=Math.max(1,X.length),W=Math.min(V,fe),A=X[W-1]||[];b.useEffect(()=>{ee(1)},[G,N,q,u.length]);async function L(H){H.preventDefault();const Q=he.trim();if(Q){ge("");try{await h({id:Q,name:pe.trim()||Q,vendor:se.trim()||"Custom",aliases:xe.split(",").map(ce=>ce.trim()).filter(Boolean),category:"通用",description:je.trim(),price:"自定义",context:"未配置上下文"}),te(""),ie(""),k(""),le(""),O("")}catch(ce){ge(ce instanceof Error?ce.message:"模型添加失败")}}}return n.jsxs("section",{className:"models-page",children:[n.jsx("div",{className:"model-hero",children:n.jsxs("div",{children:[n.jsx("span",{children:"Model Catalog"}),n.jsx("strong",{children:"Models"})]})}),n.jsx(tt,{title:"新增模型",children:n.jsxs("form",{className:"model-create-form",onSubmit:L,children:[n.jsx("input",{value:he,onChange:H=>te(H.target.value),placeholder:"模型 ID,例如 openai/gpt-4.1"}),n.jsx("input",{value:pe,onChange:H=>ie(H.target.value),placeholder:"显示名称(可选)"}),n.jsx("input",{value:se,onChange:H=>k(H.target.value),placeholder:"供应商(可选)"}),n.jsx("input",{value:xe,onChange:H=>le(H.target.value),placeholder:"代称,多个用逗号分隔(可选)"}),n.jsx("input",{className:"model-create-wide",value:je,onChange:H=>O(H.target.value),placeholder:"描述(可选)"}),n.jsx("button",{className:"primary-button",type:"submit",children:"新增模型"}),n.jsx("div",{className:"model-create-message",role:"status",children:ne})]})}),E.length>0&&n.jsx(tt,{title:"推荐模型",children:n.jsx("div",{className:"model-grid",children:E.map(H=>n.jsx(Mp,{model:H,featured:!0,onCopy:f,onUpdate:r},H.id))})}),n.jsxs(tt,{title:"全部模型",children:[n.jsxs("div",{className:"panel-toolbar model-list-toolbar",children:[n.jsx("input",{value:G,onChange:H=>F(H.target.value),placeholder:"搜索模型 ID、名称、供应商或代称"}),n.jsx("div",{className:"model-filter-actions",children:[{value:"all",label:"全部"},{value:"available",label:"可用"},{value:"disabled",label:"禁用"}].map(H=>n.jsx("button",{type:"button",className:q===H.value?"selected":"",onClick:()=>D(H.value),children:H.label},H.value))})]}),n.jsxs("div",{className:"model-provider-filter","aria-label":"按供应商筛选模型",children:[n.jsxs("button",{type:"button",className:N==="all"?"selected":"",onClick:()=>y("all"),children:[n.jsx("span",{className:"provider-icon provider-icon-all","aria-hidden":"true",children:"All"}),n.jsx("strong",{children:"全部"}),n.jsx("small",{children:u.length})]}),ze.map(H=>n.jsxs("button",{type:"button",className:N===H.provider?"selected":"",onClick:()=>y(H.provider),children:[n.jsx(hm,{provider:H.provider}),n.jsx("strong",{children:dl(H.provider)}),n.jsx("small",{children:H.count})]},H.provider))]}),n.jsxs("div",{className:"model-list-summary",children:[n.jsx("span",{children:N==="all"?"全部供应商":dl(N)}),n.jsx("strong",{children:ue.length}),n.jsx("span",{children:"个模型"})]}),ue.length>0?n.jsxs(n.Fragment,{children:[n.jsx("div",{className:"model-provider-groups",children:A.map(([H,Q])=>n.jsxs("section",{className:"model-provider-group",children:[n.jsxs("header",{children:[n.jsx(hm,{provider:H}),n.jsxs("div",{children:[n.jsx("strong",{children:dl(H)}),n.jsxs("span",{children:[Q.length," 个模型"]})]})]}),n.jsx("div",{className:"model-compact-grid",children:Q.map(ce=>n.jsx(Ep,{model:ce,onCopy:f,onUpdate:r,onDelete:C},ce.id))})]},H))}),fe>1&&n.jsxs("div",{className:"pager",children:[n.jsx("button",{className:"secondary-button compact-button",onClick:()=>ee(H=>Math.max(1,H-1)),disabled:W<=1,children:"上一页"}),n.jsxs("span",{children:[W," / ",fe]}),n.jsx("button",{className:"secondary-button compact-button",onClick:()=>ee(H=>Math.min(fe,H+1)),disabled:W>=fe,children:"下一页"})]})]}):n.jsx(Xt,{text:u.length?"没有匹配的模型":"暂无模型,请先添加你要开放给用户调用的模型 ID"})]})]})}function Ep({model:u,onCopy:f,onUpdate:h,onDelete:r}){const C=ye(u.aliases).slice(0,3),E=u.status==="disabled";return n.jsxs("article",{className:"model-compact-row",children:[n.jsxs("div",{className:"model-compact-main",children:[n.jsx("strong",{children:u.name}),n.jsx("small",{children:u.id}),C.length>0&&n.jsx("span",{children:C.map(G=>n.jsx("em",{children:G},G))})]}),n.jsxs("div",{className:"model-row-actions",children:[n.jsx(Rt,{tone:u.status,children:Ut(u.status)}),u.recommended&&n.jsx("span",{className:"model-recommended-mark",children:"推荐"}),n.jsx("button",{className:"icon-button",title:"复制模型 ID",onClick:()=>f(u.id,"模型 ID 已复制"),children:n.jsx(Dt,{name:"copy"})}),n.jsxs("details",{className:"model-row-menu",children:[n.jsx("summary",{"aria-label":"模型操作",children:"•••"}),n.jsxs("div",{children:[n.jsx("button",{type:"button",onClick:()=>h(u.id,{recommended:!u.recommended}),children:u.recommended?"取消推荐":"设为推荐"}),n.jsx("button",{type:"button",onClick:()=>h(u.id,{status:E?"available":"disabled"}),children:E?"启用模型":"停用模型"}),n.jsx("button",{className:"is-danger",type:"button",onClick:()=>r(u.id),children:"删除模型"})]})]})]})]})}function Mp({model:u,featured:f=!1,onCopy:h,onUpdate:r}){return n.jsxs("article",{className:f?"model-card featured":"model-card",children:[n.jsxs("div",{className:"model-card-head",children:[n.jsxs("div",{children:[n.jsx("strong",{children:u.name}),n.jsxs("span",{children:[u.vendor," · ",u.category]})]}),n.jsx(Rt,{tone:u.status,children:Ut(u.status)})]}),n.jsx("p",{children:u.description}),n.jsx("div",{className:"alias-row",children:ye(u.aliases).map(C=>n.jsx("span",{children:C},C))}),n.jsxs("div",{className:"model-meta",children:[n.jsxs("span",{children:["价格:",u.price]}),n.jsx("span",{children:u.context})]}),n.jsxs("div",{className:"model-id",children:[n.jsx("code",{children:u.id}),n.jsx("button",{className:"icon-button",title:"复制模型 ID",onClick:()=>h(u.id,"模型 ID 已复制"),children:n.jsx(Dt,{name:"copy"})})]}),r&&n.jsxs("div",{className:"model-card-actions",children:[n.jsx("button",{className:"secondary-button compact-button",type:"button",onClick:()=>r(u.id,{recommended:!1}),children:"取消推荐"}),n.jsx("button",{className:"secondary-button compact-button",type:"button",onClick:()=>r(u.id,{status:u.status==="disabled"?"available":"disabled"}),children:u.status==="disabled"?"启用":"停用"})]})]})}function vm({channels:u,onCreate:f,onImport:h,onCheckAccounts:r,onDeduplicateAccounts:C,onDeleteAccount:E,onUpdate:G,onStartOAuth:F,onCompleteOAuth:N,variant:y="drawing"}){const q=y==="antigravity",D=u.filter(v=>q?v.provider==="antigravity":v.provider!=="antigravity"&&(v.provider==="codex"||v.provider==="openai"||ye(v.models).some(U=>U.includes("image")))),[V,ee]=b.useState(""),[he,te]=b.useState({}),[pe,ie]=b.useState({}),[se,k]=b.useState({}),[xe,le]=b.useState({}),[je,O]=b.useState(""),[ne,ge]=b.useState(""),[Te,ze]=b.useState(""),[ue,X]=b.useState(""),fe=24,W=48;async function A(v,U){ee(`import:${v}`);try{await h(v,U)}finally{ee("")}}async function L(v,U=!1,Z=""){ee(Z?`check-account:${Z}`:`${U?"retry":"check"}:${v}`);try{await r(v,U,Z)}finally{ee("")}}async function H(v){ee(`dedupe:${v}`);try{await C(v)}finally{ee("")}}function Q(v){const U=ye(v.openaiAccounts).map(ae=>[ae.email||ae.name||ae.accountId||ae.id,ae.accountId||"",ae.status||"unchecked",ae.credentialMode||"access-token",ae.planType||"",ae.expiresAt||"",ae.lastCheckedAt||"",ae.lastUsedAt||"",String(ae.requestCount||0),ae.lastErrorCode||"",ae.lastError||""]),Z=ae=>`"${ae.replace(/"/g,'""')}"`,oe=[["账号","Account ID","状态","凭据方式","套餐","到期时间","最近检测","最近调用","调用次数","错误码","最后错误"],...U].map(ae=>ae.map(Z).join(",")).join(`\r +`),Se=new Blob(["\uFEFF"+oe],{type:"text/csv;charset=utf-8"}),_=URL.createObjectURL(Se),P=document.createElement("a");P.href=_,P.download=`${v.name||"account-pool"}-health-report.csv`,P.click(),URL.revokeObjectURL(_)}async function ce(v){ee(`status:${v.id}`);try{await G(v.id,{status:v.status==="disabled"?"healthy":"disabled",baseUrl:v.baseUrl||zi(v.provider)||Mi,provider:v.provider||"codex",models:ye(v.models).length?v.models:jm.split(",").map(U=>U.trim())})}finally{ee("")}}async function m(v,U){const Z=U.email||U.name||U.accountId||U.id;if(window.confirm(`删除账号「${Z}」?`)){ee(`delete-account:${U.id}`);try{await E(v.id,U.id)}finally{ee("")}}}return n.jsxs(tt,{title:q?"Antigravity 账号池":"账号池",children:[n.jsxs("div",{className:"panel-toolbar",children:[n.jsx("span",{className:"muted-inline",children:q?"导入 Google OAuth JSON 或 refresh token 即可入池。":"账号有两种来源,任选其一即可。"}),q?n.jsx("button",{className:"primary-button",onClick:()=>f(Ic("antigravity")),children:"新增 Antigravity 账号池"}):n.jsx("button",{className:"primary-button",onClick:()=>f(Ic("codex")),children:"新增账号池渠道"})]}),n.jsx("div",{className:"source-guide",children:q?n.jsxs(n.Fragment,{children:[n.jsxs("div",{className:"source-guide-item",children:[n.jsx("strong",{children:"Antigravity 导入(推荐)"}),n.jsx("span",{children:"粘贴 Google OAuth 凭据 JSON 或 refresh token,调用前自动换取 accessToken。"})]}),n.jsxs("div",{className:"source-guide-item",children:[n.jsx("strong",{children:"批量导入"}),n.jsx("span",{children:"支持 JSON、ZIP、TXT;TXT 可使用 JSONL 或每行一个 refresh token。"})]})]}):n.jsxs(n.Fragment,{children:[n.jsxs("div",{className:"source-guide-item",children:[n.jsx("strong",{children:"网页会话(推荐)"}),n.jsx("span",{children:"支持完整 auth/session JSON 或浏览器 Session Cookie,并在调用前重新获取 accessToken。"})]}),n.jsxs("div",{className:"source-guide-item",children:[n.jsx("strong",{children:"批量导入"}),n.jsx("span",{children:"支持 JSON、ZIP、TXT;TXT 可使用 JSONL 或每行一个 access token。"})]})]})}),n.jsxs("div",{className:"channels-stack",children:[D.map(v=>{const U=ye(v.openaiAccounts),Z=U.filter(J=>J.status==="healthy").length,oe=U.filter(J=>J.status==="invalid").length,Se=U.filter(J=>!!J.lastErrorCode).length,_=Math.max(0,U.length-Z-oe),P=U.filter(J=>J.credentialMode==="refreshable").length,ae=U.filter(J=>J.credentialMode==="browser-session").length,Ze=Math.max(0,U.length-P-ae),$=pe[v.id]||"all",Je=(se[v.id]||"").trim().toLowerCase(),$e=U.filter(J=>{const ot=$==="all"||$==="attention"&&Gp(J)||$==="invalid"&&J.status==="invalid"||$==="error"&&!!J.lastErrorCode||$==="refreshable"&&J.credentialMode==="refreshable",Zt=`${J.email||""} ${J.name||""} ${J.accountId||""} ${J.userId||""} ${J.lastError||""}`.toLowerCase();return ot&&(!Je||Zt.includes(Je))}),We=xe[v.id]||"pool",ft=[...$e].sort((J,ot)=>{if(We==="pool")return 0;const Zt=We==="expiry"?J.expiresAt||"9999-12-31":J.lastUsedAt||"",bl=We==="expiry"?ot.expiresAt||"9999-12-31":ot.lastUsedAt||"";return We==="recent"?bl.localeCompare(Zt):Zt.localeCompare(bl)}),Nt=Math.min(ft.length,he[v.id]||fe),At=ft.slice(0,Nt),il=Math.max(0,ft.length-At.length),Kt=il===0;return n.jsxs("div",{className:"channel-card",children:[n.jsxs("div",{className:"channel-card-head",children:[n.jsxs("div",{children:[n.jsx("strong",{children:v.name}),n.jsx("span",{children:v.baseUrl||zi(v.provider)||Mi}),n.jsxs("small",{children:["账号 ",U.length," 个,可用 ",Z,",无效 ",oe,",未验证 ",_]}),n.jsxs("small",{children:["可续期 ",P," · 网页会话 ",ae," · 仅 Token ",Ze]}),n.jsxs("small",{children:["自动检测 ",v.lastCheckedAt?sl(v.lastCheckedAt):"等待首次检测"]})]}),n.jsxs("div",{className:"channel-card-head-actions",children:[n.jsx(Rt,{tone:v.status,children:Ut(v.status)}),n.jsx("button",{className:"primary-button compact-button",onClick:()=>X(v.id),disabled:V!=="",children:"添加账号"}),n.jsx("button",{className:"secondary-button compact-button",onClick:()=>L(v.id),disabled:V!==""||U.length===0,children:V===`check:${v.id}`?"检测中":"批量检测"}),oe>0&&n.jsx("button",{className:"secondary-button compact-button",onClick:()=>L(v.id,!0),disabled:V!=="",children:V===`retry:${v.id}`?"复检中":`复检无效 ${oe}`}),U.length>1&&n.jsx("button",{className:"secondary-button compact-button",onClick:()=>H(v.id),disabled:V!=="",children:V===`dedupe:${v.id}`?"去重中":"账号去重"}),U.length>0&&n.jsx("button",{className:"secondary-button compact-button",onClick:()=>Q(v),disabled:V!=="",children:"导出报告"}),n.jsx("button",{className:"secondary-button compact-button",onClick:()=>ce(v),disabled:V!=="",children:v.status==="disabled"?"启用渠道":"停用渠道"})]})]}),n.jsxs("div",{className:"account-filter-bar",role:"group","aria-label":"账号筛选",children:[n.jsx("input",{value:se[v.id]||"",onChange:J=>k(ot=>({...ot,[v.id]:J.target.value})),placeholder:"搜索账号或错误","aria-label":"搜索账号"}),n.jsxs("select",{value:We,onChange:J=>le(ot=>({...ot,[v.id]:J.target.value})),"aria-label":"账号排序",children:[n.jsx("option",{value:"pool",children:"账号池顺序"}),n.jsx("option",{value:"oldest",children:"最久未用优先"}),n.jsx("option",{value:"recent",children:"最近使用优先"}),n.jsx("option",{value:"expiry",children:"最早到期优先"})]}),[["all",`全部 ${U.length}`],["attention","需关注"],["invalid",`无效 ${oe}`],["error",`有错误 ${Se}`],["refreshable",`可续期 ${P}`]].map(([J,ot])=>n.jsx("button",{type:"button",className:$===J?"selected":"",onClick:()=>ie(Zt=>({...Zt,[v.id]:J})),children:ot},J))]}),n.jsxs("div",{className:"metrics-grid",children:[n.jsx(fl,{label:"账号总数",value:U.length}),n.jsx(fl,{label:"可用账号",value:Z}),n.jsx(fl,{label:"无效账号",value:oe}),n.jsx(fl,{label:"未验证账号",value:_})]}),n.jsx("div",{className:"drawing-channel-models",children:ye(v.models).length?ye(v.models).map(J=>n.jsx("span",{children:J},J)):n.jsx("span",{children:"未绑定绘图模型"})}),U.length>0&&n.jsxs("div",{className:"account-pool-list",children:[At.map(J=>n.jsxs("div",{className:"account-pool-row",children:[n.jsxs("div",{className:"account-pool-main",children:[n.jsxs("div",{children:[n.jsxs("div",{className:"account-pool-title",children:[n.jsx("strong",{title:J.email||J.name||J.accountId||J.id,children:J.email||J.name||J.accountId||J.id}),n.jsx("span",{className:`source-tag source-tag-${J.source==="web-login"?"web":"manual"}`,children:J.source==="web-login"?"网页登录":J.source==="web-oauth"?"网页 OAuth":J.source==="oauth"?"Codex OAuth":J.source==="antigravity"||J.source==="agy"||J.source==="google-antigravity"?"Antigravity":"导入"})]}),n.jsx("span",{children:J.lastError?`${fm(J.lastErrorCode)}${fm(J.lastErrorCode)?" · ":""}${J.lastError}`:J.lastCheckedAt?`上次检测 ${sl(J.lastCheckedAt)}`:"未检测"}),n.jsxs("span",{children:[J.credentialMode==="refreshable"?"可自动续期":J.credentialMode==="browser-session"?"依赖网页会话":"仅 access token",J.expiresAt?` · 到期 ${sl(J.expiresAt)}`:""]}),n.jsxs("span",{children:["套餐 ",ap(J.planType)]}),J.lastUsedAt&&n.jsxs("span",{children:["最近调用 ",sl(J.lastUsedAt)," · ",J.requestCount||0," 次"]})]}),n.jsx(wp,{limits:J.quotaLimits})]}),n.jsxs("div",{className:"account-pool-meta",children:[n.jsx(Rt,{tone:J.status==="healthy"?"healthy":J.status==="invalid"?"disabled":"standby",children:J.status==="healthy"?"可用":J.status==="invalid"?"无效":"未验证"}),n.jsx("button",{type:"button",className:"secondary-button compact-button",disabled:V!=="",onClick:()=>L(v.id,!1,J.id),children:V===`check-account:${J.id}`?"检测中":"检测"}),n.jsx("button",{type:"button",className:"danger-button compact-button",disabled:V!=="",onClick:()=>m(v,J),children:V===`delete-account:${J.id}`?"删除中":"删除"})]})]},J.id)),$e.length===0&&n.jsx(Xt,{text:"没有匹配的账号"}),ft.length>fe&&n.jsxs("div",{className:"account-pool-more",children:[n.jsx("span",{className:"muted-inline",children:Kt?`已显示全部 ${ft.length} 个账号`:`已显示 ${At.length} 个,还有 ${il} 个`}),n.jsxs("div",{className:"account-pool-more-actions",children:[!Kt&&n.jsxs("button",{type:"button",className:"secondary-button compact-button",onClick:()=>te(J=>({...J,[v.id]:Math.min(ft.length,Nt+W)})),children:["再显示 ",Math.min(W,il)," 个"]}),!Kt&&n.jsx("button",{type:"button",className:"secondary-button compact-button",onClick:()=>te(J=>({...J,[v.id]:ft.length})),children:"全部显示"}),Nt>fe&&n.jsx("button",{type:"button",className:"secondary-button compact-button",onClick:()=>te(J=>({...J,[v.id]:fe})),children:"收起"})]})]})]})]},v.id)}),D.length===0&&n.jsx(Xt,{text:q?"暂无 Antigravity 渠道,先新增一个 Antigravity 账号池":"暂无绘图渠道,先新增一个 OpenAI 账号池渠道"})]}),je&&n.jsx(Rp,{channelId:je,variant:q?"antigravity":"openai",onStart:F,onComplete:N,onClose:()=>O("")}),ue&&n.jsx(zp,{busy:V!=="",variant:y,onAuthSession:()=>{ge(ue),X("")},onAntigravity:()=>{ze(ue),X("")},onOAuth:()=>{O(ue),X("")},onImport:async v=>{await A(ue,v),X("")},onClose:()=>X("")}),ne&&n.jsx(_p,{onImport:async v=>{await A(ne,new File([JSON.stringify(Op(v))],"authsession.json",{type:"application/json"}))},onClose:()=>ge("")}),Te&&n.jsx(Up,{onImport:async v=>{await A(Te,new File([JSON.stringify(Dp(v))],"antigravity.json",{type:"application/json"}))},onClose:()=>ze("")})]})}function zp({busy:u,variant:f="drawing",onAuthSession:h,onAntigravity:r,onOAuth:C,onImport:E,onClose:G}){const F=f==="antigravity";return n.jsx("div",{className:"modal-backdrop",onClick:G,children:n.jsxs("div",{className:"modal-card account-add-modal",onClick:N=>N.stopPropagation(),children:[n.jsxs("div",{className:"modal-head",children:[n.jsxs("div",{children:[n.jsx("strong",{children:"添加账号"}),n.jsx("span",{children:"选择一种账号接入方式"})]}),n.jsx("button",{type:"button",className:"icon-button",onClick:G,children:"×"})]}),n.jsxs("div",{className:"account-add-options",children:[F?n.jsxs(n.Fragment,{children:[n.jsxs("button",{type:"button",className:"account-add-option recommended",onClick:C,disabled:u,children:[n.jsx("span",{className:"account-add-icon",children:"G"}),n.jsx("strong",{children:"本地授权登录"}),n.jsx("small",{children:"用 Google 账号登录授权,自动获取 refresh token"})]}),n.jsxs("button",{type:"button",className:"account-add-option",onClick:r,disabled:u,children:[n.jsx("span",{className:"account-add-icon",children:"T"}),n.jsx("strong",{children:"导入 Antigravity 凭据"}),n.jsx("small",{children:"已有 Google OAuth JSON 或 refresh token 时粘贴"})]})]}):n.jsxs("button",{type:"button",className:"account-add-option recommended",onClick:h,disabled:u,children:[n.jsx("span",{className:"account-add-icon",children:"A"}),n.jsx("strong",{children:"导入网页会话"}),n.jsx("small",{children:"粘贴完整 authsession JSON,保留 sessionToken"})]}),n.jsxs("label",{className:`account-add-option${u?" disabled":""}`,children:[n.jsx("span",{className:"account-add-icon",children:"J"}),n.jsx("strong",{children:u?"导入中":"导入 JSON / ZIP / TXT"}),n.jsx("small",{children:"批量导入已有账号文件"}),n.jsx("input",{type:"file",accept:"application/json,application/zip,text/plain,.json,.zip,.txt",disabled:u,onChange:N=>{const y=N.target.files?.[0];y&&E(y),N.target.value=""}})]})]}),n.jsx("div",{className:"modal-actions",children:n.jsx("button",{type:"button",className:"secondary-button",onClick:G,children:"取消"})})]})})}function Op(u){const f=u.trim(),h=f.match(/(?:^|[;\s])(__Secure-(?:next-auth|authjs)\.session-token)=([^;\s]+)/);if(h)return{sessionToken:h[2],source:"web-login"};try{const r=JSON.parse(f),C=r.tokens&&typeof r.tokens=="object"?r.tokens:{},E=q=>typeof r[q]=="string"?r[q]:typeof C[q]=="string"?C[q]:"",G=E("accessToken")||E("access_token"),F=E("refreshToken")||E("refresh_token"),N=E("sessionToken")||E("session_token"),y=r.user&&typeof r.user=="object"?r.user:{};if(G||F||N)return{accessToken:G||void 0,refreshToken:F||void 0,sessionToken:N||void 0,email:typeof y.email=="string"?y.email:void 0,name:typeof y.name=="string"?y.name:void 0,source:"web-login"}}catch{}return{sessionToken:f,source:"web-login"}}function _p({onImport:u,onClose:f}){const[h,r]=b.useState(""),[C,E]=b.useState(!1),[G,F]=b.useState(""),[N,y]=b.useState("");async function q(){if(!h.trim()){F("请粘贴 authsession");return}E(!0),F(""),y("");try{await u(h.trim()),r(""),y("已导入,继续粘贴下一条即可")}catch(D){F(D instanceof Error?D.message:"导入失败")}finally{E(!1)}}return n.jsx("div",{className:"modal-backdrop",onClick:f,children:n.jsxs("div",{className:"modal-card",onClick:D=>D.stopPropagation(),children:[n.jsxs("div",{className:"modal-head",children:[n.jsx("strong",{children:"添加网页会话"}),n.jsx("button",{type:"button",className:"icon-button",onClick:f,children:"×"})]}),n.jsxs("p",{className:"muted-inline",children:["可直接粘贴 chatgpt.com/api/auth/session 的完整 JSON;也支持浏览器 ",n.jsx("code",{children:"__Secure-next-auth.session-token"})," 的值或完整 Cookie 字符串。"]}),n.jsxs("label",{className:"authsession-field",children:[n.jsx("span",{children:"authsession"}),n.jsx("textarea",{autoFocus:!0,value:h,onChange:D=>r(D.target.value),placeholder:"eyJhbGci..."})]}),G&&n.jsx("div",{className:"form-error",children:G}),N&&n.jsx("div",{className:"form-success",children:N}),n.jsxs("div",{className:"modal-actions",children:[n.jsx("button",{type:"button",className:"secondary-button",onClick:f,children:"取消"}),n.jsx("button",{type:"button",className:"primary-button",disabled:C,onClick:q,children:C?"导入中":"导入账号"})]})]})})}function Dp(u){const f=u.trim();try{const h=JSON.parse(f),r=h.tokens&&typeof h.tokens=="object"?h.tokens:{},C=(...y)=>{for(const q of y){if(typeof h[q]=="string"&&h[q])return h[q];if(typeof r[q]=="string"&&r[q])return r[q]}return""},E=C("access_token","accessToken"),G=C("refresh_token","refreshToken"),F=C("email"),N=C("expired","expires_at","expiresAt","expiry");if(E||G)return{accessToken:E||void 0,refreshToken:G||void 0,email:F||void 0,expired:N||void 0,source:"antigravity"}}catch{}return{refreshToken:f,source:"antigravity"}}function Up({onImport:u,onClose:f}){const[h,r]=b.useState(""),[C,E]=b.useState(!1),[G,F]=b.useState(""),[N,y]=b.useState("");async function q(){if(!h.trim()){F("请粘贴 Antigravity OAuth JSON 或 refresh token");return}E(!0),F(""),y("");try{await u(h.trim()),r(""),y("已导入,继续粘贴下一条即可")}catch(D){F(D instanceof Error?D.message:"导入失败")}finally{E(!1)}}return n.jsx("div",{className:"modal-backdrop",onClick:f,children:n.jsxs("div",{className:"modal-card",onClick:D=>D.stopPropagation(),children:[n.jsxs("div",{className:"modal-head",children:[n.jsx("strong",{children:"导入 Antigravity 账号"}),n.jsx("button",{type:"button",className:"icon-button",onClick:f,children:"×"})]}),n.jsxs("p",{className:"muted-inline",children:["粘贴 Antigravity 的 Google OAuth 凭证 JSON(含 ",n.jsx("code",{children:"access_token"})," / ",n.jsx("code",{children:"refresh_token"}),"),或直接粘贴 refresh token。导入后会自动刷新并加入账号池。"]}),n.jsxs("label",{className:"authsession-field",children:[n.jsx("span",{children:"OAuth JSON / refresh token"}),n.jsx("textarea",{autoFocus:!0,value:h,onChange:D=>r(D.target.value),placeholder:'{"access_token":"ya29...","refresh_token":"1//..."}'})]}),G&&n.jsx("div",{className:"form-error",children:G}),N&&n.jsx("div",{className:"form-success",children:N}),n.jsxs("div",{className:"modal-actions",children:[n.jsx("button",{type:"button",className:"secondary-button",onClick:f,children:"取消"}),n.jsx("button",{type:"button",className:"primary-button",disabled:C,onClick:q,children:C?"导入中":"导入账号"})]})]})})}function Rp({channelId:u,variant:f="openai",onStart:h,onComplete:r,onClose:C}){const E=f==="antigravity",[G,F]=b.useState(""),[N,y]=b.useState(""),[q,D]=b.useState(""),[V,ee]=b.useState(!1),[he,te]=b.useState("");async function pe(){ee(!0),te("");try{const se=await h(u);F(se.authorizeUrl),y(se.state),window.open(se.authorizeUrl,"_blank","noopener")}catch(se){te(se instanceof Error?se.message:"发起授权失败")}finally{ee(!1)}}async function ie(){if(!q.trim()){te("请粘贴授权完成后浏览器跳转的回调地址");return}ee(!0),te("");try{await r(u,{callbackUrl:q.trim(),state:N}),C()}catch(se){te(se instanceof Error?se.message:"完成授权失败")}finally{ee(!1)}}return n.jsx("div",{className:"modal-backdrop",onClick:C,children:n.jsxs("div",{className:"modal-card",onClick:se=>se.stopPropagation(),children:[n.jsxs("div",{className:"modal-head",children:[n.jsx("strong",{children:E?"本地授权添加 Antigravity 账号":"OAuth 授权添加网页账号"}),n.jsx("button",{type:"button",className:"icon-button",onClick:C,children:"×"})]}),n.jsx("p",{className:"muted-inline",children:E?"用 Google 账号登录授权 Antigravity 客户端,服务端换取 refresh_token 后加入账号池,凭据只保存在本服务。":"使用 ChatGPT 网页兼容的 OAuth 客户端获取 refresh_token,只调用网页 backend-api,不会走 Codex 接口。"}),n.jsxs("ol",{className:"oauth-steps",children:[n.jsxs("li",{children:[n.jsx("button",{type:"button",className:"primary-button",onClick:pe,disabled:V,children:G?"重新生成授权链接":"① 生成授权链接并打开"}),G&&n.jsxs("div",{className:"oauth-link",children:[n.jsx("input",{readOnly:!0,value:G,onFocus:se=>se.target.select()}),n.jsx("span",{className:"muted-inline",children:E?"若未自动打开,复制到浏览器手动访问,用要添加的 Google 账号登录授权。":"若未自动打开,复制到浏览器手动访问,用要添加的 ChatGPT 账号登录授权。"})]})]}),n.jsxs("li",{children:[n.jsx("label",{children:"② 粘贴授权后浏览器跳转的完整回调地址"}),n.jsx("input",{value:q,placeholder:E?"http://localhost:8788/oauth2callback?code=...&state=...":"https://platform.openai.com/auth/callback?code=...&state=...",onChange:se=>D(se.target.value),disabled:!G||V})]})]}),he&&n.jsx("p",{className:"form-error",children:he}),n.jsxs("div",{className:"modal-actions",children:[n.jsx("button",{type:"button",className:"secondary-button",onClick:C,disabled:V,children:"取消"}),n.jsx("button",{type:"button",className:"primary-button",onClick:ie,disabled:!G||V,children:V?"处理中":"完成授权"})]})]})})}function wp({limits:u}){const f=ye(u).filter(h=>h.label||h.name).slice(0,3);return f.length?n.jsx("div",{className:"quota-bars",children:f.map(h=>{const r=Hp(h);return n.jsxs("div",{className:"quota-bar",children:[n.jsxs("div",{className:"quota-bar-label",children:[n.jsx("strong",{children:h.label||h.name}),n.jsx("span",{children:Bp(h,r)})]}),n.jsx("div",{className:"quota-bar-track",children:n.jsx("span",{style:{width:`${r}%`}})})]},`${h.label||h.name}-${h.resetAt||""}`)})}):null}function Hp(u){return typeof u.percentRemaining=="number"&&Number.isFinite(u.percentRemaining)?Math.max(0,Math.min(100,u.percentRemaining)):typeof u.remaining=="number"&&typeof u.limit=="number"&&u.limit>0?Math.max(0,Math.min(100,u.remaining/u.limit*100)):typeof u.used=="number"&&typeof u.limit=="number"&&u.limit>0?Math.max(0,Math.min(100,(u.limit-u.used)/u.limit*100)):0}function Bp(u,f){const h=u.resetAt?` · ${sl(u.resetAt)}`:"";return typeof u.remaining=="number"?`${Math.round(f)}% 剩余${h}`:`${Math.round(f)}%${h}`}function qp(u){const f=ye(u.models).join(" ").toLowerCase(),h=["对话"];u.streamMode!=="disabled"&&h.push("流式"),/(image|dall-e|gpt-image)/.test(f)&&h.push("图片"),(u.openaiAccountCount??u.openaiAccounts?.length??0)>0&&h.push("账号池");const r=u.upstreamKeyCount??0;return r>1&&h.push(`${r} Key 轮询`),h}function Gp(u){if(u.status==="invalid"||u.credentialMode==="browser-session")return!0;if(!u.expiresAt)return!1;const f=new Date(u.expiresAt).getTime();return Number.isFinite(f)&&f-Date.now()<=1440*60*1e3}function kp({channels:u,groups:f,onUpdate:h,onCreate:r,onImport:C,onDelete:E,onSyncModels:G,onCheck:F}){const N=za("openai"),[y,q]=b.useState(!1),[D,V]=b.useState(N.provider),[ee,he]=b.useState(N.name),[te,pe]=b.useState(N.baseUrl),[ie,se]=b.useState(N.models.join(", ")),[k,xe]=b.useState(""),[le,je]=b.useState(""),[O,ne]=b.useState(!1),[ge,Te]=b.useState(!1);function ze(X){const fe=za(X);V(fe.provider),he(fe.name),pe(fe.baseUrl),se(fe.models.join(", ")),je("")}async function ue(X){X.preventDefault(),ne(!0),je("");try{await r({name:ee.trim()||za(D).name,provider:D,baseUrl:te.trim(),...Nm(k),models:Pc(ie),streamMode:"auto"}),q(!1),xe(""),ze(D)}catch(fe){je(fe instanceof Error?fe.message:"渠道创建失败")}finally{ne(!1)}}return n.jsxs(tt,{title:"渠道",children:[n.jsxs("div",{className:"channel-page-intro",children:[n.jsxs("div",{children:[n.jsx("strong",{children:"管理上游服务"}),n.jsx("span",{children:"每个渠道对应一个 API 上游。先添加渠道,再检测连通性并同步可用模型。"})]}),n.jsxs("div",{className:"channel-page-summary",children:[n.jsxs("span",{children:[n.jsx("b",{children:u.length})," 个渠道"]}),n.jsxs("span",{children:[n.jsx("b",{children:u.filter(X=>X.status!=="disabled").length})," 个已启用"]})]})]}),n.jsxs("div",{className:"panel-toolbar channel-toolbar",children:[n.jsx("span",{className:"muted-inline",children:"列表显示当前状态;点击任一渠道可修改连接、模型和计费设置。"}),n.jsx("button",{className:"primary-button",onClick:()=>q(X=>!X),children:y?"取消新增":"+ 新增渠道"})]}),y&&n.jsxs("form",{className:"channel-card channel-create-form",onSubmit:ue,children:[n.jsxs("label",{className:"channel-select-field",children:[n.jsx("span",{children:"选择渠道类型"}),n.jsxs("details",{className:"channel-choice-menu",children:[n.jsxs("summary",{children:[n.jsx("span",{children:za(D).label}),n.jsx("small",{children:"选择后自动填入建议配置"})]}),n.jsx("div",{role:"listbox","aria-label":"选择渠道类型",children:Fc.map(X=>n.jsxs("button",{type:"button",className:D===X.provider?"selected":"",onClick:fe=>{ze(X.provider),fe.currentTarget.closest("details")?.removeAttribute("open")},children:[n.jsx("strong",{children:X.label}),n.jsx("span",{children:"使用推荐的名称和地址"})]},X.provider))})]})]}),n.jsxs("div",{className:"channel-form-grid",children:[n.jsxs("label",{children:[n.jsx("span",{children:"渠道名称"}),n.jsx("input",{value:ee,onChange:X=>he(X.target.value),placeholder:"例如 OpenAI 主线路"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"供应商"}),n.jsx("input",{value:dl(D),readOnly:!0})]}),n.jsxs("label",{className:"channel-form-wide",children:[n.jsx("span",{children:"Base URL"}),n.jsx("input",{value:te,onChange:X=>pe(X.target.value),placeholder:"https://provider.example/v1"})]}),n.jsxs("label",{className:"channel-form-wide",children:[n.jsx("span",{children:"上游 Key"}),n.jsx("textarea",{className:"channel-key-input",value:k,onChange:X=>xe(X.target.value),placeholder:D==="codex"?"账号池导入后使用":D==="cpa"?"填写 CPA 的 API key,多个每行一个":"每行一个 Key;填多个会自动轮询分发",autoComplete:"off",rows:3})]}),n.jsxs("div",{className:"channel-form-wide channel-model-field",children:[n.jsxs("div",{className:"field-label-row",children:[n.jsx("span",{children:"模型"}),n.jsx("button",{type:"button",className:"model-pull-button",onClick:()=>{if(D!=="codex"&&!te.trim()){je("请先填写 Base URL 再获取模型");return}je(""),Te(!0)},children:"从上游获取模型"})]}),n.jsx("textarea",{value:ie,onChange:X=>se(X.target.value),placeholder:"多个模型用逗号分隔,或点上方『从上游获取模型』拉取后多选"})]})]}),n.jsxs("div",{className:"channel-card-actions",children:[n.jsx("span",{className:"model-create-message",role:"status",children:le}),n.jsx("button",{className:"secondary-button",type:"button",onClick:()=>ze(D),disabled:O,children:"填入模板"}),n.jsx("button",{className:"primary-button",type:"submit",disabled:O,children:O?"创建中":"创建渠道"})]})]}),y&&ge&&n.jsx(Am,{subtitle:`${ee.trim()||za(D).name} · 勾选需要接入的模型`,current:Pc(ie),loadModels:async()=>{const X=await me("/api/channel-model-preview",{method:"POST",body:JSON.stringify({provider:D,baseUrl:te.trim(),upstreamApiKey:k})});return ye(X.models)},onConfirm:async X=>{se(X.join(", "))},onClose:()=>Te(!1)}),n.jsxs("div",{className:"channels-stack",children:[u.map(X=>n.jsx(Yp,{channel:X,groups:f,onUpdate:h,onImport:C,onDelete:E,onSyncModels:G,onCheck:F},X.id)),u.length===0&&n.jsx(Xt,{text:"暂无渠道,先在后端添加渠道接口或导入配置"})]})]})}function Yp({channel:u,groups:f,onUpdate:h,onImport:r,onDelete:C,onSyncModels:E,onCheck:G}){const[F,N]=b.useState(u.name),[y,q]=b.useState(u.provider),[D,V]=b.useState(u.streamMode||"auto"),[ee,he]=b.useState(u.baseUrl),[te,pe]=b.useState(ye(u.allowedGroupIds)),[ie,se]=b.useState(ye(u.models).join(", ")),[k,xe]=b.useState("saved"),[le,je]=b.useState(String(u.inputPricePer1K||0)),[O,ne]=b.useState(String(u.outputPricePer1K||0)),[ge,Te]=b.useState(!!u.webEndpoint),[ze,ue]=b.useState(""),[X,fe]=b.useState(""),[W,A]=b.useState(!1),L=u.openaiAccountCount??u.openaiAccounts?.length??0,H=ye(u.models).length,Q=qp(u),ce=u.status==="disabled",m={saved:"已保存",template:"模板",manual:"手动",synced:"上游同步"}[k],v=Ci.find($=>$.value===D)||Ci[0],U={auto:"自动处理(推荐)",real:"强制流式",fake:"兼容流式",disabled:"关闭流式"};b.useEffect(()=>{N(u.name),q(u.provider),V(u.streamMode||"auto"),he(u.baseUrl),pe(ye(u.allowedGroupIds)),se(ye(u.models).join(", ")),xe("saved"),je(String(u.inputPricePer1K||0)),ne(String(u.outputPricePer1K||0)),Te(!!u.webEndpoint),ue("")},[u.id,u.name,u.provider,u.streamMode,u.baseUrl,u.models,u.inputPricePer1K,u.outputPricePer1K,u.webEndpoint]);async function Z(){const $=oe();fe("save");try{await h(u.id,$),ue("")}finally{fe("")}}function oe(){const $=zi(y),Je=!/^https?:\/\//i.test(ee.trim())&&$?$:ee,$e={name:F.trim()||u.name,provider:y,streamMode:D,baseUrl:Je,inputPricePer1K:Number(le)||0,outputPricePer1K:Number(O)||0,webEndpoint:ge,models:ie.split(",").map(We=>We.trim()).filter(Boolean),allowedGroupIds:te};return Object.assign($e,Nm(ze)),$e}async function Se(){const $=u.status==="disabled"?"healthy":"disabled";fe("status");try{await h(u.id,{...oe(),status:$}),ue("")}finally{fe("")}}async function _(){fe("sync");try{await h(u.id,oe()),ue(""),A(!0)}finally{fe("")}}function P(){const $=za(y);se($.models.join(", ")),xe("template"),$.baseUrl&&!/^https?:\/\//i.test(ee.trim())&&he($.baseUrl)}async function ae(){fe("check");try{await Z(),await G(u.id)}finally{fe("")}}async function Ze($){fe("import");try{await r(u.id,$)}finally{fe("")}}return n.jsxs(n.Fragment,{children:[n.jsxs("details",{className:"channel-card channel-card-collapsible",children:[n.jsxs("summary",{className:"channel-card-head channel-list-row",children:[n.jsxs("div",{className:"channel-identity",children:[n.jsx("strong",{children:u.name}),n.jsx("span",{children:dl(y)}),n.jsx("small",{children:u.baseUrl||"尚未配置上游地址"}),n.jsx("div",{className:"channel-capability-tags",children:Q.map($=>n.jsx("span",{children:$},$))})]}),n.jsxs("div",{className:"channel-list-meta",children:[n.jsxs("span",{children:[n.jsx("b",{children:H})," 个模型"]}),L>0&&n.jsxs("span",{children:[n.jsx("b",{children:L})," 个账号"]})]}),n.jsxs("div",{className:"channel-check-result",children:[n.jsx("span",{children:"连通性"}),n.jsx("b",{className:u.lastError?"is-error":u.lastCheckedAt?"is-ok":"",children:u.lastError?"检测失败":u.lastCheckedAt?`已检测 ${sl(u.lastCheckedAt)}`:"尚未检测"})]}),n.jsxs("div",{className:"channel-list-status",children:[n.jsx(Rt,{tone:u.status,children:ce?"已停用":Ut(u.status)}),n.jsx("span",{className:"channel-expand-hint",children:"配置"})]})]}),n.jsxs("div",{className:"channel-editor-controls",children:[n.jsxs("label",{className:"channel-select-field",children:[n.jsx("span",{children:"供应商"}),n.jsxs("details",{className:"channel-choice-menu",children:[n.jsxs("summary",{children:[n.jsx("span",{children:dl(y)}),n.jsx("small",{children:"修改上游协议类型"})]}),n.jsx("div",{role:"listbox","aria-label":"供应商",children:to.map($=>n.jsxs("button",{type:"button",className:y===$.value?"selected":"",onClick:Je=>{q($.value);const $e=zi($.value);$e&&!/^https?:\/\//i.test(ee.trim())&&he($e),Je.currentTarget.closest("details")?.removeAttribute("open")},children:[n.jsx("strong",{children:$.label}),n.jsx("span",{children:$.value==="compatible"?"适用于兼容 OpenAI 格式的服务":"选择对应的上游协议"})]},$.value))})]})]}),n.jsxs("label",{className:"channel-select-field",children:[n.jsx("span",{children:"响应方式"}),n.jsxs("details",{className:"channel-choice-menu",children:[n.jsxs("summary",{children:[n.jsx("span",{children:U[v.value]}),n.jsx("small",{children:v.description})]}),n.jsx("div",{role:"listbox","aria-label":"响应方式",children:Ci.map($=>n.jsxs("button",{type:"button",className:D===$.value?"selected":"",onClick:Je=>{V($.value),Je.currentTarget.closest("details")?.removeAttribute("open")},children:[n.jsx("strong",{children:U[$.value]}),n.jsx("span",{children:$.description})]},$.value))})]})]})]}),(y==="codex"||L>0)&&n.jsxs("div",{className:"setting",children:[n.jsxs("div",{children:[n.jsx("span",{children:"网页对话接口"}),n.jsx("small",{children:"开启后走 ChatGPT 网页对话接口,把 Plus 订阅账号包装成 API"})]}),n.jsx("div",{className:"setting-value",children:n.jsx("button",{type:"button",className:ge?"ios-switch is-on":"ios-switch","aria-label":ge?"关闭网页对话接口":"开启网页对话接口","aria-pressed":ge,onClick:()=>Te($=>!$),children:n.jsx("span",{})})})]}),n.jsxs("div",{className:"channel-form-grid",children:[n.jsxs("label",{children:[n.jsx("span",{children:"渠道名称"}),n.jsx("input",{value:F,onChange:$=>N($.target.value),placeholder:"例如 Gemini 主线路"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"供应商"}),n.jsx("input",{value:dl(y),readOnly:!0})]}),n.jsxs("label",{className:"channel-form-wide",children:[n.jsx("span",{children:"Base URL"}),n.jsx("input",{value:ee,onChange:$=>he($.target.value),placeholder:"https://provider.example/v1"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"优先级"}),n.jsx("input",{value:u.priority,readOnly:!0})]}),n.jsxs("div",{className:"channel-form-wide channel-model-field",children:[n.jsxs("div",{className:"field-label-row",children:[n.jsx("span",{children:"模型"}),n.jsxs("small",{children:["来源:",m]})]}),n.jsx("textarea",{value:ie,onChange:$=>{se($.target.value),xe("manual")},placeholder:"优先拉取上游模型,也可以手动补充,多个用逗号分隔"}),n.jsxs("div",{className:"channel-model-actions",children:[n.jsx("button",{type:"button",className:"secondary-button compact-button",onClick:P,disabled:X!=="",children:"填入模板"}),n.jsx("button",{type:"button",className:"secondary-button compact-button",onClick:_,disabled:X!=="",children:X==="sync"?"拉取中":"获取上游模型"})]})]}),n.jsxs("label",{className:"channel-form-wide",children:[n.jsxs("span",{children:["上游 Key",(u.upstreamKeyCount??0)>0?` (已配置 ${u.upstreamKeyCount} 个)`:u.upstreamKeySet?" (已配置)":""]}),n.jsx("textarea",{className:"channel-key-input",value:ze,onChange:$=>ue($.target.value),placeholder:y==="codex"?"Codex 账号池不需要上游 Key":y==="cpa"?"填写 CPA 的 API key,多个每行一个":"每行一个 Key;填多个会自动轮询分发;留空不修改",autoComplete:"off",rows:3})]}),n.jsxs("label",{children:[n.jsx("span",{children:"输入单价 / 1K Token"}),n.jsx("input",{type:"number",min:"0",step:"0.0001",value:le,onChange:$=>je($.target.value)})]}),n.jsxs("label",{children:[n.jsx("span",{children:"输出单价 / 1K Token"}),n.jsx("input",{type:"number",min:"0",step:"0.0001",value:O,onChange:$=>ne($.target.value)})]}),n.jsx("span",{className:"channel-billing-note",children:"定价可先留空;接入是否可用优先看渠道检测和模型同步结果。"})]}),n.jsxs("div",{className:"channel-group-field",children:[n.jsxs("div",{className:"field-label-row",children:[n.jsx("span",{children:"可见分组"}),n.jsx("small",{children:te.length?`已选择 ${te.length} 个分组`:"全部用户可用"})]}),f.length===0?n.jsx("p",{className:"channel-group-empty",children:"还没有用户分组。去「分组」页创建后,可在这里把渠道限制为只对特定分组开放。"}):n.jsx("div",{className:"channel-group-options",children:f.map($=>{const Je=te.includes($.id);return n.jsxs("button",{type:"button",className:Je?"selected":"","aria-pressed":Je,onClick:()=>pe($e=>Je?$e.filter(We=>We!==$.id):[...$e,$.id]),children:[n.jsx("span",{children:$.name}),$.description&&n.jsx("small",{children:$.description})]},$.id)})})]}),n.jsxs("div",{className:"channel-card-actions",children:[n.jsx("button",{className:"secondary-button",onClick:ae,disabled:X!=="",children:X==="check"?"检测中":"检测渠道"}),n.jsx("button",{className:"primary-button",onClick:Z,disabled:X!=="",children:X==="save"?"保存中":"保存"}),n.jsxs("details",{className:"channel-more-actions",children:[n.jsx("summary",{children:"更多操作"}),n.jsxs("div",{children:[n.jsxs("label",{className:"secondary-button",children:[X==="import"?"导入中":"导入账号 JSON",n.jsx("input",{type:"file",accept:"application/json,application/zip,text/plain,.json,.zip,.txt",disabled:X!=="",onChange:$=>{const Je=$.target.files?.[0];Je&&Ze(Je),$.target.value=""}})]}),n.jsx("button",{className:"secondary-button",onClick:Se,disabled:X!=="",children:u.status==="disabled"?"启用渠道":"停用渠道"}),n.jsx("button",{className:"danger-button",onClick:()=>C(u.id),disabled:X!=="",children:"删除渠道"})]})]})]})]}),W&&n.jsx(Am,{subtitle:`${u.name} · 勾选需要接入的模型`,current:ie.split(",").map($=>$.trim()).filter(Boolean),loadModels:async()=>{const $=await me(`/api/channels/${u.id}/upstream-models`,{method:"POST",body:JSON.stringify({})});return ye($.models)},onConfirm:async $=>{await E(u.id,$)},onClose:()=>A(!1)})]})}function Am({subtitle:u,current:f,loadModels:h,onConfirm:r,onClose:C}){const[E,G]=b.useState(!0),[F,N]=b.useState(""),[y,q]=b.useState([]),[D,V]=b.useState(new Set),[ee,he]=b.useState(""),[te,pe]=b.useState(!1);b.useEffect(()=>{let O=!1;return(async()=>{G(!0),N("");try{const ne=await h();if(O)return;const ge=new Set,Te=ye(ne).map(ue=>ue.trim()).filter(ue=>{if(!ue)return!1;const X=ue.toLowerCase();return ge.has(X)?!1:(ge.add(X),!0)}),ze=new Set(f.map(ue=>ue.toLowerCase()));q(Te),V(new Set(Te.filter(ue=>ze.has(ue.toLowerCase()))))}catch(ne){O||N(ne instanceof Error?ne.message:"获取上游模型失败")}finally{O||G(!1)}})(),()=>{O=!0}},[]);const ie=ee.trim().toLowerCase(),se=ie?y.filter(O=>O.toLowerCase().includes(ie)):y,k=se.length>0&&se.every(O=>D.has(O));function xe(O){V(ne=>{const ge=new Set(ne);return ge.has(O)?ge.delete(O):ge.add(O),ge})}function le(){V(O=>{const ne=new Set(O);return k?se.forEach(ge=>ne.delete(ge)):se.forEach(ge=>ne.add(ge)),ne})}async function je(){const O=new Set(y.map(ue=>ue.toLowerCase())),ne=f.filter(ue=>!O.has(ue.toLowerCase())),ge=y.filter(ue=>D.has(ue)),Te=new Set,ze=[...ne,...ge].filter(ue=>{const X=ue.toLowerCase();return Te.has(X)?!1:(Te.add(X),!0)});pe(!0);try{await r(ze),C()}catch{pe(!1)}}return n.jsx("div",{className:"modal-backdrop",onClick:C,children:n.jsxs("div",{className:"modal-card model-picker-modal",onClick:O=>O.stopPropagation(),children:[n.jsxs("div",{className:"modal-head",children:[n.jsxs("div",{children:[n.jsx("strong",{children:"选择上游模型"}),n.jsx("span",{children:u})]}),n.jsx("button",{type:"button",className:"icon-button",onClick:C,children:"×"})]}),E?n.jsx("div",{className:"model-picker-status",children:"正在获取上游模型…"}):F?n.jsx("div",{className:"model-picker-status model-picker-error",children:F}):n.jsxs(n.Fragment,{children:[n.jsxs("div",{className:"model-picker-toolbar",children:[n.jsx("input",{className:"model-picker-search",value:ee,onChange:O=>he(O.target.value),placeholder:"搜索模型名称",autoFocus:!0}),n.jsx("button",{type:"button",className:"secondary-button compact-button",onClick:le,disabled:se.length===0,children:k?"取消全选":"全选"})]}),n.jsxs("div",{className:"model-picker-count",children:["共 ",y.length," 个 · 已选 ",D.size," 个",ie?` · 匹配 ${se.length} 个`:""]}),n.jsxs("div",{className:"model-picker-list",children:[se.map(O=>{const ne=D.has(O),ge=f.some(Te=>Te.toLowerCase()===O.toLowerCase());return n.jsxs("label",{className:`model-picker-row${ne?" checked":""}`,children:[n.jsx("input",{type:"checkbox",checked:ne,onChange:()=>xe(O)}),n.jsx("span",{className:"model-picker-name",children:O}),ge&&n.jsx("span",{className:"model-picker-tag",children:"已接入"})]},O)}),se.length===0&&n.jsx("div",{className:"model-picker-status",children:"没有匹配的模型"})]})]}),n.jsxs("div",{className:"modal-actions",children:[n.jsx("button",{type:"button",className:"secondary-button",onClick:C,children:"取消"}),n.jsx("button",{type:"button",className:"primary-button",disabled:E||!!F||te,onClick:je,children:te?"保存中":`导入所选 (${D.size})`})]})]})})}function Lp({logs:u,onCopy:f}){const[h,r]=b.useState(u),[C,E]=b.useState(u.length),[G,F]=b.useState(1),[N,y]=b.useState(""),[q,D]=b.useState("all"),[V,ee]=b.useState(null),[he,te]=b.useState(!1),[pe,ie]=b.useState(!1),se=25,k=Math.max(1,Math.ceil(C/se));b.useEffect(()=>{F(1)},[N,q]),b.useEffect(()=>{G>k&&F(k)},[G,k]),b.useEffect(()=>{const le=new AbortController,je=window.setTimeout(async()=>{te(!0);try{const O=new URLSearchParams({page:String(G),pageSize:String(se),status:q,q:N.trim()}),ne=await me(`/api/logs?${O}`,{signal:le.signal}),ge=ye(ne.logs);r(ge),E(ne.total||0),ee(Te=>Te&&ge.some(ze=>ze.id===Te.id)?Te:null)}catch(O){O instanceof DOMException&&O.name==="AbortError"||(r([]),E(0))}finally{le.signal.aborted||te(!1)}},N?250:0);return()=>{window.clearTimeout(je),le.abort()}},[G,N,q]);async function xe(le){if(V?.id===le.id){ee(null);return}ee(le),ie(!0);try{const je=await me(`/api/logs/${encodeURIComponent(le.id)}`);ee(je.log)}catch{ee(le)}finally{ie(!1)}}return n.jsxs(tt,{title:"调用日志",children:[n.jsxs("div",{className:"logs-toolbar",children:[n.jsxs("div",{className:"search-box",children:[n.jsx(Dt,{name:"search"}),n.jsx("input",{value:N,onChange:le=>y(le.target.value),placeholder:"搜索请求 ID、用户、Key、模型、渠道或错误码"})]}),n.jsx("div",{className:"log-status-filter",role:"group","aria-label":"日志状态筛选",children:[{value:"all",label:"全部"},{value:"success",label:"成功"},{value:"failed",label:"失败"}].map(le=>n.jsx("button",{type:"button",className:q===le.value?"selected":"",onClick:()=>D(le.value),children:le.label},le.value))}),n.jsx("span",{className:"muted-inline",children:he?"加载中":`共 ${C} 条`})]}),n.jsxs("div",{className:V?"logs-layout has-detail":"logs-layout",children:[n.jsxs("div",{className:"table",children:[n.jsxs("div",{className:"table-head logs-table",children:[n.jsx("span",{children:"请求"}),n.jsx("span",{children:"模型"}),n.jsx("span",{children:"渠道"}),n.jsx("span",{children:"状态"})]}),h.map(le=>n.jsx("div",{className:"log-entry",children:n.jsxs("div",{className:V?.id===le.id?"table-row logs-table selected":"table-row logs-table",role:"button",tabIndex:0,onClick:()=>xe(le),onKeyDown:je=>{(je.key==="Enter"||je.key===" ")&&xe(le)},children:[n.jsxs("span",{children:[n.jsx("strong",{children:lo(le)}),n.jsxs("small",{children:[le.id," · ",sl(le.createdAt)," · ",le.latencyMs,"ms"]})]}),n.jsx("span",{children:up(le)}),n.jsx("span",{children:cp(le)}),n.jsx(Rt,{tone:le.status,children:Ut(le.status)})]})},le.id)),!he&&h.length===0&&n.jsx(Xt,{text:N||q!=="all"?"没有匹配的日志":"暂无调用日志"})]}),V&&n.jsx(Qp,{log:V,loading:pe,onCopy:f})]}),k>1&&n.jsxs("div",{className:"pagination-bar",children:[n.jsx("button",{className:"secondary-button",disabled:G<=1||he,onClick:()=>F(le=>Math.max(1,le-1)),children:"上一页"}),n.jsxs("span",{children:[G," / ",k]}),n.jsx("button",{className:"secondary-button",disabled:G>=k||he,onClick:()=>F(le=>Math.min(k,le+1)),children:"下一页"})]})]})}function Qp({log:u,loading:f,onCopy:h}){if(!u)return n.jsx("aside",{className:"log-inspector empty-inspector",children:n.jsx("span",{children:"选择一条日志查看详情"})});const r=Number(u.inputTokens||0),C=Number(u.outputTokens||0),E=typeof u.attempts=="number"?Math.max(0,u.attempts):null,G=[["请求 ID",u.id],["状态",Ut(u.status)],["时间",ip(u.createdAt)],["用户 ID",u.userId||"未识别"],["API Key",u.apiKeyPrefix?`${u.apiKeyPrefix}***`:"未识别"],["模型",u.model||"未提供"],["渠道",u.channel||"未选择"],["实际账号",u.account||"未记录"],["响应耗时",`${u.latencyMs} ms`],["尝试次数",E===null?"未记录":String(E)],["是否重试",E===null?"未记录":E>1?"是":"否"],["输入 Tokens",gl(r)],["输出 Tokens",gl(C)],["总 Tokens",gl(r+C)],["扣费",u.cost.toFixed(4)],["错误码",u.errorCode||"无"]];return n.jsxs("aside",{className:"log-inspector",children:[n.jsxs("header",{children:[n.jsxs("div",{children:[n.jsx("span",{children:f?"加载中":"日志详情"}),n.jsx("strong",{children:lo(u)})]}),n.jsx(Rt,{tone:u.status,children:Ut(u.status)})]}),n.jsx("div",{className:"log-detail",children:G.map(([F,N])=>n.jsxs("div",{children:[n.jsx("span",{children:F}),n.jsx("strong",{title:N,children:N})]},F))}),n.jsxs("div",{className:"log-actions",children:[n.jsx("button",{type:"button",className:"secondary-button compact-button",onClick:()=>h(u.id,"请求 ID 已复制"),children:"复制请求 ID"}),u.errorCode&&n.jsx("button",{type:"button",className:"secondary-button compact-button",onClick:()=>h(u.errorCode||"","错误码已复制"),children:"复制错误码"})]})]})}function Xp({models:u,channels:f,groups:h}){const[r,C]=b.useState(null),[E,G]=b.useState(null),[F,N]=b.useState("username"),[y,q]=b.useState("0"),[D,V]=b.useState(""),[ee,he]=b.useState({enabled:!0,minReward:.1,maxReward:1}),[te,pe]=b.useState({logRetentionDays:30,maxLogs:1e4,maxQuotaEntries:2e4}),[ie,se]=b.useState(""),[k,xe]=b.useState(""),[le,je]=b.useState(!1),[O,ne]=b.useState(null),[ge,Te]=b.useState(null),[ze,ue]=b.useState(""),[X,fe]=b.useState(""),[W,A]=b.useState(""),[L,H]=b.useState(""),[Q,ce]=b.useState(""),[m,v]=b.useState(""),[U,Z]=b.useState(""),[oe,Se]=b.useState(""),[_,P]=b.useState(""),[ae,Ze]=b.useState(!1),[$,Je]=b.useState("system"),$e=u.find(z=>z.recommended&&z.status==="available")?.id||u.find(z=>z.status==="available")?.id||"未配置",We=f.filter(z=>z.status!=="disabled").length,ft=[{value:"system",label:"系统",description:"运行概况"},{value:"cli",label:"CLI 接入",description:"一键配置"},{value:"auth",label:"注册",description:"开放方式"},{value:"check-in",label:"签到",description:"奖励范围"},{value:"admin",label:"管理员",description:"账号绑定"},{value:"discord",label:"Discord",description:"登录限制"},{value:"maintenance",label:"维护",description:"日志保留"},{value:"antigravity",label:"Antigravity",description:"版本与模型"},{value:"backup",label:"备份",description:"导出恢复"}],Nt=ft.find(z=>z.value===$)||ft[0];b.useEffect(()=>{Promise.all([me("/api/settings/discord"),me("/api/settings/auth"),me("/api/settings/check-in"),me("/api/account/me"),me("/api/settings/maintenance"),me("/api/settings/antigravity"),me("/api/health")]).then(([z,Ae,Fe,ht,T,Y,K])=>{C(Wc(z.discord)),Se(ye(z.discord.blockedGuildIds).join(` +`)),G(Ae.auth.registrationEnabled),N(Oi(Ae.auth.registrationMode)),q(String(Ae.auth.defaultBalance||0)),V(Ae.auth.defaultGroupId||""),he(Fe.checkIn),Te(ht.account),ue(ht.account?.username||""),fe(ht.user.name||""),A(ht.account?.email||""),H(ht.account?.discordUserId||""),pe(T.maintenance),se(Y.antigravity.clientVersion||""),xe(ye(Y.antigravity.models).join(` +`)),ne(K)}).catch(()=>P("设置加载失败"))},[]);async function At(z=E,Ae=F,Fe=Number(y),ht=D){if(z!==null)try{const T=await me("/api/settings/auth",{method:"PATCH",body:JSON.stringify({registrationEnabled:z,registrationMode:Ae,defaultBalance:Fe,defaultGroupId:ht})});G(T.auth.registrationEnabled),N(Oi(T.auth.registrationMode)),q(String(T.auth.defaultBalance||0)),V(T.auth.defaultGroupId||""),P(T.auth.registrationEnabled?"注册设置已保存":"已关闭用户注册")}catch(T){P(T instanceof Error?T.message:"注册设置保存失败")}}async function il(){E!==null&&At(!E,F)}async function Kt(){Ze(!0),P("");try{const z=await me("/api/settings/check-in",{method:"PATCH",body:JSON.stringify(ee)});he(z.checkIn),P(z.checkIn.enabled?"签到奖励设置已保存":"已关闭每日签到")}catch(z){P(z instanceof Error?z.message:"签到设置保存失败")}finally{Ze(!1)}}async function J(){if(r){Ze(!0),P("");try{const z=Wc(r),Ae=oe.split(/[\s,]+/).map(ht=>ht.trim()).filter(Boolean),Fe=await me("/api/settings/discord",{method:"PATCH",body:JSON.stringify({...z,blockedGuildIds:Ae,clientSecret:U})});C(Wc(Fe.discord)),Se(ye(Fe.discord.blockedGuildIds).join(` +`)),Z(""),P("Discord 配置已保存")}catch(z){P(z instanceof Error?z.message:"保存失败,请检查填写内容")}finally{Ze(!1)}}}async function ot(){try{const z=await me("/api/account/profile",{method:"PATCH",body:JSON.stringify({username:ze,displayName:X,email:W,discordUserId:L,currentPassword:Q,newPassword:m})});Te(z.account),ue(z.account.username),A(z.account.email||""),H(z.account.discordUserId||""),ce(""),v(""),P("管理员账号已保存")}catch(z){P(z instanceof Error?z.message:"账号设置保存失败")}}async function Zt(){Ze(!0),P("");try{const z=await me("/api/settings/maintenance",{method:"PATCH",body:JSON.stringify(te)});pe(z.maintenance),P("维护设置已保存,历史数据已按新规则清理")}catch(z){P(z instanceof Error?z.message:"维护设置保存失败")}finally{Ze(!1)}}function bl(z){const Ae=new Set,Fe=[];for(const ht of z.split(/[\s,]+/)){const T=ht.trim();T&&!Ae.has(T)&&(Ae.add(T),Fe.push(T))}return Fe}async function mt(){Ze(!0),P("");try{const z=await me("/api/settings/antigravity",{method:"PATCH",body:JSON.stringify({clientVersion:ie.trim(),models:bl(k)})});se(z.antigravity.clientVersion||""),xe(ye(z.antigravity.models).join(` +`)),P("Antigravity 设置已保存")}catch(z){P(z instanceof Error?z.message:"Antigravity 设置保存失败")}finally{Ze(!1)}}async function gn(){const z=f.find(Ae=>Ae.provider==="antigravity"&&ye(Ae.openaiAccounts).length>0)||f.find(Ae=>Ae.provider==="antigravity");if(!z){P("没有可用的 Antigravity 账号池,请先在 Antigravity 页新建并导入/授权账号");return}je(!0),P("");try{const Ae=await me(`/api/channels/${encodeURIComponent(z.id)}/antigravity/available-models`,{method:"POST",body:JSON.stringify({})}),Fe=bl([k,...ye(Ae.models)].join(` +`));xe(Fe.join(` +`)),P(`已从上游拉取 ${ye(Ae.models).length} 个模型,确认后点击保存生效`)}catch(Ae){P(Ae instanceof Error?Ae.message:"拉取上游模型失败")}finally{je(!1)}}async function Oa(){Ze(!0),P("");try{const z=await fetch("/api/backup",{credentials:"include"});if(!z.ok)throw new Error("备份导出失败");const Ae=await z.blob(),ht=(z.headers.get("Content-Disposition")||"").match(/filename="([^"]+)"/)?.[1]||"capi-backup.json",T=URL.createObjectURL(Ae),Y=document.createElement("a");Y.href=T,Y.download=ht,Y.click(),URL.revokeObjectURL(T),P("备份已导出,请妥善保管")}catch(z){P(z instanceof Error?z.message:"备份导出失败")}finally{Ze(!1)}}async function _a(z){if(window.confirm("恢复会覆盖当前全部数据,并退出现有登录会话。确定继续?")){Ze(!0),P("");try{const Ae=await fetch("/api/restore",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:z}),Fe=await Ae.json().catch(()=>null);if(!Ae.ok)throw new Error(Fe?.error?.message||"备份恢复失败");P(`已恢复 ${Fe.users} 个用户、${Fe.channels} 条渠道和 ${Fe.models} 个模型,请重新登录`)}catch(Ae){P(Ae instanceof Error?Ae.message:"备份恢复失败")}finally{Ze(!1)}}}return n.jsxs("div",{className:"settings-layout",children:[n.jsx("div",{className:"settings-tabs",children:ft.map(z=>n.jsxs("button",{type:"button",className:$===z.value?"selected":"",onClick:()=>Je(z.value),children:[n.jsx("strong",{children:z.label}),n.jsx("small",{children:z.description})]},z.value))}),n.jsxs("div",{className:"settings-tab-note",children:[n.jsx("strong",{children:Nt.label}),n.jsx("span",{children:Nt.description})]}),$==="system"&&n.jsx(tt,{title:"系统设置",children:n.jsxs("div",{className:"settings-group",children:[n.jsx(nl,{label:"接口兼容",value:"OpenAI API"}),n.jsx(nl,{label:"当前默认模型",value:$e}),n.jsx(nl,{label:"已配置渠道",value:`${f.length} 个,${We} 个启用`}),n.jsx(nl,{label:"可选供应商",value:`${to.length} 种`}),n.jsx(nl,{label:"运行版本",value:`${O?.version||"未知"} · ${O?.commit||"未知"}`}),n.jsx(nl,{label:"构建时间",value:O?.buildTime?sl(O.buildTime):"本地构建"}),n.jsx(nl,{label:"账号自动检测",value:"每 15 分钟自动检测一次"})]})}),$==="cli"&&n.jsxs(tt,{title:"CLI 工具接入",children:[n.jsx("p",{className:"cli-intro",children:"CAPI 兼容 OpenAI 和 Anthropic 协议,常见 AI 命令行工具可直接接入。"}),n.jsxs("div",{className:"cli-credentials",children:[n.jsxs("div",{className:"cli-credential",children:[n.jsx("span",{children:"Base URL"}),n.jsx("code",{children:Gl()}),n.jsx("button",{type:"button",className:"copy-button","aria-label":"复制",onClick:()=>{Ti(Gl()),P("已复制 Base URL")},children:n.jsx(Dt,{name:"copy"})})]}),n.jsxs("div",{className:"cli-credential",children:[n.jsx("span",{children:"API Key"}),n.jsx("code",{children:"cat_你的_api_key"})]})]}),n.jsxs("div",{className:"cli-tools",children:[n.jsxs("details",{className:"cli-tool",open:!0,children:[n.jsxs("summary",{children:[n.jsx("strong",{children:"Claude Code"}),n.jsx("span",{children:"Anthropic Messages 协议"})]}),n.jsx("pre",{children:`export ANTHROPIC_BASE_URL="${Gl()}" +export ANTHROPIC_AUTH_TOKEN="cat_你的_api_key" +export ANTHROPIC_MODEL="${$e}" +claude`})]}),n.jsxs("details",{className:"cli-tool",children:[n.jsxs("summary",{children:[n.jsx("strong",{children:"Codex CLI"}),n.jsx("span",{children:"OpenAI Chat 协议"})]}),n.jsxs("p",{children:["编辑 ",n.jsx("code",{children:"~/.codex/config.toml"}),":"]}),n.jsx("pre",{children:`model = "${$e}" +model_provider = "capi" + +[model_providers.capi] +name = "CAPI" +base_url = "${Gl()}/v1" +env_key = "CAPI_KEY" +wire_api = "chat"`}),n.jsx("p",{children:"然后设置环境变量并运行:"}),n.jsx("pre",{children:`export CAPI_KEY="cat_你的_api_key" +codex`})]}),n.jsxs("details",{className:"cli-tool",children:[n.jsxs("summary",{children:[n.jsx("strong",{children:"Aider"}),n.jsx("span",{children:"OpenAI 兼容"})]}),n.jsx("pre",{children:`export OPENAI_API_BASE="${Gl()}" +export OPENAI_API_KEY="cat_你的_api_key" +aider --model openai/${$e}`})]}),n.jsxs("details",{className:"cli-tool",children:[n.jsxs("summary",{children:[n.jsx("strong",{children:"Cline / Roo Code / Kilo Code"}),n.jsx("span",{children:"VS Code 插件"})]}),n.jsxs("p",{children:["在插件设置中选择 ",n.jsx("strong",{children:"OpenAI Compatible"}),":"]}),n.jsx("pre",{children:`Base URL: ${Gl()}/v1 +API Key: cat_你的_api_key +Model ID: ${$e}`})]}),n.jsxs("details",{className:"cli-tool",children:[n.jsxs("summary",{children:[n.jsx("strong",{children:"通用 OpenAI SDK"}),n.jsx("span",{children:"Python / Node.js"})]}),n.jsx("pre",{children:`export OPENAI_BASE_URL="${Gl()}" +export OPENAI_API_KEY="cat_你的_api_key"`}),n.jsx("pre",{children:`from openai import OpenAI +client = OpenAI() +response = client.chat.completions.create( + model="${$e}", + messages=[{"role": "user", "content": "hello"}] +)`})]})]}),_&&n.jsx("p",{className:"cli-message",role:"status",children:_})]}),$==="maintenance"&&n.jsx(tt,{title:"维护设置",children:n.jsxs("div",{className:"settings-group",children:[n.jsxs("div",{className:"setting",children:[n.jsx("span",{children:"日志保留天数"}),n.jsx("div",{className:"setting-value maintenance-control",children:n.jsx("input",{type:"number",min:"1",max:"3650",value:te.logRetentionDays,onChange:z=>pe(Ae=>({...Ae,logRetentionDays:Number(z.target.value)}))})})]}),n.jsxs("div",{className:"setting",children:[n.jsx("span",{children:"日志最大条数"}),n.jsx("div",{className:"setting-value maintenance-control",children:n.jsx("input",{type:"number",min:"100",max:"1000000",step:"100",value:te.maxLogs,onChange:z=>pe(Ae=>({...Ae,maxLogs:Number(z.target.value)}))})})]}),n.jsxs("div",{className:"setting",children:[n.jsx("span",{children:"额度流水最大条数"}),n.jsx("div",{className:"setting-value maintenance-control",children:n.jsx("input",{type:"number",min:"100",max:"2000000",step:"100",value:te.maxQuotaEntries,onChange:z=>pe(Ae=>({...Ae,maxQuotaEntries:Number(z.target.value)}))})})]}),n.jsxs("div",{className:"settings-save-row",children:[n.jsx("span",{role:"status",children:_}),n.jsx("button",{type:"button",className:"primary-button",disabled:ae,onClick:Zt,children:ae?"保存中":"保存维护设置"})]})]})}),$==="antigravity"&&n.jsx(tt,{title:"Antigravity 设置",children:n.jsxs("div",{className:"settings-form-grid",children:[n.jsxs("label",{className:"settings-form-wide",children:[n.jsx("span",{children:"客户端版本"}),n.jsx("input",{type:"text",value:ie,onChange:z=>se(z.target.value),placeholder:"2.9.1(留空用内置默认)"}),n.jsx("small",{children:"用于伪装 User-Agent:antigravity/hub/<版本> <平台>。改完保存即时生效,无需重新发版。"})]}),n.jsxs("label",{className:"settings-form-wide",children:[n.jsx("span",{children:"可选模型"}),n.jsx("textarea",{value:k,onChange:z=>xe(z.target.value),placeholder:"每行一个模型 ID,例如 claude-sonnet-4-6",rows:10}),n.jsx("small",{children:"路由可用的 Antigravity 模型清单。「从上游拉取」会用账号池里一个账号调 fetchAvailableModels 拉取真实可用模型并合并进来,确认后点保存生效。留空则回退到内置默认清单。"})]}),n.jsxs("div",{className:"settings-save-row",children:[n.jsx("span",{role:"status",children:_}),n.jsx("button",{type:"button",className:"secondary-button",disabled:le||ae,onClick:gn,children:le?"拉取中":"从上游拉取模型"}),n.jsx("button",{type:"button",className:"primary-button",disabled:ae||le,onClick:mt,children:ae?"保存中":"保存设置"})]})]})}),$==="backup"&&n.jsx(tt,{title:"备份与恢复",children:n.jsx("div",{className:"settings-group",children:n.jsxs("div",{className:"setting",children:[n.jsxs("span",{children:["备份与恢复",n.jsx("small",{children:"包含账号哈希和加密后的上游密钥,恢复时需要相同的 SECRET_KEY"})]}),n.jsxs("div",{className:"setting-value backup-actions",children:[n.jsx("button",{type:"button",className:"secondary-button",disabled:ae,onClick:Oa,children:"导出备份"}),n.jsxs("label",{className:"secondary-button",children:["恢复备份",n.jsx("input",{type:"file",accept:"application/json,.json",disabled:ae,onChange:z=>{const Ae=z.target.files?.[0];Ae&&_a(Ae),z.target.value=""}})]})]})]})})}),$==="auth"&&n.jsx(tt,{title:"账号与注册",children:n.jsxs("div",{className:"settings-group",children:[n.jsxs("div",{className:"setting",children:[n.jsx("span",{children:"开放用户注册"}),n.jsxs("div",{className:"setting-value",children:[n.jsx("strong",{children:E?"启用":"关闭"}),n.jsx("button",{type:"button",className:E?"ios-switch is-on":"ios-switch","aria-label":E?"关闭用户注册":"开放用户注册","aria-pressed":!!E,onClick:il,children:n.jsx("span",{})})]})]}),n.jsxs("div",{className:"setting",children:[n.jsx("span",{children:"注册方式"}),n.jsx("div",{className:"registration-mode-control",role:"group","aria-label":"注册方式",children:[{value:"username",label:"账号密码"},{value:"email",label:"邮箱"},{value:"discord",label:"Discord"}].map(z=>n.jsx("button",{type:"button",className:F===z.value?"selected":"",onClick:()=>At(E??!0,z.value),children:z.label},z.value))})]}),n.jsxs("div",{className:"setting",children:[n.jsxs("span",{children:["新用户初始额度",n.jsx("small",{children:"注册完成后自动发放,仅影响之后的新用户"})]}),n.jsxs("div",{className:"setting-value auth-default-balance",children:[n.jsx("input",{type:"number",min:"0",step:"0.01",value:y,onChange:z=>q(z.target.value),"aria-label":"新用户初始额度"}),n.jsx("button",{type:"button",className:"secondary-button",onClick:()=>At(E,F,Number(y)),children:"保存"})]})]}),n.jsxs("div",{className:"setting",children:[n.jsxs("span",{children:["默认注册分组",n.jsx("small",{children:"新注册用户自动归入该分组,决定他们能用哪些渠道"})]}),n.jsxs("div",{className:"setting-value auth-default-balance",children:[n.jsxs("select",{value:D,onChange:z=>V(z.target.value),"aria-label":"默认注册分组",children:[h.map(z=>n.jsx("option",{value:z.id,children:z.name},z.id)),h.length===0&&n.jsx("option",{value:"",children:"暂无分组"})]}),n.jsx("button",{type:"button",className:"secondary-button",disabled:h.length===0,onClick:()=>At(E,F,Number(y),D),children:"保存"})]})]})]})}),$==="check-in"&&n.jsx(tt,{title:"每日签到奖励",children:n.jsxs("div",{className:"settings-group",children:[n.jsxs("div",{className:"setting",children:[n.jsxs("span",{children:["开放每日签到",n.jsx("small",{children:"用户每天可领取一次随机额度,按北京时间刷新"})]}),n.jsxs("div",{className:"setting-value",children:[n.jsx("strong",{children:ee.enabled?"启用":"关闭"}),n.jsx("button",{type:"button",className:ee.enabled?"ios-switch is-on":"ios-switch","aria-label":ee.enabled?"关闭每日签到":"开放每日签到","aria-pressed":ee.enabled,onClick:()=>he(z=>({...z,enabled:!z.enabled})),children:n.jsx("span",{})})]})]}),n.jsxs("div",{className:"setting check-in-settings-row",children:[n.jsxs("span",{children:["随机奖励范围",n.jsx("small",{children:"领取金额精确到 0.01,直接计入用户余额和额度流水"})]}),n.jsxs("div",{className:"check-in-reward-inputs",children:[n.jsxs("label",{children:[n.jsx("span",{children:"最低"}),n.jsx("input",{type:"number",min:"0.01",max:"1000000",step:"0.01",value:ee.minReward,onChange:z=>he(Ae=>({...Ae,minReward:Number(z.target.value)}))})]}),n.jsxs("label",{children:[n.jsx("span",{children:"最高"}),n.jsx("input",{type:"number",min:"0.01",max:"1000000",step:"0.01",value:ee.maxReward,onChange:z=>he(Ae=>({...Ae,maxReward:Number(z.target.value)}))})]})]})]}),n.jsxs("div",{className:"settings-save-row",children:[n.jsx("span",{role:"status",children:_}),n.jsx("button",{type:"button",className:"primary-button",disabled:ae,onClick:Kt,children:ae?"保存中":"保存签到设置"})]})]})}),$==="admin"&&n.jsx(tt,{title:"管理员账号",children:n.jsxs("form",{className:"discord-settings",onSubmit:z=>{z.preventDefault(),ot()},children:[n.jsxs("div",{className:"settings-form-grid",children:[n.jsxs("label",{children:[n.jsx("span",{children:"登录账号"}),n.jsx("input",{value:ze,onChange:z=>ue(z.target.value),autoComplete:"username"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"显示名称"}),n.jsx("input",{value:X,onChange:z=>fe(z.target.value),autoComplete:"name"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"邮箱"}),n.jsx("input",{type:"email",value:W,onChange:z=>A(z.target.value),autoComplete:"email"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"Discord 用户 ID(可选)"}),n.jsx("input",{inputMode:"numeric",autoComplete:"off",value:L,onChange:z=>H(z.target.value),placeholder:ge?.discordUserId?"已绑定":"输入管理员的 Discord 用户 ID"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"当前密码"}),n.jsx("input",{type:"password",value:Q,onChange:z=>ce(z.target.value),autoComplete:"current-password",placeholder:"修改密码时填写"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"新密码"}),n.jsx("input",{type:"password",value:m,onChange:z=>v(z.target.value),autoComplete:"new-password",placeholder:"留空表示不修改"})]})]}),n.jsxs("div",{className:"settings-save-row",children:[n.jsx("span",{role:"status",children:_}),n.jsx("button",{className:"primary-button",type:"submit",children:"保存账号"})]})]})}),$==="discord"&&n.jsx(tt,{title:"Discord 登录",children:r?n.jsxs("form",{className:"discord-settings",autoComplete:"off",onSubmit:z=>{z.preventDefault(),J()},children:[n.jsxs("div",{className:"discord-toggle-row",children:[n.jsxs("div",{children:[n.jsx("strong",{children:"Discord 登录"}),n.jsx("span",{children:r.enabled?"已启用":"未启用"})]}),n.jsx("button",{type:"button",className:r.enabled?"ios-switch is-on":"ios-switch","aria-label":r.enabled?"停用 Discord 登录":"启用 Discord 登录","aria-pressed":r.enabled,onClick:()=>C({...r,enabled:!r.enabled}),children:n.jsx("span",{})})]}),n.jsxs("div",{className:"settings-form-grid",children:[n.jsxs("label",{children:[n.jsx("span",{children:"Client ID"}),n.jsx("input",{inputMode:"numeric",autoComplete:"off",value:r.clientId,onChange:z=>C({...r,clientId:z.target.value}),placeholder:"100000000000000001"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"Client Secret"}),n.jsx("input",{type:"password",autoComplete:"new-password",value:U,onChange:z=>Z(z.target.value),placeholder:r.clientSecretSet?"已设置,留空表示不修改":"粘贴 Discord Client Secret"})]}),n.jsxs("label",{className:"settings-form-wide",children:[n.jsx("span",{children:"回调地址"}),n.jsx("input",{type:"url",value:r.redirectUri,onChange:z=>C({...r,redirectUri:z.target.value}),placeholder:"https://你的域名/api/auth/discord/callback"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"服务器 ID"}),n.jsx("input",{inputMode:"numeric",value:r.allowedGuildId,onChange:z=>C({...r,allowedGuildId:z.target.value}),placeholder:"允许登录的服务器 ID"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"身份组 ID"}),n.jsx("input",{inputMode:"numeric",value:r.allowedRoleId,onChange:z=>C({...r,allowedRoleId:z.target.value}),placeholder:"允许登录的身份组 ID"})]}),n.jsxs("label",{className:"settings-form-wide",children:[n.jsx("span",{children:"拉黑服务器 ID"}),n.jsx("textarea",{value:oe,onChange:z=>Se(z.target.value),placeholder:"每行一个服务器 ID;命中的用户禁止注册 / 登录",rows:3}),n.jsx("small",{children:"用户若加入了这些 Discord 服务器中的任意一个,将无法注册或登录(优先于上面的允许规则)。"})]}),n.jsxs("label",{className:"settings-form-wide",children:[n.jsx("span",{children:"登录成功跳转地址"}),n.jsx("input",{type:"url",value:r.authSuccessUrl,onChange:z=>C({...r,authSuccessUrl:z.target.value}),placeholder:"https://你的域名/"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"登录有效期(小时)"}),n.jsx("input",{type:"number",min:"1",max:"8760",value:r.sessionTtlHours,onChange:z=>C({...r,sessionTtlHours:Number(z.target.value)})})]})]}),n.jsxs("div",{className:"settings-save-row",children:[n.jsx("span",{role:"status",children:_}),n.jsx("button",{className:"primary-button",type:"submit",disabled:ae,children:ae?"保存中":"保存配置"})]})]}):n.jsx("div",{className:"empty",children:"正在读取配置"})})]})}function fl({label:u,value:f}){return n.jsxs("div",{className:"metric",children:[n.jsx("span",{children:u}),n.jsx("strong",{children:f})]})}function Kp({groups:u,onCreate:f,onUpdate:h,onDelete:r}){const[C,E]=b.useState(""),[G,F]=b.useState(""),[N,y]=b.useState(!1);async function q(D){if(D.preventDefault(),!!C.trim()){y(!0);try{await f({name:C.trim(),description:G.trim()}),E(""),F("")}finally{y(!1)}}}return n.jsxs("section",{className:"models-page",children:[n.jsxs(tt,{title:"用户分组",children:[n.jsxs("form",{className:"channel-create-form",onSubmit:q,children:[n.jsxs("div",{className:"channel-form-grid",children:[n.jsxs("label",{children:[n.jsx("span",{children:"分组名称"}),n.jsx("input",{value:C,onChange:D=>E(D.target.value),placeholder:"例如 尊享用户 / 试用用户"})]}),n.jsxs("label",{children:[n.jsx("span",{children:"说明"}),n.jsx("input",{value:G,onChange:D=>F(D.target.value),placeholder:"可选"})]})]}),n.jsx("div",{className:"channel-card-actions",children:n.jsx("button",{className:"primary-button",type:"submit",disabled:N||!C.trim(),children:N?"创建中":"创建分组"})})]}),n.jsx("p",{className:"muted-inline",children:"分组用于控制渠道对用户的可见范围:把用户归入分组,并在渠道上勾选「可见分组」即可限制访问。未分组的用户只能使用未限制分组的渠道。"})]}),n.jsx(tt,{title:"全部分组",children:u.length===0?n.jsx(Xt,{text:"还没有分组"}):n.jsx("div",{className:"channels-stack",children:u.map(D=>n.jsx(Zp,{group:D,onUpdate:h,onDelete:r},D.id))})})]})}function Zp({group:u,onUpdate:f,onDelete:h}){const[r,C]=b.useState(!1),[E,G]=b.useState(u.name),[F,N]=b.useState(u.description),[y,q]=b.useState(!1);async function D(){q(!0);try{await f(u.id,{name:E.trim(),description:F.trim()}),C(!1)}finally{q(!1)}}return n.jsx("div",{className:"channel-card",children:n.jsxs("div",{className:"channel-card-head",children:[n.jsx("div",{children:r?n.jsxs("div",{className:"group-edit-fields",children:[n.jsx("input",{value:E,onChange:V=>G(V.target.value),placeholder:"分组名称"}),n.jsx("input",{value:F,onChange:V=>N(V.target.value),placeholder:"说明"})]}):n.jsxs(n.Fragment,{children:[n.jsx("strong",{children:u.name}),u.description&&n.jsx("span",{children:u.description}),n.jsxs("small",{children:["创建于 ",sl(u.createdAt)]})]})}),n.jsx("div",{className:"channel-card-head-actions",children:r?n.jsxs(n.Fragment,{children:[n.jsx("button",{className:"primary-button compact-button",onClick:D,disabled:y||!E.trim(),children:y?"保存中":"保存"}),n.jsx("button",{className:"secondary-button compact-button",onClick:()=>{G(u.name),N(u.description),C(!1)},children:"取消"})]}):n.jsxs(n.Fragment,{children:[n.jsx("button",{className:"secondary-button compact-button",onClick:()=>C(!0),children:"重命名"}),n.jsx("button",{className:"danger-button compact-button",onClick:()=>h(u.id),children:"删除"})]})})]})})}function tt({title:u,children:f}){return n.jsxs("section",{className:"panel",children:[n.jsx("div",{className:"panel-title",children:n.jsx("h2",{children:u})}),f]})}function Rt({tone:u,children:f}){return n.jsx("span",{className:`badge tone-${u}`,children:f})}function nl({label:u,value:f,switchOn:h}){return n.jsxs("div",{className:"setting",children:[n.jsx("span",{children:u}),n.jsxs("div",{className:"setting-value",children:[n.jsx("strong",{children:f}),typeof h=="boolean"&&n.jsx("div",{className:h?"ios-switch is-on":"ios-switch","aria-hidden":"true",children:n.jsx("span",{})})]})]})}function Vp({value:u,options:f,onChange:h}){return n.jsx("div",{className:"segmented-control",children:f.map(r=>n.jsx("button",{className:u===r.value?"selected":"",onClick:()=>h(r.value),children:r.label},r.value))})}function Xt({text:u}){return n.jsx("div",{className:"empty",children:u})}$0.createRoot(document.getElementById("root")).render(n.jsx(L0.StrictMode,{children:n.jsx(mp,{})})); diff --git a/dist/index.html b/dist/index.html index 6b2db7f..6991552 100644 --- a/dist/index.html +++ b/dist/index.html @@ -5,7 +5,7 @@ CAPI - + diff --git a/src/App.tsx b/src/App.tsx index 6203eea..41c8bd8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4668,6 +4668,9 @@ function SettingsView({ models, channels, groups }: { models: ModelItem[]; chann maxLogs: 10000, maxQuotaEntries: 20000 }); + const [antigravityVersion, setAntigravityVersion] = useState(""); + const [antigravityModelsText, setAntigravityModelsText] = useState(""); + const [probingModels, setProbingModels] = useState(false); const [buildHealth, setBuildHealth] = useState(null); const [account, setAccount] = useState(null); const [accountUsername, setAccountUsername] = useState(""); @@ -4691,6 +4694,7 @@ function SettingsView({ models, channels, groups }: { models: ModelItem[]; chann { value: "admin", label: "管理员", description: "账号绑定" }, { value: "discord", label: "Discord", description: "登录限制" }, { value: "maintenance", label: "维护", description: "日志保留" }, + { value: "antigravity", label: "Antigravity", description: "版本与模型" }, { value: "backup", label: "备份", description: "导出恢复" } ]; const activeSettingsTab = settingsTabs.find((tab) => tab.value === settingsTab) || settingsTabs[0]; @@ -4702,9 +4706,10 @@ function SettingsView({ models, channels, groups }: { models: ModelItem[]; chann fetchJson<{ checkIn: CheckInSettings }>("/api/settings/check-in"), fetchJson<{ account: AccountProfile; user: User }>("/api/account/me"), fetchJson<{ maintenance: MaintenanceSettings }>("/api/settings/maintenance"), + fetchJson<{ antigravity: { clientVersion: string; models: string[] } }>("/api/settings/antigravity"), fetchJson("/api/health") ]) - .then(([discordData, authData, checkInData, accountData, maintenanceData, healthData]) => { + .then(([discordData, authData, checkInData, accountData, maintenanceData, antigravityData, healthData]) => { setDiscord(withBrowserDiscordDefaults(discordData.discord)); setBlockedGuildText(arrayOf(discordData.discord.blockedGuildIds).join("\n")); setRegistrationEnabled(authData.auth.registrationEnabled); @@ -4718,6 +4723,8 @@ function SettingsView({ models, channels, groups }: { models: ModelItem[]; chann setAccountEmail(accountData.account?.email || ""); setDiscordUserId(accountData.account?.discordUserId || ""); setMaintenance(maintenanceData.maintenance); + setAntigravityVersion(antigravityData.antigravity.clientVersion || ""); + setAntigravityModelsText(arrayOf(antigravityData.antigravity.models).join("\n")); setBuildHealth(healthData); }) .catch(() => setMessage("设置加载失败")); @@ -4836,6 +4843,63 @@ function SettingsView({ models, channels, groups }: { models: ModelItem[]; chann } } + function parseModelLines(text: string) { + const seen = new Set(); + const models: string[] = []; + for (const line of text.split(/[\s,]+/)) { + const id = line.trim(); + if (id && !seen.has(id)) { + seen.add(id); + models.push(id); + } + } + return models; + } + + async function saveAntigravitySettings() { + setSaving(true); + setMessage(""); + try { + const data = await fetchJson<{ antigravity: { clientVersion: string; models: string[] } }>("/api/settings/antigravity", { + method: "PATCH", + body: JSON.stringify({ clientVersion: antigravityVersion.trim(), models: parseModelLines(antigravityModelsText) }) + }); + setAntigravityVersion(data.antigravity.clientVersion || ""); + setAntigravityModelsText(arrayOf(data.antigravity.models).join("\n")); + setMessage("Antigravity 设置已保存"); + } catch (error) { + setMessage(error instanceof Error ? error.message : "Antigravity 设置保存失败"); + } finally { + setSaving(false); + } + } + + async function probeAntigravityModels() { + const channel = channels.find((item) => item.provider === "antigravity" && arrayOf(item.openaiAccounts).length > 0) + || channels.find((item) => item.provider === "antigravity"); + if (!channel) { + setMessage("没有可用的 Antigravity 账号池,请先在 Antigravity 页新建并导入/授权账号"); + return; + } + setProbingModels(true); + setMessage(""); + try { + const data = await fetchJson<{ models: string[] }>(`/api/channels/${encodeURIComponent(channel.id)}/antigravity/available-models`, { + method: "POST", + body: JSON.stringify({}) + }); + // Merge discovered models with any the admin already typed, so a manual + // entry is never lost by a probe. + const merged = parseModelLines([antigravityModelsText, ...arrayOf(data.models)].join("\n")); + setAntigravityModelsText(merged.join("\n")); + setMessage(`已从上游拉取 ${arrayOf(data.models).length} 个模型,确认后点击保存生效`); + } catch (error) { + setMessage(error instanceof Error ? error.message : "拉取上游模型失败"); + } finally { + setProbingModels(false); + } + } + async function downloadBackup() { setSaving(true); setMessage(""); @@ -5045,6 +5109,42 @@ response = client.chat.completions.create( )} + {settingsTab === "antigravity" && ( + +
+ +