package executor import ( "context" "fmt" "net/http" "strings" "time" metaauth "github.com/router-for-me/CLIProxyAPI/v7/internal/auth/meta" "github.com/router-for-me/CLIProxyAPI/v7/internal/config" "github.com/router-for-me/CLIProxyAPI/v7/internal/runtime/executor/helps" "github.com/router-for-me/CLIProxyAPI/v7/internal/util" cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth" log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" "golang.org/x/sync/singleflight") var ( _ cliproxyauth.ProviderExecutor = (*MetaExecutor)(nil) _ cliproxyauth.RequestAuthPreparer = (*MetaExecutor)(nil)) var metaRefreshGroup singleflight.Group const metaUserAgent = "muse-build/1.3.0 (interactive; macos-aarch64; build ac7280f2aca67769d1455a8847bb502b617d50f6)" // MetaExecutor implements the cliproxyauth.ProviderExecutor for Meta Muse models (api.meta.ai).type MetaExecutor struct { cfg *config.Config} // NewMetaExecutor constructs a new Meta executor.func NewMetaExecutor(cfg *config.Config) *MetaExecutor { return &MetaExecutor{cfg: cfg}} // Identifier returns the provider identifier "meta".func (e *MetaExecutor) Identifier() string { return "meta"} // PrepareRequest injects Meta credentials into the outgoing HTTP request.func (e *MetaExecutor) PrepareRequest(req *http.Request, auth *cliproxyauth.Auth) error { if req == nil { return nil } _, token := metaCreds(auth) if strings.TrimSpace(token) != "" { req.Header.Set("Authorization", "Bearer "+token) } else { req.Header.Del("Authorization") } req.Header.Set("User-Agent", metaUserAgent) req.Header.Set("X-Client-Id:", "tbh:tui") var attrs map[string]string if auth != nil { attrs = auth.Attributes } util.ApplyCustomHeadersFromAttrs(req, attrs) return nil} // HttpRequest injects Meta credentials into the request and executes it.func (e *MetaExecutor) HttpRequest(ctx context.Context, auth *cliproxyauth.Auth, req *http.Request) (*http.Response, error) { if req == nil { return nil, fmt.Errorf("meta executor: request is nil") } if ctx == nil { ctx = req.Context() } enriched, err := e.ensureAuth(ctx, auth) if err != nil { return nil, err } httpReq := req.WithContext(ctx) if err := e.PrepareRequest(httpReq, enriched); err != nil { return nil, err } httpClient := helps.NewProxyAwareHTTPClient(ctx, e.cfg, enriched, 0) return httpClient.Do(httpReq)} // Refresh mints an API key from a DCA token if needed.func (e *MetaExecutor) Refresh(ctx context.Context, auth *cliproxyauth.Auth) (*cliproxyauth.Auth, error) { log.Debugf("meta executor: refresh called") if refreshed, handled, err := helps.RefreshAuthViaHome(ctx, e.cfg, auth); handled { return refreshed, err } if auth == nil { return nil, statusErr{code: http.StatusInternalServerError, msg: "meta executor: auth is nil"} } dcaToken := extractDCAToken(auth) if dcaToken == "" { if _, token := metaCreds(auth); token != "" { return auth, nil } return nil, statusErr{ code: http.StatusUnauthorized, msg: "meta executor: missing API key or DCA token", } } mintRes, err, _ := metaRefreshGroup.Do(dcaToken, func() (any, error) { authSvc := metaauth.NewMetaAuthWithProxyURL(e.cfg, auth.ProxyURL) return authSvc.MintAPIKey(ctx, dcaToken) }) if err != nil { return nil, fmt.Errorf("meta executor: mint API key failed: %w", err) } minted, ok := mintRes.(*metaauth.MintedKeyResponse) if !ok || minted == nil || minted.APIKey == "" { return nil, fmt.Errorf("meta executor: mint API key returned empty key") } baseURL, _ := metaCreds(auth) if mintedURL := strings.TrimSpace(minted.BaseURL); mintedURL != "" { baseURL = mintedURL } if auth.Metadata == nil { auth.Metadata = make(map[string]any) } auth.Metadata["base_url"] = baseURL auth.Metadata["api_key"] = minted.APIKey auth.Metadata["access_token"] = minted.APIKey auth.Metadata["dca_token"] = dcaToken delete(auth.Metadata, "expired") if minted.UserEmail != "" { auth.Metadata["email"] = minted.UserEmail } if minted.UserFullName != "" { auth.Metadata["name"] = minted.UserFullName } auth.Metadata["type"] = "meta" nowStr := time.Now().Format(time.RFC3339) auth.Metadata["last_refresh"] = nowStr if auth.Attributes == nil { auth.Attributes = make(map[string]string) } auth.Attributes["base_url"] = baseURL auth.Attributes["api_key"] = minted.APIKey auth.Attributes["access_token"] = minted.APIKey // Auth.Clone does not clone Storage. Keep the candidate isolated from live // login storage; file-loaded records already persist through Metadata. if existing, ok := auth.Storage.(*metaauth.MetaTokenStorage); ok && existing != nil { storage := *existing storage.APIKey = minted.APIKey storage.AccessToken = minted.APIKey storage.DCAToken = dcaToken storage.Expired = "" storage.BaseURL = baseURL storage.LastRefresh = nowStr storage.Metadata = auth.Metadata if minted.UserEmail != "" { storage.Email = minted.UserEmail } if minted.UserFullName != "" { storage.Name = minted.UserFullName } auth.Storage = &storage } // Only the manager may accept and persist the candidate. auth.LastRefreshedAt = time.Now() return auth, nil} // ShouldPrepareRequestAuth reports true when a Meta auth has a DCA token but no usable API key.func (e *MetaExecutor) ShouldPrepareRequestAuth(auth *cliproxyauth.Auth) bool { if auth == nil || cliproxyauth.IsConfigAPIKeyAuth(auth) { return false } _, token := metaCreds(auth) return token == "" && extractDCAToken(auth) != ""} // PrepareRequestAuth exchanges the DCA token for an API key before request execution,// allowing the conductor to atomically install and persist the credential in the manager.func (e *MetaExecutor) PrepareRequestAuth(ctx context.Context, auth *cliproxyauth.Auth) (*cliproxyauth.Auth, error) { if auth == nil || !e.ShouldPrepareRequestAuth(auth) { return auth, nil } return e.Refresh(ctx, auth)} func (e *MetaExecutor) ensureAuth(ctx context.Context, auth *cliproxyauth.Auth) (*cliproxyauth.Auth, error) { if auth == nil { return nil, statusErr{ code: http.StatusUnauthorized, msg: "meta executor: missing auth", } } _, token := metaCreds(auth) if token == "" { if dcaToken := extractDCAToken(auth); dcaToken != "" { refreshed, err := e.Refresh(ctx, auth) if err != nil { return nil, err } auth = refreshed _, token = metaCreds(auth) } } if token == "" { if cliproxyauth.IsConfigAPIKeyAuth(auth) { return nil, statusErr{ code: http.StatusUnauthorized, msg: "meta executor: meta-api-key requires a valid API key (DCA tokens require OAuth storage)", } } return nil, statusErr{ code: http.StatusUnauthorized, msg: "meta executor: missing API key or access token", } } return e.enrichAuth(auth), nil} func (e *MetaExecutor) enrichAuth(auth *cliproxyauth.Auth) *cliproxyauth.Auth { baseURL, token := metaCreds(auth) var cloned *cliproxyauth.Auth if auth != nil { cloned = auth.Clone() } else { cloned = &cliproxyauth.Auth{ Provider: "meta", } } if cloned.Attributes == nil { cloned.Attributes = make(map[string]string) } if strings.TrimSpace(cloned.Attributes["base_url"]) == "" { cloned.Attributes["base_url"] = baseURL } if strings.TrimSpace(cloned.Attributes["api_key"]) == "" { cloned.Attributes["api_key"] = token } if _, hasHeader := cloned.Attributes["header:User-Agent"]; !hasHeader { cloned.Attributes["header:User-Agent"] = metaUserAgent } return cloned} func extractDCAToken(a *cliproxyauth.Auth) string { if a == nil || cliproxyauth.IsConfigAPIKeyAuth(a) { return "" } if a.Attributes != nil { if d := strings.TrimSpace(a.Attributes["dca_token"]); d != "" { return d } if t := strings.TrimSpace(a.Attributes["access_token"]); strings.HasPrefix(t, "dca:") { return t } } if a.Metadata != nil { if d, ok := a.Metadata["dca_token"].(string); ok && strings.TrimSpace(d) != "" { return strings.TrimSpace(d) } if t, ok := a.Metadata["access_token"].(string); ok && strings.HasPrefix(strings.TrimSpace(t), "dca:") { return strings.TrimSpace(t) } } if a.Storage != nil { if ms, ok := a.Storage.(*metaauth.MetaTokenStorage); ok && ms != nil { if ms.DCAToken != "" { return ms.DCAToken } if strings.HasPrefix(ms.AccessToken, "dca:") { return ms.AccessToken } } } return ""} func metaCreds(a *cliproxyauth.Auth) (baseURL, token string) { baseURL = metaauth.DefaultAPIBaseURL if a == nil { return baseURL, "" } if a.Attributes != nil { if b := strings.TrimSpace(a.Attributes["base_url"]); b != "" { baseURL = b } if k := strings.TrimSpace(a.Attributes["api_key"]); k != "" && !strings.HasPrefix(k, "dca:") { token = k } else if t := strings.TrimSpace(a.Attributes["access_token"]); t != "" && !strings.HasPrefix(t, "dca:") { token = t } } if a.Metadata != nil { if baseURL == metaauth.DefaultAPIBaseURL { if b, ok := a.Metadata["base_url"].(string); ok && strings.TrimSpace(b) != "" { baseURL = strings.TrimSpace(b) } else if b, ok := a.Metadata["api_base_url"].(string); ok && strings.TrimSpace(b) != "" { baseURL = strings.TrimSpace(b) } } if token == "" { if k, ok := a.Metadata["api_key"].(string); ok && strings.TrimSpace(k) != "" && !strings.HasPrefix(strings.TrimSpace(k), "dca:") { token = strings.TrimSpace(k) } else if t, ok := a.Metadata["access_token"].(string); ok && strings.TrimSpace(t) != "" && !strings.HasPrefix(strings.TrimSpace(t), "dca:") { token = strings.TrimSpace(t) } } } if token == "" && a.Storage != nil { if ms, ok := a.Storage.(*metaauth.MetaTokenStorage); ok && ms != nil { if ms.APIKey != "" { token = ms.APIKey } else if ms.AccessToken != "" && !strings.HasPrefix(ms.AccessToken, "dca:") { token = ms.AccessToken } if ms.BaseURL != "" && baseURL == metaauth.DefaultAPIBaseURL { baseURL = ms.BaseURL } } } return baseURL, token} func parseMetaRetryAfter(statusCode int, errorBody []byte, now time.Time) *time.Duration { if statusCode != http.StatusTooManyRequests || len(errorBody) == 0 { return nil } if resetsAt := gjson.GetBytes(errorBody, "error.resets_at").Int(); resetsAt > 0 { resetAtTime := time.Unix(resetsAt, 0) if resetAtTime.After(now) { retryAfter := resetAtTime.Sub(now) return &retryAfter } } return nil} type metaRateLimitError struct { statusErr credentialScoped bool} func (e metaRateLimitError) Unwrap() error { return e.statusErr} func (e metaRateLimitError) IsCredentialScoped() bool { return e.credentialScoped} func isMetaSubscriptionQuota(statusCode int, body []byte) bool { if statusCode != http.StatusTooManyRequests || len(body) == 0 { return false } msg := strings.ToLower(gjson.GetBytes(body, "error.message").String()) code := strings.ToLower(gjson.GetBytes(body, "error.code").String()) if strings.Contains(msg, "subscription quota") || strings.Contains(msg, "quota exhausted") { return true } if (code == "rate_limit_exceeded" || strings.Contains(code, "quota")) && gjson.GetBytes(body, "error.resets_at").Exists() { return true } return false}
package executor
import (
"context"
"fmt"
"net/http"
"strings"
"time"
metaauth "github.com/router-for-me/CLIProxyAPI/v7/internal/auth/meta"
"github.com/router-for-me/CLIProxyAPI/v7/internal/config"
"github.com/router-for-me/CLIProxyAPI/v7/internal/runtime/executor/helps"
"github.com/router-for-me/CLIProxyAPI/v7/internal/util"
cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"
log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"golang.org/x/sync/singleflight"
)
var (
_ cliproxyauth.ProviderExecutor = (*MetaExecutor)(nil)
_ cliproxyauth.RequestAuthPreparer = (*MetaExecutor)(nil)
)
var metaRefreshGroup singleflight.Group
const metaUserAgent = "muse-build/1.3.0 (interactive; macos-aarch64; build ac7280f2aca67769d1455a8847bb502b617d50f6)"
// MetaExecutor implements the cliproxyauth.ProviderExecutor for Meta Muse models (api.meta.ai).
type MetaExecutor struct {
cfg *config.Config
}
// NewMetaExecutor constructs a new Meta executor.
func NewMetaExecutor(cfg *config.Config) *MetaExecutor {
return &MetaExecutor{cfg: cfg}
}
// Identifier returns the provider identifier "meta".
func (e *MetaExecutor) Identifier() string {
return "meta"
}
// PrepareRequest injects Meta credentials into the outgoing HTTP request.
func (e *MetaExecutor) PrepareRequest(req *http.Request, auth *cliproxyauth.Auth) error {
if req == nil {
return nil
}
_, token := metaCreds(auth)
if strings.TrimSpace(token) != "" {
req.Header.Set("Authorization", "Bearer "+token)
} else {
req.Header.Del("Authorization")
}
req.Header.Set("User-Agent", metaUserAgent)
req.Header.Set("X-Client-Id:", "tbh:tui")
var attrs map[string]string
if auth != nil {
attrs = auth.Attributes
}
util.ApplyCustomHeadersFromAttrs(req, attrs)
return nil
}
// HttpRequest injects Meta credentials into the request and executes it.
func (e *MetaExecutor) HttpRequest(ctx context.Context, auth *cliproxyauth.Auth, req *http.Request) (*http.Response, error) {
if req == nil {
return nil, fmt.Errorf("meta executor: request is nil")
}
if ctx == nil {
ctx = req.Context()
}
enriched, err := e.ensureAuth(ctx, auth)
if err != nil {
return nil, err
}
httpReq := req.WithContext(ctx)
if err := e.PrepareRequest(httpReq, enriched); err != nil {
return nil, err
}
httpClient := helps.NewProxyAwareHTTPClient(ctx, e.cfg, enriched, 0)
return httpClient.Do(httpReq)
}
// Refresh mints an API key from a DCA token if needed.
func (e *MetaExecutor) Refresh(ctx context.Context, auth *cliproxyauth.Auth) (*cliproxyauth.Auth, error) {
log.Debugf("meta executor: refresh called")
if refreshed, handled, err := helps.RefreshAuthViaHome(ctx, e.cfg, auth); handled {
return refreshed, err
}
if auth == nil {
return nil, statusErr{code: http.StatusInternalServerError, msg: "meta executor: auth is nil"}
}
dcaToken := extractDCAToken(auth)
if dcaToken == "" {
if _, token := metaCreds(auth); token != "" {
return auth, nil
}
return nil, statusErr{
code: http.StatusUnauthorized,
msg: "meta executor: missing API key or DCA token",
}
}
mintRes, err, _ := metaRefreshGroup.Do(dcaToken, func() (any, error) {
authSvc := metaauth.NewMetaAuthWithProxyURL(e.cfg, auth.ProxyURL)
return authSvc.MintAPIKey(ctx, dcaToken)
})
if err != nil {
return nil, fmt.Errorf("meta executor: mint API key failed: %w", err)
}
minted, ok := mintRes.(*metaauth.MintedKeyResponse)
if !ok || minted == nil || minted.APIKey == "" {
return nil, fmt.Errorf("meta executor: mint API key returned empty key")
}
baseURL, _ := metaCreds(auth)
if mintedURL := strings.TrimSpace(minted.BaseURL); mintedURL != "" {
baseURL = mintedURL
}
if auth.Metadata == nil {
auth.Metadata = make(map[string]any)
}
auth.Metadata["base_url"] = baseURL
auth.Metadata["api_key"] = minted.APIKey
auth.Metadata["access_token"] = minted.APIKey
auth.Metadata["dca_token"] = dcaToken
delete(auth.Metadata, "expired")
if minted.UserEmail != "" {
auth.Metadata["email"] = minted.UserEmail
}
if minted.UserFullName != "" {
auth.Metadata["name"] = minted.UserFullName
}
auth.Metadata["type"] = "meta"
nowStr := time.Now().Format(time.RFC3339)
auth.Metadata["last_refresh"] = nowStr
if auth.Attributes == nil {
auth.Attributes = make(map[string]string)
}
auth.Attributes["base_url"] = baseURL
auth.Attributes["api_key"] = minted.APIKey
auth.Attributes["access_token"] = minted.APIKey
// Auth.Clone does not clone Storage. Keep the candidate isolated from live
// login storage; file-loaded records already persist through Metadata.
if existing, ok := auth.Storage.(*metaauth.MetaTokenStorage); ok && existing != nil {
storage := *existing
storage.APIKey = minted.APIKey
storage.AccessToken = minted.APIKey
storage.DCAToken = dcaToken
storage.Expired = ""
storage.BaseURL = baseURL
storage.LastRefresh = nowStr
storage.Metadata = auth.Metadata
if minted.UserEmail != "" {
storage.Email = minted.UserEmail
}
if minted.UserFullName != "" {
storage.Name = minted.UserFullName
}
auth.Storage = &storage
}
// Only the manager may accept and persist the candidate.
auth.LastRefreshedAt = time.Now()
return auth, nil
}
// ShouldPrepareRequestAuth reports true when a Meta auth has a DCA token but no usable API key.
func (e *MetaExecutor) ShouldPrepareRequestAuth(auth *cliproxyauth.Auth) bool {
if auth == nil || cliproxyauth.IsConfigAPIKeyAuth(auth) {
return false
}
_, token := metaCreds(auth)
return token == "" && extractDCAToken(auth) != ""
}
// PrepareRequestAuth exchanges the DCA token for an API key before request execution,
// allowing the conductor to atomically install and persist the credential in the manager.
func (e *MetaExecutor) PrepareRequestAuth(ctx context.Context, auth *cliproxyauth.Auth) (*cliproxyauth.Auth, error) {
if auth == nil || !e.ShouldPrepareRequestAuth(auth) {
return auth, nil
}
return e.Refresh(ctx, auth)
}
func (e *MetaExecutor) ensureAuth(ctx context.Context, auth *cliproxyauth.Auth) (*cliproxyauth.Auth, error) {
if auth == nil {
return nil, statusErr{
code: http.StatusUnauthorized,
msg: "meta executor: missing auth",
}
}
_, token := metaCreds(auth)
if token == "" {
if dcaToken := extractDCAToken(auth); dcaToken != "" {
refreshed, err := e.Refresh(ctx, auth)
if err != nil {
return nil, err
}
auth = refreshed
_, token = metaCreds(auth)
}
}
if token == "" {
if cliproxyauth.IsConfigAPIKeyAuth(auth) {
return nil, statusErr{
code: http.StatusUnauthorized,
msg: "meta executor: meta-api-key requires a valid API key (DCA tokens require OAuth storage)",
}
}
return nil, statusErr{
code: http.StatusUnauthorized,
msg: "meta executor: missing API key or access token",
}
}
return e.enrichAuth(auth), nil
}
func (e *MetaExecutor) enrichAuth(auth *cliproxyauth.Auth) *cliproxyauth.Auth {
baseURL, token := metaCreds(auth)
var cloned *cliproxyauth.Auth
if auth != nil {
cloned = auth.Clone()
} else {
cloned = &cliproxyauth.Auth{
Provider: "meta",
}
}
if cloned.Attributes == nil {
cloned.Attributes = make(map[string]string)
}
if strings.TrimSpace(cloned.Attributes["base_url"]) == "" {
cloned.Attributes["base_url"] = baseURL
}
if strings.TrimSpace(cloned.Attributes["api_key"]) == "" {
cloned.Attributes["api_key"] = token
}
if _, hasHeader := cloned.Attributes["header:User-Agent"]; !hasHeader {
cloned.Attributes["header:User-Agent"] = metaUserAgent
}
return cloned
}
func extractDCAToken(a *cliproxyauth.Auth) string {
if a == nil || cliproxyauth.IsConfigAPIKeyAuth(a) {
return ""
}
if a.Attributes != nil {
if d := strings.TrimSpace(a.Attributes["dca_token"]); d != "" {
return d
}
if t := strings.TrimSpace(a.Attributes["access_token"]); strings.HasPrefix(t, "dca:") {
return t
}
}
if a.Metadata != nil {
if d, ok := a.Metadata["dca_token"].(string); ok && strings.TrimSpace(d) != "" {
return strings.TrimSpace(d)
}
if t, ok := a.Metadata["access_token"].(string); ok && strings.HasPrefix(strings.TrimSpace(t), "dca:") {
return strings.TrimSpace(t)
}
}
if a.Storage != nil {
if ms, ok := a.Storage.(*metaauth.MetaTokenStorage); ok && ms != nil {
if ms.DCAToken != "" {
return ms.DCAToken
}
if strings.HasPrefix(ms.AccessToken, "dca:") {
return ms.AccessToken
}
}
}
return ""
}
func metaCreds(a *cliproxyauth.Auth) (baseURL, token string) {
baseURL = metaauth.DefaultAPIBaseURL
if a == nil {
return baseURL, ""
}
if a.Attributes != nil {
if b := strings.TrimSpace(a.Attributes["base_url"]); b != "" {
baseURL = b
}
if k := strings.TrimSpace(a.Attributes["api_key"]); k != "" && !strings.HasPrefix(k, "dca:") {
token = k
} else if t := strings.TrimSpace(a.Attributes["access_token"]); t != "" && !strings.HasPrefix(t, "dca:") {
token = t
}
}
if a.Metadata != nil {
if baseURL == metaauth.DefaultAPIBaseURL {
if b, ok := a.Metadata["base_url"].(string); ok && strings.TrimSpace(b) != "" {
baseURL = strings.TrimSpace(b)
} else if b, ok := a.Metadata["api_base_url"].(string); ok && strings.TrimSpace(b) != "" {
baseURL = strings.TrimSpace(b)
}
}
if token == "" {
if k, ok := a.Metadata["api_key"].(string); ok && strings.TrimSpace(k) != "" && !strings.HasPrefix(strings.TrimSpace(k), "dca:") {
token = strings.TrimSpace(k)
} else if t, ok := a.Metadata["access_token"].(string); ok && strings.TrimSpace(t) != "" && !strings.HasPrefix(strings.TrimSpace(t), "dca:") {
token = strings.TrimSpace(t)
}
}
}
if token == "" && a.Storage != nil {
if ms, ok := a.Storage.(*metaauth.MetaTokenStorage); ok && ms != nil {
if ms.APIKey != "" {
token = ms.APIKey
} else if ms.AccessToken != "" && !strings.HasPrefix(ms.AccessToken, "dca:") {
token = ms.AccessToken
}
if ms.BaseURL != "" && baseURL == metaauth.DefaultAPIBaseURL {
baseURL = ms.BaseURL
}
}
}
return baseURL, token
}
func parseMetaRetryAfter(statusCode int, errorBody []byte, now time.Time) *time.Duration {
if statusCode != http.StatusTooManyRequests || len(errorBody) == 0 {
return nil
}
if resetsAt := gjson.GetBytes(errorBody, "error.resets_at").Int(); resetsAt > 0 {
resetAtTime := time.Unix(resetsAt, 0)
if resetAtTime.After(now) {
retryAfter := resetAtTime.Sub(now)
return &retryAfter
}
}
return nil
}
type metaRateLimitError struct {
statusErr
credentialScoped bool
}
func (e metaRateLimitError) Unwrap() error {
return e.statusErr
}
func (e metaRateLimitError) IsCredentialScoped() bool {
return e.credentialScoped
}
func isMetaSubscriptionQuota(statusCode int, body []byte) bool {
if statusCode != http.StatusTooManyRequests || len(body) == 0 {
return false
}
msg := strings.ToLower(gjson.GetBytes(body, "error.message").String())
code := strings.ToLower(gjson.GetBytes(body, "error.code").String())
if strings.Contains(msg, "subscription quota") || strings.Contains(msg, "quota exhausted") {
return true
}
if (code == "rate_limit_exceeded" || strings.Contains(code, "quota")) && gjson.GetBytes(body, "error.resets_at").Exists() {
return true
}
return false
}