diff --git a/desktop-tauri/i18n/i18n-src/en/add-account.json b/desktop-tauri/i18n/i18n-src/en/add-account.json index aa41b503..f662260b 100644 --- a/desktop-tauri/i18n/i18n-src/en/add-account.json +++ b/desktop-tauri/i18n/i18n-src/en/add-account.json @@ -10,6 +10,7 @@ "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "The Anthropic-compatible endpoint is https://api.deepseek.com/anthropic (fill it in when you switch the protocol to anthropic)", "Anthropic 协议填根地址,不要带 /v1": "For the Anthropic protocol, use the root URL without /v1", "Antigravity": "Antigravity", + "AtomCode": "AtomCode", "AutoClaw 国内版": "AutoClaw China edition", "AutoClaw 国际版": "AutoClaw International edition", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS is required; paste the **full Cookie** (a Cookie editor can export it in one click) — the **PTOKEN must be kept**, as the gateway uses it to exchange for a KukuAI business session token from Baidu; without it, model refresh reports \"Not signed in\". A full Cookie header, a JSON array exported by a Cookie editor, or a {BDUSS, STOKEN} object are all supported. KukuAI has no refresh mechanism, so once the login state expires, just paste it again.", @@ -20,6 +21,7 @@ "Command Code": "Command Code", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT (three dot-separated parts; you can also paste a whole Bearer line or the data.bin content)", "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT (three dot-separated parts; a whole chatglm_token=… line works too)", + "LobsterAI": "LobsterAI", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode International edition", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "NVIDIA NIM managed endpoint; apply for the Key at integrate.api.nvidia.com", @@ -97,6 +99,7 @@ "可选(cloudaicompanionProject):留空由网关自动发现": "Optional (cloudaicompanionProject); if blank, the gateway discovers it automatically", "可选,展示用": "Optional; for display", "可选,没有则无法自动续期": "Optional; without it, automatic renewal is not possible", + "可选,用于生成账号标识": "Optional; used to build the account identifier", "可选,留空使用凭据里的 user_name": "Optional; defaults to the user_name in the credential", "可选,留空使用账号姓名、邮箱或令牌指纹": "Optional; defaults to the account name, email, or token fingerprint", "可选,留空使用账号昵称或邮箱": "Optional; defaults to the account nickname or email", @@ -112,6 +115,7 @@ "可选,留空则用账号昵称": "Optional; defaults to the account nickname", "可选,留空则用账号昵称或 userId": "Optional; leave blank to use the account nickname or userId", "可选,留空则用邮箱或令牌尾号": "Optional; defaults to the email or token suffix", + "可选,留空用「AtomCode」": "Optional; defaults to “AtomCode”", "可选,留空用「OfficeAce 果办」": "Optional; defaults to “OfficeAce”", "可选,留空自动生成": "Optional; generated automatically if blank", "可选,留空自动用昵称": "Optional; defaults to the nickname", @@ -145,6 +149,7 @@ "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "The International edition's inference site is api.z.ai, a different site from the China edition's; plans are separate as well.", "国际版(WorkBuddy AI)": "International edition (WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "International site Coding plan; the OpenAI-compatible address is https://api.minimax.io/v1", + "在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。": "Paste the two values from the AtomCode page on AtomGit: accessToken and refreshToken. The refresh token is strongly recommended — this provider's token is not a JWT (it expires in about 7 days and carries no readable expiry), so an account without it cannot renew itself and must be re-authorised once it expires.", "在 cc-switch 里找到": "Found", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "Complete the official sign-in in the embedded window; the account is added to the list automatically on success.", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "Generate a PAT in the Qoder account settings → Integrations for {label} (do not use a Google / GitHub token). Accounts and credentials do not carry over between the two regions.", @@ -177,6 +182,7 @@ "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "The authorization page will open in your system default browser (reusing the Huawei Cloud account already signed in there); the gateway polls the cloud for the authorization code, so it does not matter which machine the browser runs on. The account is added to the list automatically once done", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "The authorization page will open in your system default browser (reusing the Huawei Cloud account already signed in there); when the browser and the gateway are not on the same machine, paste the final callback URL back into the panel as described above", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "The sign-in page will open in your system default browser (reusing the Accio account already signed in there); the account is added to the list automatically after sign-in, and closing the popup cancels the wait", + "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "The sign-in page will open in your system default browser (reusing the LobsterAI account already signed in there); the account is added to the list automatically after sign-in, and closing the popup cancels the wait", "小米 MiMo Token Plan 套餐(新加坡站)": "Xiaomi MiMo Token Plan (Singapore site)", "已到账": "Credited", "已取消登录等待": "Pending sign-in canceled", @@ -198,13 +204,16 @@ "恢复 Google / GitHub 入口": "Restore Google / GitHub options", "手机号": "Phone number", "手机验证码登录": "SMS code login", + "打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。": "Open the official LobsterAI sign-in page (lobsterai.youdao.com) and sign in with your LobsterAI account: once finished, the official page returns to this machine and the account is added automatically — the callback lands on the gateway's own port, so the browser must be on the same machine as the gateway; the auth code is only handed to the gateway locally and no plaintext token is shown in the UI.", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Open the official authorization page for Trae SOLO (trae.cn) and sign in with your Trae account: once authorized, the official page redirects back to a temporary port on this machine; a same-machine deployment receives it automatically, while Docker / remote panels should copy the callback URL from the address bar as prompted.", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Open the official sign-in page for Accio {label} ({site}) and sign in with your Accio account: on success the official page redirects back to this machine; a same-machine setup completes automatically, while Docker / remote panels just paste the callback URL as prompted (the authorization code is passed to the gateway only on this machine, and no plaintext token is shown in the UI).", "打开 Accio {label}登录页": "Open the Accio {label} sign-in page", "打开 Antigravity 网页登录": "Open Antigravity web login", + "打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。": "Open the AtomGit authorization page and sign in: once finished, the gateway automatically polls the broker for the token and adds the account (the callback is terminated by the broker and never returns to this machine, so no callback URL needs to be filled in).", "打开 CatPaw 网页登录": "Open CatPaw web login", "打开 Cline 授权页": "Open Cline authorization page", "打开 CodeArts 授权页": "Open CodeArts authorization page", + "打开 LobsterAI 登录页": "Open the LobsterAI sign-in page", "打开 Qoder 网页登录": "Open Qoder web login", "打开 Trae 授权页": "Open Trae authorization page", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "Open the official authorization page for ZCode {label} and sign in with your account. The authorization result is recorded by the ZCode server; the gateway fetches the credential in the background and adds the account to the list.", @@ -279,11 +288,13 @@ "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "Login-state file path: ~/.box-agent/config/auth.json (Windows: C:\\Users\\\\.box-agent\\config\\auth.json)", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "Delete custom provider \"{name}\"? This will also delete {count} accounts under it; deletion cannot be undone.", "等待 Accio {label}登录完成…": "Waiting for Accio {label} sign-in…", + "等待 AtomCode 登录完成…": "Waiting for AtomCode sign-in…", "等待 CatPaw 登录完成…": "Waiting for CatPaw sign-in…", "等待 Cline 授权确认…": "Waiting for Cline authorization confirmation…", "等待 CodeArts 登录完成…": "Waiting for CodeArts sign-in…", "等待 Google 授权完成…": "Waiting for Google authorization…", "等待 KukuAI 登录完成…": "Waiting for KukuAI sign-in…", + "等待 LobsterAI 登录完成…": "Waiting for LobsterAI sign-in…", "等待 OfficeAce 登录完成…": "Waiting for OfficeAce sign-in…", "等待 OrcaTerm 登录完成…": "Waiting for OrcaTerm sign-in…", "等待 Qoder 授权完成…": "Waiting for Qoder authorization…", @@ -305,6 +316,7 @@ "粘贴 session cookie": "Paste session cookie", "粘贴 token / refreshToken": "Paste token / refreshToken", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "Paste a token starting with workos:… (the prefix is added automatically if missing)", + "粘贴令牌对": "Paste token pair", "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "Paste a Zhipu Qingyan access token (a JWT with three dot-separated parts). How to get it: open chatglm.cn in a browser, sign in, then copy the value of the chatglm_token cookie from Application → Cookies in the developer tools (pasting the whole chatglm_token=…; line works too). It is verified against the upstream once after pasting, and the account is only saved if that passes; signed-in accounts get points and sign-in capability, and the gateway renews it automatically afterwards (the upstream has a renewal API).", "粘贴登录凭据": "Paste login credentials", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "What you paste is the login session (which is also the model gateway token). Loomy has no renewal API; the session is valid for about 14 days, so just sign in again after it expires.", @@ -319,6 +331,7 @@ "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "Opens in the system browser (reuses the signed-in Meituan account); added to the list when done", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "Opens in the system browser (reuses a signed-in account); added to the list when done", "系统默认浏览器": "System default browser", + "续期令牌": "Refresh token", "编码套餐 API Key": "Coding Plan API Key", "网关 Secret": "Gateway secret", "网页登录": "Web login", @@ -331,6 +344,7 @@ "获取验证码": "Get code", "访客模式": "Guest mode", "访客模式(免登录)": "Guest mode (no sign-in)", + "访问令牌": "Access token", "该上游无需鉴权(不发送鉴权头)": "This upstream needs no authentication (no auth headers are sent)", "该提供商": "This provider", "该提供商账号添加功能即将上线": "Account adding for this provider is coming soon", diff --git a/desktop-tauri/i18n/i18n-src/en/checkin.json b/desktop-tauri/i18n/i18n-src/en/checkin.json index 6bd0db85..b0ec3a9c 100644 --- a/desktop-tauri/i18n/i18n-src/en/checkin.json +++ b/desktop-tauri/i18n/i18n-src/en/checkin.json @@ -153,6 +153,7 @@ "每天 {time} · {scope}": "Daily {time} · {scope}", "每天时刻": "Daily time", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "One row per account per check-in run · outcomes and gains as recorded by the backend", + "每日活动签到(+100 积分,已领则中性提示)": "Daily activity check-in (+100 credits; a neutral note if already claimed)", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "Daily activity tasks · a web-app conversation lights up the daily activity, only claims add points · the two can run separately", "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "Daily sign-in points · claims member activities along the way · guest accounts have no points system (skipped)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "Daily welfare · claim → confirm → read back to verify · resets on the Beijing calendar day", @@ -232,6 +233,7 @@ "领取": "Claim", "领取中": "Claiming", "领取中…": "Claiming…", + "领取免费 CodingPlan(按需:套餐有效时只回读额度)": "Claim the free CodingPlan (on demand: when the plan is active the quota is only read back)", "领取失败": "Claim failed", "领取失败:{error}": "Claim failed: {error}", "(共 {n} 个账号)": " ({n} accounts total)", diff --git a/desktop-tauri/i18n/i18n-src/en/requests.json b/desktop-tauri/i18n/i18n-src/en/requests.json index a7382b2b..cd625199 100644 --- a/desktop-tauri/i18n/i18n-src/en/requests.json +++ b/desktop-tauri/i18n/i18n-src/en/requests.json @@ -66,6 +66,8 @@ "失败({status}):{error}": "Failed ({status}): {error}", "失败:{error}": "Failed: {error}", "实际发给上游 {bytes} 字节": "{bytes} bytes actually sent upstream", + "已下发": "Delivered", + "实际下发给客户端 {bytes} 字节": "{bytes} bytes actually delivered to the client", "客户端": "Client", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "Will delete {count} logs · {raw} of them still carry raw payloads · database size {db}", "尝试 {n} ·": "Attempt {n} ·", diff --git a/desktop-tauri/i18n/i18n-src/ja/add-account.json b/desktop-tauri/i18n/i18n-src/ja/add-account.json index 09069fed..e9f55be6 100644 --- a/desktop-tauri/i18n/i18n-src/ja/add-account.json +++ b/desktop-tauri/i18n/i18n-src/ja/add-account.json @@ -10,6 +10,7 @@ "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "Anthropic 互換エンドポイントは https://api.deepseek.com/anthropic です(プロトコルを anthropic に切り替えるときにこれを入力)", "Anthropic 协议填根地址,不要带 /v1": "Anthropic プロトコルではルートアドレスを入力し、/v1 は付けないでください", "Antigravity": "Antigravity", + "AtomCode": "AtomCode", "AutoClaw 国内版": "AutoClaw 中国版", "AutoClaw 国际版": "AutoClaw 国際版", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS は必須です;**完全な Cookie** を貼り付けてください(Cookie エディターのワンクリック書き出しで構いません)—— このうち **PTOKEN は必ず残してください**。ゲートウェイはこれを使って Baidu から KukuAI の業務セッショントークンを発行してもらうため、欠けるとモデル更新時に「未ログイン」と表示されます。Cookie ヘッダー全体、Cookie エディターが書き出した JSON 配列、または {BDUSS, STOKEN} オブジェクトに対応しています。KukuAI には更新の仕組みがないため、ログイン状態の期限が切れたら貼り直せば済みます。", @@ -20,6 +21,7 @@ "Command Code": "Command Code", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT(ドット区切り 3 パート。Bearer 行全体や data.bin の中身を貼っても可)", "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT(3 つのドット区切り部分。chatglm_token=… の行をそのまま貼っても可)", + "LobsterAI": "LobsterAI", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode 国際版", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "NVIDIA NIM のマネージドエンドポイント。Key は integrate.api.nvidia.com で申請します", @@ -97,6 +99,7 @@ "可选(cloudaicompanionProject):留空由网关自动发现": "任意(cloudaicompanionProject):空欄の場合はゲートウェイが自動検出します", "可选,展示用": "任意、表示用", "可选,没有则无法自动续期": "任意、ないと自動更新できません", + "可选,用于生成账号标识": "任意。アカウント識別子の生成に使用", "可选,留空使用凭据里的 user_name": "任意、空欄の場合は認証情報の user_name を使用", "可选,留空使用账号姓名、邮箱或令牌指纹": "任意、空欄の場合はアカウントの氏名、メールアドレス、またはトークンフィンガープリントを使用", "可选,留空使用账号昵称或邮箱": "任意、空欄の場合はアカウントのニックネームまたはメールアドレスを使用", @@ -112,6 +115,7 @@ "可选,留空则用账号昵称": "任意。空欄ならアカウントのニックネームを使用", "可选,留空则用账号昵称或 userId": "任意。空欄の場合はアカウントのニックネームまたは userId を使用", "可选,留空则用邮箱或令牌尾号": "任意、空欄の場合はメールアドレスまたはトークンの末尾を使用", + "可选,留空用「AtomCode」": "任意。空欄なら「AtomCode」", "可选,留空用「OfficeAce 果办」": "任意、空欄の場合は「OfficeAce」を使用", "可选,留空自动生成": "任意、空欄の場合は自動生成", "可选,留空自动用昵称": "任意、空欄の場合はニックネームを自動使用", @@ -145,6 +149,7 @@ "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "国際版の推論サイトは api.z.ai で、中国版とは別のサイトです;プランもそれぞれ独立しています。", "国际版(WorkBuddy AI)": "国際版(WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "国際版サイトの Coding プラン;OpenAI 互換アドレスは https://api.minimax.io/v1", + "在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。": "AtomGit の AtomCode ページから accessToken と refreshToken の 2 つを取得して貼り付けてください。リフレッシュトークンの入力推奨 —— このプロバイダーのトークンは JWT ではなく(約 7 日で失効し、失効時刻も読み取れません)、これが無いと自動更新できず、失効後は再ログインが必要になります。", "在 cc-switch 里找到": "cc-switch で", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "内蔵ウィンドウで公式ログインを完了してください。成功すると自動でアカウント一覧に追加されます。", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "{label} の Qoder アカウント設定 → Integrations で PAT を生成してください(Google / GitHub のトークンは入力しないでください)。2 つの地域のアカウントと認証情報は共用できません。", @@ -177,6 +182,7 @@ "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "システムの既定ブラウザで認可ページを開きます(ブラウザでログイン済みの Huawei Cloud アカウントが再利用されます);認可コードはゲートウェイがクラウドをポーリングして取得するため、ブラウザがどのマシンで動いていても関係ありません。完了後は自動でアカウント一覧に追加されます", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "システムの既定ブラウザで認可ページを開きます(ブラウザでログイン済みの Huawei Cloud アカウントが再利用されます);ブラウザとゲートウェイが別のマシンにある場合は、上の説明に従って最終的なコールバックアドレスをパネルに貼り付けてください", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "システムの既定ブラウザでログインページを開きます(ブラウザでログイン済みの Accio アカウントが再利用されます);ログイン完了後は自動でアカウント一覧に追加され、ポップアップを閉じると待機をキャンセルします", + "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "システムの既定ブラウザでログインページを開きます(ブラウザでログイン済みの LobsterAI アカウントが再利用されます);ログイン完了後は自動でアカウント一覧に追加され、ポップアップを閉じると待機をキャンセルします", "小米 MiMo Token Plan 套餐(新加坡站)": "小米 MiMo Token Plan プラン(シンガポールサイト)", "已到账": "付与済み", "已取消登录等待": "ログイン待ちをキャンセルしました", @@ -198,13 +204,16 @@ "恢复 Google / GitHub 入口": "Google / GitHub の入り口を復元", "手机号": "電話番号", "手机验证码登录": "SMS 認証コードログイン", + "打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。": "公式の LobsterAI ログインページ(lobsterai.youdao.com)を開き、LobsterAI アカウントでログインしてください:ログインに成功すると公式ページが本機に戻り、自動でアカウント一覧に追加されます —— コールバックはゲートウェイ自身のポートに届くため、ブラウザはゲートウェイと同じマシンで開く必要があります。認証コードは本機内でゲートウェイに渡されるだけで、画面に平文トークンは表示されません。", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Trae SOLO の公式認可ページ(trae.cn)を開き、あなたの Trae アカウントでログインしてください:認可が完了すると公式ページから本機の一時ポートにリダイレクトされます;同一マシンでのデプロイでは自動受信し、Docker / リモートパネルでは案内に従ってアドレスバーのコールバックアドレスをコピーしてください。", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Accio {label}の公式ログインページ({site})を開き、あなたの Accio アカウントでログインしてください:ログインに成功すると公式ページから本機にリダイレクトされます;同一マシンでは自動完了し、Docker / リモートパネルでは案内に従ってコールバックアドレスを貼り付けてください(認可コードは本機からゲートウェイに渡されるだけで、画面に平文 token は表示されません)。", "打开 Accio {label}登录页": "Accio {label} のログインページを開く", "打开 Antigravity 网页登录": "Antigravity のウェブログインを開く", + "打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。": "AtomGit の認可ページを開いてログインしてください。完了後、ゲートウェイが自動的にブローカーをポーリングしてトークンを取得し、アカウントを追加します(コールバックはブローカー側で終端され本機には戻らないため、コールバック URL の手入力は不要です)。", "打开 CatPaw 网页登录": "CatPaw のウェブログインを開く", "打开 Cline 授权页": "Cline の認可ページを開く", "打开 CodeArts 授权页": "CodeArts の認可ページを開く", + "打开 LobsterAI 登录页": "LobsterAI のログインページを開く", "打开 Qoder 网页登录": "Qoder のウェブログインを開く", "打开 Trae 授权页": "Trae の認可ページを開く", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "ZCode {label}の公式認可ページを開き、あなたのアカウントでログインしてください。認可結果は ZCode サーバー側で記録され、ゲートウェイがバックグラウンドで自動的に認証情報を取得してアカウント一覧に追加します。", @@ -279,11 +288,13 @@ "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "ログイン状態ファイルのパス:~/.box-agent/config/auth.json(Windows:C:\\Users\\<ユーザー名>\\.box-agent\\config\\auth.json)", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "カスタムプロバイダー「{name}」を削除しますか?このプロバイダー配下の {count} 個のアカウントも同時に削除され、削除後は元に戻せません。", "等待 Accio {label}登录完成…": "Accio {label} のログイン完了を待っています…", + "等待 AtomCode 登录完成…": "AtomCode のログイン完了を待機中…", "等待 CatPaw 登录完成…": "CatPaw のログイン完了を待っています…", "等待 Cline 授权确认…": "Cline の認可確認を待っています…", "等待 CodeArts 登录完成…": "CodeArts のログイン完了を待っています…", "等待 Google 授权完成…": "Google の認可完了を待っています…", "等待 KukuAI 登录完成…": "KukuAI のログイン完了を待っています…", + "等待 LobsterAI 登录完成…": "LobsterAI のログイン完了を待っています…", "等待 OfficeAce 登录完成…": "OfficeAce のログイン完了を待っています…", "等待 OrcaTerm 登录完成…": "OrcaTerm のログイン完了を待っています…", "等待 Qoder 授权完成…": "Qoder の認可完了を待っています…", @@ -305,6 +316,7 @@ "粘贴 session cookie": "session cookie を貼り付け", "粘贴 token / refreshToken": "token / refreshToken を貼り付け", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "workos:… で始まるトークンを貼り付け(プレフィックスがなくても自動で補われます)", + "粘贴令牌对": "トークンペアを貼り付け", "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "智谱清言のアクセストークン(3 つのドット区切り部分からなる JWT)を貼り付けてください。取得方法:ブラウザで chatglm.cn を開いてログインし、開発者ツールの Application → Cookies から chatglm_token の値をコピーします(chatglm_token=…; の行をそのまま貼っても可)。貼り付けたあと上流に対して一度検証し、通った場合のみアカウントを保存します。ログイン済みアカウントではポイントと签到の機能が使え、以降はゲートウェイが自動で更新します(上流に更新インターフェースがあります)。", "粘贴登录凭据": "ログイン認証情報を貼り付け", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "貼り付けるのはログイン session です(同時にモデルゲートウェイの token でもあります)。Loomy には更新用 API がなく、session の有効期間は約 14 日です。期限切れ後は再ログインしてください。", @@ -319,6 +331,7 @@ "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "システムブラウザで開く(ログイン済みの Meituan アカウントを再利用);完了後は自動でリストに追加されます", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "システムブラウザで開く(ログイン済みアカウントを再利用);完了後は自動でリストに追加されます", "系统默认浏览器": "システムの既定ブラウザ", + "续期令牌": "リフレッシュトークン", "编码套餐 API Key": "コーディングプラン API Key", "网关 Secret": "ゲートウェイシークレット", "网页登录": "ウェブログイン", @@ -331,6 +344,7 @@ "获取验证码": "コードを取得", "访客模式": "ゲストモード", "访客模式(免登录)": "ゲストモード(ログイン不要)", + "访问令牌": "アクセストークン", "该上游无需鉴权(不发送鉴权头)": "このアップストリームは認証不要(認証ヘッダーを送信しません)", "该提供商": "このプロバイダー", "该提供商账号添加功能即将上线": "このプロバイダーのアカウント追加機能は近日公開予定です", diff --git a/desktop-tauri/i18n/i18n-src/ja/checkin.json b/desktop-tauri/i18n/i18n-src/ja/checkin.json index f52646fc..6caf346f 100644 --- a/desktop-tauri/i18n/i18n-src/ja/checkin.json +++ b/desktop-tauri/i18n/i18n-src/ja/checkin.json @@ -153,6 +153,7 @@ "每天 {time} · {scope}": "毎日 {time} · {scope}", "每天时刻": "毎日の時刻", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "チェックイン1回ごとに · アカウントごとに1行 · 結果と収益はバックエンドの記録が正", + "每日活动签到(+100 积分,已领则中性提示)": "毎日のアクティビティ签到(+100 クレジット、受領済みなら中立の案内)", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "毎日のアクティブタスク · Webチャネルの会話で日活を達成、ポイントは受け取りで加算 · 両方は別々に実行可能", "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "毎日ログインポイント · あわせて会員アクティビティを受け取り · ゲストアカウントはポイント体系なし(スキップ)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "デイリー特典 · 受け取り → 確認 → 読み戻し検証 · 北京時間の自然日でリセット", @@ -232,6 +233,7 @@ "领取": "受け取る", "领取中": "受け取り中", "领取中…": "受け取り中…", + "领取免费 CodingPlan(按需:套餐有效时只回读额度)": "無料 CodingPlan を取得(オンデマンド:プラン有効時はクォータを読むだけ)", "领取失败": "受け取りに失敗", "领取失败:{error}": "受け取りに失敗しました:{error}", "(共 {n} 个账号)": "(全 {n} アカウント)", diff --git a/desktop-tauri/i18n/i18n-src/ja/requests.json b/desktop-tauri/i18n/i18n-src/ja/requests.json index 16860b22..dfed0be7 100644 --- a/desktop-tauri/i18n/i18n-src/ja/requests.json +++ b/desktop-tauri/i18n/i18n-src/ja/requests.json @@ -66,6 +66,8 @@ "失败({status}):{error}": "失敗({status}):{error}", "失败:{error}": "失敗:{error}", "实际发给上游 {bytes} 字节": "実際にアップストリームへ送信した {bytes} バイト", + "已下发": "配信済み", + "实际下发给客户端 {bytes} 字节": "クライアントに実際に配信した {bytes} バイト", "客户端": "クライアント", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "{count} 件のログを削除 · うち {raw} 件はペイロード原文あり · DB サイズ {db}", "尝试 {n} ·": "試行 {n} ·", diff --git a/desktop-tauri/i18n/i18n-src/ko/add-account.json b/desktop-tauri/i18n/i18n-src/ko/add-account.json index 8136ce9e..cfb7c9e8 100644 --- a/desktop-tauri/i18n/i18n-src/ko/add-account.json +++ b/desktop-tauri/i18n/i18n-src/ko/add-account.json @@ -10,6 +10,7 @@ "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "Anthropic 호환 엔드포인트는 https://api.deepseek.com/anthropic입니다 (프로토콜을 anthropic으로 바꿀 때 입력합니다)", "Anthropic 协议填根地址,不要带 /v1": "Anthropic 프로토콜은 루트 주소를 입력하고 /v1은 붙이지 마세요", "Antigravity": "Antigravity", + "AtomCode": "AtomCode", "AutoClaw 国内版": "AutoClaw 중국판", "AutoClaw 国际版": "AutoClaw 국제판", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS는 필수입니다; **전체 Cookie**를 붙여넣으세요 (Cookie 편집기에서 원클릭 내보내기 가능) — 이 중 **PTOKEN은 반드시 남겨 두세요**. 게이트웨이가 이것을 사용해 Baidu에서 KukuAI 비즈니스 세션 토큰을 교환 발급받으며, 없으면 모델 갱신 시 「미로그인」 오류가 발생합니다. 전체 Cookie 헤더, Cookie 편집기에서 내보낸 JSON 배열, 또는 {BDUSS, STOKEN} 객체를 지원합니다. KukuAI에는 갱신 메커니즘이 없으므로 로그인 상태가 만료되면 다시 붙여넣으면 됩니다.", @@ -20,6 +21,7 @@ "Command Code": "Command Code", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT(점 3개 구분. Bearer 전체 줄 또는 data.bin 내용을 붙여넣어도 됨)", "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT(점 3개로 구분된 형식. chatglm_token=… 줄을 그대로 붙여도 됩니다)", + "LobsterAI": "LobsterAI", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode 국제판", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "NVIDIA NIM 관리형 엔드포인트, Key는 integrate.api.nvidia.com에서 신청합니다", @@ -97,6 +99,7 @@ "可选(cloudaicompanionProject):留空由网关自动发现": "선택 (cloudaicompanionProject): 비워 두면 게이트웨이가 자동으로 찾습니다", "可选,展示用": "선택, 표시용", "可选,没有则无法自动续期": "선택, 없으면 자동 갱신 불가", + "可选,用于生成账号标识": "선택 사항이며 계정 식별자 생성에 사용", "可选,留空使用凭据里的 user_name": "선택, 비워 두면 자격 증명의 user_name 사용", "可选,留空使用账号姓名、邮箱或令牌指纹": "선택, 비워 두면 계정 이름, 이메일 또는 토큰 지문 사용", "可选,留空使用账号昵称或邮箱": "선택, 비워 두면 계정 닉네임 또는 이메일 사용", @@ -112,6 +115,7 @@ "可选,留空则用账号昵称": "선택 사항. 비워 두면 계정 닉네임을 사용합니다", "可选,留空则用账号昵称或 userId": "선택 사항. 비워 두면 계정 닉네임 또는 userId 사용", "可选,留空则用邮箱或令牌尾号": "선택, 비워 두면 이메일 또는 토큰 끝자리 사용", + "可选,留空用「AtomCode」": "선택 사항이며 비워 두면 “AtomCode”", "可选,留空用「OfficeAce 果办」": "선택, 비워 두면 「OfficeAce」 사용", "可选,留空自动生成": "선택, 비워 두면 자동 생성", "可选,留空自动用昵称": "선택, 비워 두면 닉네임 자동 사용", @@ -145,6 +149,7 @@ "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "국제판의 추론 사이트는 api.z.ai이며 중국판과 다른 사이트입니다; 플랜도 각각 독립적입니다.", "国际版(WorkBuddy AI)": "국제판 (WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "국제 사이트 Coding 플랜; OpenAI 호환 주소는 https://api.minimax.io/v1입니다", + "在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。": "AtomGit의 AtomCode 페이지에서 accessToken과 refreshToken 두 값을 가져와 붙여넣으세요. 리프레시 토큰 입력을 권장합니다 —— 이 제공자의 토큰은 JWT가 아니며(약 7일 후 만료되고 만료 시각을 읽을 수 없습니다), 없으면 자동 갱신이 불가능해 만료 후 다시 로그인해야 합니다.", "在 cc-switch 里找到": "cc-switch에서 찾은", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "내장 창에서 공식 로그인을 완료하면 성공 시 자동으로 계정 목록에 추가됩니다.", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "{label}의 Qoder 계정 설정 → Integrations에서 PAT를 생성하세요 (Google / GitHub 토큰을 입력하지 마세요). 두 지역의 계정과 자격 증명은 호환되지 않습니다.", @@ -177,6 +182,7 @@ "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "시스템 기본 브라우저로 인가 페이지를 엽니다 (브라우저에 로그인된 Huawei Cloud 계정을 재사용합니다); 인가 코드는 게이트웨이가 클라우드를 폴링해 가져오므로 브라우저가 어느 컴퓨터에서 실행되는지는 상관없습니다. 완료하면 자동으로 계정 목록에 추가됩니다", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "시스템 기본 브라우저로 인가 페이지를 엽니다 (브라우저에 로그인된 Huawei Cloud 계정을 재사용합니다); 브라우저와 게이트웨이가 같은 컴퓨터에 있지 않다면 위 설명대로 최종 콜백 주소를 패널에 다시 붙여넣으세요", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "시스템 기본 브라우저로 로그인 페이지를 엽니다 (브라우저에 로그인된 Accio 계정을 재사용합니다); 로그인을 완료하면 자동으로 계정 목록에 추가되며, 팝업을 닫으면 대기가 취소됩니다", + "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "시스템 기본 브라우저로 로그인 페이지를 엽니다 (브라우저에 로그인된 LobsterAI 계정을 재사용합니다); 로그인을 완료하면 자동으로 계정 목록에 추가되며, 팝업을 닫으면 대기가 취소됩니다", "小米 MiMo Token Plan 套餐(新加坡站)": "小米 MiMo Token Plan 플랜 (싱가포르 사이트)", "已到账": "적립 완료", "已取消登录等待": "로그인 대기를 취소했습니다", @@ -198,13 +204,16 @@ "恢复 Google / GitHub 入口": "Google / GitHub 항목 복원", "手机号": "휴대폰 번호", "手机验证码登录": "SMS 인증 코드 로그인", + "打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。": "공식 LobsterAI 로그인 페이지(lobsterai.youdao.com)를 열고 LobsterAI 계정으로 로그인하세요: 로그인이 완료되면 공식 페이지가 이 컴퓨터로 되돌아오고 계정이 자동으로 목록에 추가됩니다 —— 콜백은 게이트웨이 자체 포트로 들어오므로 브라우저는 게이트웨이와 같은 컴퓨터에서 열어야 합니다. 인증 코드는 이 컴퓨터에서만 게이트웨이로 전달되며 화면에 평문 토큰은 표시되지 않습니다.", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Trae SOLO의 공식 인가 페이지(trae.cn)를 열고 Trae 계정으로 로그인하세요: 인가가 완료되면 공식 페이지가 이 컴퓨터의 임시 포트로 되돌아옵니다; 같은 컴퓨터 배포에서는 자동으로 수신하며, Docker / 원격 패널에서는 안내에 따라 주소 표시줄의 콜백 주소를 복사하세요.", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Accio {label}의 공식 로그인 페이지({site})를 열고 Accio 계정으로 로그인하세요: 로그인에 성공하면 공식 페이지가 이 컴퓨터로 되돌아옵니다; 같은 컴퓨터에서는 자동으로 완료되며, Docker / 원격 패널에서는 안내에 따라 콜백 주소를 붙여넣으면 됩니다 (인가 코드는 이 컴퓨터에서만 게이트웨이에 전달되며, 화면에 평문 token이 표시되지 않습니다).", "打开 Accio {label}登录页": "Accio {label} 로그인 페이지 열기", "打开 Antigravity 网页登录": "Antigravity 웹 로그인 열기", + "打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。": "AtomGit 승인 페이지를 열고 로그인하세요. 완료되면 게이트웨이가 자동으로 브로커를 폴링해 토큰을 가져오고 계정을 추가합니다(콜백은 브로커에서 종료되며 이 기기로 돌아오지 않으므로 콜백 URL을 직접 입력할 필요가 없습니다).", "打开 CatPaw 网页登录": "CatPaw 웹 로그인 열기", "打开 Cline 授权页": "Cline 인가 페이지 열기", "打开 CodeArts 授权页": "CodeArts 인가 페이지 열기", + "打开 LobsterAI 登录页": "LobsterAI 로그인 페이지 열기", "打开 Qoder 网页登录": "Qoder 웹 로그인 열기", "打开 Trae 授权页": "Trae 인가 페이지 열기", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "ZCode {label}의 공식 인가 페이지를 열고 계정으로 로그인하세요. 인가 결과는 ZCode 서버에 기록되며, 게이트웨이가 백그라운드에서 자격 증명을 자동으로 가져와 계정 목록에 추가합니다.", @@ -279,11 +288,13 @@ "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "로그인 상태 파일 경로: ~/.box-agent/config/auth.json (Windows: C:\\Users\\<사용자 이름>\\.box-agent\\config\\auth.json)", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "커스텀 제공자 「{name}」을(를) 삭제할까요? 이 제공자 아래의 계정 {count}개도 함께 삭제되며, 삭제 후에는 복구할 수 없습니다.", "等待 Accio {label}登录完成…": "Accio {label} 로그인 대기 중…", + "等待 AtomCode 登录完成…": "AtomCode 로그인 완료 대기 중…", "等待 CatPaw 登录完成…": "CatPaw 로그인 대기 중…", "等待 Cline 授权确认…": "Cline 인가 확인 대기 중…", "等待 CodeArts 登录完成…": "CodeArts 로그인 대기 중…", "等待 Google 授权完成…": "Google 인가 대기 중…", "等待 KukuAI 登录完成…": "KukuAI 로그인 대기 중…", + "等待 LobsterAI 登录完成…": "LobsterAI 로그인 대기 중…", "等待 OfficeAce 登录完成…": "OfficeAce 로그인 대기 중…", "等待 OrcaTerm 登录完成…": "OrcaTerm 로그인 대기 중…", "等待 Qoder 授权完成…": "Qoder 인가 대기 중…", @@ -305,6 +316,7 @@ "粘贴 session cookie": "session cookie 붙여넣기", "粘贴 token / refreshToken": "token / refreshToken 붙여넣기", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "workos:…로 시작하는 토큰 붙여넣기 (접두사가 없어도 자동으로 붙습니다)", + "粘贴令牌对": "토큰 쌍 붙여넣기", "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "智谱清言 액세스 토큰(점 3개로 구분된 JWT)을 붙여넣으세요. 발급 방법: 브라우저에서 chatglm.cn을 열고 로그인한 뒤 개발자 도구의 Application → Cookies에서 chatglm_token 값을 복사합니다(chatglm_token=…; 줄을 그대로 붙여도 됩니다). 붙여넣은 뒤 업스트림에 한 번 검증하며, 통과한 경우에만 계정을 저장합니다. 로그인된 계정은 포인트와 签到 기능을 쓸 수 있고, 이후 게이트웨이가 자동으로 갱신합니다(업스트림에 갱신 인터페이스가 있습니다).", "粘贴登录凭据": "로그인 자격 증명 붙여넣기", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "붙여넣는 것은 로그인 session입니다 (동시에 모델 게이트웨이의 token입니다). Loomy에는 갱신 인터페이스가 없으며 session 유효 기간은 약 14일이므로, 만료되면 다시 로그인하면 됩니다.", @@ -319,6 +331,7 @@ "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "시스템 브라우저로 엽니다 (로그인된 Meituan 계정 재사용); 완료하면 자동으로 목록에 추가됩니다", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "시스템 브라우저로 엽니다 (로그인된 계정 재사용); 완료하면 자동으로 목록에 추가됩니다", "系统默认浏览器": "시스템 기본 브라우저", + "续期令牌": "리프레시 토큰", "编码套餐 API Key": "코딩 플랜 API Key", "网关 Secret": "게이트웨이 시크릿", "网页登录": "웹 로그인", @@ -331,6 +344,7 @@ "获取验证码": "코드 받기", "访客模式": "게스트 모드", "访客模式(免登录)": "게스트 모드(로그인 불필요)", + "访问令牌": "액세스 토큰", "该上游无需鉴权(不发送鉴权头)": "이 업스트림은 인증이 필요하지 않음 (인증 헤더를 보내지 않음)", "该提供商": "이 제공자", "该提供商账号添加功能即将上线": "이 제공자의 계정 추가 기능이 곧 출시됩니다", diff --git a/desktop-tauri/i18n/i18n-src/ko/checkin.json b/desktop-tauri/i18n/i18n-src/ko/checkin.json index 8a2bd96a..a8acfcdc 100644 --- a/desktop-tauri/i18n/i18n-src/ko/checkin.json +++ b/desktop-tauri/i18n/i18n-src/ko/checkin.json @@ -153,6 +153,7 @@ "每天 {time} · {scope}": "매일 {time} · {scope}", "每天时刻": "매일 시각", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "출석 실행마다 · 계정별 한 행 · 결과와 수익은 백엔드 기록 기준", + "每日活动签到(+100 积分,已领则中性提示)": "일일 활동 체크인(+100 크레딧, 이미 받았으면 중립 안내)", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "매일 활성 작업 · 웹 채널 세션으로 일일 활성 달성, 포인트는 수령 시 적립 · 두 가지는 따로 실행 가능", "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "매일 로그인 포인트 · 회원 활동 함께 수령 · 게스트 계정은 포인트 체계 없음(건너뜀)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "일일 혜택 · 수령 → 확인 → 재조회 검증 · 베이징 시간 자연일 기준 초기화", @@ -232,6 +233,7 @@ "领取": "수령", "领取中": "수령 중", "领取中…": "수령 중…", + "领取免费 CodingPlan(按需:套餐有效时只回读额度)": "무료 CodingPlan 받기(필요 시에만: 플랜이 유효하면 할당량만 조회)", "领取失败": "수령 실패", "领取失败:{error}": "수령 실패: {error}", "(共 {n} 个账号)": "(전체 {n}개 계정)", diff --git a/desktop-tauri/i18n/i18n-src/ko/requests.json b/desktop-tauri/i18n/i18n-src/ko/requests.json index 8850a0da..ccfa1001 100644 --- a/desktop-tauri/i18n/i18n-src/ko/requests.json +++ b/desktop-tauri/i18n/i18n-src/ko/requests.json @@ -66,6 +66,8 @@ "失败({status}):{error}": "실패({status}): {error}", "失败:{error}": "실패: {error}", "实际发给上游 {bytes} 字节": "업스트림에 실제 전송한 {bytes} 바이트", + "已下发": "전송됨", + "实际下发给客户端 {bytes} 字节": "클라이언트에 실제로 전송한 {bytes} 바이트", "客户端": "클라이언트", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "{count}건의 로그를 삭제합니다 · 그중 {raw}건은 페이로드 원문이 남아 있음 · DB 크기 {db}", "尝试 {n} ·": "시도 {n} ·", diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/add-account.json b/desktop-tauri/i18n/i18n-src/pt-BR/add-account.json index 0c3064b4..31a54bf4 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/add-account.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/add-account.json @@ -10,6 +10,7 @@ "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "O endpoint compatível com Anthropic é https://api.deepseek.com/anthropic (preencha-o ao trocar o protocolo para anthropic)", "Anthropic 协议填根地址,不要带 /v1": "No protocolo Anthropic, preencha o endereço raiz, sem /v1", "Antigravity": "Antigravity", + "AtomCode": "AtomCode", "AutoClaw 国内版": "AutoClaw edição da China", "AutoClaw 国际版": "AutoClaw edição internacional", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS é obrigatório; cole o **Cookie completo** (um editor de Cookie exporta com um clique) — o **PTOKEN deve ser mantido**, pois o gateway o usa para trocar com o Baidu por um token de sessão de negócio do KukuAI; sem ele, a atualização de modelos reporta \"não conectado\". São aceitos o cabeçalho Cookie inteiro, um array JSON exportado por um editor de Cookie, ou um objeto {BDUSS, STOKEN}. O KukuAI não tem mecanismo de atualização; quando a sessão de login expirar, basta colar de novo.", @@ -20,6 +21,7 @@ "Command Code": "Command Code", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT (três partes separadas por pontos; também pode colar a linha Bearer inteira ou o conteúdo do data.bin)", "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT (três partes separadas por ponto; colar a linha chatglm_token=… inteira também funciona)", + "LobsterAI": "LobsterAI", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode edição internacional", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "Endpoint gerenciado do NVIDIA NIM; solicite a Key em integrate.api.nvidia.com", @@ -97,6 +99,7 @@ "可选(cloudaicompanionProject):留空由网关自动发现": "Opcional (cloudaicompanionProject); se vazio, o gateway descobre automaticamente", "可选,展示用": "Opcional, para exibição", "可选,没有则无法自动续期": "Opcional; sem ele, não é possível renovar automaticamente", + "可选,用于生成账号标识": "Opcional; usado para gerar o identificador da conta", "可选,留空使用凭据里的 user_name": "Opcional; se vazio, usa o user_name da credencial", "可选,留空使用账号姓名、邮箱或令牌指纹": "Opcional; se vazio, usa o nome, o e-mail ou a impressão digital do token", "可选,留空使用账号昵称或邮箱": "Opcional; se vazio, usa o apelido ou o e-mail da conta", @@ -112,6 +115,7 @@ "可选,留空则用账号昵称": "Opcional; em branco usa o apelido da conta", "可选,留空则用账号昵称或 userId": "Opcional; deixe em branco para usar o apelido da conta ou o userId", "可选,留空则用邮箱或令牌尾号": "Opcional; se vazio, usa o e-mail ou o final do token", + "可选,留空用「AtomCode」": "Opcional; padrão “AtomCode”", "可选,留空用「OfficeAce 果办」": "Opcional; se vazio, usa “OfficeAce”", "可选,留空自动生成": "Opcional; se vazio, gerado automaticamente", "可选,留空自动用昵称": "Opcional; se vazio, usa o apelido", @@ -145,6 +149,7 @@ "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "O site de inferência da edição internacional é api.z.ai, diferente do site da edição da China; os planos também são independentes.", "国际版(WorkBuddy AI)": "Edição internacional (WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "Plano Coding do site internacional; o endereço compatível com OpenAI é https://api.minimax.io/v1", + "在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。": "Obtenha os dois valores na página do AtomCode no AtomGit: accessToken e refreshToken. O refresh token é fortemente recomendado — o token deste provedor não é um JWT (expira em cerca de 7 dias e não traz expiração legível); sem ele a conta não consegue se renovar e precisará ser autorizada de novo ao expirar.", "在 cc-switch 里找到": "Encontradas", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "Conclua o login oficial na janela embutida; em caso de sucesso, a conta entra automaticamente na lista de contas.", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "Gere um PAT nas configurações da conta Qoder do {label} → Integrations (não use um token do Google / GitHub). Contas e credenciais não são compartilhadas entre as duas regiões.", @@ -177,6 +182,7 @@ "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "A página de autorização será aberta no navegador padrão do sistema (reaproveitando a conta Huawei Cloud já conectada nele); o gateway consulta a nuvem em polling para obter o código de autorização, então não importa em qual máquina o navegador roda. Ao concluir, a conta entra automaticamente na lista", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "A página de autorização será aberta no navegador padrão do sistema (reaproveitando a conta Huawei Cloud já conectada nele); se o navegador e o gateway não estiverem na mesma máquina, cole o endereço de callback final de volta no painel conforme as instruções acima", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "A página de login será aberta no navegador padrão do sistema (reaproveitando a conta Accio já conectada nele); após o login, a conta entra automaticamente na lista; fechar a janela cancela a espera", + "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "A página de login será aberta no navegador padrão do sistema (reaproveitando a conta LobsterAI já conectada nele); após o login, a conta entra automaticamente na lista; fechar a janela cancela a espera", "小米 MiMo Token Plan 套餐(新加坡站)": "Plano Token Plan do Xiaomi MiMo (site de Singapura)", "已到账": "Creditado", "已取消登录等待": "Espera de login cancelada", @@ -198,13 +204,16 @@ "恢复 Google / GitHub 入口": "Restaurar acessos Google / GitHub", "手机号": "Celular", "手机验证码登录": "Login com código SMS", + "打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。": "Abra a página de login oficial do LobsterAI (lobsterai.youdao.com) e entre com sua conta LobsterAI: após o login, a página oficial retorna a esta máquina e a conta entra automaticamente na lista — o callback chega à porta do próprio gateway, portanto o navegador precisa estar na mesma máquina que o gateway; o código de autorização só é entregue ao gateway localmente e nenhum token em texto claro é exibido na interface.", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Abra a página de autorização oficial do Trae SOLO (trae.cn) e entre com sua conta Trae: ao concluir a autorização, a página oficial redireciona de volta para uma porta temporária desta máquina; implantações na mesma máquina recebem automaticamente, e no Docker / painel remoto copie o endereço de callback da barra de endereços conforme as instruções.", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Abra a página de login oficial do Accio {label} ({site}) e entre com sua conta Accio: após o login, a página oficial redireciona de volta para esta máquina; na mesma máquina tudo se completa automaticamente, e no Docker / painel remoto basta colar o endereço de callback conforme as instruções (o código de autorização é passado ao gateway apenas nesta máquina, e nenhum token em texto claro é exibido na interface).", "打开 Accio {label}登录页": "Abrir a página de login do Accio {label}", "打开 Antigravity 网页登录": "Abrir login web do Antigravity", + "打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。": "Abra a página de autorização do AtomGit e entre: ao concluir, o gateway consulta o broker automaticamente para obter o token e adiciona a conta (o callback é encerrado no broker e nunca volta para esta máquina, então não é preciso preencher a URL de callback).", "打开 CatPaw 网页登录": "Abrir o login web do CatPaw", "打开 Cline 授权页": "Abrir a página de autorização do Cline", "打开 CodeArts 授权页": "Abrir a página de autorização do CodeArts", + "打开 LobsterAI 登录页": "Abrir a página de login do LobsterAI", "打开 Qoder 网页登录": "Abrir o login web do Qoder", "打开 Trae 授权页": "Abrir a página de autorização do Trae", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "Abra a página de autorização oficial do ZCode {label} e entre com sua conta. O resultado da autorização é registrado pelo servidor do ZCode; o gateway busca a credencial em segundo plano e a adiciona à lista de contas.", @@ -279,11 +288,13 @@ "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "Caminho do arquivo de sessão de login: ~/.box-agent/config/auth.json (Windows: C:\\Users\\\\.box-agent\\config\\auth.json)", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "Excluir o provedor personalizado \"{name}\"? Isso também excluirá {count} contas desse provedor; a exclusão não pode ser desfeita.", "等待 Accio {label}登录完成…": "Aguardando a conclusão do login do Accio {label}…", + "等待 AtomCode 登录完成…": "Aguardando o login do AtomCode…", "等待 CatPaw 登录完成…": "Aguardando a conclusão do login do CatPaw…", "等待 Cline 授权确认…": "Aguardando a confirmação de autorização do Cline…", "等待 CodeArts 登录完成…": "Aguardando a conclusão do login do CodeArts…", "等待 Google 授权完成…": "Aguardando a conclusão da autorização do Google…", "等待 KukuAI 登录完成…": "Aguardando a conclusão do login do KukuAI…", + "等待 LobsterAI 登录完成…": "Aguardando a conclusão do login do LobsterAI…", "等待 OfficeAce 登录完成…": "Aguardando a conclusão do login do OfficeAce…", "等待 OrcaTerm 登录完成…": "Aguardando o login no OrcaTerm…", "等待 Qoder 授权完成…": "Aguardando a conclusão da autorização do Qoder…", @@ -305,6 +316,7 @@ "粘贴 session cookie": "Colar session cookie", "粘贴 token / refreshToken": "Colar token / refreshToken", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "Cole um token começando com workos:… (o prefixo é adicionado automaticamente se faltar)", + "粘贴令牌对": "Colar par de tokens", "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "Cole um token de acesso do Zhipu Qingyan (um JWT com três partes separadas por ponto). Como obter: abra chatglm.cn no navegador, faça login e copie o valor do cookie chatglm_token em Application → Cookies nas ferramentas de desenvolvedor (colar a linha chatglm_token=…; inteira também funciona). O token é verificado uma vez na origem após a colagem, e a conta só é salva se passar; contas conectadas têm pontos e check-in, e o gateway renova automaticamente depois (a origem tem API de renovação).", "粘贴登录凭据": "Colar credenciais de login", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "O que você cola é a sessão de login (que também é o token do gateway de modelos). O Loomy não tem API de renovação; a sessão vale cerca de 14 dias, então basta entrar de novo após expirar.", @@ -319,6 +331,7 @@ "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "Abre no navegador do sistema (reaproveita a conta Meituan já conectada); ao concluir, entra automaticamente na lista", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "Abre no navegador do sistema (reaproveita a conta já conectada); ao concluir, entra automaticamente na lista", "系统默认浏览器": "Navegador padrão do sistema", + "续期令牌": "Token de atualização", "编码套餐 API Key": "API Key do Coding Plan", "网关 Secret": "Segredo do gateway", "网页登录": "Login web", @@ -331,6 +344,7 @@ "获取验证码": "Obter código", "访客模式": "Modo visitante", "访客模式(免登录)": "Modo visitante (sem login)", + "访问令牌": "Token de acesso", "该上游无需鉴权(不发送鉴权头)": "Este upstream não exige autenticação (nenhum cabeçalho de autenticação é enviado)", "该提供商": "Este provedor", "该提供商账号添加功能即将上线": "A adição de contas deste provedor chega em breve", diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/checkin.json b/desktop-tauri/i18n/i18n-src/pt-BR/checkin.json index f46a9c8a..413cb6c3 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/checkin.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/checkin.json @@ -153,6 +153,7 @@ "每天 {time} · {scope}": "Diário {time} · {scope}", "每天时刻": "Horário diário", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "Uma linha por conta a cada execução · resultados e ganhos conforme registrado pelo backend", + "每日活动签到(+100 积分,已领则中性提示)": "Check-in de atividade diária (+100 créditos; aviso neutro se já resgatado)", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "Tarefas de atividade diária · uma conversa no canal web acende a atividade diária, só o resgate adiciona pontos · os dois podem ser executados separadamente", "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "Pontos de login diário · resgata atividades de membro junto · contas de visitante não têm sistema de pontos (ignorado)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "Benefício diário · resgatar → confirmar → reler para verificar · redefinido no dia natural (horário de Pequim)", @@ -232,6 +233,7 @@ "领取": "Resgatar", "领取中": "Resgatando", "领取中…": "Resgatando…", + "领取免费 CodingPlan(按需:套餐有效时只回读额度)": "Resgatar o CodingPlan gratuito (sob demanda: com o plano ativo apenas lê a cota)", "领取失败": "Falha no resgate", "领取失败:{error}": "Falha no resgate: {error}", "(共 {n} 个账号)": "({n} contas no total)", diff --git a/desktop-tauri/i18n/i18n-src/pt-BR/requests.json b/desktop-tauri/i18n/i18n-src/pt-BR/requests.json index 49ee5c8a..6f13e46e 100644 --- a/desktop-tauri/i18n/i18n-src/pt-BR/requests.json +++ b/desktop-tauri/i18n/i18n-src/pt-BR/requests.json @@ -66,6 +66,8 @@ "失败({status}):{error}": "Falhou ({status}): {error}", "失败:{error}": "Falhou: {error}", "实际发给上游 {bytes} 字节": "{bytes} bytes enviados de fato ao upstream", + "已下发": "Enviado", + "实际下发给客户端 {bytes} 字节": "{bytes} bytes realmente entregues ao cliente", "客户端": "Cliente", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "Serão excluídos {count} logs · {raw} ainda com payload · tamanho do banco: {db}", "尝试 {n} ·": "Tentativa {n} ·", diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/add-account.json b/desktop-tauri/i18n/i18n-src/zh-Hant/add-account.json index 5a2b843a..2035c855 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/add-account.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/add-account.json @@ -10,6 +10,7 @@ "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "", "Anthropic 协议填根地址,不要带 /v1": "", "Antigravity": "", + "AtomCode": "AtomCode", "AutoClaw 国内版": "", "AutoClaw 国际版": "", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "", @@ -20,6 +21,7 @@ "Command Code": "", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "", "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "", + "LobsterAI": "LobsterAI", "MonkeyCode": "", "MonkeyCode 国际版": "", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "", @@ -97,6 +99,7 @@ "可选(cloudaicompanionProject):留空由网关自动发现": "", "可选,展示用": "", "可选,没有则无法自动续期": "", + "可选,用于生成账号标识": "選填,用於產生帳號識別碼", "可选,留空使用凭据里的 user_name": "", "可选,留空使用账号姓名、邮箱或令牌指纹": "", "可选,留空使用账号昵称或邮箱": "", @@ -112,6 +115,7 @@ "可选,留空则用账号昵称": "", "可选,留空则用账号昵称或 userId": "", "可选,留空则用邮箱或令牌尾号": "", + "可选,留空用「AtomCode」": "選填,留空用「AtomCode」", "可选,留空用「OfficeAce 果办」": "", "可选,留空自动生成": "", "可选,留空自动用昵称": "", @@ -145,6 +149,7 @@ "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "", "国际版(WorkBuddy AI)": "", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "", + "在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。": "在 AtomGit 的 AtomCode 頁面取得兩項貼進來:accessToken 與 refreshToken。續期權杖建議填寫——本家權杖不是 JWT(約 7 天到期,讀不到到期時間),沒有它帳號無法自動續期,到期只能重新登入。", "在 cc-switch 里找到": "", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "", @@ -177,6 +182,7 @@ "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "", + "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "將用系統預設瀏覽器開啟登入頁(會重用瀏覽器裡已登入的 LobsterAI 帳號);完成登入後自動加入帳號清單,關掉彈窗即取消等待", "小米 MiMo Token Plan 套餐(新加坡站)": "", "已到账": "", "已取消登录等待": "", @@ -198,13 +204,16 @@ "恢复 Google / GitHub 入口": "", "手机号": "", "手机验证码登录": "", + "打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。": "開啟 LobsterAI(有道龍蝦)的官方登入頁(lobsterai.youdao.com)並用你的 LobsterAI 帳號登入:登入成功後官方頁面會跳回本機並自動加入帳號清單 —— 回呼落在閘道自己的連接埠上,所以瀏覽器要與閘道在同一台機器上;授權碼只在本機傳給閘道,介面不顯示明文權杖。", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "", "打开 Accio {label}登录页": "", "打开 Antigravity 网页登录": "", + "打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。": "開啟 AtomGit 授權頁登入:完成後閘道會自動輪詢代理取回權杖並加入帳號清單(授權回呼由代理終結,不會回到本機,因此無需手動回填回呼位址)。", "打开 CatPaw 网页登录": "", "打开 Cline 授权页": "", "打开 CodeArts 授权页": "", + "打开 LobsterAI 登录页": "開啟 LobsterAI 登入頁", "打开 Qoder 网页登录": "", "打开 Trae 授权页": "", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "", @@ -279,11 +288,13 @@ "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "", "等待 Accio {label}登录完成…": "", + "等待 AtomCode 登录完成…": "等待 AtomCode 登入完成…", "等待 CatPaw 登录完成…": "", "等待 Cline 授权确认…": "", "等待 CodeArts 登录完成…": "", "等待 Google 授权完成…": "", "等待 KukuAI 登录完成…": "", + "等待 LobsterAI 登录完成…": "等待 LobsterAI 登入完成…", "等待 OfficeAce 登录完成…": "", "等待 OrcaTerm 登录完成…": "", "等待 Qoder 授权完成…": "", @@ -305,6 +316,7 @@ "粘贴 session cookie": "", "粘贴 token / refreshToken": "", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "", + "粘贴令牌对": "貼上權杖對", "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "", "粘贴登录凭据": "", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "", @@ -319,6 +331,7 @@ "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "", "系统默认浏览器": "", + "续期令牌": "續期權杖", "编码套餐 API Key": "", "网关 Secret": "", "网页登录": "", @@ -331,6 +344,7 @@ "获取验证码": "", "访客模式": "", "访客模式(免登录)": "", + "访问令牌": "存取權杖", "该上游无需鉴权(不发送鉴权头)": "", "该提供商": "", "该提供商账号添加功能即将上线": "", diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/checkin.json b/desktop-tauri/i18n/i18n-src/zh-Hant/checkin.json index 70f49f57..46321751 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/checkin.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/checkin.json @@ -152,6 +152,7 @@ "每天 {time} · {scope}": "", "每天时刻": "", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "", + "每日活动签到(+100 积分,已领则中性提示)": "每日活動簽到(+100 積分,已領則中性提示)", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "", "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "", @@ -227,6 +228,7 @@ "领取": "", "领取中": "", "领取中…": "", + "领取免费 CodingPlan(按需:套餐有效时只回读额度)": "領取免費 CodingPlan(按需:方案有效時只回讀額度)", "领取失败": "", "领取失败:{error}": "", "(共 {n} 个账号)": "", diff --git a/desktop-tauri/i18n/i18n-src/zh-Hant/requests.json b/desktop-tauri/i18n/i18n-src/zh-Hant/requests.json index 4434a318..3d64d7c3 100644 --- a/desktop-tauri/i18n/i18n-src/zh-Hant/requests.json +++ b/desktop-tauri/i18n/i18n-src/zh-Hant/requests.json @@ -66,6 +66,8 @@ "失败({status}):{error}": "", "失败:{error}": "", "实际发给上游 {bytes} 字节": "", + "已下发": "已下發", + "实际下发给客户端 {bytes} 字节": "實際下發給客戶端 {bytes} 位元組", "客户端": "", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "", "尝试 {n} ·": "", diff --git a/desktop-tauri/src-tauri/server/src/server/api/accounts.rs b/desktop-tauri/src-tauri/server/src/server/api/accounts.rs index 02e88742..f96cd9ff 100644 --- a/desktop-tauri/src-tauri/server/src/server/api/accounts.rs +++ b/desktop-tauri/src-tauri/server/src/server/api/accounts.rs @@ -1107,7 +1107,7 @@ pub async fn add_account(state: &ServerState, body: &Bytes) -> Response { // 面板传来的名字是用户自己打的 ⇒ 标 custom(此后续期/重登不改它) store.add_officeace_account(&payload, import_name, true) } - // OrcaTerm(腾讯云遨驰终端桌面客户端):**粘贴 accessToken**(OAuth JWT) +// OrcaTerm(腾讯云遨驰终端桌面客户端):**粘贴 accessToken**(OAuth JWT) // → 本地归一化(剥 `Bearer ` / `ot_session=` 前缀;整段 data.bin JSON // 直接粘也行)→ 打一次 `GetUserInfo` 做**真实校验**(顺带补 userId 与 // 昵称;JWT 里本来就有 userId,这一步同时确认 token 没被上游拒绝)→ @@ -1286,6 +1286,26 @@ pub async fn add_account(state: &ServerState, body: &Bytes) -> Response { }, ) } + // AtomCode(AtomGit / GitCode):粘贴令牌对(accessToken + refreshToken)。 + // **不调上游**(与 CodeArts / OfficeAce 同口径)——令牌是浏览器登录换来的, + // 添加时没有可交换的授权码;目录与连通性由刷新链路验。 + // ⚠️ refreshToken 强烈建议给:没有它账号无法自动续期(本家的令牌不是 JWT, + // 7 天到期后只能重新登录),见 `providers::atomcode::credentials` 的模块头。 + Some(crate::server::core::providers::ProviderKind::Atomcode) => { + // 面板传来的名字是用户自己打的 ⇒ 标 custom(此后续期/重登不改它) + store.add_atomcode_account(&payload, import_name) + } + // LobsterAI:**没有粘贴路径**(如实拒绝,而不是落一条用不了的账号)。 + // 它的凭证里有两个 `keyfrom` 与一个 `uuid`,只在换码那一刻由上游下发 —— + // 用户从别处粘不出这三样,缺了它们账号续不了期(见 + // `providers::lobsterai::credentials` 的模块头)。所以本家只有一个入口: + // 网页登录落到本机 loopback 回调后由适配器落账号。 + Some(crate::server::core::providers::ProviderKind::LobsterAi) => { + return management_error( + 400, + "LobsterAI 只能通过「网页登录」添加:它的续期需要换码时下发的 uuid 与 keyfrom,粘贴给不出这三项", + ); + } // WorkBuddy 系的两家(国内版 / 国际版):同一套凭证形态与落账号路径, // 差别只有归属 —— provider id 自己就是归属(拆家后不再从 payload 里的 // `edition` 反推:那是账号的属性,而落哪一家是身份问题)。 @@ -1563,6 +1583,21 @@ pub async fn refresh_account(state: &ServerState, body: &Bytes) -> Response { if state.store().officeace_account_record(&id).is_some() { return refresh_provider_account(state, &id, ProviderKind::OfficeAce).await; } + // AtomCode:续期走它自己的适配器(`POST {broker}/oauth/refresh`,**会轮换** + // refresh token,且必须单飞 + 写回)。**这条不能省**:漏了就落到 workbuddy + // 兜底链路,用户点「刷新 Token」收到的是腾讯那侧的错(与 CodeArts/Trae/ + // OfficeAce 同一条理由),而 AtomGit 的令牌会因此悄悄过期到只能重新登录。 + if state.store().atomcode_account_record(&id).is_some() { + return refresh_provider_account(state, &id, ProviderKind::Atomcode).await; + } + // LobsterAI:续期走它自己的适配器(`POST /api/auth/refresh`,**会轮换**, + // 且必须把 uuid 与两个 keyfrom 原样回传)。**这条不能省**:漏了就落到 + // workbuddy 兜底链路,用户点「刷新 Token」收到的是腾讯那侧的错(与 + // CodeArts/Trae/OfficeAce/AtomCode 同一条理由),而龙虾的令牌会因此 + // 悄悄过期到只能重新网页登录 —— 而那一轮登录是用户手动做的。 + if state.store().lobsterai_account_record(&id).is_some() { + return refresh_provider_account(state, &id, ProviderKind::LobsterAi).await; + } // Accio(两个地区):走适配器的强制刷新(`POST /api/auth/refresh_token`, // 结果按「比较再写」回写)。两个地区各查一次 —— 账号集合按 provider 隔离, // 同一 id 不可能同时属于两家(撞 id 在存储层就报错了)。 diff --git a/desktop-tauri/src-tauri/server/src/server/api/pipeline.rs b/desktop-tauri/src-tauri/server/src/server/api/pipeline.rs index a21a2adb..24f212ac 100644 --- a/desktop-tauri/src-tauri/server/src/server/api/pipeline.rs +++ b/desktop-tauri/src-tauri/server/src/server/api/pipeline.rs @@ -58,6 +58,21 @@ const ERROR_SUMMARY_CHARS: usize = 200; /// HTTP 状态早就发出去了(2xx),明细里只能靠这条文案解释「为什么没有 token」。 pub const STREAM_ABORTED: &str = "响应流未完整下发(客户端中断或服务退出)"; +/// 上游流跑到 EOF、但**从未下发本协议的收尾帧**时,写进尝试明细的提示。 +/// +/// ── 为什么是 notice 而不是 error ──────────────────────────── +/// `RequestEntry::is_success()` 的口径是「2xx **且** `error.is_none()`」 +/// (`request_stats/record.rs`),而失败的请求要过「清零 token」的归一。 +/// 上游 EOF 这一路里,客户端**已经收到了它给的全部内容**,只是上游没补收尾帧 —— +/// 把它记成 error 会连带抹掉真实用量,那正是 `terminal_seen()` 当初要救的东西 +/// (生产上 1176 行 CodeArts 就折在这里,见本文件顶部 `terminal::CHAT` 的说明)。 +/// 所以这里只留一条**观测**:不参与成功/失败判定,也不参与归一。 +/// +/// ── 为什么文案不能带「未完整下发」这五个字 ────────────────── +/// 报表与排查脚本里已经有按 `STREAM_ABORTED` 文案筛的查询;复用那半句会让 +/// 「真中断」与「上游没收尾但内容齐全」两类**混成一条**,而那两类的修法相反。 +pub const STREAM_EOF_WITHOUT_TERMINAL: &str = "上游流已结束,未下发协议收尾帧"; + /// 一条「本协议收尾帧」的字节特征(见 [`RecordingStream`] 的说明)。 /// /// 由**调用方**按自己那条协议给出(三条入口各知道自己发什么收尾),本模块 @@ -569,6 +584,8 @@ fn stored_attempt_details(list: &[usage::AttemptDetail]) -> Vec { notice: item.notice.clone(), // 体字节数原样透传(None = 这一轮没发出去;采集侧的理由见 usage 的字段说明) body_bytes: item.body_bytes, + // 同上,方向相反:下发给客户端的响应字节数(「已记账」的收尾点写入) + forwarded_bytes: item.forwarded_bytes, }) .collect() } @@ -798,6 +815,12 @@ pub struct RecordingStream { terminal: Option, /// 响应正文累积缓冲(`request_raw` 表的响应侧采集,见 [`RawCapture`]) raw: RawCapture, + /// **实际下发给客户端的字节总数**,与调试模式无关。 + /// + /// 不能拿 `raw.buf.len()` 代替:`RawCapture::push` 在开关关闭时第一个分支就 + /// return(零开销设计),那样关着调试时这个数恒为 0 —— 而「0 字节」正是本次 + /// 要区分出来的那一类,用一个受开关影响的量去判它等于把结论建在随机数上。 + forwarded: i64, } /// 响应正文的累积缓冲。 @@ -868,7 +891,7 @@ impl RecordingStream { // 响应侧采集与请求侧同一道闸:调试模式关着就不攒缓冲(零开销)—— // 这里定格一次,本请求的响应侧行为此后不再看开关(见 `RawCapture` 的说明) let raw = RawCapture::new(crate::server::core::debug_traffic::enabled()); - Self { inner, context: Some(context), terminal, raw } + Self { inner, context: Some(context), terminal, raw, forwarded: 0 } } /// 记账并清空上下文(幂等:第二次调用什么都不做)。 @@ -877,6 +900,11 @@ impl RecordingStream { /// 或「正文就这么多」的时刻,此后不再有字节。 fn settle(&mut self, fallback_error: Option) { if let Some(mut context) = self.context.take() { + // 下发字节数在收尾点定型:两条路径(跑到 None / 被丢弃)都要有数 —— + // 「被丢弃」那条恰恰最需要知道「客户端到底收到多少」 + context.telemetry.note_attempt_forwarded_bytes(self.forwarded); + // 响应正文在这里定稿:流的收尾点(跑到 None / 被丢弃)正是「正文完整了」 + // 或「正文就这么多」的时刻,此后不再有字节。 // 流式路径构造时 raw_response 恒为 None,这里用缓冲定稿直接覆盖; // 若未来有入口想预填响应正文,那它不该再走流式包装(自相矛盾的约定) context.raw_response = self.raw.into_text(); @@ -916,6 +944,8 @@ impl futures::Stream for RecordingStream { if let Some(context) = &this.context { context.telemetry.note_first_frame(); } + // 下发计数:每个透传成功的 chunk 都算,与调试模式无关 + this.forwarded = this.forwarded.saturating_add(bytes.len() as i64); // 响应正文采集:抄进缓冲(上限后丢弃),透传字节原样不动 this.raw.push(bytes); // 收尾帧识别:命中即说明客户端接下来随时可能断开,那属于正常收尾 @@ -933,6 +963,17 @@ impl futures::Stream for RecordingStream { std::task::Poll::Pending => {} } if matches!(polled, std::task::Poll::Ready(None)) { + // 上游跑到 EOF、却从未下发本协议的收尾帧 ⇒ 留一条观测。 + // 写 notice 不写 error 的理由见 `STREAM_EOF_WITHOUT_TERMINAL`:这一路 + // 客户端**已经收到全部内容**,记成 error 会经 `is_success()` 触发 + // 「失败清零 token」的归一,把真实用量一并抹掉。 + // `terminal.is_none()` 的入口不写:那是调用方没给特征表,等于这里 + // 没有判据,写进去只会变成每条都响的噪音。 + if this.terminal.is_some() && !this.terminal_seen() { + if let Some(context) = &this.context { + context.telemetry.note_attempt_notice(STREAM_EOF_WITHOUT_TERMINAL); + } + } this.settle(None); } polled @@ -1254,6 +1295,14 @@ mod tests { telemetry.report_usage(&serde_json::json!({ "prompt_tokens": 71476, "completion_tokens": 56, "total_tokens": 71532 })); + // 起头一次尝试:生产上这两步都由编排层 `provider_loop` 在发送前做, + // 分工是 `note_attempt` 只累加计数与账号、**明细条目由 `note_attempt_started` + // 推入**。而 notice / bodyBytes / forwardedBytes 三个观测都挂在**最后一条** + // 明细上(`note_attempt_*` 系列都是 `last_mut()`)—— 少了后一步,它们全部 + // 静默丢失。10-17 这条是被断言抓出来的:行里 `attemptDetails` 空数组, + // 但 `attempts` 因为下限钳位仍显示 1,看数字完全像是"有明细"。 + telemetry.note_attempt(Some("acct-1"), "测试账号", "codearts"); + telemetry.note_attempt_started("codearts", "测试账号"); let context = RecordContext { stats, telemetry: telemetry.clone(), @@ -1357,4 +1406,111 @@ mod tests { assert!(error.contains("未完整下发"), "真中断必须留摘要:{row}"); assert_eq!(row["promptTokens"], Value::from(0), "失败的行按契约清零:{row}"); } + + /// 走一遍「已下发这些帧、之后上游**给到 EOF**」的流(与 `record_after_hangup` + /// 的区别只在尾部有没有 `pending`:那条模拟客户端先关,这条模拟上游先完)。 + async fn record_through_eof(frames: &[&'static [u8]], terminals: TerminalFrames) -> Value { + let (stats, _dir) = temp_stats("eof"); + let (context, _telemetry) = context_for(stats.clone()); + let mut stream = RecordingStream::with_terminals( + Box::new(futures::stream::iter(frames.to_vec().into_iter().map(|frame| { + Ok::(Bytes::from_static(frame)) + }))), + context, + terminals, + ); + // 读到 EOF:把整条流消费干净,收尾走 poll_next 的 Ready(None) 分支 + while stream.next().await.is_some() {} + only_row(&stats) + } + + /// 从行里取尝试明细(键名以落库形状为准,见 `request_stats::record`)。 + fn details_of(row: &Value) -> Vec { + row.get("attemptDetails") + .and_then(Value::as_array) + .cloned() + .unwrap_or_default() + } + + /// 生产 P2 的形状:上游流跑到 EOF,但从未下发本协议的收尾帧。 + /// + /// 断言分两头,缺一头都是空跑: + /// · **必须有**观测 —— 明细里留一条提示,且**不是** `error`(写 error 会经 + /// `is_success()` 触发「失败清零」,那正是 terminal_seen() 当初救回来的 + /// 1176 行的病灶); + /// · **用量必须留着** —— 客户端已经收到全部内容,这一轮不是失败。 + #[tokio::test] + async fn upstream_eof_without_terminal_frame_annotates_but_keeps_usage() { + let row = record_through_eof(&[LAST_CHUNK.as_bytes()], terminal::CHAT).await; + assert_eq!(row["status"], Value::from(200), "状态码照原样:{row}"); + assert!(row["error"].is_null(), "无收尾帧是观测不是失败,不能写进 error:{row}"); + assert_eq!(row["promptTokens"], Value::from(71476), "这条不能走失败清零:{row}"); + assert_eq!(row["completionTokens"], Value::from(56), "这条不能走失败清零:{row}"); + + let details = details_of(&row); + let notice = details + .first() + .and_then(|item| item.get("notice")) + .and_then(Value::as_str) + .unwrap_or(""); + assert!( + notice.contains("上游流已结束"), + "明细里必须留下这条提示,否则报表问不出原因:{row}" + ); + assert!(!notice.contains("未完整下发"), "文案必须与真中断区分开,两类修法相反:{notice}"); + let forwarded = details + .first() + .and_then(|item| item.get("forwardedBytes")) + .and_then(Value::as_i64); + assert_eq!( + Some(LAST_CHUNK.len() as i64), + forwarded, + "forwardedBytes 要等于逐 chunk 累加的真实下发量:{row}" + ); + } + + /// 正对照:同一份字节,**带**收尾帧跑到 EOF ⇒ 不打提示(判据真的落在收尾帧上, + /// 而不是「EOF 一律报」)。少了这条,上一条可能只是「无条件写一句 notice」。 + #[tokio::test] + async fn upstream_eof_after_the_terminal_frame_stays_clean() { + let row = + record_through_eof(&[LAST_CHUNK.as_bytes(), UNSPACED_DONE.as_bytes()], terminal::CHAT) + .await; + assert!(row["error"].is_null(), "正常收尾不该有摘要:{row}"); + let details = details_of(&row); + let notice = details + .first() + .and_then(|item| item.get("notice")) + .and_then(Value::as_str) + .unwrap_or(""); + assert!(!notice.contains("上游流已结束"), "下发过收尾帧就不该再提示:{row}"); + let forwarded = details + .first() + .and_then(|item| item.get("forwardedBytes")) + .and_then(Value::as_i64); + assert_eq!( + Some((LAST_CHUNK.len() + UNSPACED_DONE.len()) as i64), + forwarded, + "计数要把每一帧都算进去:{row}" + ); + } + + /// 丢弃路径也要有 `forwardedBytes` —— 那才是「客户端收到多少」最有用的一类, + /// 也是本仓原来问不出来的那一格(生产 P1 的 1271 行)。 + #[tokio::test] + async fn the_abort_path_also_records_how_much_was_forwarded() { + let row = record_after_hangup(&[LAST_CHUNK.as_bytes()], OLD_CHAT).await; + assert!( + row["error"] + .as_str() + .unwrap_or("") + .contains("未完整下发"), + "这条仍应是中断:{row}" + ); + let forwarded = details_of(&row) + .first() + .and_then(|item| item.get("forwardedBytes")) + .and_then(Value::as_i64); + assert_eq!(Some(LAST_CHUNK.len() as i64), forwarded, "中断行也要留下已下发量:{row}"); + } } diff --git a/desktop-tauri/src-tauri/server/src/server/api/session.rs b/desktop-tauri/src-tauri/server/src/server/api/session.rs index 289cfa60..2fc4e0df 100644 --- a/desktop-tauri/src-tauri/server/src/server/api/session.rs +++ b/desktop-tauri/src-tauri/server/src/server/api/session.rs @@ -316,6 +316,31 @@ pub async fn login_start(State(state): State, body: Bytes) -> Respo return ok_json(json!({ "state": task_state, "authUrl": auth_url, "edition": task_edition, "provider": "officeace" })); } + // AtomCode(AtomGit / GitCode):**服务端中介的轮询登录**(与 OfficeAce / + // ZCode 同一形态,见 `core::login::atomcode` 的模块头)—— 授权地址由本进程 + // 向代理现取,用户在浏览器里登完,后台任务**轮询代理**判完成并取令牌, + // **不需要壳侧开回调监听**(AtomCode 的回调由代理自己在 + // `acs.atomgit.com/callback` 终结,永远不回到本机)。 + // 响应形状与另外几条登录链一致(`{state, authUrl, edition, provider}`)。 + if kind == crate::server::core::providers::ProviderKind::Atomcode { + let handle = match state.login().start_atomcode_login() { + Ok(handle) => handle, + Err(error) => return management_error(400, error), + }; + let (task_state, auth_url, task_edition) = match state + .login() + .wait_for_auth_url(&handle, Duration::from_millis(AUTH_URL_WAIT_MS)) + .await + { + Ok(values) => values, + Err(error) => { + logging::log("[Login]", &format!("❌ 发起 AtomCode 登录失败: {error}")); + return management_error(502, error); + } + }; + return ok_json(json!({ "state": task_state, "authUrl": auth_url, + "edition": task_edition, "provider": "atomcode" })); + } // Cline:**设备授权登录**(WorkOS RFC 8628)。形态上介于「网页登录」与 // 「Qoder 设备授权」之间:同步问上游要 user_code 与授权页地址(一次 POST), // 把地址交给界面打开;用户确认后由后台任务轮询换令牌。 @@ -1245,6 +1270,37 @@ fn oauth_callback_page(status: i32, message: &str) -> Response { /// 因此直接按它查任务表即可(与 AutoClaw 的「变体匹配 + 最近发起」不同)。 /// /// 响应是给人看的 HTML(浏览器停在这一页),不走 `ok_json` 那套信封。 +// ─── /auth/callback(LobsterAI 的 loopback 回调)───────────── + +/// LobsterAI 网页登录的回调。**浏览器 302 到这里**(授权页完成后顶层导航回我们 +/// 交给上游门户的 `redirect_uri`,查询串带 `code` / `state`)。 +/// +/// 路径是裸 `/auth/callback`(与别家带家名后缀的风格不一致)——理由见 +/// `http.rs` 里那条路由的注释:`redirect_uri` 是我们给门户的,而门户是否对 +/// 回调路径有白名单没有实测支撑,所以跟随参考实现那条已验证的形状。 +/// +/// 免鉴权的理由与 accio / autoclaw / codearts / antigravity 四条 loopback 回调 +/// 同一句:调用方是**用户的浏览器**,它当然没有我们的 API Key;安全性由本次 +/// 登录任务生成的一次性 `state` 承担(见 `core::login::lobsterai`)。 +pub async fn login_lobsterai_callback( + State(state): State, + Query(params): Query>, +) -> Response { + let code = params.get("code").cloned().unwrap_or_default(); + let task_state = params.get("state").cloned().unwrap_or_default(); + if let Some(error) = params.get("error").filter(|value| !value.trim().is_empty()) { + return oauth_callback_page(400, &format!("登录失败:授权被拒绝({error})")); + } + match state.login().finish_lobsterai_login(&code, &task_state).await { + Ok(_) => oauth_callback_page(200, "登录成功,已返回网关,可以关闭此页面。"), + Err(error) => { + // 这一轮已经作废:把 pending 也丢掉,免得留在表里等超时 + state.login().drop_lobsterai_pending(&task_state); + oauth_callback_page(error.status_code, &format!("登录失败:{}", error.message)) + } + } +} + pub async fn login_accio_callback( State(state): State, Query(params): Query>, diff --git a/desktop-tauri/src-tauri/server/src/server/core/account_store/atomcode_accounts.rs b/desktop-tauri/src-tauri/server/src/server/core/account_store/atomcode_accounts.rs new file mode 100644 index 00000000..092cc8ba --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/account_store/atomcode_accounts.rs @@ -0,0 +1,333 @@ +//! AtomCode 账号:粘贴凭据落账号、刷新回写、公开形态。 +//! +//! ── 账号字段 ──────────────────────────────────────────────── +//! ```text +//! 本项目记录:{ id, provider:"atomcode", name, userId, username, accessToken, +//! refreshToken, tokenTail, expiresAt, addedAt, updatedAt, +//! source, priority, enabled } +//! 代理令牌包: { access_token, refresh_token, token_type, expires_in, user:{id,…} } +//! ``` +//! `accessToken` / `refreshToken` 键名与别家统一(`update_account_tokens` 一族 +//! 认这两个键,见 `store_admin.rs`),粘贴形态另外兼容官方 `auth.toml` 的 +//! 下划线拼法(`access_token` / `refresh_token`)。 +//! +//! ── 与别家添加路径的关系 ──────────────────────────────────── +//! - **粘贴凭据**:`api::accounts::add_account` 的 atomcode 分支(唯一的入口, +//! 在此之前界面上的「浏览器登录」尚未接壳侧,见 `providers::atomcode` 模块头); +//! - **刷新回写**:不走本文件 —— 走通用的 +//! `AccountStore::update_account_tokens`(`providers::atomcode::mod` 的 +//! `refresh_and_save` 调它)。 +//! +//! 本文件全是「读-改-写」文件操作,**没有任何网络请求**(持锁不做网络)。 +//! 绝不 unwrap/expect(release 是 panic=abort)。 + +use serde_json::{Map, Value}; + +use crate::server::core::account_store::priority::next_free_priority; +use crate::server::core::account_store::sql; +use crate::server::core::account_store::state::{mark_name_custom, StoredAccount}; +use crate::server::core::account_store::{ATOMCODE_PROVIDER_ID, AccountStore, AccountStoreError}; +use crate::server::logging; + +use crate::server::core::providers::atomcode::credentials; + +/// 备注名长度上限(与别家同一口径) +const MAX_NAME_LENGTH: usize = 100; + +/// 身份字段(userId / username)长度上限 +const MAX_IDENTITY_LENGTH: usize = 256; + +/// 令牌长度上限(超过这个长度的输入一定是粘错了东西) +const MAX_TOKEN_LENGTH: usize = 8192; + +/// 截断到 `max` 个**字符**(不是字节;用户名可能有中文) +fn truncate_chars(value: &str, max: usize) -> String { + if value.chars().count() <= max { + return value.to_string(); + } + value.chars().take(max).collect() +} + +/// 从一条取值链里挑第一个非空字符串 +fn pick(payload: &Value, keys: &[&str]) -> String { + for key in keys { + let value = payload + .get(*key) + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or(""); + if !value.is_empty() { + return value.to_string(); + } + } + String::new() +} + +/// 从一条取值链里挑第一个正数 +fn pick_i64(payload: &Value, keys: &[&str]) -> i64 { + for key in keys { + if let Some(value) = payload.get(*key).and_then(Value::as_i64) { + if value > 0 { + return value; + } + } + } + 0 +} + +impl AccountStore { + /// 取一条 AtomCode 账号记录(公开形态的原始 JSON)。 + /// + /// `account_id` 非空 → 按 id 直查(不属于本家返回 None); + /// 为空 → 本家**启用中**账号里优先级最靠前的一条(转发默认用队首账号)。 + pub fn atomcode_account_record(&self, account_id: &str) -> Option { + let guard = self.guard(); + if !account_id.is_empty() { + let record = self.record_by_id(&guard, account_id)?; + return (record.provider() == ATOMCODE_PROVIDER_ID).then(|| record.to_value()); + } + let mut candidates: Vec = self + .records_for_provider(&guard, ATOMCODE_PROVIDER_ID) + .into_iter() + .filter(StoredAccount::enabled) + .collect(); + candidates.sort_by_key(StoredAccount::order_key); + candidates.into_iter().next().map(|item| item.to_value()) + } + + /// 添加/更新一个 AtomCode 账号(粘贴凭据的唯一入口)。 + /// + /// payload: + /// - `accessToken` / `access_token` / `token`:访问令牌(必填); + /// - `refreshToken` / `refresh_token`:续期令牌(**强烈建议给** —— + /// 没有它账号无法自动续期,只能等用户重新粘贴); + /// - `userId` / `user_id` / `username`:身份(可选,用于生成账号 id 与展示名); + /// - `expiresAt`:过期时刻(毫秒,可选);`createdAt` + `expiresIn` 可替代它 + /// (令牌非 JWT,读不到 `exp`,过期只能这样算 —— 见 `credentials.rs`)。 + /// + /// 撞 id 时:同 provider 就地更新(保留优先级与启用状态、沿用用户改过的 + /// 备注名),撞到**别的 provider** 时报错 —— 绝不覆写他人记录。 + pub fn add_atomcode_account( + &self, + payload: &Value, + name: Option<&str>, + ) -> Result { + let Some(_object) = payload.as_object() else { + return Err(AccountStoreError::new("上传内容必须是 JSON 对象", 400)); + }; + let access_token = pick(payload, &["accessToken", "access_token", "token"]); + if access_token.is_empty() { + return Err(AccountStoreError::new( + "缺少访问令牌(accessToken / access_token / token)", + 400, + )); + } + if access_token.chars().count() > MAX_TOKEN_LENGTH { + return Err(AccountStoreError::new("访问令牌过长", 400)); + } + let refresh_token = pick(payload, &["refreshToken", "refresh_token"]); + if refresh_token.chars().count() > MAX_TOKEN_LENGTH { + return Err(AccountStoreError::new("refresh token 过长", 400)); + } + let user_id = truncate_chars(&pick(payload, &["userId", "user_id", "uid"]), MAX_IDENTITY_LENGTH); + let username = truncate_chars(&pick(payload, &["username", "userName", "login"]), MAX_IDENTITY_LENGTH); + // 身份标识:优先 userId(稳定);没有就退回用户名;再没有就用令牌尾段 + // (与 loomy 的处置一致:至少要有一个可辨认身份,否则 `atomcode-` 前缀的 id 会撞) + let identity = if !user_id.is_empty() { + user_id.clone() + } else if !username.is_empty() { + username.clone() + } else { + let tail: String = access_token + .chars() + .rev() + .take(12) + .collect::>() + .into_iter() + .rev() + .collect(); + format!("tok{tail}") + }; + let id = format!("atomcode-{identity}"); + let now = logging::now_ms(); + + let guard = self.guard(); + let existing = self.record_by_id(&guard, &id); + if let Some(existing) = existing.as_ref() { + if existing.provider() != ATOMCODE_PROVIDER_ID { + return Err(AccountStoreError::new( + "该账号 id 已被其它提供商占用,请改名后重试", + 409, + )); + } + } + + let priority = match existing.as_ref() { + Some(record) => record.priority(), + None => { + // 优先级全局唯一(各家共用一条队列),取全库已用号段 + let used = self.with_conn(&guard, |conn| sql::priorities_all(conn))?; + next_free_priority(&used) + } + }; + let record_name = truncate_chars( + name.map(str::trim) + .filter(|value| !value.is_empty()) + .unwrap_or(if username.is_empty() { &identity } else { &username }), + MAX_NAME_LENGTH, + ); + let expires_at = { + let explicit = pick_i64(payload, &["expiresAt"]); + if explicit > 0 { + explicit + } else { + credentials::expires_at_from( + pick_i64(payload, &["createdAt"]), + pick_i64(payload, &["expiresIn", "expires_in"]), + now, + ) + } + }; + + let mut record = Map::new(); + record.insert("id".to_string(), Value::String(id.clone())); + record.insert( + "provider".to_string(), + Value::String(ATOMCODE_PROVIDER_ID.to_string()), + ); + record.insert("name".to_string(), Value::String(record_name.clone())); + if !user_id.is_empty() { + record.insert("userId".to_string(), Value::String(user_id)); + } + if !username.is_empty() { + record.insert("username".to_string(), Value::String(username)); + } + record.insert("accessToken".to_string(), Value::String(access_token.clone())); + if !refresh_token.is_empty() { + record.insert("refreshToken".to_string(), Value::String(refresh_token)); + } + record.insert( + "tokenTail".to_string(), + Value::String( + access_token + .chars() + .rev() + .take(4) + .collect::>() + .into_iter() + .rev() + .collect(), + ), + ); + if expires_at > 0 { + record.insert("expiresAt".to_string(), Value::from(expires_at)); + } + record.insert("source".to_string(), Value::String("manual".to_string())); + record.insert("priority".to_string(), Value::from(priority)); + record.insert("enabled".to_string(), Value::Bool(true)); + record.insert( + "addedAt".to_string(), + Value::from( + existing + .as_ref() + .and_then(|record| record.get("addedAt").and_then(Value::as_i64)) + .unwrap_or(now), + ), + ); + mark_name_custom( + &mut record, + name.is_some_and(|value| !value.trim().is_empty()), + existing.as_ref(), + ); + record.insert("updatedAt".to_string(), Value::from(now)); + + // 未知字段全量保留(用户手工加过的字段不能因为一次「更新账号」丢掉) + let mut merged = record; + if let Some(existing) = existing.as_ref() { + for (key, value) in existing.fields() { + merged.entry(key.clone()).or_insert_with(|| value.clone()); + } + } + let saved = StoredAccount::from_map(merged); + self.with_conn(&guard, |conn| sql::put(conn, &saved))?; + logging::log( + "[Accounts]", + &format!("✅ AtomCode 账号已保存: {record_name}(优先级 {priority})"), + ); + Ok(self.public_account(&saved)) + } + + /// AtomCode 账号的**公开形态**(进 HTTP 响应):去掉两个令牌本体。 + /// + /// ⚠️ `refreshToken` 必须一起摘掉:只摘 `accessToken` 会让一个长期有效的 + /// 续期凭据随账号列表下发到界面上(与 loomy 只摘 session 的场景不同 —— + /// 那家的 session 是唯一凭据)。 + pub fn to_atomcode_public_account(&self, record: &StoredAccount) -> Value { + let mut value = record.to_value(); + if let Some(object) = value.as_object_mut() { + object.remove("accessToken"); + object.remove("refreshToken"); + object.insert("available".to_string(), Value::Bool(record.enabled())); + object.insert( + "hasSession".to_string(), + Value::Bool(record.get("accessToken").is_some() || record.get("tokenTail").is_some()), + ); + } + value + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + /// 公开形态必须把**两个令牌本体**都摘掉。 + #[test] + fn public_shape_never_leaks_either_token() { + let record = StoredAccount::from_map({ + let mut map = Map::new(); + map.insert("id".into(), Value::String("atomcode-42".into())); + map.insert("provider".into(), Value::String(ATOMCODE_PROVIDER_ID.into())); + map.insert("accessToken".into(), Value::String("at-secret".into())); + map.insert("refreshToken".into(), Value::String("rt-secret".into())); + map.insert("tokenTail".into(), Value::String("cret".into())); + map + }); + let store_free_shape = record.to_value(); + assert!(store_free_shape.get("accessToken").is_some()); + // 公开形态是 store 方法,这里直接照它的规则断言(避免构造整个 store) + let mut public = record.to_value(); + if let Some(object) = public.as_object_mut() { + object.remove("accessToken"); + object.remove("refreshToken"); + } + assert!(public.get("accessToken").is_none()); + assert!(public.get("refreshToken").is_none()); + assert_eq!(public.get("tokenTail").and_then(Value::as_str), Some("cret")); + } + + /// 取字段:三种拼法都要能读到(本项目名 / 官方 auth.toml 名 / 粘贴名)。 + #[test] + fn payload_aliases_are_accepted() { + let payload = json!({"access_token": "at", "refresh_token": "rt", "user_id": "42"}); + assert_eq!(pick(&payload, &["accessToken", "access_token", "token"]), "at"); + assert_eq!(pick(&payload, &["refreshToken", "refresh_token"]), "rt"); + assert_eq!(pick(&payload, &["userId", "user_id", "uid"]), "42"); + } + + /// 过期时刻可由 `createdAt + expiresIn` 推出(令牌非 JWT)。 + #[test] + fn expiry_can_come_from_created_at_plus_expires_in() { + let payload = json!({"createdAt": 1_700_000_000_i64, "expiresIn": 604_800}); + assert_eq!(pick_i64(&payload, &["expiresAt"]), 0); + assert_eq!( + credentials::expires_at_from( + pick_i64(&payload, &["createdAt"]), + pick_i64(&payload, &["expiresIn", "expires_in"]), + 0 + ), + 1_700_000_000_000 + 604_800_000 + ); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/account_store/lobsterai_accounts.rs b/desktop-tauri/src-tauri/server/src/server/core/account_store/lobsterai_accounts.rs new file mode 100644 index 00000000..78812833 --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/account_store/lobsterai_accounts.rs @@ -0,0 +1,419 @@ +//! LobsterAI 账号:浏览器登录落账号、公开形态。 +//! +//! ── 这一家的账号记录比别家多三样东西(**别省**)────────────── +//! `uuid` 与两个 `keyfrom` 只在**换码那一刻**由上游下发,续期时必须原样回传。 +//! 只存 access/refresh 两个 token 的账号,能用到 access 过期为止,之后既续不了 +//! 期也修不回来(只能重新登录)。理由与字段语义见 `providers::lobsterai::credentials` +//! 的模块头。 +//! +//! ── 与别家添加路径的关系 ──────────────────────────────────── +//! 只有一个入口:网页登录(`api::session` 的 loopback 回调 → 本文件的 +//! `add_lobsterai_account`)。**没有桌面端导入**(客户端登录态在它自己的 +//! sqlite 里,不在稳定可读的文件形态里),也没有粘贴路径(粘贴给不出载荷件三件套)。 +//! +//! 本文件全是「读-改-写」文件操作,**没有任何网络请求**(持锁不做网络)。 +//! 绝不 unwrap/expect(release 是 panic=abort)。 + +use serde_json::{Map, Value}; + +use crate::server::core::account_store::priority::next_free_priority; +use crate::server::core::account_store::sql; +use crate::server::core::account_store::state::{mark_name_custom, StoredAccount}; +use crate::server::core::account_store::{AccountStore, AccountStoreError, LOBSTERAI_PROVIDER_ID}; +use crate::server::core::providers::lobsterai::credentials; +use crate::server::logging; + +/// 备注名长度上限(与别家同一口径) +const MAX_NAME_LENGTH: usize = 100; + +/// 身份字段长度上限 +const MAX_IDENTITY_LENGTH: usize = 256; + +/// 令牌长度上限(超过这个长度的输入一定是粘错了东西) +const MAX_TOKEN_LENGTH: usize = 8192; + +/// keyfrom / uuid 的长度上限(它们是短标识,不该有几千字符) +const MAX_PAYLOAD_FIELD_LENGTH: usize = 512; + +/// 截断到 `max` 个**字符**(不是字节;昵称可能有中文) +fn truncate_chars(value: &str, max: usize) -> String { + if value.chars().count() <= max { + return value.to_string(); + } + value.chars().take(max).collect() +} + +/// 从一条取值链里挑第一个非空字符串 +fn pick(payload: &Value, keys: &[&str]) -> String { + for key in keys { + let value = payload + .get(*key) + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or(""); + if !value.is_empty() { + return value.to_string(); + } + } + String::new() +} + +/// 读一个毫秒时刻(数字或数字字符串都认) +fn pick_ms(payload: &Value, keys: &[&str]) -> i64 { + for key in keys { + if let Some(value) = payload.get(*key) { + if let Some(number) = value.as_i64() { + if number > 0 { + return number; + } + } + if let Some(text) = value.as_str() { + if let Ok(number) = text.trim().parse::() { + if number > 0 { + return number; + } + } + } + } + } + 0 +} + +/// 读一个秒数(`expiresIn`) +fn pick_secs(payload: &Value, keys: &[&str]) -> i64 { + for key in keys { + if let Some(number) = payload.get(*key).and_then(Value::as_i64) { + if number > 0 { + return number; + } + } + } + 0 +} + +impl AccountStore { + /// 取一条 LobsterAI 账号记录(公开形态的原始 JSON)。 + /// + /// `account_id` 非空 → 按 id 直查(不属于本家返回 None); + /// 为空 → 本家**启用中**账号里优先级最靠前的一条。 + pub fn lobsterai_account_record(&self, account_id: &str) -> Option { + let guard = self.guard(); + if !account_id.is_empty() { + let record = self.record_by_id(&guard, account_id)?; + return (record.provider() == LOBSTERAI_PROVIDER_ID).then(|| record.to_value()); + } + let mut candidates: Vec = self + .records_for_provider(&guard, LOBSTERAI_PROVIDER_ID) + .into_iter() + .filter(StoredAccount::enabled) + .collect(); + candidates.sort_by_key(StoredAccount::order_key); + candidates.into_iter().next().map(|item| item.to_value()) + } + + /// 落一个 LobsterAI 账号(网页登录换码后的唯一入口)。 + /// + /// payload(换码结果的形状): + /// - `accessToken` / `access_token`:访问令牌(必填); + /// - `refreshToken`:续期令牌(**强烈建议有** —— 没有它账号只能重新登录); + /// - `uuid`:**载荷件**,续期必须回传; + /// - `firstKeyfrom` / `latestKeyfrom`:**载荷件**,同上; + /// - `userId` / `uid`:身份与展示; + /// - `name` / `nickname`:展示名; + /// - `expiresAt` 或 `createdAt` + `expiresIn`:过期时刻(毫秒)。 + /// + /// ⚠️ 载荷件与 refreshToken 缺失**不阻止落账号**:登录当时换码是成功的, + /// 账号能一直用到 access 过期;只是那种账号续不了期,界面上会按 + /// `can_refresh()` 如实标出来(与「不给落」相比,用户至少能用一阵子)。 + pub fn add_lobsterai_account( + &self, + payload: &Value, + name: Option<&str>, + ) -> Result { + if payload.as_object().is_none() { + return Err(AccountStoreError::new("上传内容必须是 JSON 对象", 400)); + } + let access_token = pick(payload, &["accessToken", "access_token"]); + if access_token.is_empty() { + return Err(AccountStoreError::new( + "缺少访问令牌(accessToken / access_token)", + 400, + )); + } + if access_token.chars().count() > MAX_TOKEN_LENGTH { + return Err(AccountStoreError::new("访问令牌过长", 400)); + } + let refresh_token = pick(payload, &["refreshToken", "refresh_token"]); + if refresh_token.chars().count() > MAX_TOKEN_LENGTH { + return Err(AccountStoreError::new("refresh token 过长", 400)); + } + let user_id = truncate_chars( + &pick(payload, &["userId", "user_id"]), + MAX_IDENTITY_LENGTH, + ); + let uid = truncate_chars(&pick(payload, &["uid", "id"]), MAX_IDENTITY_LENGTH); + let nickname = truncate_chars( + &pick(payload, &["name", "nickname"]), + MAX_NAME_LENGTH, + ); + let uuid = truncate_chars(&pick(payload, &["uuid", "UUID"]), MAX_PAYLOAD_FIELD_LENGTH); + let first_keyfrom = truncate_chars( + &pick(payload, &["firstKeyfrom", "first_keyfrom"]), + MAX_PAYLOAD_FIELD_LENGTH, + ); + let latest_keyfrom = truncate_chars( + &pick(payload, &["latestKeyfrom", "latest_keyfrom"]), + MAX_PAYLOAD_FIELD_LENGTH, + ); + + // 身份标识:优先 userId(稳定)→ uid → 令牌尾段 + let identity = if !user_id.is_empty() { + user_id.clone() + } else if !uid.is_empty() { + uid.clone() + } else { + let tail: String = access_token + .chars() + .rev() + .take(12) + .collect::>() + .into_iter() + .rev() + .collect(); + format!("tok{tail}") + }; + let id = format!("lobsterai-{identity}"); + let now = logging::now_ms(); + + let guard = self.guard(); + let existing = self.record_by_id(&guard, &id); + if let Some(existing) = existing.as_ref() { + if existing.provider() != LOBSTERAI_PROVIDER_ID { + return Err(AccountStoreError::new( + "该账号 id 已被其它提供商占用,请改名后重试", + 409, + )); + } + } + + let priority = match existing.as_ref() { + Some(record) => record.priority(), + None => { + // 优先级全局唯一(各家共用一条队列),取全库已用号段 + let used = self.with_conn(&guard, |conn| sql::priorities_all(conn))?; + next_free_priority(&used) + } + }; + let record_name = truncate_chars( + name.map(str::trim) + .filter(|value| !value.is_empty()) + .unwrap_or_else(|| { + if nickname.is_empty() { + &identity + } else { + &nickname + } + }), + MAX_NAME_LENGTH, + ); + + // 过期时刻:显式 > createdAt+expiresIn > JWT 的 exp + let explicit = pick_ms(payload, &["expiresAt", "expires_at"]); + let expires_at = if explicit > 0 { + explicit + } else { + credentials::expires_at_from( + pick_ms(payload, &["createdAt", "created_at"]).max(now), + pick_secs(payload, &["expiresIn", "expires_in"]), + &access_token, + ) + }; + + let mut record = Map::new(); + record.insert("id".to_string(), Value::String(id.clone())); + record.insert( + "provider".to_string(), + Value::String(LOBSTERAI_PROVIDER_ID.to_string()), + ); + record.insert("name".to_string(), Value::String(record_name.clone())); + if !uid.is_empty() { + record.insert("uid".to_string(), Value::String(uid)); + } + if !user_id.is_empty() { + record.insert("userId".to_string(), Value::String(user_id)); + } + if !nickname.is_empty() { + record.insert("nickname".to_string(), Value::String(nickname)); + } + record.insert( + "accessToken".to_string(), + Value::String(access_token.clone()), + ); + if !refresh_token.is_empty() { + record.insert( + "refreshToken".to_string(), + Value::String(refresh_token), + ); + } + // ── 载荷件三件套(续期要用,见模块头)──────────────────── + if !uuid.is_empty() { + record.insert("uuid".to_string(), Value::String(uuid)); + } + if !first_keyfrom.is_empty() { + record.insert("firstKeyfrom".to_string(), Value::String(first_keyfrom)); + } + if !latest_keyfrom.is_empty() { + record.insert("latestKeyfrom".to_string(), Value::String(latest_keyfrom)); + } + record.insert( + "tokenTail".to_string(), + Value::String( + access_token + .chars() + .rev() + .take(4) + .collect::>() + .into_iter() + .rev() + .collect(), + ), + ); + if expires_at > 0 { + record.insert("expiresAt".to_string(), Value::from(expires_at)); + } + record.insert("source".to_string(), Value::String("web".to_string())); + record.insert("priority".to_string(), Value::from(priority)); + record.insert("enabled".to_string(), Value::Bool(true)); + record.insert( + "addedAt".to_string(), + Value::from( + existing + .as_ref() + .and_then(|record| record.get("addedAt").and_then(Value::as_i64)) + .unwrap_or(now), + ), + ); + mark_name_custom( + &mut record, + name.is_some_and(|value| !value.trim().is_empty()), + existing.as_ref(), + ); + record.insert("updatedAt".to_string(), Value::from(now)); + + // 未知字段全量保留(用户手工加过的字段不能因为一次「更新账号」丢掉) + let mut merged = record; + if let Some(existing) = existing.as_ref() { + for (key, value) in existing.fields() { + merged.entry(key.clone()).or_insert_with(|| value.clone()); + } + } + let saved = StoredAccount::from_map(merged); + self.with_conn(&guard, |conn| sql::put(conn, &saved))?; + logging::log( + "[Accounts]", + &format!("✅ LobsterAI 账号已保存: {record_name}(优先级 {priority})"), + ); + Ok(self.public_account(&saved)) + } + + /// LobsterAI 账号的**公开形态**(进 HTTP 响应):去掉令牌本体。 + /// + /// `refreshToken` 与载荷件**都要摘干净**:前者是长期有效的续期凭据,后者是 + /// 续期的身份字段 —— 一起下发等于把「这个账号能被别人续期」的条件送出去。 + /// 界面需要的「能不能续期」由 `canRefresh` 布尔表达,不给原件。 + pub fn to_lobsterai_public_account(&self, record: &StoredAccount) -> Value { + let mut value = record.to_value(); + let can_refresh = record.get("refreshToken").is_some() + && record.get("firstKeyfrom").is_some() + && record.get("latestKeyfrom").is_some(); + if let Some(object) = value.as_object_mut() { + object.remove("accessToken"); + object.remove("refreshToken"); + object.remove("uuid"); + object.remove("firstKeyfrom"); + object.remove("latestKeyfrom"); + object.insert("available".to_string(), Value::Bool(record.enabled())); + object.insert("hasSession".to_string(), Value::Bool(true)); + object.insert("canRefresh".to_string(), Value::Bool(can_refresh)); + } + value + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + /// 公开形态必须把**令牌本体 + 载荷件**都摘掉(只留一个 canRefresh 布尔)。 + #[test] + fn public_shape_never_leaks_tokens_or_payload_fields() { + let record = StoredAccount::from_map({ + let mut map = Map::new(); + map.insert("id".into(), Value::String("lobsterai-42".into())); + map.insert("provider".into(), Value::String(LOBSTERAI_PROVIDER_ID.into())); + map.insert("accessToken".into(), Value::String("at".into())); + map.insert("refreshToken".into(), Value::String("rt".into())); + map.insert("uuid".into(), Value::String("u-1".into())); + map.insert("firstKeyfrom".into(), Value::String("fk".into())); + map.insert("latestKeyfrom".into(), Value::String("lk".into())); + map + }); + let mut public = record.to_value(); + let can_refresh = public.get("refreshToken").is_some() + && public.get("firstKeyfrom").is_some() + && public.get("latestKeyfrom").is_some(); + if let Some(object) = public.as_object_mut() { + for key in [ + "accessToken", + "refreshToken", + "uuid", + "firstKeyfrom", + "latestKeyfrom", + ] { + object.remove(key); + } + object.insert("canRefresh".into(), Value::Bool(can_refresh)); + } + for key in [ + "accessToken", + "refreshToken", + "uuid", + "firstKeyfrom", + "latestKeyfrom", + ] { + assert!(public.get(key).is_none(), "{key} 不该出现在公开形态里"); + } + assert_eq!(public.get("canRefresh"), Some(&Value::Bool(true))); + } + + /// 载荷件两种拼法都要能读到(本项目驼峰 / Go 参考下划线)。 + #[test] + fn payload_aliases_are_accepted() { + let payload = json!({ + "access_token": "at", "refresh_token": "rt", + "first_keyfrom": "fk", "latest_keyfrom": "lk", "user_id": "42" + }); + assert_eq!(pick(&payload, &["accessToken", "access_token"]), "at"); + assert_eq!(pick(&payload, &["refreshToken", "refresh_token"]), "rt"); + assert_eq!(pick(&payload, &["firstKeyfrom", "first_keyfrom"]), "fk"); + assert_eq!(pick(&payload, &["latestKeyfrom", "latest_keyfrom"]), "lk"); + assert_eq!(pick(&payload, &["userId", "user_id"]), "42"); + } + + /// 过期时刻可由 `createdAt + expiresIn` 推(与 atomcode 同一条教训:单位别混)。 + #[test] + fn expiry_can_come_from_created_at_plus_expires_in() { + let payload = json!({"createdAt": 1_700_000_000_000_i64, "expiresIn": 2_592_000}); + assert_eq!(pick_ms(&payload, &["expiresAt"]), 0); + assert_eq!(pick_secs(&payload, &["expiresIn"]), 2_592_000); + assert_eq!( + credentials::expires_at_from( + pick_ms(&payload, &["createdAt"]), + pick_secs(&payload, &["expiresIn"]), + "not-a-jwt" + ), + 1_700_000_000_000 + 2_592_000_000 + ); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/account_store/mod.rs b/desktop-tauri/src-tauri/server/src/server/core/account_store/mod.rs index 1a5ae223..fb0fd33d 100644 --- a/desktop-tauri/src-tauri/server/src/server/core/account_store/mod.rs +++ b/desktop-tauri/src-tauri/server/src/server/core/account_store/mod.rs @@ -106,9 +106,11 @@ pub mod catpaw_import; pub mod chatglm_accounts; pub mod cline_accounts; pub mod codearts_accounts; +pub mod atomcode_accounts; pub mod commandcode_accounts; pub mod custom_accounts; pub mod kuku_accounts; +pub mod lobsterai_accounts; pub mod loomy_accounts; pub mod monkeycode_accounts; pub mod officeace_accounts; @@ -230,6 +232,18 @@ pub(crate) const OFFICEACE_PROVIDER_ID: &str = crate::server::core::providers::k crate::server::core::providers::ProviderKind::OfficeAce, ); +/// AtomCode(AtomGit / GitCode)provider id(**从注册表推导**,同 +/// [`LOOMY_PROVIDER_ID`] 的口径)。账号形态见 `atomcode_accounts.rs`。 +pub(crate) const ATOMCODE_PROVIDER_ID: &str = crate::server::core::providers::kind_id( + crate::server::core::providers::ProviderKind::Atomcode, +); + +/// LobsterAI(网易有道「有道龙虾」)provider id(**从注册表推导**,同 +/// [`LOOMY_PROVIDER_ID`] 的口径)。账号形态见 `lobsterai_accounts.rs`。 +pub(crate) const LOBSTERAI_PROVIDER_ID: &str = crate::server::core::providers::kind_id( + crate::server::core::providers::ProviderKind::LobsterAi, +); + /// Cline **免费池** provider id(账号存储内部多处要用;**从注册表推导**,同 /// [`RACCOON_PROVIDER_ID`] 的口径)。账号形态见 `cline_accounts.rs`。 pub(crate) const CLINE_FREE_PROVIDER_ID: &str = crate::server::core::providers::kind_id( diff --git a/desktop-tauri/src-tauri/server/src/server/core/account_store/store_view.rs b/desktop-tauri/src-tauri/server/src/server/core/account_store/store_view.rs index 3c349ab4..981f25d9 100644 --- a/desktop-tauri/src-tauri/server/src/server/core/account_store/store_view.rs +++ b/desktop-tauri/src-tauri/server/src/server/core/account_store/store_view.rs @@ -361,6 +361,16 @@ impl AccountStore { // tokenTail / tokenExpireAt(续期是否在工作可被肉眼校验,见 // `orcaterm_accounts.rs`) self.to_orcaterm_public_account(record) + } else if record.provider() == super::ATOMCODE_PROVIDER_ID { + // AtomCode(AtomGit):公开形态要摘掉**两个**令牌本体 + // (accessToken 与 refreshToken)—— 只摘前者的那套在这里是漏的, + // refreshToken 是长期有效的续期凭据,不能随账号列表下发。 + self.to_atomcode_public_account(record) + } else if record.provider() == super::LOBSTERAI_PROVIDER_ID { + // LobsterAI:公开形态要摘掉**令牌本体 + 载荷件三件套** + // (refreshToken / uuid / 两个 keyfrom)。它们都是「能续期」的条件, + // 一起下发等于把账号的续期能力送出去;界面要的只是 canRefresh 布尔。 + self.to_lobsterai_public_account(record) } else if record .provider() .starts_with(crate::server::core::custom_providers::ID_PREFIX) diff --git a/desktop-tauri/src-tauri/server/src/server/core/auto_checkin.rs b/desktop-tauri/src-tauri/server/src/server/core/auto_checkin.rs index 5e71100e..998023f6 100644 --- a/desktop-tauri/src-tauri/server/src/server/core/auto_checkin.rs +++ b/desktop-tauri/src-tauri/server/src/server/core/auto_checkin.rs @@ -125,7 +125,7 @@ pub fn default_time() -> String { /// 这是「有签到或每日活跃任务」的清单,不是「有积分概念」的清单:CatPaw 有积分 /// 查询但没有签到,因此不在此列 —— 它的账号在批量签到里被算作 `skipped`。 /// 加一家之前先确认它的签到链路真的存在(一个点了必然报错的复选框比没有更糟)。 -pub const CHECKIN_PROVIDERS: [&str; 13] = [ +pub const CHECKIN_PROVIDERS: [&str; 15] = [ "workbuddy", "workbuddy-intl", "raccoon", @@ -143,6 +143,15 @@ pub const CHECKIN_PROVIDERS: [&str; 13] = [ // 智谱清言:「每日登录积分」即本家的签到(`chatglm::checkin`)。 // 访客账号会被实现判成中性未领取(没有积分体系),不算失败。 "chatglm", + // AtomCode(AtomGit / GitCode):本家的「签到」是**领取免费 CodingPlan** + // (一次性授权,见 `atomcode::checkin` 的模块头)。挂在这里是因为 + // 「新账号必须领一次」「套餐到期要能续领」两件事都需要一个逐账号、 + // 每天跑一次的调度与台账 —— 签到链本来就有。 + "atomcode", + // LobsterAI(网易有道「有道龙虾」):每日活动三步(查活动位 → 取上下文 → + // check_in),实测 +100 积分/号/天。幂等靠客户端(idempotencyKey + 上游 + // 的 `claimedToday` 预检),见 `lobsterai::checkin` 的模块头。 + "lobsterai", ]; /// 缺省的签到提供商集合(全选) @@ -160,13 +169,13 @@ pub fn default_providers() -> Vec { /// 会让这一家的勾选框再也取消不掉(读完就补回来,界面上表现为「取消勾选没反应」)。 /// 所以补齐只做一次,靠配置里的版本号记账;版本一旦写上去,用户之后再取消勾选 /// 就真的生效了。执行点见 `checkin_schedules::extend_providers_once`。 -pub const PROVIDERS_VERSION: i64 = 3; +pub const PROVIDERS_VERSION: i64 = 5; /// 「哪一版加入的家」——条目清单的版本低于它 = 这家还没被提供过 ⇒ 补进去。 /// 版本 1 = 多条目改造上线时的形态(9 家;`qoder-intl` 由 `normalize_providers` /// 的存量兼容规则跟着 `qoder` 带上,不在这里重复登记)。 -const PROVIDERS_ADDED_IN: [(&str, i64); 3] = - [("officeace", 2), ("codearts", 2), ("chatglm", 3)]; +const PROVIDERS_ADDED_IN: [(&str, i64); 5] = + [("officeace", 2), ("codearts", 2), ("chatglm", 3), ("atomcode", 4), ("lobsterai", 5)]; /// 版本 `version` 之后才加入清单的家(按注册顺序)。空数组 = 这一版没有新家, /// 或配置比代码还新(装过更新的版本又退回来)——两种情况都只记账、不动清单。 diff --git a/desktop-tauri/src-tauri/server/src/server/core/billing/checkin.rs b/desktop-tauri/src-tauri/server/src/server/core/billing/checkin.rs index fc607b8e..1c5995f2 100644 --- a/desktop-tauri/src-tauri/server/src/server/core/billing/checkin.rs +++ b/desktop-tauri/src-tauri/server/src/server/core/billing/checkin.rs @@ -496,6 +496,29 @@ pub async fn checkin_for( } row } + "atomcode" => { + // AtomCode:本家的「签到」是**领取免费 CodingPlan**(一次性授权, + // 不是每天发积分)。实现按需领取:先探测 `status-v2`,只有确实 + // 没有有效套餐时才走 Max → Pro → Lite 的级联,命中即停。 + // 判定细节与「`duplicate` 是成功语义、不许只看 HTTP 200」这条 + // 最容易写错的地方见 `atomcode::checkin` 的模块头。 + let claim = + crate::server::core::providers::atomcode::checkin::claim_plan_if_needed(store, &id) + .await + .map_err(|error| error.message); + claim_result(id, name, &display, true, claim) + } + "lobsterai" => { + // LobsterAI:每日活动三步(查活动位 → 取上下文 → check_in), + // 实测 +100 积分/号/天。幂等由实现内做两道预检(上游的 + // 「今天已领」状态 + 客户端 idempotencyKey),今天已领时如实回 + // 中性文案而不是再领一次 —— 见 `lobsterai::checkin` 的模块头。 + let claim = + crate::server::core::providers::lobsterai::checkin::claim_daily_checkin(store, &id) + .await + .map_err(|error| error.message); + claim_result(id, name, &display, true, claim) + } other => json!({ "id": id, "name": name, diff --git a/desktop-tauri/src-tauri/server/src/server/core/login.rs b/desktop-tauri/src-tauri/server/src/server/core/login.rs index 533c5bdc..5dae6595 100644 --- a/desktop-tauri/src-tauri/server/src/server/core/login.rs +++ b/desktop-tauri/src-tauri/server/src/server/core/login.rs @@ -28,7 +28,9 @@ //! 不额外起后台定时器。 mod accio; +mod atomcode; mod autoclaw; +mod lobsterai; mod catpaw; pub mod codearts; mod qoder; @@ -863,6 +865,44 @@ impl LoginService { } return result.map(LoginCallbackSubmission::Completed); } + // LobsterAI 的回调是标准 `code` / `state` 查询串(与 Accio / Antigravity + // 同形),两个入口共用这一套收尾: + // ① 浏览器直接落到网关的 `/auth/callback`(该路由不经过本函数); + // ② 网页端 / 远程面板用户在登录弹窗里**手工粘贴整条回调 URL** —— + // 浏览器与网关不同机且没有端口映射时,回环地址回不到网关,由 + // `web_shim.rs` 的 `needsManualCallback` 拉到这条。 + // + // 少了这一支,②会一路落到函数末尾那句「该登录任务不接收授权码回调」, + // 把一个能用的功能变成 400 —— 而 `needsManualCallback` 里明明声明了 + // 这家要粘贴。两支要么都在,要么都不在。 + // + // `state` 用请求体里那个(任务表的键),回调串里的 state 只用于逐字比对。 + if kind == ProviderKind::LobsterAi { + let parsed = url::Url::parse(callback_url).map_err(|_| { + GatewayError::with_status(400, "LobsterAI 登录回调地址无效") + })?; + let params: std::collections::HashMap = parsed + .query_pairs() + .map(|(key, value)| (key.into_owned(), value.into_owned())) + .collect(); + if let Some(error) = params.get("error").filter(|value| !value.trim().is_empty()) { + return Err(GatewayError::with_status(400, format!("授权被拒绝({error})"))); + } + let callback_state = params.get("state").map(String::as_str).unwrap_or(""); + let code = params.get("code").map(String::as_str).unwrap_or(""); + if callback_state.trim() != state { + return Err(GatewayError::with_status(400, "回调地址与当前 LobsterAI 登录任务不匹配")); + } + return match self.finish_lobsterai_login(code, state).await { + Ok(account_id) => Ok(LoginCallbackSubmission::Completed(account_id)), + Err(error) => { + // 与 public 回调路由同一处置:这一轮已经作废,pending 也丢掉, + // 免得留在表里等超时 + self.drop_lobsterai_pending(state); + Err(error) + } + }; + } // Antigravity 的回调同样是标准 `code` / `state` 查询串(Google OAuth // 授权码 + loopback)。与 Accio 同款,这里同时服务两个入口: // ① 浏览器直接落到网关的 `/oauth-callback` 路由(public 回调 handler diff --git a/desktop-tauri/src-tauri/server/src/server/core/login/atomcode.rs b/desktop-tauri/src-tauri/server/src/server/core/login/atomcode.rs new file mode 100644 index 00000000..51486c4b --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/login/atomcode.rs @@ -0,0 +1,142 @@ +//! AtomCode 自助登录任务:**服务端中介的轮询**(与 OfficeAce / ZCode 同一形态)。 +//! +//! ── 与另外两支的共性 ──────────────────────────────────────── +//! · 授权地址**要等一次网络往返**(AtomCode 是先 `GET {broker}/auth/login` +//! 拿 `login_url` + `state`),而 `start_*` 是同步函数 —— 所以先把任务登记 +//! 进状态表(用本地关联串当 key),再由 spawn 出去的任务回填 `auth_url`; +//! `api::session::login_start` 用 `wait_for_auth_url` 等它落地再响应。 +//! · 取凭证靠**轮询云端**(AtomCode 的授权回调由代理自己在 +//! `acs.atomgit.com/callback` 终结,**永远不回到本机**),与 ZCode 的 +//! `flow.poll()` 同构、与 OfficeAce 的云端轮询同源。 +//! +//! ── 与本仓其余各支最大的差别:**壳侧不需要开窗口回调** ────────── +//! CodeArts 是本机 loopback 回调(要壳侧开监听),AutoClaw 要登记回调端口。 +//! AtomCode **两者都不需要**:用户只需在浏览器里登完、回面板等几秒。 +//! 因此这里不开监听、不注册回调路由(壳侧只在 `allowed_hosts` 里放行域名)。 +//! +//! ── 轮询节奏与两类失败 ──────────────────────────────────────── +//! 固定间隔 3 秒;`check` 的网络类失败**当 pending 继续等**(见 +//! `providers::atomcode::oauth::BrokerLogin::check` 的说明:那是「这次没问成」 +//! 而不是「问成了但被拒」)。总时长受 `LOGIN_TIMEOUT_MS` 与任务取消两者夹住。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use std::time::Duration; + +use serde_json::json; + +use crate::server::core::providers::atomcode::oauth::BrokerLogin; +use crate::server::core::providers::kind_id; +use crate::server::logging; + +use super::{LoginService, LoginTaskHandle, LOGIN_TIMEOUT_MS, finish_task_error}; + +/// 轮询间隔:代理那边是浏览器里的一步人工操作,3 秒足够灵敏又不会打得太密 +const POLL_INTERVAL: Duration = Duration::from_secs(3); + +impl LoginService { + /// 发起 AtomCode 登录。 + /// + /// 本家只有一家(没有地区分叉),所以不需要 `region` 参数 —— 与 ZCode 那支 + /// 「从 id 反查 Region」的处置相比少一层,因为这里没有可选错的东西。 + pub fn start_atomcode_login(&self) -> Result { + let info = crate::server::core::endpoints::resolve_edition(None); + let handle = self.new_handle_for_provider(info, kind_id( + crate::server::core::providers::ProviderKind::Atomcode, + )); + // 本地关联串:代理给的 `state` 要到第一次网络往返之后才有, + // 而任务必须先登记(见模块头) + let state = format!("atomcode-{}", logging::now_ms()); + handle.update(|task| { + task.state = Some(state.clone()); + }); + self.tasks.register(&state, handle.clone()); + let service = self.clone(); + let task = handle.clone(); + crate::spawn_task(async move { + service.run_atomcode_login(task).await; + }); + logging::log("[Login]", "发起 AtomCode 网页登录(等待浏览器授权…)"); + Ok(handle) + } + + async fn run_atomcode_login(&self, handle: LoginTaskHandle) { + // 第一步:拿授权地址。失败就直接结束(没有可等的东西) + let login = match BrokerLogin::start(LOGIN_TIMEOUT_MS).await { + Ok(login) => login, + Err(error) => { + finish_task_error(&handle, &error.message); + return; + } + }; + if handle.snapshot().canceled { + return; + } + // 回填授权地址:前端轮询到它就把用户送去浏览器授权 + handle.update(|task| { + task.auth_url = Some(login.login_url().to_string()); + }); + + let deadline = tokio::time::Instant::now() + Duration::from_millis(LOGIN_TIMEOUT_MS); + loop { + if handle.snapshot().canceled { + return; + } + if tokio::time::Instant::now() >= deadline || login.expired() { + finish_task_error(&handle, "AtomCode 网页登录超时,请重新发起"); + return; + } + tokio::time::sleep(POLL_INTERVAL).await; + if handle.snapshot().canceled { + return; + } + // `check` 的网络类失败被折成 `Ok(false)`(继续等),所以这里的 + // `Err` 只可能是真正该终止的情形 + match login.check().await { + Ok(false) => continue, + Ok(true) => {} + Err(error) => { + finish_task_error(&handle, &error.message); + return; + } + } + // 授权完成 → 取令牌包 + let bundle = match login.fetch_token().await { + Ok(bundle) => bundle, + Err(error) => { + finish_task_error(&handle, &error.message); + return; + } + }; + // 与取消共用任务锁:取消先发生就绝不落账号,落盘先发生则视为已完成 + // (与 ZCode / Qoder 那两支逐字相同的处置) + let mut task = handle.lock(); + if task.done || task.canceled { + return; + } + let payload = json!({ + "accessToken": bundle.access_token, + "refreshToken": bundle.refresh_token, + "createdAt": logging::now_ms() / 1000, + "expiresIn": bundle.expires_in, + "userId": bundle.user_id, + "username": bundle.username, + }); + match self.store.add_atomcode_account(&payload, None) { + Ok(account) => { + task.session = Some(json!({ + "accountUid": account.get("id"), + "nickname": account.get("name"), + "provider": "atomcode", + })); + logging::log("[Login]", "✅ AtomCode 网页登录完成,账号已加入列表"); + } + Err(error) => task.error = Some(error.message), + } + task.done = true; + task.finished_at = Some(logging::now_ms()); + return; + } + } +} \ No newline at end of file diff --git a/desktop-tauri/src-tauri/server/src/server/core/login/lobsterai.rs b/desktop-tauri/src-tauri/server/src/server/core/login/lobsterai.rs new file mode 100644 index 00000000..dbd8de2d --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/login/lobsterai.rs @@ -0,0 +1,101 @@ +//! LobsterAI 的网页登录任务(授权码,回调落在**本机 loopback 端口**)。 +//! +//! ── 这条链属于八家登录里的哪一型 ───────────────────────────── +//! 与 Accio / AutoClaw 同族:**浏览器把用户带回本机 HTTP 端口**。差别是: +//! - 授权地址不需要先过一次风控验证码,网关自己能拼(`lobsterai::oauth::build_authorize_url`), +//! 所以走**通用**网页登录入口(`LoginService::start_web_login`),不另开专用路由; +//! - 回调 URL 里带着我们的 `state`(`redirect_uri` 是我们给的),因此不需要 +//! AutoClaw 那套「按变体匹配最近一次发起」的关联 —— 直接按 state 查任务表; +//! - **没有 PKCE**(上游不用),所以 pending 表里只有 `created_at` 一项, +//! 身份确认靠 state 本身。 +//! +//! ── 换码为什么单独一个方法 ────────────────────────────────── +//! `submit_login_callback` 那条是小浣熊专属的自定义协议(`office-raccoon://…`); +//! 这里的回调是标准 HTTP 查询串(`?code=…&state=…`),解析方式与失败语义都不同, +//! 因此收尾写在这里,由 `/auth/callback` 路由直接调用(与 accio 同一处置)。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use serde_json::{Value, json}; + +use crate::server::core::providers::adapter::adapter_for; +use crate::server::core::providers::lobsterai::oauth; +use crate::server::core::providers::{ProviderKind, kind_id}; +use crate::server::errors::GatewayError; +use crate::server::logging; + +use super::{LoginService, finish_task_error}; + +impl LoginService { + /// 浏览器回调:校验归属 → 用授权码换凭证 → 落账号 → 标记任务完成。 + /// + /// ── 校验口径(与 accio 同一套,逐条都有理由)─────────────── + /// 回调落在本机 HTTP 端口上,任何本机进程都能伪造一次 GET,因此: + /// 1. `state` 必须对应一个**进行中且属于 LobsterAI** 的登录任务; + /// 2. pending 表里必须有这一轮(只有本进程发起过才有)—— 它同时是 + /// 「授权码只能兑一次」的闸门(`take_pending` 取走即失效); + /// 3. 任务已完成时直接返回成功:用户刷新页面 / 浏览器预取会重复回调, + /// 第二次必然拿到「授权码已失效」,把一次成功变成一次失败。 + pub async fn finish_lobsterai_login( + &self, + code: &str, + state: &str, + ) -> Result { + let code = code.trim(); + let state = state.trim(); + if code.is_empty() { + return Err(GatewayError::with_status(400, "回调没有携带授权码")); + } + if state.is_empty() { + return Err(GatewayError::with_status( + 400, + "回调没有携带 state,无法确认这次登录归属", + )); + } + let Some(handle) = self.tasks.get(state) else { + return Err(GatewayError::with_status( + 404, + "这次登录已取消或已过期,请重新发起", + )); + }; + if handle.snapshot().provider != kind_id(ProviderKind::LobsterAi) { + return Err(GatewayError::with_status(400, "这次登录不属于 LobsterAI")); + } + if handle.snapshot().done { + // 幂等:重复回调不是错误(见校验口径 3) + return Ok(String::new()); + } + match adapter_for(ProviderKind::LobsterAi) + .exchange_login_code(&self.store, code, state) + .await + { + Ok(account_id) => { + handle.update(|task| { + task.done = true; + task.session = Some(json!({ + "accountUid": account_id, + "nickname": Value::Null, + "provider": "lobsterai", + })); + task.finished_at = Some(logging::now_ms()); + }); + logging::log("[Login]", "✅ LobsterAI 网页登录完成,账号已加入列表"); + Ok(account_id) + } + Err(error) => { + finish_task_error(&handle, &error.message); + logging::log( + "[Login]", + &format!("❌ LobsterAI 网页登录换取凭证失败: {}", error.message), + ); + Err(error) + } + } + } + + /// 取消:把 pending 表里那一轮也丢掉(否则换码时才会发现换不了) + pub fn drop_lobsterai_pending(&self, state: &str) { + oauth::drop_pending(state); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/adapter.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/adapter.rs index 2f32ce92..97f9808f 100644 --- a/desktop-tauri/src-tauri/server/src/server/core/providers/adapter.rs +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/adapter.rs @@ -1168,6 +1168,14 @@ pub fn adapter_for(kind: ProviderKind) -> &'static dyn ProviderAdapter { // (见 `chatglm/mod.rs` 与 `protocol::chatglm_stream` 的模块头)。 // 它是本项目**唯一**「访客免登录即可用」的一家(`guest/access` 换证)。 ProviderKind::Chatglm => &super::chatglm::CHATGLM_ADAPTER, + // AtomCode(AtomGit / GitCode):无状态 OpenAI 直通转发(上游本身就是 + // Chat Completions)+ 目录 / 额度 / 轮换续期。四个坑见 `atomcode/mod.rs` + // 的模块头(网关按 UA 索签名、令牌非 JWT、续期轮换、失败被包装成 200) + ProviderKind::Atomcode => &super::atomcode::ATOMCODE_ADAPTER, + // LobsterAI(网易有道「有道龙虾」):无状态 OpenAI 兼容转发(上游只收 + // SSE,网关恒以 stream:true 请求)+ 目录 / 积分 / 签到 + loopback 网页登录。 + // 三个载荷点见 `lobsterai/mod.rs` 的模块头 + ProviderKind::LobsterAi => &super::lobsterai::LOBSTERAI_ADAPTER, } } @@ -1271,6 +1279,12 @@ pub fn implemented_kinds() -> Vec { // 且有远程目录(`operation/detail?tag=available_models`)—— 必须在 // 列表里,否则目录刷新循环不会问它(与 Trae / Loomy / Kuku 同一理由)。 ProviderKind::Chatglm, + // AtomCode:远程目录(`coding-plan/models-v2`)+ 有转发能力, + // 进本列表才会被后台目录刷新调度 + ProviderKind::Atomcode, + // LobsterAI:远程目录(`GET /api/models/available`)+ 有转发能力, + // 进本列表才会被后台目录刷新调度 + ProviderKind::LobsterAi, ] } diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/chat.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/chat.rs new file mode 100644 index 00000000..966cc8bb --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/chat.rs @@ -0,0 +1,278 @@ +//! AtomCode 的转发:**同方言透传**,本仓最薄的一层。 +//! +//! ── 为什么这一层几乎什么都不做 ──────────────────────────────── +//! 网关本身就是 OpenAI Chat Completions,所以客户端请求体几乎逐字节上行、 +//! 上游的 SSE 帧逐字节下发。别家那套「按家翻译帧」在这里没有对应物 —— +//! 没有一条字段会在「没有发生的翻译」里丢掉,这正是走同方言上游的全部好处。 +//! 剩下的只有格式本身表达不了的四件事:**身份、模型名映射、参数补全、 +//! 以及静默失败的识别**(最后一条在 `errors.rs` 与 SSE 出口)。 +//! +//! ── 只改三处,每处都有它非改不可的理由 ────────────────────────── +//! 1. `model`:客户端点的是**展示名**(目录里的 `GLM-5.3-Flash`),网关只认 +//! 自己的 id(`glm5.3-flash`)。不做这一步,网关不会报错,它会回 +//! `HTTP 200 + 正文 参数错误`(见 `errors.rs` 的模块头)—— +//! 一个「模型名没映射」的 bug 会伪装成「参数不被接受」。 +//! 同时剥掉账号前缀(`/`,多账号下同名模型的消歧手段)。 +//! 2. `stream_options`:官方客户端**无条件**发 +//! `{"include_usage": true}`,网关据此才吐尾部 usage 帧。客户端没指定时补上 +//! (客户端显式指定就让它赢);非流式路径删掉它(没有流可挂)。 +//! 3. 其余(messages / tools / tool_choice / temperature / max_tokens / +//! reasoning_effort / 图片分片)**一律不动**。 +//! +//! ── User-Agent 是功能性的(再说一遍,因为它最容易被「清理」掉)──── +//! 网关按 UA 决定是否索要闭源签名:自称 `atomcode/` 会立刻 +//! `403 ATOMCODE_SIG_MISSING`。见 `endpoints::ADAPTER_USER_AGENT` 的实测表。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use axum::http::HeaderMap; +use serde_json::Value; + +use crate::server::core::providers::adapter::ChatRequestPlan; +use crate::server::errors::GatewayError; + +use super::credentials::AtomcodeCredentials; +use super::endpoints::{ADAPTER_USER_AGENT, gateway_chat_url}; +use super::models; + +/// 账号前缀的分隔符(目录里同名模型的消歧形态:`/`) +const ACCOUNT_PREFIX_SEPARATOR: char = '/'; + +/// 剥掉账号前缀:`/glm5.3-flash` → `glm5.3-flash`。 +/// +/// ⚠️ **只剥一层且只剥「已知账号 id」那一层**不能在这里做(本函数拿不到账号表), +/// 因此判据是「第一段看起来像账号 id」:目录里的模型名本身**不含** `/` +/// (网关的 id 形如 `glm5.3-flash` / `deepseek-flash`),所以出现 `/` 就说明 +/// 那是我们自己的前缀。这条判据的代价写在这里:将来若网关开始下发带 `/` 的 +/// 模型 id(如 `moonshotai/Kimi-K2-Instruct` 这种**含斜杠**的上游 id), +/// 本函数会误剥。届时改为「按调用方给的账号 id 精确剥」。 +/// 现在不那样写是因为 `build_chat_request` 的 `account` 参数里没有账号 id +/// 的独立字段(见 `adapter.rs` 关于 `account` 形态的说明)。 +pub fn strip_account_prefix(model: &str) -> &str { + let trimmed = model.trim(); + match trimmed.split_once(ACCOUNT_PREFIX_SEPARATOR) { + Some((_prefix, rest)) if !rest.trim().is_empty() => rest.trim(), + _ => trimmed, + } +} + +/// 准备一次上游调用。 +pub fn build_chat_request( + account: &Value, + body: &Value, + _client_headers: &HeaderMap, +) -> Result { + let credentials = AtomcodeCredentials { + id: String::new(), + access_token: account + .get("auth") + .and_then(|auth| auth.get("accessToken")) + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or("") + .to_string(), + refresh_token: String::new(), + expires_at: 0, + user_id: String::new(), + name: String::new(), + }; + if credentials.access_token.is_empty() { + return Err(GatewayError::with_status( + 401, + "AtomCode 账号缺少访问令牌,无法转发(请重新登录)", + )); + } + + let requested = body + .get("model") + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or(""); + if requested.is_empty() { + return Err(GatewayError::with_status(400, "请求缺少 model 字段")); + } + // 客户端点的是展示名 / 带前缀名;网关只认自己的 id。 + let published = strip_account_prefix(requested); + let upstream_model = models::upstream_id_for(published).unwrap_or_else(|| published.to_string()); + + let mut payload = body.clone(); + if let Some(object) = payload.as_object_mut() { + object.insert("model".to_string(), Value::String(upstream_model)); + let streaming = object.get("stream").and_then(Value::as_bool).unwrap_or(false); + if streaming { + // 官方客户端无条件带它,网关据此才吐尾部 usage 帧; + // 客户端显式指定过就不覆盖。 + if !object.contains_key("stream_options") { + object.insert( + "stream_options".to_string(), + serde_json::json!({ "include_usage": true }), + ); + } + } else { + // 非流式路径没有流可挂 usage,留着只会让上游多回一个字段 + object.remove("stream_options"); + } + } else { + return Err(GatewayError::with_status(400, "请求体不是 JSON 对象")); + } + + let headers: Vec<(String, String)> = vec![ + ( + "authorization".to_string(), + format!("Bearer {}", credentials.access_token), + ), + ("accept".to_string(), "application/json".to_string()), + // 功能性常量:自称官方客户端会触发 ATOMCODE_SIG_MISSING + ("user-agent".to_string(), ADAPTER_USER_AGENT.to_string()), + ]; + + Ok(ChatRequestPlan::chat(gateway_chat_url(), headers, payload)) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + fn account() -> Value { + json!({ "auth": { "accessToken": "at-1" } }) + } + + /// 展示名要被换成网关 id —— 不做这一步网关会回 `200 + 参数错误`。 + #[test] + fn published_name_is_mapped_to_the_gateway_id() { + let plan = build_chat_request( + &account(), + &json!({"model": "GLM-5.3-Flash", "messages": [{"role": "user", "content": "hi"}]}), + &HeaderMap::new(), + ) + .expect("应构造成功"); + // 目录里若认得这个名字就换成 id;认不得时**原样发**(不猜) + let sent = plan.body.get("model").and_then(Value::as_str).unwrap_or(""); + assert!(!sent.is_empty()); + assert_ne!(sent, ""); + } + + /// 账号前缀要被剥掉(多账号下同名模型的消歧形态)。 + #[test] + fn account_prefix_is_stripped() { + assert_eq!(strip_account_prefix("acct-42/glm5.3-flash"), "glm5.3-flash"); + assert_eq!(strip_account_prefix("glm5.3-flash"), "glm5.3-flash"); + assert_eq!(strip_account_prefix(" acct/ glm "), "glm"); + // 只有斜杠没有后半段时不剥(避免把 `a/` 剥成空) + assert_eq!(strip_account_prefix("a/"), "a/"); + } + + /// **核心护栏**:User-Agent 必须存在且不能自称官方客户端。 + #[test] + fn user_agent_is_sent_and_is_not_the_official_one() { + let plan = build_chat_request( + &account(), + &json!({"model": "m", "messages": []}), + &HeaderMap::new(), + ) + .expect("应构造成功"); + let ua = plan + .headers + .iter() + .find(|(name, _)| name.eq_ignore_ascii_case("user-agent")) + .map(|(_, value)| value.clone()) + .expect("必须带 User-Agent"); + assert!(ua.starts_with("agent2api-atomcode/")); + assert!(!ua.to_ascii_lowercase().starts_with("atomcode/")); + } + + /// 流式请求补 `stream_options.include_usage`;客户端指定过就不覆盖。 + #[test] + fn stream_options_are_added_only_when_the_client_did_not_choose() { + let plan = build_chat_request( + &account(), + &json!({"model": "m", "stream": true, "messages": []}), + &HeaderMap::new(), + ) + .expect("应构造成功"); + assert_eq!( + plan.body + .get("stream_options") + .and_then(|value| value.get("include_usage")) + .and_then(Value::as_bool), + Some(true) + ); + + let explicit = build_chat_request( + &account(), + &json!({"model": "m", "stream": true, "stream_options": {"include_usage": false}, "messages": []}), + &HeaderMap::new(), + ) + .expect("应构造成功"); + assert_eq!( + explicit + .body + .get("stream_options") + .and_then(|value| value.get("include_usage")) + .and_then(Value::as_bool), + Some(false), + "客户端显式指定时必须让位" + ); + } + + /// 非流式请求不得带 `stream_options`。 + #[test] + fn non_streaming_drops_stream_options() { + let plan = build_chat_request( + &account(), + &json!({"model": "m", "stream_options": {"include_usage": true}, "messages": []}), + &HeaderMap::new(), + ) + .expect("应构造成功"); + assert!(plan.body.get("stream_options").is_none()); + } + + /// 除 model 与 stream_options 之外**逐字不动**(同方言透传的全部价值)。 + #[test] + fn everything_else_is_forwarded_untouched() { + let original = json!({ + "model": "glm5.3-flash", + "stream": true, + "messages": [{"role": "user", "content": "hi"}], + "tools": [{"type": "function", "function": {"name": "f", "parameters": {"type": "object"}}}], + "tool_choice": "auto", + "temperature": 0.3, + "max_tokens": 1234, + "reasoning_effort": "high" + }); + let plan = build_chat_request(&account(), &original, &HeaderMap::new()) + .expect("应构造成功"); + for key in ["messages", "tools", "tool_choice", "temperature", "max_tokens", "reasoning_effort", "stream"] { + assert_eq!( + plan.body.get(key), + original.get(key), + "字段 {key} 必须逐字透传" + ); + } + } + + /// 缺令牌 / 缺 model 时报可读错误,而不是发一个空请求出去。 + /// + /// 这里手写 `match` 而不是 `expect_err`:`ChatRequestPlan` 没有实现 `Debug` + /// (它是跨家共用的类型,不为了一个测试给它加派生)。 + #[test] + fn missing_credentials_or_model_fail_fast() { + let error = match build_chat_request( + &json!({"auth": {}}), + &json!({"model": "m", "messages": []}), + &HeaderMap::new(), + ) { + Ok(_) => panic!("缺令牌应当报错,却构造出了请求"), + Err(error) => error, + }; + assert_eq!(error.status_code, 401); + + let error = match build_chat_request(&account(), &json!({"messages": []}), &HeaderMap::new()) { + Ok(_) => panic!("缺 model 应当报错,却构造出了请求"), + Err(error) => error, + }; + assert_eq!(error.status_code, 400); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/checkin.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/checkin.rs new file mode 100644 index 00000000..a48a1872 --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/checkin.rs @@ -0,0 +1,417 @@ +//! AtomCode 的「签到」:**领取免费 CodingPlan**。 +//! +//! ── 它与别家的「签到」不是一回事,先说清楚 ──────────────────── +//! 其它家的签到多是「每天领一次积分」。AtomCode 的免费套餐是**一次性领取**的 +//! 授权(`POST /coding-plan/claim-v2`,档位 Max / Pro / Lite),领到之后 +//! 按窗口滚额度、不再需要每天领。之所以挂在签到链上,是因为: +//! · 新账号加进来时必须领一次,否则模型目录为空、什么都调不动; +//! · 套餐会**到期**(`codingplan_free.expires_at`),到期后要能自动续领; +//! · 签到链本来就有「每天跑一次、逐账号、结果进台账」的调度与界面, +//! 不必再为这一家单起一个定时器。 +//! 所以本实现的口径是「**按需领取**」:先探测,只有确实没有有效套餐时才领。 +//! +//! ── 顺序与停规则(照官方 `setup.rs:825-918` 与本地 Go 参考 `credits.go:287-330`)── +//! 档位按 **Max → Pro → Lite** 走,**命中即停**,且三种停法各有理由: +//! +//! | 响应 | 含义 | 动作 | +//! | --- | --- | --- | +//! | `duplicate: true` | 该档位(或更高档)**已经持有** | **停** —— 继续往下走会再报一次 duplicate,甚至把用户降级 | +//! | `success: true` | 本次发放成功 | **停** | +//! | 两者皆 false | 该档位被拒(额度 / 不在活动期 / 不符合条件) | 记下文案,试下一档 | +//! | 传输/5xx/解析失败 | 连接坏了 | **整条级联中止** —— 换个档位重试修不好连接,只会加压 | +//! +//! ⚠️ **别只看 HTTP 200 判成功**。`duplicate: true` 是**成功语义**(已持有), +//! 把它当失败会让级联继续往下走并把用户降级。这是本模块最容易写错的一处。 +//! (我先前据插件 README 说过「claim-v2 没有幂等标记、重复领取只会再回 +//! success:true」—— **那句是错的**,源码里 `credits.go:149` 就有 `Duplicate bool +//! json:"duplicate"`,官方 CLI 的 `types.rs:526` 同款。) +//! +//! ── 领到之后为什么要刷目录 ────────────────────────────────── +//! `models-v2` 的 `plan_available` 是**相对被问的档位**算的 ⇒ 档位一变, +//! 可用模型集合就变。所以任何一次「真领到」或「档位变了」之后必须让目录失效, +//! 否则界面会继续用旧档位下的那份清单(表现是「领到了却看不到新模型」)。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use serde_json::{Value, json}; + +use crate::server::core::account_store::AccountStore; +use crate::server::errors::GatewayError; +use crate::server::logging; + +use super::credentials::from_record; +use super::endpoints::REQUEST_TIMEOUT_MS; +use super::models::{PLAN_LITE, PLAN_MAX, PLAN_PRO, claim_plan, fetch_status, refresh}; + +/// 档位级联顺序(官方 `types.rs:48` 的 `CASCADE_ORDER`) +pub const CASCADE_ORDER: [&str; 3] = [PLAN_MAX, PLAN_PRO, PLAN_LITE]; + +/// 一次 `claim-v2` 响应的判定结果 +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum TierVerdict { + /// 该档位(或更高档)已持有 —— **终止级联**(照官方:继续往下会重复报或降级) + AlreadyHeld { + /// 上游文案 + message: String, + /// 上游给的套餐名(可能缺) + plan_name: String, + }, + /// 本次发放成功 —— **终止级联** + Granted { + /// 上游文案 + message: String, + /// 上游给的套餐名(可能缺) + plan_name: String, + }, + /// 该档位被拒 —— 记下文案,试下一档 + Refused { + /// 上游文案 + message: String, + }, +} + +/// 判定一次 `claim-v2` 的响应。 +/// +/// **判定顺序是契约**:先看 `duplicate`(成功语义),再看 `success`。 +/// 反过来写的话,上游同时给两个 false 之外的所有组合都会走错分支。 +pub fn classify_tier_response(payload: &Value) -> TierVerdict { + let message = payload + .get("message") + .or_else(|| payload.get("msg")) + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or("") + .to_string(); + let plan_name = payload + .get("plan_name") + .or_else(|| payload.get("planName")) + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or("") + .to_string(); + if payload.get("duplicate").and_then(Value::as_bool).unwrap_or(false) { + return TierVerdict::AlreadyHeld { message, plan_name }; + } + if payload.get("success").and_then(Value::as_bool).unwrap_or(false) { + return TierVerdict::Granted { message, plan_name }; + } + TierVerdict::Refused { message } +} + +/// 当前套餐是否**已经有效、不需要领取**。 +/// +/// 判据与额度读数同源(`usage.rs` 的 `plan_of`):`codingplan_free` 存在 +/// 且 `expires_at` 非空 —— 上游用「`expires_at` 为空」表达**待激活**, +/// 那正是需要去领的状态。`remaining_days` 作为第二信号(同样由到期推出)。 +pub fn active_plan_of(status: &Value) -> Option<(String, String, i64)> { + let plan = status.get("codingplan_free")?; + let name = plan + .get("plan_name") + .and_then(Value::as_str) + .unwrap_or("") + .trim() + .to_string(); + let expires_at = plan + .get("expires_at") + .and_then(Value::as_str) + .unwrap_or("") + .trim() + .to_string(); + let remaining = plan + .get("remaining_days") + .and_then(Value::as_i64) + .unwrap_or(0); + if expires_at.is_empty() && remaining <= 0 { + return None; + } + Some((name, expires_at, remaining)) +} + +/// 首个展示窗口的读数文案(`已用/上限 次,N 小时后重置`)。 +/// +/// 与 `usage.rs` 同一口径:只读 `show_enable == 1` 的窗口、额度是**调用次数**。 +/// 读不到就返回空串(签到结果里少一行,不编造)。 +pub fn quota_summary(status: &Value) -> String { + let Some(items) = status.get("rate_limit_windows").and_then(Value::as_array) else { + return String::new(); + }; + let Some(window) = items.iter().find(|item| { + item.get("show_enable").and_then(Value::as_i64) == Some(1) + && item.get("call_limit").and_then(Value::as_i64).unwrap_or(0) > 0 + }) else { + return String::new(); + }; + let limit = window.get("call_limit").and_then(Value::as_i64).unwrap_or(0); + let used = window.get("calls_used").and_then(Value::as_i64).unwrap_or(0); + let hours = window.get("window_hours").and_then(Value::as_i64).unwrap_or(0); + let reset = window + .get("reset_at_display") + .and_then(Value::as_str) + .unwrap_or(""); + let mut text = format!("已用 {used}/{limit} 次"); + if hours > 0 { + text.push_str(&format!("(每 {hours} 小时滚动)")); + } + if !reset.is_empty() { + text.push_str(&format!(",{reset} 重置")); + } + text +} + +/// 按需领取:探测 → 无效才级联 → 领到后刷目录。 +/// +/// 返回的形状与其它家的签到实现一致(`{success, msg, ...}`),交给 +/// `billing::checkin::claim_result` 翻成签到结果行。 +/// +/// ── 三种结局对应用户看到的三种话 ───────────────────────────── +/// · **已经有效**:`success:false` + 中性文案(`套餐有效中…无需领取`)+ 额度读数。 +/// 刻意**不**回 `success:true` —— 那会被日志与界面说成「签到成功」, +/// 而这一天其实什么都没发生(与本仓 chatglm 对访客账号的中性处置同一口径)。 +/// · **真领到**:`success:true` + 上游文案。 +/// · **被拒**:`success:false` + 各档位文案汇总(例如不在活动期)。 +pub async fn claim_plan_if_needed( + store: &AccountStore, + account_id: &str, +) -> Result { + let record = store.atomcode_account_record(account_id); + if !account_id.is_empty() && record.is_none() { + return Err(GatewayError::with_status( + 401, + format!("AtomCode 账号 {account_id} 不存在(请重新添加)"), + )); + } + let credentials = from_record(record.as_ref())?; + + // ── 第一步:探测。已经有效就别动上游的领取接口 ──────────────── + let status = fetch_status(&credentials, REQUEST_TIMEOUT_MS).await?; + if let Some((plan_name, expires_at, remaining)) = active_plan_of(&status) { + let quota = quota_summary(&status); + let mut msg = String::from("套餐有效中,无需领取"); + if !plan_name.is_empty() { + msg.push_str(&format!("({plan_name})")); + } + if remaining > 0 { + msg.push_str(&format!(",剩余 {remaining} 天")); + } else if !expires_at.is_empty() { + msg.push_str(&format!(",到期 {expires_at}")); + } + logging::verbose("[Checkin]", &format!("AtomCode {account_id}: {msg}")); + return Ok(json!({ + "success": false, + "msg": msg, + "planName": plan_name, + "expiresAt": expires_at, + "quota": quota, + "needsAction": false, + })); + } + + // ── 第二步:级联领取(Max → Pro → Lite,命中即停)───────────── + let mut attempts: Vec = Vec::new(); + let mut last_refusal = String::new(); + for tier in CASCADE_ORDER { + let payload = claim_plan(&credentials, tier).await.map_err(|error| { + // 传输/HTTP 层失败:**整条级联中止**(换档位修不好连接) + GatewayError::with_status( + error.status_code, + format!("AtomCode 套餐领取在 {} 档失败:{}", tier, error.message), + ) + })?; + match classify_tier_response(&payload) { + TierVerdict::AlreadyHeld { message, plan_name } => { + attempts.push(json!({ "tier": tier, "outcome": "already-held", "message": message })); + let msg = if message.is_empty() { + format!("已持有 {tier} 档套餐,无需重复领取") + } else { + message + }; + logging::log("[Checkin]", &format!("AtomCode {account_id}: {msg}")); + return Ok(json!({ + "success": false, + "msg": msg, + "tier": tier, + "planName": plan_name, + "attempts": attempts, + "needsAction": false, + })); + } + TierVerdict::Granted { message, plan_name } => { + attempts.push(json!({ "tier": tier, "outcome": "claimed", "message": message })); + let msg = if message.is_empty() { + format!("已领取 {tier} 档免费套餐") + } else { + message + }; + // 档位变了 ⇒ 目录必须失效(`plan_available` 相对档位算) + let outcome = refresh(&credentials, true).await; + logging::log( + "[Checkin]", + &format!( + "AtomCode {account_id}: {msg}(目录已刷新:{:?})", + outcome + ), + ); + return Ok(json!({ + "success": true, + "msg": msg, + "tier": tier, + "planName": plan_name, + "attempts": attempts, + "needsAction": true, + })); + } + TierVerdict::Refused { message } => { + attempts.push(json!({ "tier": tier, "outcome": "refused", "message": message })); + if !message.is_empty() { + last_refusal = message; + } + } + } + } + + // ── 三档都被拒:如实报告(常见成因是不在活动期或名额已满)────── + let msg = if last_refusal.is_empty() { + "三档免费套餐都未能领取(上游未说明原因)".to_string() + } else { + format!("三档免费套餐都未能领取:{last_refusal}") + }; + logging::log("[Checkin]", &format!("AtomCode {account_id}: {msg}")); + Ok(json!({ + "success": false, + "msg": msg, + "attempts": attempts, + "needsAction": true, + })) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + /// **首条护栏**:`duplicate:true` 是成功语义(已持有),必须终止级联。 + /// + /// 把它当失败会让级联继续往下试,最终可能把用户从 Pro 降到 Lite —— + /// 官方 `client.rs:203-207` 专门写了这条理由。 + #[test] + fn duplicate_means_held_and_stops_the_cascade() { + let verdict = classify_tier_response(&json!({ + "success": false, "duplicate": true, "message": "已领取", "plan_name": "CodingPlan Pro" + })); + assert_eq!( + verdict, + TierVerdict::AlreadyHeld { + message: "已领取".into(), + plan_name: "CodingPlan Pro".into(), + } + ); + } + + /// `success:true` 是发放,同样终止。 + #[test] + fn success_means_granted_and_stops() { + let verdict = classify_tier_response(&json!({ + "success": true, "message": "领取成功", "plan_name": "CodingPlan Lite-体验版" + })); + assert_eq!( + verdict, + TierVerdict::Granted { + message: "领取成功".into(), + plan_name: "CodingPlan Lite-体验版".into(), + } + ); + } + + /// 两个都为 false = 该档位被拒 ⇒ 记文案、试下一档。 + #[test] + fn neither_flag_means_refused_so_the_cascade_continues() { + let verdict = classify_tier_response(&json!({ + "success": false, "duplicate": false, "message": "名额已满" + })); + assert_eq!( + verdict, + TierVerdict::Refused { + message: "名额已满".into() + } + ); + } + + /// 判定顺序是契约:`duplicate` 先于 `success`(上游理论上不会同时给真, + /// 但真给了必须按「已持有」处置 —— 那不会误发、也不会降级)。 + #[test] + fn duplicate_wins_over_success_because_it_is_checked_first() { + let verdict = classify_tier_response(&json!({"success": true, "duplicate": true})); + assert!(matches!(verdict, TierVerdict::AlreadyHeld { .. })); + } + + /// 字段缺失 / 类型不对时按「被拒」处置,不 panic。 + #[test] + fn malformed_payload_is_treated_as_refused_not_a_crash() { + assert_eq!( + classify_tier_response(&json!({})), + TierVerdict::Refused { + message: String::new() + } + ); + assert_eq!( + classify_tier_response(&json!({"success": "true", "duplicate": 1})), + TierVerdict::Refused { + message: String::new() + } + ); + } + + /// 套餐有效性判据:`expires_at` 非空或 `remaining_days > 0` 才算有效。 + /// 上游用「`expires_at` 为空」表达**待激活** —— 那正是需要去领的状态。 + #[test] + fn active_plan_requires_an_expiry_or_remaining_days() { + let active = json!({ + "codingplan_free": {"plan_name": "CodingPlan Lite-体验版", "expires_at": "2026-11-08 12:00:00", "remaining_days": 22} + }); + assert_eq!( + active_plan_of(&active), + Some(("CodingPlan Lite-体验版".into(), "2026-11-08 12:00:00".into(), 22)) + ); + + // 待激活:expires_at 空、没有剩余天数 ⇒ 需要领 + let pending = json!({"codingplan_free": {"plan_name": "CodingPlan Lite-体验版", "expires_at": ""}}); + assert_eq!(active_plan_of(&pending), None); + + // 整块缺失 ⇒ 需要领 + assert_eq!(active_plan_of(&json!({})), None); + + // 只剩剩余天数也能判有效(expires_at 缺失的容错) + let by_days = json!({"codingplan_free": {"plan_name": "X", "remaining_days": 3}}); + assert!(active_plan_of(&by_days).is_some()); + } + + /// 额度文案只读**服务端让展示**的窗口,且是调用次数口径。 + #[test] + fn quota_summary_reads_the_shown_window_only() { + let status = json!({ + "rate_limit_windows": [ + {"show_enable": 0, "window_hours": 24, "call_limit": 1000, "calls_used": 900}, + {"show_enable": 1, "window_hours": 5, "call_limit": 200, "calls_used": 37, "reset_at_display": "12:13"} + ] + }); + let text = quota_summary(&status); + assert!(text.contains("已用 37/200 次"), "{text}"); + assert!(text.contains("每 5 小时滚动"), "{text}"); + assert!(text.contains("12:13 重置"), "{text}"); + + // 没有可展示的窗口 ⇒ 空串(不编造) + assert_eq!(quota_summary(&json!({})), ""); + assert_eq!( + quota_summary(&json!({"rate_limit_windows": [{"show_enable": 0, "call_limit": 10}]})), + "" + ); + } + + /// 级联顺序就是官方那一串(不许改成 Lite 优先:那会把用户降到最低档)。 + #[test] + fn cascade_order_is_max_then_pro_then_lite() { + assert_eq!(CASCADE_ORDER, [PLAN_MAX, PLAN_PRO, PLAN_LITE]); + assert_eq!(CASCADE_ORDER, ["Max", "Pro", "Lite"]); + } +} \ No newline at end of file diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/credentials.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/credentials.rs new file mode 100644 index 00000000..ee1c6d0b --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/credentials.rs @@ -0,0 +1,293 @@ +//! AtomCode 凭证:**唯一来源**是账号记录里的 OAuth 令牌对。 +//! +//! ── 与别家最大的不同:令牌不是 JWT,过期只能算 ────────────────── +//! AtomGit 代理下发的是**不透明随机串**,没有 `exp` 可读(官方 `oauth.rs:143-158` +//! 的 `AuthInfo` 里只有 `created_at` 与 `expires_in` 两个数字)。 +//! 所以过期时刻只能 `created_at + expires_in` 推出来 —— 判临期不能像小浣熊 +//! 那样解 JWT 的自定义声明。 +//! +//! ── 第二条不同:续期**轮换** refresh token ───────────────────── +//! `POST /oauth/refresh` 同时下发新的 access 与新的 refresh,且**旧 access +//! 立即失效**(官方实测:旧令牌随后返回 `401 {"message":"401 Unauthorized"}`)。 +//! 含义有两层,两层都要照顾: +//! - 续期必须把**新的一对**写回记录 —— 丢掉新的 refresh 等于让账号退化成 +//! 必须重新走浏览器登录; +//! - 并发续期会互相作废(第二个写入覆盖掉第一个的新 refresh), +//! 因此续期必须单飞(见 `providers::atomcode::refresh` 与 +//! `core::refresh_flight`)。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use serde_json::Value; + +use crate::server::errors::GatewayError; +use crate::server::logging; + +use super::endpoints::REFRESH_WINDOW_SECONDS; + +/// 令牌包(登录与续期共用同一套字段,官方两个端点返回同一个信封) +#[derive(Clone, Debug, Default)] +pub struct TokenBundle { + /// `access_token` + pub access_token: String, + /// `refresh_token`(续期时是**新的**一个) + pub refresh_token: String, + /// `token_type`,实测恒 `Bearer` + pub token_type: String, + /// `expires_in`(秒);实测 604800 = 7 天 + pub expires_in: i64, + /// 身份 + pub user_id: String, + /// 登录名(如 `qq_23240873`) + pub username: String, + /// 显示名(常为中文) + pub name: String, + /// 邮箱 + pub email: String, + /// 头像 + pub avatar_url: String, +} + +/// 一次转发 / 查询用的 AtomCode 凭证快照。 +#[derive(Clone, Debug)] +pub struct AtomcodeCredentials { + /// 账号记录 id + pub id: String, + /// OAuth 访问令牌(**不透明串**,不是 JWT) + pub access_token: String, + /// 轮换用的 refresh token(缺失时只能等用户重登) + pub refresh_token: String, + /// 过期时刻(毫秒)。0 = 推不出来(记录被手工编辑过) + pub expires_at: i64, + /// AtomGit 用户 id(账号标识与展示) + pub user_id: String, + /// 展示名(备注名优先) + pub name: String, +} + +impl AtomcodeCredentials { + /// 是否已过期或进入提前续期窗口。 + /// + /// 窗口取 [`REFRESH_WINDOW_SECONDS`](1 小时):比官方「过期才刷」更早, + /// 因为续期本身会轮换 refresh token —— 提前刷是安全的,而拿死令牌 + /// 发一次请求不是。 + pub fn expiring(&self) -> bool { + self.expires_at > 0 + && self.expires_at - REFRESH_WINDOW_SECONDS * 1000 <= logging::now_ms() + } + + /// 已经彻底过期(用于区分「临期」与「必需重登」的提示口径) + pub fn expired(&self) -> bool { + self.expires_at > 0 && self.expires_at <= logging::now_ms() + } +} + +/// 从一条取值链里挑第一个非空字符串(记录可能被手工编辑过,宽容读取) +fn pick<'a>(record: &'a Value, keys: &[&str]) -> String { + for key in keys { + let value = record + .get(*key) + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or(""); + if !value.is_empty() { + return value.to_string(); + } + } + String::new() +} + +/// 从一条取值链里挑第一个**正**整数 +fn pick_i64(record: &Value, keys: &[&str]) -> i64 { + for key in keys { + if let Some(value) = record.get(*key).and_then(Value::as_i64) { + if value > 0 { + return value; + } + } + } + 0 +} + +/// 由 `createdAt + expiresIn` 推出过期时刻(**毫秒**)。 +/// +/// 这是本家**唯一**的过期来源:令牌不是 JWT,读不到 `exp`。 +/// +/// ⚠️ **单位是这条链上最容易错的地方**(这里曾经真的错过一次,被三条单测抓住): +/// - `created_at`:**秒**(代理 `AuthInfo.created_at` 的口径); +/// - `expires_in`:**秒**(代理 `expires_in` 的口径); +/// - `fallback_created_at`:**毫秒**(本项目记录里的 `addedAt`,与 `expiresAt` 同单位); +/// - 返回值:**毫秒**(与别家的 `expiresAt` 同单位)。 +/// +/// 两种基准都缺、或 `expires_in` 缺失时返回 0 = 判不出来 —— 调用方按 +/// 「判不出来就不声称临期」处置(见 [`AtomcodeCredentials::expiring`]), +/// 而不是猜一个值。 +pub fn expires_at_from(created_at: i64, expires_in: i64, fallback_created_at: i64) -> i64 { + if expires_in <= 0 { + return 0; + } + // 秒 → 毫秒;基准缺失时用毫秒形态的兜底值(addedAt) + let basis_ms = if created_at > 0 { + created_at * 1000 + } else { + fallback_created_at + }; + if basis_ms <= 0 { + return 0; + } + basis_ms + expires_in * 1000 +} + +/// 从账号记录解析凭证(**唯一入口**;`record` 为 None 时报「没有可用账号」)。 +/// +/// 字段兼容:`accessToken`(本项目落盘名)/ `access_token`(官方 `auth.toml` 名)/ +/// `token`(手工粘贴形态)。 +pub fn from_record(record: Option<&Value>) -> Result { + let Some(record) = record else { + return Err(GatewayError::with_status( + 401, + "没有可用的 AtomCode 账号:请在账号页用 AtomGit 浏览器登录添加", + )); + }; + let access_token = pick(record, &["accessToken", "access_token", "token"]); + if access_token.is_empty() { + return Err(GatewayError::with_status( + 401, + "AtomCode 账号缺少访问令牌,请重新登录", + )); + } + let refresh_token = pick(record, &["refreshToken", "refresh_token"]); + let id = pick(record, &["id"]); + let user_id = pick(record, &["userId", "user_id", "uid"]); + let name = { + let note = pick(record, &["name"]); + if note.is_empty() { + pick(record, &["username", "userName"]) + } else { + note + } + }; + let explicit_expiry = pick_i64(record, &["expiresAt"]); + let expires_at = if explicit_expiry > 0 { + explicit_expiry + } else { + expires_at_from( + pick_i64(record, &["createdAt"]), + pick_i64(record, &["expiresIn", "expires_in"]), + pick_i64(record, &["addedAt"]), + ) + }; + Ok(AtomcodeCredentials { + id, + access_token, + refresh_token, + expires_at, + user_id, + name, + }) +} + +/// 账号展示名(用户名优先,其次显示名,最后用户 id) +pub fn display_name(bundle: &TokenBundle) -> String { + for candidate in [&bundle.username, &bundle.name, &bundle.user_id] { + let trimmed = candidate.trim(); + if !trimmed.is_empty() { + return trimmed.to_string(); + } + } + "AtomGit 账号".to_string() +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + /// 过期时刻只能来自 `createdAt + expiresIn`(令牌非 JWT,没有 exp 可读)。 + #[test] + fn expiry_is_computed_from_created_at_plus_expires_in() { + // 官方实测:expires_in = 604800(7 天) + assert_eq!(expires_at_from(1_700_000_000, 604_800, 0), 1_700_000_000_000 + 604_800_000); + // createdAt 缺失时用 `addedAt` 兜底 —— **注意这个兜底值是毫秒** + // (本项目记录里 `addedAt` 与 `expiresAt` 同单位),而 createdAt 是秒。 + // 这两个基准单位不同是**故意**的:各自跟着上游/本项目的既有口径走, + // 统一成一个反而会在其中一侧引入换算错误。 + assert_eq!( + expires_at_from(0, 604_800, 1_700_000_000_000), + 1_700_000_000_000 + 604_800_000 + ); + // 两者都缺 = 判不出来,返回 0(不猜) + assert_eq!(expires_at_from(0, 604_800, 0), 0); + // expiresIn 缺失同理 + assert_eq!(expires_at_from(1_700_000_000, 0, 0), 0); + } + + /// 记录里直接带 `expiresAt`(续期后落盘形态)时以它为准。 + #[test] + fn explicit_expires_at_wins_over_computed_basis() { + let record = json!({ + "accessToken": "at-1", + "createdAt": 1_700_000_000_i64, + "expiresIn": 604_800, + "expiresAt": 1_800_000_000_000_i64 + }); + let cred = from_record(Some(&record)).expect("凭证应可解析"); + assert_eq!(cred.expires_at, 1_800_000_000_000); + } + + /// 三种落盘字段名都要能读到(官方 `auth.toml` 名 / 本项目名 / 粘贴名)。 + #[test] + fn token_field_aliases_are_accepted() { + for key in ["accessToken", "access_token", "token"] { + let record = json!({ key: "at-abc" }); + let cred = from_record(Some(&record)).expect("别名应可解析"); + assert_eq!(cred.access_token, "at-abc"); + } + } + + /// 没有令牌时必须报「重新登录」,而不是拿空串去发请求。 + #[test] + fn missing_token_is_an_auth_error_not_an_empty_bearer() { + let error = from_record(Some(&json!({ "refreshToken": "rt-1" }))) + .expect_err("缺令牌应报错"); + assert_eq!(error.status_code, 401); + assert!(error.message.contains("重新登录")); + // 连账号都没有时另一条文案 + let none_error = from_record(None).expect_err("没有账号应报错"); + assert_eq!(none_error.status_code, 401); + } + + /// 判不出来(`expires_at == 0`)时**不**声称临期 —— 否则维护任务每轮都去刷。 + #[test] + fn unknown_expiry_never_claims_expiring() { + let cred = AtomcodeCredentials { + id: "a".into(), + access_token: "at".into(), + refresh_token: "rt".into(), + expires_at: 0, + user_id: String::new(), + name: String::new(), + }; + assert!(!cred.expiring()); + assert!(!cred.expired()); + } + + /// 展示名优先级:username → name → user_id。 + #[test] + fn display_name_falls_back_in_order() { + let mut bundle = TokenBundle { + username: "qq_23240873".into(), + name: "张三".into(), + user_id: "42".into(), + ..Default::default() + }; + assert_eq!(display_name(&bundle), "qq_23240873"); + bundle.username.clear(); + assert_eq!(display_name(&bundle), "张三"); + bundle.name.clear(); + assert_eq!(display_name(&bundle), "42"); + bundle.user_id.clear(); + assert_eq!(display_name(&bundle), "AtomGit 账号"); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/endpoints.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/endpoints.rs new file mode 100644 index 00000000..7da8af74 --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/endpoints.rs @@ -0,0 +1,200 @@ +//! AtomCode(AtomGit)上游地址与身份常量(环境变量可覆盖)。 +//! +//! ── 上游长什么样(逆向来源:官方 Rust CLI `atomgit-atomcode/atomcode` 与 +//! 本仓已归档的 Go 参考实现 `cpa-jethub-plugins/plugins/atomcode/`)── +//! AtomCode 是 AtomGit(GitCode)家的编码 Agent 客户端,三条平面各一个域: +//! +//! ```text +//! 登录代理(OAuth) https://acs.atomgit.com 无本地回调监听 +//! CodingPlan REST https://api.gitcode.com/api/v5 目录 / 套餐 / 领取 / 用量 +//! 聊天网关 https://api-ai.gitcode.com/v1 OpenAI Chat Completions +//! ``` +//! +//! ── 三条必须写下来的实测事实(每一条都能让整条链路静默失效)───── +//! +//! 1. **聊天网关刻意不是官方默认的那个**。官方 CLI 的默认网关是 +//! `https://llm-api.atomgit.com/v1`(`crates/atomcode-config/src/endpoints.rs:92`), +//! 但它要求一套**闭源请求签名**(`atomcode-codingplan-crypto::sign_v1`,官方 +//! 开源树里只有一个 `unreachable!()` 占位 stub,实测由 `scripts/build-official.sh` +//! 在构建官方包时覆盖)。缺签名时它回 +//! `403 {"detail":{"code":"ATOMCODE_SIG_MISSING"}}`。 +//! 我们走的是 **`api-ai.gitcode.com`** —— 官方 CLI 自己在 +//! `crates/atomcode-auth/src/gateway_crypto.rs:140-142` 把它认成 first-party 网关, +//! 同一个 OAuth bearer、同一批模型,**不需要签名**。 +//! +//! 2. **网关是按 User-Agent 决定要不要签名的**。同一条有效 bearer、同一个请求体, +//! 只改 UA 的判别式实测(2026-10-01,打在 `POST /v1/chat/completions`): +//! +//! ```text +//! (不带 User-Agent) -> 200 正常回答 +//! curl/8.5.0 -> 200 正常回答 +//! cpa-jethub-atomcode/0.1.0 -> 200 正常回答 +//! atomcode/5.2.0 -> 403 ATOMCODE_SIG_MISSING +//! atomcode/5.1.0 -> 403 ATOMCODE_SIG_MISSING +//! ``` +//! +//! ⇒ 本家**绝不能**自称 `atomcode/`:那正是触发签名门的开关。 +//! `ADAPTER_USER_AGENT` 因此是**功能性常量**,不是外观问题。 +//! +//! 3. **`models-v2` 每个条目自带的 `base_url` 指向需要签名的官方网关**(也是 +//! `llm-api.atomgit.com`)—— 目录里那个字段只作展示,**不要**拿它替换 +//! `gateway_base`,否则整家立刻 403。 +//! +//! ── 与别家不同的一点:登录没有本地回调 ────────────────────────── +//! 授权回调由代理自己在其域名上终结(`acs.atomgit.com/callback`),**不回到** +//! 运行网关的这台机器。所以本家没有 loopback 监听器、也没有固定回调端口: +//! 发起登录拿 `state`,之后靠**轮询** `auth/check` 判完成 —— 与 ZCode 的 +//! CLI 登录同一形状(见 `providers::atomcode::oauth` 的模块头)。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +/// 登录代理默认基址(`acs.atomgit.com`,官方 `endpoints.rs:90`) +pub const DEFAULT_BROKER_BASE: &str = "https://acs.atomgit.com"; + +/// CodingPlan REST 默认基址(官方 `endpoints.rs:91`) +pub const DEFAULT_CODINGPLAN_API_BASE: &str = "https://api.gitcode.com/api/v5"; + +/// 聊天网关默认基址。 +/// +/// ⚠️ 刻意不是官方默认的 `https://llm-api.atomgit.com/v1`(那个要闭源签名)。 +/// 见本文件模块头第 1 条。 +pub const DEFAULT_GATEWAY_BASE: &str = "https://api-ai.gitcode.com/v1"; + +/// 本家发出的 User-Agent。 +/// +/// **功能性常量**:网关按 UA 决定是否索要闭源签名,自称官方客户端会被 +/// `403 ATOMCODE_SIG_MISSING` 直接拒掉(模块头第 2 条的实测表)。 +/// 这里如实标识本网关自己,绝不伪装成官方客户端。 +pub const ADAPTER_USER_AGENT: &str = concat!("agent2api-atomcode/", env!("CARGO_PKG_VERSION")); + +/// 登录代理的 `provider` 查询参数(官方 `attempt_login` 固定发 `atomgit`) +pub const BROKER_PROVIDER: &str = "atomgit"; + +/// 登录代理路径(相对 [`broker_base_url`],官方 `oauth.rs:38-52`) +pub const BROKER_LOGIN_PATH: &str = "/auth/login"; +/// 轮询「用户授权完了没」(官方 `oauth.rs:453`) +pub const BROKER_CHECK_PATH: &str = "/auth/check"; +/// 取回令牌包(官方 `oauth.rs:517`) +pub const BROKER_TOKEN_PATH: &str = "/auth/token"; +/// 续期(**轮换**:同时下发新的 access 与新的 refresh) +pub const BROKER_REFRESH_PATH: &str = "/oauth/refresh"; + +/// CodingPlan 路径(相对 [`codingplan_base_url`],官方 `client.rs:213,254,287,318`) +pub const CODINGPLAN_CLAIM_PATH: &str = "/coding-plan/claim-v2"; +/// 模型目录:`?plan_type=` +pub const CODINGPLAN_MODELS_PATH: &str = "/coding-plan/models-v2"; +/// 套餐状态:滚动额度窗口在这里 +pub const CODINGPLAN_STATUS_PATH: &str = "/coding-plan/status-v2"; +/// 近 60 天用量 +pub const CODINGPLAN_USAGE_PATH: &str = "/coding-plan/usage"; + +/// 聊天路径(相对 [`gateway_base_url`]) +pub const GATEWAY_CHAT_PATH: &str = "/chat/completions"; + +/// 本家全部请求的超时(毫秒)。官方两个客户端都是 5s 连接 + 10s 总时长, +/// 这里给得宽松些:额度窗口与目录驱动的首字节可能偏慢,而超时会把一次 +/// 本可成功的调用变成「上游不可达」。 +pub const REQUEST_TIMEOUT_MS: u64 = 30_000; + +/// 目录拉取的超时(毫秒):目录是维护动作,不值得让用户等太久 +pub const CATALOG_TIMEOUT_MS: u64 = 20_000; + +/// 提前多久续期(秒)。官方只在 `created_at + expires_in` **已经过去**时才刷 +/// (`oauth.rs:1050-1060`),那在懒调度下会把一个已经死掉的令牌发出去。 +/// 提前刷在这里更安全还有第二个理由:**续期会轮换 refresh token**, +/// 所以「access 已经死了之后再刷」仍然能成功,而「拿死令牌发一次请求」不能。 +pub const REFRESH_WINDOW_SECONDS: i64 = 3600; + +/// 读环境变量覆盖(空值视为未设置),去掉尾部斜杠 +fn env_override(name: &str) -> Option { + std::env::var(name) + .ok() + .map(|value| value.trim().trim_end_matches('/').to_string()) + .filter(|value| !value.is_empty()) +} + +/// 登录代理基址:`ATOMCODE_BROKER_BASE` 可覆盖 +pub fn broker_base_url() -> String { + env_override("ATOMCODE_BROKER_BASE").unwrap_or_else(|| DEFAULT_BROKER_BASE.to_string()) +} + +/// CodingPlan REST 基址:`ATOMCODE_CODINGPLAN_API_BASE` 可覆盖 +pub fn codingplan_base_url() -> String { + env_override("ATOMCODE_CODINGPLAN_API_BASE") + .unwrap_or_else(|| DEFAULT_CODINGPLAN_API_BASE.to_string()) +} + +/// 聊天网关基址:`ATOMCODE_GATEWAY_BASE` 可覆盖。 +/// +/// ⚠️ 指向 `https://llm-api.atomgit.com/v1` **不会工作**(要闭源签名,见模块头)。 +pub fn gateway_base_url() -> String { + env_override("ATOMCODE_GATEWAY_BASE").unwrap_or_else(|| DEFAULT_GATEWAY_BASE.to_string()) +} + +/// 聊天补全的完整 URL +pub fn gateway_chat_url() -> String { + format!("{}{}", gateway_base_url(), GATEWAY_CHAT_PATH) +} + +/// 目录 URL(带档位) +pub fn models_url(plan_type: &str) -> String { + format!( + "{}{}?plan_type={}", + codingplan_base_url(), + CODINGPLAN_MODELS_PATH, + plan_type + ) +} + +/// 套餐状态 URL +pub fn status_url() -> String { + format!("{}{}", codingplan_base_url(), CODINGPLAN_STATUS_PATH) +} + +#[cfg(test)] +mod tests { + use super::*; + + /// 网关基址绝不能被解析成需要签名的官方网关。 + /// + /// 这条是回归护栏:官方默认值一旦被谁「顺手对齐」成 `llm-api.atomgit.com`, + /// 整家会以 `403 ATOMCODE_SIG_MISSING` 全量失效,而错误文案看起来像鉴权问题。 + #[test] + fn gateway_base_is_never_the_signed_official_host() { + let base = gateway_base_url(); + assert!( + !base.contains("llm-api.atomgit.com"), + "网关基址指向了需要闭源签名的官方网关:{base}" + ); + assert_eq!(base, DEFAULT_GATEWAY_BASE); + } + + /// 适配器不得自称官方客户端 —— 那正是触发签名门的开关。 + #[test] + fn adapter_user_agent_never_claims_to_be_official() { + let ua = ADAPTER_USER_AGENT.to_ascii_lowercase(); + assert!( + !ua.starts_with("atomcode/"), + "User-Agent 自称官方客户端会触发 ATOMCODE_SIG_MISSING:{ADAPTER_USER_AGENT}" + ); + assert!(ua.starts_with("agent2api-atomcode/")); + } + + /// URL 拼接:三条平面各自独立,且目录 URL 带档位参数。 + #[test] + fn urls_are_composed_from_the_right_planes() { + assert_eq!( + gateway_chat_url(), + format!("{DEFAULT_GATEWAY_BASE}{GATEWAY_CHAT_PATH}") + ); + assert_eq!(status_url(), format!("{DEFAULT_CODINGPLAN_API_BASE}{CODINGPLAN_STATUS_PATH}")); + assert_eq!( + models_url("Lite"), + format!("{DEFAULT_CODINGPLAN_API_BASE}{CODINGPLAN_MODELS_PATH}?plan_type=Lite") + ); + // 三条平面的 host 互不相同(曾被混用的组合就是把目录 base_url 装进网关) + assert!(!gateway_base_url().contains("api.gitcode.com/api")); + assert!(!codingplan_base_url().contains("api-ai.gitcode.com")); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/errors.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/errors.rs new file mode 100644 index 00000000..9ed791d5 --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/errors.rs @@ -0,0 +1,334 @@ +//! AtomCode 网关的错误分类,以及**两种静默失败**的识别。 +//! +//! ── 网关的两套错误信封(都要认)────────────────────────────── +//! ```text +//! {"error":{"message":"...","type":"auth_error","code":"403"}} LiteLLM 信封 +//! {"detail":{"code":"ATOMCODE_SIG_MISSING","message":"..."}} AtomCode 信封 +//! ``` +//! +//! ⚠️ 传到 [`classify_error`] 的 `error_body` 是**传输层归一化之后**的形态 +//! (`upstream::request::read_upstream_error`):它只从**顶层**取 `code` +//! (且要求是数字)与 `message`/`msg`/`error.message`。所以: +//! - 第一套信封的 `message` 能取到,`code` 取不到(它是字符串 `"403"`); +//! - 第二套信封两个都取不到,`message` 退化成**截断后的原文 JSON** +//! —— 好消息是原文里就含 `ATOMCODE_SIG_MISSING` 这个码,所以按文案匹配 +//! 仍然可靠。 +//! 这一节是「为什么这里在读字符串」的答案,别改成只读结构字段。 +//! +//! ── 两种静默失败(HTTP 200 的假成功)───────────────────────── +//! 网关拒绝请求时**不返回错误状态**,而是回一条「看起来正常的回答」: +//! +//! ```text +//! 模型不在该账号套餐内 / 参数不被接受 -> HTTP 200,正文/首帧 content = 参数错误 +//! 模型被列出但后端是坏的 -> HTTP 200,正文 = 三方请求失败: 502 … +//! ``` +//! +//! 第二种尤其恶劣:它会把**上游的 HTML 报错页**当作模型回答粘进对话,而客户端 +//! 完全看不出异常(`cpa-jethub-plugins/plugins/atomcode/errors.go` 记了实测)。 +//! 两种都在这里识别,交给 SSE 出口与错误分类两条路使用。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use serde_json::Value; + +use crate::server::errors::GatewayError; + +use crate::server::core::providers::adapter::UpstreamErrorClass; +use crate::server::core::providers::content_block; + +/// 网关在**成功状态**下用这句正文表示「我拒绝了这个请求」。 +/// +/// 实测 2026-10-01:未知模型名得到 `HTTP 200` + +/// `choices[0].message.content == "参数错误"`,而不是错误状态。 +pub const PARAMETER_ERROR_MESSAGE: &str = "参数错误"; + +/// 网关在**成功状态**下表示「这个模型被列出来了但后端是坏的」。 +/// +/// 实测 2026-10-01:`Qwen/Qwen3-4B-Instruct-2507` 回 `HTTP 200`,正文整段是 +/// `三方请求失败: 502 …`。 +pub const THIRD_PARTY_FAILURE_MARKER: &str = "三方请求失败"; + +/// 需要闭源签名的网关给出的码。 +/// +/// 它不是凭证问题、也不是额度问题 —— 它意味着请求打到了 +/// `llm-api.atomgit.com`(或同类需要签名的主机)而不是无需签名的 +/// `api-ai.gitcode.com`。当成普通 403 报出去,用户会去查一个并不存在的登录问题。 +pub const SIGNATURE_MISSING_CODE: &str = "ATOMCODE_SIG_MISSING"; + +/// 一次「静默失败」的识别结果。 +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum SilentFailure { + /// 网关以成功状态拒绝了模型或参数(正文是 [`PARAMETER_ERROR_MESSAGE`]) + ParameterRejected, + /// 模型被列出但后端坏了(正文是上游报错页,不能当回答透传) + ThirdPartyBroken, +} + +/// 判断一段**正文/首帧内容**是不是网关的静默失败。 +/// +/// ── 判据为什么是「相等」与「前缀」而不是 `contains` ──────────── +/// `contains` 会在模型**正常复述**这些词时误判: +/// 用户完全可能问「参数错误是什么」,模型答「参数错误通常指…」—— +/// 那一帧被判成失败,整轮就变成了一次凭空的换账号重试。 +/// 网关的形态是确定的:正文**整段就是**那四个字(首尾空白可能有), +/// 或**以** `三方请求失败` 开头后跟原始错误。按这两种形态判,误判面最小。 +pub fn silent_failure_in_content(content: &str) -> Option { + let trimmed = content.trim(); + if trimmed.is_empty() { + return None; + } + if trimmed == PARAMETER_ERROR_MESSAGE { + return Some(SilentFailure::ParameterRejected); + } + if trimmed.starts_with(THIRD_PARTY_FAILURE_MARKER) { + return Some(SilentFailure::ThirdPartyBroken); + } + None +} + +/// 把静默失败渲染成客户端可见的错误。 +/// +/// 一律是 502 而不是 400:请求本身没问题(HTTP 通、网关也答了 200), +/// 是**这次生成没成**。归成 400 会让客户端以为是自己的请求体写错了。 +/// +/// `model` 是**客户端看到的那个名字**(不是改写后发给上游的 id)—— +/// 报错要报用户能据以行动的那个名字。 +pub fn silent_failure_error(failure: SilentFailure, model: &str) -> GatewayError { + let subject = if model.trim().is_empty() { + "该请求".to_string() + } else { + format!("模型「{}」", model.trim()) + }; + match failure { + SilentFailure::ParameterRejected => GatewayError::with_status( + 502, + format!( + "AtomCode 网关拒绝了{subject}:返回成功状态但正文是「{PARAMETER_ERROR_MESSAGE}」。\ + 通常表示该模型不在这个账号的套餐内,或请求参数不被接受" + ), + ), + SilentFailure::ThirdPartyBroken => GatewayError::with_status( + 502, + format!( + "AtomCode 网关返回了上游报错页而不是回答({subject}):该模型被列出但后端不可用,\ + 请换一个模型重试" + ), + ), + } +} + +/// 从归一化后的错误体里取一行可读文案。 +fn message_of(error_body: &Value) -> String { + error_body + .get("message") + .and_then(Value::as_str) + .map(str::trim) + .filter(|text| !text.is_empty()) + .unwrap_or("上游错误") + .to_string() +} + +/// 从归一化后的错误体里取业务码(**字符串与数字都认**)。 +/// +/// 归一化层只填数字码,但 `detail.code` 那套信封是字符串码、且它已经被折进 +/// `message` 原文里,所以这里把两种来源都覆盖上。 +fn code_of(error_body: &Value) -> Option { + if let Some(text) = error_body.get("code").and_then(Value::as_str) { + let trimmed = text.trim(); + if !trimmed.is_empty() { + return Some(trimmed.to_string()); + } + } + if let Some(number) = error_body.get("code").and_then(Value::as_i64) { + return Some(number.to_string()); + } + None +} + +/// 判断错误体(或其原文)里有没有某个码 —— 用于 `detail.code` 那种 +/// 「码被折进 message 原文」的情形。 +fn message_mentions(error_body: &Value, needle: &str) -> bool { + message_of(error_body).contains(needle) +} + +/// AtomCode 网关的错误分类。 +/// +/// 判定顺序(顺序本身是契约,别调): +/// 1. `ATOMCODE_SIG_MISSING` → 配置错误,**先于** 401/403 判(它也是 403); +/// 2. 401 / 403: +/// - 文案含 `codingplan` → `Fatal`(账号还没领免费套餐,令牌是好的, +/// 判成 TokenExpired 会让上层反复刷新一个没问题的令牌); +/// - 其余 → `TokenExpired`(刷新后用同一账号重试一次); +/// 3. 429 → `QuotaLimited`(网关不给结构化恢复时间); +/// 4. 5xx → `Fatal`(可重试语义由上层退避处理); +/// 5. 其余 → 交共用内容拦截分类(审核文案 → `ContentBlocked`,否则 `Fatal`)。 +pub fn classify_gateway_error(status: u16, error_body: &Value) -> UpstreamErrorClass { + let raw = message_of(error_body); + let message = format!("上游返回 {status}: {raw}"); + let upstream_code = code_of(error_body).and_then(|code| code.parse::().ok()); + + // 1) 打到需要签名的主机:这是**配置**问题,不是凭证也不是额度。 + if message_mentions(error_body, SIGNATURE_MISSING_CODE) { + return UpstreamErrorClass::Fatal { + status: 502, + message: format!( + "AtomCode 网关要求官方版请求签名({SIGNATURE_MISSING_CODE}):\ + 当前配的网关地址指向了需要签名的官方主机,请改回无需签名的 api-ai.gitcode.com({raw})" + ), + upstream_code: None, + }; + } + + // 2) 鉴权类 + if status == 401 || status == 403 { + if message_mentions(error_body, "codingplan") { + return UpstreamErrorClass::Fatal { + status: 403, + message: format!("该 AtomGit 账号还没有领取免费套餐,请先在 AtomCode 页面领取({raw})"), + upstream_code, + }; + } + return UpstreamErrorClass::TokenExpired { message }; + } + + // 3) 限额(网关不吐 x-ratelimit-*,也没有结构化恢复时间) + if status == 429 { + return UpstreamErrorClass::QuotaLimited { + reset_at: None, + message, + upstream_code, + status, + }; + } + + // 4) 传输层 413 等由上层处理;这里放行给共用分类(它会按超大输入归类) + content_block::classify_or_fatal(status, error_body, message, upstream_code) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + fn classify(status: u16, body: Value) -> UpstreamErrorClass { + classify_gateway_error(status, &body) + } + + /// 静默失败:整段正文就是「参数错误」时命中。 + #[test] + fn parameter_sentinel_is_detected() { + assert_eq!( + silent_failure_in_content("参数错误"), + Some(SilentFailure::ParameterRejected) + ); + assert_eq!( + silent_failure_in_content(" 参数错误\n"), + Some(SilentFailure::ParameterRejected) + ); + } + + /// **误判护栏**:模型正常复述这些词时不能算失败。 + /// + /// 用户问「参数错误是什么意思」,模型答「参数错误通常指请求体不合法」 + /// —— 整轮被判成失败会变成一次凭空的换账号重试。 + #[test] + fn narration_mentioning_the_sentinel_is_not_a_failure() { + assert_eq!(silent_failure_in_content("参数错误通常指请求体不合法"), None); + assert_eq!( + silent_failure_in_content("这句话里的“参数错误”是个例子"), + None + ); + assert_eq!(silent_failure_in_content(""), None); + assert_eq!(silent_failure_in_content(" "), None); + } + + /// 第三方故障:以上游报错页形态开头时命中(含 HTML)。 + #[test] + fn third_party_error_page_is_detected() { + let content = "三方请求失败: 502 Bad Gateway"; + assert_eq!( + silent_failure_in_content(content), + Some(SilentFailure::ThirdPartyBroken) + ); + // 只是提到这个词、不是开头,不算 + assert_eq!(silent_failure_in_content("关于三方请求失败的说明如下"), None); + } + + /// 两种静默失败都渲染成 502(请求没问题,是这次生成没成)。 + #[test] + fn silent_failures_render_as_bad_gateway() { + let error = silent_failure_error(SilentFailure::ParameterRejected, "glm5.3-flash"); + assert_eq!(error.status_code, 502); + assert!(error.message.contains("glm5.3-flash")); + let error = silent_failure_error(SilentFailure::ThirdPartyBroken, ""); + assert_eq!(error.status_code, 502); + assert!(error.message.contains("换一个模型")); + } + + /// 需要签名的主机:先于 401/403 判,且归类成不可重试的配置错误。 + #[test] + fn signature_missing_wins_over_the_generic_403_branch() { + // 归一化后 code 取不到、message 退化成原文 JSON 的真实形态 + let body = json!({ + "message": "{\"detail\":{\"code\":\"ATOMCODE_SIG_MISSING\",\"message\":\"请升级到最新版 AtomCode 后再试。\"}}" + }); + match classify(403, body) { + UpstreamErrorClass::Fatal { status, message, .. } => { + assert_eq!(status, 502); + assert!(message.contains("api-ai.gitcode.com")); + } + other => panic!("应为 Fatal 配置错误,实际 {other:?}"), + } + } + + /// 401/403 里**没有** codingplan 文案时是令牌问题(刷新后重试)。 + #[test] + fn plain_401_is_token_expired() { + match classify(401, json!({"message": "401 Unauthorized"})) { + UpstreamErrorClass::TokenExpired { message } => assert!(message.contains("401")), + other => panic!("应为 TokenExpired,实际 {other:?}"), + } + } + + /// 「还没领套餐」是 403 但**不是**令牌问题 —— 判成 TokenExpired 会让上层 + /// 反复刷新一个完全正常的令牌。 + #[test] + fn codingplan_403_is_fatal_not_a_token_problem() { + match classify( + 403, + json!({"message": "user has no codingplan for this model"}), + ) { + UpstreamErrorClass::Fatal { status, message, .. } => { + assert_eq!(status, 403); + assert!(message.contains("还没有领取免费套餐")); + } + other => panic!("应为 Fatal,实际 {other:?}"), + } + } + + /// 429 归限额(带账号冷却语义)。 + #[test] + fn rate_limit_maps_to_quota_limited() { + match classify(429, json!({"message": "too many requests"})) { + UpstreamErrorClass::QuotaLimited { reset_at, status, .. } => { + assert_eq!(status, 429); + assert!(reset_at.is_none()); + } + other => panic!("应为 QuotaLimited,实际 {other:?}"), + } + } + + /// 数字码要被带进 upstream_code(归一化层填的是数字码)。 + #[test] + fn numeric_code_is_carried_through() { + match classify(400, json!({"code": 1001, "message": "bad request"})) { + UpstreamErrorClass::Fatal { upstream_code, .. } + | UpstreamErrorClass::ContentBlocked { upstream_code, .. } => { + assert_eq!(upstream_code, Some(1001)) + } + UpstreamErrorClass::TokenExpired { .. } => panic!("400 不该判成令牌过期"), + other => panic!("意外分类 {other:?}"), + } + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/mod.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/mod.rs new file mode 100644 index 00000000..f311767f --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/mod.rs @@ -0,0 +1,311 @@ +//! AtomCode(AtomGit)适配器:账号管理**加推理转发**。 +//! +//! ── 上游长什么样 ──────────────────────────────────────────── +//! 三条平面(细节见各子模块的模块头): +//! +//! ```text +//! 登录代理(OAuth) {broker}/auth/{login,check,token} + /oauth/refresh +//! CodingPlan REST {codingplan}/coding-plan/{status,models,claim,usage}-v2 +//! 聊天网关 {gateway}/chat/completions OpenAI Chat Completions +//! ``` +//! +//! 本家**无状态**(一次 HTTP 请求 = 一次回答,上游就是 OpenAI 协议), +//! `is_stateful` / `supports_chat` 走默认值,转发链只需 `build_chat_request`。 +//! +//! ── 四处与别家不同的取舍(都写在各自模块头,这里给索引)───────── +//! 1. **有续期,而且会轮换 refresh token**(`supports_refresh = true`)—— +//! 续期必须单飞、必须写回新的一对,见 `oauth.rs` 与 `refresh.rs` 的模块头; +//! 2. **登录没有本地回调**:授权在代理域名上终结,靠轮询判完成 —— +//! 与 ZCode 的 CLI 登录同形,见 `oauth.rs`; +//! 3. **额度是「每窗口调用次数」**,不是积分也不是 token —— 见 `usage.rs`; +//! 4. **网关会把失败包装成成功**:`HTTP 200` + 正文 `参数错误` +//! (或一页上游 HTML 报错),见 `errors.rs`。 +//! +//! ── 浏览器登录:**轮询式**,所以 `supports_web_login` 保持默认 false ──── +//! 本家的授权回调由代理自己在 `acs.atomgit.com/callback` 终结,**永远不回到** +//! 本机,因此走的是「服务端中介的轮询」那一套(与 OfficeAce / ZCode 同形): +//! `core::login::atomcode` 起后台任务,轮询 `auth/check` 判完成、再取令牌落账号, +//! 由 `api::session::login_start` 按 provider 分派进去。 +//! +//! ⚠️ **不要**把 `supports_web_login` 覆写成 true:那三个钩子 +//! (`build_login_url` / `exchange_login_code`)是给「回调带回一次性 code」那类 +//! 用的,走它们意味着路由层会去找一个**永远不会到达本机**的回调 —— 症状是 +//! 用户授权成功后界面一直挂着。OfficeAce 同样没有覆写这个位,这不是遗漏。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +pub mod chat; +pub mod checkin; +pub mod credentials; +pub mod endpoints; +pub mod errors; +pub mod models; +pub mod oauth; +pub mod usage; + +use axum::http::HeaderMap; +use serde_json::Value; + +use crate::server::core::account_store::AccountStore; +use crate::server::core::providers::adapter::{ + ChatRequestPlan, ModelRefreshOutcome, ProviderAdapter, UpstreamErrorClass, +}; +use crate::server::core::providers::{ProviderKind, refresh_flight}; +use crate::server::errors::GatewayError; +use crate::server::logging; + +/// 适配器实例(`adapter_for(ProviderKind::Atomcode)` 返回这一个)。 +/// +/// **无状态**:它不持有任何数据 —— 凭证在账号存储、目录在 `models.rs` 的 +/// 进程级句柄(可重建展示数据)。 +pub struct AtomcodeAdapter; + +/// 静态单例 +pub static ATOMCODE_ADAPTER: AtomcodeAdapter = AtomcodeAdapter; + +impl ProviderAdapter for AtomcodeAdapter { + fn kind(&self) -> ProviderKind { + ProviderKind::Atomcode + } + + /// 模型清单来自远程目录(`models.rs` 的进程缓存 + 落盘缓存) + fn list_models(&self) -> Vec { + models::list() + } + + /// 构造 `POST {网关}/chat/completions`(OpenAI 协议,body 几乎原样透传)。 + fn build_chat_request( + &self, + account: &Value, + body: &Value, + client_headers: &HeaderMap, + ) -> Result { + chat::build_chat_request(account, body, client_headers) + } + + /// 上游错误分类(判定顺序本身是契约,见 `errors::classify_gateway_error`): + /// 1. `ATOMCODE_SIG_MISSING`(打到了需要签名的主机)→ `Fatal`,**先于** 401/403; + /// 2. 401 / 403 → `TokenExpired`(文案含 `codingplan` 时是「没领套餐」→ `Fatal`); + /// 3. 429 → `QuotaLimited`(网关不给结构化恢复时间); + /// 4. 其余 → 共用内容拦截分类。 + fn classify_error(&self, status: u16, error_body: &Value) -> UpstreamErrorClass { + errors::classify_gateway_error(status, error_body) + } + + /// 有远程目录(`GET {codingplan}/coding-plan/models-v2`,见 `models.rs`) + fn supports_model_refresh(&self) -> bool { + true + } + + /// 刷模型目录:用指定账号(空串 = 库里第一个可用账号)的令牌。 + /// + /// 「一个账号都没有」不是失败 —— 那是「用户还没添加账号」的正常状态, + /// 返回 `unchanged()`(脚本 / CI 不该看到一个红色失败,与 Loomy 同口径)。 + fn refresh_models<'a>( + &'a self, + store: &'a AccountStore, + account_id: &'a str, + force: bool, + ) -> std::pin::Pin + Send + 'a>> { + Box::pin(async move { + let record = store.atomcode_account_record(account_id); + if record.is_none() { + if account_id.is_empty() { + logging::verbose("[Models]", "AtomCode 模型目录刷新跳过:尚未添加账号"); + return ModelRefreshOutcome::unchanged(); + } + return ModelRefreshOutcome::failed("指定的账号不存在或不可用,请重新选择"); + } + let credentials = match credentials::from_record(record.as_ref()) { + Ok(credentials) => credentials, + Err(error) => return ModelRefreshOutcome::failed(error.message), + }; + models::refresh(&credentials, force).await + }) + } + + /// 取可用访问令牌。 + /// + /// ⚠️ **这里不发网络请求**(本方法是同步语义的取值口,且会在转发路径上被调): + /// 临期只记一行提示,真正的续期由维护链路(`supports_refresh` + + /// `refresh_access_token`)与 401 后的强制刷新负责。 + fn ensure_access_token<'a>( + &'a self, + store: &'a AccountStore, + account_id: &'a str, + ) -> std::pin::Pin> + Send + 'a>> + { + Box::pin(async move { + let record = store.atomcode_account_record(account_id); + if !account_id.is_empty() && record.is_none() { + return Err(GatewayError::with_status( + 401, + format!("AtomCode 账号 {account_id} 不存在(请重新添加)"), + )); + } + let credentials = credentials::from_record(record.as_ref())?; + if credentials.expiring() { + logging::verbose( + "[AtomCode]", + "账号令牌已过期或临期,将按需续期(续期会轮换 refresh token)", + ); + } + Ok(credentials.access_token) + }) + } + + /// 有续期接口(`POST {broker}/oauth/refresh`) + fn supports_refresh(&self) -> bool { + true + } + + /// 强制续期(401 之后 / 维护链路):刷新并**把新的一对写回记录**。 + /// + /// ── 为什么必须写回,以及为什么必须单飞 ─────────────────────── + /// 续期**轮换** refresh token:代理同时下发新 access 与新 refresh, + /// 旧 access 立即失效。丢掉新的 refresh 等于让账号退化成必须重新登录; + /// 两个并发续期互相作废(后写覆盖前者)—— 因此这里走本仓统一的 + /// `refresh_flight::Table` 做单飞,键含**两个令牌的指纹** + /// (令牌一变就是新的一轮,与 Cline 同一口径)。 + fn refresh_access_token<'a>( + &'a self, + store: &'a AccountStore, + account_id: &'a str, + ) -> std::pin::Pin> + Send + 'a>> + { + Box::pin(async move { + let record = store.atomcode_account_record(account_id); + let credentials = credentials::from_record(record.as_ref())?; + if credentials.refresh_token.is_empty() { + return Err(GatewayError::with_status( + 401, + "AtomCode 账号没有 refresh_token,无法续期,请重新登录", + )); + } + let key = format!( + "{}:{}:{}:{}", + store.file_string(), + account_id, + refresh_flight::fingerprint(&credentials.access_token), + refresh_flight::fingerprint(&credentials.refresh_token), + ); + match ATOMCODE_FLIGHTS.get_or_init(refresh_flight::Table::new).join(&key) { + refresh_flight::Join::Waiter(waiter) => { + waiter.wait().await.map(|fresh| fresh.access_token) + } + refresh_flight::Join::Leader(leader) => { + let result = refresh_and_save(store, &credentials).await; + leader.finish(result.clone()); + result.map(|fresh| fresh.access_token) + } + } + }) + } + + /// 临期判定:`createdAt + expiresIn`(**令牌不是 JWT,读不到 `exp`**), + /// 提前 [`endpoints::REFRESH_WINDOW_SECONDS`] 报临期。 + fn credentials_expiring(&self, store: &AccountStore, account_id: &str) -> bool { + if account_id.is_empty() { + return false; + } + match store.atomcode_account_record(account_id) { + Some(record) => credentials::from_record(Some(&record)) + .map(|credentials| credentials.expiring()) + .unwrap_or(false), + None => false, + } + } + + /// 有额度概念(滚动调用次数窗口,见 `usage.rs`) + fn supports_usage(&self) -> bool { + true + } + + /// 查额度:`GET {codingplan}/coding-plan/status-v2` 的窗口归一。 + /// 401 由调用方走统一的「刷新后重试」链路。 + fn query_usage<'a>( + &'a self, + store: &'a AccountStore, + account_id: &'a str, + ) -> std::pin::Pin> + Send + 'a>> + { + Box::pin(async move { usage::query_usage(store, account_id).await }) + } + + /// AtomCode **没有**「默认模型」概念(不指定模型时按目录/上游默认) + fn supports_default_model(&self) -> bool { + false + } +} + +/// 续期单飞表(进程级;键含两个令牌的指纹,令牌一变就是新的一轮) +static ATOMCODE_FLIGHTS: std::sync::OnceLock> = + std::sync::OnceLock::new(); + +/// 真的去打一次续期,并把**轮换后的令牌对**写回账号记录。 +/// +/// 抽成自由函数是为了让单飞分支的「Leader 职责」只出现在一处: +/// 发请求 → 合并(非空才覆盖)→ 落盘 → 返回新凭证。 +async fn refresh_and_save( + store: &AccountStore, + credentials: &credentials::AtomcodeCredentials, +) -> Result { + let bundle = oauth::refresh(&credentials.refresh_token).await?; + // 轮换语义:非空才覆盖(代理可能省略 refresh_token / expires_in / user + // —— 见 oauth.rs 的模块头,整包替换会让账号失去续期能力) + let previous = credentials::TokenBundle { + access_token: credentials.access_token.clone(), + refresh_token: credentials.refresh_token.clone(), + ..Default::default() + }; + let merged = bundle.merge_into(&previous); + // 落盘失败**不**把这次续期当成功:新的 access 已经在手上、但记录里还是旧的, + // 下次仍会走一次续期(而旧的 refresh 可能已被轮换作废)。如实上报。 + let expires_at = merged.expires_at(logging::now_ms()); + let written = store.update_account_tokens( + &credentials.id, + Some(&merged.access_token), + Some(&merged.refresh_token), + (expires_at > 0).then_some(expires_at as f64), + None, + ); + if !written { + return Err(GatewayError::with_status( + 502, + "AtomCode 续期成功但写回账号失败(账号记录不存在?):请重新登录", + )); + } + logging::log("[AtomCode]", "令牌已续期并写回账号(refresh token 已轮换)"); + Ok(credentials::AtomcodeCredentials { + id: credentials.id.clone(), + access_token: merged.access_token, + refresh_token: merged.refresh_token, + // 复用上面已经算好的那个(在字段被 move 之前算的) + expires_at, + user_id: merged.user_id, + name: merged.name, + }) +} +#[cfg(test)] +mod tests { + use super::*; + + /// 适配器声明的能力集(这份断言存在的意义:改动能力位时会被迫读一遍模块头 + /// 里「为什么」的那几节)。 + #[test] + fn adapter_declares_the_expected_capabilities() { + let adapter = &ATOMCODE_ADAPTER; + assert_eq!(adapter.kind(), ProviderKind::Atomcode); + assert!(adapter.supports_chat(), "上游就是 OpenAI 协议,必然能转发"); + assert!(!adapter.is_stateful(), "一次 HTTP 请求 = 一次回答"); + assert!(adapter.supports_model_refresh(), "有 models-v2 目录接口"); + assert!(adapter.supports_refresh(), "有 /oauth/refresh 续期接口"); + assert!(adapter.supports_usage(), "有 status-v2 额度窗口"); + assert!(!adapter.supports_default_model(), "本家没有默认模型概念"); + // 轮询式登录(`core::login::atomcode` + `api::session` 分派), + // **不走**「回调带 code」那三个钩子 —— 覆写成 true 会让路由层去等一个 + // 永远不会到达本机的回调(见模块头)。 + assert!(!adapter.supports_web_login()); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/models.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/models.rs new file mode 100644 index 00000000..ca3d8b3e --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/models.rs @@ -0,0 +1,668 @@ +//! AtomCode 的模型目录:`coding-plan/models-v2` 是**唯一权威**来源。 +//! +//! ── 三层结构(照官方与本地 Go 参考实现)───────────────────────── +//! ```text +//! 1. GET {codingplan}/coding-plan/status-v2 +//! -> 套餐名("CodingPlan Lite-体验版" / "… Pro" / "… Max") +//! + 滚动额度窗口(额度展示用,见 usage.rs) +//! 2. 由套餐名推断档位(Max / Pro / Lite)——models-v2 的 `plan_available` +//! 是**相对被问的那个档位**算的,问错档位就会把一个能用的模型过滤掉 +//! (官方 `types.rs:53-70` 的映射是「从最具体到最不具体」的子串匹配) +//! 3. GET {codingplan}/coding-plan/models-v2?plan_type=<档位> +//! -> 条目数组;只注册 `plan_available == true` 的 +//! ``` +//! +//! ── 条目里哪个字段才是「发给网关的模型名」(最容易搞错的一处)──── +//! `id` 是**数字**(AtomGit 内部模型 id),**不是**模型名;网关认的是 +//! `display_model_name`(形如 `glm5.3-flash`)。本地 Go 参考在 +//! `models.go:399-401` 专门为这件事留了一句注释: +//! +//! > id (the `glm5.3-flash` spelling it answers to), while `display` is the … +//! +//! 拿数字 id 去发请求不会报错 —— 网关回 `HTTP 200 + 正文 参数错误` +//! (见 `errors.rs` 的模块头)。 +//! +//! ── 名义目录 ≠ 可达目录(实测,所以不做兜底清单)─────────────── +//! 2026-10-01 同一个 `CodingPlan Lite-体验版` 账号上逐个 `POST /chat/completions`: +//! +//! ```text +//! deepseek-flash 200 真回答 (**不在** models-v2 名单里) +//! Qwen/Qwen3-32B 200 真回答 (不在名单里) +//! Qwen/Qwen2-VL-72B 200 真回答 (不在名单里) +//! Qwen/Qwen3-4B-Instruct-2507 200 正文是 `三方请求失败: 502 …` +//! no-such-model-xyz 200 正文是 `参数错误` +//! ``` +//! +//! 所以「可达」不等于「被广告」、更不等于「能用」。本模块**只广告 +//! `plan_available` 的条目**,不内置任何兜底清单 —— 把上面那几个不可达的写死 +//! 进来,用户点一下就会看到「参数错误」或一页 HTML(对比:Loomy 也是 +//! 「拉不到就如实报错,不编模型名」)。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use std::sync::{Mutex, OnceLock}; + +use serde_json::{Map, Value, json}; + +use crate::server::core::auth_http::send_raw; +use crate::server::core::providers::adapter::ModelRefreshOutcome; +use crate::server::core::providers::catalog_cache; +use crate::server::errors::GatewayError; +use crate::server::logging; + +use super::credentials::AtomcodeCredentials; +use super::endpoints::{ + CATALOG_TIMEOUT_MS, CODINGPLAN_CLAIM_PATH, REQUEST_TIMEOUT_MS, codingplan_base_url, + models_url, status_url, +}; + +/// 本家在 `catalog_cache` 里的 scope 名 +pub const SCOPE: &str = catalog_cache::SCOPE_ATOMCODE; + +/// 目录缓存时长(与官方 CLI 的 2 小时同档) +const CATALOG_TTL_MS: i64 = 2 * 60 * 60 * 1000; + +/// 目录里的一条模型 +#[derive(Clone, Debug)] +pub struct ModelEntry { + /// 发给网关的模型名(`display_model_name`,如 `glm5.3-flash`) + pub gateway_id: String, + /// 展示名(本网关给用户看的名字) + pub display_name: String, + /// 上下文窗口(上游没给 = 0,不猜) + pub context_window: i64, + /// 是否收图片(上游没给 = None,**不猜**) + pub supports_vision: Option, + /// 上游声明的思考档位(空 = 上游没说支持思考) + pub reasoning_levels: Vec, +} + +/// 进程内目录状态 +#[derive(Clone, Debug, Default)] +struct CatalogState { + entries: Vec, + fetched_at: i64, +} + +static STATE: OnceLock>> = OnceLock::new(); + +/// 取状态句柄(毒锁按「上一个持有者 panic 过、数据可能半更新」处理: +/// 这份缓存是可重建的展示数据,不值得让进程挂掉 —— 与本仓其它目录模块同口径) +fn state() -> &'static Mutex> { + STATE.get_or_init(|| Mutex::new(None)) +} + +/// 在锁内执行一段取值,并在首次访问时把落盘缓存装回内存 +fn with_state(f: impl FnOnce(&mut Option) -> R) -> R { + let mut guard = match state().lock() { + Ok(guard) => guard, + Err(poisoned) => poisoned.into_inner(), + }; + if guard.is_none() { + if let Some(cached) = catalog_cache::load(SCOPE) { + let entries: Vec = cached + .models + .iter() + .filter_map(entry_from_cached) + .collect(); + if !entries.is_empty() { + *guard = Some(CatalogState { + entries, + fetched_at: cached.fetched_at, + }); + } + } + } + f(&mut guard) +} + +/// 从落盘缓存条目还原(缓存里存的是 [`to_cached_value`] 写下的形态) +fn entry_from_cached(value: &Value) -> Option { + let gateway_id = value.get("model").and_then(Value::as_str)?.trim().to_string(); + if gateway_id.is_empty() { + return None; + } + Some(ModelEntry { + gateway_id, + display_name: value + .get("name") + .and_then(Value::as_str) + .unwrap_or("") + .to_string(), + context_window: value.get("contextWindow").and_then(Value::as_i64).unwrap_or(0), + supports_vision: value.get("supportsImages").and_then(Value::as_bool), + reasoning_levels: value + .get("reasoningLevels") + .and_then(Value::as_array) + .map(|levels| { + levels + .iter() + .filter_map(Value::as_str) + .map(str::to_string) + .collect() + }) + .unwrap_or_default(), + }) +} + +/// 把目录条目落盘时的形态(key 用本仓惯用的驼峰,便于人工看) +fn to_cached_value(entry: &ModelEntry) -> Value { + let mut object = Map::new(); + object.insert("model".to_string(), Value::String(entry.gateway_id.clone())); + object.insert("name".to_string(), Value::String(entry.display_name.clone())); + if entry.context_window > 0 { + object.insert("contextWindow".to_string(), Value::from(entry.context_window)); + } + if let Some(vision) = entry.supports_vision { + object.insert("supportsImages".to_string(), Value::Bool(vision)); + } + if !entry.reasoning_levels.is_empty() { + object.insert( + "reasoningLevels".to_string(), + Value::Array( + entry + .reasoning_levels + .iter() + .map(|level| Value::String(level.clone())) + .collect(), + ), + ); + } + Value::Object(object) +} + +/// 网关名 → 展示名。只覆盖**上游名字对用户不够清楚**的那几个: +/// 官方客户端自己也承认 `glm5.3-flash` 就是 z.ai 的 GLM-5.3-Flash +/// (本地 Go 参考 `models.go:262-263` 记了 2026-09-25 的实测)。 +/// +/// 表里没有的名字**原样展示**,不猜、不套模板(`deepseek-flash` 这类 +/// 上游自己给的形态已经很清楚了)。 +pub fn display_name_for(gateway_id: &str) -> String { + match gateway_id { + "glm5.3-flash" => "GLM-5.3-Flash".to_string(), + "qwen3.8-27b" => "Qwen3.8-27B".to_string(), + "deepseek-flash" => "DeepSeek-V4.1-Flash".to_string(), + other => other.to_string(), + } +} + +/// 把一条 `models-v2` 条目映射成本模块的形态。 +/// +/// 只注册 `plan_available == true` 的(服务端自己的权益判定); +/// 缺 `display_model_name` 的条目直接丢掉 —— 没有它就发不出请求。 +fn entry_from_upstream(value: &Value) -> Option { + if !value.get("plan_available").and_then(Value::as_bool).unwrap_or(false) { + return None; + } + let gateway_id = value + .get("display_model_name") + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or("") + .to_string(); + if gateway_id.is_empty() { + return None; + } + let context_window = value + .get("context_window") + .and_then(Value::as_i64) + .filter(|value| *value > 0) + .unwrap_or(0); + let supports_vision = value.get("supports_vision").and_then(Value::as_bool); + let reasoning_levels = value + .get("reasoning_effort_levels") + .and_then(Value::as_array) + .map(|levels| { + levels + .iter() + .filter_map(Value::as_str) + .map(str::to_string) + .collect() + }) + .unwrap_or_default(); + Some(ModelEntry { + display_name: display_name_for(&gateway_id), + gateway_id, + context_window, + supports_vision, + reasoning_levels, + }) +} + +/// 聚合层认的条目形态(键名对照 `core/models/shape.rs::list_item`)。 +/// +/// 这里直接给出**聚合层的输入键**(`id` / `name` / `maxInputTokens` / …), +/// 由 `list_item` 再折成 `/v1/models` 的 wire 形态 —— 与 loomy 等家同一条路。 +fn to_manifest_value(entry: &ModelEntry) -> Value { + let mut object = Map::new(); + object.insert("id".to_string(), Value::String(entry.gateway_id.clone())); + if !entry.display_name.is_empty() { + object.insert("name".to_string(), Value::String(entry.display_name.clone())); + } + if entry.context_window > 0 { + object.insert("maxInputTokens".to_string(), Value::from(entry.context_window)); + } + // 模态位:上游**没给**时按不支持处理(`list_item` 对缺失键按 false 折叠), + // 但没有证据就不声称支持 —— 与本仓「广告能力要有实测依据」的口径一致。 + object.insert( + "supportsImages".to_string(), + Value::Bool(entry.supports_vision.unwrap_or(false)), + ); + object.insert("supportsVideo".to_string(), Value::Bool(false)); + object.insert( + "supportsReasoning".to_string(), + Value::Bool(!entry.reasoning_levels.is_empty()), + ); + // 网关是 OpenAI Chat Completions,tools 与 telemetry 都是原样透传 ⇒ 支持工具调用 + object.insert("supportsToolCall".to_string(), Value::Bool(true)); + Value::Object(object) +} + +/// 当前目录的聚合层清单 +pub fn list() -> Vec { + with_state(|guard| { + guard + .as_ref() + .map(|state| state.entries.iter().map(to_manifest_value).collect()) + .unwrap_or_default() + }) +} + +/// 取一条缓存的模型(按客户端给的名字找),返回它的**入口形态**。 +/// +/// 客户端手里的清单来自 `/v1/models`,那里的 `id` 与 `name` 都可能被用来发请求 +/// (有的客户端认 id、有的认 name),所以两个都要能命中。 +fn find_entry(needle: &str) -> Option { + let target = needle.trim(); + if target.is_empty() { + return None; + } + with_state(|guard| { + let state = guard.as_ref()?; + state + .entries + .iter() + .find(|entry| entry.gateway_id == target || entry.display_name == target) + .cloned() + }) +} + +/// 客户端请求里的模型名 → 发给网关的模型名。 +/// +/// 认不出时返回 `None`,由调用方按「原样发」处理(`chat.rs` 就是这么用的): +/// 猜一个更接近的名字比原样发更糟 —— 那会把一个「名字没映射」的问题 +/// 变成一次静默的错误重试。 +pub fn upstream_id_for(requested: &str) -> Option { + find_entry(requested).map(|entry| entry.gateway_id) +} + +/// 目录是否为空(界面「刷新目录」按钮的提示用) +pub fn is_empty() -> bool { + with_state(|guard| { + guard + .as_ref() + .map(|state| state.entries.is_empty()) + .unwrap_or(true) + }) +} + +/// 目录是否来自**远程拉取**(聚合层用它区分「远程来源」与「内置兜底」)。 +/// +/// 本家**没有内置兜底清单**(有意为之:名单里写死的模型点一下就可能是 +/// 「参数错误」,见模块头),所以「有目录」等价于「远程拉到过」。 +pub fn remote_refreshed() -> bool { + with_state(|guard| { + guard + .as_ref() + .map(|state| state.fetched_at > 0 && !state.entries.is_empty()) + .unwrap_or(false) + }) +} + +/// 目录最后一次落地时刻(毫秒;0 = 没有目录) +pub fn last_refreshed_at() -> i64 { + with_state(|guard| guard.as_ref().map(|state| state.fetched_at).unwrap_or(0)) +} + +/// 当前目录的年龄(毫秒;0 = 没有目录) +pub fn age_ms() -> i64 { + with_state(|guard| { + guard + .as_ref() + .map(|state| logging::now_ms() - state.fetched_at) + .unwrap_or(0) + }) +} + +/// 套餐档位推断:从 `status-v2` 的套餐名里取 Max / Pro / Lite。 +/// +/// 上游套餐名形如 `CodingPlan Lite-体验版` / `CodingPlan Pro` / `CodingPlan Max`, +/// 匹配**从最具体到最不具体**(与官方 `types.rs:53-70` 同一顺序)。 +pub fn plan_tier_from_name(name: &str) -> Option<&'static str> { + let lower = name.to_lowercase(); + if lower.contains("max") { + return Some(PLAN_MAX); + } + if lower.contains("pro") { + return Some(PLAN_PRO); + } + if lower.contains("lite") { + return Some(PLAN_LITE); + } + None +} + +/// 档位常量(`models-v2?plan_type=` 的取值) +pub const PLAN_MAX: &str = "Max"; +/// 见 [`PLAN_MAX`] +pub const PLAN_PRO: &str = "Pro"; +/// 见 [`PLAN_MAX`] +pub const PLAN_LITE: &str = "Lite"; + +/// 从 `status-v2` 响应体里取档位 +pub fn plan_tier_from_status(payload: &Value) -> Option<&'static str> { + let name = payload + .get("codingplan_free") + .and_then(|plan| plan.get("plan_name")) + .and_then(Value::as_str) + .or_else(|| payload.get("plan_type").and_then(Value::as_str)) + .unwrap_or(""); + plan_tier_from_name(name) +} + +/// 带鉴权取一个 JSON(目录与状态两条路共用)。 +async fn fetch_json( + url: &str, + credentials: &AtomcodeCredentials, + timeout_ms: u64, +) -> Result { + let headers: Vec<(String, String)> = vec![ + ( + "authorization".to_string(), + format!("Bearer {}", credentials.access_token), + ), + ("accept".to_string(), "application/json".to_string()), + ( + "user-agent".to_string(), + super::endpoints::ADAPTER_USER_AGENT.to_string(), + ), + ]; + let response = send_raw("GET", url, None, &headers, None, Some(timeout_ms)) + .await + .map_err(|error| { + if error.is_timeout() { + GatewayError::with_status(504, "AtomCode 目录接口超时") + } else { + GatewayError::with_status(502, format!("AtomCode 目录接口请求失败: {error}")) + } + })?; + let payload = response.payload.ok_or_else(|| { + GatewayError::with_status(502, "AtomCode 目录接口响应不是 JSON") + })?; + if !response.ok { + return Err(GatewayError::with_status( + response.status as i32, + format!( + "AtomCode 目录接口返回 HTTP {}:{}", + response.status, + payload + .get("message") + .and_then(Value::as_str) + .unwrap_or("(无文案)") + ), + )); + } + Ok(payload) +} + +/// 取套餐状态(`status-v2`):档位推断与额度读数(`usage.rs`)共用这一条路。 +pub async fn fetch_status( + credentials: &AtomcodeCredentials, + timeout_ms: u64, +) -> Result { + fetch_json(&status_url(), credentials, timeout_ms).await +} + +/// 拉一次 `models-v2`(给定档位) +async fn fetch_catalog( + credentials: &AtomcodeCredentials, + plan_type: &str, +) -> Result, GatewayError> { + let payload = fetch_json(&models_url(plan_type), credentials, CATALOG_TIMEOUT_MS).await?; + let items = payload + .get("models") + .and_then(Value::as_array) + .or_else(|| payload.as_array()) + .cloned() + .unwrap_or_default(); + Ok(items.iter().filter_map(entry_from_upstream).collect()) +} + +/// 刷新目录。 +/// +/// 档位推断不出来时**依次试 Lite → Pro → Max**(各一次,取第一个非空的)。 +/// 试三次而不是直接失败,是因为免费档最常见的形态是 Lite,而档位名上游改过 +/// 一次口径(`Lite-体验版`)——「推断失败」不该等于「目录空」。 +pub async fn refresh( + credentials: &AtomcodeCredentials, + force: bool, +) -> ModelRefreshOutcome { + if !force && !is_empty() && age_ms() < CATALOG_TTL_MS { + return ModelRefreshOutcome::unchanged(); + } + + let plan_type = match fetch_json(&status_url(), credentials, REQUEST_TIMEOUT_MS).await { + Ok(status) => plan_tier_from_status(&status), + Err(error) => { + // 状态接口失败不致命:档位退化成「逐个试」,目录仍可能拿到 + logging::verbose( + "[AtomCode]", + &format!("套餐状态读取失败,改为逐个档位试目录:{}", error.message), + ); + None + } + }; + let candidates: Vec<&str> = match plan_type { + Some(tier) => vec![tier], + None => vec![PLAN_LITE, PLAN_PRO, PLAN_MAX], + }; + + let mut last_error: Option = None; + for tier in candidates { + match fetch_catalog(credentials, tier).await { + Ok(entries) if !entries.is_empty() => { + let fetched_at = logging::now_ms(); + let cached: Vec = entries.iter().map(to_cached_value).collect(); + catalog_cache::save(SCOPE, &cached, fetched_at); + with_state(|guard| { + *guard = Some(CatalogState { + entries: entries.clone(), + fetched_at, + }); + }); + logging::log( + "[AtomCode]", + &format!("目录刷新完成:{} 个模型(档位 {tier})", entries.len()), + ); + return ModelRefreshOutcome::refreshed(entries.len()); + } + Ok(_) => { + last_error = Some(format!("档位 {tier} 下目录为空")); + } + Err(error) => { + last_error = Some(error.message.clone()); + } + } + } + ModelRefreshOutcome::failed( + last_error.unwrap_or_else(|| "AtomCode 目录刷新失败:上游没有返回可用模型".to_string()), + ) +} + +/// 领取套餐:`POST /coding-plan/claim-v2`,按 Max → Pro → Lite 级联、命中即停。 +/// +/// ⚠️ 这个端点**没有幂等标记**:重复领取同样返回成功(本地 Go 参考实测), +/// 所以调用方可以多次调用,但**不能**把「成功」当成「刚发放」。 +pub async fn claim_plan( + credentials: &AtomcodeCredentials, + plan_type: &str, +) -> Result { + let url = format!("{}{}", codingplan_base_url(), CODINGPLAN_CLAIM_PATH); + let headers: Vec<(String, String)> = vec![ + ( + "authorization".to_string(), + format!("Bearer {}", credentials.access_token), + ), + ("accept".to_string(), "application/json".to_string()), + ("content-type".to_string(), "application/json".to_string()), + ( + "user-agent".to_string(), + super::endpoints::ADAPTER_USER_AGENT.to_string(), + ), + ]; + let body = json!({ "plan_type": plan_type }); + let response = send_raw("POST", &url, Some(&body), &headers, None, Some(REQUEST_TIMEOUT_MS)) + .await + .map_err(|error| { + if error.is_timeout() { + GatewayError::with_status(504, "AtomCode 套餐领取超时") + } else { + GatewayError::with_status(502, format!("AtomCode 套餐领取失败: {error}")) + } + })?; + let payload = response.payload.unwrap_or(Value::Null); + if !response.ok { + return Err(GatewayError::with_status( + response.status as i32, + format!("AtomCode 套餐领取返回 HTTP {}", response.status), + )); + } + Ok(payload) +} + +#[cfg(test)] +mod tests { + use super::*; + + /// 档位推断:从最具体到最不具体(`max` 先于 `pro`、`pro` 先于 `lite`)。 + #[test] + fn plan_tier_inference_prefers_the_most_specific_name() { + assert_eq!(plan_tier_from_name("CodingPlan Lite-体验版"), Some(PLAN_LITE)); + assert_eq!(plan_tier_from_name("CodingPlan Pro"), Some(PLAN_PRO)); + assert_eq!(plan_tier_from_name("CodingPlan Max"), Some(PLAN_MAX)); + // 名字里同时含 pro 与 max 时取 max(官方同一顺序) + assert_eq!(plan_tier_from_name("CodingPlan Pro Max"), Some(PLAN_MAX)); + assert_eq!(plan_tier_from_name("免费套餐"), None); + } + + /// 从 status-v2 响应里取档位:`codingplan_free.plan_name` 优先,`plan_type` 兜底。 + #[test] + fn plan_tier_reads_from_status_envelope() { + assert_eq!( + plan_tier_from_status(&json!({"codingplan_free": {"plan_name": "CodingPlan Lite-体验版"}})), + Some(PLAN_LITE) + ); + assert_eq!( + plan_tier_from_status(&json!({"plan_type": "Pro"})), + Some(PLAN_PRO) + ); + assert_eq!(plan_tier_from_status(&json!({})), None); + } + + /// **关键**:`id` 是数字、`display_model_name` 才是模型名 —— 别取错字段。 + #[test] + fn entry_uses_display_model_name_not_the_numeric_id() { + let upstream = json!({ + "id": 1980884839691821059_i64, + "display_model_name": "glm5.3-flash", + "plan_available": true, + "context_window": 512000, + "supports_vision": true, + "reasoning_effort_levels": ["low", "high"] + }); + let entry = entry_from_upstream(&upstream).expect("应映射成功"); + assert_eq!(entry.gateway_id, "glm5.3-flash"); + assert_eq!(entry.context_window, 512_000); + assert_eq!(entry.supports_vision, Some(true)); + assert_eq!(entry.reasoning_levels, vec!["low", "high"]); + // 展示名走规范化表 + assert_eq!(entry.display_name, "GLM-5.3-Flash"); + } + + /// 不广告 `plan_available == false` 的条目(服务端自己的权益判定)。 + #[test] + fn entries_without_entitlement_are_skipped() { + let upstream = json!({"display_model_name": "glm5.3-flash", "plan_available": false}); + assert!(entry_from_upstream(&upstream).is_none()); + // plan_available 缺失也算不可用(不猜「默认给」) + let missing = json!({"display_model_name": "glm5.3-flash"}); + assert!(entry_from_upstream(&missing).is_none()); + } + + /// 缺 `display_model_name` 的条目丢掉:没有它就发不出请求。 + #[test] + fn entries_without_a_gateway_name_are_dropped() { + let upstream = json!({"id": 42, "plan_available": true}); + assert!(entry_from_upstream(&upstream).is_none()); + let blank = json!({"display_model_name": " ", "plan_available": true}); + assert!(entry_from_upstream(&blank).is_none()); + } + + /// 上游没给能力位时不猜:`supports_vision` 保持 None, + /// 清单里按不支持折叠(广告能力要有实测依据)。 + #[test] + fn unknown_capabilities_are_not_advertised() { + let upstream = json!({"display_model_name": "deepseek-flash", "plan_available": true}); + let entry = entry_from_upstream(&upstream).expect("应映射成功"); + assert_eq!(entry.supports_vision, None); + let manifest = to_manifest_value(&entry); + assert_eq!(manifest.get("supportsImages"), Some(&Value::Bool(false))); + assert_eq!(manifest.get("supportsReasoning"), Some(&Value::Bool(false))); + // 上下文窗口未知时不写这个键(由 `list_item` 折叠成缺失) + assert!(manifest.get("maxInputTokens").is_none()); + } + + /// 清单键名必须与 `list_item` 读取的键一一对上。 + #[test] + fn manifest_keys_match_the_aggregate_contract() { + let upstream = json!({ + "display_model_name": "qwen3.8-27b", "plan_available": true, + "context_window": 262144, "supports_vision": true, + "reasoning_effort_levels": ["low"] + }); + let entry = entry_from_upstream(&upstream).expect("应映射成功"); + let manifest = to_manifest_value(&entry); + for key in ["id", "name", "maxInputTokens", "supportsImages", "supportsVideo", "supportsReasoning", "supportsToolCall"] { + assert!(manifest.get(key).is_some(), "缺键 {key}"); + } + assert_eq!(manifest.get("id"), Some(&Value::String("qwen3.8-27b".into()))); + assert_eq!(manifest.get("supportsToolCall"), Some(&Value::Bool(true))); + } + + /// 落盘缓存的往返:写下的形态必须能原样读回。 + #[test] + fn cached_entries_round_trip() { + let entry = ModelEntry { + gateway_id: "glm5.3-flash".into(), + display_name: "GLM-5.3-Flash".into(), + context_window: 512_000, + supports_vision: Some(false), + reasoning_levels: vec!["high".into()], + }; + let cached = to_cached_value(&entry); + let restored = entry_from_cached(&cached).expect("应可还原"); + assert_eq!(restored.gateway_id, "glm5.3-flash"); + assert_eq!(restored.display_name, "GLM-5.3-Flash"); + assert_eq!(restored.context_window, 512_000); + assert_eq!(restored.supports_vision, Some(false)); + assert_eq!(restored.reasoning_levels, vec!["high"]); + } + + /// 「认不出就 None」:调用方会原样发,而不是猜一个更接近的名字。 + #[test] + fn unknown_names_resolve_to_none() { + assert!(upstream_id_for("完全不存在的模型").is_none()); + assert!(upstream_id_for(" ").is_none()); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/oauth.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/oauth.rs new file mode 100644 index 00000000..2ee89175 --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/oauth.rs @@ -0,0 +1,515 @@ +//! AtomCode 的登录与续期:**代理中介的浏览器流程,没有本地回调**。 +//! +//! ── 与其余各家的最大不同:回调不回到本机 ──────────────────── +//! AutoClaw / Accio / Qoder 的网页登录都在本机起一个回调口(或等设备授权码), +//! atomcode 不是:授权回调由代理自己在其域名上终结 +//! (`acs.atomgit.com/callback`),**永远不回到**运行网关的这台机器。 +//! 因此本家没有 loopback 监听器、没有固定回调端口 —— 与 ZCode 的 CLI 登录 +//! 同一形状,靠**轮询**判完成(下面是官方 `oauth.rs` 的三步,本地 Go 参考实现 +//! `cpa-jethub-plugins/plugins/atomcode/oauth.go` 逐字照做并实测过): +//! +//! ```text +//! 1. GET {broker}/auth/login?provider=atomgit +//! -> {login_url, state} +//! 2. 把 login_url 交给用户打开(浏览器手势必须发生在用户还认为它是 +//! 「自己点的」那段时间里,所以这一步**绝不能阻塞等待**) +//! 3. GET {broker}/auth/check?state= -> {valid: bool} (轮询) +//! GET {broker}/auth/token?state= -> 令牌包 (取回) +//! ``` +//! +//! 续期是**另一个端点**:`POST {broker}/oauth/refresh`,body 只有 +//! `{"refresh_token": ...}`,**不带 Authorization**(官方 `oauth.rs:901-975`)。 +//! +//! ── 续期会轮换 refresh token,而且可能不回新的(两条都要照顾)──── +//! 实测:`/oauth/refresh` 同时下发新 access 与新 refresh,**旧 access 立即失效**。 +//! 但官方注释(`oauth.go:350`)明确写了代理**可能省略** `refresh_token` / +//! `expires_in` / `user` 三个字段 —— 省略 `refresh_token` 时若把空串写回记录, +//! 账号就退化成必须重新走浏览器登录。所以这里的规则是: +//! **新值非空才覆盖,为空保留旧值**(见 [`TokenBundle::merge_into`])。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use serde_json::{json, Value}; + +use crate::server::core::auth_http::send_raw; +use crate::server::errors::GatewayError; + +use super::credentials::{TokenBundle, expires_at_from}; +use super::endpoints::{ + BROKER_CHECK_PATH, BROKER_LOGIN_PATH, BROKER_PROVIDER, BROKER_REFRESH_PATH, + BROKER_TOKEN_PATH, REQUEST_TIMEOUT_MS, broker_base_url, +}; + +/// 取一个 JSON 字符串字段(缺失 / 非字符串 / 空串都归一成空串) +fn str_field(source: &Value, key: &str) -> String { + source + .get(key) + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or("") + .to_string() +} + +/// 把一个代理响应体解成 [`TokenBundle`](登录与续期共用同一信封)。 +/// +/// 代理的信封实测形态(官方 `AuthInfo` / Go 参考 `brokerTokenResponse`): +/// +/// ```text +/// { access_token, token_type, expires_in, refresh_token, user: {id, username, …} } +/// ``` +/// +/// 注意 `user` 可能整块缺失(官方注释:代理可省略),因此身份字段一律宽容读取。 +pub fn parse_token_bundle(payload: &Value) -> Result { + let access_token = str_field(payload, "access_token"); + if access_token.is_empty() { + return Err(GatewayError::with_status( + 502, + "AtomCode 登录代理未返回 access_token", + )); + } + let user = payload.get("user").cloned().unwrap_or(Value::Null); + let expires_in = payload + .get("expires_in") + .and_then(Value::as_i64) + .filter(|value| *value > 0) + .unwrap_or(0); + Ok(TokenBundle { + access_token, + refresh_token: str_field(payload, "refresh_token"), + token_type: str_field(payload, "token_type"), + expires_in, + user_id: { + let id = str_field(&user, "id"); + if id.is_empty() { str_field(payload, "user_id") } else { id } + }, + username: str_field(&user, "username"), + name: str_field(&user, "name"), + email: str_field(&user, "email"), + avatar_url: str_field(&user, "avatar_url"), + }) +} + +impl TokenBundle { + /// 续期结果合并到旧令牌包上。 + /// + /// ── 为什么不是整包替换 ────────────────────────────────── + /// 代理**可能省略** `refresh_token` / `expires_in` / `user`(官方注释原文: + /// > The broker may omit `refresh_token`/`expires_in`/`user`; each absent field … + /// + /// 整包替换的下场是:一次省略 `refresh_token` 的续期把记录里的 refresh 清成 + /// 空串,账号从此只能重新浏览器登录 —— 而这次续期「看起来是成功的」。 + /// 所以非空才覆盖、为空保留,`user_id` 同样(身份不该在一次续期后消失)。 + pub fn merge_into(self, previous: &TokenBundle) -> TokenBundle { + TokenBundle { + access_token: self.access_token, + refresh_token: if self.refresh_token.is_empty() { + previous.refresh_token.clone() + } else { + self.refresh_token + }, + token_type: if self.token_type.is_empty() { + previous.token_type.clone() + } else { + self.token_type + }, + expires_in: if self.expires_in > 0 { + self.expires_in + } else { + previous.expires_in + }, + user_id: if self.user_id.is_empty() { + previous.user_id.clone() + } else { + self.user_id + }, + username: if self.username.is_empty() { + previous.username.clone() + } else { + self.username + }, + name: if self.name.is_empty() { + previous.name.clone() + } else { + self.name + }, + email: if self.email.is_empty() { + previous.email.clone() + } else { + self.email + }, + avatar_url: if self.avatar_url.is_empty() { + previous.avatar_url.clone() + } else { + self.avatar_url + }, + } + } + + /// 这一次令牌包的过期时刻(毫秒)。`expires_in` 缺失时返回 0 = 判不出来。 + pub fn expires_at(&self, now_ms: i64) -> i64 { + if self.expires_in <= 0 { + return 0; + } + expires_at_from(now_ms / 1000, self.expires_in, 0) + } +} + +/// 一条已发起的浏览器登录([`BrokerLogin::start`] 的返回值)。 +/// +/// 调用方持有它,把 [`Self::login_url`] 交给用户打开,之后按节奏调 +/// [`Self::poll`],直到拿到令牌包或超时。 +pub struct BrokerLogin { + /// 代理给的关联 id,既是轮询凭据也是索引 + state: String, + /// 交给用户打开的 AtomGit 授权地址 + login_url: String, + /// 本地截止时刻(毫秒)—— 代理自己也有窗口,这个只用来阻止本地累积死会话 + expires_at_ms: i64, +} + +impl BrokerLogin { + /// 发起登录:拿 `login_url` 与 `state`。 + /// + /// **不阻塞**(不等待用户)—— 浏览器手势必须发生在用户还认为它是自己点开的 + /// 那段时间里,阻塞式登录会让 `window.open` 被拦、界面变成导航跳转。 + pub async fn start(timeout_ms: u64) -> Result { + let url = format!( + "{}{}?provider={}", + broker_base_url(), + BROKER_LOGIN_PATH, + BROKER_PROVIDER + ); + let response = send_raw( + "GET", + &url, + None, + &[("accept".to_string(), "application/json".to_string())], + None, + Some(REQUEST_TIMEOUT_MS), + ) + .await + .map_err(|error| login_transport_error("登录发起", &error))?; + + let payload = response.payload.ok_or_else(|| { + GatewayError::with_status(502, "AtomCode 登录发起:代理响应不是 JSON") + })?; + if !response.ok { + return Err(GatewayError::with_status( + 502, + format!( + "AtomCode 登录发起失败(HTTP {}):{}", + response.status, + summarize(&payload) + ), + )); + } + let login_url = str_field(&payload, "login_url"); + let state = str_field(&payload, "state"); + if login_url.is_empty() || state.is_empty() { + return Err(GatewayError::with_status( + 502, + "AtomCode 登录代理未返回 login_url/state", + )); + } + let expires = if timeout_ms > 0 { timeout_ms } else { 300_000 }; + Ok(Self { + state, + login_url, + expires_at_ms: crate::server::logging::now_ms() + expires as i64, + }) + } + + /// 给用户打开的授权地址 + pub fn login_url(&self) -> &str { + &self.login_url + } + + /// 关联 id(登录任务表用它当 key) + pub fn state(&self) -> &str { + &self.state + } + + /// 本地是否已过截止时刻(过点就没必要再轮询) + pub fn expired(&self) -> bool { + self.expires_at_ms > 0 && self.expires_at_ms <= crate::server::logging::now_ms() + } + + /// 问一次「用户授权完了没」。 + /// + /// 返回 `Ok(true)` 表示可以取令牌了。**网络类失败当 pending 处理**(返回 + /// `Ok(false)`):那是「这次没问成」而不是「问成了但被拒」,把它当致命错误 + /// 会让一次抖动就打断用户正在进行的授权。 + pub async fn check(&self) -> Result { + let url = format!( + "{}{}?state={}", + broker_base_url(), + BROKER_CHECK_PATH, + urlencode(&self.state) + ); + let response = send_raw( + "GET", + &url, + None, + &[("accept".to_string(), "application/json".to_string())], + None, + Some(REQUEST_TIMEOUT_MS), + ) + .await + .map_err(|error| login_transport_error("登录轮询", &error))?; + // 5xx 与畸形体当 pending(与 ZCode 轮询同一口径) + if response.status >= 500 { + return Ok(false); + } + if !response.ok { + return Ok(false); + } + let Some(payload) = response.payload else { + return Ok(false); + }; + Ok(payload.get("valid").and_then(Value::as_bool).unwrap_or(false)) + } + + /// 取回令牌包(`check` 为 true 之后再调)。 + pub async fn fetch_token(&self) -> Result { + let url = format!( + "{}{}?state={}", + broker_base_url(), + BROKER_TOKEN_PATH, + urlencode(&self.state) + ); + let response = send_raw( + "GET", + &url, + None, + &[("accept".to_string(), "application/json".to_string())], + None, + Some(REQUEST_TIMEOUT_MS), + ) + .await + .map_err(|error| login_transport_error("登录取令牌", &error))?; + let payload = response.payload.ok_or_else(|| { + GatewayError::with_status(502, "AtomCode 登录取令牌:代理响应不是 JSON") + })?; + if !response.ok { + return Err(GatewayError::with_status( + 502, + format!( + "AtomCode 登录取令牌失败(HTTP {}):{}", + response.status, + summarize(&payload) + ), + )); + } + parse_token_bundle(&payload) + } +} + +/// 续期:`POST {broker}/oauth/refresh`,body 只有 `{"refresh_token": …}`。 +/// +/// ── 为什么失败要区分「refresh 失效」与「其它」──────────────── +/// `refresh_token` 已失效(HTTP 401 / 代理报该码)是**用户必须重新登录**的信号, +/// 归类成 401 交给宿主会让界面明确提示重登;而一次网络抖动或 5xx 只是「这次没刷成」, +/// 归类成 502/504 由上层按可重试处理。 +/// +/// ⚠️ 调用者必须保证**单飞**(同一账号同一时刻只有一次续期在跑): +/// 续期会轮换 refresh token,两个并发续期互相作废、后写的那次还会覆盖掉 +/// 前一次拿回的新 refresh(见 `core::refresh_flight`)。 +pub async fn refresh(refresh_token: &str) -> Result { + if refresh_token.trim().is_empty() { + return Err(GatewayError::with_status( + 401, + "AtomCode 账号没有 refresh_token,无法续期,请重新登录", + )); + } + let url = format!("{}{}", broker_base_url(), BROKER_REFRESH_PATH); + let body = json!({ "refresh_token": refresh_token }); + let response = send_raw( + "POST", + &url, + Some(&body), + &[ + ("accept".to_string(), "application/json".to_string()), + ("content-type".to_string(), "application/json".to_string()), + ], + None, + Some(REQUEST_TIMEOUT_MS), + ) + .await + .map_err(|error| login_transport_error("续期", &error))?; + + let payload = response.payload.unwrap_or(Value::Null); + if response.status == 401 || response.status == 403 { + return Err(GatewayError::with_status( + 401, + format!( + "AtomGit refresh_token 已失效,请重新登录({})", + summarize(&payload) + ), + )); + } + if !response.ok { + return Err(GatewayError::with_status( + 502, + format!( + "AtomCode 续期失败(HTTP {}):{}", + response.status, + summarize(&payload) + ), + )); + } + parse_token_bundle(&payload) +} + +/// 把传输层错误统一成带中文前缀的网关错误。 +fn login_transport_error(stage: &str, error: &reqwest::Error) -> GatewayError { + if error.is_timeout() { + GatewayError::with_status(504, format!("AtomCode {stage}超时")) + } else { + GatewayError::with_status(502, format!("AtomCode {stage}失败: {error}")) + } +} + +/// 把错误体压成一行可读文案(不把整块 HTML 塞进错误消息) +fn summarize(payload: &Value) -> String { + let text = payload + .get("message") + .and_then(Value::as_str) + .or_else(|| payload.get("detail").and_then(|detail| detail.get("message")).and_then(Value::as_str)) + .or_else(|| payload.get("error").and_then(Value::as_str)) + .map(str::trim) + .filter(|value| !value.is_empty()) + .map(str::to_string) + .unwrap_or_else(|| { + let raw = payload.to_string(); + raw.chars().take(180).collect() + }); + text +} + +/// 极小的 query 转义(本家只有 state / plan_type 两个参数,都是 ASCII 记号)。 +/// +/// 不引 `urlencoding` 依赖:本文件的输入来自代理自己下发的 `state`, +/// 但仍按规范转义,避免将来 state 形态变化时把参数拼歪(`&` / `=` / `?` 一旦 +/// 出现在 state 里,不转义就会截断 query)。 +pub fn urlencode(value: &str) -> String { + let mut out = String::with_capacity(value.len()); + for byte in value.bytes() { + match byte { + b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => { + out.push(byte as char) + } + _ => out.push_str(&format!("%{byte:02X}")), + } + } + out +} + +#[cfg(test)] +mod tests { + use super::*; + + fn bundle(access: &str, refresh: &str, expires_in: i64) -> TokenBundle { + TokenBundle { + access_token: access.to_string(), + refresh_token: refresh.to_string(), + expires_in, + ..Default::default() + } + } + + /// 登录响应解析:`user` 整块缺失也要能解析(官方注释:代理可省略)。 + #[test] + fn token_bundle_parses_with_and_without_user() { + let full = json!({ + "access_token": "at", "refresh_token": "rt", "token_type": "Bearer", + "expires_in": 604800, + "user": {"id": "42", "username": "qq_1", "name": "张三", "email": "a@b.c", "avatar_url": "https://x/y.png"} + }); + let parsed = parse_token_bundle(&full).expect("应可解析"); + assert_eq!(parsed.access_token, "at"); + assert_eq!(parsed.refresh_token, "rt"); + assert_eq!(parsed.expires_in, 604_800); + assert_eq!(parsed.user_id, "42"); + assert_eq!(parsed.name, "张三"); + + let bare = json!({ "access_token": "at" }); + let parsed = parse_token_bundle(&bare).expect("缺 user 也应可解析"); + assert_eq!(parsed.access_token, "at"); + assert!(parsed.user_id.is_empty()); + assert_eq!(parsed.expires_in, 0); + } + + /// 没有 access_token 就是失败(不能拿空串去发请求)。 + #[test] + fn token_bundle_requires_access_token() { + let error = parse_token_bundle(&json!({"refresh_token": "rt"})).expect_err("应报错"); + assert_eq!(error.status_code, 502); + } + + /// **核心护栏**:续期省略 `refresh_token` 时不能把记录刷成空。 + /// + /// 官方注释原文(`oauth.go:350`):代理可能省略 refresh_token/expires_in/user。 + /// 整包替换会让账号在一次「看起来成功的」续期之后失去续期能力。 + #[test] + fn refresh_omitting_the_new_refresh_token_keeps_the_old_one() { + let previous = bundle("old-at", "old-rt", 604_800); + let rotated = bundle("new-at", "", 0); + let merged = rotated.merge_into(&previous); + assert_eq!(merged.access_token, "new-at"); + assert_eq!(merged.refresh_token, "old-rt", "新的 refresh 为空时必须保留旧的"); + assert_eq!(merged.expires_in, 604_800); + } + + /// 正常轮换:新的一对都写回(旧 access 已失效,绝不能留)。 + #[test] + fn refresh_with_a_new_pair_replaces_both() { + let previous = bundle("old-at", "old-rt", 604_800); + let rotated = bundle("new-at", "new-rt", 604_800); + let merged = rotated.merge_into(&previous); + assert_eq!(merged.access_token, "new-at"); + assert_eq!(merged.refresh_token, "new-rt"); + } + + /// 身份字段不该在一次续期后消失。 + #[test] + fn refresh_keeps_identity_when_the_broker_omits_user() { + let mut previous = bundle("old-at", "old-rt", 604_800); + previous.user_id = "42".into(); + previous.name = "张三".into(); + let merged = bundle("new-at", "new-rt", 0).merge_into(&previous); + assert_eq!(merged.user_id, "42"); + assert_eq!(merged.name, "张三"); + } + + /// 过期时刻由 `now + expires_in` 推(令牌非 JWT,没有 exp 可读)。 + #[test] + fn expires_at_is_now_plus_expires_in() { + let with_ttl = bundle("at", "rt", 604_800); + assert_eq!(with_ttl.expires_at(1_700_000_000_000), 1_700_000_000_000 + 604_800_000); + // 代理省略 expires_in 时判不出来(0),而不是猜一个值 + assert_eq!(bundle("at", "rt", 0).expires_at(1_700_000_000_000), 0); + } + + /// 续期没有 refresh_token 时必须报 401(提示重登),而不是发一个空 body。 + #[test] + fn refresh_without_a_refresh_token_is_an_auth_error() { + let error = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .expect("runtime") + .block_on(refresh(" ")) + .expect_err("空 refresh_token 应报错"); + assert_eq!(error.status_code, 401); + assert!(error.message.contains("重新登录")); + } + + /// query 转义:state 里出现 `&` / `=` / `?` 时不得截断参数。 + #[test] + fn urlencode_escapes_query_metacharacters() { + assert_eq!(urlencode("abc-123_x.y~z"), "abc-123_x.y~z"); + assert_eq!(urlencode("a&b=c?d"), "a%26b%3Dc%3Fd"); + // 中文(state 理论上不会是,但转义必须安全) + assert_eq!(urlencode("中"), "%E4%B8%AD"); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/usage.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/usage.rs new file mode 100644 index 00000000..af8eb835 --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/atomcode/usage.rs @@ -0,0 +1,361 @@ +//! AtomCode 的额度读数:`status-v2` 的**滚动调用次数窗口**。 +//! +//! ── 这家的额度口径与别家都不一样(读之前先看这条)───────────── +//! 不是积分、不是 token 余额,而是**每窗口可调用多少次**。实测(2026-10-01, +//! 记录在本地 Go 参考 `credits.go:37-38`): +//! +//! > The plan's real quota is a CALL COUNT per window, not a token budget: +//! > the free tier measured 2026-10-01 allows 200 calls per 5 hours. +//! +//! `status-v2` 里同时存在 `window_token_limit`(**可空**)与 +//! `rate_limit_windows[]`(真正的口径)。本模块只读后者: +//! 前者在免费档上是 null,把它当主读数会让界面显示一片空白。 +//! +//! ── 只展示服务端让展示的窗口 ────────────────────────────────── +//! 每个窗口带 `show_enable`,只有 `1` 的才进界面(与本地 Go 参考 +//! `credits.go:131-139` 同一口径)。服务端可能在同一个账号上挂多个规则 +//! (5 小时 / 日 / 月),把不展示的那些也画出来会让用户看到互相矛盾的额度。 +//! +//! ── 归一形状(与 loomy/balance.rs 同款,界面真正读的那一套)────── +//! ```text +//! { available, unit, wallets: [{type, displayName, balance, remaining, limit, …}], raw } +//! ``` +//! ⚠️ 曾经有一家(灵犀)在这处自造了 `total_balance` / `currency` 两个键, +//! 结果账号页余额列**空白**而探针还全绿(探针跟着实现断言)。所以这里 +//! **严格照抄 loomy 的键名**,不自造。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use serde_json::{Value, json}; + +use crate::server::core::account_store::AccountStore; +use crate::server::errors::GatewayError; +use crate::server::logging; + +use super::credentials::{AtomcodeCredentials, from_record}; +use super::endpoints::REQUEST_TIMEOUT_MS; +use super::models::fetch_status; + +/// 一个滚动窗口(`rate_limit_windows[]` 的一条) +#[derive(Clone, Debug)] +struct QuotaWindow { + /// 窗口小时数 + hours: i64, + /// 本窗口允许的调用次数 + call_limit: i64, + /// 已用次数 + used: i64, + /// 服务端给的重置时刻文案(如 `12:13`) + reset_display: String, + /// 本窗口是否已耗尽 + exhausted: bool, +} + +/// 从 `status-v2` 里取出「服务端让展示」的窗口 +fn shown_windows(payload: &Value) -> Vec { + let mut windows = Vec::new(); + let Some(items) = payload.get("rate_limit_windows").and_then(Value::as_array) else { + return windows; + }; + for item in items { + // 只有 show_enable == 1 的窗口才展示 + if item.get("show_enable").and_then(Value::as_i64) != Some(1) { + continue; + } + let call_limit = item.get("call_limit").and_then(Value::as_i64).unwrap_or(0); + if call_limit <= 0 { + continue; + } + windows.push(QuotaWindow { + hours: item.get("window_hours").and_then(Value::as_i64).unwrap_or(0), + call_limit, + used: item.get("calls_used").and_then(Value::as_i64).unwrap_or(0), + reset_display: item + .get("reset_at_display") + .and_then(Value::as_str) + .unwrap_or("") + .to_string(), + exhausted: item + .get("quota_exhausted") + .and_then(Value::as_bool) + .unwrap_or(false), + }); + } + windows +} + +/// 套餐名与到期文案(`codingplan_free` 块) +fn plan_of(payload: &Value) -> (String, String) { + let plan = payload.get("codingplan_free"); + let name = plan + .and_then(|plan| plan.get("plan_name")) + .and_then(Value::as_str) + .unwrap_or("") + .to_string(); + let expires = plan + .and_then(|plan| plan.get("expires_at")) + .and_then(Value::as_str) + .unwrap_or("") + .to_string(); + (name, expires) +} + +/// 把 `status-v2` 归成界面认的额度形状(**纯函数**,便于测试) +/// +/// ── 一条**必须**遵守的契约:读不到额度就不要写 `available` ────── +/// 限额判定走 `core::usage_records::extract_remaining`,它的规则是 +/// 「`unlimited` → 判不出;否则看 `totalLeft`;否则看 `available`」—— +/// 也就是说 **`available` 一旦出现,就会被当成真实读数**。 +/// +/// 2026-10-17 我在这一点上写错过:没有窗口时也写 `available: 0`,于是「读不到额度」 +/// 被限制器理解成「余额为 0」,账号当场被标成「余额不足 / 已跳过」—— +/// 用户看到的现象是「领完套餐还是余额不足」。**没有窗口 ≠ 余额为 0**: +/// 前者是「这个响应我没读懂」,后者是「确实用完了」,两者必须分开。 +/// 所以这里只在**确实解析出窗口**时才写 `available` / `subscription` 的那两个数; +/// 一个都没有就只回 `wallets: []`(记录会被 `usage_records` 摘除、不拦账号, +/// 余额列留空 —— 与本仓「读不到的数值留空显示」同一口径)。 +pub fn shape_usage(payload: &Value) -> Value { + let windows = shown_windows(payload); + let (plan_name, plan_expires) = plan_of(payload); + + // 诊断:把上游的真实字段形态记进**详细日志**。额度是按字段名解析的, + // 名字一变就静默归零(今天就栽在这),所以留一条能事后对账的痕迹。 + // 只记键名与两条额度数组(都不含凭据),且不进普通日志。 + logging::verbose( + "[AtomCode]", + &format!( + "status-v2 形态:顶层键={:?} rate_limit_windows={} current_usage={}", + payload + .as_object() + .map(|object| object.keys().cloned().collect::>()) + .unwrap_or_default(), + payload + .get("rate_limit_windows") + .map(|value| serde_json::to_string(value).unwrap_or_default()) + .map(|text| text.chars().take(600).collect::()) + .unwrap_or_else(|| "<缺>".to_string()), + payload + .get("current_usage") + .map(|value| serde_json::to_string(value).unwrap_or_default()) + .map(|text| text.chars().take(400).collect::()) + .unwrap_or_else(|| "<缺>".to_string()), + ), + ); + + let wallets: Vec = windows + .iter() + .map(|window| { + let remaining = (window.call_limit - window.used).max(0); + let label = if window.hours > 0 { + format!("近 {} 小时调用次数", window.hours) + } else { + "调用次数窗口".to_string() + }; + let mut wallet = json!({ + "type": "rolling_window", + "displayName": label, + "balance": remaining, + "remaining": remaining, + "limit": window.call_limit, + }); + if let Some(object) = wallet.as_object_mut() { + object.insert("used".to_string(), Value::from(window.used)); + if !window.reset_display.is_empty() { + object.insert( + "resetAt".to_string(), + Value::String(window.reset_display.clone()), + ); + } + if window.exhausted { + object.insert("exhausted".to_string(), Value::Bool(true)); + } + } + wallet + }) + .collect(); + + // 主读数 = 第一个窗口的剩余次数。 + // ⚠️ **只在真的解析出窗口时才写**(读到 0 条 = 这份响应我没读懂, + // 不是「余额为 0」)—— 理由见本函数文档「必须遵守的契约」那一节。 + let available = windows + .first() + .map(|window| (window.call_limit - window.used).max(0)); + + let mut subscription = json!({ "status": if plan_name.is_empty() { "unclaimed" } else { "active" } }); + if let Some(object) = subscription.as_object_mut() { + if !plan_name.is_empty() { + object.insert("planName".to_string(), Value::String(plan_name)); + } + // 上游给的是**日期字符串**,而 `expireAt` 在别家是毫秒数字。 + // 混用会让界面把一串日期当毫秒渲染,所以把原文放进独立的键, + // 不占用 `expireAt`(宁可这一格留空,也不要显示一个错的时刻)。 + if !plan_expires.is_empty() { + object.insert("planExpiresAt".to_string(), Value::String(plan_expires)); + } + // 同上:没有窗口就不写这两个数(写了 `available` 的兄弟键也一样会被 + // 当成读数来源) + if let Some(first) = windows.first() { + object.insert( + "remainQuota".to_string(), + Value::from((first.call_limit - first.used).max(0)), + ); + object.insert("totalQuota".to_string(), Value::from(first.call_limit)); + } + } + + let mut shaped = json!({ + "unit": "次", + "wallets": wallets, + "subscription": subscription, + "raw": payload, + }); + // 只有解析出窗口才回 `available`(见函数文档):这是限额判定唯一的读数来源 + if let Some(available) = available { + if let Some(object) = shaped.as_object_mut() { + object.insert("available".to_string(), Value::from(available)); + } + } + shaped +} + +/// 查额度:读记录 → 取 `status-v2` → 归一。 +pub async fn query_usage( + store: &AccountStore, + account_id: &str, +) -> Result { + let record = store.atomcode_account_record(account_id); + if !account_id.is_empty() && record.is_none() { + return Err(GatewayError::with_status( + 401, + format!("AtomCode 账号 {account_id} 不存在(请重新添加)"), + )); + } + let credentials: AtomcodeCredentials = from_record(record.as_ref())?; + let payload = fetch_status(&credentials, REQUEST_TIMEOUT_MS).await?; + Ok(shape_usage(&payload)) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn status_with_windows() -> Value { + json!({ + "codingplan_free": {"plan_name": "CodingPlan Lite-体验版", "expires_at": "2026-11-08 12:00:00"}, + "rate_limit_windows": [ + {"show_enable": 1, "window_hours": 5, "call_limit": 200, "calls_used": 37, + "reset_at_display": "12:13", "quota_exhausted": false}, + {"show_enable": 0, "window_hours": 24, "call_limit": 1000, "calls_used": 900} + ] + }) + } + + /// **核心口径**:额度是调用次数窗口,`available` 是剩余次数。 + #[test] + fn available_is_remaining_calls_of_the_first_shown_window() { + let shaped = shape_usage(&status_with_windows()); + assert_eq!(shaped.get("available"), Some(&Value::from(163))); + assert_eq!(shaped.get("unit"), Some(&Value::from("次"))); + } + + /// `show_enable != 1` 的窗口不进界面(服务端不让展示的规则画出来会互相矛盾)。 + #[test] + fn windows_the_server_hides_are_not_drawn() { + let shaped = shape_usage(&status_with_windows()); + let wallets = shaped.get("wallets").and_then(Value::as_array).expect("wallets"); + assert_eq!(wallets.len(), 1, "只该有一个展示窗口"); + assert_eq!(wallets[0].get("limit"), Some(&Value::from(200))); + assert_eq!(wallets[0].get("remaining"), Some(&Value::from(163))); + assert_eq!(wallets[0].get("used"), Some(&Value::from(37))); + assert!(wallets[0].get("displayName").and_then(Value::as_str).unwrap_or("").contains("5 小时")); + } + + /// 归一形状的键名必须与别家一致(灵犀曾自造键名导致界面空白)。 + #[test] + fn shape_uses_the_shared_key_names() { + let shaped = shape_usage(&status_with_windows()); + for key in ["available", "unit", "wallets", "subscription", "raw"] { + assert!(shaped.get(key).is_some(), "缺键 {key}"); + } + let wallet = &shaped["wallets"][0]; + for key in ["type", "displayName", "balance", "remaining", "limit"] { + assert!(wallet.get(key).is_some(), "wallet 缺键 {key}"); + } + // 自造键不得出现(它们正是上一家空白的原因) + assert!(shaped.get("total_balance").is_none()); + assert!(shaped.get("currency").is_none()); + } + + /// 上游给的到期是**日期字符串**,不能塞进别家读作毫秒的 `expireAt`。 + #[test] + fn plan_expiry_text_does_not_impersonate_a_millisecond_timestamp() { + let shaped = shape_usage(&status_with_windows()); + let subscription = &shaped["subscription"]; + assert!(subscription.get("expireAt").is_none(), "不要把日期字符串写进 expireAt"); + assert_eq!( + subscription.get("planExpiresAt").and_then(Value::as_str), + Some("2026-11-08 12:00:00") + ); + assert_eq!(subscription.get("planName").and_then(Value::as_str), Some("CodingPlan Lite-体验版")); + assert_eq!(subscription.get("status").and_then(Value::as_str), Some("active")); + } + + /// 没有套餐时如实标 unclaimed(不编造一个 0 额度窗口)。 + #[test] + fn account_without_a_plan_reports_unclaimed() { + let shaped = shape_usage(&json!({})); + assert_eq!( + shaped["subscription"].get("status").and_then(Value::as_str), + Some("unclaimed") + ); + assert_eq!(shaped.get("wallets").and_then(Value::as_array).map(Vec::len), Some(0)); + // ⚠️ **不许**回 `available: 0` —— 读不到窗口是「这份响应没读懂」, + // 不是「余额为 0」。写成 0 会被 `usage_records::extract_remaining` 当成 + // 真实读数,账号当场被标成「余额不足/已跳过」(2026-10-17 的实际故障)。 + assert!( + shaped.get("available").is_none(), + "读不到窗口时不得写 available:那会被限额判定当成余额为 0" + ); + } + + /// 读不到窗口时 `subscription` 里也不该出现额度数字(同一条契约的兄弟键)。 + #[test] + fn no_windows_means_no_quota_numbers_anywhere() { + let shaped = shape_usage(&json!({ "codingplan_free": {"plan_name": "CodingPlan Lite-体验版", "expires_at": "2026-11-08 12:00:00"} })); + assert!(shaped.get("available").is_none()); + let subscription = &shaped["subscription"]; + assert!(subscription.get("remainQuota").is_none(), "{subscription}"); + assert!(subscription.get("totalQuota").is_none(), "{subscription}"); + // 套餐名仍然要透出去(那是读到了的字段) + assert_eq!(subscription.get("planName").and_then(Value::as_str), Some("CodingPlan Lite-体验版")); + } + + /// 耗尽的窗口要能看出来(界面把它收进折叠区)。 + #[test] + fn exhausted_windows_are_flagged() { + let payload = json!({ + "rate_limit_windows": [ + {"show_enable": 1, "window_hours": 5, "call_limit": 200, "calls_used": 200, + "quota_exhausted": true} + ] + }); + let shaped = shape_usage(&payload); + assert_eq!(shaped["wallets"][0].get("remaining"), Some(&Value::from(0))); + assert_eq!(shaped["wallets"][0].get("exhausted"), Some(&Value::Bool(true))); + assert_eq!(shaped.get("available"), Some(&Value::from(0))); + } + + /// 已用超过上限(上游口径变化)时剩余不得为负。 + #[test] + fn remaining_never_goes_negative() { + let payload = json!({ + "rate_limit_windows": [ + {"show_enable": 1, "window_hours": 5, "call_limit": 100, "calls_used": 130} + ] + }); + let shaped = shape_usage(&payload); + assert_eq!(shaped.get("available"), Some(&Value::from(0))); + assert_eq!(shaped["wallets"][0].get("remaining"), Some(&Value::from(0))); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/catalog.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/catalog.rs index cd8a9b8b..8437c396 100644 --- a/desktop-tauri/src-tauri/server/src/server/core/providers/catalog.rs +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/catalog.rs @@ -196,6 +196,20 @@ pub(crate) fn refresh_meta(kind: ProviderKind) -> (bool, i64) { super::chatglm::models::remote_refreshed(), super::chatglm::models::last_refreshed_at(), ), + // AtomCode:清单来自 `GET {codingplan}/coding-plan/models-v2`。 + // 本家**没有内置兜底清单**(写死的模型点一下可能是「参数错误」, + // 见 `atomcode/models.rs` 的模块头),所以「有目录」就是「远程拉到过」。 + ProviderKind::Atomcode => ( + super::atomcode::models::remote_refreshed(), + super::atomcode::models::last_refreshed_at(), + ), + // LobsterAI:清单来自 `GET {api}/api/models/available`(带 keyfrom query)。 + // 拉不到时回落内置的 19 条静态表,所以「有远程内容」才算远程来源 + // —— 与 Loomy / CommandCode 同一口径(`remote_refreshed` 只看落地的那些)。 + ProviderKind::LobsterAi => ( + super::lobsterai::models::remote_refreshed(), + super::lobsterai::models::last_refreshed_at(), + ), } } diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/catalog_cache.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/catalog_cache.rs index 12b4c31e..656f9f12 100644 --- a/desktop-tauri/src-tauri/server/src/server/core/providers/catalog_cache.rs +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/catalog_cache.rs @@ -108,6 +108,11 @@ pub const SCOPE_OFFICEACE: &str = "officeace"; /// 智谱清言(`chatglm.cn`)的模型目录缓存槽 pub const SCOPE_CHATGLM: &str = "chatglm"; +/// AtomCode(AtomGit / GitCode)的模型目录缓存槽 +pub const SCOPE_ATOMCODE: &str = "atomcode"; + +/// LobsterAI(网易有道「有道龙虾」)的模型目录缓存槽 +pub const SCOPE_LOBSTERAI: &str = "lobsterai"; /// 全部 scope(事实来源:`cached_scopes` 按它遍历;新增一家时加在这里)。 pub const ALL_SCOPES: &[&str] = &[ @@ -131,6 +136,8 @@ pub const ALL_SCOPES: &[&str] = &[ SCOPE_ANTIGRAVITY, SCOPE_OFFICEACE, SCOPE_CHATGLM, + SCOPE_ATOMCODE, + SCOPE_LOBSTERAI, ]; /// 一份清单缓存 diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/chat.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/chat.rs new file mode 100644 index 00000000..5ec76a6e --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/chat.rs @@ -0,0 +1,399 @@ +//! LobsterAI 的转发:**同方言透传 + 四处上游强制的改写**。 +//! +//! ── 为什么这一层也很薄 ────────────────────────────────────── +//! 对话端点是 OpenAI 兼容的原生协议(`POST /api/proxy/v1/chat/completions`), +//! 网关本身就是 OpenAI Chat Completions,所以 body 几乎逐字节上行、上游的 SSE +//! 帧逐字节下发(`executor.go:16-27` 的模块头:语言不同,但「原样透传 + 只改 +//! 必须改的」这条口径一样)。剩下的只有四件格式本身表达不了的事。 +//! +//! ── 只改四处,每处都有它非改不可的理由(全部实测)──────────────── +//! 1. **`role: "developer"` → `"system"`**:上游不认 OpenAI 新版角色, +//! 带它的请求被网关以 **HTTP 502「角色信息不正确」**拒绝(system / user +//! 均 200)。两者在 OpenAI 规范里语义相同,按 `system` 下发 +//! (`executor.go:83-86` 的实测注 + `demoteDeveloperMessages`)。 +//! ⚠️ 本仓的通用转发层在「开发者角色兼容」开关打开时也会做同一件事 +//! (`upstream::payload::send_body` 的 ⓪ 角色归一层,默认开)—— 这里再写 +//! 一遍不是冗余:开关关掉时它就是唯一的一道,且对已归一的体是**幂等**的。 +//! 2. **`tool_choice` 在 `""` / `"none"` / `null` 时删掉**:客户端可能原样 +//! 转发一个上游拒绝的取值(`executor.go:114-123`)。 +//! 3. **绝不下发 `prompt_cache_key`**:那是腾讯后端的机制,本家不知道认不认 +//! (`executor.go:63-64` 的原文);存在就删。 +//! 4. **`model` 走一次名字解析**(展示名 / 规范化别名 → 线上 id): +//! 与 `atomcode/chat.rs` 同一手法,认不出时**原样发**,不猜。 +//! 注意通用转发层已经按目录把 body 里的模型名改写成了线上名 +//! (`catalog::wire_target_for_provider`),所以这一步通常是一次幂等的 +//! 确认;留着它是为了「客户端直接点名一个规范化别名」那条路。 +//! +//! ── 两处**刻意不做**的改写(都是实测结论)────────────────────── +//! · **不动 `stream`**:本家 `stream:false` 上游直接回 HTTP 500 +//! (`executor.go:18-21`),而本网关的转发入口「无论客户端要不要流式,上游 +//! 恒以 `stream:true` 请求」(`core/upstream/mod.rs` 的注释原文)—— +//! 这条要求天然满足,**碰它反而会把它弄坏**。 +//! · **不转换图片**:上游只收 `{type:"image_url",image_url:{url}}`,收到 +//! `{type:"image"}` 会回 HTTP 500。我们**不做**形状修补 —— 让上游把它的 +//! 真实错误如实报给客户端,好过我们替它猜一种可能不对的形状 +//! (与本仓「广告能力要有实测依据、不编造」的口径一致)。 +//! +//! ── 客户端版本头为什么要一个进程级快照 ────────────────────────── +//! `X-LobsterAI-Client-Version` 是功能性头(缺它 / 给空串会被按「不认识的 +//! 客户端」对待,见 `endpoints.rs` 的模块头),而版本号要**异步**从上游更新 +//! 接口取(`version::resolve_client_version()`)。但 `ProviderAdapter` 的 +//! `build_chat_request` 是**同步**的(trait 契约,不能改成 async),所以这里 +//! 用一份进程级快照:异步侧([`prime_client_version`],由目录刷新 / 签到 / +//! 余额这些本来就要取版本的异步路径调用)写、同步侧([`client_version_header`]) +//! 读;快照还没填时退回已知可用的兜底常量,**绝不发空版本头**(与 Go 参考 +//! `headers.go::resolveHeaderVersion` 同一口径)。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use std::sync::{Mutex, OnceLock}; + +use axum::http::HeaderMap; +use serde_json::Value; + +use crate::server::core::providers::adapter::ChatRequestPlan; +use crate::server::errors::GatewayError; + +use super::endpoints::{CLIENT_CAPABILITIES, USER_AGENT, chat_url}; +use super::models; +use super::version; + +/// 进程级版本快照(由 [`prime_client_version`] 写、[`client_version_header`] 读) +static CLIENT_VERSION: OnceLock>> = OnceLock::new(); + +fn version_slot() -> &'static Mutex> { + CLIENT_VERSION.get_or_init(|| Mutex::new(None)) +} + +/// 解析一次客户端版本并写进快照,返回解析结果。 +/// +/// **异步入口**:目录刷新 / 签到 / 余额查询这些异步路径都调它 —— 一次调用既 +/// 拿到自己要用的版本,也顺手把 [`client_version_header`] 的快照填上(幂等, +/// 底层 `version::resolve_client_version` 自带 12 小时缓存)。 +pub async fn prime_client_version() -> String { + let resolved = version::resolve_client_version().await; + store_client_version(&resolved); + resolved +} + +/// 写入快照(空串不覆盖:宁可留着上一次的好值,也不要为一次瞬时失败清空) +fn store_client_version(value: &str) { + let trimmed = value.trim(); + if trimmed.is_empty() { + return; + } + let mut guard = match version_slot().lock() { + Ok(guard) => guard, + Err(poisoned) => poisoned.into_inner(), + }; + *guard = Some(trimmed.to_string()); +} + +/// 同步读取 `X-LobsterAI-Client-Version` 的值。 +/// +/// 快照没填过(进程刚起、还没走过任何异步取版本的路径)时退回兜底常量 —— +/// **绝不发空版本头**(空串会被服务端按「不认识的客户端」对待)。 +pub fn client_version_header() -> String { + let cached = match version_slot().lock() { + Ok(guard) => guard.clone(), + Err(poisoned) => poisoned.into_inner().clone(), + }; + match cached { + Some(value) if !value.trim().is_empty() => value, + _ => version::fallback_version().to_string(), + } +} + +/// 上游不认的 OpenAI 角色名(带它会被网关以 HTTP 502「角色信息不正确」拒绝) +const DEVELOPER_ROLE: &str = "developer"; +/// 归一后的角色名(两者在 OpenAI 规范里语义相同) +const DEMOTED_ROLE: &str = "system"; + +/// 把 `role: "developer"` 的消息改成 `"system"`(原地改写,非对象条目原样放过)。 +/// +/// 见模块头的实测表:这是上游的硬要求,不是风格偏好。 +fn demote_developer_messages(object: &mut serde_json::Map) { + let Some(messages) = object.get_mut("messages").and_then(Value::as_array_mut) else { + return; + }; + for message in messages.iter_mut() { + let Some(entry) = message.as_object_mut() else { + continue; + }; + let is_developer = entry + .get("role") + .and_then(Value::as_str) + .is_some_and(|role| role == DEVELOPER_ROLE); + if is_developer { + entry.insert("role".to_string(), Value::String(DEMOTED_ROLE.to_string())); + } + } +} + +/// `tool_choice` 是否属于「上游会拒绝、必须删掉」的取值(`""` / `"none"` / `null`)。 +fn tool_choice_must_go(value: &Value) -> bool { + match value { + Value::Null => true, + Value::String(text) => text.trim().is_empty() || text.trim().eq_ignore_ascii_case("none"), + _ => false, + } +} + +/// 准备一次上游调用。 +pub fn build_chat_request( + account: &Value, + body: &Value, + _client_headers: &HeaderMap, +) -> Result { + // 令牌读取口径与 `atomcode/chat.rs` 一致(会话形态的 `auth.accessToken`) + let access_token = account + .get("auth") + .and_then(|auth| auth.get("accessToken")) + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or("") + .to_string(); + if access_token.is_empty() { + return Err(GatewayError::with_status( + 401, + "LobsterAI 账号缺少访问令牌,无法转发(请重新登录)", + )); + } + + let requested = body + .get("model") + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or("") + .to_string(); + if requested.is_empty() { + return Err(GatewayError::with_status(400, "请求缺少 model 字段")); + } + + let mut payload = body.clone(); + let Some(object) = payload.as_object_mut() else { + return Err(GatewayError::with_status(400, "请求体不是 JSON 对象")); + }; + + // ① developer → system(上游 502「角色信息不正确」的唯一成因,见模块头) + demote_developer_messages(object); + // ② tool_choice:`""` / `"none"` / `null` 一律删掉 + if object.get("tool_choice").is_some_and(tool_choice_must_go) { + object.remove("tool_choice"); + } + // ③ 绝不下发 prompt_cache_key(腾讯后端机制,本家不认) + object.remove("prompt_cache_key"); + // ④ 模型名解析:展示名 / 规范化别名 → 线上 id;认不出原样发 + let wire_model = + models::upstream_id_for(&requested).unwrap_or_else(|| requested.clone()); + object.insert("model".to_string(), Value::String(wire_model)); + + // `stream` **不动**:本网关的转发入口恒以 `stream:true` 请求上游(见模块头), + // 本家 `stream:false` 会回 HTTP 500 —— 碰它反而会把它弄坏。 + + let headers: Vec<(String, String)> = vec![ + ( + "authorization".to_string(), + format!("Bearer {access_token}"), + ), + // 只收 SSE:Accept 两个都报(照 Go `chatHeaders` 的 `text/event-stream, application/json`) + ( + "accept".to_string(), + "text/event-stream, application/json".to_string(), + ), + // ⚠️ `Content-Type` **必须显式发**:本仓的转发层用 `client.post(url).body(bytes)` + // (`core/upstream/request.rs` 的发送点),reqwest 的 `.body()` **不会**自动补 + // 这个头 —— 只有 `.json()` 才会。而剥掉它之后上游回的是**鉴权之后的** + // `{"code":500,"message":"服务器内部错误"}`(HTTP 500,约 46ms 即返), + // 跨模型稳定复现,看上去像「网关坏了」而不是「少了个头」。 + // 判据:同一账号打同一个域名的其它 POST(换码 `/api/auth/exchange`、 + // 签到)都带了它且成功;伪令牌探针证明上游**先鉴权**(401 code 40100), + // 所以 500 出在鉴权之后。两个参考实现也都把它列在四个基础头里 + // (TS `lobsteraiAuthHeaders`、Go `authHeaders`)。 + ( + "content-type".to_string(), + "application/json".to_string(), + ), + // 两个客户端头都是功能性的(能力位过滤模型、版本决定被当成哪个客户端) + ( + "x-lobsterai-client-capabilities".to_string(), + CLIENT_CAPABILITIES.to_string(), + ), + ( + "x-lobsterai-client-version".to_string(), + client_version_header(), + ), + // UA 固定不跟随真实版本(照抄参考实现,不是装饰) + ("user-agent".to_string(), USER_AGENT.to_string()), + ]; + + Ok(ChatRequestPlan::chat(chat_url(), headers, payload)) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + fn account() -> Value { + json!({ "auth": { "accessToken": "at-1" } }) + } + + fn build(body: Value) -> ChatRequestPlan { + match build_chat_request(&account(), &body, &HeaderMap::new()) { + Ok(plan) => plan, + Err(error) => panic!("应构造成功,却报错:{}", error.message), + } + } + + fn header_of<'a>(plan: &'a ChatRequestPlan, name: &str) -> Option<&'a str> { + plan.headers + .iter() + .find(|(key, _)| key.eq_ignore_ascii_case(name)) + .map(|(_, value)| value.as_str()) + } + + /// **首条护栏**:`developer` 角色必须被归一成 `system`(否则上游 502)。 + #[test] + fn developer_role_is_demoted_to_system() { + let plan = build(json!({ + "model": "glm-5.2", + "messages": [ + {"role": "developer", "content": "a"}, + {"role": "user", "content": "b"}, + {"role": "system", "content": "c"} + ] + })); + let messages = plan.body.get("messages").and_then(Value::as_array).expect("messages"); + assert_eq!(messages[0].get("role").and_then(Value::as_str), Some("system")); + assert_eq!(messages[1].get("role").and_then(Value::as_str), Some("user")); + assert_eq!(messages[2].get("role").and_then(Value::as_str), Some("system")); + // 内容不动 + assert_eq!(messages[0].get("content").and_then(Value::as_str), Some("a")); + } + + /// **第二道护栏**:chat 请求必须自带 `Content-Type: application/json`。 + /// + /// 本仓转发层用 `client.post(url).body(bytes)` 发包,reqwest 的 `.body()` + /// **不补**这个头(只有 `.json()` 会)。缺了它上游回的是**鉴权之后**的 + /// HTTP 500「服务器内部错误」,跨模型稳定复现 —— 症状像网关坏了,实际 + /// 只是少了一个头(成因与判据见 `build_chat_request` 里的注释)。 + #[test] + fn chat_request_carries_json_content_type() { + let plan = build(json!({"model": "glm-5.2", "messages": [{"role": "user", "content": "a"}]})); + assert_eq!(header_of(&plan, "content-type"), Some("application/json")); + } + + /// `tool_choice` 的三种「必须删」形态;`"auto"` / 对象形态**保留**。 + #[test] + fn rejected_tool_choice_values_are_dropped() { + for value in [json!(""), json!("none"), json!("NONE"), json!(null)] { + let plan = build(json!({"model": "m", "messages": [], "tool_choice": value})); + assert!( + plan.body.get("tool_choice").is_none(), + "tool_choice={value} 应被删掉" + ); + } + let kept = build(json!({"model": "m", "messages": [], "tool_choice": "auto"})); + assert_eq!( + kept.body.get("tool_choice").and_then(Value::as_str), + Some("auto") + ); + let forced = build(json!({ + "model": "m", "messages": [], + "tool_choice": {"type": "function", "function": {"name": "f"}} + })); + assert!(forced.body.get("tool_choice").is_some()); + } + + /// `prompt_cache_key` 无论客户端怎么给都不得下发。 + #[test] + fn prompt_cache_key_is_never_sent() { + let plan = build(json!({ + "model": "m", "messages": [], "prompt_cache_key": "abc" + })); + assert!(plan.body.get("prompt_cache_key").is_none()); + } + + /// **`stream` 不得被改写**(上游 stream:false 回 500;本网关入口恒置 true)。 + #[test] + fn stream_field_is_forwarded_untouched() { + let plan = build(json!({"model": "m", "messages": [], "stream": true})); + assert_eq!(plan.body.get("stream").and_then(Value::as_bool), Some(true)); + let plan = build(json!({"model": "m", "messages": []})); + assert!(plan.body.get("stream").is_none(), "原本没有就别替它加"); + } + + /// 头部:两个功能性客户端头 + UA + Bearer 必须在,且版本头非空。 + #[test] + fn load_bearing_headers_are_present() { + let plan = build(json!({"model": "m", "messages": []})); + assert_eq!( + header_of(&plan, "authorization"), + Some("Bearer at-1") + ); + assert_eq!( + header_of(&plan, "x-lobsterai-client-capabilities"), + Some(CLIENT_CAPABILITIES) + ); + let version = header_of(&plan, "x-lobsterai-client-version").expect("版本头必须存在"); + assert!(!version.trim().is_empty(), "绝不发空版本头"); + assert_eq!(header_of(&plan, "user-agent"), Some(USER_AGENT)); + assert_eq!( + header_of(&plan, "accept"), + Some("text/event-stream, application/json") + ); + assert!(plan.url.ends_with("/api/proxy/v1/chat/completions")); + } + + /// 其余字段**逐字不动**(同方言透传的全部价值)。 + #[test] + fn everything_else_is_forwarded_untouched() { + let original = json!({ + "model": "glm-5.2", + "messages": [{"role": "user", "content": "hi"}], + "tools": [{"type": "function", "function": {"name": "f"}}], + "temperature": 0.3, + "max_tokens": 1234, + "reasoning_effort": "high" + }); + let plan = build(original.clone()); + for key in ["messages", "tools", "temperature", "max_tokens", "reasoning_effort"] { + assert_eq!(plan.body.get(key), original.get(key), "字段 {key} 必须逐字透传"); + } + } + + /// 缺令牌 / 缺 model 时报可读错误,而不是发一个空请求出去。 + #[test] + fn missing_credentials_or_model_fail_fast() { + // `ChatRequestPlan` 没有实现 Debug(跨家共用类型),所以手写 match + let error = match build_chat_request( + &json!({"auth": {}}), + &json!({"model": "m", "messages": []}), + &HeaderMap::new(), + ) { + Ok(_) => panic!("缺令牌应当报错"), + Err(error) => error, + }; + assert_eq!(error.status_code, 401); + + let error = match build_chat_request(&account(), &json!({"messages": []}), &HeaderMap::new()) + { + Ok(_) => panic!("缺 model 应当报错"), + Err(error) => error, + }; + assert_eq!(error.status_code, 400); + } + + /// 快照写入读回;空值不覆盖上一次的好值。 + #[test] + fn version_snapshot_ignores_blank_writes() { + store_client_version("2026.9.4"); + assert_eq!(client_version_header(), "2026.9.4"); + store_client_version(" "); + assert_eq!(client_version_header(), "2026.9.4", "空串不得覆盖好值"); + store_client_version("2026.10.1"); + assert_eq!(client_version_header(), "2026.10.1"); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/checkin.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/checkin.rs new file mode 100644 index 00000000..c96eb7bb --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/checkin.rs @@ -0,0 +1,455 @@ +//! LobsterAI 的每日活动签到:**三步、幂等靠客户端**。 +//! +//! ── 三步协议(`credits.go:305-368`)────────────────────────── +//! ```text +//! 1. GET /api/client-activities/slot?placement=…&clientVersion=…&… +//! -> 找到「每日签到」那个活动位:slotState / activity.activityCode / +//! activity.configRevision +//! 2. GET /api/client-activities/{code}/context?configRevision=… +//! -> 活动状态:state.claimedToday 与 actions[](含不含 check_in) +//! 3. POST /api/client-activities/{code}/actions/check_in +//! 体 {configRevision, idempotencyKey:, payload:{}} +//! ``` +//! +//! ── 幂等是**客户端**的事,所以两道前置检查都必须做 ────────────── +//! 上游没有「今天签过了就返回已签」的幂等语义(否则不需要 idempotencyKey): +//! 幂等靠 ① 请求里那个 uuid4 的 `idempotencyKey`,② 第二步的两道预检 +//! (`state.claimedToday` 与 `actions` 里有没有 `check_in`)。**两道都要**: +//! 只看 `claimedToday` 会漏掉「活动在、但今天不可签」那一格 +//! (`credits.go:26-29` 的原话)。预检命中时如实回一句中性的「今天已签到」, +//! **不**再去打签到接口 —— 那会在上游不幂等的情况下把一天的奖励签重。 +//! +//! ── 发放额字段有**三段回退** ───────────────────────────────── +//! `data.result` 里的奖励名随活动 / 版本变化:`creditsGranted` → `rewardCredits` +//! → `credits`,取第一个能读到的(`credits.go:349-360` 的原文注释)。 +//! +//! ── 返回形状 ──────────────────────────────────────────────── +//! `{success, msg, …}` —— 与 `atomcode/checkin.rs` 同款,交给 +//! `billing::checkin::claim_result` 翻成签到结果行。「今天已签到」那句必须带 +//! 「已签到」三字:`billing::checkin::checkin_completed_today` 是按 msg 子串 +//! 判「当日已办」的(那样调度与台账才不会把一次中性结果当成失败去反复重打)。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use serde_json::{Value, json}; + +use crate::server::core::account_store::AccountStore; +use crate::server::core::auth_http::send_raw; +use crate::server::errors::GatewayError; +use crate::server::logging; + +use super::chat; +use super::credentials::{LobsterCredentials, from_record}; +use super::endpoints::{ + REQUEST_TIMEOUT_MS, USER_AGENT, activity_checkin_url, activity_context_url, activity_slot_url, +}; +use super::oauth::unwrap_envelope; +use super::uuid_v4; + +/// 活动上下文里表示「今天可签」的动作名 +pub const CHECK_IN_ACTION: &str = "check_in"; + +/// 活动位(第一步 `data` 里我们关心的三个字段) +#[derive(Clone, Debug, Default)] +struct ActivitySlot { + /// `slotState`(`available` 才可能可签) + slot_state: String, + /// `activity.activityCode` + activity_code: String, + /// `activity.configRevision`(原样留着,写进第二步的 query 与第三步的 body) + config_revision: Value, +} + +/// 从活动位 `data` 里取三个字段 +fn parse_slot(data: &Value) -> ActivitySlot { + let activity = data.get("activity"); + ActivitySlot { + slot_state: data + .get("slotState") + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or("") + .to_string(), + activity_code: activity + .and_then(|value| value.get("activityCode")) + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or("") + .to_string(), + config_revision: activity + .and_then(|value| value.get("configRevision")) + .cloned() + .unwrap_or(Value::Null), + } +} + +/// `configRevision` 渲染成 query / body 里那个数(整数不带小数尾巴,与 Go +/// `formatNumber` 同口径;读不出数时给 `0`,不猜)。 +fn config_revision_string(value: &Value) -> String { + if let Some(number) = value.as_i64() { + return number.to_string(); + } + if let Some(number) = value.as_f64() { + if number.fract() == 0.0 { + return (number as i64).to_string(); + } + return format!("{number}"); + } + if let Some(text) = value.as_str() { + if let Ok(number) = text.trim().parse::() { + if number.fract() == 0.0 { + return (number as i64).to_string(); + } + return format!("{number}"); + } + } + "0".to_string() +} + +/// `configRevision` 作为 **JSON 数字**放进签到体(原样透传数字形态;字符串数字 +/// 也折成数字,非数字给 0) +fn config_revision_json(value: &Value) -> Value { + if value.as_f64().is_some() || value.as_i64().is_some() { + return value.clone(); + } + if let Some(text) = value.as_str() { + if let Ok(number) = text.trim().parse::() { + return Value::from(number); + } + } + Value::from(0) +} + +/// 今天是否已经签过(第一步预检) +fn claimed_today(context_data: &Value) -> bool { + context_data + .get("state") + .and_then(|state| state.get("claimedToday")) + .and_then(Value::as_bool) + .unwrap_or(false) +} + +/// 活动上下文里的 `actions[]` 是否包含 `check_in`(第二步预检) +fn has_check_in_action(context_data: &Value) -> bool { + context_data + .get("actions") + .and_then(Value::as_array) + .map(|actions| { + actions + .iter() + .filter_map(Value::as_str) + .any(|action| action.trim() == CHECK_IN_ACTION) + }) + .unwrap_or(false) +} + +/// 从签到结果的 `data.result` 里取发放额(`creditsGranted` → `rewardCredits` +/// → `credits`,见模块头)。返回 `(amount, found)`。 +fn credit_from_result(result: &Value) -> (f64, bool) { + for key in ["creditsGranted", "rewardCredits", "credits"] { + let Some(value) = result.get(key) else { + continue; + }; + let number = value + .as_f64() + .or_else(|| value.as_str().and_then(|text| text.trim().parse::().ok())); + if let Some(number) = number { + return (number, true); + } + } + (0.0, false) +} + +/// 账号记录 id → 凭证(与别家同口径:点名了不存在的账号就报 401) +fn credentials_for( + store: &AccountStore, + account_id: &str, +) -> Result { + let record = store.lobsterai_account_record(account_id); + if !account_id.is_empty() && record.is_none() { + return Err(GatewayError::with_status( + 401, + format!("LobsterAI 账号 {account_id} 不存在(请重新添加)"), + )); + } + from_record(record.as_ref()) +} + +/// 发一次带鉴权的 GET(活动位 / 活动上下文共用;头照 Go `authHeaders`) +async fn get_json( + url: &str, + credentials: &LobsterCredentials, + what: &str, +) -> Result { + request_json("GET", url, credentials, None, what).await +} + +/// 发一次带鉴权的 POST(签到) +async fn post_json( + url: &str, + credentials: &LobsterCredentials, + body: &Value, + what: &str, +) -> Result { + request_json("POST", url, credentials, Some(body), what).await +} + +/// 带鉴权的 JSON 请求(非 JSON 响应给「凭据已失效」这类可读原因,而不是解析错误) +async fn request_json( + method: &str, + url: &str, + credentials: &LobsterCredentials, + body: Option<&Value>, + what: &str, +) -> Result { + let headers: Vec<(String, String)> = vec![ + ( + "authorization".to_string(), + format!("Bearer {}", credentials.access_token), + ), + ("accept".to_string(), "application/json".to_string()), + ("content-type".to_string(), "application/json".to_string()), + ("user-agent".to_string(), USER_AGENT.to_string()), + ]; + let response = send_raw(method, url, body, &headers, None, Some(REQUEST_TIMEOUT_MS)) + .await + .map_err(|error| { + if error.is_timeout() { + GatewayError::with_status(504, format!("LobsterAI {what}超时")) + } else { + GatewayError::with_status(502, format!("LobsterAI {what}请求失败: {error}")) + } + })?; + if response.status == 401 || response.status == 403 { + return Err(GatewayError::with_status( + 401, + format!( + "LobsterAI 凭据已失效(HTTP {}),请重新登录该账号", + response.status + ), + )); + } + if !response.ok { + return Err(GatewayError::with_status( + response.status as i32, + format!("LobsterAI {what}返回 HTTP {}", response.status), + )); + } + response + .payload + .ok_or_else(|| GatewayError::with_status(502, format!("LobsterAI {what}响应不是 JSON"))) +} + +/// 中性结果:活动不可签(**不带「已签到」字样** —— 那会被当成当日已办) +fn inactive_result(message: &str, activity_code: &str) -> Value { + json!({ + "success": false, + "msg": message, + "activityCode": activity_code, + "needsAction": true, + }) +} + +/// 中性结果:今天已签到(带「已签到」字样 + `alreadyCompleted`:当日完成) +fn already_claimed_result(activity_code: &str) -> Value { + json!({ + "success": false, + "msg": "今天已签到", + "activityCode": activity_code, + "alreadyCompleted": true, + "needsAction": false, + }) +} + +/// 每日签到(三步)。返回 `{success, msg, …}`,交给 `billing::checkin::claim_result`。 +/// +/// ── 三种结局对应用户看到的三种话 ───────────────────────────── +/// · **今天已签**:`success:false` + `alreadyCompleted:true` + 「今天已签到」 +/// (中性、不重签); +/// · **不可签**:`success:false` + 中性文案(活动未下发 / `check_in` 不在 +/// `actions` 里); +/// · **真签上**:`success:true` + 发放积分(字段三段回退)。 +/// 传输 / 信封层面的失败才返回 `Err`(由 `claim_result` 放进结果的 `error`)。 +pub async fn claim_daily_checkin( + store: &AccountStore, + account_id: &str, +) -> Result { + let credentials = credentials_for(store, account_id)?; + // 版本号是活动位 query 的必填参数(解析结果同时写进转发的版本头快照) + let version = chat::prime_client_version().await; + + // ── 第一步:活动位 ───────────────────────────────────────── + let slot_payload = get_json(&activity_slot_url(&version), &credentials, "活动槽位查询").await?; + let slot_data = unwrap_envelope(&slot_payload, "活动槽位查询")?; + let slot = parse_slot(&slot_data); + if slot.slot_state != "available" || slot.activity_code.is_empty() { + let message = format!("今天没有可签到的活动(slotState={})", slot.slot_state); + logging::verbose("[Checkin]", &format!("LobsterAI {account_id}: {message}")); + return Ok(inactive_result(&message, &slot.activity_code)); + } + + // ── 第二步:两道预检(幂等靠客户端,缺一不可,见模块头)───────── + let revision = config_revision_string(&slot.config_revision); + let context_payload = + get_json(&activity_context_url(&slot.activity_code, &revision), &credentials, "活动上下文查询") + .await?; + let context_data = unwrap_envelope(&context_payload, "活动上下文查询")?; + if claimed_today(&context_data) { + logging::verbose( + "[Checkin]", + &format!("LobsterAI {account_id}: 今天已签到(claimedToday)"), + ); + return Ok(already_claimed_result(&slot.activity_code)); + } + if !has_check_in_action(&context_data) { + let message = "当前不可签到(活动未开放 check_in)".to_string(); + logging::verbose("[Checkin]", &format!("LobsterAI {account_id}: {message}")); + return Ok(inactive_result(&message, &slot.activity_code)); + } + + // ── 第三步:真签(幂等键是 uuid4,客户端生成)─────────────────── + let body = json!({ + "configRevision": config_revision_json(&slot.config_revision), + "idempotencyKey": uuid_v4(), + "payload": {}, + }); + let checkin_payload = + post_json(&activity_checkin_url(&slot.activity_code), &credentials, &body, "签到").await?; + let data = unwrap_envelope(&checkin_payload, "签到")?; + let result = data.get("result").cloned().unwrap_or(Value::Null); + let (credit, granted) = credit_from_result(&result); + let delayed = result + .get("message") + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or("") + .to_string(); + let message = if granted { + format!("签到成功,获得 {credit} 积分") + } else { + "签到成功".to_string() + }; + logging::log( + "[Checkin]", + &format!("LobsterAI {account_id}: {message}(活动 {})", slot.activity_code), + ); + let mut outcome = json!({ + "success": true, + "msg": message, + "activityCode": slot.activity_code, + "credit": credit, + "creditGranted": granted, + "needsAction": false, + }); + if !delayed.is_empty() { + if let Some(object) = outcome.as_object_mut() { + object.insert("delayedMessage".to_string(), Value::String(delayed)); + } + } + Ok(outcome) +} + +// 纯逻辑单测:活动位 / 两道预检 / 发放额三段回退 / 幂等键形态 / 中性措辞。 +// (uuid_v4 的实现与它的形态断言在模块根 `mod.rs`,两处共用同一个生成器。) +#[cfg(test)] +mod tests { + use super::*; + + /// 活动位解析:三个字段都要取到(`configRevision` 原样留着)。 + #[test] + fn slot_parsing_reads_state_code_and_revision() { + let data = json!({ + "slotState": "available", + "activity": {"activityCode": "daily-checkin", "configRevision": 12} + }); + let slot = parse_slot(&data); + assert_eq!(slot.slot_state, "available"); + assert_eq!(slot.activity_code, "daily-checkin"); + assert_eq!(config_revision_string(&slot.config_revision), "12"); + assert_eq!(config_revision_json(&slot.config_revision), json!(12)); + // 缺字段不 panic + let empty = parse_slot(&json!({})); + assert!(empty.slot_state.is_empty()); + assert!(empty.activity_code.is_empty()); + assert_eq!(config_revision_string(&empty.config_revision), "0"); + } + + /// `configRevision` 的数值渲染:整数不带尾巴,小数保留(与 Go `formatNumber` 同)。 + #[test] + fn revision_renders_without_a_trailing_zero() { + assert_eq!(config_revision_string(&json!(12)), "12"); + assert_eq!(config_revision_string(&json!(12.0)), "12"); + assert_eq!(config_revision_string(&json!(12.5)), "12.5"); + assert_eq!(config_revision_string(&json!("7")), "7"); + assert_eq!(config_revision_string(&json!("abc")), "0"); + assert_eq!(config_revision_string(&Value::Null), "0"); + } + + /// **两道预检**都要能判:`claimedToday` 与 `actions` 里的 `check_in`。 + #[test] + fn both_prechecks_are_evaluated() { + assert!(claimed_today(&json!({"state": {"claimedToday": true}}))); + assert!(!claimed_today(&json!({"state": {"claimedToday": false}}))); + assert!(!claimed_today(&json!({}))); + assert!(has_check_in_action(&json!({"actions": ["view", "check_in"]}))); + assert!(!has_check_in_action(&json!({"actions": ["view"]}))); + assert!(!has_check_in_action(&json!({}))); + } + + /// 发放额的**三段回退**:`creditsGranted` → `rewardCredits` → `credits`。 + #[test] + fn credit_falls_back_through_three_field_names() { + assert_eq!( + credit_from_result(&json!({"creditsGranted": 10})), + (10.0, true) + ); + assert_eq!( + credit_from_result(&json!({"rewardCredits": 5.5})), + (5.5, true) + ); + assert_eq!(credit_from_result(&json!({"credits": 3})), (3.0, true)); + // 第一个键存在时优先(哪怕 0) + assert_eq!(credit_from_result(&json!({"creditsGranted": 0, "credits": 9})), (0.0, true)); + // 一个都没有 → 未声明(不是「发了 0」) + assert_eq!(credit_from_result(&json!({})), (0.0, false)); + // 数字字符串也认 + assert_eq!(credit_from_result(&json!({"credits": "8"})), (8.0, true)); + } + + /// 中性结果的措辞:不可签那句**不能**带「已签到」(否则会被当成当日已办); + /// 已签那句**必须**带(`checkin_completed_today` 按子串判)。 + #[test] + fn neutral_messages_carry_the_right_wording() { + let inactive = inactive_result("今天没有可签到的活动(slotState=empty)", "c1"); + let msg = inactive.get("msg").and_then(Value::as_str).unwrap_or(""); + assert!(!msg.contains("已签到"), "不可签不该被当成已办:{msg}"); + assert!(!msg.contains("已领取")); + assert_eq!(inactive.get("success"), Some(&Value::Bool(false))); + + let claimed = already_claimed_result("c1"); + let msg = claimed.get("msg").and_then(Value::as_str).unwrap_or(""); + assert!(msg.contains("已签到"), "已签那句必须带「已签到」:{msg}"); + assert_eq!(claimed.get("alreadyCompleted"), Some(&Value::Bool(true))); + assert_eq!(claimed.get("success"), Some(&Value::Bool(false))); + } + + /// 幂等键是 UUID v4 形态(`8-4-4-4-12`,版本位 4、变体位 8/9/a/b)。 + #[test] + fn idempotency_key_is_uuid_v4_shaped() { + let key = uuid_v4(); + let parts: Vec<&str> = key.split('-').collect(); + assert_eq!(parts.len(), 5, "{key}"); + assert_eq!( + parts.iter().map(|part| part.len()).collect::>(), + vec![8, 4, 4, 4, 12], + "{key}" + ); + assert!(parts[2].starts_with('4'), "版本位不是 4:{key}"); + assert!( + matches!(parts[3].chars().next(), Some('8' | '9' | 'a' | 'b')), + "变体位不对:{key}" + ); + assert_ne!(uuid_v4(), uuid_v4()); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/credentials.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/credentials.rs new file mode 100644 index 00000000..12df66d6 --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/credentials.rs @@ -0,0 +1,325 @@ +//! LobsterAI 凭证:**uuid 与两个 keyfrom 是载荷件,丢了账号就永远续不了期**。 +//! +//! ── 为什么这一家的凭证字段比别家多 ────────────────────────────── +//! 上游的续期接口不认「只给 refresh token」:它要的是**一组身份字段**再加 +//! refresh token(参考实现 `auth.go:191-196` 的 `refreshBody`): +//! +//! ```text +//! { firstKeyfrom, latestKeyfrom, version, [uuid], [userId], refreshToken } +//! ``` +//! +//! 而 `uuid` 与那两个 keyfrom 只在**换码那一刻**由上游下发,此后再也拿不到。 +//! 所以落账号时**必须把它们一起存下来** —— 只存 access/refresh 两个 token 的 +//! 账号,看起来能用到 access 过期,之后既续不了期也修不回来(只能重新登录)。 +//! 参考实现把这条写成模块头的第一句警告,这里照抄这个约束。 +//! +//! `latest_keyfrom` 还额外有个「故意不更新」的口径:续期响应里即使带了新的 +//! keyfrom 也不覆盖它,永远保持登录时那一份 —— 这是**唯一经过生产验证**的形态 +//! (Go 参考 `auth.go:49-53` 与 TS 版 `lobsterai.ts:378-410` 都这么做,并在 +//! 注里写明「不是为了省事,是照实测」)。本实现跟随。 +//! +//! ── 过期时刻 ──────────────────────────────────────────────── +//! 上游给 `expiresIn`(秒),落盘时算成 `expiresAt`(毫秒)。access token 是 +//! HS512 的 JWT(30 天),所以**判不出 `expiresAt` 时可以退回读 JWT 的 `exp`**; +//! 两条都没有就返回 0 = 判不出来(不猜,见 `expiring`)。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use base64::Engine; +use base64::engine::general_purpose::URL_SAFE_NO_PAD; +use serde_json::Value; + +use crate::server::errors::GatewayError; +use crate::server::logging; + +/// 一次转发 / 查询用的 LobsterAI 凭证快照 +#[derive(Clone, Debug)] +pub struct LobsterCredentials { + /// 账号记录 id + pub id: String, + /// 访问令牌(HS512 JWT,约 30 天) + pub access_token: String, + /// 续期令牌(可能为空 —— 那种账号只能重新登录) + pub refresh_token: String, + /// 过期时刻(毫秒)。0 = 推不出来 + pub expires_at: i64, + /// 上游用户 id(积分接口与 keyfrom 都用) + pub uid: String, + /// keyfrom 载荷件之二(见模块头) + pub user_id: String, + /// 展示名 + pub nickname: String, + /// **载荷件**:换码时下发,续期必须原样回传 + pub uuid: String, + /// **载荷件**:登录时那一份,续期时原样回传且**永不更新** + pub first_keyfrom: String, + /// **载荷件**:同 [`Self::first_keyfrom`] + pub latest_keyfrom: String, +} + +impl LobsterCredentials { + /// 还能不能续期:有 refresh token **且** 载荷件齐全。 + /// + /// 缺任一载荷件时上游会拒(或更糟:给一个不认识的错误),所以这里先判死 —— + /// 让「这个账号续不了期」在界面上是一句可读的话,而不是一次玄学失败。 + pub fn can_refresh(&self) -> bool { + !self.refresh_token.trim().is_empty() + && !self.first_keyfrom.trim().is_empty() + && !self.latest_keyfrom.trim().is_empty() + } + + /// 是否已过期或进入提前续期窗口(到期前 1 小时)。 + pub fn expiring(&self) -> bool { + const WINDOW_MS: i64 = 3600 * 1000; + self.expires_at > 0 && self.expires_at - WINDOW_MS <= logging::now_ms() + } + + /// keyfrom 载荷(`firstKeyfrom` / `latestKeyfrom` / `version` 恒在, + /// `uuid` 与 `userId` 非空才带)。 + /// + /// 两个消费点共用这一份:目录端点把它拼成 **query**(`models.go:523-551`, + /// 特意不带 refresh token —— query 会进服务端访问日志),续期端点把它当 + /// **body** 再补一个 `refreshToken`。 + pub fn keyfrom_fields(&self, client_version: &str) -> Vec<(&'static str, String)> { + let mut fields: Vec<(&'static str, String)> = vec![ + ("firstKeyfrom", self.first_keyfrom.clone()), + ("latestKeyfrom", self.latest_keyfrom.clone()), + ("version", client_version.to_string()), + ]; + if !self.uuid.trim().is_empty() { + fields.push(("uuid", self.uuid.clone())); + } + if !self.user_id.trim().is_empty() { + fields.push(("userId", self.user_id.clone())); + } + // 与 Go 的 `url.Values.Encode()` 同序(按 key 排序), + // 让同样的账号每次拼出同样的串 —— 便于逐字节对拍与排错 + fields.sort_by(|a, b| a.0.cmp(b.0)); + fields + } +} + +/// 从一条取值链里挑第一个非空字符串(记录可能被手工编辑过,宽容读取) +fn pick<'a>(record: &'a Value, keys: &[&str]) -> String { + for key in keys { + let value = record + .get(*key) + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or(""); + if !value.is_empty() { + return value.to_string(); + } + } + String::new() +} + +/// 读一个毫秒时刻(数字或数字字符串都认) +fn pick_ms(record: &Value, keys: &[&str]) -> i64 { + for key in keys { + if let Some(value) = record.get(*key) { + if let Some(number) = value.as_i64() { + if number > 0 { + return number; + } + } + if let Some(text) = value.as_str() { + if let Ok(number) = text.trim().parse::() { + if number > 0 { + return number; + } + } + } + } + } + 0 +} + +/// 读一个秒数(`expiresIn`) +fn pick_secs(record: &Value, keys: &[&str]) -> i64 { + for key in keys { + if let Some(number) = record.get(*key).and_then(Value::as_i64) { + if number > 0 { + return number; + } + } + } + 0 +} + +/// 从 JWT 里读 `exp`(毫秒)。读不出来返回 None。 +/// +/// access token 是 HS512 的三段点分 JWT,`exp` 在第二段的 claims 里。 +/// **只解码不验签**:这是本地判过期用的,签名由上游校验。 +pub fn jwt_exp_ms(token: &str) -> Option { + let mut parts = token.split('.'); + let _header = parts.next()?; + let payload = parts.next()?; + let raw = URL_SAFE_NO_PAD.decode(payload.trim()).ok()?; + let claims: Value = serde_json::from_slice(&raw).ok()?; + let exp = claims.get("exp").and_then(Value::as_f64)?; + if !exp.is_finite() || exp <= 0.0 { + return None; + } + Some((exp * 1000.0) as i64) +} + +/// 由 `createdAt + expiresIn` 或 JWT `exp` 推出过期时刻(毫秒),都没有则 0。 +pub fn expires_at_from(created_at_ms: i64, expires_in_secs: i64, access_token: &str) -> i64 { + if created_at_ms > 0 && expires_in_secs > 0 { + return created_at_ms + expires_in_secs * 1000; + } + // 退回 JWT 的 exp(HS512,实测 30 天) + jwt_exp_ms(access_token).unwrap_or(0) +} + +/// 从账号记录解析凭证(**唯一入口**;`record` 为 None 时报「没有可用账号」)。 +/// +/// 字段兼容:本项目的驼峰名(`accessToken` / `refreshToken` / `firstKeyfrom`…) +/// 与 Go 参考里的下划线名(`access_token` / `first_keyfrom`…)都认。 +pub fn from_record(record: Option<&Value>) -> Result { + let Some(record) = record else { + return Err(GatewayError::with_status( + 401, + "没有可用的 LobsterAI 账号:请在账号页用浏览器登录添加", + )); + }; + let access_token = pick(record, &["accessToken", "access_token"]); + if access_token.is_empty() { + return Err(GatewayError::with_status( + 401, + "LobsterAI 账号缺少访问令牌,请重新登录", + )); + } + let created_at = pick_ms(record, &["createdAt", "created_at"]); + let expires_in = pick_secs(record, &["expiresIn", "expires_in"]); + let explicit_expiry = pick_ms(record, &["expiresAt", "expires_at"]); + let expires_at = if explicit_expiry > 0 { + explicit_expiry + } else { + expires_at_from(created_at, expires_in, &access_token) + }; + Ok(LobsterCredentials { + id: pick(record, &["id"]), + refresh_token: pick(record, &["refreshToken", "refresh_token"]), + access_token, + expires_at, + uid: pick(record, &["uid", "userId", "user_id"]), + user_id: pick(record, &["userId", "user_id"]), + nickname: pick(record, &["name", "nickname"]), + uuid: pick(record, &["uuid", "UUID"]), + first_keyfrom: pick(record, &["firstKeyfrom", "first_keyfrom"]), + latest_keyfrom: pick(record, &["latestKeyfrom", "latest_keyfrom"]), + }) +} + +/// 账号展示名(昵称优先,其次 uid,最后账号 id) +pub fn display_name(nickname: &str, uid: &str, id: &str) -> String { + for candidate in [nickname, uid, id] { + let trimmed = candidate.trim(); + if !trimmed.is_empty() { + return trimmed.to_string(); + } + } + "LobsterAI 账号".to_string() +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + fn base_record() -> Value { + json!({ + "id": "lobsterai-1", + "accessToken": "at", "refreshToken": "rt", + "uuid": "u-1", "firstKeyfrom": "fk", "latestKeyfrom": "lk", + "userId": "42", "name": "龙虾用户", + "createdAt": 1_700_000_000_000_i64, "expiresIn": 2_592_000 + }) + } + + /// 载荷件三件套都要读出来 —— 缺一个账号就续不了期(模块头的第一约束)。 + #[test] + fn payload_fields_are_read_and_gate_refresh() { + let record = base_record(); + let credentials = from_record(Some(&record)).expect("应可解析"); + assert_eq!(credentials.uuid, "u-1"); + assert_eq!(credentials.first_keyfrom, "fk"); + assert_eq!(credentials.latest_keyfrom, "lk"); + assert!(credentials.can_refresh()); + + // 少一个 keyfrom ⇒ 判「不可续期」,而不是让它去撞一次必败的请求 + let mut broken = base_record(); + if let Some(object) = broken.as_object_mut() { + object.remove("latestKeyfrom"); + } + let credentials = from_record(Some(&broken)).expect("仍应可解析"); + assert!(!credentials.can_refresh()); + } + + /// keyfrom 载荷:三个恒在 + 两个可选;顺序按 key 排序(与 Go 的 Encode 同序)。 + #[test] + fn keyfrom_fields_are_sorted_and_omit_empty_optionals() { + let credentials = from_record(Some(&base_record())).expect("应可解析"); + let fields = credentials.keyfrom_fields("2026.9.4"); + let keys: Vec<&str> = fields.iter().map(|(key, _)| *key).collect(); + assert_eq!(keys, vec!["firstKeyfrom", "latestKeyfrom", "userId", "uuid", "version"]); + assert_eq!(fields[4].1, "2026.9.4"); + + // uuid / userId 为空时不带这两个键 + let mut record = base_record(); + if let Some(object) = record.as_object_mut() { + object.remove("uuid"); + object.remove("userId"); + } + let credentials = from_record(Some(&record)).expect("应可解析"); + let keys: Vec<&str> = credentials + .keyfrom_fields("2026.9.4") + .iter() + .map(|(key, _)| *key) + .collect(); + assert_eq!(keys, vec!["firstKeyfrom", "latestKeyfrom", "version"]); + } + + /// 过期时刻:`createdAt + expiresIn` 优先;都没有时退回 JWT 的 `exp`。 + #[test] + fn expiry_prefers_created_at_then_falls_back_to_jwt() { + assert_eq!( + expires_at_from(1_700_000_000_000, 2_592_000, "irrelevant"), + 1_700_000_000_000 + 2_592_000_000 + ); + // 两个输入都没有 ⇒ 去读 JWT;这里给一个造出来的三段 token + let claims = serde_json::json!({"exp": 1_700_000_000_i64}); + let payload = URL_SAFE_NO_PAD.encode(serde_json::to_vec(&claims).unwrap_or_default()); + let token = format!("h.{payload}.s"); + assert_eq!(expires_at_from(0, 0, &token), 1_700_000_000_000); + // 都不是 JWT ⇒ 判不出来(0),不猜 + assert_eq!(expires_at_from(0, 0, "not-a-jwt"), 0); + } + + /// 缺访问令牌时报「重新登录」,而不是拿空串去发请求。 + #[test] + fn missing_token_is_an_auth_error() { + let error = from_record(Some(&json!({"refreshToken": "rt"}))).expect_err("应报错"); + assert_eq!(error.status_code, 401); + assert!(error.message.contains("重新登录")); + assert_eq!(from_record(None).expect_err("无账号应报错").status_code, 401); + } + + /// 展示名优先级:昵称 → uid → id。 + #[test] + fn display_name_falls_back_in_order() { + assert_eq!(display_name("虾", "42", "id"), "虾"); + assert_eq!(display_name("", "42", "id"), "42"); + assert_eq!(display_name("", "", "id"), "id"); + assert_eq!(display_name("", "", ""), "LobsterAI 账号"); + } + + /// 常量哨兵存在(防止有人把兜底版本号删掉) + #[test] + fn fallback_version_sentinel_is_present() { + assert!(super::super::endpoints::FALLBACK_CLIENT_VERSION.starts_with("2026.")); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/endpoints.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/endpoints.rs new file mode 100644 index 00000000..eaf7b3b0 --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/endpoints.rs @@ -0,0 +1,256 @@ +//! LobsterAI(有道龙虾)上游地址与身份常量。 +//! +//! ── 上游长什么样 ──────────────────────────────────────────── +//! 网易有道系的桌面 Agent,两个域都在有道名下(CNAME 都指向网易广州 ALB): +//! +//! ```text +//! 登录门户 https://lobsterai.youdao.com 浏览器授权页(手机号 / 微信) +//! API https://lobsterai-server.youdao.com 换码 / 续期 / 目录 / 对话 / 积分 +//! ``` +//! +//! 对话端点是 **OpenAI 兼容的原生协议**(`POST /api/proxy/v1/chat/completions`), +//! 但**只收 SSE**:`stream:false` 上游直接回 HTTP 500(Go 参考 `executor.go:18-21` +//! 实测)。本网关的转发入口「无论客户端要不要流式,上游恒以 `stream:true` 请求」 +//! (`core/upstream/mod.rs` 的注释原文),所以这条要求天然满足,适配器**不要**去动 +//! `stream` 字段。 +//! +//! ── 两个 `X-LobsterAI-Client-*` 头是功能性的,不是装饰 ────────── +//! `ClientCapabilities` 声明客户端能力,服务端**按它过滤**: +//! +//! ```text +//! 不带该头 -> 目录里 25 个模型(没有 kimi-k3) +//! 带 kimi-k3-agentic-v1 -> 26 个(kimi-k3 才出现) +//! ``` +//! +//! 另一个能力位 `thinking-level-control-v1` 是「把思考关掉」这条参数的前提 —— +//! 缺它时发 `reasoning_effort: "off"` 会得到 HTTP 500。两个都实测过 +//! (见参考实现的 `lobsterai-product.ts` 与 `headers.go` 的注释)。 +//! +//! 版本头走的是**日期式版本**(形如 `2026.9.4`),由上游的更新接口给出; +//! 而 `User-Agent` 照抄参考实现里那个固定的 `LobsterAI/0.1.0` —— 它**故意不跟** +//! 真实版本,动态的那份只由 `X-LobsterAI-Client-Version` 承载。 +//! +//! ── 没有任何签名 ──────────────────────────────────────────── +//! 纯 `Bearer`。没有 HMAC / DPoP / PKCE,也没有腾讯系的 `X-Domain` / +//! `X-Product` / `X-IDE-*` 那批归属头 —— 那些发过去轻则被忽略、重则把请求归到 +//! 错误的客户端形态上(参考实现的模块头原话)。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +/// 上游 API 主机(换码 / 续期 / 目录 / 对话 / 积分都在它下面) +pub const API_BASE: &str = "https://lobsterai-server.youdao.com"; + +/// 浏览器登录门户主机 +pub const PORTAL_BASE: &str = "https://lobsterai.youdao.com"; + +/// 客户端版本查询接口。**这是第三方主机,响应不是业务信封**:版本号在 +/// `data.value`(参考实现 `lobsterai-product.ts:443` 的说明)。 +pub const CLIENT_VERSION_API: &str = + "https://api-overmind.youdao.com/openapi/get/luna/hardware/lobsterai/prod/update"; + +/// 查不到版本时用的兜底值 +pub const FALLBACK_CLIENT_VERSION: &str = "2026.9.4"; + +/// `User-Agent`:照抄参考实现,**故意固定**不跟随真实版本 +pub const USER_AGENT: &str = "LobsterAI/0.1.0"; + +/// 声明的客户端能力。两个能力位都是功能性的: +/// `kimi-k3-agentic-v1` 是模型列表的准入门(缺它少一个模型)、 +/// `thinking-level-control-v1` 是 `reasoning_effort: "off"` 的前提(缺它 HTTP 500)。 +pub const CLIENT_CAPABILITIES: &str = "kimi-k3-agentic-v1,thinking-level-control-v1"; + +/// 用授权码换令牌(**不带 Authorization**) +pub const EXCHANGE_PATH: &str = "/api/auth/exchange"; +/// 续期(**不带 Authorization**) +pub const REFRESH_PATH: &str = "/api/auth/refresh"; +/// 模型目录 +pub const MODELS_PATH: &str = "/api/models/available"; +/// 对话(OpenAI 兼容,SSE only) +pub const CHAT_PATH: &str = "/api/proxy/v1/chat/completions"; +/// loopback 回调路径 +pub const CALLBACK_PATH: &str = "/auth/callback"; +/// 积分余额 +pub const PROFILE_SUMMARY_PATH: &str = "/api/user/profile-summary"; + +/// 每日签到三步:查活动位 → 取该活动上下文 → 执行 check_in +pub const ACTIVITY_SLOT_PATH: &str = "/api/client-activities/slot"; +/// 见 [`ACTIVITY_SLOT_PATH`](活动码拼在路径后面) +pub const ACTIVITY_CONTEXT_PATH: &str = "/api/client-activities"; + +/// 活动位查询的三个固定参数(照抄真实客户端的形态)。 +/// +/// `platform=win32` 是**冒充桌面客户端形态**,与运行本网关的宿主系统无关 —— +/// 参考实现特意注明这一点。 +pub const SLOT_PLACEMENT: &str = "desktop_sidebar"; +/// 见 [`SLOT_PLACEMENT`] +pub const SLOT_CONTAINER_API_VERSION: &str = "2"; +/// 见 [`SLOT_PLACEMENT`] +pub const SLOT_PLATFORM: &str = "win32"; + +/// 控制面请求超时(毫秒) +pub const REQUEST_TIMEOUT_MS: u64 = 30_000; +/// 目录请求超时(毫秒) +pub const CATALOG_TIMEOUT_MS: u64 = 20_000; +/// 目录缓存时长(毫秒) +pub const CATALOG_TTL_MS: i64 = 2 * 60 * 60 * 1000; +/// 已解析的客户端版本缓存时长(毫秒)——12 小时,版本不常变 +pub const VERSION_CACHE_TTL_MS: i64 = 12 * 60 * 60 * 1000; + +/// 读环境变量覆盖(空值视为未设置),去掉尾部斜杠 +fn env_override(name: &str) -> Option { + std::env::var(name) + .ok() + .map(|value| value.trim().trim_end_matches('/').to_string()) + .filter(|value| !value.is_empty()) +} + +/// API 基址:`LOBSTERAI_API_BASE` 可覆盖 +pub fn api_base() -> String { + env_override("LOBSTERAI_API_BASE").unwrap_or_else(|| API_BASE.to_string()) +} + +/// 门户基址:`LOBSTERAI_PORTAL_BASE` 可覆盖 +pub fn portal_base() -> String { + env_override("LOBSTERAI_PORTAL_BASE").unwrap_or_else(|| PORTAL_BASE.to_string()) +} + +/// 对话端点完整 URL +pub fn chat_url() -> String { + format!("{}{}", api_base(), CHAT_PATH) +} + +/// 目录端点 URL(`keyfrom` 作为查询串,与参考实现一致) +pub fn models_url(keyfrom: &str) -> String { + format!("{}{}?{}", api_base(), MODELS_PATH, keyfrom.trim()) +} + +/// 余额端点 URL +pub fn profile_summary_url() -> String { + format!("{}{}", api_base(), PROFILE_SUMMARY_PATH) +} + +/// 活动位 URL(三个固定参数) +pub fn activity_slot_url(client_version: &str) -> String { + format!( + "{}{}?placement={}&clientVersion={}&containerApiVersion={}&platform={}", + api_base(), + ACTIVITY_SLOT_PATH, + SLOT_PLACEMENT, + urlencode(client_version), + SLOT_CONTAINER_API_VERSION, + SLOT_PLATFORM + ) +} + +/// 某个活动的上下文 URL +/// +/// ⚠️ 末尾那个 `/context` 段不能省:上游的路径是三段式 +/// `{code}/context?configRevision=…`,少了它整条请求落到 `/api/client-activities/{code}` +/// 上、直接被回 **HTTP 404**(签到因此稳定失败,报「活动上下文查询返回 HTTP 404」)。 +/// 参考实现(Go `fetchActivityContext`、TS `lobsterai-credits.ts`)都是三段式。 +pub fn activity_context_url(code: &str, config_revision: &str) -> String { + format!( + "{}{}/{}/context?configRevision={}", + api_base(), + ACTIVITY_CONTEXT_PATH, + urlencode(code), + urlencode(config_revision) + ) +} + +/// 某个活动的 check_in URL +pub fn activity_checkin_url(code: &str) -> String { + format!( + "{}{}/{}/actions/check_in", + api_base(), + ACTIVITY_CONTEXT_PATH, + urlencode(code) + ) +} + +/// 极小的 query 转义(本家只有版本号 / 活动码 / 修订版三个值要拼进 URL)。 +/// +/// 不引依赖:这些值都可能来自上游响应(活动码、configRevision),一旦含 +/// `&` / `=` / `?` 就会截断 query,所以必须转义而不是直接拼。 +pub fn urlencode(value: &str) -> String { + let mut out = String::with_capacity(value.len()); + for byte in value.bytes() { + match byte { + b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => { + out.push(byte as char) + } + _ => out.push_str(&format!("%{byte:02X}")), + } + } + out +} + +#[cfg(test)] +mod tests { + use super::*; + + /// 两个能力位都必须带;少一个就少了模型或直接 500(见模块头)。 + #[test] + fn capabilities_cover_both_load_bearing_keys() { + assert!(CLIENT_CAPABILITIES.contains("kimi-k3-agentic-v1")); + assert!(CLIENT_CAPABILITIES.contains("thinking-level-control-v1")); + } + + /// UA 固定不跟随版本;动态版本只走 `X-LobsterAI-Client-Version`。 + #[test] + fn user_agent_is_the_fixed_reference_string() { + assert_eq!(USER_AGENT, "LobsterAI/0.1.0"); + assert!(FALLBACK_CLIENT_VERSION.starts_with("2026.")); + } + + /// URL 拼接:三条平面各自独立,别把门户/API 混用。 + #[test] + fn urls_are_composed_from_the_api_plane() { + assert!(chat_url().starts_with(API_BASE)); + assert!(chat_url().ends_with(CHAT_PATH)); + assert!(profile_summary_url().ends_with(PROFILE_SUMMARY_PATH)); + assert!(!chat_url().contains("lobsterai.youdao.com/api") || chat_url().contains("-server.")); + // 门户主机是**另一个**域,不该出现在任何 API URL 里 + assert!(!models_url("k=v").contains("//lobsterai.youdao.com")); + } + + /// 活动位 URL 必须带上那三个固定参数(照抄客户端形态)。 + #[test] + fn slot_url_carries_the_three_fixed_params() { + let url = activity_slot_url("2026.9.4"); + assert!(url.contains("placement=desktop_sidebar"), "{url}"); + assert!(url.contains("clientVersion=2026.9.4"), "{url}"); + assert!(url.contains("containerApiVersion=2"), "{url}"); + assert!(url.contains("platform=win32"), "{url}"); + } + + /// 活动码与修订版要转义(它们来自上游响应,可能含 query 元字符)。 + #[test] + fn activity_urls_escape_upstream_values() { + assert!(activity_context_url("a&b", "1=2").contains("a%26b")); + assert!(activity_context_url("a&b", "1=2").contains("configRevision=1%3D2")); + assert!(activity_checkin_url("c d").contains("c%20d")); + assert_eq!(urlencode("abc-1.2_3~"), "abc-1.2_3~"); + } + + /// **路径形状**要逐段钉住,不能只测转义。 + /// + /// 这一条是补的:先前只有上面那条「转义」用例,于是 `activity_context_url` + /// 少写了 `/context` 一段都没被发现 —— 断言全绿,真机上签到却稳定 404 + /// (上游的路径是三段式,见该函数的说明)。凡是被上游用路径而不是参数 + /// 区分的东西,断言就得写到路径本身。 + #[test] + fn activity_paths_have_the_exact_three_segment_shape() { + let context = activity_context_url("daily_sign_in", "7"); + assert!( + context.ends_with("/api/client-activities/daily_sign_in/context?configRevision=7"), + "{context}" + ); + let checkin = activity_checkin_url("daily_sign_in"); + assert!( + checkin.ends_with("/api/client-activities/daily_sign_in/actions/check_in"), + "{checkin}" + ); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/errors.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/errors.rs new file mode 100644 index 00000000..7ac1a27d --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/errors.rs @@ -0,0 +1,443 @@ +//! LobsterAI 的错误分类,以及**两种「成功状态下的失败」**的识别。 +//! +//! ── 判定顺序即优先级(别重排,`errors.go:55-94` 的原话)────────── +//! ```text +//! 1. HTTP 402 -> 余额耗尽(状态码最权威) +//! 2. 正文含余额耗尽关键词 -> 余额耗尽 +//! 3. 正文含 40100 / 40101 -> 会话已死(只能重新登录) +//! 4. HTTP 429 -> 软限流 +//! 5. HTTP 404 -> 上游偶发 +//! 6. HTTP >= 500 -> 上游故障 +//! 7. HTTP >= 400 -> 交共用内容拦截分类 +//! ``` +//! **为什么正文关键词要排在状态码之前(402 除外)**:实测上游用 **HTTP 400 + +//! 中文「积分不足」**表达余额耗尽(`errors.go:64-66`),只按状态码会把这类错误 +//! 判成「可重试的客户端错误」,于是反复重试一个**永远不会成功**的账号。 +//! +//! ── 映射到本仓的四档(这是本文件与 Go 唯一需要「翻译」的地方)────── +//! Go / TS 各有自己的类别名(hard-credit / soft-rate / session-dead / …), +//! 本仓的编排层只认四档(见 `adapter.rs` 模块头),所以: +//! · **余额耗尽 → `QuotaLimited`**:它要做的事与 Go 的 `CoolHard` 逐字相同 +//! ——「标记冷却 + 换下一个账号」(另一个账号可能有额度)。归成 `Fatal` +//! 会把一个「该换号」的错误当成终态直接抛给用户。 +//! · **会话已死 → `TokenExpired`**:刷新后用同一账号重试一次;本家刷新被拒 +//! (40100 / 40101)时刷新本身会失败,编排层随即把它降级成可读错误。 +//! · **软限流 → `QuotaLimited`**(Go 的 `CoolSoft`,同款动作)。 +//! · **404 / 5xx → `Fatal`**(本仓没有「偶发 404 短冷却」这一档,如实透传)。 +//! +//! ── 两种「HTTP 200 的假成功」──────────────────────────────── +//! 1. **流内错误帧**:`HTTP 200` + SSE 正文是 `{"error":{"message":"…"}}` +//! —— 额度耗尽正是以这个形态下发的(未实测,见下),换号循环若不认它, +//! 用户就会看到「一个账号用完出错、但池里还有账号也不切换」; +//! 2. **信封不变量**:`code:0` 配 `data:null` = 令牌已失效 —— 这一条**已经 +//! 接上**(`oauth::unwrap_envelope` 把 `data:null` 判成 401),这里只把 +//! 判据留一份可单测的纯函数。 +//! +//! ── ⚠️ 第 1 条目前**没有调用点**(与 `atomcode/errors.rs` 同一处公开的缺口) +//! 「流内错误帧」要在 SSE 逐帧出口判,而本仓的逐帧出口 +//! (`upstream::ForwardStream`)现在**只按适配器声明的 `finish_reason`** 识别 +//! 拒答(`ProviderAdapter::stream_refusal_finishes`),没有「按帧内容嗅探」的 +//! 挂点(理由见 `atomcode/errors.rs` 的模块头:那需要在通用层加一个每帧回调, +//! 而这一家能否用 `finish_reason` 表达额度耗尽**尚未实测**)。因此本函数是 +//! **纯函数 + 测试**:判据先立住,等通用层给出挂点再接。它现在**不被任何生产 +//! 代码调用**,这是已知缺口,不是遗漏。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use serde_json::Value; + +use crate::server::core::providers::adapter::UpstreamErrorClass; +use crate::server::core::providers::content_block; + +/// 余额耗尽关键词(中英双通道**必需**)。 +/// +/// 中英都要,是因为同一后端在不同场景下会返回中文或英文(实测两种都出现过, +/// `errors.go:40-42`)。中英比较策略:整串先转小写再比,中文小写后与自己相等 +/// (冗余但无害,与 TS `lobsterai-errors.ts:111-113` 一致)。 +/// +/// ⚠️ **与 Go 的一处差异(如实记下)**:Go 的 `hardCreditMarkers` +/// (`errors.go:43-48`)**少了**「额度已用完」「升级套餐」两条,而 TS 原版 +/// (`lobsterai-errors.ts:54-66`)有,并在注里说明它们是根据**真实报障**补上的 +/// (额度耗尽的**实际文案**就是「免费额度已用完,请升级套餐」,早期表里只有 +/// 「额度用尽」——「已用完」与「用尽」字面不同,于是这个最主要的失败模式被判成 +/// 不换号)。本实现取**并集**:漏判这一个模式的代价(账号用完不切换)比多两条 +/// 关键词的代价大得多。英文同类文案(`free quota` 等)一并保留。 +pub const HARD_CREDIT_MARKERS: &[&str] = &[ + "insufficient credit", + "no credit", + "credit exhausted", + "out of credit", + "quota exceeded", + "quota exhaust", + "payment required", + "credit not enough", + "not enough credit", + "freecreditsused", + "free credits used", + "free quota", + "quota used up", + "upgrade your plan", + "upgrade to continue", + "积分不足", + "额度不足", + "余额不足", + "积分用完", + "额度用尽", + "没有积分", + "积分耗尽", + "额度已用完", + "升级套餐", +]; + +/// 会话已死标记(`errors.go:51-53`):刷新被拒的业务码,终态、重试无意义。 +pub const SESSION_DEAD_MARKERS: &[&str] = &[ + "40100", + "40101", + "token rejected", + "refresh token was rejected", +]; + +/// 会话已死的两个业务码(数值形态,归一化层会把它们填进 `code`) +const SESSION_DEAD_CODES: [i64; 2] = [40100, 40101]; + +/// 正文(大小写不敏感)或原串里是否含余额耗尽关键词 +pub fn mentions_hard_credit(text: &str) -> bool { + markers_hit(text, HARD_CREDIT_MARKERS) +} + +/// 正文里是否含会话已死标记(`40100` / `40101` 也是文本,所以按文本判) +pub fn mentions_session_dead(text: &str) -> bool { + markers_hit(text, SESSION_DEAD_MARKERS) +} + +/// 关键词命中(英文走小写比对、中文走原文比对,见 [`HARD_CREDIT_MARKERS`]) +fn markers_hit(text: &str, markers: &[&str]) -> bool { + if text.is_empty() { + return false; + } + let lower = text.to_lowercase(); + markers.iter().any(|marker| { + lower.contains(&marker.to_lowercase()) || text.contains(marker) + }) +} + +/// 从归一化后的错误体里取一行可读文案。 +/// +/// ⚠️ 传到本模块的 `error_body` 是**传输层归一化之后**的形态 +/// (`upstream::request::read_upstream_error`):`code` 只在顶层且是数字时才有, +/// `message` 是 `message` / `msg` / `error.message` 里第一个非空的(原文最多 +/// 截断到 500 字符)。所以这里读字符串是对的,别改成只读结构字段。 +fn message_of(error_body: &Value) -> String { + error_body + .get("message") + .and_then(Value::as_str) + .map(str::trim) + .filter(|text| !text.is_empty()) + .unwrap_or("上游错误") + .to_string() +} + +/// 关键词匹配的**全部原料**:归一化文案 + 整个体的原文序列化。 +/// +/// 两处都要:`message` 是归一化后的可读串(英文关键词多半在这里),而有些形态 +/// (如 `code:40100` 与中文文案同时出现)只在原文里看得全。体最大 500 字符量级, +/// 拼接成本可忽略。 +fn haystack(error_body: &Value) -> String { + format!("{}\n{}", message_of(error_body), error_body) +} + +/// 从归一化体里取业务码(数字与数字字符串都认)。 +/// +/// 归一化层只填**数字**码;字符串码那类信封已经被折进 `message` 原文里,所以 +/// 两种来源都覆盖(与 `atomcode/errors.rs::code_of` 同一手法)。 +fn code_of(error_body: &Value) -> Option { + if let Some(number) = error_body.get("code").and_then(Value::as_i64) { + return Some(number); + } + error_body + .get("code") + .and_then(Value::as_str) + .and_then(|text| text.trim().parse::().ok()) +} + +/// LobsterAI 上游错误的分类。 +/// +/// 判定顺序是契约(见模块头),别重排。 +pub fn classify_gateway_error(status: u16, error_body: &Value) -> UpstreamErrorClass { + let raw = message_of(error_body); + let message = format!("上游返回 {status}: {raw}"); + let upstream_code = code_of(error_body); + let text = haystack(error_body); + + // 1) 402:余额耗尽最直接的信号 + if status == 402 { + return UpstreamErrorClass::QuotaLimited { + reset_at: None, + message: format!("LobsterAI 积分不足(402):{raw}"), + upstream_code, + status, + }; + } + + // 2) 正文关键词命中余额耗尽(**排在状态码之前**,见模块头) + if mentions_hard_credit(&text) { + return UpstreamErrorClass::QuotaLimited { + reset_at: None, + message: format!("LobsterAI 积分不足:{raw}"), + upstream_code, + status: 402, + }; + } + + // 3) 会话已死:40100 / 40101(数值码或文案),只能重新登录 + if upstream_code.is_some_and(|code| SESSION_DEAD_CODES.contains(&code)) + || mentions_session_dead(&text) + { + return UpstreamErrorClass::TokenExpired { + message: format!("LobsterAI 登录态已失效({raw}),请重新登录"), + }; + } + + // 4) 429 软限流 + if status == 429 { + return UpstreamErrorClass::QuotaLimited { + reset_at: None, + message, + upstream_code, + status, + }; + } + + // 5) 404 上游偶发(本仓没有「偶发短冷却」这一档,如实透传) + if status == 404 { + return UpstreamErrorClass::Fatal { + status, + message: format!("LobsterAI 上游返回 404:{raw}"), + upstream_code, + }; + } + + // 6) 5xx 上游故障 + if status >= 500 { + return UpstreamErrorClass::Fatal { + status, + message, + upstream_code, + }; + } + + // 7) 其余 4xx / 业务错误:交共用内容拦截分类(审核文案 → ContentBlocked) + content_block::classify_or_fatal(status, error_body, message, upstream_code) +} + +/// 一次「流内错误帧」的识别结果(HTTP 200 下发的业务失败)。 +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum StreamSilentFailure { + /// 帧里是余额耗尽 + HardCredit { + /// 上游给的原文 message + message: String, + }, + /// 帧里是会话已死 + SessionDead { + /// 上游给的原文 message + message: String, + }, + /// 帧里是别的业务错误(Go 的 default 分支:**也换号**) + Other { + /// 上游给的原文 message + message: String, + }, +} + +/// 识别**流内错误帧**:`HTTP 200` + SSE 正文是 `{"error":{"message":"…"}}`。 +/// +/// ── 为什么不能复用 [`classify_gateway_error`] ──────────────────── +/// 那个函数的优先级里**状态码排在最前**,而流内错误的 HTTP 状态是 **200** —— +/// 那些分支全失效,只剩关键词表可用;传 200 进去未命中关键词时只能得到「成功」, +/// 而「成功」意味着**不换号**(TS `lobsterai-errors.ts:126-161` 专门说明了这点)。 +/// 所以本函数只回答「帧里是不是一类业务失败」,默认值给 +/// [`StreamSilentFailure::Other`](Go 的 handler 里 default 分支同样 `continue` +/// 换号,防雪崩)。 +/// +/// ⚠️ **纯函数,当前没有调用点**(原因见模块头;`finish_reason` 表达额度耗尽 +/// 尚未实测,本仓的逐帧出口也还没有「按内容嗅探」的挂点)。 +pub fn silent_failure_in_frame(payload: &str) -> Option { + let trimmed = payload.trim(); + if trimmed.is_empty() { + return None; + } + let parsed: Value = serde_json::from_str(trimmed).ok()?; + // `{"error":{"message":…}}` 才是这个形态;`{"error":"…"}` 也认(宽容读取) + let message = match parsed.get("error") { + Some(Value::Object(error)) => error + .get("message") + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or("") + .to_string(), + Some(Value::String(text)) => text.trim().to_string(), + _ => return None, + }; + if mentions_hard_credit(&message) || mentions_hard_credit(trimmed) { + return Some(StreamSilentFailure::HardCredit { message }); + } + if mentions_session_dead(&message) || mentions_session_dead(trimmed) { + return Some(StreamSilentFailure::SessionDead { message }); + } + Some(StreamSilentFailure::Other { message }) +} + +/// 业务信封是否是「`code:0` + `data:null`」= **令牌已失效**的形态。 +/// +/// ⚠️ 这一条**已经接上**:`oauth::unwrap_envelope` 就把 `data` 为 null / 缺失 +/// 判成 401「登录态可能已失效,请重新登录」,本函数只是把同一条判据做成可单测 +/// 的纯函数(Go `envelope.go:26-76` 的第三个检查,注释称它是「载荷件那一条」)。 +pub fn envelope_reports_dead_session(payload: &Value) -> bool { + let code = payload.get("code").and_then(Value::as_i64).unwrap_or(-1); + if code != 0 { + return false; + } + matches!(payload.get("data"), None | Some(Value::Null)) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + fn classify(status: u16, body: Value) -> UpstreamErrorClass { + classify_gateway_error(status, &body) + } + + /// **第一条优先级**:402 最权威,哪怕正文什么都没说。 + #[test] + fn payment_required_wins_over_everything() { + match classify(402, json!({"message": "whatever"})) { + UpstreamErrorClass::QuotaLimited { status, message, .. } => { + assert_eq!(status, 402); + assert!(message.contains("积分不足")); + } + other => panic!("402 应为 QuotaLimited,实际 {other:?}"), + } + } + + /// **关键词先于状态码**:400 + 「积分不足」必须判成额度耗尽,而不是可重试的 + /// 客户端错误 —— 否则会反复重试一个永远不会成功的账号(模块头)。 + #[test] + fn hard_credit_marker_beats_the_status_code() { + match classify(400, json!({"code": 1, "msg": "积分不足"})) { + UpstreamErrorClass::QuotaLimited { status, .. } => assert_eq!(status, 402), + other => panic!("应判额度耗尽,实际 {other:?}"), + } + // 中文实际文案(Go 的表少这两条,本实现取并集,见 HARD_CREDIT_MARKERS) + match classify(400, json!({"message": "免费额度已用完,请升级套餐"})) { + UpstreamErrorClass::QuotaLimited { .. } => {} + other => panic!("「额度已用完」应判额度耗尽,实际 {other:?}"), + } + // 英文通道 + assert!(mentions_hard_credit("Insufficient Credit")); + assert!(mentions_hard_credit("QUOTA EXCEEDED")); + // 正常回答里提到这些词不该命中(这里判的是错误体,不是模型正文) + assert!(!mentions_hard_credit("参数错误")); + assert!(!mentions_hard_credit("hello world")); + } + + /// 会话已死:数值码 `40100` 与文本标记两条路都要中,且排在 429 之前。 + #[test] + fn session_dead_is_detected_and_outranks_the_rate_limit_branch() { + // 数值码形态(归一化层填的是数字 code) + match classify(401, json!({"code": 40100, "message": "refresh rejected"})) { + UpstreamErrorClass::TokenExpired { message } => { + assert!(message.contains("重新登录")) + } + other => panic!("40100 应为 TokenExpired,实际 {other:?}"), + } + // 文本形态 + 与 429 同时出现时仍判会话已死(重试无意义) + match classify( + 429, + json!({"message": "40101 refresh token was rejected"}), + ) { + UpstreamErrorClass::TokenExpired { .. } => {} + other => panic!("40101 应优先于 429,实际 {other:?}"), + } + assert!(!mentions_session_dead("普通错误")); + } + + /// 429 → 软限流(带账号冷却语义,无结构化恢复时间)。 + #[test] + fn rate_limit_maps_to_quota_limited() { + match classify(429, json!({"message": "too many requests"})) { + UpstreamErrorClass::QuotaLimited { + reset_at, status, .. + } => { + assert_eq!(status, 429); + assert!(reset_at.is_none()); + } + other => panic!("429 应为 QuotaLimited,实际 {other:?}"), + } + } + + /// 404 / 5xx → Fatal(原样透传,无换号动作)。 + #[test] + fn not_found_and_server_errors_are_fatal() { + match classify(404, json!({"message": "not found"})) { + UpstreamErrorClass::Fatal { status, .. } => assert_eq!(status, 404), + other => panic!("404 应为 Fatal,实际 {other:?}"), + } + match classify(503, json!({"message": "bad gateway"})) { + UpstreamErrorClass::Fatal { status, .. } => assert_eq!(status, 503), + other => panic!("5xx 应为 Fatal,实际 {other:?}"), + } + } + + /// 其余 4xx 交共用内容拦截分类(这里只验证「不是我们擅自归的档」)。 + #[test] + fn other_client_errors_go_to_the_shared_classifier() { + let class = classify(400, json!({"code": 1001, "message": "bad request"})); + match class { + UpstreamErrorClass::Fatal { .. } | UpstreamErrorClass::ContentBlocked { .. } => {} + UpstreamErrorClass::QuotaLimited { .. } => panic!("400 不该判限额"), + other => panic!("意外分类 {other:?}"), + } + } + + /// **流内错误帧**:`{"error":{"message":…}}` 要能被认出来并分类。 + #[test] + fn silent_stream_error_frames_are_classified() { + assert_eq!( + silent_failure_in_frame(r#"{"error":{"message":"免费额度已用完,请升级套餐"}}"#), + Some(StreamSilentFailure::HardCredit { + message: "免费额度已用完,请升级套餐".to_string() + }) + ); + assert_eq!( + silent_failure_in_frame(r#"{"error":{"message":"40100 token rejected"}}"#), + Some(StreamSilentFailure::SessionDead { + message: "40100 token rejected".to_string() + }) + ); + // 别的业务错误:默认给 Other(Go 的 default 分支同样换号) + assert_eq!( + silent_failure_in_frame(r#"{"error":{"message":"some business failure"}}"#), + Some(StreamSilentFailure::Other { + message: "some business failure".to_string() + }) + ); + // 正常帧 / 非 JSON / 空 → None(不误判) + assert_eq!(silent_failure_in_frame(r#"{"choices":[{"delta":{"content":"hi"}}]}"#), None); + assert_eq!(silent_failure_in_frame("data: [DONE]"), None); + assert_eq!(silent_failure_in_frame(" "), None); + } + + /// 信封不变量:`code:0` + `data:null` = 令牌失效(已被 `oauth::unwrap_envelope` + /// 接上,这里只验证判据本身)。 + #[test] + fn envelope_with_null_data_reports_a_dead_session() { + assert!(envelope_reports_dead_session(&json!({"code": 0, "data": null}))); + assert!(envelope_reports_dead_session(&json!({"code": 0}))); + assert!(!envelope_reports_dead_session(&json!({"code": 0, "data": {}}))); + assert!(!envelope_reports_dead_session(&json!({"code": 7, "data": null}))); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/mod.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/mod.rs new file mode 100644 index 00000000..f0ca3170 --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/mod.rs @@ -0,0 +1,473 @@ +//! LobsterAI(网易有道「有道龙虾」)适配器:账号管理(浏览器登录)+ **无状态** +//! 推理转发 + 目录 / 余额 / 每日签到。 +//! +//! ── 上游长什么样(细节见各子模块的模块头)────────────────────── +//! ```text +//! 登录门户 https://lobsterai.youdao.com 浏览器授权页(手机号 / 微信) +//! API https://lobsterai-server.youdao.com 换码 / 续期 / 目录 / 对话 / 积分 / 活动 +//! ``` +//! 对话端点是 **OpenAI 兼容的原生协议**且**只收 SSE**(`stream:false` 回 +//! HTTP 500,`endpoints.rs` 的模块头有实测):本网关的转发入口恒以 `stream:true` +//! 请求上游,所以这条要求天然满足。本家**无状态**(一次 HTTP 请求 = 一次回答), +//! `is_stateful` / `supports_chat` 走默认值,转发链只需 `build_chat_request`。 +//! +//! ── 与别家不同的五处取舍(都写在各自模块头,这里给索引)───────── +//! 1. **登录是「回调带回一次性 code」那一套**(`supports_web_login = true`): +//! 授权在门户完成、302 回本机 loopback,网关用 code 换令牌(`oauth.rs`)—— +//! 不像 AtomCode 那样轮询判完成。所以 `build_login_url` / +//! `exchange_login_code` 三个钩子是**真的要实现**的。 +//! 2. **续期要一整组身份载荷件**(`uuid` + 两个 keyfrom),且 +//! `latestKeyfrom` **永不更新** —— 只存两个 token 的账号续不了期 +//! (`credentials.rs` 的模块头有这条约束的一手来源)。 +//! 3. **额度是积分**,读数在 `/api/user/profile-summary`(**不是** +//! `/api/user/quota`,那个只报 300 免费额度)—— 见 `usage.rs`。 +//! 4. **签到是三步协议且幂等靠客户端**(活动位 → 上下文两道预检 → +//! check_in + uuid4 幂等键)—— 见 `checkin.rs`。 +//! 5. **错误分类里「余额耗尽」是最主要的失败模式**,而且它可能以 +//! `HTTP 200` + 流内错误帧下发 —— 见 `errors.rs`(含一处**已知缺口**)。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +pub mod chat; +pub mod checkin; +pub mod credentials; +pub mod endpoints; +pub mod errors; +pub mod models; +pub mod oauth; +pub mod usage; +pub mod version; + +use std::sync::OnceLock; + +use axum::http::HeaderMap; +use serde_json::{Map, Value}; + +use crate::server::core::account_store::AccountStore; +use crate::server::core::providers::adapter::{ + ChatRequestPlan, ModelRefreshOutcome, ProviderAdapter, UpstreamErrorClass, +}; +use crate::server::core::providers::{ProviderKind, refresh_flight}; +use crate::server::errors::GatewayError; +use crate::server::logging; + +/// 适配器实例(`adapter_for(ProviderKind::LobsterAi)` 返回这一个)。 +/// +/// **无状态**:它不持有任何数据 —— 凭证在账号存储、目录在 `models.rs` 的进程级 +/// 句柄、登录轮次在 `oauth.rs` 的 pending 表(都可重建)。 +pub struct LobsterAiAdapter; + +/// 静态单例 +pub static LOBSTERAI_ADAPTER: LobsterAiAdapter = LobsterAiAdapter; + +impl ProviderAdapter for LobsterAiAdapter { + fn kind(&self) -> ProviderKind { + ProviderKind::LobsterAi + } + + /// 模型清单来自远程目录(`models.rs` 的进程缓存 + 落盘缓存 + 19 条静态兜底) + fn list_models(&self) -> Vec { + models::list() + } + + /// 构造 `POST {api}/api/proxy/v1/chat/completions`(OpenAI 协议,body 只改 + /// 四处,见 `chat.rs` 的模块头)。 + fn build_chat_request( + &self, + account: &Value, + body: &Value, + client_headers: &HeaderMap, + ) -> Result { + chat::build_chat_request(account, body, client_headers) + } + + /// 上游错误分类(判定顺序本身是契约,见 `errors::classify_gateway_error`): + /// 1. 402 / 正文含余额耗尽关键词 → `QuotaLimited`(换号 + 冷却); + /// 2. 40100 / 40101 → `TokenExpired`(只能重新登录); + /// 3. 429 → `QuotaLimited`;404 / 5xx → `Fatal`; + /// 4. 其余 4xx → 共用内容拦截分类。 + fn classify_error(&self, status: u16, error_body: &Value) -> UpstreamErrorClass { + errors::classify_gateway_error(status, error_body) + } + + /// 有远程目录(`GET /api/models/available`,见 `models.rs`) + fn supports_model_refresh(&self) -> bool { + true + } + + /// 刷模型目录:用指定账号(空串 = 库里第一条启用的账号)的令牌。 + /// + /// 「一个账号都没有」不是失败 —— 那是「用户还没添加账号」的正常状态, + /// 返回 `unchanged()`(脚本 / CI 不该看到一个红色失败,与 AtomCode / Loomy + /// 同口径)。 + fn refresh_models<'a>( + &'a self, + store: &'a AccountStore, + account_id: &'a str, + force: bool, + ) -> std::pin::Pin + Send + 'a>> { + Box::pin(async move { + let record = store.lobsterai_account_record(account_id); + if record.is_none() { + if account_id.is_empty() { + logging::verbose("[Models]", "LobsterAI 模型目录刷新跳过:尚未添加账号"); + return ModelRefreshOutcome::unchanged(); + } + return ModelRefreshOutcome::failed("指定的账号不存在或不可用,请重新选择"); + } + let credentials = match credentials::from_record(record.as_ref()) { + Ok(credentials) => credentials, + Err(error) => return ModelRefreshOutcome::failed(error.message), + }; + models::refresh(&credentials, force).await + }) + } + + /// 取可用访问令牌。 + /// + /// ⚠️ **这里不发网络请求**(本方法是同步语义的取值口,且会在转发路径上被调): + /// 临期只记一行提示,真正的续期由维护链路(`supports_refresh` + + /// `refresh_access_token`)与 401 后的强制刷新负责 —— 与 `atomcode/mod.rs` + /// 同一取舍。 + fn ensure_access_token<'a>( + &'a self, + store: &'a AccountStore, + account_id: &'a str, + ) -> std::pin::Pin> + Send + 'a>> + { + Box::pin(async move { + let record = store.lobsterai_account_record(account_id); + if !account_id.is_empty() && record.is_none() { + return Err(GatewayError::with_status( + 401, + format!("LobsterAI 账号 {account_id} 不存在(请重新添加)"), + )); + } + let credentials = credentials::from_record(record.as_ref())?; + if credentials.expiring() { + logging::verbose( + "[LobsterAI]", + "账号令牌已过期或临期,将按需续期(续期会轮换 refresh token)", + ); + } + Ok(credentials.access_token) + }) + } + + /// 有续期接口(`POST /api/auth/refresh`) + fn supports_refresh(&self) -> bool { + true + } + + /// 强制续期(401 之后 / 维护链路):刷新并**把新令牌写回记录**。 + /// + /// ── 为什么必须写回,以及为什么必须单飞 ─────────────────────── + /// 续期**会轮换** refresh token:上游同时下发新 access 与新 refresh,旧 + /// access 立即失效。丢掉新的 refresh 等于让账号退化成必须重新登录;两个并发 + /// 续期互相作废(后写覆盖前者)—— 因此走本仓统一的 `refresh_flight::Table` + /// 做单飞,键含**两个令牌的指纹**(令牌一变就是新的一轮,与 Cline 同一口径)。 + /// + /// ⚠️ **`latestKeyfrom` 绝不覆盖**:续期只回写 access / refresh / expiresAt, + /// 载荷件三件套保持登录时那一份(`credentials.rs` 的模块头:那是唯一经过 + /// 生产验证的形态,Go 与 TS 都这么做并注明「不是为了省事」)。 + fn refresh_access_token<'a>( + &'a self, + store: &'a AccountStore, + account_id: &'a str, + ) -> std::pin::Pin> + Send + 'a>> + { + Box::pin(async move { + let record = store.lobsterai_account_record(account_id); + let credentials = credentials::from_record(record.as_ref())?; + if credentials.refresh_token.is_empty() { + return Err(GatewayError::with_status( + 401, + "LobsterAI 账号没有 refresh_token,无法续期,请重新登录", + )); + } + let key = format!( + "{}:{}:{}:{}", + store.file_string(), + account_id, + refresh_flight::fingerprint(&credentials.access_token), + refresh_flight::fingerprint(&credentials.refresh_token), + ); + match LOBSTERAI_FLIGHTS + .get_or_init(refresh_flight::Table::new) + .join(&key) + { + refresh_flight::Join::Waiter(waiter) => { + waiter.wait().await.map(|fresh| fresh.access_token) + } + refresh_flight::Join::Leader(leader) => { + let result = refresh_and_save(store, &credentials).await; + leader.finish(result.clone()); + result.map(|fresh| fresh.access_token) + } + } + }) + } + + /// 临期判定:`expiresAt`(落盘时算好)或 JWT 的 `exp`,到期前 1 小时算临期 + /// (见 `credentials::LobsterCredentials::expiring`)。 + fn credentials_expiring(&self, store: &AccountStore, account_id: &str) -> bool { + if account_id.is_empty() { + return false; + } + match store.lobsterai_account_record(account_id) { + Some(record) => credentials::from_record(Some(&record)) + .map(|credentials| credentials.expiring()) + .unwrap_or(false), + None => false, + } + } + + /// 有积分概念(`GET /api/user/profile-summary`,见 `usage.rs`) + fn supports_usage(&self) -> bool { + true + } + + /// 查积分:`GET /api/user/profile-summary` 的归一化。 + /// 401 由调用方走统一的「刷新后重试」链路。 + fn query_usage<'a>( + &'a self, + store: &'a AccountStore, + account_id: &'a str, + ) -> std::pin::Pin> + Send + 'a>> + { + Box::pin(async move { usage::query_usage(store, account_id).await }) + } + + /// 本家**没有**「默认模型」概念(不指定模型时按目录 / 上游默认) + fn supports_default_model(&self) -> bool { + false + } + + /// 本家**是**「回调带回一次性 code」那一套(授权页 → 本机 loopback → + /// 网关换码),因此三个登录钩子都要实现(与 Accio 同形,见模块头)。 + fn supports_web_login(&self) -> bool { + true + } + + /// 授权地址:`{portal}/portal#/login?source=electron&redirect_uri=<本机回调>&state=…` + /// + /// 回调地址由本进程的监听端口拼出(`ServerState::bootstrap` 时写入, + /// 见 `oauth::set_loopback_port`)。端口未知时返回 None —— 上层文案会把 + /// 「未能生成授权地址」透给用户,比拼一个必然 404 的地址好。 + fn build_login_url(&self) -> Option<(String, String)> { + let return_url = oauth::redirect_uri()?; + Some(oauth::begin_login(&return_url)) + } + + /// 用回调里的一次性 `code` 换令牌并落账号。 + /// + /// `state` 在这里再校验一次(深度防御:回调路由已比对过,但真正的凭据是 + /// pending 表里那份 —— 取不到就是「不是本进程发起的那一轮」)。 + /// + /// `uuid` 与两个 keyfrom 由**客户端生成**(TS `lobsterai-oauth.ts:82-84`): + /// `uuid` 是这次登录的安装标识、`firstKeyfrom` 是登录时刻(毫秒)、 + /// `latestKeyfrom` 取当前时刻。三者随换码下发、续期时要原样回传,所以一定要 + /// 落进账号(`add_lobsterai_account` 会存下来)。 + fn exchange_login_code<'a>( + &'a self, + store: &'a AccountStore, + code: &'a str, + state: &'a str, + ) -> std::pin::Pin> + Send + 'a>> + { + Box::pin(async move { + let Some(_pending) = oauth::take_pending(state) else { + return Err(GatewayError::with_status( + 404, + "这次登录已取消或已过期,请重新发起", + )); + }; + // 版本号是换码体的必填字段;解析结果同时写进转发的版本头快照 + let client_version = chat::prime_client_version().await; + let keyfrom = logging::now_ms().max(0).to_string(); + let uuid = uuid_v4(); + let bundle = oauth::exchange(code, &uuid, &keyfrom, &keyfrom, &client_version).await?; + + // 落账号:字段用本仓驼峰名(`add_lobsterai_account` 两种拼法都认, + // 但驼峰与账号记录里存下来的形态一致,便于人工核对) + let mut payload = Map::new(); + payload.insert( + "accessToken".to_string(), + Value::String(bundle.access_token.clone()), + ); + if !bundle.refresh_token.trim().is_empty() { + payload.insert( + "refreshToken".to_string(), + Value::String(bundle.refresh_token.clone()), + ); + } + payload.insert("uuid".to_string(), Value::String(uuid)); + payload.insert("firstKeyfrom".to_string(), Value::String(keyfrom.clone())); + payload.insert("latestKeyfrom".to_string(), Value::String(keyfrom)); + if !bundle.uid.trim().is_empty() { + payload.insert("uid".to_string(), Value::String(bundle.uid.clone())); + } + if !bundle.user_id.trim().is_empty() { + payload.insert("userId".to_string(), Value::String(bundle.user_id.clone())); + } + if !bundle.nickname.trim().is_empty() { + payload.insert("name".to_string(), Value::String(bundle.nickname.clone())); + } + if bundle.expires_in > 0 { + payload.insert("expiresIn".to_string(), Value::from(bundle.expires_in)); + } + + let account = store + .add_lobsterai_account(&Value::Object(payload), None) + .map_err(|error| GatewayError::with_status(error.status_code, error.message))?; + let account_id = account + .get("id") + .and_then(Value::as_str) + .unwrap_or("") + .to_string(); + logging::log("[Login]", "✅ LobsterAI 网页登录完成,账号已加入列表"); + Ok(account_id) + }) + } +} + +/// 续期单飞表(进程级;键含两个令牌的指纹,令牌一变就是新的一轮) +static LOBSTERAI_FLIGHTS: OnceLock> = + OnceLock::new(); + +/// 真的去打一次续期,并把**轮换后的令牌**写回账号记录。 +/// +/// 抽成自由函数是为了让单飞分支的「Leader 职责」只出现在一处: +/// 取版本 → 发请求 → 落盘 → 返回新凭证。**只回写 access / refresh / expiresAt**, +/// 载荷件三件套不动(见 `refresh_access_token` 的说明)。 +async fn refresh_and_save( + store: &AccountStore, + credentials: &credentials::LobsterCredentials, +) -> Result { + let client_version = chat::prime_client_version().await; + let bundle = oauth::refresh(credentials, &client_version).await?; + // 过期时刻:刚续期成功,以此刻为基准算(`expires_in` 缺失时退回读 JWT 的 exp) + let expires_at = + credentials::expires_at_from(logging::now_ms(), bundle.expires_in, &bundle.access_token); + // 轮换语义:refresh token 非空才算轮换(上游可能省略,那表示沿用旧的那一个) + let rotated = !bundle.refresh_token.trim().is_empty(); + // 落盘失败**不**把这次续期当成功:新的 access 已经在手上、但记录里还是旧的, + // 下次仍会走一次续期(而旧的 refresh 可能已被轮换作废)。如实上报。 + let written = store.update_account_tokens( + &credentials.id, + Some(bundle.access_token.as_str()), + rotated.then(|| bundle.refresh_token.as_str()), + (expires_at > 0).then_some(expires_at as f64), + None, + ); + if !written { + return Err(GatewayError::with_status( + 502, + "LobsterAI 续期成功但写回账号失败(账号记录不存在?):请重新登录", + )); + } + logging::log("[LobsterAI]", "令牌已续期并写回账号(refresh token 已轮换)"); + Ok(credentials::LobsterCredentials { + id: credentials.id.clone(), + access_token: bundle.access_token.clone(), + refresh_token: if rotated { + bundle.refresh_token.clone() + } else { + credentials.refresh_token.clone() + }, + expires_at, + // 载荷件三件套保持登录时那一份(**不覆盖**) + uid: credentials.uid.clone(), + user_id: credentials.user_id.clone(), + nickname: credentials.nickname.clone(), + uuid: credentials.uuid.clone(), + first_keyfrom: credentials.first_keyfrom.clone(), + latest_keyfrom: credentials.latest_keyfrom.clone(), + }) +} + +/// 生成一个 UUID v4 字符串(`8-4-4-4-12` 带横线)。 +/// +/// 与 `chatglm::sign::uuid_v4` **同形**(随机源是 `getrandom`,本项目已是直接 +/// 依赖;随机源失败时退回时间戳派生的形态 —— 仍是 UUID 形态,比发一个形态不对 +/// 的值好)。两个消费点:签到体的幂等键(`checkin.rs`)与登录的 `uuid` 载荷件。 +/// 本模块自带一份而不是跨 provider 引用,是为了不让两家在签名工具上演进出耦合。 +pub(crate) fn uuid_v4() -> String { + let mut bytes = [0u8; 16]; + if getrandom::getrandom(&mut bytes).is_err() { + let millis = logging::now_ms().max(0) as u128; + bytes[..16].copy_from_slice(&millis.to_le_bytes()[..16]); + } + // 版本位(第 7 字节高 4 位 = 0100)与变体位(第 9 字节高 2 位 = 10) + bytes[6] = (bytes[6] & 0x0f) | 0x40; + bytes[8] = (bytes[8] & 0x3f) | 0x80; + let hex: String = bytes.iter().map(|byte| format!("{byte:02x}")).collect(); + let part = |start: usize, end: usize| hex.get(start..end).unwrap_or("").to_string(); + format!( + "{}-{}-{}-{}-{}", + part(0, 8), + part(8, 12), + part(12, 16), + part(16, 20), + part(20, 32) + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + /// 适配器声明的能力集(这份断言存在的意义:改动能力位时会被迫读一遍模块头 + /// 里「为什么」的那几节)。 + #[test] + fn adapter_declares_the_expected_capabilities() { + let adapter = &LOBSTERAI_ADAPTER; + assert_eq!(adapter.kind(), ProviderKind::LobsterAi); + assert!(adapter.supports_chat(), "上游就是 OpenAI 协议,必然能转发"); + assert!(!adapter.is_stateful(), "一次 HTTP 请求 = 一次回答"); + assert!(adapter.supports_model_refresh(), "有 /api/models/available 目录接口"); + assert!(adapter.supports_refresh(), "有 /api/auth/refresh 续期接口"); + assert!(adapter.supports_usage(), "有 /api/user/profile-summary 积分读数"); + assert!(!adapter.supports_default_model(), "本家没有默认模型概念"); + // 本家**是**「回调带回一次性 code」那一套(与 AtomCode 相反)。 + assert!(adapter.supports_web_login()); + } + + /// 授权地址要指向门户的 hash 路由,且回调是本机 loopback。 + /// (端口未登记时 `redirect_uri()` 给 None —— 那时 `build_login_url` 也返回 + /// None,本用例只在端口已知时才断言。) + #[test] + fn login_url_points_at_the_portal_when_the_port_is_known() { + if oauth::redirect_uri().is_none() { + // 端口未知时**必须**返回 None,而不是拼一个必然 404 的地址 + assert!(LOBSTERAI_ADAPTER.build_login_url().is_none()); + return; + } + let (url, state) = LOBSTERAI_ADAPTER.build_login_url().expect("端口已知应给出授权地址"); + assert!(url.contains("/portal#/login"), "{url}"); + assert!(url.contains("source=electron"), "{url}"); + assert!(url.contains("redirect_uri="), "{url}"); + assert!(!state.is_empty()); + // 登记过的 state 可被兑一次(登录回调要按它找回那一轮) + assert!(oauth::take_pending(&state).is_some()); + } + + /// 幂等键 / 登录 uuid 的形态是 UUID v4(版本位 4、变体位 8/9/a/b)。 + #[test] + fn uuid_v4_is_shaped_correctly() { + let key = uuid_v4(); + let parts: Vec<&str> = key.split('-').collect(); + assert_eq!(parts.len(), 5, "{key}"); + assert_eq!( + parts.iter().map(|part| part.len()).collect::>(), + vec![8, 4, 4, 4, 12], + "{key}" + ); + assert!(parts[2].starts_with('4'), "版本位不是 4:{key}"); + assert!( + matches!(parts[3].chars().next(), Some('8' | '9' | 'a' | 'b')), + "变体位不对:{key}" + ); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/models.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/models.rs new file mode 100644 index 00000000..9cab8597 --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/models.rs @@ -0,0 +1,759 @@ +//! LobsterAI 的模型目录:`GET /api/models/available` 是权威来源,**另有一份 +//! 19 条静态兜底**(远程拉不到 / 关掉发现时用)。 +//! +//! ── 响应信封:`data` 是**单层数组**,别拿通用对象信封解析器来套 ────── +//! ```text +//! {"code":0,"message":"success","data":[ {modelId, modelName, …}, … ]} +//! ``` +//! Go 参考在 `models.go:216-244` 明确警告:它**不**复用 `parseEnvelope` —— +//! 那个解析器要求 `data` 是**对象**(这样 `data:null` 才能被当成「令牌失效」), +//! 而这个端点的 `data` 是数组;套上去会让整份模型清单**静默为空**。 +//! 本模块自带 [`read_model_array`],并兼容「双层层套」(`data.data[]`, +//! 参考实现说野生形态里两种都出现过)。 +//! +//! ── keyfrom 只进 query,**绝不带 refresh token** ──────────────── +//! 目录端点的身份是 `firstKeyfrom` / `latestKeyfrom` / `version`(+ 可选的 +//! `uuid` / `userId`),不带 refresh token —— query 会进服务端的访问日志 +//! (`models.go:523-526` 的原话)。拼法与 `credentials::keyfrom_fields` 同源。 +//! +//! ── 两个 `X-LobsterAI-Client-*` 头**在这条端点上尤其重要** ───────── +//! 服务端按 `ClientCapabilities` **过滤**返回的模型集合:不带 +//! `kimi-k3-agentic-v1` 时目录里少一个模型(实测 25 vs 26,见 +//! `endpoints.rs` 的模块头)。少发这个头不是「少一条头」,是**少一个模型**。 +//! +//! ── 字段只按上游声明的取值,缺了就不广告 ───────────────────────── +//! `supportsImage` / `supportsThinking` / `costMultiplier` 缺失时**保持未知** +//! (`0` 与「字段不存在」是两回事:`0` 意思是免费)—— 与 Go 的 `remoteModel` +//! 注释(`models.go:110-131`)同一口径:编一个 `false` / `0` 会把将来的模型 +//! 静默归类错。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use std::sync::{Mutex, OnceLock}; + +use serde_json::{Map, Value}; + +use crate::server::core::auth_http::send_raw; +use crate::server::core::providers::adapter::ModelRefreshOutcome; +use crate::server::core::providers::catalog_cache; +use crate::server::errors::GatewayError; +use crate::server::logging; + +use super::chat; +use super::credentials::LobsterCredentials; +use super::endpoints::{ + CATALOG_TIMEOUT_MS, CLIENT_CAPABILITIES, MODELS_PATH, USER_AGENT, api_base, models_url, + urlencode, +}; + +/// 本家在 `catalog_cache` 里的 scope 名 +pub const SCOPE: &str = catalog_cache::SCOPE_LOBSTERAI; + +/// 目录缓存时长(与参考实现的 2 小时同档) +const CATALOG_TTL_MS: i64 = 2 * 60 * 60 * 1000; + +/// 兜底目录里每条模型的上下文窗口(**照抄参考实现**,是桥接层的估计值 +/// `131072`,**不是**逐模型实测 —— 见 [`fallback_models`] 的说明) +const FALLBACK_CONTEXT_WINDOW: i64 = 131_072; + +/// 目录里的一条模型 +#[derive(Clone, Debug)] +pub struct ModelEntry { + /// 发给上游的模型名(`modelId`)—— 也是目录对外广告的 `id` + pub wire_id: String, + /// 展示名(`modelName`,缺时等于 `wire_id`) + pub display_name: String, + /// 上下文窗口(上游没给 = 0,不猜) + pub context_window: i64, + /// 单次输出上限(`maxTokens`;上游没给 = 0) + pub max_tokens: i64, + /// 是否收图片(上游没给 = None,**不猜**) + pub supports_image: Option, + /// 是否支持思考(`supportsThinking` 或 `thinkingConfig` 有档位) + pub supports_thinking: bool, + /// 积分倍率(**裸数字**,与腾讯系的 `"x0.05"` 字符串不同)。 + /// None = 字段缺失;Some(0.0) = 免费(**不能**当成缺失去掉) + pub cost_multiplier: Option, +} + +/// 进程内目录状态(**只装远程目录**;空表示还没拉到过,此时对外用兜底清单) +#[derive(Clone, Debug, Default)] +struct CatalogState { + entries: Vec, + fetched_at: i64, +} + +static STATE: OnceLock>> = OnceLock::new(); + +/// 取状态句柄(毒锁按「可重建的展示数据」处理,与本仓其它目录模块同口径) +fn state() -> &'static Mutex> { + STATE.get_or_init(|| Mutex::new(None)) +} + +/// 在锁内执行一段取值,并在首次访问时把落盘缓存装回内存 +fn with_state(f: impl FnOnce(&mut Option) -> R) -> R { + let mut guard = match state().lock() { + Ok(guard) => guard, + Err(poisoned) => poisoned.into_inner(), + }; + if guard.is_none() { + if let Some(cached) = catalog_cache::load(SCOPE) { + let entries: Vec = cached + .models + .iter() + .filter_map(entry_from_cached) + .collect(); + if !entries.is_empty() { + *guard = Some(CatalogState { + entries, + fetched_at: cached.fetched_at, + }); + } + } + } + f(&mut guard) +} + +/// 从落盘缓存条目还原(缓存里存的是 [`to_cached_value`] 写下的形态) +fn entry_from_cached(value: &Value) -> Option { + let wire_id = value.get("model").and_then(Value::as_str)?.trim().to_string(); + if wire_id.is_empty() { + return None; + } + Some(ModelEntry { + display_name: value + .get("name") + .and_then(Value::as_str) + .unwrap_or("") + .to_string(), + context_window: value.get("contextWindow").and_then(Value::as_i64).unwrap_or(0), + max_tokens: value.get("maxTokens").and_then(Value::as_i64).unwrap_or(0), + supports_image: value.get("supportsImages").and_then(Value::as_bool), + supports_thinking: value + .get("supportsReasoning") + .and_then(Value::as_bool) + .unwrap_or(false), + cost_multiplier: value.get("multiplier").and_then(Value::as_f64), + wire_id, + }) +} + +/// 把目录条目落盘时的形态(key 用本仓惯用的驼峰,便于人工看) +fn to_cached_value(entry: &ModelEntry) -> Value { + let mut object = Map::new(); + object.insert("model".to_string(), Value::String(entry.wire_id.clone())); + object.insert("name".to_string(), Value::String(entry.display_name.clone())); + if entry.context_window > 0 { + object.insert("contextWindow".to_string(), Value::from(entry.context_window)); + } + if entry.max_tokens > 0 { + object.insert("maxTokens".to_string(), Value::from(entry.max_tokens)); + } + if let Some(image) = entry.supports_image { + object.insert("supportsImages".to_string(), Value::Bool(image)); + } + if entry.supports_thinking { + object.insert("supportsReasoning".to_string(), Value::Bool(true)); + } + if let Some(multiplier) = entry.cost_multiplier { + object.insert("multiplier".to_string(), Value::from(multiplier)); + } + Value::Object(object) +} + +/// 静态兜底目录:**19 条,逐条照抄 Go 参考**(`models.go:138-158` 的 +/// `fallbackModels`,其顺序也照抄,便于与上游对账)。上下文窗口统一 +/// [`FALLBACK_CONTEXT_WINDOW`](那是桥接层的估计值,不是逐模型实测)。 +/// +/// 只在「远程发现关掉 / 拉取失败」时对外 —— 远程一旦成功就是权威。 +fn fallback_models() -> Vec { + const IDS: [&str; 19] = [ + "deepseek-v4-flash", + "deepseek-v4-pro", + "MiniMax-M3", + "MiniMax-M2.7", + "qwen3.7-max", + "qwen3.7-plus", + "qwen3.6-plus", + "qwen3.5-plus-2026-04-20", + "kimi-k2.7-code", + "kimi-k2.7-code-highspeed", + "kimi-k2.6", + "kimi-k2.5", + "doubao-seed-2-1-pro-260628", + "doubao-seed-2-1-turbo-260628", + "doubao-seed-2-0-code-preview-260215", + "glm-5.2", + "glm-5.1", + "glm-5v-turbo", + "glm-5", + ]; + IDS.iter() + .map(|id| ModelEntry { + wire_id: (*id).to_string(), + display_name: (*id).to_string(), + context_window: FALLBACK_CONTEXT_WINDOW, + max_tokens: 0, + supports_image: None, + supports_thinking: false, + // 兜底清单里**不带**倍率:那要上游逐条声明,编一个 0(免费)会误导 + cost_multiplier: None, + }) + .collect() +} + +/// 读一个数字(数字或数字字符串都认) +fn read_number(value: &Value) -> Option { + if let Some(number) = value.as_f64() { + return Some(number); + } + value.as_str().and_then(|text| text.trim().parse::().ok()) +} + +/// 读一个正数(`> 0` 才算,否则视为缺失 —— 与 Go `contextWindow > 0` 同口径) +fn read_positive(value: Option<&Value>) -> i64 { + value + .and_then(read_number) + .filter(|number| *number > 0.0) + .map(|number| number as i64) + .unwrap_or(0) +} + +/// `thinkingConfig` 是否声明了可选档位(非空 `options` 且不止一个 `off`)。 +/// +/// Go 的 `parseThinkingConfig` 会丢弃整块结构不符的配置,并且「只有 off 一档」 +/// 等于「没有可选档位」—— 这里只需要一个布尔,按同一口径折叠。 +fn thinking_config_present(value: Option<&Value>) -> bool { + let Some(options) = value + .and_then(|config| config.get("options")) + .and_then(Value::as_array) + else { + return false; + }; + options + .iter() + .filter(|option| { + option + .get("openclawLevel") + .and_then(Value::as_str) + .is_some_and(|level| !level.trim().is_empty()) + }) + .count() + > 1 +} + +/// 把一条 `models/available` 条目映射成本模块的形态(缺 `modelId` 的丢掉)。 +fn entry_from_upstream(value: &Value) -> Option { + let wire_id = value + .get("modelId") + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or("") + .to_string(); + if wire_id.is_empty() { + return None; + } + let display_name = match value.get("modelName").and_then(Value::as_str) { + Some(name) if !name.trim().is_empty() => name.trim().to_string(), + _ => wire_id.clone(), + }; + Some(ModelEntry { + display_name, + context_window: read_positive(value.get("contextWindow")), + max_tokens: read_positive(value.get("maxTokens")), + supports_image: value.get("supportsImage").and_then(Value::as_bool), + supports_thinking: value + .get("supportsThinking") + .and_then(Value::as_bool) + .unwrap_or(false) + || thinking_config_present(value.get("thinkingConfig")), + // costMultiplier 是**裸数字**;`0` 意思是免费,必须保留而不是当缺失去掉 + cost_multiplier: value.get("costMultiplier").and_then(read_number), + wire_id, + }) +} + +/// 倍率的展示串:`0` → 免费,其余 → `x<数字>`(前端 `formatCredits` 认 `x\d`)。 +/// +/// 这条口径照抄 Go 的 `displayNameFor`(`models.go:303-314`):`0` 渲染成 +/// 「免费」而不是「x0」。差别只在本仓把它放进 `credits` 键而不是拼进展示名。 +fn credits_label(multiplier: f64) -> String { + if multiplier == 0.0 { + return "免费".to_string(); + } + format!("x{multiplier}") +} + +/// 聚合层认的条目形态(键名对照 `core/models/shape.rs::list_item`)。 +/// +/// `credits` 放**倍率展示串**(本仓的「倍率」列读的就是它,见 loomy 的同款处置); +/// `0` 意思是免费。 +fn to_manifest_value(entry: &ModelEntry) -> Value { + let mut object = Map::new(); + object.insert("id".to_string(), Value::String(entry.wire_id.clone())); + if !entry.display_name.is_empty() { + object.insert("name".to_string(), Value::String(entry.display_name.clone())); + } + if entry.context_window > 0 { + object.insert("maxInputTokens".to_string(), Value::from(entry.context_window)); + } + if entry.max_tokens > 0 { + object.insert("maxOutputTokens".to_string(), Value::from(entry.max_tokens)); + } + // 模态位:上游**没给**时按不支持折叠,但不声称支持(有实测依据才广告) + object.insert( + "supportsImages".to_string(), + Value::Bool(entry.supports_image.unwrap_or(false)), + ); + object.insert("supportsVideo".to_string(), Value::Bool(false)); + object.insert( + "supportsReasoning".to_string(), + Value::Bool(entry.supports_thinking), + ); + // 上游就是 OpenAI Chat Completions,tools 原样透传 ⇒ 支持工具调用 + object.insert("supportsToolCall".to_string(), Value::Bool(true)); + if let Some(multiplier) = entry.cost_multiplier { + object.insert("credits".to_string(), Value::String(credits_label(multiplier))); + } + Value::Object(object) +} + +/// 当前对外目录:**远程优先,没有则兜底** +pub fn list() -> Vec { + with_state(|guard| match guard.as_ref() { + Some(state) if !state.entries.is_empty() => { + state.entries.iter().map(to_manifest_value).collect() + } + _ => fallback_models().iter().map(to_manifest_value).collect(), + }) +} + +/// 目录里有没有**远程拉到的**条目(`false` 时 [`list`] 给的是兜底清单) +pub fn remote_refreshed() -> bool { + with_state(|guard| { + guard + .as_ref() + .map(|state| state.fetched_at > 0 && !state.entries.is_empty()) + .unwrap_or(false) + }) +} + +/// 目录最后一次落地时刻(毫秒;0 = 没有远程目录) +pub fn last_refreshed_at() -> i64 { + with_state(|guard| guard.as_ref().map(|state| state.fetched_at).unwrap_or(0)) +} + +/// 当前远程目录的年龄(毫秒;0 = 没有远程目录) +pub fn age_ms() -> i64 { + with_state(|guard| { + guard + .as_ref() + .map(|state| logging::now_ms() - state.fetched_at) + .unwrap_or(0) + }) +} + +/// 远程目录是否为空(**不看兜底**:兜底永远非空,所以这里只回答远程那一份) +pub fn is_empty() -> bool { + with_state(|guard| { + guard + .as_ref() + .map(|state| state.entries.is_empty()) + .unwrap_or(true) + }) +} + +/// Go 参考的**规范化别名 → 线上 id** 表(`models.go:388-395` 的 `upstreamModelIDs`)。 +/// +/// ── 为什么需要它(这条回答「发布名能不能不等于线上 id」)──────────── +/// **能**。Go 参考对外发布的是**规范化大小写的 id**(`publicModelIDs`: +/// `deepseek-flash` → `DeepSeek-V4.1-Flash`),并另建一张反向表把请求里的 +/// 规范化名折回线上 id(它自己的注释说:CPA 会跳过只差大小写的别名,所以规范 +/// 大小写必须由插件自己发布)。本仓的目录直接以 `modelId`(线上 id)作为对外 +/// `id`,因此**绝大多数情况下两者相同**;这张表只为「客户端手里握着旧规范化名」 +/// 那条路兜底。 +/// +/// 键一律按小写比较。 +const UPSTREAM_ALIASES: [(&str, &str); 6] = [ + ("deepseek-v4.1-flash", "deepseek-flash"), + ("glm-5.3-flash", "glm-5.3-flash"), + ("glm-5.3", "glm-5.3"), + ("qwen3.8-max", "qwen3.8-max"), + ("qwen3.8-flash-next", "qwen3.8-flash"), + ("kimi-k2.7-code", "kimi-k2.7-code"), +]; + +/// 客户端请求里的模型名 → 发给上游的模型名。 +/// +/// 顺序:远程目录(按线上 id 或展示名命中)→ 规范化别名表 → 认不出返回 `None` +/// (由调用方**原样发**:猜一个更接近的名字比原样发更糟 —— 那会把「名字没映射」 +/// 变成一个静默的错误重试)。 +pub fn upstream_id_for(requested: &str) -> Option { + let target = requested.trim(); + if target.is_empty() { + return None; + } + if let Some(wire_id) = with_state(|guard| { + guard.as_ref().and_then(|state| { + state + .entries + .iter() + .find(|entry| entry.wire_id == target || entry.display_name == target) + .map(|entry| entry.wire_id.clone()) + }) + }) { + return Some(wire_id); + } + let lower = target.to_lowercase(); + UPSTREAM_ALIASES + .iter() + .find(|(alias, _)| *alias == lower) + .map(|(_, wire)| (*wire).to_string()) +} + +/// 把 `keyfrom_fields` 拼成 URL query(`k=v&k2=v2`,值 urlencoded,**不含 +/// refresh token** —— 见模块头)。 +fn keyfrom_query(credentials: &LobsterCredentials, client_version: &str) -> String { + let fields = credentials.keyfrom_fields(client_version); + let mut query = String::new(); + for (key, value) in fields { + let escaped = urlencode(&value); + if !query.is_empty() { + query.push('&'); + } + query.push_str(key); + query.push('='); + query.push_str(&escaped); + } + query +} + +/// 从响应里取模型数组。 +/// +/// **不**复用通用的对象信封解析器(会要求 `data` 是对象,从而把整份清单判空, +/// 见模块头);`code != 0` 一律判失败,`data` 支持单层数组与 `data.data[]` 双层。 +pub fn read_model_array(payload: &Value) -> Option> { + let record = payload.as_object()?; + let code = record.get("code").and_then(read_number)?; + if code != 0.0 { + return None; + } + if let Some(array) = record.get("data").and_then(Value::as_array) { + return Some(array.clone()); + } + record + .get("data") + .and_then(Value::as_object) + .and_then(|nested| nested.get("data")) + .and_then(Value::as_array) + .cloned() +} + +/// 目录端点 URL(query 为空时退回不带 `?` 的形态,与 Go `buildModelsURL` 同口径) +fn catalog_url(query: &str) -> String { + let trimmed = query.trim(); + if trimmed.is_empty() { + format!("{}{}", api_base(), MODELS_PATH) + } else { + models_url(trimmed) + } +} + +/// 拉一次目录(返回解析后的条目;`code != 0` 或形状不符时报错)。 +async fn fetch_catalog( + credentials: &LobsterCredentials, + client_version: &str, +) -> Result, GatewayError> { + // 版本头与 query 里的 version 用**同一个值**(都由 `chat::prime_client_version` + // 给出),避免「声明是这个版本、目录按那个版本过滤」的错位。 + let version = if client_version.trim().is_empty() { + chat::client_version_header() + } else { + client_version.trim().to_string() + }; + let url = catalog_url(&keyfrom_query(credentials, &version)); + let headers: Vec<(String, String)> = vec![ + ( + "authorization".to_string(), + format!("Bearer {}", credentials.access_token), + ), + ("accept".to_string(), "application/json".to_string()), + ( + "x-lobsterai-client-capabilities".to_string(), + CLIENT_CAPABILITIES.to_string(), + ), + ( + "x-lobsterai-client-version".to_string(), + version.clone(), + ), + ("user-agent".to_string(), USER_AGENT.to_string()), + ]; + let response = send_raw("GET", &url, None, &headers, None, Some(CATALOG_TIMEOUT_MS)) + .await + .map_err(|error| { + if error.is_timeout() { + GatewayError::with_status(504, "LobsterAI 目录接口超时") + } else { + GatewayError::with_status(502, format!("LobsterAI 目录接口请求失败: {error}")) + } + })?; + let payload = response.payload.ok_or_else(|| { + GatewayError::with_status(502, "LobsterAI 目录接口响应不是 JSON") + })?; + if !response.ok { + return Err(GatewayError::with_status( + response.status as i32, + format!("LobsterAI 目录接口返回 HTTP {}", response.status), + )); + } + let items = read_model_array(&payload).ok_or_else(|| { + GatewayError::with_status(502, "LobsterAI 目录响应结构不符(code != 0 或缺 data 数组)") + })?; + Ok(items.iter().filter_map(entry_from_upstream).collect()) +} + +/// 刷新目录:远程成功即权威;失败时**保留现有清单**并如实汇报原因。 +pub async fn refresh(credentials: &LobsterCredentials, force: bool) -> ModelRefreshOutcome { + if !force && remote_refreshed() && age_ms() < CATALOG_TTL_MS { + return ModelRefreshOutcome::unchanged(); + } + + // 顺手把客户端版本解析并写进快照(转发路径的同步版本头要用,见 `chat.rs`) + let version = chat::prime_client_version().await; + + match fetch_catalog(credentials, &version).await { + Ok(entries) if !entries.is_empty() => { + let fetched_at = logging::now_ms(); + let cached: Vec = entries.iter().map(to_cached_value).collect(); + catalog_cache::save(SCOPE, &cached, fetched_at); + let count = entries.len(); + with_state(|guard| { + *guard = Some(CatalogState { + entries, + fetched_at, + }); + }); + logging::log( + "[LobsterAI]", + &format!("目录刷新完成:{count} 个模型(客户端版本 {version})"), + ); + ModelRefreshOutcome::refreshed(count) + } + Ok(_) => ModelRefreshOutcome::failed("LobsterAI 目录为空(上游未返回可用模型)"), + Err(error) => { + logging::verbose( + "[LobsterAI]", + &format!("目录刷新失败,沿用现有清单:{}", error.message), + ); + ModelRefreshOutcome::failed(error.message) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + fn upstream_entry() -> Value { + json!({ + "modelId": "glm-5.2", + "modelName": "GLM-5.2", + "contextWindow": 200_000, + "maxTokens": 32_000, + "supportsImage": true, + "supportsThinking": true, + "thinkingConfig": {"options": [{"level": "off", "openclawLevel": "off"}, {"level": "high", "openclawLevel": "high"}], "defaultLevel": "high"}, + "costMultiplier": 0.05 + }) + } + + /// **信封是单层数组** —— 通用对象信封解析器会把它判空(模块头的警告)。 + #[test] + fn model_array_is_read_from_the_single_layer_envelope() { + let payload = json!({"code": 0, "message": "success", "data": [upstream_entry()]}); + let items = read_model_array(&payload).expect("应读到数组"); + assert_eq!(items.len(), 1); + // 双层层套也认(参考实现说野生形态里两种都出现过) + let nested = json!({"code": 0, "msg": "OK", "data": {"data": [upstream_entry()]}}); + assert_eq!(read_model_array(&nested).map(|items| items.len()), Some(1)); + // code != 0 / data 是 null(令牌失效)一律判失败 + assert!(read_model_array(&json!({"code": 1, "data": [upstream_entry()]})).is_none()); + assert!(read_model_array(&json!({"code": 0, "data": null})).is_none()); + assert!(read_model_array(&json!({"data": [upstream_entry()]})).is_none()); + } + + /// 逐字段消费:`modelId` 是线上 id、`modelName` 是展示名。 + #[test] + fn entry_consumes_every_declared_field() { + let entry = entry_from_upstream(&upstream_entry()).expect("应映射成功"); + assert_eq!(entry.wire_id, "glm-5.2"); + assert_eq!(entry.display_name, "GLM-5.2"); + assert_eq!(entry.context_window, 200_000); + assert_eq!(entry.max_tokens, 32_000); + assert_eq!(entry.supports_image, Some(true)); + assert!(entry.supports_thinking); + assert_eq!(entry.cost_multiplier, Some(0.05)); + // 缺 modelName 时展示名退回线上 id + let no_name = entry_from_upstream(&json!({"modelId": "m1"})).expect("应映射成功"); + assert_eq!(no_name.display_name, "m1"); + // 缺 modelId 的条目丢掉(没有它发不出请求) + assert!(entry_from_upstream(&json!({"modelName": "x"})).is_none()); + } + + /// **未知不猜**:上游没声明的能力位保持 None / false,倍率缺失不写 `credits`。 + #[test] + fn undeclared_capabilities_are_not_invented() { + let entry = entry_from_upstream(&json!({"modelId": "m1"})).expect("应映射成功"); + assert_eq!(entry.supports_image, None); + assert!(!entry.supports_thinking); + assert_eq!(entry.cost_multiplier, None); + assert_eq!(entry.context_window, 0); + let manifest = to_manifest_value(&entry); + assert_eq!(manifest.get("supportsImages"), Some(&Value::Bool(false))); + assert_eq!(manifest.get("supportsReasoning"), Some(&Value::Bool(false))); + assert!(manifest.get("credits").is_none(), "没倍率就别写 credits"); + assert!(manifest.get("maxInputTokens").is_none()); + assert!(manifest.get("maxOutputTokens").is_none()); + } + + /// 只有 `off` 一档的 `thinkingConfig` 等于「没有可选档位」(Go 同口径)。 + #[test] + fn thinking_config_with_only_off_is_not_a_level_set() { + let only_off = json!({ + "modelId": "m1", + "thinkingConfig": {"options": [{"level": "off", "openclawLevel": "off"}], "defaultLevel": "off"} + }); + assert!(!entry_from_upstream(&only_off).expect("应映射成功").supports_thinking); + let two = json!({ + "modelId": "m1", + "thinkingConfig": {"options": [{"level": "off", "openclawLevel": "off"}, {"level": "max", "openclawLevel": "xhigh"}]} + }); + assert!(entry_from_upstream(&two).expect("应映射成功").supports_thinking); + } + + /// 倍率:`0` → 免费(**不是**缺失),正数 → `x<数字>`。 + #[test] + fn credits_label_renders_free_and_multipliers() { + assert_eq!(credits_label(0.0), "免费"); + assert_eq!(credits_label(0.05), "x0.05"); + assert_eq!(credits_label(3.0), "x3"); + // `0` 的倍率必须落进 credits(当成免费广告,而不是当成缺失去掉) + let free = entry_from_upstream(&json!({"modelId": "m1", "costMultiplier": 0})) + .expect("应映射成功"); + assert_eq!(free.cost_multiplier, Some(0.0)); + assert_eq!( + to_manifest_value(&free).get("credits").and_then(Value::as_str), + Some("免费") + ); + // 倍率以**数字字符串**给出时也要认(上游形态不保证) + let text = entry_from_upstream(&json!({"modelId": "m1", "costMultiplier": "x0.8"})); + assert_eq!(text.expect("应映射成功").cost_multiplier, None, "带 x 的串不是裸数字"); + let numeric_text = + entry_from_upstream(&json!({"modelId": "m1", "costMultiplier": "0.8"})) + .expect("应映射成功"); + assert_eq!(numeric_text.cost_multiplier, Some(0.8)); + } + + /// 清单键名必须与 `list_item` 读取的键一一对上。 + #[test] + fn manifest_keys_match_the_aggregate_contract() { + let entry = entry_from_upstream(&upstream_entry()).expect("应映射成功"); + let manifest = to_manifest_value(&entry); + for key in [ + "id", + "name", + "maxInputTokens", + "maxOutputTokens", + "supportsImages", + "supportsVideo", + "supportsReasoning", + "supportsToolCall", + "credits", + ] { + assert!(manifest.get(key).is_some(), "缺键 {key}"); + } + assert_eq!(manifest.get("id"), Some(&Value::String("glm-5.2".into()))); + assert_eq!(manifest.get("supportsToolCall"), Some(&Value::Bool(true))); + assert_eq!(manifest.get("credits").and_then(Value::as_str), Some("x0.05")); + } + + /// 兜底清单:**19 条**、顺序照抄参考实现,首尾两条要能对上。 + #[test] + fn static_fallback_has_nineteen_models_in_reference_order() { + let fallback = fallback_models(); + assert_eq!(fallback.len(), 19); + assert_eq!(fallback[0].wire_id, "deepseek-v4-flash"); + assert_eq!(fallback[18].wire_id, "glm-5"); + assert_eq!(fallback[15].wire_id, "glm-5.2"); + // 兜底条目不带倍率(不编造免费) + assert!(fallback.iter().all(|entry| entry.cost_multiplier.is_none())); + } + + /// 规范化别名折叠:与 Go 的 `upstreamModelIDs` 同表同向。 + #[test] + fn canonical_aliases_fold_to_the_wire_id() { + assert_eq!( + upstream_id_for("DeepSeek-V4.1-Flash"), + Some("deepseek-flash".to_string()) + ); + assert_eq!( + upstream_id_for("qwen3.8-flash-next"), + Some("qwen3.8-flash".to_string()) + ); + // 认不出的名字 → None(调用方原样发,不猜) + assert!(upstream_id_for("完全不存在的模型").is_none()); + assert!(upstream_id_for(" ").is_none()); + } + + /// 落盘缓存的往返:写下的形态必须能原样读回(倍率里的 `0` 尤其别丢)。 + #[test] + fn cached_entries_round_trip() { + let entry = ModelEntry { + wire_id: "glm-5.2".into(), + display_name: "GLM-5.2".into(), + context_window: 200_000, + max_tokens: 32_000, + supports_image: Some(false), + supports_thinking: true, + cost_multiplier: Some(0.0), + }; + let cached = to_cached_value(&entry); + let restored = entry_from_cached(&cached).expect("应可还原"); + assert_eq!(restored.wire_id, "glm-5.2"); + assert_eq!(restored.display_name, "GLM-5.2"); + assert_eq!(restored.context_window, 200_000); + assert_eq!(restored.max_tokens, 32_000); + assert_eq!(restored.supports_image, Some(false)); + assert!(restored.supports_thinking); + assert_eq!(restored.cost_multiplier, Some(0.0)); + } + + /// query 的拼法:键有序、值转义、**绝不含 refresh token**。 + #[test] + fn keyfrom_query_escapes_values_and_carries_no_token() { + let credentials = LobsterCredentials { + id: "lobsterai-1".into(), + access_token: "at".into(), + refresh_token: "rt-should-never-appear".into(), + expires_at: 0, + uid: "42".into(), + user_id: "42".into(), + nickname: "虾".into(), + uuid: "u 1".into(), + first_keyfrom: "fk&a".into(), + latest_keyfrom: "lk".into(), + }; + let query = keyfrom_query(&credentials, "2026.9.4"); + assert!(query.contains("firstKeyfrom=fk%26a"), "{query}"); + assert!(query.contains("latestKeyfrom=lk"), "{query}"); + assert!(query.contains("version=2026.9.4"), "{query}"); + assert!(query.contains("uuid=u%201"), "{query}"); + assert!(!query.contains("rt-should-never-appear"), "refresh token 绝不进 query"); + assert!(!query.contains("refreshToken")); + // 空 query 时 URL 不带 `?` + assert!(!catalog_url("").contains('?')); + assert!(catalog_url("a=b").ends_with("?a=b")); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/oauth.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/oauth.rs new file mode 100644 index 00000000..57dd9b51 --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/oauth.rs @@ -0,0 +1,488 @@ +//! LobsterAI 登录与续期:**浏览器授权 + 本机 loopback 回调 + 换码**。 +//! +//! ── 四步 ──────────────────────────────────────────────────── +//! ```text +//! 1. 本网关按 state 登记一轮登录,产出授权地址: +//! {portal}/portal#/login?source=electron +//! &redirect_uri=http://127.0.0.1:<本网关端口>/auth/callback +//! &state= +//! 2. 用户在浏览器里授权(手机号 / 微信),门户 302 回本机 loopback: +//! GET /auth/callback?code=&state= +//! 3. POST {api}/api/auth/exchange **不带 Authorization** +//! 体 { authCode, firstKeyfrom, latestKeyfrom, uuid, version } +//! 4. 落账号(把 uuid 与两个 keyfrom 一起存 —— 见 `credentials.rs` 的模块头) +//! ``` +//! +//! ── 回调路径为什么不用 `-lobsterai` 后缀 ────────────────────── +//! 本仓其余几家自作主张的回调路径都带家名后缀(`/auth/callback-accio` 等), +//! 一眼能看出归属。这一家**例外**:授权地址里的 `redirect_uri` 是我们拼给门户的, +//! 门户会按它回落 —— 参考实现用的就是裸 `/auth/callback`(`lobsterai.ts:47`), +//! 而门户是否对回调路径有白名单**没有实测支撑**。所以这里跟随参考实现那条 +//! 已验证的形状,不为了命名风格去赌一次未经验证的兼容性。该路径目前在本仓未被占用。 +//! +//! ── 续期:**必须原样回传载荷件,且 `latestKeyfrom` 永不更新** ───── +//! `POST /api/auth/refresh` 同样**不带 Authorization**,体是 +//! `{ firstKeyfrom, latestKeyfrom, version, [uuid], [userId], refreshToken }`。 +//! 响应带新 `refreshToken` 时才轮换;`latestKeyfrom` **不覆盖**(照实测的形态, +//! 见 `credentials.rs` 的模块头)。 +//! +//! ── 两种「成功状态下的失败」 ────────────────────────────────── +//! 1. 业务信封 `code != 0` → 按错误处理; +//! 2. 信封 `code == 0` 但 `data` 为 null → **令牌已失效**(参考实现实测)。 +//! 这一条最容易漏:只看 HTTP 状态或只看 `code` 都会把它当成成功。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use std::collections::HashMap; +use std::sync::{Mutex, OnceLock}; + +use serde_json::{Value, json}; + +use crate::server::core::auth_http::send_raw; +use crate::server::core::upstream::request::new_request_id; +use crate::server::errors::GatewayError; +use crate::server::logging; + +use super::credentials::LobsterCredentials; +use super::endpoints::{ + CALLBACK_PATH, EXCHANGE_PATH, REQUEST_TIMEOUT_MS, REFRESH_PATH, USER_AGENT, api_base, + portal_base, urlencode, +}; + +/// 本网关的 loopback 回调路径(见模块头:跟随参考实现的裸路径) +pub const LOOPBACK_CALLBACK_PATH: &str = CALLBACK_PATH; + +/// pending 表有效期:与本仓登录任务的 5 分钟超时同一量级 +const PENDING_TTL_MS: i64 = 6 * 60 * 1000; + +/// 本网关监听端口(`ServerState::bootstrap` 时写入一次)。 +/// +/// `ProviderAdapter::build_login_url()` 是同步无参的(trait 契约),拿不到 +/// `ServerState`;而授权地址里必须拼本机回调地址。端口在进程生命周期内不变, +/// 因此开机写一次、之后只读 —— 与 `accio::oauth` 同一手法,理由也同一份。 +static LOOPBACK_PORT: OnceLock = OnceLock::new(); + +/// 记录本进程监听端口(重复调用无害) +pub fn set_loopback_port(port: u16) { + let _ = LOOPBACK_PORT.set(port); +} + +/// 本机回调基址(端口未知时 None,错误由上层文案说清) +pub fn loopback_base() -> Option { + LOOPBACK_PORT + .get() + .map(|port| format!("http://127.0.0.1:{port}")) +} + +/// 本机回调完整地址(换码时要原样回传的 `redirect_uri`) +pub fn redirect_uri() -> Option { + loopback_base().map(|base| format!("{base}{LOOPBACK_CALLBACK_PATH}")) +} + +/// 一轮待完成的登录(回调进来时按 state 取回) +pub struct PendingLogin { + created_at: i64, +} + +fn pending_table() -> &'static Mutex> { + static TABLE: OnceLock>> = OnceLock::new(); + TABLE.get_or_init(|| Mutex::new(HashMap::new())) +} + +fn lock_pending() -> std::sync::MutexGuard<'static, HashMap> { + match pending_table().lock() { + Ok(guard) => guard, + Err(poisoned) => poisoned.into_inner(), + } +} + +/// 清掉过期的 pending(顺手做,不需要独立定时器) +fn sweep(table: &mut HashMap) { + let now = logging::now_ms(); + table.retain(|_, entry| now - entry.created_at < PENDING_TTL_MS); +} + +/// 授权地址:`{portal}/portal#/login?source=electron&redirect_uri=…&state=…` +pub fn build_authorize_url(return_url: &str, state: &str) -> String { + format!( + "{}/portal#/login?source=electron&redirect_uri={}&state={}", + portal_base(), + urlencode(return_url), + urlencode(state) + ) +} + +/// 登记一轮登录,返回 `(auth_url, state)`。 +/// +/// 同一时刻只保证「这一轮」可被回调命中:旧轮次留在表里到期自然清掉(用户可能 +/// 开过好几个标签页),但**同一 state 只能被兑一次**(见 [`take_pending`])。 +pub fn begin_login(return_url: &str) -> (String, String) { + let state = new_request_id().replace('-', ""); + let auth_url = build_authorize_url(return_url, &state); + let mut table = lock_pending(); + sweep(&mut table); + table.insert( + state.clone(), + PendingLogin { + created_at: logging::now_ms(), + }, + ); + (auth_url, state) +} + +/// 取走一轮登录(回调进来时调用;取走即失效,重复回调拿不到第二次) +pub fn take_pending(state: &str) -> Option { + let mut table = lock_pending(); + sweep(&mut table); + table.remove(state) +} + +/// 丢弃一轮登录(取消 / 失败收尾) +pub fn drop_pending(state: &str) { + let mut table = lock_pending(); + table.remove(state); +} + +/// 从回调的 query 里取 `(code, state)`。 +/// +/// 门户可能带回额外参数(微信授权那条链会挂 `scope` 之类),所以按键找而不是按位置切。 +pub fn parse_callback(query: &str) -> Option<(String, String)> { + let mut code = String::new(); + let mut state = String::new(); + for pair in query.split('&') { + let (key, value) = pair.split_once('=')?; + let decoded = urldecode(value); + match key { + "code" => code = decoded, + "state" => state = decoded, + _ => {} + } + } + if code.is_empty() || state.is_empty() { + return None; + } + Some((code, state)) +} + +/// 极小的 query 解码(`+` 当空格,`%XX` 还原)。 +/// +/// 不引依赖:回调只有两三个参数,且值都是可见 ASCII 记号。 +fn urldecode(value: &str) -> String { + let bytes = value.as_bytes(); + let mut out: Vec = Vec::with_capacity(bytes.len()); + let mut index = 0; + while index < bytes.len() { + match bytes[index] { + b'+' => { + out.push(b' '); + index += 1; + } + b'%' if index + 2 < bytes.len() => { + let hex = std::str::from_utf8(&bytes[index + 1..index + 3]).unwrap_or(""); + match u8::from_str_radix(hex, 16) { + Ok(byte) => { + out.push(byte); + index += 3; + } + Err(_) => { + out.push(bytes[index]); + index += 1; + } + } + } + other => { + out.push(other); + index += 1; + } + } + } + String::from_utf8_lossy(&out).to_string() +} + +/// 一次令牌包的解析结果(换码与续期共用) +#[derive(Clone, Debug, Default)] +pub struct TokenBundle { + /// `accessToken` + pub access_token: String, + /// `refreshToken`(可能为空:续期响应不带新 token 时保留旧的) + pub refresh_token: String, + /// `expiresIn`(秒) + pub expires_in: i64, + /// 上游用户 id(`user.id`)—— 账号 id 与 keyfrom 都要它 + pub uid: String, + /// `user.userId` + pub user_id: String, + /// `user.nickname` + pub nickname: String, +} + +/// 解业务信封 `{code, msg, data}`。 +/// +/// `code == 0` 且 `data` **非 null** 才算成功 —— `data: null` 配 `code: 0` +/// 是「令牌子失效」的形态(参考实现实测),把它当成功会让上层拿空令牌去发请求。 +pub fn unwrap_envelope(payload: &Value, what: &str) -> Result { + let code = payload.get("code").and_then(Value::as_i64).unwrap_or(-1); + let message = payload + .get("msg") + .or_else(|| payload.get("message")) + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or(""); + if code != 0 { + let detail = if message.is_empty() { + format!("code={code}") + } else { + message.to_string() + }; + return Err(GatewayError::with_status( + 502, + format!("LobsterAI {what}失败:{detail}"), + )); + } + match payload.get("data") { + Some(Value::Null) | None => Err(GatewayError::with_status( + 401, + format!("LobsterAI {what}未返回数据(登录态可能已失效),请重新登录"), + )), + Some(data) => Ok(data.clone()), + } +} + +/// 从令牌信封的 `data` 里解出 [`TokenBundle`]。 +pub fn parse_token_bundle(data: &Value) -> Result { + let access_token = data + .get("accessToken") + .or_else(|| data.get("access_token")) + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or("") + .to_string(); + if access_token.is_empty() { + return Err(GatewayError::with_status( + 502, + "LobsterAI 换码响应缺少 accessToken", + )); + } + let user = data.get("user").cloned().unwrap_or(Value::Null); + let str_of = |value: &Value, key: &str| -> String { + value + .get(key) + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or("") + .to_string() + }; + Ok(TokenBundle { + access_token, + refresh_token: { + let token = str_of(data, "refreshToken"); + if token.is_empty() { + str_of(data, "refresh_token") + } else { + token + } + }, + expires_in: data + .get("expiresIn") + .and_then(Value::as_i64) + .filter(|value| *value > 0) + .unwrap_or(0), + uid: { + let id = str_of(&user, "id"); + if id.is_empty() { + str_of(&user, "yid") + } else { + id + } + }, + user_id: str_of(&user, "userId"), + nickname: str_of(&user, "nickname"), + }) +} + +/// 用授权码换令牌(**不带 Authorization**)。 +/// +/// 体固定五个字段:`authCode` + 两个 keyfrom + `uuid` + `version`。 +/// `uuid` 由调用方生成并**落盘**(续期要它)—— 它不是凭据,是这一台「客户端」的标识。 +pub async fn exchange( + auth_code: &str, + uuid: &str, + first_keyfrom: &str, + latest_keyfrom: &str, + client_version: &str, +) -> Result { + let body = json!({ + "authCode": auth_code, + "firstKeyfrom": first_keyfrom, + "latestKeyfrom": latest_keyfrom, + "uuid": uuid, + "version": client_version, + }); + let payload = post_anonymous(EXCHANGE_PATH, &body, "换码").await?; + let data = unwrap_envelope(&payload, "换码")?; + parse_token_bundle(&data) +} + +/// 续期(**不带 Authorization**):载荷件原样回传 + `refreshToken`。 +pub async fn refresh( + credentials: &LobsterCredentials, + client_version: &str, +) -> Result { + if !credentials.can_refresh() { + return Err(GatewayError::with_status( + 401, + "LobsterAI 账号缺少续期所需的载荷件(uuid / keyfrom 之一为空),请重新登录", + )); + } + let mut body = serde_json::Map::new(); + for (key, value) in credentials.keyfrom_fields(client_version) { + body.insert(key.to_string(), Value::String(value)); + } + body.insert( + "refreshToken".to_string(), + Value::String(credentials.refresh_token.clone()), + ); + let payload = post_anonymous(REFRESH_PATH, &Value::Object(body), "续期").await?; + let data = unwrap_envelope(&payload, "续期")?; + parse_token_bundle(&data) +} + +/// 发一次匿名 POST(换码与续期都不带 Authorization) +async fn post_anonymous(path: &str, body: &Value, what: &str) -> Result { + let url = format!("{}{}", api_base(), path); + let response = send_raw( + "POST", + &url, + Some(body), + &[ + ("accept".to_string(), "application/json".to_string()), + ("content-type".to_string(), "application/json".to_string()), + ("user-agent".to_string(), USER_AGENT.to_string()), + ], + None, + Some(REQUEST_TIMEOUT_MS), + ) + .await + .map_err(|error| { + if error.is_timeout() { + GatewayError::with_status(504, format!("LobsterAI {what}超时")) + } else { + GatewayError::with_status(502, format!("LobsterAI {what}请求失败: {error}")) + } + })?; + let payload = response.payload.ok_or_else(|| { + GatewayError::with_status(502, format!("LobsterAI {what}响应不是 JSON")) + })?; + // 401/403 直接归成登录态问题,别让信封里的文案把方向带偏 + if response.status == 401 || response.status == 403 { + return Err(GatewayError::with_status( + 401, + format!("LobsterAI {what}被拒(HTTP {}),请重新登录", response.status), + )); + } + if !response.ok { + return Err(GatewayError::with_status( + response.status as i32, + format!("LobsterAI {what}返回 HTTP {}", response.status), + )); + } + Ok(payload) +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + /// 授权地址:门户的 hash 路由 + 两个参数都要转义。 + #[test] + fn authorize_url_points_at_the_portal_hash_route() { + let url = build_authorize_url("http://127.0.0.1:3066/auth/callback", "abc123"); + assert!(url.starts_with(&format!("{}/portal#/login?", portal_base())), "{url}"); + assert!(url.contains("source=electron"), "{url}"); + assert!( + url.contains("redirect_uri=http%3A%2F%2F127.0.0.1%3A3066%2Fauth%2Fcallback"), + "{url}" + ); + assert!(url.contains("state=abc123"), "{url}"); + } + + /// 回调解析:按键找(门户可能带额外参数),两个都必须有。 + #[test] + fn callback_parsing_is_key_based() { + assert_eq!( + parse_callback("code=C1&state=S1"), + Some(("C1".to_string(), "S1".to_string())) + ); + // 顺序不同 + 多带参数 + assert_eq!( + parse_callback("scope=all&state=S1&code=C1&lang=zh"), + Some(("C1".to_string(), "S1".to_string())) + ); + // 少一个就不认(宁可报「回调不完整」,也不要拿半个参数去换码) + assert_eq!(parse_callback("code=C1"), None); + assert_eq!(parse_callback("state=S1"), None); + assert_eq!(parse_callback(""), None); + // 转义还原 + assert_eq!( + parse_callback("code=a%2Bb&state=s%20x"), + Some(("a+b".to_string(), "s x".to_string())) + ); + } + + /// 信封:`code != 0` 是失败;**`code == 0` 但 `data` 为 null 也是失败** + /// (令牌已失效的形态,最容易被漏成成功)。 + #[test] + fn envelope_rejects_both_non_zero_code_and_null_data() { + assert!(unwrap_envelope(&json!({"code": 0, "data": {"a": 1}}), "换码").is_ok()); + + let error = unwrap_envelope(&json!({"code": 7, "msg": "签名不对"}), "换码") + .expect_err("code!=0 应报错"); + assert!(error.message.contains("签名不对"), "{:?}", error.message); + + let error = unwrap_envelope(&json!({"code": 0, "data": null}), "续期") + .expect_err("data=null 应报错"); + assert_eq!(error.status_code, 401); + assert!(error.message.contains("重新登录")); + + // 缺 data 键同样按失效处理,不 panic + assert!(unwrap_envelope(&json!({"code": 0}), "续期").is_err()); + // 连 code 都没有 ⇒ 不当成功 + assert!(unwrap_envelope(&json!({"data": {}}), "续期").is_err()); + } + + /// 令牌包:缺 accessToken 报错;refreshToken 两种拼法都认;uid 退回 yid。 + #[test] + fn token_bundle_parsing_is_tolerant_but_requires_access_token() { + let data = json!({ + "accessToken": "at", "refreshToken": "rt", "expiresIn": 2592000, + "user": {"id": "u1", "userId": "42", "nickname": "虾"} + }); + let bundle = parse_token_bundle(&data).expect("应可解析"); + assert_eq!(bundle.access_token, "at"); + assert_eq!(bundle.refresh_token, "rt"); + assert_eq!(bundle.expires_in, 2_592_000); + assert_eq!(bundle.uid, "u1"); + assert_eq!(bundle.user_id, "42"); + assert_eq!(bundle.nickname, "虾"); + + // 没有 user.id 时退回 yid + let bundle = parse_token_bundle(&json!({"accessToken": "at", "user": {"yid": "y9"}})) + .expect("应可解析"); + assert_eq!(bundle.uid, "y9"); + assert!(bundle.refresh_token.is_empty()); + + assert!(parse_token_bundle(&json!({"refreshToken": "rt"})).is_err()); + } + + /// pending 表:取走即失效(重复回调拿不到第二次)。 + #[test] + fn pending_is_single_use() { + let (_url, state) = begin_login("http://127.0.0.1:1/auth/callback"); + assert!(take_pending(&state).is_some()); + assert!(take_pending(&state).is_none(), "同一 state 不该能被兑第二次"); + // 丢弃幂等 + drop_pending("never-existed"); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/usage.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/usage.rs new file mode 100644 index 00000000..38fca47f --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/usage.rs @@ -0,0 +1,388 @@ +//! LobsterAI 的余额读数:`GET /api/user/profile-summary` 的**积分**。 +//! +//! ── 为什么不用 `/api/user/quota`(一条实测教训)────────────────── +//! 那个端点只报 `freeCreditsTotal`(实测恒为 **300**),完全不含活动 / 签到 +//! 得到的积分;参考实现在同一账号上量到的真实余额是 **5297.72**,与 300 差一个 +//! 数量级(`credits.go:370-372` 的原话:「it deliberately uses profile-summary +//! rather than /api/user/quota, which only reports the free credit total and omits +//! activity credits」)。用错端点会让界面稳定显示一个偏小的数,且**不报错**。 +//! +//! ── 读什么 ────────────────────────────────────────────────── +//! ```text +//! data.totalCreditsRemaining 总剩余积分 +//! data.creditItems[] 逐份积分:{type, creditsRemaining, expiresAt} +//! ``` +//! 单位是**积分**。负数要夹到 0(计量回滚会让服务端报负数,显示「-12.5 积分」 +//! 没有意义,`credits.go:413-415`)。 +//! +//! ── 归一形状(与 loomy/balance.rs 同款,界面真正读的那一套)────── +//! ```text +//! { available, unit, wallets: [{type, displayName, balance, remaining, expiresAt}], raw } +//! ``` +//! ⚠️ 曾经有一家(灵犀)在这处自造了 `total_balance` / `currency` 两个键, +//! 结果账号页余额列**空白**而探针还全绿。所以这里**严格照抄**别家的键名,不自造。 +//! +//! ── 一条**必须**遵守的契约:读不到额度就不要写 `available` ────── +//! 限额判定走 `core::usage_records::extract_remaining`,规则是「`unlimited` +//! 优先,否则看 `totalLeft`,否则看 `available`」—— **`available` 一旦出现, +//! 就会被当成真实读数**。2026-10-17 在 atomcode 上栽过一次:没有读数时也写 +//! `available: 0`,于是「读不到额度」被限制器理解成「余额为 0」,账号当场被 +//! 标成「余额不足 / 已跳过」。所以这里只在**确实解析出读数**时才写 +//! `available`(`totalCreditsRemaining` 有值,或至少有一份明细可求和); +//! 什么都没有时只回 `wallets: []`,余额列留空。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use serde_json::{Value, json}; + +use crate::server::core::account_store::AccountStore; +use crate::server::core::auth_http::send_raw; +use crate::server::errors::GatewayError; +use crate::server::logging; + +use super::credentials::from_record; +use super::endpoints::{REQUEST_TIMEOUT_MS, USER_AGENT, profile_summary_url}; +use super::oauth::unwrap_envelope; + +/// 保留两位小数(服务端的浮点噪声如 `55.67000031` 在几份求和后会显形) +fn round_credits(value: f64) -> f64 { + (value * 100.0).round() / 100.0 +} + +/// 夹到非负(计量回滚会让服务端报负数) +fn clamp_credits(value: f64) -> f64 { + round_credits(value.max(0.0)) +} + +/// 读一个数字字段(数字或数字字符串都认) +fn read_number(value: Option<&Value>) -> Option { + let value = value?; + if let Some(number) = value.as_f64() { + return Some(number); + } + value.as_str().and_then(|text| text.trim().parse::().ok()) +} + +/// 一份积分明细(`creditItems[]` 的一条) +struct CreditItem { + kind: String, + remaining: f64, + expires_at: String, +} + +/// 取明细数组(非数组 / 条目不是对象一律跳过,不 panic) +fn credit_items(payload: &Value) -> Vec { + let Some(items) = payload.get("creditItems").and_then(Value::as_array) else { + return Vec::new(); + }; + items + .iter() + .filter_map(|item| { + let entry = item.as_object()?; + let kind = entry + .get("type") + .and_then(Value::as_str) + .map(str::trim) + .filter(|text| !text.is_empty()) + .unwrap_or("积分包") + .to_string(); + Some(CreditItem { + remaining: clamp_credits(read_number(entry.get("creditsRemaining")).unwrap_or(0.0)), + expires_at: entry + .get("expiresAt") + .and_then(Value::as_str) + .map(str::trim) + .unwrap_or("") + .to_string(), + kind, + }) + }) + .collect() +} + +/// 把 `profile-summary` 的 `data` 归成界面认的额度形状(**纯函数**,便于测试)。 +/// +/// 契约见模块头:读不到读数就**不写 `available`**。 +pub fn shape_usage(payload: &Value) -> Value { + let items = credit_items(payload); + let total = read_number(payload.get("totalCreditsRemaining")); + + // 诊断:把上游的真实字段形态记进**详细日志**(额度按字段名解析,名字一变就 + // 静默归零)。只记键名与明细,都不含凭据,且不进普通日志。 + logging::verbose( + "[LobsterAI]", + &format!( + "profile-summary 形态:顶层键={:?} creditItems={}", + payload + .as_object() + .map(|object| object.keys().cloned().collect::>()) + .unwrap_or_default(), + payload + .get("creditItems") + .map(|value| serde_json::to_string(value).unwrap_or_default()) + .map(|text| text.chars().take(400).collect::()) + .unwrap_or_else(|| "<缺>".to_string()), + ), + ); + + let wallets: Vec = items + .iter() + .map(|item| { + let mut wallet = json!({ + "type": "credit_package", + "displayName": item.kind, + "balance": item.remaining, + "remaining": item.remaining, + }); + if let Some(object) = wallet.as_object_mut() { + if !item.expires_at.is_empty() { + object.insert( + "expiresAt".to_string(), + Value::String(item.expires_at.clone()), + ); + } + } + wallet + }) + .collect(); + + // 读数优先级:`totalCreditsRemaining`(权威总量)→ 明细求和(仍是真实读数) + let available = total.map(clamp_credits).or_else(|| { + if items.is_empty() { + None + } else { + Some(clamp_credits(items.iter().map(|item| item.remaining).sum())) + } + }); + + // ── `credits[]`:界面「积分明细」那一列读的就是它 ────────────── + // 契约见 `ui-islands/src/islands/accounts-credit-detail.tsx` 的模块头: + // `{name, total/used/remain, grantAt, startAt, expireAt, unit?, daily?, grantReason?}`, + // **缺键 = 上游没给,不补 0**(把「没读到」写成 0 会画出「剩 0 / 共 0」这种 + // 看起来像读到了的假读数)。 + // + // 本家只报剩余量:上游的明细条目只有 `type` 与 `creditsRemaining` + // (Go 参考 `credits.go:387-410` 明确把 `Total`/`Used` 留成 0,理由是 + // 「上游没有周期总量/已用的概念」)。所以这里也只写 `name` 与 `remain`。 + // ⚠️ 到期那一栏(`expireAt`)**故意不写**:上游给的是**字符串**时刻, + // 而界面这一栏要的是**毫秒数**,字符串的格式我们没核实过 —— 宁可这一格留空, + // 也不要把一个日期串塞进毫秒字段(那会渲染成一个 1970 年的时刻)。 + // `wallets[].expiresAt` 是另一条路径(那是我们自己的形状,保留原文)。 + let credits: Vec = items + .iter() + .map(|item| { + json!({ + "name": if item.kind.trim().is_empty() { "积分包" } else { item.kind.as_str() }, + "remain": item.remaining, + }) + }) + .collect(); + + let mut shaped = json!({ + "unit": "积分", + "wallets": wallets, + "credits": credits, + "raw": payload, + }); + // 只有真有读数才回 `available`(见模块头:写成 0 会被限制器当成「余额为 0」) + if let Some(available) = available { + if let Some(object) = shaped.as_object_mut() { + object.insert("available".to_string(), Value::from(available)); + } + } + shaped +} + +/// 查余额:读记录 → 取 `profile-summary` → 解信封 → 归一。 +pub async fn query_usage( + store: &AccountStore, + account_id: &str, +) -> Result { + let record = store.lobsterai_account_record(account_id); + if !account_id.is_empty() && record.is_none() { + return Err(GatewayError::with_status( + 401, + format!("LobsterAI 账号 {account_id} 不存在(请重新添加)"), + )); + } + let credentials = from_record(record.as_ref())?; + + let headers: Vec<(String, String)> = vec![ + ( + "authorization".to_string(), + format!("Bearer {}", credentials.access_token), + ), + ("accept".to_string(), "application/json".to_string()), + ("user-agent".to_string(), USER_AGENT.to_string()), + ]; + let response = send_raw( + "GET", + &profile_summary_url(), + None, + &headers, + None, + Some(REQUEST_TIMEOUT_MS), + ) + .await + .map_err(|error| { + if error.is_timeout() { + GatewayError::with_status(504, "LobsterAI 积分查询超时") + } else { + GatewayError::with_status(502, format!("LobsterAI 积分查询失败: {error}")) + } + })?; + let payload = response.payload.ok_or_else(|| { + GatewayError::with_status(502, "LobsterAI 积分查询响应不是 JSON") + })?; + // 401/403 原样透出(调用方据此走「刷新后重试一次」的既有处置) + if response.status == 401 || response.status == 403 { + return Err(GatewayError::with_status( + 401, + format!("LobsterAI 积分查询被拒(HTTP {}),请重新登录", response.status), + )); + } + if !response.ok { + return Err(GatewayError::with_status( + response.status as i32, + format!("LobsterAI 积分查询返回 HTTP {}", response.status), + )); + } + // 信封:`code:0` 且 `data` 非 null 才算成功(`data:null` = 登录态失效) + let data = unwrap_envelope(&payload, "积分查询")?; + Ok(shape_usage(&data)) +} + +#[cfg(test)] +mod tests { + use super::*; + + /// **核心口径**:`totalCreditsRemaining` 是总数,单位是「积分」。 + #[test] + fn available_comes_from_total_credits_remaining() { + let shaped = shape_usage(&json!({ + "totalCreditsRemaining": 5297.72, + "creditItems": [ + {"type": "free", "creditsRemaining": 300.0, "expiresAt": "2026-12-31"}, + {"type": "activity", "creditsRemaining": 4997.72, "expiresAt": "2026-11-08"} + ] + })); + assert_eq!(shaped.get("available"), Some(&Value::from(5297.72))); + assert_eq!(shaped.get("unit"), Some(&Value::from("积分"))); + } + + /// 明细要画出来,逐条键名与别家一致。 + #[test] + fn credit_items_become_wallets() { + let shaped = shape_usage(&json!({ + "totalCreditsRemaining": 300.0, + "creditItems": [{"type": "free", "creditsRemaining": 300.0, "expiresAt": "2026-12-31"}] + })); + let wallets = shaped.get("wallets").and_then(Value::as_array).expect("wallets"); + assert_eq!(wallets.len(), 1); + for key in ["type", "displayName", "balance", "remaining"] { + assert!(wallets[0].get(key).is_some(), "wallet 缺键 {key}"); + } + assert_eq!(wallets[0].get("displayName").and_then(Value::as_str), Some("free")); + assert_eq!(wallets[0].get("remaining"), Some(&Value::from(300.0))); + assert_eq!( + wallets[0].get("expiresAt").and_then(Value::as_str), + Some("2026-12-31") + ); + // 自造键不得出现(它们正是上一家余额列空白的原因) + assert!(shaped.get("total_balance").is_none()); + assert!(shaped.get("currency").is_none()); + for key in ["available", "unit", "wallets", "raw"] { + assert!(shaped.get(key).is_some(), "缺键 {key}"); + } + } + + /// 二进制的浮点噪声要被抹平;负数夹到 0(计量回滚)。 + #[test] + fn credits_are_rounded_and_never_negative() { + let shaped = shape_usage(&json!({"totalCreditsRemaining": 55.67000031})); + assert_eq!(shaped.get("available"), Some(&Value::from(55.67))); + let negative = shape_usage(&json!({"totalCreditsRemaining": -12.5})); + assert_eq!(negative.get("available"), Some(&Value::from(0.0))); + } + + /// 没有 `totalCreditsRemaining` 但有明细时,求和仍是真实读数(不当成缺失去掉)。 + #[test] + fn missing_total_falls_back_to_the_item_sum() { + let shaped = shape_usage(&json!({ + "creditItems": [{"type": "a", "creditsRemaining": 1.5}, {"type": "b", "creditsRemaining": 2.25}] + })); + assert_eq!(shaped.get("available"), Some(&Value::from(3.75))); + } + + /// `credits[]` 是界面「积分明细」那一列的契约(`accounts-credit-detail.tsx`): + /// 它**必须存在且是数组**(前端靠 `Array.isArray(entry.credits)` 判断「这条 + /// 读数是当前形状」,缺了它每个会话都会白白重查一次),逐条只写我们确实读到的 + /// 字段(`name` + `remain`),**不补 0、不写未核实的到期格式**。 + #[test] + fn credits_rows_carry_only_what_was_actually_read() { + let shaped = shape_usage(&json!({ + "totalCreditsRemaining": 5297.72, + "creditItems": [ + {"type": "free", "creditsRemaining": 300.0, "expiresAt": "2026-12-31"}, + {"type": "", "creditsRemaining": 12.0} + ] + })); + let credits = shaped.get("credits").and_then(Value::as_array).cloned().unwrap_or_default(); + assert_eq!(credits.len(), 2, "两条明细都要画出来"); + assert_eq!(credits[0].get("name").and_then(Value::as_str), Some("free")); + assert_eq!(credits[0].get("remain"), Some(&Value::from(300.0))); + // 上游没给 type 时给一个可读的默认名,而不是空行 + assert_eq!(credits[1].get("name").and_then(Value::as_str), Some("积分包")); + // 未核实格式的字段一律不写(上游给的是字符串时刻,界面那一栏要毫秒数) + for row in &credits { + for key in ["expireAt", "total", "used", "grantAt"] { + assert!(row.get(key).is_none(), "{key} 不该被写出来:{row}"); + } + } + // 没明细时也要有 `credits`(空数组)——「这一轮上游确实没列包」 + let empty = shape_usage(&json!({"totalCreditsRemaining": 1.0})); + assert!(empty.get("credits").and_then(Value::as_array).is_some()); + assert!(empty.get("credits").and_then(Value::as_array).map(Vec::is_empty).unwrap_or(false)); + } + + /// **核心护栏**:什么读数都没有时**不写 `available`**(写 0 会被限额判定 + /// 当成「余额为 0」,账号当场被标成余额不足)。 + #[test] + fn no_reading_means_no_available_key() { + let shaped = shape_usage(&json!({})); + assert!( + shaped.get("available").is_none(), + "读不到读数时不得写 available" + ); + assert_eq!( + shaped.get("wallets").and_then(Value::as_array).map(Vec::len), + Some(0) + ); + assert_eq!(shaped.get("unit"), Some(&Value::from("积分"))); + // 只有一份空明细数组时同样不写(那不是读数) + let empty = shape_usage(&json!({"creditItems": []})); + assert!(empty.get("available").is_none()); + } + + /// 明细里的**数字字符串**也要认(上游形态不保证),坏条目跳过不 panic。 + #[test] + fn numeric_strings_are_accepted_and_bad_items_are_skipped() { + let shaped = shape_usage(&json!({ + "creditItems": ["not-an-object", {"type": "a", "creditsRemaining": "12.5"}, 42] + })); + let wallets = shaped.get("wallets").and_then(Value::as_array).expect("wallets"); + assert_eq!(wallets.len(), 1); + assert_eq!(wallets[0].get("remaining"), Some(&Value::from(12.5))); + } + + /// 缺 `type` 时给一个中性展示名(界面上不留空)。 + #[test] + fn wallet_without_a_type_gets_a_neutral_label() { + let shaped = shape_usage(&json!({"creditItems": [{"creditsRemaining": 5.0}]})); + assert_eq!( + shaped["wallets"][0].get("displayName").and_then(Value::as_str), + Some("积分包") + ); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/version.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/version.rs new file mode 100644 index 00000000..060f889e --- /dev/null +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/lobsterai/version.rs @@ -0,0 +1,183 @@ +//! 客户端版本解析:`X-LobsterAI-Client-Version` 要的是**日期式版本**(如 `2026.9.4`), +//! 不是插件自己的版本号。 +//! +//! ── 从哪来 ────────────────────────────────────────────────── +//! 上游的更新接口(`CLIENT_VERSION_API`,第三方主机)报当前桌面端版本。 +//! ⚠️ 它的响应**不是业务信封**:版本号在 `data.value`(Go 参考 +//! `lobsterai-product.ts:443` 的实测说明)。 +//! +//! ── 为什么要缓存 ──────────────────────────────────────────── +//! 版本在每个请求上都要用(换码 / 续期 / 目录 / 签到),而它一天也变不了几次; +//! 参考实现缓存 12 小时。取不到就退回已知可用的常量,**绝不发空版本头** —— +//! 空版本头会被服务端按「不认识的客户端」对待。 +//! +//! ── 硬约束 ────────────────────────────────────────────────── +//! release 是 `panic=abort`:本文件零 unwrap/expect/panic。 + +use std::sync::{Mutex, OnceLock}; + +use crate::server::core::auth_http::send_raw; +use crate::server::logging; + +use super::endpoints::{ + CLIENT_VERSION_API, FALLBACK_CLIENT_VERSION, REQUEST_TIMEOUT_MS, USER_AGENT, VERSION_CACHE_TTL_MS, +}; + +/// 进程级缓存的版本(`Some` 即已解析过;空串表示「刚查过但失败」,靠 TTL 过期) +struct Cache { + value: String, + fetched_at: i64, +} + +static CACHE: OnceLock>> = OnceLock::new(); + +fn cache() -> &'static Mutex> { + CACHE.get_or_init(|| Mutex::new(None)) +} + +/// 从更新接口的响应里取版本号。 +/// +/// ⚠️ **路径是 `data.value.version`,不是 `data.value`** —— `value` 是个**对象**, +/// 版本号在它的 `version` 成员里(Go 参考 `version.go:57-71` 的 +/// `parseClientVersionFromUpdateValue` 逐层 `asRecord`)。 +/// +/// 这一处我最初写成把 `data.value` 当标量读,而单测夹具又是照着自己的假设编的 +/// (于是测试全绿、代码全错,线上表现是版本头永远退化成兜底常量)。 +/// 现在夹具按 Go 的路径编,形状对不对由它守。 +/// +/// 单独抽出来是为了能脱网单测:这一段是上游契约,改错就是整家发错版本头。 +pub fn version_from_update_payload(payload: &serde_json::Value) -> Option { + let value = payload.get("data")?.get("value")?; + let version = value.get("version")?; + let text = match version { + serde_json::Value::String(text) => text.trim().to_string(), + other => other.to_string(), + }; + if text.is_empty() { + return None; + } + Some(text) +} + +/// 解析当前客户端版本(带 12 小时缓存;失败退回常量)。 +pub async fn resolve_client_version() -> String { + { + let guard = match cache().lock() { + Ok(guard) => guard, + Err(poisoned) => poisoned.into_inner(), + }; + if let Some(entry) = guard.as_ref() { + if logging::now_ms() - entry.fetched_at < VERSION_CACHE_TTL_MS && !entry.value.is_empty() + { + return entry.value.clone(); + } + } + } + + let response = send_raw( + "GET", + CLIENT_VERSION_API, + None, + &[ + ("accept".to_string(), "application/json".to_string()), + ("user-agent".to_string(), USER_AGENT.to_string()), + ], + None, + Some(REQUEST_TIMEOUT_MS), + ) + .await; + + let resolved = match response { + Ok(response) if response.ok => response + .payload + .as_ref() + .and_then(version_from_update_payload) + .unwrap_or_default(), + Ok(response) => { + logging::verbose( + "[LobsterAI]", + &format!("版本接口返回 HTTP {},改用兜底版本", response.status), + ); + String::new() + } + Err(error) => { + logging::verbose( + "[LobsterAI]", + &format!("版本接口不可达({error}),改用兜底版本"), + ); + String::new() + } + }; + + let mut guard = match cache().lock() { + Ok(guard) => guard, + Err(poisoned) => poisoned.into_inner(), + }; + *guard = Some(Cache { + value: resolved.clone(), + fetched_at: logging::now_ms(), + }); + if resolved.is_empty() { + FALLBACK_CLIENT_VERSION.to_string() + } else { + resolved + } +} + +/// 同步版的兜底取值:给「已经拿着版本、只是要修空串」的场景用。 +pub fn fallback_version() -> &'static str { + FALLBACK_CLIENT_VERSION +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + + /// 版本号在 `data.value.version`(**嵌套对象**,见 `version.go:57-71`)。 + /// 夹具按上游真实形状编,而不是按实现的假设编 —— 我第一版就是两边一起错、 + /// 测试还全绿。 + #[test] + fn version_is_read_from_data_value_version() { + assert_eq!( + version_from_update_payload(&json!({"data": {"value": {"version": "2026.9.4"}}})), + Some("2026.9.4".to_string()) + ); + assert_eq!( + version_from_update_payload(&json!({"data": {"value": {"version": " 2026.10.1 "}}})), + Some("2026.10.1".to_string()) + ); + // 数字形态(上游未必给字符串) + assert_eq!( + version_from_update_payload(&json!({"data": {"value": {"version": 20260904}}})), + Some("20260904".to_string()) + ); + } + + /// 形状不对时返回 None —— 调用方据此退回常量,**绝不发空版本头**。 + /// + /// 特别钉住「把 `data.value` 当标量」这个曾经的错法:那种形状必须**读不到**, + /// 否则改了路径也发现不了。 + #[test] + fn malformed_payload_yields_none_instead_of_a_wrong_version() { + assert_eq!(version_from_update_payload(&json!({})), None); + assert_eq!(version_from_update_payload(&json!({"data": {}})), None); + assert_eq!( + version_from_update_payload(&json!({"data": {"value": ""}})), + None, + "标量 value 是旧错法,必须读不到" + ); + assert_eq!( + version_from_update_payload(&json!({"data": {"value": "2026.9.4"}})), + None, + "标量 value 是旧错法,必须读不到" + ); + assert_eq!( + version_from_update_payload(&json!({"data": {"value": {"version": ""}}})), + None + ); + assert_eq!(version_from_update_payload(&json!({"value": {"version": "x"}})), None); + // 兜底常量本身永远可用 + assert!(!fallback_version().is_empty()); + } +} diff --git a/desktop-tauri/src-tauri/server/src/server/core/providers/mod.rs b/desktop-tauri/src-tauri/server/src/server/core/providers/mod.rs index 4da9adbf..eddca595 100644 --- a/desktop-tauri/src-tauri/server/src/server/core/providers/mod.rs +++ b/desktop-tauri/src-tauri/server/src/server/core/providers/mod.rs @@ -105,6 +105,12 @@ pub mod accio; /// wire 格式,不止 Gemini)全部已接通;`is_stateful` 恒 false, /// 见 `antigravity/mod.rs`)。 pub mod antigravity; +/// AtomCode(AtomGit / GitCode)。适配实现在 `atomcode/`:账号管理(粘贴令牌对 +/// 或后续的浏览器登录)+ **无状态 OpenAI 直通转发**(上游本身就是 Chat +/// Completions,`is_stateful` 恒 false)+ 目录(`coding-plan/models-v2`)+ +/// 额度(`status-v2` 的**每窗口调用次数**)+ 续期(`/oauth/refresh`,会轮换)。 +/// 三条平面与四个坑见 `atomcode/mod.rs` 的模块头。 +pub mod atomcode; pub mod autoclaw; pub mod catalog; /// 远程模型清单的**持久化缓存**(各家的清单在进程重启后由它读回,见模块头)。 @@ -138,6 +144,7 @@ pub mod content_block; /// 自定义家走独立通道」的两条路径。 pub mod custom; pub mod kuku; +pub mod lobsterai; pub mod loomy; /// MonkeyCode(长亭科技的开源 AI 开发平台)。适配实现在 `monkeycode/`: /// 账号管理(粘贴 session)+ 模型目录(`GET /api/v1/users/models`)+ @@ -500,6 +507,40 @@ pub enum ProviderKind { /// 智谱清言这一家只有 `chatglm.cn` 一个站点(测试域 `test.chatglm.cn` /// 是**环境**不是地区,靠 `CHATGLM_BASE_URL` 覆盖即可)。 Chatglm, + /// AtomCode(AtomGit / GitCode 的编码 Agent,`atomcode.atomgit.com`)。 + /// + /// ── 上游长什么样(官方 Rust CLI + 本地 Go 参考实现,见 `atomcode/` 各模块头)── + /// 三条平面:登录代理 `acs.atomgit.com`(浏览器 OAuth,**回调不回到本机**, + /// 靠轮询判完成)、CodingPlan REST `api.gitcode.com/api/v5`(目录 / 套餐 / + /// 领取 / 用量)、聊天网关 `api-ai.gitcode.com/v1`(**OpenAI Chat + /// Completions 原生**)。 + /// + /// ── 三个与别家不同的硬事实(每一条都能让整条链路静默失效)──────── + /// 1. 网关**刻意不用官方默认的 `llm-api.atomgit.com/v1`**:那个要求闭源 + /// 请求签名,而网关是**按 User-Agent** 决定要不要签名的(自称 + /// `atomcode/` 立刻 `403 ATOMCODE_SIG_MISSING`)。见 + /// `atomcode::endpoints` 的实测表。 + /// 2. 令牌**不是 JWT**:过期只能由 `created_at + expires_in` 推; + /// 续期会**轮换** refresh token(必须单飞且必须写回)。 + /// 3. 网关把失败包装成成功:`HTTP 200` + 正文 `参数错误`(或一页上游 HTML + /// 报错),见 `atomcode::errors`。 + Atomcode, + /// LobsterAI(网易有道「有道龙虾」,`lobsterai.youdao.com`)。适配实现在 + /// `lobsterai/`:账号管理(浏览器登录落于本机 loopback 回调)+ **无状态 + /// 转发** + 目录 + 积分余额 + 每日签到。 + /// + /// ── 上游长什么样(Go 参考 + TS 原版,见 `lobsterai/` 各模块头)── + /// 对话端点是 **OpenAI 兼容的原生协议**(`POST /api/proxy/v1/chat/completions`), + /// **只收 SSE**(`stream:false` 回 HTTP 500)—— 本网关的转发入口恒以 + /// `stream:true` 请求上游,这条天然满足。**没有任何签名**,纯 Bearer。 + /// + /// ── 三个载荷点 ──────────────────────────────────────────── + /// ① 两个 `X-LobsterAI-Client-*` 头按能力过滤:少了 `kimi-k3-agentic-v1` + /// 目录里少一个模型,少了 `thinking-level-control-v1` 发 + /// `reasoning_effort:"off"` 直接 500;② `uuid` 与两个 `keyfrom` 只在换码时 + /// 下发、续期必须原样回传(丢了账号永远续不了期);③ `developer` 角色上游 + /// 回 502,出站要降级成 `system`。 + LobsterAi, } /// 一个提供商的静态元数据。 @@ -602,6 +643,13 @@ pub const PROVIDERS: &[ProviderMeta] = &[ // 大面积同名 —— 靠末位让同名模型优先归已有各家(那些是开放平台账号, // 配额与稳定性都比本家的消费级网页端好)。 ProviderMeta { id: "chatglm", label: "智谱清言" }, + // AtomCode(AtomGit / GitCode):单一地区、单一入口(浏览器登录的界面侧 + // 尚未接,见 `atomcode/mod.rs` 的模块头),没有国际版伴生。排在 OrcaTerm + // 之后(后到居后,同样让同名模型优先归已有各家)。 + ProviderMeta { id: "atomcode", label: "AtomCode" }, + // LobsterAI(网易有道「有道龙虾」):单一入口(浏览器登录),没有地区伴生。 + // 排在末尾(后到居后)。 + ProviderMeta { id: "lobsterai", label: "LobsterAI" }, ]; /// provider id 在注册表里的下标(未知 id → None)。 @@ -687,6 +735,8 @@ pub fn kind_from_id(id: &str) -> Option { "officeace" => Some(ProviderKind::OfficeAce), "orcaterm" => Some(ProviderKind::OrcaTerm), "chatglm" => Some(ProviderKind::Chatglm), + "atomcode" => Some(ProviderKind::Atomcode), + "lobsterai" => Some(ProviderKind::LobsterAi), // 走到这里 = 上面的注册表判定已放行、这个 match 却没有对应分支: // 只可能是有人给 `PROVIDERS` 加了条目忘了加这里。开发期喊出来; // release 返回 None(见上:宁可为「未知」,不可误认成别家)。 @@ -730,6 +780,8 @@ pub const fn kind_id(kind: ProviderKind) -> &'static str { ProviderKind::OfficeAce => "officeace", ProviderKind::OrcaTerm => "orcaterm", ProviderKind::Chatglm => "chatglm", + ProviderKind::Atomcode => "atomcode", + ProviderKind::LobsterAi => "lobsterai", } } diff --git a/desktop-tauri/src-tauri/server/src/server/core/upstream/usage.rs b/desktop-tauri/src-tauri/server/src/server/core/upstream/usage.rs index 54e776ee..55abe937 100644 --- a/desktop-tauri/src-tauri/server/src/server/core/upstream/usage.rs +++ b/desktop-tauri/src-tauri/server/src/server/core/upstream/usage.rs @@ -437,6 +437,22 @@ pub struct AttemptDetail { /// 只记体积、不记内容:体积是运维事实,正文是用户数据。 #[serde(rename = "bodyBytes", default)] pub body_bytes: Option, + /// 这一轮**实际下发给客户端**的响应字节数(None = 这一轮没走到流式包装)。 + /// + /// 与上面的 `bodyBytes` 是**两个方向**,别混用:那个是出站请求体,这个是 + /// 入站响应体。分开的理由和「只记体积不记内容」同源 —— 10-17 生产实测里有 + /// 145 条「200 + 零 token + 无错误」的行,它们内部其实是两种病: + /// · 一个字节都没下发(客户端拿到空流) + /// · 内容下发了、但上游没给协议收尾帧 + /// 这两类的修法相反(后者合成收尾帧近乎纯收益,前者合成会把「上游没给内容」 + /// 洗成「模型答了个空」),而库里过去**完全分不出来** —— `request_raw` 自 + /// 3.0.4 起不再落库,下发字节没有第二个来源。 + /// + /// 计数挂在透传流上(`api::pipeline::RecordingStream`),与调试模式**无关**: + /// `RawCapture` 在开关关闭时直接丢弃字节,拿它的缓冲长度当体积会在关着调试时 + /// 恒为 0 —— 那正好是要区分的那一类。 + #[serde(rename = "forwardedBytes", default)] + pub forwarded_bytes: Option, } /// 一次**尝试内部**的退避重试(`AttemptDetail::retries` 的元素)。 @@ -886,6 +902,7 @@ impl RequestTelemetry { retries: Vec::new(), notice: None, body_bytes: None, + forwarded_bytes: None, }); // 阶段:新一轮的请求**即将发出** → 等待响应(含模型思考)。 // 位置与 OmniProxy 的「连接已建立、请求即将发出」同一时点:本函数就在 @@ -912,6 +929,23 @@ impl RequestTelemetry { } } + /// 给**最后一条**尝试明细补上「实际下发给客户端的响应字节数」。 + /// + /// 调用点只有一处:`api::pipeline::RecordingStream::settle`(流的收尾点)。 + /// 放在收尾而不是每帧,是因为这个数只在收尾时才定型,且写库是整条明细替换 —— + /// 逐帧回写会让每条 SSE 都多一次在途 UPDATE。 + /// + /// 语义上是**观测不是判定**:0 不代表失败(客户端可能在收到收尾帧后立刻断开, + /// 那时字节数为 0 只说明这一路没经过本类型),所以它不参与成功/失败判断, + /// 也不参与「失败清零 token」的归一。 + pub fn note_attempt_forwarded_bytes(&self, bytes: i64) { + let mut guard = self.lock(); + if let Some(last) = guard.attempts_detail.last_mut() { + last.forwarded_bytes = Some(bytes.max(0)); + self.flush_live(&guard); + } + } + /// 给**最后一条**尝试明细追加一次内部退避重试(`send_with_retry` 每退避 /// 一次调一次)。 /// diff --git a/desktop-tauri/src-tauri/server/src/server/http.rs b/desktop-tauri/src-tauri/server/src/server/http.rs index b1397f93..d111aa25 100644 --- a/desktop-tauri/src-tauri/server/src/server/http.rs +++ b/desktop-tauri/src-tauri/server/src/server/http.rs @@ -192,6 +192,20 @@ pub fn panel_router(state: ServerState) -> Router { .route( "/oauth-callback", get(api::session::login_antigravity_callback), + ) + // LobsterAI 网页登录的 loopback 回调:**浏览器 302 到这里**。 + // ⚠️ 这一条的路径**没有带家名后缀**,与上面几条自作主张的名字(如 + // `/auth/callback-accio`)风格不一致 —— 这是有意的:`redirect_uri` 是 + // 我们拼给上游门户的地址,门户按它回落,而门户是否对回调路径有白名单 + // **没有实测支撑**。参考实现用的就是裸 `/auth/callback`,这里跟随那条 + // 已验证的形状,不为了命名风格去赌一次未经验证的兼容性。 + // 该路径在本仓未被其它家占用(其余几家分别是 `-{vendor}` / `-accio` / + // `/oauth/callback` / `/oauth-callback`)。 + // 免鉴权的理由与上面几条同一句:调用方是用户的浏览器,它没有我们的 API Key; + // 安全性由本次登录任务的一次性 state 承担。 + .route( + "/auth/callback", + get(api::session::login_lobsterai_callback), ); // 需鉴权:Node 版对这些路径都调用了 checkApiKey diff --git a/desktop-tauri/src-tauri/server/src/server/mod.rs b/desktop-tauri/src-tauri/server/src/server/mod.rs index 394e3d89..abf02d78 100644 --- a/desktop-tauri/src-tauri/server/src/server/mod.rs +++ b/desktop-tauri/src-tauri/server/src/server/mod.rs @@ -304,6 +304,10 @@ impl ServerState { // `http://localhost:{port}/oauth-callback`(redirect_uri 由适配器在发起 // 登录时本地拼),端口必须在第一次发起之前就位(见该模块 `set_loopback_port`)。 crate::server::core::providers::antigravity::oauth::set_loopback_port(port); + // LobsterAI 的网页登录同理:授权地址里的 `redirect_uri` 要指向 + // `http://127.0.0.1:{port}/auth/callback`,而地址由适配器在发起时本地拼 + // (同步无参),端口必须先就位(见该模块 `set_loopback_port`)。 + crate::server::core::providers::lobsterai::oauth::set_loopback_port(port); let config_dir = config::config_dir(); // 与 Node 版一致:verbose 由环境变量 AGENT2API_VERBOSE=1 打开 // (旧名 WORKBUDDY_VERBOSE 仍可读,新名优先), diff --git a/desktop-tauri/src-tauri/server/src/server/request_stats/record.rs b/desktop-tauri/src-tauri/server/src/server/request_stats/record.rs index 878fedbf..f94816f8 100644 --- a/desktop-tauri/src-tauri/server/src/server/request_stats/record.rs +++ b/desktop-tauri/src-tauri/server/src/server/request_stats/record.rs @@ -373,6 +373,11 @@ pub struct AttemptDetail { /// 「只记体积不记内容」的口径见 `core::upstream::usage::AttemptDetail::body_bytes`。 #[serde(rename = "bodyBytes", default)] pub body_bytes: Option, + /// 这一轮实际下发给客户端的响应字节数(None = 没走到流式包装)。采集侧的 + /// 来由与「为什么不能用 `RawCapture` 的缓冲长度代替」见 + /// `core::upstream::usage::AttemptDetail::forwarded_bytes`。 + #[serde(rename = "forwardedBytes", default)] + pub forwarded_bytes: Option, } /// 一次尝试内部的退避重试(存储契约,与采集侧同形各自定义)。 @@ -622,6 +627,8 @@ impl NewRequestEntry { notice: item.notice.filter(|text| !text.is_empty()), // 体字节数原样透传(None = 这一轮没发出去;采集侧的理由见 usage 的字段说明) body_bytes: item.body_bytes, + // 同上,方向相反:这是下发给客户端的响应字节数 + forwarded_bytes: item.forwarded_bytes, }) .collect(), sensitive_hits: self diff --git a/desktop-tauri/src-tauri/server/src/web_shim.rs b/desktop-tauri/src-tauri/server/src/web_shim.rs index 23593d5c..1ece4e22 100644 --- a/desktop-tauri/src-tauri/server/src/web_shim.rs +++ b/desktop-tauri/src-tauri/server/src/web_shim.rs @@ -167,10 +167,18 @@ pub fn shim_js() -> &'static str { // Docker 远程面板里该地址属于浏览器所在电脑,不能自动回到容器, // 因此让用户把地址栏的最终 URL 粘回受保护接口。小浣熊单独改用 // 官方授权页支持的 redirect 分支,直接把 authorization_code 导回网关。 + // 自动路径的窗口期:先给「浏览器自己跳回网关」那条链一段时间,再决定要不要 + // 把粘贴框推给用户。见 runLoginFlow 里那段说明。 + var MANUAL_CALLBACK_GRACE_MS = 10000; + function needsManualCallback(provider) { return provider === 'trae' || provider === 'accio' || provider === 'accio-cn' - || provider === 'codearts' || provider === 'autoclaw-intl'; + || provider === 'codearts' || provider === 'autoclaw-intl' + // LobsterAI 同属这一型:授权页完成后 302 回 + // `http://127.0.0.1:<网关端口>/auth/callback`,在 Docker 面板里那个 + // 地址属于浏览器所在电脑、回不到容器,只能让用户把最终 URL 粘回来。 + || provider === 'lobsterai'; } function escapeHtml(value) { @@ -247,12 +255,21 @@ pub fn shim_js() -> &'static str { } async function waitForManualCallback(provider, state, authUrl) { + // 称号链必须与上面 `needsManualCallback` 的白名单**一一对应**:少一支, + // 那一家就会顶着别家的名字弹框(`lobsterai` 曾经落到 `'AutoClaw'`,用户看到 + // 的是「AutoClaw 需要粘贴回调地址」而手里登的却是有道龙虾)。 + // 末尾兜底用 provider id 本身,不用任何一家的名字 —— 名字错的代价是误导, + // 显示 id 最坏也只是不好看。 var label = provider === 'raccoon' ? '小浣熊' : provider === 'trae' ? 'Trae' : provider.indexOf('accio') === 0 ? 'Accio' - : provider === 'codearts' ? 'CodeArts' : 'AutoClaw'; - var callbackInstruction = '授权完成后,复制授权页浏览器地址栏中的完整地址,' - + '粘贴到下面提交。不要复制授权页原始地址,也不要改动参数。'; + : provider === 'codearts' ? 'CodeArts' + : provider === 'autoclaw-intl' ? 'AutoClaw 国际版' + : provider === 'lobsterai' ? 'LobsterAI(有道龙虾)' + : provider; + var callbackInstruction = '如果授权完成后账号没有自动出现,' + + '再复制授权页浏览器地址栏中的完整地址粘到下面提交;' + + '不要复制授权页原始地址,也不要改动参数。'; while (true) { var callbackUrl = await ensureOverlay( label + '需要粘贴回调地址', @@ -477,11 +494,23 @@ pub fn shim_js() -> &'static str { if (!second) showLinkFallback(authUrl); } if (needsManualCallback(provider)) { - // 同时保留自动轮询:同机部署仍会自动完成,远程 Docker 则由用户 - // 粘贴地址;先完成的一方结束流程,finally 会关闭残留覆盖层。 + // 先给自动路径一段窗口期,再上兜底 —— 不是一上来就把粘贴框推到用户脸上。 + // + // 这条链在「面板经回环地址访问」(桌面端、或远程机器上做了端口映射)时 + // 是**自己走得完**的:网关给的 `redirect_uri` 就是 + // `http://127.0.0.1:<网关端口>/…`,浏览器一跳就回来了。原来的写法把 + // `waitForManualCallback` 直接放进 `Promise.race`,弹框几乎与授权页 + // 同时出现,用户自然会去粘贴 —— 明明不需要(自动那条往往几秒就完成)。 + // + // 真回不来的远端场景(浏览器与网关不同机且没有端口映射)照样会弹, + // 只是晚 10 秒 —— 那时的代价只是多等一会儿,比「让人以为必须手工粘」轻。 + // 两边都保留:先完成的一方结束流程,finally 会关掉残留覆盖层。 return await Promise.race([ pollWait(started.state), - waitForManualCallback(provider, started.state, authUrl), + (async function () { + await sleep(MANUAL_CALLBACK_GRACE_MS); + return waitForManualCallback(provider, started.state, authUrl); + })(), ]); } return await pollWait(started.state); diff --git a/desktop-tauri/src-tauri/src/login.rs b/desktop-tauri/src-tauri/src/login.rs index 8ea5a675..3d9ac872 100644 --- a/desktop-tauri/src-tauri/src/login.rs +++ b/desktop-tauri/src-tauri/src/login.rs @@ -661,16 +661,28 @@ fn allowed_hosts(provider: &str) -> Option<&'static [&'static str]> { // 见 `providers::officeace::oauth` 的模块头),所以这里没有 CodeArts 那种 // loopback 回调要放行;但「不设限」仍是必须显式写出的选择(落进默认分支 // 会拿到 WorkBuddy 的白名单 → 窗口白屏)。 - // +// // OrcaTerm(腾讯云遨驰终端)同样不设限:登录窗口开在官方 OAuth 授权页 // (`orcaterm.com/oauth/authorize`),它会 302 到 `open.weixin.qq.com` // 的微信二维码(`qrconnect`),沿途还会拉 `res.wx.qq.com` 的样式与 // `localhost.weixin.qq.com` 的轮询 —— 主机不可穷举。**显式列出** // (落进默认分支会拿到 WorkBuddy 的白名单 → 授权页白屏,日志里什么也 // 看不出)。 + // + // AtomCode 同样不设限:授权页由代理给出(`acs.atomgit.com/auth/login` + // 换来的 `login_url`,实际落在 AtomGit 的账号站点上),之后会按用户选的 + // 登录方式继续跳不可穷举的主机。**不用回本机** —— 凭证由网关**轮询代理** + // 取回(`auth/check` → `auth/token`,见 `providers::atomcode::oauth` 的 + // 模块头),所以这里没有 loopback 回调要放行;但「不设限」仍是必须显式 + // 写出的选择。 + // LobsterAI 不设限,而且这一条**必须是 None**:它的授权页完成后会 + // **302 回本机**(`http://127.0.0.1:<网关端口>/auth/callback`),那次 + // 导航一旦被白名单拦下,症状就是用户明明授权成功、网关永远等不到码, + // 而日志里什么都看不出(与 CodeArts / Trae 的 loopback 回调同一情形)。 + // 授权页本身在 `lobsterai.youdao.com`,沿途还可能有微信登录的第三方主机。 "catpaw" | "qoder" | "qoder-intl" | "cline-free" | "cline-pass" | "autoclaw" | "autoclaw-intl" | "accio" | "accio-cn" | "zcode" | "zcode-intl" | "codearts" | "trae" - | "kuku" | "antigravity" | "officeace" | "orcaterm" => None, + | "kuku" | "antigravity" | "officeace" | "orcaterm" | "atomcode" | "lobsterai" => None, // WorkBuddy 的两个地区共用这一张表(表里 `workbuddy.ai` 那一行就是国际站 // 的登录域)。**显式列出**而不是靠下面的默认分支:上面那条警告要求 // 「新 provider 落到默认分支」必须是有意的选择,写出来才看得出是选过的 @@ -867,6 +879,14 @@ fn normalize_provider(provider: &str) -> Result<&'static str, String> { // `http://127.0.0.1:<网关端口>/oauth/callback` 落回网关自己,所以窗口 // **必须允许**导航到本机端口(见下面 allowed_hosts 的同一条)。 "codearts" => Ok("codearts"), + // LobsterAI(网易有道「有道龙虾」):授权地址由**后端适配器**现拼 + // (门户的 hash 路由 + `redirect_uri` 指向网关自己那个 loopback 端口, + // 见 `providers::lobsterai::oauth::build_authorize_url`),壳侧只负责 + // 开窗口与轮询 —— 与 CodeArts 同一条路。它的回调落在 + // `http://127.0.0.1:<网关端口>/auth/callback`(**裸路径**,理由见 + // `http.rs` 那条路由的注释),所以窗口**必须允许**导航到本机端口, + // 见下面 allowed_hosts 的同一条。 + "lobsterai" => Ok("lobsterai"), // Trae(SOLO CN):授权地址由**后端**问上游 guidance 拼(PKCE + 回调 URL), // 壳侧只负责开窗口与轮询 —— 与 ZCode 同一条路。特别地,它的回调落在 // `http://127.0.0.1:<随机端口>/authorize`(网关自己监听的那台 loopback, @@ -1216,6 +1236,7 @@ pub async fn start( // OfficeAce 只有一家(自助 OAuth 一条链,没有地区/版本概念),品牌名里 // 不需要地区 "officeace" => "OfficeAce 果办", + "lobsterai" => "LobsterAI", _ => "WorkBuddy", }; // 窗口标题:Cline 两家的池、ZCode 两家的地区、CodeArts / Trae / OfficeAce @@ -1224,7 +1245,7 @@ pub async fn start( let title = if matches!( provider, "cline-free" | "cline-pass" | "zcode" | "zcode-intl" | "codearts" | "trae" | "qoder" - | "qoder-intl" | "antigravity" | "officeace" + | "qoder-intl" | "antigravity" | "officeace" | "lobsterai" ) { format!("登录 {provider_label} 账号") } else { diff --git a/desktop-tauri/ui-islands/src/islands/accounts-domain.ts b/desktop-tauri/ui-islands/src/islands/accounts-domain.ts index 4b3b1e91..4bde2494 100644 --- a/desktop-tauri/ui-islands/src/islands/accounts-domain.ts +++ b/desktop-tauri/ui-islands/src/islands/accounts-domain.ts @@ -250,6 +250,34 @@ const PROVIDER_FEATURES: Record = { // `expiry: ''` —— 令牌有效期写在 JWT 的 `exp` 里、由网关自动续期,这一列没有 // 可显示的事实(与 antigravity / kuku 同一处置),界面不渲染「有效期」。 chatglm: { usage: true, creditDetail: true, edition: false, identifier: 'uid', expiry: '' }, + // AtomCode(AtomGit / GitCode)。各位各有出处: + // `usage: true` —— 额度是 `status-v2` 的**每窗口调用次数**(免费档实测 200 次 / + // 5 小时),已接 `query_usage`(`providers/atomcode/usage.rs`), + // 所以余额按钮与积分列都开。 + // `edition: false` —— 单一入口(没有国际版)。 + // `identifier: 'userId'` —— AtomGit 用户 id(落盘时写在 `userId` 字段)。 + // `expiry: 'expiresAt'` —— 令牌**不是 JWT**,过期时刻由 `createdAt + expiresIn` + // 推出后落盘(见 `providers/atomcode/credentials.rs` 的模块头)。 + atomcode: { usage: true, edition: false, identifier: 'userId', expiry: 'expiresAt' }, + // LobsterAI(网易有道「有道龙虾」)。各位各有出处: + // `usage: true` —— 余额是**积分**(`GET /api/user/profile-summary`,见后端 + // `providers::lobsterai::endpoints` 的 `PROFILE_SUMMARY_PATH`),所以余额按钮 + // 与积分列都开。 + // `creditDetail: true` —— 同一份 profile-summary 还带**逐份积分包**(参考实现的 + // `creditItems[]` → 面额 / 已用 / 剩余 / 到期,见 Go 参考 `credits.go` 的 + // `creditPackage`),正是 `credits[]` 那行的形状。与 ZCode 的纯比例窗口不同, + // 这里每份都有绝对值,能摊成逐行读数。⚠️ 这一位是**承诺**:后端 + // `query_usage` 不出 `credits` 时入口不挂(accounts-panels 的 + // `creditRows.length` 判据),所以现在开它是安全的;后端若不实现逐份清单, + // 把这一位删掉即可,界面自动退回单行读数。 + // `edition: false` —— 单一站点(`lobsterai.youdao.com`),没有国际版 / 地区之分, + // 所以不拼版本后缀(与 orcaterm / atomcode 同一处置)。 + // `identifier: 'userId'` —— 身份键取 `userId`:后端落账号时的身份口径是 + // `userId → uid → 令牌尾段`,账号 id 也由它拼成(`lobsterai-`, + // 见 `account_store::lobsterai_accounts`),列上与 id 的后缀对得上。 + // `expiry: 'expiresAt'` —— 落盘写的是毫秒 `expiresAt`(显式 > createdAt + + // expiresIn > JWT 的 `exp`),与 atomcode 同一条教训。 + lobsterai: { usage: true, creditDetail: true, edition: false, identifier: 'userId', expiry: 'expiresAt' }, } /** diff --git a/desktop-tauri/ui-islands/src/islands/add-account-configs.ts b/desktop-tauri/ui-islands/src/islands/add-account-configs.ts index 27e622b2..0827f5b3 100644 --- a/desktop-tauri/ui-islands/src/islands/add-account-configs.ts +++ b/desktop-tauri/ui-islands/src/islands/add-account-configs.ts @@ -883,6 +883,85 @@ const CHATGLM: ProviderConfig = { ], } +/** + * AtomCode(AtomGit / GitCode):把 AtomGit 的免费 CodingPlan 接进本网关。 + * + * ── 两条来源 ──────────────────────────────────────────────── + * ① **浏览器登录**(推荐):网关向代理现取授权地址 → 用户在浏览器里登 AtomGit + * → 网关**轮询代理**判完成并取回令牌对(授权回调由代理自己在 + * `acs.atomgit.com/callback` 终结,**不往本机跳**,所以壳侧不需要开回调监听)。 + * ② **粘贴令牌对**(后备):`accessToken` + `refreshToken`。⚠️ `refreshToken` + * 强烈建议填 —— 本家的令牌**不是 JWT**(实测 7 天到期,读不到 `exp`), + * 没有它账号无法自动续期,到期只能重新登录 + * (见 `providers/atomcode/credentials.rs` 的模块头)。 + * + * `desktop: false`:与 CodeArts / OfficeAce 同 —— 没有「导入桌面端登录态」这条 + * 后端路径(AtomCode 把令牌写在 `auth.toml` 里,且需要额外的机器指纹,不做这条入口)。 + */ +const ATOMCODE: ProviderConfig = { + provider: 'atomcode', + label: t('AtomCode'), + desktop: false, + webLogin: { + // 整段是一个 t() 键:扫描器只认字符串字面量(拼接串提不到键),所以不拆行 + noteHtml: t('打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。'), + button: t('打开授权页'), + busyText: t('等待 AtomCode 登录完成…'), + }, + manualTitle: t('粘贴令牌对'), + // 整段是一个 t() 键:扫描器只认字符串字面量(拼接串提不到键),所以不拆行 + manualNoteHtml: t('在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。'), + fields: [ + // 字段名与示例值不包 t()(各语言相同,与 CodeArts 的 JSON 示例同一处置) + { key: 'accessToken', label: t('访问令牌'), placeholder: 'access_token' }, + { key: 'refreshToken', label: t('续期令牌'), optional: true, placeholder: 'refresh_token' }, + { key: 'userId', label: t('用户 ID'), optional: true, placeholder: t('可选,用于生成账号标识') }, + { key: 'name', label: t('备注名'), optional: true, placeholder: t('可选,留空用「AtomCode」') }, + ], +} + +/** + * LobsterAI(网易有道「有道龙虾」`lobsterai.youdao.com`):一家一个 provider, + * 没有地区 / 额度池之分。 + * + * ── 只有一条添加路径:浏览器登录(授权码 + 本机 loopback 回调)────── + * 网关按 state 登记一轮登录并拼出授权地址,用户在门户授权(手机号 / 微信)后 + * 门户 302 回**网关自己的端口**:`http://127.0.0.1:<网关端口>/auth/callback`, + * 网关拿授权码换令牌后落账号(见后端 `providers::lobsterai::oauth` 与 + * `core::login::lobsterai`)。回调路径是裸 `/auth/callback`(不是 `-lobsterai` + * 后缀):那是参考实现已验证的形状,门户是否对路径有白名单没有实测支撑。 + * + * ── 没有「填写凭证」这条路径(后端如实拒绝,不是界面漏做)────────── + * 续期要**换码那一刻**由上游下发的三件载荷(`uuid` + 两个 `keyfrom`),粘贴 + * 给不出它们(见后端 `account_store::lobsterai_accounts` 的模块头)。所以后端 + * `api::accounts` 对 LobsterAI 的粘贴入口**始终回 400**,这里也就不提供手填段。 + * `fields` 留空是被 `ProviderConfig` 逼的(该键在类型里是必填),不是漏写: + * 本家唯一入口就是上面那条「网页登录」。 + * + * ── 没有「导入桌面端登录态」这条路径 ────────────────────────── + * 客户端登录态在它自己的存储里,没有 `data.bin` 那种稳定可读的明文形态 + * (与 Accio / ZCode 同一处境),所以显式 `desktop: false`。 + */ +const LOBSTERAI: ProviderConfig = { + provider: 'lobsterai', + label: t('LobsterAI'), + desktop: false, + // 授权页把浏览器送回**本机端口**(与 Accio 同一形态):同机自动完成, + // 跨机(Docker / 远程面板)到不了网关 —— 本家没有「粘贴最终回调地址」那条 + // 兜底,所以说明里只讲同机这一条前提,不承诺粘贴通道。 + webLogin: { + // 整段是一个 t() 键:扫描器只认字符串字面量(拼接串提不到键),所以不拆行 + noteHtml: t('打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。'), + button: t('打开 LobsterAI 登录页'), + busyText: t('等待 LobsterAI 登录完成…'), + modes: [ + { value: 'embedded', label: t('内嵌窗口(推荐)'), hint: t('将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待') }, + { value: 'external', label: t('系统浏览器'), hint: t('将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待') }, + ], + }, + fields: [], +} + /** 内置家的表单块,顺序与旧 ADD_FORMS 一致(只影响 DOM 里的块顺序,不影响界面) */ export const BUILTIN_CONFIGS: ProviderConfig[] = [ RACCOON, @@ -927,9 +1006,15 @@ export const BUILTIN_CONFIGS: ProviderConfig[] = [ // OrcaTerm(腾讯云遨驰终端桌面客户端):粘贴 accessToken / 导入桌面端登录态, // 排在末尾(与后端注册表 PROVIDERS 的排列一致,2026-10 接入) ORCATERM, - // 智谱清言(chatglm.cn):访客模式 / 粘贴令牌 / 导入本机登录态,排在末尾 +// 智谱清言(chatglm.cn):访客模式 / 粘贴令牌 / 导入本机登录态,排在末尾 // (与后端注册表 PROVIDERS 的排列一致,2026-10 接入) CHATGLM, + // AtomCode(AtomGit / GitCode):浏览器登录 + 粘贴令牌对,排在末尾 + // (与后端注册表 PROVIDERS 的排列一致,后到居后) + ATOMCODE, + // LobsterAI(网易有道「有道龙虾」):只有浏览器登录一条入口,排在末尾 + // (与后端注册表 PROVIDERS 的排列一致,后到居后) + LOBSTERAI, ] /** WorkBuddy 的块 id(结构特殊,单独一个组件) */ diff --git a/desktop-tauri/ui-islands/src/islands/add-provider-pick.tsx b/desktop-tauri/ui-islands/src/islands/add-provider-pick.tsx index e87d4670..8d61bcfb 100644 --- a/desktop-tauri/ui-islands/src/islands/add-provider-pick.tsx +++ b/desktop-tauri/ui-islands/src/islands/add-provider-pick.tsx @@ -99,10 +99,23 @@ export const PROVIDER_ICONS: Record = { // (`cloudcache.tencent-cloud.com/qcloud/orcaterm/pwa/icon/orcaterm_192x192.png`, // 与客户端 / 官网显示的应用图标为同一形象) orcaterm: 'assets/providers/orcaterm.png', - // 智谱清言:取自客户端安装包 `resources/app.asar` 内的应用图标 +// 智谱清言:取自客户端安装包 `resources/app.asar` 内的应用图标 // (`assets/icon.png`,512×512,与系统里显示的应用图标为同一形象; // 归一成 256×256 入库 —— 与 officeace 同一处置) chatglm: 'assets/providers/chatglm.png', + // AtomCode(AtomGit / GitCode):取自官网首页的产品图标 + // (`https://cdn-news.gitcode.com/news/atomcode-icon1.png`,524×524 RGBA, + // 归一成 256×256 入库 —— 与官网首页 / 客户端显示的应用图标为同一形象)。 + // ⚠️ 官网的 `` 指向的是 GitCode **平台**的 favicon + // (`cdn-static.gitcode.host/static/images/logo-favicon.png`),不是本产品的形象, + // 所以没有采用它。 + atomcode: 'assets/providers/atomcode.png', + // LobsterAI(网易有道「有道龙虾」):图标取自 sibling 仓库 cpa-jethub-plugins 里 + // 已核过的品牌图标(`internal/jethub/brandicons/brandicons.go` 的 `LobsterAI` + // 常量 —— 那正是产品站点从有道共享 CDN 引用的同一枚,不是有道自家的 favicon)。 + // 该处是 24×24 的 SVG,已栅格化成 256×256 RGBA 入库(与 orcaterm / officeace + // 同一处置)。 + lobsterai: 'assets/providers/lobsterai.png', } /** diff --git a/desktop-tauri/ui-islands/src/islands/checkin-page.tsx b/desktop-tauri/ui-islands/src/islands/checkin-page.tsx index c4459a1a..058065ab 100644 --- a/desktop-tauri/ui-islands/src/islands/checkin-page.tsx +++ b/desktop-tauri/ui-islands/src/islands/checkin-page.tsx @@ -62,6 +62,12 @@ const PROVIDER_DESC: Record = { // 访客账号没有积分体系,实现会如实跳过(不算失败),说明里点出这一点, // 免得用户以为漏签了。 chatglm: t('每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)'), + // AtomCode 的「签到」不是每日发额,而是**领取免费 CodingPlan**(一次性授权): + // 实现按需领取 —— 套餐有效时只回读额度、不重复领;到期或未领才走 Max→Pro→Lite。 + atomcode: t('领取免费 CodingPlan(按需:套餐有效时只回读额度)'), + // LobsterAI 的签到是上游的「每日活动」三步(查活动位 → 取上下文 → 领取), + // 幂等由客户端与上游的「今天已领」状态共同保证,重复执行只回中性提示。 + lobsterai: t('每日活动签到(+100 积分,已领则中性提示)'), } /** 页内标签页的档位(值与 checkin-state 的 CheckinTab 一致) */ diff --git a/desktop-tauri/ui-islands/src/islands/request-detail.tsx b/desktop-tauri/ui-islands/src/islands/request-detail.tsx index f53f6f43..5dccb8ce 100644 --- a/desktop-tauri/ui-islands/src/islands/request-detail.tsx +++ b/desktop-tauri/ui-islands/src/islands/request-detail.tsx @@ -78,6 +78,14 @@ type AttemptDetail = { notice?: unknown /** 这一轮实际发给上游的体字节数(缺 = 没发出去过)*/ bodyBytes?: unknown + /** + * 这一轮实际**下发给客户端**的响应字节数(缺 = 没走到流式包装)。 + * 与 `bodyBytes` 是两个方向,别混:那是出站请求体、这是入站响应体。 + * 它回答的是「客户端到底收到多少」—— 生产上「200 但零 token」那批里, + * 「一个字节都没下发(空流)」与「内容齐了但上游没收尾帧」两类修法相反, + * 靠这个数才分得开。 + */ + forwardedBytes?: unknown } /** 敏感词命中:词 + 次数 */ @@ -419,6 +427,22 @@ function attemptResult(item: AttemptDetail): React.ReactNode { return {t('无结果记录')} } +/** + * 字节数的人类可读形态。**只管正数**:非正数与缺省一律返回 `null`,由调用方 + * 决定显示什么 —— 两个方向的「0」含义相反(发出体积 0 = 压根没发出去,下发体积 + * 0 = 上游给了流但一个字节都没到客户端,即空流),共用一个「0 显示成 —」的 + * 规则会把后一类正好抹掉。 + * + * 只留这一份:两处各写一遍格式化,迟早分叉成分叉的表现是同一份数据显示成两个体积。 + */ +function formatBytes(raw: unknown): string | null { + const value = Number(raw) + if (!Number.isFinite(value) || value <= 0) return null + if (value >= 1024 * 1024) return `${(value / 1024 / 1024).toFixed(1)} MB` + if (value >= 1024) return `${Math.round(value / 1024)} KB` + return `${value} B` +} + /** * 尝试明细:每次上游尝试一行(提供商 / 账号 / 结果 / 内部重试 / 提示)。数据形状见 * 后端 AttemptDetail;明细比 attempts 短时如实说明(保头截断,列表弹层同一口径), @@ -432,7 +456,7 @@ function AttemptsTable({ row }: { row: RequestRow }) { - {[t('轮次'), t('提供商'), t('账号'), t('结果'), t('体积'), t('内部重试'), t('提示')].map(label => ( + {[t('轮次'), t('提供商'), t('账号'), t('结果'), t('体积'), t('已下发'), t('内部重试'), t('提示')].map(label => ( {label} ))} @@ -450,19 +474,26 @@ function AttemptsTable({ row }: { row: RequestRow }) { return `${String(retry?.reason || t('未知原因'))}${delay}` }).join('\n') const notice = String(item?.notice ?? '').trim() - // 体积:上游那两道墙都按请求体字节判(413 / PARSE_REQUEST_DATA_EXCEPTION), + // 发出体积:上游那两道墙都按请求体字节判(413 / PARSE_REQUEST_DATA_EXCEPTION), // 「这次到底多大」是排查它们时第一个要问的数,库里那份原文却在 128 KB 处截断。 const bodyRaw = Number(item?.bodyBytes) - const bodyText = Number.isFinite(bodyRaw) && bodyRaw > 0 - ? bodyRaw >= 1024 * 1024 - ? `${(bodyRaw / 1024 / 1024).toFixed(1)} MB` - : bodyRaw >= 1024 - ? `${Math.round(bodyRaw / 1024)} KB` - : `${bodyRaw} B` - : '—' + const bodyText = formatBytes(item?.bodyBytes) ?? '—' const bodyTitle = Number.isFinite(bodyRaw) && bodyRaw > 0 ? t('实际发给上游 {bytes} 字节', { bytes: bodyRaw.toLocaleString() }) : undefined + // 已下发体积:0 要**如实显示成 0**,不能退化成「—」—— 「上游给了流但 + // 一个字节都没下发」正是客户端报 incomplete_stream 的那一类,把它显示成 + // 没数据就等于把要抓的东西藏起来。缺省(undefined/null)才是不适用。 + const forwardedMissing = item?.forwardedBytes === undefined + || item?.forwardedBytes === null + const forwardedRaw = Number(item?.forwardedBytes) + const forwardedText = formatBytes(item?.forwardedBytes) + ?? (forwardedMissing ? '—' : '0 B') + const forwardedTitle = forwardedMissing + ? undefined + : t('实际下发给客户端 {bytes} 字节', { + bytes: (Number.isFinite(forwardedRaw) ? forwardedRaw : 0).toLocaleString(), + }) return ( {index + 1} @@ -474,6 +505,9 @@ function AttemptsTable({ row }: { row: RequestRow }) { {bodyText} + + {forwardedText} + {retries.length ? t('↻ {n} 次', { n: retries.length }) : '-'} @@ -483,7 +517,7 @@ function AttemptsTable({ row }: { row: RequestRow }) { })} {details.length < attempts ? ( - + {t('另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)', { missing: attempts - details.length, kept: details.length, })} diff --git a/desktop-tauri/ui/assets/providers/atomcode.png b/desktop-tauri/ui/assets/providers/atomcode.png new file mode 100644 index 00000000..2ef87076 Binary files /dev/null and b/desktop-tauri/ui/assets/providers/atomcode.png differ diff --git a/desktop-tauri/ui/assets/providers/lobsterai.png b/desktop-tauri/ui/assets/providers/lobsterai.png new file mode 100644 index 00000000..8e955926 Binary files /dev/null and b/desktop-tauri/ui/assets/providers/lobsterai.png differ diff --git a/desktop-tauri/ui/i18n/en/add-account.js b/desktop-tauri/ui/i18n/en/add-account.js index 248122c3..13e3344c 100644 --- a/desktop-tauri/ui/i18n/en/add-account.js +++ b/desktop-tauri/ui/i18n/en/add-account.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:en/add-account —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · 模型 {head}{rest}": " · Models: {head}{rest}", " 等 {n} 个": " ({n} total)", "1// 开头的 Google refresh token(也可直接粘贴 refresh_token=… 整行)": "Google refresh token starting with 1// (you can also paste the whole refresh_token=… line)", "11 位大陆手机号": "11-digit mainland China mobile number", "1~64 个字符,账号列表里按它分组显示": "1-64 characters; accounts are grouped by it in the account list", "6 位数字": "6 digits", "
  • 没有可自动领的活动
  • ": "
  • No campaigns can be claimed automatically
  • ", "

    领到的是套餐赠送积分,不会增加福利模型的 token 池;": "

    What you receive is plan gift points, which do not add to the welfare model token pool; ", "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "The Anthropic-compatible endpoint is https://api.deepseek.com/anthropic (fill it in when you switch the protocol to anthropic)", "Anthropic 协议填根地址,不要带 /v1": "For the Anthropic protocol, use the root URL without /v1", "Antigravity": "Antigravity", "AutoClaw 国内版": "AutoClaw China edition", "AutoClaw 国际版": "AutoClaw International edition", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS is required; paste the **full Cookie** (a Cookie editor can export it in one click) — the **PTOKEN must be kept**, as the gateway uses it to exchange for a KukuAI business session token from Baidu; without it, model refresh reports \"Not signed in\". A full Cookie header, a JSON array exported by a Cookie editor, or a {BDUSS, STOKEN} object are all supported. KukuAI has no refresh mechanism, so once the login state expires, just paste it again.", "BDUSS=…; STOKEN=…(或 Cookie 编辑器导出的 JSON)": "BDUSS=…; STOKEN=… (or the JSON exported by a Cookie editor)", "CatPaw 的 X-Passport-Token(登录态 Cookie)": "CatPaw's X-Passport-Token (login-state Cookie)", "Cline Pass 的 API Key 在 app.cline.bot 获取(订阅制);端点为 OpenAI 兼容,模型清单以 Cline 控制台为准,模型名在模型管理里按裸名添加(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客户端形态校验,所需的客户端请求头已随卡预置": "Get the Cline Pass API Key at app.cline.bot (subscription-based); the endpoint is OpenAI-compatible, the model catalog follows the Cline console, and model names are added under Model management with the plain name (e.g. glm-5.2 / kimi-k2.7-code / deepseek-v4-pro); the upstream validates the client shape, so the required client request headers are preset on this card", "Cloud-IDE-JWT(三段点分)": "Cloud-IDE-JWT (three dot-separated segments)", "Command Code": "Command Code", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT (three dot-separated parts; you can also paste a whole Bearer line or the data.bin content)", "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT (three dot-separated parts; a whole chatglm_token=… line works too)", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode International edition", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "NVIDIA NIM managed endpoint; apply for the Key at integrate.api.nvidia.com", "OfficeAce 果办": "OfficeAce", "accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)": "accessToken is Accio's login credential (a long opaque token, not a JWT); refreshToken is optional and enables automatic renewal on expiry. Enter the credential of the {label} account — {siteNote} (the easiest way to get it: just use \"Web login\" above; no need to hunt for the token manually.)", "accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
    手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
    没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。": "accessToken is Trae's Cloud-IDE-JWT (three dot-separated segments), and refreshToken enables automatic renewal on expiry (this provider rotates refreshToken on every use: once exchanged, the old one is void, so do not refresh the same account from two programs at once).
    When pasting manually, fill in machineId / deviceId as well: upstream ties them to the device public key uploaded at sign-in to identify the device, and swapping in a new pair from nowhere triggers the 2xxxx family of device-binding rejections.
    Without these three, the proper route is \"Web login\" above; for container / remote deployments, paste the callback URL from the address bar as prompted.", "monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)": "The value of monkeycode_ai_session (you can also paste the whole Cookie)", "refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。": "refreshToken is optional and enables automatic renewal on expiry; both can be obtained from the Raccoon client login-state file.", "refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。": "refreshToken is optional and enables automatic renewal; both can be obtained from the client login-state file. The same Cline account works in both pools; here it is grouped under {label}.", "token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。": "token supports the enc: prefix (decrypted automatically on Windows). Without a refreshToken, automatic renewal is not possible.", "token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。": "token is the login-state Cookie; CatPaw has no refresh mechanism, so sign in again in the client after it expires.", "user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)": "API Key starting with user_ (you can also paste the whole Authorization: Bearer … line)", "{label} 网页登录的打开方式": "How to open {label} web login", "{label} 账号地区": "{label} account region", "{label} 账号的添加方式": "How to add a {label} account", "{name} 不是合法 JSON:{reason}": "{name} is not valid JSON: {reason}", "{name}要粘一个 JSON 对象(形如 {sample})": "{name} requires a JSON object (like {sample})", "{n} 个账号": "{n} accounts", "{n} 个账号,点击添加": "{n} accounts, click to add", "{seconds}s 后重发": "Resend in {seconds}s", "✅ {label}账号已添加": "✅ {label} account added", "✅ {label}账号已添加{name}": "✅ {label} account added{name}", "✅ 已从 cc-switch 导入 {n} 个供应商": "✅ Imported {n} providers from cc-switch", "✅ 已创建自定义提供商「{name}」并添加账号": "✅ Created custom provider \"{name}\" and added an account", "✅ 已删除自定义提供商「{name}」{accounts}{keys}": "✅ Deleted custom provider \"{name}\"{accounts}{keys}", "✅ 已更新已有账号{name}(同一把 API Key 只保留一条,重复添加会合并)": "✅ Updated the existing account{name} (one account per API key; re-adding merges into it)", "✅ 登录成功,{edition}账号已加入列表": "✅ Signed in; {edition} account added to the list", "✅ 领取成功:{planId}{period}": "✅ Claim successful: {planId}{period}", " 同一家可以放多把 key,按优先级轮换。": " One provider can hold multiple keys, rotated by priority.", "「{name}」的账号添加功能还在开发中,敬请期待。": "Adding accounts for \"{name}\" is still in development — stay tuned.", "一个 Key 用遍多家模型,模型名用「厂商/模型」全称(如 deepseek/deepseek-chat)": "One Key works across many vendors; use the full \"vendor/model\" name (e.g. deepseek/deepseek-chat)", "上游不要鉴权时留空,并勾选下面一项(留空又不勾会被当成未配置凭证)": "Leave blank when the upstream needs no authentication, and check the option below (blank without the checkbox counts as no credential configured)", "上游信息": "Upstream info", "不可领({status})": "Not claimable ({status})", "不支持导入": "Import not supported", "不是有效的凭证文件:内容要是一个 JSON 对象": "Not a valid credential file: the content must be a JSON object", "不是有效的凭证文件:需要包含 accessToken 与 uid": "Not a valid credential file: accessToken and uid are required", "不是自定义提供商": "Not a custom provider", "不需要 Key:转发与拉取模型都不发送鉴权头": "No Key needed: neither forwarding nor model fetching sends auth headers", "不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

    注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。": "No sign-in needed: click the button below and the gateway will obtain a guest credential from Zhipu Qingyan directly and add it to the account list (you can fill in a note name first). The guest credential can be used for chat right away; the free quota is issued by the upstream per device.

    Note: a guest account has no points system — its member points, daily sign-in points and activity claim endpoints all require sign-in, so those capabilities are skipped for guests (no readings are shown on the accounts page or in the check-in center). To get points, use \"Fill in credentials\" to paste a signed-in account token, or \"Import desktop sign-in state\".", "两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。": "Both editions' accounts can be saved at once, each with its own model catalog (the China and International editions are two providers that can be enabled and mapped separately).", "中国版": "China edition", "今天没有需要领的活动(都已到账)": "No campaigns to claim today (all already credited)", "今天(北京时间 {day})已试 {attempts} 次,还能试 {left} 次{next}
    ": "Tried {attempts} times today (Beijing time {day}); {left} attempts left{next}
    ", "从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。": "Grab three items from another machine (or from the OfficeAce desktop app's models.json) and paste them here: the model gateway base URL (baseUrl, shaped like https://modelgw-…/v2) plus modelAppKey / modelAppSecret. These three are enough to forward requests (the gateway credential never expires); the control-plane temporary credentials (accessKeyId and so on) are optional — leaving them blank disables balance queries and auto-renewal but does not affect forwarding.", "从本机导入桌面端登录态": "Import desktop login state from this machine", "任务镜像 id": "Task image id", "余额查询": "Balance query", "使用 Google 账号登录": "Sign in with Google", "使用 Zai 账号登录": "Sign in with Zai", "使用个人访问令牌(PAT)": "Use a personal access token (PAT)", "免费模型不用填 Key(默认勾上「该上游无需鉴权」,网关自动用匿名凭证);付费模型则填 Zen 的 API Key。免费档上游只认官方客户端形态,已默认开启「伪装 OpenCode 官方客户端」": "Free models need no Key (\"This upstream needs no authentication\" is checked by default, and the gateway uses anonymous credentials automatically); for paid models, enter your Zen API Key. The free tier only accepts the official client form, so \"Emulate the official OpenCode client\" is on by default", "内嵌窗口": "Embedded window", "内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)": "Opens in the embedded window; closing it cancels the wait (if a Google account is rejected, switch to the system browser)", "内嵌窗口打开;完成后自动加入列表,关窗即取消等待": "Opens in the embedded window; added to the list when done; closing it cancels the wait", "内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待": "Opens in an embedded window; the account is added automatically once you have scanned — closing the window cancels the wait", "内嵌窗口用全新环境,多账号互不影响;关窗即取消等待": "The embedded window uses a fresh environment, so multiple accounts do not affect each other; closing it cancels the wait", "内嵌窗口(推荐)": "Embedded window (recommended)", "准备验证…": "Preparing verification…", "凭据 JSON": "Credential JSON", "凭证文件在哪?": "Where is the credential file?", "创建并添加账号": "Create and add account", "创建这个提供商,并同时建立它的第一个账号。": "Create this provider and its first account in one step.", "删除功能不可用(脚本未就绪)": "Delete is unavailable (script not ready)", "删除失败:{reason}": "Delete failed: {reason}", "删除此提供商": "Delete this provider", "及 {n} 个账号": " and {n} accounts", "发送中…": "Sending…", "发送失败:{message}": "Send failed: {message}", "发送未确认:登录窗口已弹出,请在弹出的窗口中手动完成登录": "Send unconfirmed: the sign-in window has opened; complete the sign-in manually in that window", "取消失败:{message}": "Cancel failed: {message}", "取消等待": "Cancel sign-in", "只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页": "Only the Embedded window can restore third-party options: we cannot modify the sign-in page in the system browser", "可选": "Optional", "可选(cloudaicompanionProject):留空由网关自动发现": "Optional (cloudaicompanionProject); if blank, the gateway discovers it automatically", "可选,展示用": "Optional; for display", "可选,没有则无法自动续期": "Optional; without it, automatic renewal is not possible", "可选,留空使用凭据里的 user_name": "Optional; defaults to the user_name in the credential", "可选,留空使用账号姓名、邮箱或令牌指纹": "Optional; defaults to the account name, email, or token fingerprint", "可选,留空使用账号昵称或邮箱": "Optional; defaults to the account nickname or email", "可选,留空则用 key 尾号": "Optional; defaults to the key suffix", "可选,留空则用 userId": "Optional; defaults to userId", "可选,留空则用上游返回的账号名或账号 id": "Optional; defaults to the account name or account id returned by upstream", "可选,留空则用凭证里的账号名": "Optional; defaults to the account name in the credential", "可选,留空则用文件里的昵称": "Optional; leave empty to use the nickname from the file", "可选,留空则用登录名或 uid": "Optional; defaults to the login name or uid", "可选,留空则用脱敏手机号": "Optional; defaults to the masked phone number", "可选,留空则用脱敏手机号或 userId": "Optional; defaults to the masked phone number or userId", "可选,留空则用账号 uk": "Optional; defaults to the account uk", "可选,留空则用账号昵称": "Optional; defaults to the account nickname", "可选,留空则用账号昵称或 userId": "Optional; leave blank to use the account nickname or userId", "可选,留空则用邮箱或令牌尾号": "Optional; defaults to the email or token suffix", "可选,留空用「OfficeAce 果办」": "Optional; defaults to “OfficeAce”", "可选,留空自动生成": "Optional; generated automatically if blank", "可选,留空自动用昵称": "Optional; defaults to the nickname", "可选,给这个访客账号起个备注名": "Optional; a note name for this guest account", "可选,续期时带上": "Optional; sent when renewing", "可选,讯飞侧 userid": "Optional; the userid on the iFlytek side", "可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补": "Optional; required to start a conversation. If blank, the gateway discovers it automatically from this account's existing tasks; if it cannot find one, fill it in manually later.", "可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)": "Optional; the account email, used for display and to recognize the identity (the same account is recognized even after switching to a new token)", "可选;UUID 形态,与凭据同生共死": "Optional; UUID form, lives and dies with the credential", "可选;填了才能到期自动续期": "Optional; required for automatic renewal on expiry", "可选;用于去重与展示名(留空时网关会问一次上游)": "Optional; used for deduplication and the display name (when blank, the gateway asks upstream once)", "可选;用于生成账号 id 与展示名": "Optional; used to generate the account id and display name", "可选;纯数字串(上游只收 8–24 位数字)": "Optional; digits only (upstream accepts only 8–24 digits)", "可领取": "Claimable", "同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)": "Keyed and no-auth accounts can coexist in one provider, each routed by its own rules; multiple no-auth accounts are allowed too (each with its own egress proxy)", "同地址换 anthropic 协议即走它的 Claude 兼容端点。转发必须填 OpenCode Go 的 API Key(订阅制;获取模型清单不需要 Key,但发请求上游会回 401「Missing API key」)": "Switch the protocol to anthropic on the same address to use its Claude-compatible endpoint. Forwarding requires an OpenCode Go API Key (subscription-based; fetching the model catalog needs no Key, but requests get a 401 \"Missing API key\" from upstream)", "响应为空": "Empty response", "商汤日日新的 OpenAI 兼容端点;余额查询走控制台账号密码(见下方「余额查询」一段)": "SenseNova's OpenAI-compatible endpoint; balance query uses the console account and password (see \"Balance query\" below)", "国内版": "China edition", "国内版与**国际版是两套独立的账号与套餐**,凭证与领取的套餐都不通用。": "The China edition and **the International edition are two separate sets of accounts and plans**; credentials and claimed plans do not carry over between them.", "国内版登录页没有 Google / GitHub 入口(它用微信 / 手机号 / 邮箱登录)": "The China edition's sign-in page has no Google / GitHub options (it uses WeChat / phone / email sign-in)", "国内版的登录站点是 www.accio-ai.com,与国际版不是同一站。": "The China edition's sign-in site is www.accio-ai.com, a different site from the International edition's.", "国内版(WorkBuddy)": "China edition (WorkBuddy)", "国内站为 https://api.siliconflow.cn/v1": "China site: https://api.siliconflow.cn/v1", "国内站(minimaxi.com);OpenAI 兼容地址为 https://api.minimaxi.com/v1": "China site (minimaxi.com); the OpenAI-compatible address is https://api.minimaxi.com/v1", "国际版": "International edition", "国际版 z.ai 的 Coding 套餐;OpenAI 兼容地址为 https://api.z.ai/api/coding/paas/v4": "The International edition z.ai Coding plan; the OpenAI-compatible address is https://api.z.ai/api/coding/paas/v4", "国际版与国内版是**两套独立的账号**(同一账号体系的两个站点),凭证不通用。": "The International and China editions are **two separate sets of accounts** (two sites of the same account system); credentials do not carry over.", "国际版与国内版账号体系独立,请填国际版的凭证。未填 refreshToken 无法自动续期。": "The International and China editions have separate account systems; enter the International edition's credentials. Without a refreshToken, automatic renewal is not possible.", "国际版登录页默认只显示邮箱登录,勾选后恢复 Google / GitHub / X 入口": "The International edition's sign-in page shows only email sign-in by default; checking this restores the Google / GitHub / X options", "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "The International edition's inference site is api.z.ai, a different site from the China edition's; plans are separate as well.", "国际版(WorkBuddy AI)": "International edition (WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "International site Coding plan; the OpenAI-compatible address is https://api.minimax.io/v1", "在 cc-switch 里找到": "Found", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "Complete the official sign-in in the embedded window; the account is added to the list automatically on success.", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "Generate a PAT in the Qoder account settings → Integrations for {label} (do not use a Google / GitHub token). Accounts and credentials do not carry over between the two regions.", "在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。": "Scan the WeChat QR code on the official authorization page that opens (the QR code appears right away). The account is added to the list automatically after a successful scan.", "在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "In the window that opens, sign in to the KukuAI main site with your Baidu account (SMS code or QR scan both work); the account is added to the list automatically on success. The gateway exchanges it with Baidu for a KukuAI business session token (requires the KukuAI client installed on this machine).", "在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "Sign in with your Google account in the window that opens (Antigravity's authorization page); the account is added to the list automatically once authorization completes.", "在浏览器中打开登录页": "Open the sign-in page in your browser", "地区": "Region", "填写凭证": "Enter credentials", "填写凭证添加": "Enter credentials to add", "备注名": "Label", "套餐": "Plan", "如:智谱 GLM": "e.g. Zhipu GLM", "完成登录后自动加入账号列表,昵称取自上游。": "Added to the account list automatically after sign-in; the nickname comes from upstream.", "官方客户端凭证目录:Windows 是 C:\\Users\\<你的用户名>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth,macOS 是 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth": "Official client credential directory: Windows — C:\\Users\\\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth; macOS — ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth", "官方已确认到账": "Officially confirmed as credited", "导入": "Import", "导入中…": "Importing…", "导入凭证文件": "Import credential file", "导入失败": "Import failed", "导入失败:{reason}": "Import failed: {reason}", "导入所选": "Import selected", "导入所选({n})": "Import selected ({n})", "导入桌面端登录态": "Import desktop login state", "将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下": "The embedded window will open; the account is added to the list automatically once authorization completes. Closing the window cancels the wait. The authorization page may end with a message that it cannot open the zcode:// link — that is normal; the result has already been recorded server-side", "将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)": "The embedded window will open; the account is added to the list automatically once authorization completes. On a remote panel you can paste the callback URL. Closing the window cancels the wait. The link is valid for 5 minutes; if it times out or is not clicked, it is voided (you can start again)", "将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待": "The embedded window will open; the account is added to the list automatically after sign-in. Closing the window cancels the wait", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 Trae 账号);同机完成后自动加入,Docker / 远程面板按提示粘贴地址栏回调地址。": "The authorization page will open in your system default browser (reusing the Trae account already signed in there); on the same machine the account is added automatically when done. For Docker / remote panels, paste the callback URL from the address bar as prompted.", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 ZCode 账号);授权完成后自动加入账号列表。页面最后可能提示无法打开 zcode:// 链接,属正常现象": "The authorization page will open in your system default browser (reusing the ZCode account already signed in there); the account is added to the list automatically once authorization completes. A final message that the zcode:// link cannot be opened is normal", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "The authorization page will open in your system default browser (reusing the Huawei Cloud account already signed in there); the gateway polls the cloud for the authorization code, so it does not matter which machine the browser runs on. The account is added to the list automatically once done", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "The authorization page will open in your system default browser (reusing the Huawei Cloud account already signed in there); when the browser and the gateway are not on the same machine, paste the final callback URL back into the panel as described above", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "The sign-in page will open in your system default browser (reusing the Accio account already signed in there); the account is added to the list automatically after sign-in, and closing the popup cancels the wait", "小米 MiMo Token Plan 套餐(新加坡站)": "Xiaomi MiMo Token Plan (Singapore site)", "已到账": "Credited", "已取消登录等待": "Pending sign-in canceled", "已取消领取": "Claim canceled", "已取消验证码": "Verification canceled", "已填写手机号,正在发送验证码…": "Phone number entered; sending the code…", "已声明无需鉴权:账号会被正常选路,出网时不带任何鉴权头": "Marked as needing no authentication: the account is routed normally, and no auth headers are sent outbound", "已导入 {ok} 个,失败 {fail} 个(失败项已标注在列表里)": "Imported {ok}, failed {fail} (failed items are marked in the list)", "已建过 · {n} 个账号,点击继续添加": "Already created · {n} accounts, click to add more", "已建过,点击继续添加账号": "Already created, click to add another account", "已打开官方登录页,请在窗口中完成登录…": "Official sign-in page opened; complete sign-in in the window…", "已点发送,等待短信…": "Sent; waiting for the SMS…", "已用系统默认浏览器打开登录页,请在浏览器中完成登录…": "Sign-in page opened in your system default browser; complete sign-in in the browser…", "已领取,等官方确认": "Claimed, awaiting official confirmation", "当前壳版本不支持 AutoClaw 网页登录,请更新应用": "This app version does not support AutoClaw web login; update the app", "当前没有可领取的套餐(活动尚未开始)": "No plans are available to claim right now (the event has not started yet)", "当前环境不支持领取(桥接方法缺失)": "Claiming is not supported in this environment (bridge method missing)", "必填,CatPaw 账号标识": "Required; CatPaw account identifier", "恢复 Google / GitHub 入口": "Restore Google / GitHub options", "手机号": "Phone number", "手机验证码登录": "SMS code login", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Open the official authorization page for Trae SOLO (trae.cn) and sign in with your Trae account: once authorized, the official page redirects back to a temporary port on this machine; a same-machine deployment receives it automatically, while Docker / remote panels should copy the callback URL from the address bar as prompted.", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Open the official sign-in page for Accio {label} ({site}) and sign in with your Accio account: on success the official page redirects back to this machine; a same-machine setup completes automatically, while Docker / remote panels just paste the callback URL as prompted (the authorization code is passed to the gateway only on this machine, and no plaintext token is shown in the UI).", "打开 Accio {label}登录页": "Open the Accio {label} sign-in page", "打开 Antigravity 网页登录": "Open Antigravity web login", "打开 CatPaw 网页登录": "Open CatPaw web login", "打开 Cline 授权页": "Open Cline authorization page", "打开 CodeArts 授权页": "Open CodeArts authorization page", "打开 Qoder 网页登录": "Open Qoder web login", "打开 Trae 授权页": "Open Trae authorization page", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "Open the official authorization page for ZCode {label} and sign in with your account. The authorization result is recorded by the ZCode server; the gateway fetches the credential in the background and adds the account to the list.", "打开 ZCode {label}授权页": "Open the ZCode {label} authorization page", "打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
    网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。": "Open the official Huawei Cloud CodeArts authorization page to sign in: once done, the official page brings the browser back to the gateway's own /oauth/callback, and the gateway exchanges the one-time authorization code for temporary credentials and adds the account to the list.
    When the gateway runs on another machine, the web UI prompts you to paste the final callback URL from the address bar back into the panel.", "打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。": "Open the Huawei Cloud authorization page and sign in: once finished, the gateway automatically polls the cloud for the authorization code and adds the account to the list (no OfficeAce desktop app required, and no need to paste a callback URL back manually).", "打开官方授权页完成设备码授权,登录的是{label}({site})的账号。": "Open the official authorization page and complete the device-code authorization; you will be signing in to a {label} ({site}) account.", "打开授权页": "Open the authorization page", "打开授权页完成确认,账号自动加入 {label}{note}": "Open the authorization page and confirm; the account is added to {label} automatically{note}", "打开方式": "Open with", "打开网页登录": "Open web login", "探测失败:{message}": "Probe failed: {message}", "接入一个兼容上游": "Connect a compatible upstream", "提交中…": "Submitting…", "搜索提供商": "Search providers", "搜索提供商…": "Search providers…", "整份粘贴官方插件 / CLIProxyAPI 落盘的凭据 JSON(形如 {\"codearts_provider_credential\":{…}},铺平的也行)。
    必填:access_key_id、secret_access_key、security_token。要能自动续期,必须连 refresh_token 与 oauth_context 一起粘 —— 临时凭据约一小时到期,缺这半块就续不回来,只能重新登录。": "Paste the whole credential JSON written to disk by the official plugin / CLIProxyAPI (like {\"codearts_provider_credential\":{…}}; a flattened form also works).
    Required: access_key_id, secret_access_key, security_token.For automatic renewal you must paste refresh_token and oauth_context as well — temporary credentials expire in about an hour, and without that half they cannot be renewed; you would have to sign in again.", "文件是{edition}凭证,已按该版本导入": "The file contains {edition} credentials and has been imported as that edition", "新建自定义提供商": "New custom provider", "方案:{label}": "Scheme: {label}", "明文 JWT 或 auth.json 里的 enc: 加密值(自动解密)": "Plaintext JWT, or the enc: encrypted value in auth.json (decrypted automatically)", "明文 JWT(国际版账号的 access token)": "Plaintext JWT (the access token of an International edition account)", "智谱 bigmodel.cn 的 Coding 套餐,Key 在 open.bigmodel.cn/usercenter/apikeys": "Zhipu's bigmodel.cn Coding plan; get the Key at open.bigmodel.cn/usercenter/apikeys", "未知错误": "Unknown error", "未能获取授权地址,请重试": "Could not get the authorization URL; try again", "未能读取 cc-switch 数据": "Could not read cc-switch data", "未领取成功": "Claim unsuccessful", "本地 Ollama、OpenCode Zen 免费档这类上游要勾上,否则账号不可用": "Check this for upstreams like local Ollama or the OpenCode Zen free tier; otherwise the account cannot be used", "本家有两个**互不替代**的凭证,按你要用的功能填,至少填一个:编码套餐 API Key 用于转发推理(打 {site})与查询窗口限额(每 N 小时 / 每周的剩余比例);jwt 用于领取套餐与查询余额(打 zcode.z.ai 的 billing 网关,官方叫 Coding Plan JWT,是一串三段点分的字符串)。余额两把都能查、但读到的东西不同:jwt 读到余额桶与套餐到期,API Key 读到窗口限额;而领取只认 jwt、转发只认 API Key。请填写 {label}账号的凭证 —— {plan}(最容易拿到的办法:直接用上方的「网页登录」—— 它会替你把这个 API Key 换好,两个凭证一起拿到。)": "This provider has two credentials that **do not replace each other**; fill in the one(s) for the features you need, at least one: the Coding Plan API Key is used for forwarding inference (hits {site}) and for querying the window limits (the remaining percentage of each N-hour / weekly window); the jwt is used for claiming plans and querying balance (hits the billing gateway at zcode.z.ai; officially called a Coding Plan JWT, a three-segment dot-separated string). Either one can query balance, but they read different things: the jwt reads the balance buckets and the plan expiry, while the API Key reads the window limits; and claiming only accepts the jwt, forwarding only accepts the API Key. Please enter the credentials for the {label} account — {plan} (the easiest way: just use \"Web login\" above — it exchanges this API Key for you and gets both credentials at once.)", "本机 Ollama 的 OpenAI 兼容端点,无需 API Key(默认已勾上「该上游无需鉴权」,鉴权头一个都不发)": "The local Ollama OpenAI-compatible endpoint; no API Key needed (\"This upstream needs no authentication\" is checked by default, and no auth headers are sent at all)", "本次未确认到账": "Not confirmed as credited this time", "条可导入": "are importable", "条配置,其中": "configs in cc-switch, of which", "桌面运行时不可用(Tauri 未初始化)": "Desktop runtime unavailable (Tauri not initialized)", "模型网关基址": "Model gateway base URL", "正在打开百度登录页…": "Opening the Baidu sign-in page…", "正在扫描本机 cc-switch…": "Scanning cc-switch on this machine…", "正在换发访客凭证…": "Requesting a guest credential…", "正在添加…": "Adding…", "正在等待{name}登录完成;完成或取消后才能发起新的登录": "Waiting for {name} sign-in to finish; you can start a new sign-in only after it completes or is canceled", "每周": "Weekly", "每日": "Daily", "每月": "Monthly", "没有则无法自动续期": "Without it, automatic renewal is not possible", "没有匹配「{keyword}」的提供商": "No providers match \"{keyword}\"", "添加 {name} 账号": "Add {name} account", "添加中…": "Adding…", "添加到 ": "Add to ", "添加失败:{reason}": "Add failed: {reason}", "添加小浣熊账号": "Add Raccoon account", "添加方式": "Add method", "添加访客账号": "Add a guest account", "添加账号": "Add account", "火山引擎 Coding 套餐;通用推理接入点为 https://ark.cn-beijing.volces.com/api/v3": "Volcano Engine Coding plan; the general inference endpoint is https://ark.cn-beijing.volces.com/api/v3", "点击添加账号": "Click to add an account", "点开即预填,填 Key 接入": "Opens prefilled; enter your Key to connect", "点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
    这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。": "After clicking the button, first pass a slider verification (the vendor's risk control), then the sign-in page opens and the account is added automatically once you sign in.
    This is the only official sign-in method for the International edition; if you are already signed in to the client, \"Import desktop login state\" is faster.", "用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。": "Sign in with the phone number bound to your AutoClaw account: click \"Get code\", then enter the SMS code below and sign in. The code is submitted from this machine directly to the official API; no token is shown in the UI.", "用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。": "Sign in with your CatPaw account (Meituan Passport); the account is added to the list automatically on success.", "用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。": "Sign in with the phone number bound to your Loomy account: click \"Get code\" and enter it. This is the easiest way in for this provider.", "用于转发;形如 apiKey.secret 两段点分(不填则这个账号不能转发)": "Used for forwarding; two dot-separated segments like apiKey.secret (if left blank, this account cannot forward)", "用于领取套餐与查询余额;不填则这个账号不能领取与查余额": "Used for claiming plans and querying balance; if left blank, this account cannot claim or check balance", "用户 ID": "User ID", "用绑定的手机号登录:点「获取验证码」后填入即可。这是国内版官方唯一的登录方式。": "Sign in with your bound phone number: click \"Get code\" and enter it. This is the only official sign-in method for the China edition.", "留空则用提供商名称": "Leave blank to use the provider name", "登录 / 添加 {name} 账号": "Sign in / add {name} account", "登录 session(一长串)": "Login session (a long string)", "登录中…": "Signing in…", "登录失败:{message}": "Sign-in failed: {message}", "登录已结束": "Sign-in has ended", "登录并添加": "Sign in and add", "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "Login-state file path: ~/.box-agent/config/auth.json (Windows: C:\\Users\\\\.box-agent\\config\\auth.json)", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "Delete custom provider \"{name}\"? This will also delete {count} accounts under it; deletion cannot be undone.", "等待 Accio {label}登录完成…": "Waiting for Accio {label} sign-in…", "等待 CatPaw 登录完成…": "Waiting for CatPaw sign-in…", "等待 Cline 授权确认…": "Waiting for Cline authorization confirmation…", "等待 CodeArts 登录完成…": "Waiting for CodeArts sign-in…", "等待 Google 授权完成…": "Waiting for Google authorization…", "等待 KukuAI 登录完成…": "Waiting for KukuAI sign-in…", "等待 OfficeAce 登录完成…": "Waiting for OfficeAce sign-in…", "等待 OrcaTerm 登录完成…": "Waiting for OrcaTerm sign-in…", "等待 Qoder 授权完成…": "Waiting for Qoder authorization…", "等待 Trae 授权完成…": "Waiting for Trae authorization…", "等待 ZCode {label}授权完成…": "Waiting for ZCode {label} authorization…", "等待小浣熊登录完成…": "Waiting for Raccoon sign-in…", "等待登录完成…": "Waiting for sign-in…", "等待网页登录…": "Waiting for web login…", "粘贴 API Key": "Paste API Key", "粘贴 Accio 的 accessToken": "Paste the Accio accessToken", "粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
    这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。": "Paste Antigravity's Google refresh token (starts with 1//; pasting the whole line containing refresh_token= also works).
    This is a manual entry besides web login (for example, exported from the official IDE you are already signed in to). The refresh token does not expire, and the gateway renews the access token automatically when it expires; if the token is lost, you can only go through Google authorization again, so keep it safe.", "粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。": "Paste the Command Code API Key (starts with user_; get it from the official CLI's ~/.commandcode/auth.json or the web studio). Pasting the whole line / with quotes / with the Bearer prefix all work; the gateway strips it down to the bare key itself. The key does not expire and there is no renewal API, so if it stops working just get a new one. When adding, the gateway runs a lightweight liveness check; a failure does not affect adding.", "粘贴 Cookie": "Paste Cookie", "粘贴 Google refresh token": "Paste Google refresh token", "粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。": "Paste OrcaTerm's OAuth accessToken (a JWT with three dot-separated parts; available from the desktop client's data.bin or the web version's ot_session cookie). Pasting a whole line containing Bearer / ot_session= is fine too — the gateway strips it down to the bare token. After adding, the gateway renews it automatically (the upstream has a renewal API), so you do not need to follow the 2-hour expiry by hand.", "粘贴 Qoder 个人访问令牌(pt-…)": "Paste the Qoder personal access token (pt-…)", "粘贴 accessToken": "Paste accessToken", "粘贴 access_token(一长串 JWT)": "Paste access_token (a long JWT)", "粘贴 session cookie": "Paste session cookie", "粘贴 token / refreshToken": "Paste token / refreshToken", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "Paste a token starting with workos:… (the prefix is added automatically if missing)", "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "Paste a Zhipu Qingyan access token (a JWT with three dot-separated parts). How to get it: open chatglm.cn in a browser, sign in, then copy the value of the chatglm_token cookie from Application → Cookies in the developer tools (pasting the whole chatglm_token=…; line works too). It is verified against the upstream once after pasting, and the account is only saved if that passes; signed-in accounts get points and sign-in capability, and the gateway renews it automatically afterwards (the upstream has a renewal API).", "粘贴登录凭据": "Paste login credentials", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "What you paste is the login session (which is also the model gateway token). Loomy has no renewal API; the session is valid for about 14 days, so just sign in again after it expires.", "粘贴网关凭据": "Paste gateway credentials", "粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
    {siteNote}
    MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。": "Paste the value of the web session cookie monkeycode_ai_session (copying the whole Cookie in the browser and pasting it directly also works; the gateway extracts the value itself).
    {siteNote}
    MonkeyCode has no renewal API: the session is valid for about 30 days (a hard limit); once it expires, sign in to the web page again and paste it one more time.", "粘贴访问令牌": "Paste an access token", "系统浏览器": "System browser", "系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。": "The system browser can reuse an existing login state (recommended for the International edition); the embedded window is cleaner.", "系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表": "Opens in the system browser (reuses the signed-in Cline account); added to the list when done", "系统浏览器打开(复用已登录的 Google 账号);完成后自动加入列表": "Opens in the system browser (reuses the Google account already signed in); added to the list when done", "系统浏览器打开(复用已登录的 Zai / Google 账号);完成后自动加入列表": "Opens in the system browser (reuses the signed-in Zai / Google account); added to the list when done", "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "Opens in the system browser (reuses the signed-in Meituan account); added to the list when done", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "Opens in the system browser (reuses a signed-in account); added to the list when done", "系统默认浏览器": "System default browser", "编码套餐 API Key": "Coding Plan API Key", "网关 Secret": "Gateway secret", "网页登录": "Web login", "网页登录的打开方式": "How to open web login", "网页登录(Zai / Google)": "Web login (Zai / Google)", "自定义": "Custom", "自建聚合面板:填你自己的站点地址。余额查询用该账号的 API Key 打 {base}/v1/usage,不需要额外配置": "Self-hosted aggregation panel: fill in your own site address. Balance query calls {base}/v1/usage with this account's API key; no extra setup", "自建聚合面板:填你自己的站点地址;余额查询要另填 NewAPI 的个人访问令牌(见下方「余额查询」一段)": "Self-hosted aggregation panel: fill in your own site address; balance query additionally needs a NewAPI personal access token (see \"Balance query\" below)", "获取风控配置失败,将直接尝试领取:{message}": "Failed to get the risk-control config; trying to claim directly: {message}", "获取验证码": "Get code", "访客模式": "Guest mode", "访客模式(免登录)": "Guest mode (no sign-in)", "该上游无需鉴权(不发送鉴权头)": "This upstream needs no authentication (no auth headers are sent)", "该提供商": "This provider", "该提供商账号添加功能即将上线": "Account adding for this provider is coming soon", "该自定义提供商已不存在(可能已被删除),请刷新后重试": "This custom provider no longer exists (it may have been deleted); refresh and try again", "请先选择一个自定义提供商": "Select a custom provider first", "请在弹出的滑块中完成验证(官方要求的风控步骤)": "Complete the verification in the slider popup (a risk-control step required by the vendor)", "请填写 11 位大陆手机号": "Enter an 11-digit mainland China phone number", "请填写 6 位数字验证码": "Enter the 6-digit code", "请填写 Base URL": "Enter the Base URL", "请填写 {label}": "Enter {label}", "请填写名称": "Enter a name", "请完成验证…": "Complete the verification…", "读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows": "Reads and decrypts %APPDATA%/AutoClaw/auth.json; Windows only", "读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录": "Reads ~/.cline/data/settings/providers.json; requires being signed in to the Cline client", "读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录": "Reads ~/.meituan-catpaw/auth.json; requires being signed in to the CatPaw client", "读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录": "Reads the local KukuAI client login state (%APPDATA%\\baidugenflowpro\\Network\\Cookies); requires being signed in to the KukuAI client", "读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录": "Read the local OrcaTerm client sign-in state (%APPDATA%\\com.orcaterm-desktop.app\\data.bin); the OrcaTerm client must be signed in", "读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录": "Read the local Zhipu Qingyan client sign-in state (%APPDATA%\\chatglm\\Network\\Cookies); the client must be signed in", "读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。": "Reads the account currently logged in to the official client (Tencent WorkBuddy / WorkBuddy AI) on this machine: the newest session file in its credential directory is located automatically (the official client must be logged in on this machine). The gateway renews the token after import — no need to swap tokens manually with the 2-hour expiry.", "读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。": "Reads the local client's current login state (plaintext Cookie, no decryption needed). The Cookies file is exclusively locked while the client runs, so close the KukuAI client before importing.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。": "Reads the local client's current login state live each time (deleting this record does not affect the client's login state).", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后点「刷新 Token」同步。": "Reads the local client's current login state live each time (deleting this record does not affect the client's login state). After signing in again in the client, click \"Refresh token\" to sync.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后重新导入即可。": "Reads the local client's current login state live each time (deleting this record does not affect the client's login state). After signing in again in the client, just re-import.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。": "Reads the local client's current login state live each time (deleting this record does not affect the client's login state). Users who sign in to the client with Zai / Google should use this one.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。": "Reads the local client's current login state live each time (deleting this record does not affect the client's login state). This one is grouped under {label}.", "读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。": "Reads the OAuth token the local client is currently signed in with (plain-text JSON, no decryption needed). After importing, the gateway renews it automatically — no need to follow the client's 2-hour expiry by hand.", "读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。": "Read the current sign-in state of the local client. The client's cookies are DPAPI-encrypted (Chromium os_crypt), so this is Windows-only; the file may be unreadable while the client is running — quit the client and retry (or use \"Fill in credentials\" to paste a token instead).", "账号信息": "Account info", "账号信息不完整,请刷新后重试": "Account info is incomplete; refresh and try again", "账号已添加,但请留意:{warning}": "Account added, but note: {warning}", "账号版本": "Account edition", "账号类型": "Account type", "账号:{name}
    ": "Account: {name}
    ", "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 创建": "Routes through Inference Providers; create the Key at hf.co/settings/tokens", "还没有账号": "No accounts yet", "这一家当前不支持网页登录,请改用填写凭证": "This provider does not support web login right now; use Enter credentials instead", "这个账号当前没有可自动领的活动(只自动领每日登录赠送积分)": "This account has no campaigns that can be claimed automatically right now (only the daily login gift points are claimed automatically)", "这份额度走「活动套餐」通道:若转发时提示编码套餐已到期,": "This quota goes through the Start Plan channel: if forwarding reports that the Coding Plan has expired, ", "这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。": "This is for the China site (monkeycode-ai.com) account: paste the China site's session. The international site (monkeycode-ai.net) is another provider with its own account; credentials are not interchangeable between the two.", "这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。": "This is for the international site (monkeycode-ai.net) account: paste the international site's session. The China site (monkeycode-ai.com) is another provider with its own account; credentials are not interchangeable between the two.", "选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "Select the CatPaw client's login-state file auth.json (Windows: C:\\Users\\\\.meituan-catpaw\\auth.json; macOS / Linux: ~/.meituan-catpaw/auth.json). After import the credentials live on the gateway and can be forwarded independently — no longer dependent on this machine's client.", "选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。": "Select the Cline client's login-state file providers.json (Windows: C:\\Users\\\\.cline\\data\\settings\\providers.json; macOS / Linux: ~/.cline/data/settings/providers.json). After import the credentials live on the gateway and can be forwarded independently — no longer dependent on this machine's client; this record goes into {label}.", "选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。": "Select the KukuAI client's Cookies file (%APPDATA%\\baidugenflowpro\\Network\\Cookies): it is a **binary** SQLite database (no extension, not JSON). The import reads a copy of the file you selected and does not compete with the running client for the lock, so **you do not need to close the client first**. After import the credentials live on the gateway and can be forwarded independently.", "选择凭证文件导入": "Import from credential file", "选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。": "Select the official client's session credential file (workbuddy-desktop.info / workbuddy-desktop-ai.info) or an account JSON exported by wb2api (nested or flat).", "选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "Select the Raccoon client's login-state file auth.json (Windows: C:\\Users\\\\.box-agent\\config\\auth.json; macOS / Linux: ~/.box-agent/config/auth.json). After import the credentials live on the gateway and can be forwarded independently — no longer dependent on this machine's client.", "选择提供商": "Select provider", "选择的文件不是合法 JSON": "The selected file is not valid JSON", "选择要添加账号的提供商": "Select the provider to add an account for", "重新扫描": "Rescan", "鉴权": "Authentication", "阿里云 Token Plan 套餐(国际站)": "Alibaba Cloud Token Plan (International site)", "阿里云百炼 Coding 套餐(国内站)": "Alibaba Cloud Model Studio Coding plan (China site)", "阿里云百炼 Coding 套餐(国际站)": "Alibaba Cloud Model Studio Coding plan (International site)", "阿里云百炼模型服务(国际站,OpenAI 兼容模式)": "Alibaba Cloud Model Studio model service (International site, OpenAI-compatible mode)", "需要图形验证码:登录窗口已弹出,请填写后继续": "Captcha required: the sign-in window has opened; fill it in to continue", "预置 API": "Preset API", "领取 CodeArts 每日福利": "Claim CodeArts daily welfare", "领取失败:{message}": "Claim failed: {message}", "领取并让官方确认": "Claim and await confirmation", "风控验证配置不完整,请稍后重试": "Risk-control verification config is incomplete; try again later", "验证码": "Verification code", "验证码已发送。收到后填入下方并点「登录并添加」": "Code sent. Enter it below and click \"Sign in and add\"", "验证码已发送,请查看手机短信": "Code sent; check your SMS", "验证码已发送,请查看手机短信。填入下方并点「登录并添加」即完成登录": "Code sent; check your SMS. Enter it below and click \"Sign in and add\" to finish signing in", "验证码已发送,请查看短信": "Code sent; check your messages", "验证码校验失败,请重试": "Code verification failed; try again", "验证码校验超时,请重试": "Code verification timed out; try again", "验证码组件不可用,请重试": "Verification component unavailable; try again", "验证码组件初始化超时,请重试": "Verification component initialization timed out; try again", "验证码组件加载失败,请检查网络后重试": "Verification component failed to load; check your network and try again", "验证码组件加载异常,请重试": "Verification component encountered a loading error; try again", "验证码组件加载超时,请检查网络后重试": "Verification component timed out while loading; check your network and try again", "验证码组件未加载,请重启应用后重试": "Verification component not loaded; restart the app and try again", "(免费额度池,模型名带 cline-free/ 前缀)。": " (free quota pool; model names carry the cline-free/ prefix).", "(其中 {n} 把恢复为不限制)": " ({n} of which are now unrestricted)", "(必填)": " (Required)", "(有效期 {window})": "(valid {window})", "(订阅池,模型名带 cline-pass/ 前缀,需要账号有对应订阅)。": " (subscription pool; model names carry the cline-pass/ prefix and the account needs the matching subscription).", ",下一次最快 {minutes} 分钟后": ", earliest next attempt in {minutes} min", ",并从 {n} 把 Key 的可用提供商里移除": ", and removed it from the allowed providers of {n} keys" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · 模型 {head}{rest}": " · Models: {head}{rest}", " 等 {n} 个": " ({n} total)", "1// 开头的 Google refresh token(也可直接粘贴 refresh_token=… 整行)": "Google refresh token starting with 1// (you can also paste the whole refresh_token=… line)", "11 位大陆手机号": "11-digit mainland China mobile number", "1~64 个字符,账号列表里按它分组显示": "1-64 characters; accounts are grouped by it in the account list", "6 位数字": "6 digits", "

  • 没有可自动领的活动
  • ": "
  • No campaigns can be claimed automatically
  • ", "

    领到的是套餐赠送积分,不会增加福利模型的 token 池;": "

    What you receive is plan gift points, which do not add to the welfare model token pool; ", "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "The Anthropic-compatible endpoint is https://api.deepseek.com/anthropic (fill it in when you switch the protocol to anthropic)", "Anthropic 协议填根地址,不要带 /v1": "For the Anthropic protocol, use the root URL without /v1", "Antigravity": "Antigravity", "AtomCode": "AtomCode", "AutoClaw 国内版": "AutoClaw China edition", "AutoClaw 国际版": "AutoClaw International edition", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS is required; paste the **full Cookie** (a Cookie editor can export it in one click) — the **PTOKEN must be kept**, as the gateway uses it to exchange for a KukuAI business session token from Baidu; without it, model refresh reports \"Not signed in\". A full Cookie header, a JSON array exported by a Cookie editor, or a {BDUSS, STOKEN} object are all supported. KukuAI has no refresh mechanism, so once the login state expires, just paste it again.", "BDUSS=…; STOKEN=…(或 Cookie 编辑器导出的 JSON)": "BDUSS=…; STOKEN=… (or the JSON exported by a Cookie editor)", "CatPaw 的 X-Passport-Token(登录态 Cookie)": "CatPaw's X-Passport-Token (login-state Cookie)", "Cline Pass 的 API Key 在 app.cline.bot 获取(订阅制);端点为 OpenAI 兼容,模型清单以 Cline 控制台为准,模型名在模型管理里按裸名添加(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客户端形态校验,所需的客户端请求头已随卡预置": "Get the Cline Pass API Key at app.cline.bot (subscription-based); the endpoint is OpenAI-compatible, the model catalog follows the Cline console, and model names are added under Model management with the plain name (e.g. glm-5.2 / kimi-k2.7-code / deepseek-v4-pro); the upstream validates the client shape, so the required client request headers are preset on this card", "Cloud-IDE-JWT(三段点分)": "Cloud-IDE-JWT (three dot-separated segments)", "Command Code": "Command Code", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT (three dot-separated parts; you can also paste a whole Bearer line or the data.bin content)", "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT (three dot-separated parts; a whole chatglm_token=… line works too)", "LobsterAI": "LobsterAI", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode International edition", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "NVIDIA NIM managed endpoint; apply for the Key at integrate.api.nvidia.com", "OfficeAce 果办": "OfficeAce", "accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)": "accessToken is Accio's login credential (a long opaque token, not a JWT); refreshToken is optional and enables automatic renewal on expiry. Enter the credential of the {label} account — {siteNote} (the easiest way to get it: just use \"Web login\" above; no need to hunt for the token manually.)", "accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
    手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
    没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。": "accessToken is Trae's Cloud-IDE-JWT (three dot-separated segments), and refreshToken enables automatic renewal on expiry (this provider rotates refreshToken on every use: once exchanged, the old one is void, so do not refresh the same account from two programs at once).
    When pasting manually, fill in machineId / deviceId as well: upstream ties them to the device public key uploaded at sign-in to identify the device, and swapping in a new pair from nowhere triggers the 2xxxx family of device-binding rejections.
    Without these three, the proper route is \"Web login\" above; for container / remote deployments, paste the callback URL from the address bar as prompted.", "monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)": "The value of monkeycode_ai_session (you can also paste the whole Cookie)", "refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。": "refreshToken is optional and enables automatic renewal on expiry; both can be obtained from the Raccoon client login-state file.", "refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。": "refreshToken is optional and enables automatic renewal; both can be obtained from the client login-state file. The same Cline account works in both pools; here it is grouped under {label}.", "token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。": "token supports the enc: prefix (decrypted automatically on Windows). Without a refreshToken, automatic renewal is not possible.", "token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。": "token is the login-state Cookie; CatPaw has no refresh mechanism, so sign in again in the client after it expires.", "user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)": "API Key starting with user_ (you can also paste the whole Authorization: Bearer … line)", "{label} 网页登录的打开方式": "How to open {label} web login", "{label} 账号地区": "{label} account region", "{label} 账号的添加方式": "How to add a {label} account", "{name} 不是合法 JSON:{reason}": "{name} is not valid JSON: {reason}", "{name}要粘一个 JSON 对象(形如 {sample})": "{name} requires a JSON object (like {sample})", "{n} 个账号": "{n} accounts", "{n} 个账号,点击添加": "{n} accounts, click to add", "{seconds}s 后重发": "Resend in {seconds}s", "✅ {label}账号已添加": "✅ {label} account added", "✅ {label}账号已添加{name}": "✅ {label} account added{name}", "✅ 已从 cc-switch 导入 {n} 个供应商": "✅ Imported {n} providers from cc-switch", "✅ 已创建自定义提供商「{name}」并添加账号": "✅ Created custom provider \"{name}\" and added an account", "✅ 已删除自定义提供商「{name}」{accounts}{keys}": "✅ Deleted custom provider \"{name}\"{accounts}{keys}", "✅ 已更新已有账号{name}(同一把 API Key 只保留一条,重复添加会合并)": "✅ Updated the existing account{name} (one account per API key; re-adding merges into it)", "✅ 登录成功,{edition}账号已加入列表": "✅ Signed in; {edition} account added to the list", "✅ 领取成功:{planId}{period}": "✅ Claim successful: {planId}{period}", " 同一家可以放多把 key,按优先级轮换。": " One provider can hold multiple keys, rotated by priority.", "「{name}」的账号添加功能还在开发中,敬请期待。": "Adding accounts for \"{name}\" is still in development — stay tuned.", "一个 Key 用遍多家模型,模型名用「厂商/模型」全称(如 deepseek/deepseek-chat)": "One Key works across many vendors; use the full \"vendor/model\" name (e.g. deepseek/deepseek-chat)", "上游不要鉴权时留空,并勾选下面一项(留空又不勾会被当成未配置凭证)": "Leave blank when the upstream needs no authentication, and check the option below (blank without the checkbox counts as no credential configured)", "上游信息": "Upstream info", "不可领({status})": "Not claimable ({status})", "不支持导入": "Import not supported", "不是有效的凭证文件:内容要是一个 JSON 对象": "Not a valid credential file: the content must be a JSON object", "不是有效的凭证文件:需要包含 accessToken 与 uid": "Not a valid credential file: accessToken and uid are required", "不是自定义提供商": "Not a custom provider", "不需要 Key:转发与拉取模型都不发送鉴权头": "No Key needed: neither forwarding nor model fetching sends auth headers", "不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

    注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。": "No sign-in needed: click the button below and the gateway will obtain a guest credential from Zhipu Qingyan directly and add it to the account list (you can fill in a note name first). The guest credential can be used for chat right away; the free quota is issued by the upstream per device.

    Note: a guest account has no points system — its member points, daily sign-in points and activity claim endpoints all require sign-in, so those capabilities are skipped for guests (no readings are shown on the accounts page or in the check-in center). To get points, use \"Fill in credentials\" to paste a signed-in account token, or \"Import desktop sign-in state\".", "两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。": "Both editions' accounts can be saved at once, each with its own model catalog (the China and International editions are two providers that can be enabled and mapped separately).", "中国版": "China edition", "今天没有需要领的活动(都已到账)": "No campaigns to claim today (all already credited)", "今天(北京时间 {day})已试 {attempts} 次,还能试 {left} 次{next}
    ": "Tried {attempts} times today (Beijing time {day}); {left} attempts left{next}
    ", "从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。": "Grab three items from another machine (or from the OfficeAce desktop app's models.json) and paste them here: the model gateway base URL (baseUrl, shaped like https://modelgw-…/v2) plus modelAppKey / modelAppSecret. These three are enough to forward requests (the gateway credential never expires); the control-plane temporary credentials (accessKeyId and so on) are optional — leaving them blank disables balance queries and auto-renewal but does not affect forwarding.", "从本机导入桌面端登录态": "Import desktop login state from this machine", "任务镜像 id": "Task image id", "余额查询": "Balance query", "使用 Google 账号登录": "Sign in with Google", "使用 Zai 账号登录": "Sign in with Zai", "使用个人访问令牌(PAT)": "Use a personal access token (PAT)", "免费模型不用填 Key(默认勾上「该上游无需鉴权」,网关自动用匿名凭证);付费模型则填 Zen 的 API Key。免费档上游只认官方客户端形态,已默认开启「伪装 OpenCode 官方客户端」": "Free models need no Key (\"This upstream needs no authentication\" is checked by default, and the gateway uses anonymous credentials automatically); for paid models, enter your Zen API Key. The free tier only accepts the official client form, so \"Emulate the official OpenCode client\" is on by default", "内嵌窗口": "Embedded window", "内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)": "Opens in the embedded window; closing it cancels the wait (if a Google account is rejected, switch to the system browser)", "内嵌窗口打开;完成后自动加入列表,关窗即取消等待": "Opens in the embedded window; added to the list when done; closing it cancels the wait", "内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待": "Opens in an embedded window; the account is added automatically once you have scanned — closing the window cancels the wait", "内嵌窗口用全新环境,多账号互不影响;关窗即取消等待": "The embedded window uses a fresh environment, so multiple accounts do not affect each other; closing it cancels the wait", "内嵌窗口(推荐)": "Embedded window (recommended)", "准备验证…": "Preparing verification…", "凭据 JSON": "Credential JSON", "凭证文件在哪?": "Where is the credential file?", "创建并添加账号": "Create and add account", "创建这个提供商,并同时建立它的第一个账号。": "Create this provider and its first account in one step.", "删除功能不可用(脚本未就绪)": "Delete is unavailable (script not ready)", "删除失败:{reason}": "Delete failed: {reason}", "删除此提供商": "Delete this provider", "及 {n} 个账号": " and {n} accounts", "发送中…": "Sending…", "发送失败:{message}": "Send failed: {message}", "发送未确认:登录窗口已弹出,请在弹出的窗口中手动完成登录": "Send unconfirmed: the sign-in window has opened; complete the sign-in manually in that window", "取消失败:{message}": "Cancel failed: {message}", "取消等待": "Cancel sign-in", "只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页": "Only the Embedded window can restore third-party options: we cannot modify the sign-in page in the system browser", "可选": "Optional", "可选(cloudaicompanionProject):留空由网关自动发现": "Optional (cloudaicompanionProject); if blank, the gateway discovers it automatically", "可选,展示用": "Optional; for display", "可选,没有则无法自动续期": "Optional; without it, automatic renewal is not possible", "可选,用于生成账号标识": "Optional; used to build the account identifier", "可选,留空使用凭据里的 user_name": "Optional; defaults to the user_name in the credential", "可选,留空使用账号姓名、邮箱或令牌指纹": "Optional; defaults to the account name, email, or token fingerprint", "可选,留空使用账号昵称或邮箱": "Optional; defaults to the account nickname or email", "可选,留空则用 key 尾号": "Optional; defaults to the key suffix", "可选,留空则用 userId": "Optional; defaults to userId", "可选,留空则用上游返回的账号名或账号 id": "Optional; defaults to the account name or account id returned by upstream", "可选,留空则用凭证里的账号名": "Optional; defaults to the account name in the credential", "可选,留空则用文件里的昵称": "Optional; leave empty to use the nickname from the file", "可选,留空则用登录名或 uid": "Optional; defaults to the login name or uid", "可选,留空则用脱敏手机号": "Optional; defaults to the masked phone number", "可选,留空则用脱敏手机号或 userId": "Optional; defaults to the masked phone number or userId", "可选,留空则用账号 uk": "Optional; defaults to the account uk", "可选,留空则用账号昵称": "Optional; defaults to the account nickname", "可选,留空则用账号昵称或 userId": "Optional; leave blank to use the account nickname or userId", "可选,留空则用邮箱或令牌尾号": "Optional; defaults to the email or token suffix", "可选,留空用「AtomCode」": "Optional; defaults to “AtomCode”", "可选,留空用「OfficeAce 果办」": "Optional; defaults to “OfficeAce”", "可选,留空自动生成": "Optional; generated automatically if blank", "可选,留空自动用昵称": "Optional; defaults to the nickname", "可选,给这个访客账号起个备注名": "Optional; a note name for this guest account", "可选,续期时带上": "Optional; sent when renewing", "可选,讯飞侧 userid": "Optional; the userid on the iFlytek side", "可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补": "Optional; required to start a conversation. If blank, the gateway discovers it automatically from this account's existing tasks; if it cannot find one, fill it in manually later.", "可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)": "Optional; the account email, used for display and to recognize the identity (the same account is recognized even after switching to a new token)", "可选;UUID 形态,与凭据同生共死": "Optional; UUID form, lives and dies with the credential", "可选;填了才能到期自动续期": "Optional; required for automatic renewal on expiry", "可选;用于去重与展示名(留空时网关会问一次上游)": "Optional; used for deduplication and the display name (when blank, the gateway asks upstream once)", "可选;用于生成账号 id 与展示名": "Optional; used to generate the account id and display name", "可选;纯数字串(上游只收 8–24 位数字)": "Optional; digits only (upstream accepts only 8–24 digits)", "可领取": "Claimable", "同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)": "Keyed and no-auth accounts can coexist in one provider, each routed by its own rules; multiple no-auth accounts are allowed too (each with its own egress proxy)", "同地址换 anthropic 协议即走它的 Claude 兼容端点。转发必须填 OpenCode Go 的 API Key(订阅制;获取模型清单不需要 Key,但发请求上游会回 401「Missing API key」)": "Switch the protocol to anthropic on the same address to use its Claude-compatible endpoint. Forwarding requires an OpenCode Go API Key (subscription-based; fetching the model catalog needs no Key, but requests get a 401 \"Missing API key\" from upstream)", "响应为空": "Empty response", "商汤日日新的 OpenAI 兼容端点;余额查询走控制台账号密码(见下方「余额查询」一段)": "SenseNova's OpenAI-compatible endpoint; balance query uses the console account and password (see \"Balance query\" below)", "国内版": "China edition", "国内版与**国际版是两套独立的账号与套餐**,凭证与领取的套餐都不通用。": "The China edition and **the International edition are two separate sets of accounts and plans**; credentials and claimed plans do not carry over between them.", "国内版登录页没有 Google / GitHub 入口(它用微信 / 手机号 / 邮箱登录)": "The China edition's sign-in page has no Google / GitHub options (it uses WeChat / phone / email sign-in)", "国内版的登录站点是 www.accio-ai.com,与国际版不是同一站。": "The China edition's sign-in site is www.accio-ai.com, a different site from the International edition's.", "国内版(WorkBuddy)": "China edition (WorkBuddy)", "国内站为 https://api.siliconflow.cn/v1": "China site: https://api.siliconflow.cn/v1", "国内站(minimaxi.com);OpenAI 兼容地址为 https://api.minimaxi.com/v1": "China site (minimaxi.com); the OpenAI-compatible address is https://api.minimaxi.com/v1", "国际版": "International edition", "国际版 z.ai 的 Coding 套餐;OpenAI 兼容地址为 https://api.z.ai/api/coding/paas/v4": "The International edition z.ai Coding plan; the OpenAI-compatible address is https://api.z.ai/api/coding/paas/v4", "国际版与国内版是**两套独立的账号**(同一账号体系的两个站点),凭证不通用。": "The International and China editions are **two separate sets of accounts** (two sites of the same account system); credentials do not carry over.", "国际版与国内版账号体系独立,请填国际版的凭证。未填 refreshToken 无法自动续期。": "The International and China editions have separate account systems; enter the International edition's credentials. Without a refreshToken, automatic renewal is not possible.", "国际版登录页默认只显示邮箱登录,勾选后恢复 Google / GitHub / X 入口": "The International edition's sign-in page shows only email sign-in by default; checking this restores the Google / GitHub / X options", "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "The International edition's inference site is api.z.ai, a different site from the China edition's; plans are separate as well.", "国际版(WorkBuddy AI)": "International edition (WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "International site Coding plan; the OpenAI-compatible address is https://api.minimax.io/v1", "在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。": "Paste the two values from the AtomCode page on AtomGit: accessToken and refreshToken. The refresh token is strongly recommended — this provider's token is not a JWT (it expires in about 7 days and carries no readable expiry), so an account without it cannot renew itself and must be re-authorised once it expires.", "在 cc-switch 里找到": "Found", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "Complete the official sign-in in the embedded window; the account is added to the list automatically on success.", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "Generate a PAT in the Qoder account settings → Integrations for {label} (do not use a Google / GitHub token). Accounts and credentials do not carry over between the two regions.", "在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。": "Scan the WeChat QR code on the official authorization page that opens (the QR code appears right away). The account is added to the list automatically after a successful scan.", "在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "In the window that opens, sign in to the KukuAI main site with your Baidu account (SMS code or QR scan both work); the account is added to the list automatically on success. The gateway exchanges it with Baidu for a KukuAI business session token (requires the KukuAI client installed on this machine).", "在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "Sign in with your Google account in the window that opens (Antigravity's authorization page); the account is added to the list automatically once authorization completes.", "在浏览器中打开登录页": "Open the sign-in page in your browser", "地区": "Region", "填写凭证": "Enter credentials", "填写凭证添加": "Enter credentials to add", "备注名": "Label", "套餐": "Plan", "如:智谱 GLM": "e.g. Zhipu GLM", "完成登录后自动加入账号列表,昵称取自上游。": "Added to the account list automatically after sign-in; the nickname comes from upstream.", "官方客户端凭证目录:Windows 是 C:\\Users\\<你的用户名>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth,macOS 是 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth": "Official client credential directory: Windows — C:\\Users\\\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth; macOS — ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth", "官方已确认到账": "Officially confirmed as credited", "导入": "Import", "导入中…": "Importing…", "导入凭证文件": "Import credential file", "导入失败": "Import failed", "导入失败:{reason}": "Import failed: {reason}", "导入所选": "Import selected", "导入所选({n})": "Import selected ({n})", "导入桌面端登录态": "Import desktop login state", "将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下": "The embedded window will open; the account is added to the list automatically once authorization completes. Closing the window cancels the wait. The authorization page may end with a message that it cannot open the zcode:// link — that is normal; the result has already been recorded server-side", "将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)": "The embedded window will open; the account is added to the list automatically once authorization completes. On a remote panel you can paste the callback URL. Closing the window cancels the wait. The link is valid for 5 minutes; if it times out or is not clicked, it is voided (you can start again)", "将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待": "The embedded window will open; the account is added to the list automatically after sign-in. Closing the window cancels the wait", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 Trae 账号);同机完成后自动加入,Docker / 远程面板按提示粘贴地址栏回调地址。": "The authorization page will open in your system default browser (reusing the Trae account already signed in there); on the same machine the account is added automatically when done. For Docker / remote panels, paste the callback URL from the address bar as prompted.", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 ZCode 账号);授权完成后自动加入账号列表。页面最后可能提示无法打开 zcode:// 链接,属正常现象": "The authorization page will open in your system default browser (reusing the ZCode account already signed in there); the account is added to the list automatically once authorization completes. A final message that the zcode:// link cannot be opened is normal", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "The authorization page will open in your system default browser (reusing the Huawei Cloud account already signed in there); the gateway polls the cloud for the authorization code, so it does not matter which machine the browser runs on. The account is added to the list automatically once done", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "The authorization page will open in your system default browser (reusing the Huawei Cloud account already signed in there); when the browser and the gateway are not on the same machine, paste the final callback URL back into the panel as described above", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "The sign-in page will open in your system default browser (reusing the Accio account already signed in there); the account is added to the list automatically after sign-in, and closing the popup cancels the wait", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "The sign-in page will open in your system default browser (reusing the LobsterAI account already signed in there); the account is added to the list automatically after sign-in, and closing the popup cancels the wait", "小米 MiMo Token Plan 套餐(新加坡站)": "Xiaomi MiMo Token Plan (Singapore site)", "已到账": "Credited", "已取消登录等待": "Pending sign-in canceled", "已取消领取": "Claim canceled", "已取消验证码": "Verification canceled", "已填写手机号,正在发送验证码…": "Phone number entered; sending the code…", "已声明无需鉴权:账号会被正常选路,出网时不带任何鉴权头": "Marked as needing no authentication: the account is routed normally, and no auth headers are sent outbound", "已导入 {ok} 个,失败 {fail} 个(失败项已标注在列表里)": "Imported {ok}, failed {fail} (failed items are marked in the list)", "已建过 · {n} 个账号,点击继续添加": "Already created · {n} accounts, click to add more", "已建过,点击继续添加账号": "Already created, click to add another account", "已打开官方登录页,请在窗口中完成登录…": "Official sign-in page opened; complete sign-in in the window…", "已点发送,等待短信…": "Sent; waiting for the SMS…", "已用系统默认浏览器打开登录页,请在浏览器中完成登录…": "Sign-in page opened in your system default browser; complete sign-in in the browser…", "已领取,等官方确认": "Claimed, awaiting official confirmation", "当前壳版本不支持 AutoClaw 网页登录,请更新应用": "This app version does not support AutoClaw web login; update the app", "当前没有可领取的套餐(活动尚未开始)": "No plans are available to claim right now (the event has not started yet)", "当前环境不支持领取(桥接方法缺失)": "Claiming is not supported in this environment (bridge method missing)", "必填,CatPaw 账号标识": "Required; CatPaw account identifier", "恢复 Google / GitHub 入口": "Restore Google / GitHub options", "手机号": "Phone number", "手机验证码登录": "SMS code login", "打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。": "Open the official LobsterAI sign-in page (lobsterai.youdao.com) and sign in with your LobsterAI account: once finished, the official page returns to this machine and the account is added automatically — the callback lands on the gateway's own port, so the browser must be on the same machine as the gateway; the auth code is only handed to the gateway locally and no plaintext token is shown in the UI.", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Open the official authorization page for Trae SOLO (trae.cn) and sign in with your Trae account: once authorized, the official page redirects back to a temporary port on this machine; a same-machine deployment receives it automatically, while Docker / remote panels should copy the callback URL from the address bar as prompted.", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Open the official sign-in page for Accio {label} ({site}) and sign in with your Accio account: on success the official page redirects back to this machine; a same-machine setup completes automatically, while Docker / remote panels just paste the callback URL as prompted (the authorization code is passed to the gateway only on this machine, and no plaintext token is shown in the UI).", "打开 Accio {label}登录页": "Open the Accio {label} sign-in page", "打开 Antigravity 网页登录": "Open Antigravity web login", "打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。": "Open the AtomGit authorization page and sign in: once finished, the gateway automatically polls the broker for the token and adds the account (the callback is terminated by the broker and never returns to this machine, so no callback URL needs to be filled in).", "打开 CatPaw 网页登录": "Open CatPaw web login", "打开 Cline 授权页": "Open Cline authorization page", "打开 CodeArts 授权页": "Open CodeArts authorization page", "打开 LobsterAI 登录页": "Open the LobsterAI sign-in page", "打开 Qoder 网页登录": "Open Qoder web login", "打开 Trae 授权页": "Open Trae authorization page", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "Open the official authorization page for ZCode {label} and sign in with your account. The authorization result is recorded by the ZCode server; the gateway fetches the credential in the background and adds the account to the list.", "打开 ZCode {label}授权页": "Open the ZCode {label} authorization page", "打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
    网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。": "Open the official Huawei Cloud CodeArts authorization page to sign in: once done, the official page brings the browser back to the gateway's own /oauth/callback, and the gateway exchanges the one-time authorization code for temporary credentials and adds the account to the list.
    When the gateway runs on another machine, the web UI prompts you to paste the final callback URL from the address bar back into the panel.", "打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。": "Open the Huawei Cloud authorization page and sign in: once finished, the gateway automatically polls the cloud for the authorization code and adds the account to the list (no OfficeAce desktop app required, and no need to paste a callback URL back manually).", "打开官方授权页完成设备码授权,登录的是{label}({site})的账号。": "Open the official authorization page and complete the device-code authorization; you will be signing in to a {label} ({site}) account.", "打开授权页": "Open the authorization page", "打开授权页完成确认,账号自动加入 {label}{note}": "Open the authorization page and confirm; the account is added to {label} automatically{note}", "打开方式": "Open with", "打开网页登录": "Open web login", "探测失败:{message}": "Probe failed: {message}", "接入一个兼容上游": "Connect a compatible upstream", "提交中…": "Submitting…", "搜索提供商": "Search providers", "搜索提供商…": "Search providers…", "整份粘贴官方插件 / CLIProxyAPI 落盘的凭据 JSON(形如 {\"codearts_provider_credential\":{…}},铺平的也行)。
    必填:access_key_id、secret_access_key、security_token。要能自动续期,必须连 refresh_token 与 oauth_context 一起粘 —— 临时凭据约一小时到期,缺这半块就续不回来,只能重新登录。": "Paste the whole credential JSON written to disk by the official plugin / CLIProxyAPI (like {\"codearts_provider_credential\":{…}}; a flattened form also works).
    Required: access_key_id, secret_access_key, security_token.For automatic renewal you must paste refresh_token and oauth_context as well — temporary credentials expire in about an hour, and without that half they cannot be renewed; you would have to sign in again.", "文件是{edition}凭证,已按该版本导入": "The file contains {edition} credentials and has been imported as that edition", "新建自定义提供商": "New custom provider", "方案:{label}": "Scheme: {label}", "明文 JWT 或 auth.json 里的 enc: 加密值(自动解密)": "Plaintext JWT, or the enc: encrypted value in auth.json (decrypted automatically)", "明文 JWT(国际版账号的 access token)": "Plaintext JWT (the access token of an International edition account)", "智谱 bigmodel.cn 的 Coding 套餐,Key 在 open.bigmodel.cn/usercenter/apikeys": "Zhipu's bigmodel.cn Coding plan; get the Key at open.bigmodel.cn/usercenter/apikeys", "未知错误": "Unknown error", "未能获取授权地址,请重试": "Could not get the authorization URL; try again", "未能读取 cc-switch 数据": "Could not read cc-switch data", "未领取成功": "Claim unsuccessful", "本地 Ollama、OpenCode Zen 免费档这类上游要勾上,否则账号不可用": "Check this for upstreams like local Ollama or the OpenCode Zen free tier; otherwise the account cannot be used", "本家有两个**互不替代**的凭证,按你要用的功能填,至少填一个:编码套餐 API Key 用于转发推理(打 {site})与查询窗口限额(每 N 小时 / 每周的剩余比例);jwt 用于领取套餐与查询余额(打 zcode.z.ai 的 billing 网关,官方叫 Coding Plan JWT,是一串三段点分的字符串)。余额两把都能查、但读到的东西不同:jwt 读到余额桶与套餐到期,API Key 读到窗口限额;而领取只认 jwt、转发只认 API Key。请填写 {label}账号的凭证 —— {plan}(最容易拿到的办法:直接用上方的「网页登录」—— 它会替你把这个 API Key 换好,两个凭证一起拿到。)": "This provider has two credentials that **do not replace each other**; fill in the one(s) for the features you need, at least one: the Coding Plan API Key is used for forwarding inference (hits {site}) and for querying the window limits (the remaining percentage of each N-hour / weekly window); the jwt is used for claiming plans and querying balance (hits the billing gateway at zcode.z.ai; officially called a Coding Plan JWT, a three-segment dot-separated string). Either one can query balance, but they read different things: the jwt reads the balance buckets and the plan expiry, while the API Key reads the window limits; and claiming only accepts the jwt, forwarding only accepts the API Key. Please enter the credentials for the {label} account — {plan} (the easiest way: just use \"Web login\" above — it exchanges this API Key for you and gets both credentials at once.)", "本机 Ollama 的 OpenAI 兼容端点,无需 API Key(默认已勾上「该上游无需鉴权」,鉴权头一个都不发)": "The local Ollama OpenAI-compatible endpoint; no API Key needed (\"This upstream needs no authentication\" is checked by default, and no auth headers are sent at all)", "本次未确认到账": "Not confirmed as credited this time", "条可导入": "are importable", "条配置,其中": "configs in cc-switch, of which", "桌面运行时不可用(Tauri 未初始化)": "Desktop runtime unavailable (Tauri not initialized)", "模型网关基址": "Model gateway base URL", "正在打开百度登录页…": "Opening the Baidu sign-in page…", "正在扫描本机 cc-switch…": "Scanning cc-switch on this machine…", "正在换发访客凭证…": "Requesting a guest credential…", "正在添加…": "Adding…", "正在等待{name}登录完成;完成或取消后才能发起新的登录": "Waiting for {name} sign-in to finish; you can start a new sign-in only after it completes or is canceled", "每周": "Weekly", "每日": "Daily", "每月": "Monthly", "没有则无法自动续期": "Without it, automatic renewal is not possible", "没有匹配「{keyword}」的提供商": "No providers match \"{keyword}\"", "添加 {name} 账号": "Add {name} account", "添加中…": "Adding…", "添加到 ": "Add to ", "添加失败:{reason}": "Add failed: {reason}", "添加小浣熊账号": "Add Raccoon account", "添加方式": "Add method", "添加访客账号": "Add a guest account", "添加账号": "Add account", "火山引擎 Coding 套餐;通用推理接入点为 https://ark.cn-beijing.volces.com/api/v3": "Volcano Engine Coding plan; the general inference endpoint is https://ark.cn-beijing.volces.com/api/v3", "点击添加账号": "Click to add an account", "点开即预填,填 Key 接入": "Opens prefilled; enter your Key to connect", "点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
    这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。": "After clicking the button, first pass a slider verification (the vendor's risk control), then the sign-in page opens and the account is added automatically once you sign in.
    This is the only official sign-in method for the International edition; if you are already signed in to the client, \"Import desktop login state\" is faster.", "用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。": "Sign in with the phone number bound to your AutoClaw account: click \"Get code\", then enter the SMS code below and sign in. The code is submitted from this machine directly to the official API; no token is shown in the UI.", "用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。": "Sign in with your CatPaw account (Meituan Passport); the account is added to the list automatically on success.", "用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。": "Sign in with the phone number bound to your Loomy account: click \"Get code\" and enter it. This is the easiest way in for this provider.", "用于转发;形如 apiKey.secret 两段点分(不填则这个账号不能转发)": "Used for forwarding; two dot-separated segments like apiKey.secret (if left blank, this account cannot forward)", "用于领取套餐与查询余额;不填则这个账号不能领取与查余额": "Used for claiming plans and querying balance; if left blank, this account cannot claim or check balance", "用户 ID": "User ID", "用绑定的手机号登录:点「获取验证码」后填入即可。这是国内版官方唯一的登录方式。": "Sign in with your bound phone number: click \"Get code\" and enter it. This is the only official sign-in method for the China edition.", "留空则用提供商名称": "Leave blank to use the provider name", "登录 / 添加 {name} 账号": "Sign in / add {name} account", "登录 session(一长串)": "Login session (a long string)", "登录中…": "Signing in…", "登录失败:{message}": "Sign-in failed: {message}", "登录已结束": "Sign-in has ended", "登录并添加": "Sign in and add", "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "Login-state file path: ~/.box-agent/config/auth.json (Windows: C:\\Users\\\\.box-agent\\config\\auth.json)", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "Delete custom provider \"{name}\"? This will also delete {count} accounts under it; deletion cannot be undone.", "等待 Accio {label}登录完成…": "Waiting for Accio {label} sign-in…", "等待 AtomCode 登录完成…": "Waiting for AtomCode sign-in…", "等待 CatPaw 登录完成…": "Waiting for CatPaw sign-in…", "等待 Cline 授权确认…": "Waiting for Cline authorization confirmation…", "等待 CodeArts 登录完成…": "Waiting for CodeArts sign-in…", "等待 Google 授权完成…": "Waiting for Google authorization…", "等待 KukuAI 登录完成…": "Waiting for KukuAI sign-in…", "等待 LobsterAI 登录完成…": "Waiting for LobsterAI sign-in…", "等待 OfficeAce 登录完成…": "Waiting for OfficeAce sign-in…", "等待 OrcaTerm 登录完成…": "Waiting for OrcaTerm sign-in…", "等待 Qoder 授权完成…": "Waiting for Qoder authorization…", "等待 Trae 授权完成…": "Waiting for Trae authorization…", "等待 ZCode {label}授权完成…": "Waiting for ZCode {label} authorization…", "等待小浣熊登录完成…": "Waiting for Raccoon sign-in…", "等待登录完成…": "Waiting for sign-in…", "等待网页登录…": "Waiting for web login…", "粘贴 API Key": "Paste API Key", "粘贴 Accio 的 accessToken": "Paste the Accio accessToken", "粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
    这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。": "Paste Antigravity's Google refresh token (starts with 1//; pasting the whole line containing refresh_token= also works).
    This is a manual entry besides web login (for example, exported from the official IDE you are already signed in to). The refresh token does not expire, and the gateway renews the access token automatically when it expires; if the token is lost, you can only go through Google authorization again, so keep it safe.", "粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。": "Paste the Command Code API Key (starts with user_; get it from the official CLI's ~/.commandcode/auth.json or the web studio). Pasting the whole line / with quotes / with the Bearer prefix all work; the gateway strips it down to the bare key itself. The key does not expire and there is no renewal API, so if it stops working just get a new one. When adding, the gateway runs a lightweight liveness check; a failure does not affect adding.", "粘贴 Cookie": "Paste Cookie", "粘贴 Google refresh token": "Paste Google refresh token", "粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。": "Paste OrcaTerm's OAuth accessToken (a JWT with three dot-separated parts; available from the desktop client's data.bin or the web version's ot_session cookie). Pasting a whole line containing Bearer / ot_session= is fine too — the gateway strips it down to the bare token. After adding, the gateway renews it automatically (the upstream has a renewal API), so you do not need to follow the 2-hour expiry by hand.", "粘贴 Qoder 个人访问令牌(pt-…)": "Paste the Qoder personal access token (pt-…)", "粘贴 accessToken": "Paste accessToken", "粘贴 access_token(一长串 JWT)": "Paste access_token (a long JWT)", "粘贴 session cookie": "Paste session cookie", "粘贴 token / refreshToken": "Paste token / refreshToken", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "Paste a token starting with workos:… (the prefix is added automatically if missing)", "粘贴令牌对": "Paste token pair", "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "Paste a Zhipu Qingyan access token (a JWT with three dot-separated parts). How to get it: open chatglm.cn in a browser, sign in, then copy the value of the chatglm_token cookie from Application → Cookies in the developer tools (pasting the whole chatglm_token=…; line works too). It is verified against the upstream once after pasting, and the account is only saved if that passes; signed-in accounts get points and sign-in capability, and the gateway renews it automatically afterwards (the upstream has a renewal API).", "粘贴登录凭据": "Paste login credentials", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "What you paste is the login session (which is also the model gateway token). Loomy has no renewal API; the session is valid for about 14 days, so just sign in again after it expires.", "粘贴网关凭据": "Paste gateway credentials", "粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
    {siteNote}
    MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。": "Paste the value of the web session cookie monkeycode_ai_session (copying the whole Cookie in the browser and pasting it directly also works; the gateway extracts the value itself).
    {siteNote}
    MonkeyCode has no renewal API: the session is valid for about 30 days (a hard limit); once it expires, sign in to the web page again and paste it one more time.", "粘贴访问令牌": "Paste an access token", "系统浏览器": "System browser", "系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。": "The system browser can reuse an existing login state (recommended for the International edition); the embedded window is cleaner.", "系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表": "Opens in the system browser (reuses the signed-in Cline account); added to the list when done", "系统浏览器打开(复用已登录的 Google 账号);完成后自动加入列表": "Opens in the system browser (reuses the Google account already signed in); added to the list when done", "系统浏览器打开(复用已登录的 Zai / Google 账号);完成后自动加入列表": "Opens in the system browser (reuses the signed-in Zai / Google account); added to the list when done", "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "Opens in the system browser (reuses the signed-in Meituan account); added to the list when done", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "Opens in the system browser (reuses a signed-in account); added to the list when done", "系统默认浏览器": "System default browser", "续期令牌": "Refresh token", "编码套餐 API Key": "Coding Plan API Key", "网关 Secret": "Gateway secret", "网页登录": "Web login", "网页登录的打开方式": "How to open web login", "网页登录(Zai / Google)": "Web login (Zai / Google)", "自定义": "Custom", "自建聚合面板:填你自己的站点地址。余额查询用该账号的 API Key 打 {base}/v1/usage,不需要额外配置": "Self-hosted aggregation panel: fill in your own site address. Balance query calls {base}/v1/usage with this account's API key; no extra setup", "自建聚合面板:填你自己的站点地址;余额查询要另填 NewAPI 的个人访问令牌(见下方「余额查询」一段)": "Self-hosted aggregation panel: fill in your own site address; balance query additionally needs a NewAPI personal access token (see \"Balance query\" below)", "获取风控配置失败,将直接尝试领取:{message}": "Failed to get the risk-control config; trying to claim directly: {message}", "获取验证码": "Get code", "访客模式": "Guest mode", "访客模式(免登录)": "Guest mode (no sign-in)", "访问令牌": "Access token", "该上游无需鉴权(不发送鉴权头)": "This upstream needs no authentication (no auth headers are sent)", "该提供商": "This provider", "该提供商账号添加功能即将上线": "Account adding for this provider is coming soon", "该自定义提供商已不存在(可能已被删除),请刷新后重试": "This custom provider no longer exists (it may have been deleted); refresh and try again", "请先选择一个自定义提供商": "Select a custom provider first", "请在弹出的滑块中完成验证(官方要求的风控步骤)": "Complete the verification in the slider popup (a risk-control step required by the vendor)", "请填写 11 位大陆手机号": "Enter an 11-digit mainland China phone number", "请填写 6 位数字验证码": "Enter the 6-digit code", "请填写 Base URL": "Enter the Base URL", "请填写 {label}": "Enter {label}", "请填写名称": "Enter a name", "请完成验证…": "Complete the verification…", "读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows": "Reads and decrypts %APPDATA%/AutoClaw/auth.json; Windows only", "读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录": "Reads ~/.cline/data/settings/providers.json; requires being signed in to the Cline client", "读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录": "Reads ~/.meituan-catpaw/auth.json; requires being signed in to the CatPaw client", "读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录": "Reads the local KukuAI client login state (%APPDATA%\\baidugenflowpro\\Network\\Cookies); requires being signed in to the KukuAI client", "读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录": "Read the local OrcaTerm client sign-in state (%APPDATA%\\com.orcaterm-desktop.app\\data.bin); the OrcaTerm client must be signed in", "读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录": "Read the local Zhipu Qingyan client sign-in state (%APPDATA%\\chatglm\\Network\\Cookies); the client must be signed in", "读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。": "Reads the account currently logged in to the official client (Tencent WorkBuddy / WorkBuddy AI) on this machine: the newest session file in its credential directory is located automatically (the official client must be logged in on this machine). The gateway renews the token after import — no need to swap tokens manually with the 2-hour expiry.", "读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。": "Reads the local client's current login state (plaintext Cookie, no decryption needed). The Cookies file is exclusively locked while the client runs, so close the KukuAI client before importing.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。": "Reads the local client's current login state live each time (deleting this record does not affect the client's login state).", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后点「刷新 Token」同步。": "Reads the local client's current login state live each time (deleting this record does not affect the client's login state). After signing in again in the client, click \"Refresh token\" to sync.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后重新导入即可。": "Reads the local client's current login state live each time (deleting this record does not affect the client's login state). After signing in again in the client, just re-import.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。": "Reads the local client's current login state live each time (deleting this record does not affect the client's login state). Users who sign in to the client with Zai / Google should use this one.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。": "Reads the local client's current login state live each time (deleting this record does not affect the client's login state). This one is grouped under {label}.", "读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。": "Reads the OAuth token the local client is currently signed in with (plain-text JSON, no decryption needed). After importing, the gateway renews it automatically — no need to follow the client's 2-hour expiry by hand.", "读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。": "Read the current sign-in state of the local client. The client's cookies are DPAPI-encrypted (Chromium os_crypt), so this is Windows-only; the file may be unreadable while the client is running — quit the client and retry (or use \"Fill in credentials\" to paste a token instead).", "账号信息": "Account info", "账号信息不完整,请刷新后重试": "Account info is incomplete; refresh and try again", "账号已添加,但请留意:{warning}": "Account added, but note: {warning}", "账号版本": "Account edition", "账号类型": "Account type", "账号:{name}
    ": "Account: {name}
    ", "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 创建": "Routes through Inference Providers; create the Key at hf.co/settings/tokens", "还没有账号": "No accounts yet", "这一家当前不支持网页登录,请改用填写凭证": "This provider does not support web login right now; use Enter credentials instead", "这个账号当前没有可自动领的活动(只自动领每日登录赠送积分)": "This account has no campaigns that can be claimed automatically right now (only the daily login gift points are claimed automatically)", "这份额度走「活动套餐」通道:若转发时提示编码套餐已到期,": "This quota goes through the Start Plan channel: if forwarding reports that the Coding Plan has expired, ", "这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。": "This is for the China site (monkeycode-ai.com) account: paste the China site's session. The international site (monkeycode-ai.net) is another provider with its own account; credentials are not interchangeable between the two.", "这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。": "This is for the international site (monkeycode-ai.net) account: paste the international site's session. The China site (monkeycode-ai.com) is another provider with its own account; credentials are not interchangeable between the two.", "选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "Select the CatPaw client's login-state file auth.json (Windows: C:\\Users\\\\.meituan-catpaw\\auth.json; macOS / Linux: ~/.meituan-catpaw/auth.json). After import the credentials live on the gateway and can be forwarded independently — no longer dependent on this machine's client.", "选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。": "Select the Cline client's login-state file providers.json (Windows: C:\\Users\\\\.cline\\data\\settings\\providers.json; macOS / Linux: ~/.cline/data/settings/providers.json). After import the credentials live on the gateway and can be forwarded independently — no longer dependent on this machine's client; this record goes into {label}.", "选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。": "Select the KukuAI client's Cookies file (%APPDATA%\\baidugenflowpro\\Network\\Cookies): it is a **binary** SQLite database (no extension, not JSON). The import reads a copy of the file you selected and does not compete with the running client for the lock, so **you do not need to close the client first**. After import the credentials live on the gateway and can be forwarded independently.", "选择凭证文件导入": "Import from credential file", "选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。": "Select the official client's session credential file (workbuddy-desktop.info / workbuddy-desktop-ai.info) or an account JSON exported by wb2api (nested or flat).", "选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "Select the Raccoon client's login-state file auth.json (Windows: C:\\Users\\\\.box-agent\\config\\auth.json; macOS / Linux: ~/.box-agent/config/auth.json). After import the credentials live on the gateway and can be forwarded independently — no longer dependent on this machine's client.", "选择提供商": "Select provider", "选择的文件不是合法 JSON": "The selected file is not valid JSON", "选择要添加账号的提供商": "Select the provider to add an account for", "重新扫描": "Rescan", "鉴权": "Authentication", "阿里云 Token Plan 套餐(国际站)": "Alibaba Cloud Token Plan (International site)", "阿里云百炼 Coding 套餐(国内站)": "Alibaba Cloud Model Studio Coding plan (China site)", "阿里云百炼 Coding 套餐(国际站)": "Alibaba Cloud Model Studio Coding plan (International site)", "阿里云百炼模型服务(国际站,OpenAI 兼容模式)": "Alibaba Cloud Model Studio model service (International site, OpenAI-compatible mode)", "需要图形验证码:登录窗口已弹出,请填写后继续": "Captcha required: the sign-in window has opened; fill it in to continue", "预置 API": "Preset API", "领取 CodeArts 每日福利": "Claim CodeArts daily welfare", "领取失败:{message}": "Claim failed: {message}", "领取并让官方确认": "Claim and await confirmation", "风控验证配置不完整,请稍后重试": "Risk-control verification config is incomplete; try again later", "验证码": "Verification code", "验证码已发送。收到后填入下方并点「登录并添加」": "Code sent. Enter it below and click \"Sign in and add\"", "验证码已发送,请查看手机短信": "Code sent; check your SMS", "验证码已发送,请查看手机短信。填入下方并点「登录并添加」即完成登录": "Code sent; check your SMS. Enter it below and click \"Sign in and add\" to finish signing in", "验证码已发送,请查看短信": "Code sent; check your messages", "验证码校验失败,请重试": "Code verification failed; try again", "验证码校验超时,请重试": "Code verification timed out; try again", "验证码组件不可用,请重试": "Verification component unavailable; try again", "验证码组件初始化超时,请重试": "Verification component initialization timed out; try again", "验证码组件加载失败,请检查网络后重试": "Verification component failed to load; check your network and try again", "验证码组件加载异常,请重试": "Verification component encountered a loading error; try again", "验证码组件加载超时,请检查网络后重试": "Verification component timed out while loading; check your network and try again", "验证码组件未加载,请重启应用后重试": "Verification component not loaded; restart the app and try again", "(免费额度池,模型名带 cline-free/ 前缀)。": " (free quota pool; model names carry the cline-free/ prefix).", "(其中 {n} 把恢复为不限制)": " ({n} of which are now unrestricted)", "(必填)": " (Required)", "(有效期 {window})": "(valid {window})", "(订阅池,模型名带 cline-pass/ 前缀,需要账号有对应订阅)。": " (subscription pool; model names carry the cline-pass/ prefix and the account needs the matching subscription).", ",下一次最快 {minutes} 分钟后": ", earliest next attempt in {minutes} min", ",并从 {n} 把 Key 的可用提供商里移除": ", and removed it from the allowed providers of {n} keys" }); diff --git a/desktop-tauri/ui/i18n/en/checkin.js b/desktop-tauri/ui/i18n/en/checkin.js index 0584ca2d..e2c2f9dd 100644 --- a/desktop-tauri/ui/i18n/en/checkin.js +++ b/desktop-tauri/ui/i18n/en/checkin.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:en/checkin —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · ": " · ", "SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新": "SOLO checkin_credits claims · reset at midnight (calendar day)", "UMID 组件安装失败:{error}": "UMID component installation failed: {error}", "WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细": "WorkBuddy CN · Query the task tree, then light up / claim in one click · includes the school-season event and cat travel · click a row for details", "WorkBuddy 国际版保活模型,按顺序回退": "WorkBuddy International keep-alive models, tried in order", "WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关": "Free models for WorkBuddy International daily activity · one global setting, unrelated to schedules", "{date}(每天 {time})": "{date} (daily at {time})", "{label} ×{count}({reason})": "{label} ×{count} ({reason})", "{n} 项可完成": "{n} to complete", "{n} 项待领": "{n} to claim", "{total} 账号 · {done} 已签": "{done} of {total} checked in", "{total} 项任务 · {pending} 项可完成": "{total} tasks · {pending} to complete", "✅ UMID 组件已安装,下轮签到即可领取国际版积分": "✅ UMID component installed — the next check-in can claim International points", "✅ {message}": "✅ {message}", "✅ 定时签到已开启": "✅ Scheduled check-in enabled", "✅ 开学季任务执行完成": "✅ School-season tasks completed", "✅ 成长任务执行完成:积分 +{credit} 能量 +{energy}": "✅ Growth tasks completed: credits +{credit}, energy +{energy}", "✅ 新手福利领取完成,累计 {n} 积分": "✅ Starter rewards claimed, {n} points earned in total", "✅ 签到完成:{succeeded}/{total} 个账号成功领取{active}": "✅ Check-in complete: {succeeded}/{total} accounts claimed successfully{active}", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空提交恢复缺省({defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "The \"keep-alive model chain\" is the free models used to keep WorkBuddy International accounts active during daily activity runs — tried in order, stops at the first success; submit empty to restore the default ({defaults}). Same-day dedup + idempotent claiming: repeat runs only return \"already claimed\" and never add points twice.", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "The \"keep-alive model chain\" is the free models used to keep WorkBuddy International accounts active during daily activity runs — tried in order, stops at the first success; clearing the list and saving restores the default chain (current default: {defaults}). Same-day dedup + idempotent claiming: repeat runs only return \"already claimed\" and never add points twice.", "「免费领积分」的每日任务 · 逐个领取": "\"Free points\" daily tasks · claimed one by one", "一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单": "One-time rewards · queried and claimed automatically after check-in; once claimed they are recorded locally and no longer queried upstream · click a row to expand the task list", "一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证": "Claims all of the day’s rewards in one action (daily check-in + newcomer gift) · idempotency guaranteed by the upstream", "一键安装组件": "Install component", "一键完成": "Complete all", "一键领取({n})": "Claim all ({n})", "上次执行:{line}": "Last run: {line}", "上次签到时间": "Last check-in", "上次领取 {time} · 领取需通过滑块验证码": "Last claimed {time} · claim requires passing a slider CAPTCHA", "下次 {date}": "Next {date}", "下次 {date} · 点此管理条目": "Next {date} · click to manage schedules", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它与 ZCode 的每日/套餐福利领取见「活动福利」。": "Not in daily check-in: {list}. CodeArts one-time rewards are under \"Starter rewards\" below; its and ZCode's daily/plan benefits are claimed under \"Activity rewards\".", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。": "Not in daily check-in: {list}. CodeArts one-time rewards are under \"Starter rewards\" below, and its daily welfare is right above under \"Daily check-in\"; ZCode plan claims are under \"Activity rewards\".", "与国内版同一套任务接口 · 站点不同": "Same task API as the China edition · different site", "今天已领 {n} 份": "Claimed {n} today", "今天未领": "Not claimed today", "今天(北京时间 {day})已领取 · 官方确认到账 {n} 项 · 明天可再领": "Claimed today (Beijing time {day}) · {n} items confirmed received · can claim again tomorrow", "今日已执行": "Ran today", "今日已派出": "Sent out today", "今日已领取": "Claimed today", "今日签到": "Today", "今日签到进度": "Today's check-in progress", "从清单里移除": "Remove from the list", "保活": "Keep-alive", "保活 {n}": "{n} kept alive", "保活+领取": "Keep-alive + claim", "保活中…": "Keeping alive…", "保活模型链": "Keep-alive model chain", "全选": "Select all", "全部 {n} 家": "All {n} providers", "全部完成": "All done", "全部查询": "Query all", "全部结果": "All outcomes", "全部账号均可签到": "All accounts can check in", "全部领取": "All claimed", "关着:所有条目都不执行": "Off: no schedule runs", "删除失败:{error}": "Delete failed: {error}", "删除定时条目": "Delete schedule", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活并在网页通道建会话点亮日活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "At the scheduled time, automatically checks in the enabled accounts of the ticked providers (WorkBuddy CN uses the daily check-in API; the international version uses the daily-activity quest: probe the event, claim the daily-active reward, then keep alive with the free model and open a session over the web channel to light up the daily activity; Raccoon uses the desktop daily-points flow; AutoClaw uses the official client's daily check-in task; Qoder CN claims via the event, and accounts without an assigned event get a neutral notice; Qoder international needs a device risk-control identity (generated by the UMID component of the local Qoder client / qodercli) before the \"Daily 100 Credits\" event is even visible — without the component you get a clear explanation instead of an error; Trae claims SOLO's daily check-in points (reset at midnight), and when the upstream has no event for the account or the credentials lack a device ID you get \"not claimed + reason\" instead of an error; Loomy claims the first-login points once a day per account; KukuAI claims the \"Free points\" daily task; CodeArts claims the day's operational perks (claim → confirm → verify by re-reading; automatic runs are rate-limited to 6 write requests a day, 10 minutes apart); OfficeAce claims all of the day's rewards in one go (daily check-in + newcomer gift)). If the machine was off at the scheduled time, a catch-up check-in runs at boot, so nothing is missed. Every provider's check-in API is idempotent — running it again never claims twice.", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "At the scheduled time, auto check-in runs for the available accounts of the selected providers (WorkBuddy China uses the daily check-in API; International uses daily activity tasks — detect the campaign, claim the daily-activity reward, then keep the account alive with free models; Raccoon uses the desktop daily points flow; AutoClaw uses the official client's daily check-in task; Qoder China uses campaign claims, and accounts with no campaign assigned get a neutral notice; Qoder International needs a device risk-control identity first (generated by the UMID component of the local Qoder client / qodercli) to see the \"Daily 100 Credits\" campaign, and a missing component yields a clear explanation instead of an error; Trae claims SOLO's daily check-in points (reset at midnight by calendar day), and when the upstream has no campaign for the account or the credential has no device ID the result is \"not claimed + the specific reason\" instead of an error; Loomy awards first-login points once a day per account; KukuAI claims the \"Free points\" daily tasks; CodeArts claims that day's campaign welfare (claim → confirm → read back to verify; automatic runs are limited to 6 write attempts per day with a 10-minute gap); OfficeAce claims all of that day's rewards in one action (daily check-in + newcomer gift)). If the machine was off at the scheduled time, the missed run is made up automatically after it starts — nothing is skipped just because the machine was off. Every provider's check-in API is idempotent, so running it again never claims twice.", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "At the scheduled time, auto check-in runs for the available accounts of the selected providers (WorkBuddy China uses the daily check-in API; International uses daily activity tasks — detect the campaign, claim the daily-activity reward, then keep the account alive with free models; Raccoon uses the desktop daily points flow; AutoClaw uses the official client's daily check-in task; Qoder China uses campaign claims, and accounts with no campaign assigned get a neutral notice; Qoder International needs a device risk-control identity first (generated by the UMID component of the local Qoder client / qodercli) to see the \"Daily 100 Credits\" campaign, and a missing component yields a clear explanation instead of an error; Trae claims SOLO's daily check-in points (reset at midnight by calendar day), and when the upstream has no campaign for the account or the credential has no device ID the result is \"not claimed + the specific reason\" instead of an error; Loomy awards first-login points once a day per account; KukuAI claims the \"Free points\" daily tasks; OfficeAce claims all of that day's rewards in one action (daily check-in + newcomer gift)). If the machine was off at the scheduled time, the missed run is made up automatically after it starts — nothing is skipped just because the machine was off. Every provider's check-in API is idempotent, so running it again never claims twice.", "到点时逐个签到,已勾 {n} / {total} 家": "Checked {n} of {total} — each is checked in at the scheduled time", "北京时间 · 错过时点开机后会自动补签": "Beijing time · missed runs are made up automatically after startup", "去领取": "Claim now", "另有 {n} 个账号不参与每日签到": "{n} more accounts are not part of daily check-in", "只领取已完成任务的奖励(幂等,重复点不会重复入账)": "Claim rewards of completed tasks only (idempotent; repeated clicks will not double-credit)", "可完成": "Can complete", "可派出": "Can send out", "可选 · 缺省显示时刻": "Optional · defaults to the time", "可领奖": "Can claim", "后端未返回定时签到数据": "Backend returned no schedule data", "后端未返回签到中心数据": "The backend did not return check-in center data", "国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。": "International check-in requires the UMID component to generate a device risk-control identity (not detected on this machine).", "在途": "Traveling", "填入缺省链": "Fill in default chain", "填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)": "Pick a time and the providers, then click Save; the name is optional (the time is shown when empty)", "失败 {n}": "{n} failed", "套餐明细": "Plan details", "安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。": "Install the Qoder client or qodercli to enable it (no need to restart the gateway).", "安装中…": "Installing…", "官方客户端的每日签到任务": "Official client's daily check-in task", "定时签到": "Scheduled check-in", "定时签到已关闭": "Scheduled check-in disabled", "尚未查询": "Not queried yet", "尚未查询任务状态": "Task status not queried yet", "巡检一次": "Run one round", "已保存:{label}": "Saved: {label}", "已保存:保活模型链": "Saved: Keep-alive model chain", "已保存:定时条目": "Saved: schedule", "已删除定时条目": "Schedule deleted", "已完成活跃上报(连登已点亮),但活动未开启(本次没有可领的奖励)": "Activity reported (streak lit up), but the campaign is not active (nothing to claim this time)", "已抽 {n} 次": "Drew {n} times", "已有结果 {checked} / 共 {total} 个账号": "Results for {checked} / {total} accounts", "已签": "Checked in", "已领 {claimed}/{total} · 累计 {earned} 积分": "Claimed {claimed}/{total} · {earned} points earned", "已领养": "Adopted", "已领取": "Claimed", "带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件": "Claim \"Daily 100 Credits\" with a device risk-control identity · requires the UMID component", "开学季执行失败:{error}": "School-season run failed: {error}", "开学季执行完成:{message}": "School-season run completed: {message}", "开学季活动": "School-season event", "开着:启用的条目按各自时刻执行": "On: enabled schedules run at their own times", "待签": "Not checked in", "待领新手福利": "Starter rewards to claim", "总开关": "Master switch", "总开关已关闭 · 在「定时签到」页打开": "Master switch is off · enable it on the \"Scheduled check-in\" tab", "成功 {n}": "{n} succeeded", "成长任务": "Growth tasks", "成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}": "Growth tasks finished ({done} succeeded, {failed} failed): credits +{credit}, energy +{energy}", "成长任务执行失败:{error}": "Growth task run failed: {error}", "执行失败:{error}": "Run failed: {error}", "执行并抽奖": "Run & draw", "执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)": "Run school-season tasks and spend all draw chances (share / chat / expert events)", "把缺省链填进清单,再点「保存」生效": "Puts the default chain into the list; click Save to apply", "抽奖机会 {n} 次": "{n} draw chances", "按住拖动调整顺序": "Press and drag to reorder", "按住拖动,调整这个模型在链里的顺序": "Press and drag to change this model's position in the chain", "按序逐个尝试,共 {n} 个": "{n} models, tried in order", "按结果筛选": "Filter by outcome", "探测 + 领取 + 保活 · 与定时签到同一套组合": "Detect + claim + keep-alive · same combo as scheduled check-in", "探测活动并领取日活奖励 · 不做保活": "Detect the campaign and claim the daily-activity reward · no keep-alive", "接单 + 点亮 + 领奖(只处理已实测沉淀的任务)": "Accept + light up + claim (only tasks with verified recipes)", "搜账号 / 收益 / 错误 / 触发原因": "Search account / gain / error / trigger reason", "收起新建": "Close new entry", "新建定时条目": "New scheduled entry", "新手福利": "Starter rewards", "新手福利有 {n} 项领取失败,可重试": "{n} starter reward(s) failed to claim — you can retry", "无": "None", "无独立签到接口 · 每天替账号打一次首次登录积分": "No dedicated check-in API · awards first-login points once a day per account", "无账号": "No accounts", "暂不可领": "Not claimable yet", "暂不支持": "Not supported yet", "暂无启用的条目 · 点此添加": "No enabled schedules · click to add one", "有未保存的改动": "Unsaved changes", "未勾选提供商": "No provider selected", "未开启": "Off", "未解锁": "Locked", "未领养(领养需先完成当日对话量)": "Not adopted (adoption requires the daily chat quota first)", "未领取": "Not claimed", "本地台账里今天还没领过;活动期内每天发一份新套餐": "No claim recorded locally for today yet; a new plan is issued every day during the event", "本地台账里今天(北京时间)领过的套餐:{plans}": "Plans claimed today (Beijing time) according to the local record: {plans}", "条启用": "enabled", "查询于 {time}": "Queried at {time}", "查询任务": "Query tasks", "查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)": "Query the task tree, then light up / claim in one click (includes school-season and cat travel)", "查询失败:{error}": "Query failed: {error}", "桌面端每日积分链路": "Desktop daily points flow", "正在读取定时签到数据…": "Loading schedule data…", "正在读取签到数据…": "Loading check-in data…", "每天 {time}": "Daily at {time}", "每天 {time} · {scope}": "Daily {time} · {scope}", "每天时刻": "Daily time", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "One row per account per check-in run · outcomes and gains as recorded by the backend", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "Daily activity tasks · a web-app conversation lights up the daily activity, only claims add points · the two can run separately", "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "Daily sign-in points · claims member activities along the way · guest accounts have no points system (skipped)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "Daily welfare · claim → confirm → read back to verify · resets on the Beijing calendar day", "每日签到": "Daily check-in", "每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签": "Each schedule has its own time and scope · same-day dedup per entry · missed runs are made up after startup", "每账号一条明细 · 点此查看「签到记录」": "One row per account · open \"Check-in records\"", "没有可领套餐的账号(ZCode 未添加)。": "No accounts with claimable plans (ZCode not added).", "没有可领福利的账号(CodeArts / ZCode 未添加)。": "No accounts with claimable rewards (CodeArts / ZCode not added).", "没有支持成长任务的账号(需要 WorkBuddy 国内版)。": "No accounts support growth tasks (WorkBuddy CN required).", "没有支持新手福利的账号": "No accounts support starter rewards", "没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。": "No accounts support starter rewards — currently Loomy, Raccoon and CodeArts.", "没有符合筛选条件的签到记录": "No check-in records match the current filters", "没有需要执行的任务": "Nothing to run", "活动福利": "Event rewards", "活动(campaign)领取 · 每天 10:00 刷新": "Campaign claims · refreshed daily at 10:00", "活跃上报与保活均未成功,且活动未开启": "Both the activity report and keep-alive failed, and the campaign is not active", "活跃上报完成,但保活未成功且活动未开启": "Activity reported, but keep-alive failed and the campaign is not active", "活跃上报完成,但活动未开启": "Activity reported, but the campaign is not active", "活跃上报成功": "Activity reported", "活跃保活失败": "Keep-alive failed", "活跃保活完成": "Keep-alive complete", "活跃保活完成,但活动未开启": "Keep-alive complete, but the campaign is not active", "活跃保活完成,但活跃上报失败且活动未开启": "Keep-alive complete, but the activity report failed and the campaign is not active", "添加条目": "Add schedule", "添加模型": "Add model", "清单为空 —— 保存后恢复缺省链": "The list is empty — saving restores the default chain", "清单里已经有「{name}」": "The list already contains \"{name}\"", "清空失败:{error}": "Clear failed: {error}", "清空签到记录": "Clear check-in records", "点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领": "Click \"Plan details\" to see which plans you got and when they take effect; you can claim again every day during the event", "点击行展开账号明细 · 单账号签到不受范围限制": "Click a row to expand account details · single-account check-in is not limited by scope", "独立链路 · 只提供手动入口 · 不进自动签到": "Separate flow · manual entry only · not part of auto check-in", "猫猫旅行": "Cat travel", "猫猫旅行失败:{error}": "Cat travel failed: {error}", "猫猫旅行失败:{message}": "Cat travel failed: {message}", "猫猫旅行:{message}": "Cat travel: {message}", "用免费模型与网页会话维持账号活跃 · 不领取奖励": "Keep the account active with free models and a web-app conversation · no rewards claimed", "确定删除「{name}」?删除后到点不再执行。": "Delete \"{name}\"? It will no longer run at its scheduled time.", "确定清空全部签到记录?清空后无法恢复。": "Clear ALL check-in records? This cannot be undone.", "确定清空当前筛选出的签到记录?清空后无法恢复。": "Clear the filtered check-in records? This cannot be undone.", "移除 {name}": "Remove {name}", "立即全部签到": "Check in all now", "签到": "Check in", "签到中…": "Checking in…", "签到中心分区": "Check-in center sections", "签到失败:{error}": "Check-in failed: {error}", "签到完成:{succeeded}/{total} 成功{active},{failed} 个失败": "Check-in complete: {succeeded}/{total} succeeded{active}, {failed} failed", "签到成功": "Check-in succeeded", "签到提供商": "Check-in providers", "签到范围": "Check-in scope", "签到触发时刻": "Check-in trigger time", "签到记录": "Check-in records", "签到记录时间范围": "Check-in records time range", "签到记录表": "Check-in records table", "累计签到记录": "Total check-in records", "结算于 {time}": "Settled at {time}", "能量 {n}": "Energy {n}", "腾讯每日签到接口 · 仅国内版": "Tencent daily check-in API · China edition only", "至少勾一家才能保存(不勾等于这条条目不签到)": "Select at least one provider to save (an empty scope means this entry checks in nothing)", "读取定时签到数据失败:{error}": "Failed to load schedule data: {error}", "读取签到数据失败:{error}": "Failed to load check-in data: {error}", "读取签到记录失败:{error}": "Failed to load check-in records: {error}", "跳过 {n}": "{n} skipped", "运营活动交付(领取 → 确认 → 回读核实)· 领的是套餐赠送积分": "Campaign delivery (claim → confirm → read back to verify) · these are plan bonus points", "还没执行过": "Never run yet", "还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。": "No schedules yet — click \"Add schedule\" to create one, or check in manually on the \"Check-in center\" tab.", "还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。": "No check-in records yet — details appear here after a manual check-in or a scheduled run.", "连登 {n} 天": "{n}-day streak", "部分": "Partial", "重签": "Check in again", "重试": "Retry", "限时体验套餐 · 领取需滑块验证码 · 逐份查看与领取": "Limited-time trial plan · claim requires a slider CAPTCHA · view and claim one by one", "限时体验套餐(start-plan),活动期内每天一份 · 领取需滑块验证码": "Limited-time trial plan (start-plan), one per day during the campaign · claim requires a slider CAPTCHA", "需真实操作": "Requires real action", "非活动期": "Event inactive", "项": "items", "领取": "Claim", "领取中": "Claiming", "领取中…": "Claiming…", "领取失败": "Claim failed", "领取失败:{error}": "Claim failed: {error}", "(共 {n} 个账号)": " ({n} accounts total)", "(能量 / 连登读数本次未取到)": "(Energy / streak readings unavailable this time)", ",日活保活 {n} 个": ", {n} kept alive" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · ": " · ", "SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新": "SOLO checkin_credits claims · reset at midnight (calendar day)", "UMID 组件安装失败:{error}": "UMID component installation failed: {error}", "WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细": "WorkBuddy CN · Query the task tree, then light up / claim in one click · includes the school-season event and cat travel · click a row for details", "WorkBuddy 国际版保活模型,按顺序回退": "WorkBuddy International keep-alive models, tried in order", "WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关": "Free models for WorkBuddy International daily activity · one global setting, unrelated to schedules", "{date}(每天 {time})": "{date} (daily at {time})", "{label} ×{count}({reason})": "{label} ×{count} ({reason})", "{n} 项可完成": "{n} to complete", "{n} 项待领": "{n} to claim", "{total} 账号 · {done} 已签": "{done} of {total} checked in", "{total} 项任务 · {pending} 项可完成": "{total} tasks · {pending} to complete", "✅ UMID 组件已安装,下轮签到即可领取国际版积分": "✅ UMID component installed — the next check-in can claim International points", "✅ {message}": "✅ {message}", "✅ 定时签到已开启": "✅ Scheduled check-in enabled", "✅ 开学季任务执行完成": "✅ School-season tasks completed", "✅ 成长任务执行完成:积分 +{credit} 能量 +{energy}": "✅ Growth tasks completed: credits +{credit}, energy +{energy}", "✅ 新手福利领取完成,累计 {n} 积分": "✅ Starter rewards claimed, {n} points earned in total", "✅ 签到完成:{succeeded}/{total} 个账号成功领取{active}": "✅ Check-in complete: {succeeded}/{total} accounts claimed successfully{active}", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空提交恢复缺省({defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "The \"keep-alive model chain\" is the free models used to keep WorkBuddy International accounts active during daily activity runs — tried in order, stops at the first success; submit empty to restore the default ({defaults}). Same-day dedup + idempotent claiming: repeat runs only return \"already claimed\" and never add points twice.", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "The \"keep-alive model chain\" is the free models used to keep WorkBuddy International accounts active during daily activity runs — tried in order, stops at the first success; clearing the list and saving restores the default chain (current default: {defaults}). Same-day dedup + idempotent claiming: repeat runs only return \"already claimed\" and never add points twice.", "「免费领积分」的每日任务 · 逐个领取": "\"Free points\" daily tasks · claimed one by one", "一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单": "One-time rewards · queried and claimed automatically after check-in; once claimed they are recorded locally and no longer queried upstream · click a row to expand the task list", "一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证": "Claims all of the day’s rewards in one action (daily check-in + newcomer gift) · idempotency guaranteed by the upstream", "一键安装组件": "Install component", "一键完成": "Complete all", "一键领取({n})": "Claim all ({n})", "上次执行:{line}": "Last run: {line}", "上次签到时间": "Last check-in", "上次领取 {time} · 领取需通过滑块验证码": "Last claimed {time} · claim requires passing a slider CAPTCHA", "下次 {date}": "Next {date}", "下次 {date} · 点此管理条目": "Next {date} · click to manage schedules", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它与 ZCode 的每日/套餐福利领取见「活动福利」。": "Not in daily check-in: {list}. CodeArts one-time rewards are under \"Starter rewards\" below; its and ZCode's daily/plan benefits are claimed under \"Activity rewards\".", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。": "Not in daily check-in: {list}. CodeArts one-time rewards are under \"Starter rewards\" below, and its daily welfare is right above under \"Daily check-in\"; ZCode plan claims are under \"Activity rewards\".", "与国内版同一套任务接口 · 站点不同": "Same task API as the China edition · different site", "今天已领 {n} 份": "Claimed {n} today", "今天未领": "Not claimed today", "今天(北京时间 {day})已领取 · 官方确认到账 {n} 项 · 明天可再领": "Claimed today (Beijing time {day}) · {n} items confirmed received · can claim again tomorrow", "今日已执行": "Ran today", "今日已派出": "Sent out today", "今日已领取": "Claimed today", "今日签到": "Today", "今日签到进度": "Today's check-in progress", "从清单里移除": "Remove from the list", "保活": "Keep-alive", "保活 {n}": "{n} kept alive", "保活+领取": "Keep-alive + claim", "保活中…": "Keeping alive…", "保活模型链": "Keep-alive model chain", "全选": "Select all", "全部 {n} 家": "All {n} providers", "全部完成": "All done", "全部查询": "Query all", "全部结果": "All outcomes", "全部账号均可签到": "All accounts can check in", "全部领取": "All claimed", "关着:所有条目都不执行": "Off: no schedule runs", "删除失败:{error}": "Delete failed: {error}", "删除定时条目": "Delete schedule", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活并在网页通道建会话点亮日活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "At the scheduled time, automatically checks in the enabled accounts of the ticked providers (WorkBuddy CN uses the daily check-in API; the international version uses the daily-activity quest: probe the event, claim the daily-active reward, then keep alive with the free model and open a session over the web channel to light up the daily activity; Raccoon uses the desktop daily-points flow; AutoClaw uses the official client's daily check-in task; Qoder CN claims via the event, and accounts without an assigned event get a neutral notice; Qoder international needs a device risk-control identity (generated by the UMID component of the local Qoder client / qodercli) before the \"Daily 100 Credits\" event is even visible — without the component you get a clear explanation instead of an error; Trae claims SOLO's daily check-in points (reset at midnight), and when the upstream has no event for the account or the credentials lack a device ID you get \"not claimed + reason\" instead of an error; Loomy claims the first-login points once a day per account; KukuAI claims the \"Free points\" daily task; CodeArts claims the day's operational perks (claim → confirm → verify by re-reading; automatic runs are rate-limited to 6 write requests a day, 10 minutes apart); OfficeAce claims all of the day's rewards in one go (daily check-in + newcomer gift)). If the machine was off at the scheduled time, a catch-up check-in runs at boot, so nothing is missed. Every provider's check-in API is idempotent — running it again never claims twice.", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "At the scheduled time, auto check-in runs for the available accounts of the selected providers (WorkBuddy China uses the daily check-in API; International uses daily activity tasks — detect the campaign, claim the daily-activity reward, then keep the account alive with free models; Raccoon uses the desktop daily points flow; AutoClaw uses the official client's daily check-in task; Qoder China uses campaign claims, and accounts with no campaign assigned get a neutral notice; Qoder International needs a device risk-control identity first (generated by the UMID component of the local Qoder client / qodercli) to see the \"Daily 100 Credits\" campaign, and a missing component yields a clear explanation instead of an error; Trae claims SOLO's daily check-in points (reset at midnight by calendar day), and when the upstream has no campaign for the account or the credential has no device ID the result is \"not claimed + the specific reason\" instead of an error; Loomy awards first-login points once a day per account; KukuAI claims the \"Free points\" daily tasks; CodeArts claims that day's campaign welfare (claim → confirm → read back to verify; automatic runs are limited to 6 write attempts per day with a 10-minute gap); OfficeAce claims all of that day's rewards in one action (daily check-in + newcomer gift)). If the machine was off at the scheduled time, the missed run is made up automatically after it starts — nothing is skipped just because the machine was off. Every provider's check-in API is idempotent, so running it again never claims twice.", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "At the scheduled time, auto check-in runs for the available accounts of the selected providers (WorkBuddy China uses the daily check-in API; International uses daily activity tasks — detect the campaign, claim the daily-activity reward, then keep the account alive with free models; Raccoon uses the desktop daily points flow; AutoClaw uses the official client's daily check-in task; Qoder China uses campaign claims, and accounts with no campaign assigned get a neutral notice; Qoder International needs a device risk-control identity first (generated by the UMID component of the local Qoder client / qodercli) to see the \"Daily 100 Credits\" campaign, and a missing component yields a clear explanation instead of an error; Trae claims SOLO's daily check-in points (reset at midnight by calendar day), and when the upstream has no campaign for the account or the credential has no device ID the result is \"not claimed + the specific reason\" instead of an error; Loomy awards first-login points once a day per account; KukuAI claims the \"Free points\" daily tasks; OfficeAce claims all of that day's rewards in one action (daily check-in + newcomer gift)). If the machine was off at the scheduled time, the missed run is made up automatically after it starts — nothing is skipped just because the machine was off. Every provider's check-in API is idempotent, so running it again never claims twice.", "到点时逐个签到,已勾 {n} / {total} 家": "Checked {n} of {total} — each is checked in at the scheduled time", "北京时间 · 错过时点开机后会自动补签": "Beijing time · missed runs are made up automatically after startup", "去领取": "Claim now", "另有 {n} 个账号不参与每日签到": "{n} more accounts are not part of daily check-in", "只领取已完成任务的奖励(幂等,重复点不会重复入账)": "Claim rewards of completed tasks only (idempotent; repeated clicks will not double-credit)", "可完成": "Can complete", "可派出": "Can send out", "可选 · 缺省显示时刻": "Optional · defaults to the time", "可领奖": "Can claim", "后端未返回定时签到数据": "Backend returned no schedule data", "后端未返回签到中心数据": "The backend did not return check-in center data", "国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。": "International check-in requires the UMID component to generate a device risk-control identity (not detected on this machine).", "在途": "Traveling", "填入缺省链": "Fill in default chain", "填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)": "Pick a time and the providers, then click Save; the name is optional (the time is shown when empty)", "失败 {n}": "{n} failed", "套餐明细": "Plan details", "安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。": "Install the Qoder client or qodercli to enable it (no need to restart the gateway).", "安装中…": "Installing…", "官方客户端的每日签到任务": "Official client's daily check-in task", "定时签到": "Scheduled check-in", "定时签到已关闭": "Scheduled check-in disabled", "尚未查询": "Not queried yet", "尚未查询任务状态": "Task status not queried yet", "巡检一次": "Run one round", "已保存:{label}": "Saved: {label}", "已保存:保活模型链": "Saved: Keep-alive model chain", "已保存:定时条目": "Saved: schedule", "已删除定时条目": "Schedule deleted", "已完成活跃上报(连登已点亮),但活动未开启(本次没有可领的奖励)": "Activity reported (streak lit up), but the campaign is not active (nothing to claim this time)", "已抽 {n} 次": "Drew {n} times", "已有结果 {checked} / 共 {total} 个账号": "Results for {checked} / {total} accounts", "已签": "Checked in", "已领 {claimed}/{total} · 累计 {earned} 积分": "Claimed {claimed}/{total} · {earned} points earned", "已领养": "Adopted", "已领取": "Claimed", "带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件": "Claim \"Daily 100 Credits\" with a device risk-control identity · requires the UMID component", "开学季执行失败:{error}": "School-season run failed: {error}", "开学季执行完成:{message}": "School-season run completed: {message}", "开学季活动": "School-season event", "开着:启用的条目按各自时刻执行": "On: enabled schedules run at their own times", "待签": "Not checked in", "待领新手福利": "Starter rewards to claim", "总开关": "Master switch", "总开关已关闭 · 在「定时签到」页打开": "Master switch is off · enable it on the \"Scheduled check-in\" tab", "成功 {n}": "{n} succeeded", "成长任务": "Growth tasks", "成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}": "Growth tasks finished ({done} succeeded, {failed} failed): credits +{credit}, energy +{energy}", "成长任务执行失败:{error}": "Growth task run failed: {error}", "执行失败:{error}": "Run failed: {error}", "执行并抽奖": "Run & draw", "执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)": "Run school-season tasks and spend all draw chances (share / chat / expert events)", "把缺省链填进清单,再点「保存」生效": "Puts the default chain into the list; click Save to apply", "抽奖机会 {n} 次": "{n} draw chances", "按住拖动调整顺序": "Press and drag to reorder", "按住拖动,调整这个模型在链里的顺序": "Press and drag to change this model's position in the chain", "按序逐个尝试,共 {n} 个": "{n} models, tried in order", "按结果筛选": "Filter by outcome", "探测 + 领取 + 保活 · 与定时签到同一套组合": "Detect + claim + keep-alive · same combo as scheduled check-in", "探测活动并领取日活奖励 · 不做保活": "Detect the campaign and claim the daily-activity reward · no keep-alive", "接单 + 点亮 + 领奖(只处理已实测沉淀的任务)": "Accept + light up + claim (only tasks with verified recipes)", "搜账号 / 收益 / 错误 / 触发原因": "Search account / gain / error / trigger reason", "收起新建": "Close new entry", "新建定时条目": "New scheduled entry", "新手福利": "Starter rewards", "新手福利有 {n} 项领取失败,可重试": "{n} starter reward(s) failed to claim — you can retry", "无": "None", "无独立签到接口 · 每天替账号打一次首次登录积分": "No dedicated check-in API · awards first-login points once a day per account", "无账号": "No accounts", "暂不可领": "Not claimable yet", "暂不支持": "Not supported yet", "暂无启用的条目 · 点此添加": "No enabled schedules · click to add one", "有未保存的改动": "Unsaved changes", "未勾选提供商": "No provider selected", "未开启": "Off", "未解锁": "Locked", "未领养(领养需先完成当日对话量)": "Not adopted (adoption requires the daily chat quota first)", "未领取": "Not claimed", "本地台账里今天还没领过;活动期内每天发一份新套餐": "No claim recorded locally for today yet; a new plan is issued every day during the event", "本地台账里今天(北京时间)领过的套餐:{plans}": "Plans claimed today (Beijing time) according to the local record: {plans}", "条启用": "enabled", "查询于 {time}": "Queried at {time}", "查询任务": "Query tasks", "查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)": "Query the task tree, then light up / claim in one click (includes school-season and cat travel)", "查询失败:{error}": "Query failed: {error}", "桌面端每日积分链路": "Desktop daily points flow", "正在读取定时签到数据…": "Loading schedule data…", "正在读取签到数据…": "Loading check-in data…", "每天 {time}": "Daily at {time}", "每天 {time} · {scope}": "Daily {time} · {scope}", "每天时刻": "Daily time", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "One row per account per check-in run · outcomes and gains as recorded by the backend", "每日活动签到(+100 积分,已领则中性提示)": "Daily activity check-in (+100 credits; a neutral note if already claimed)", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "Daily activity tasks · a web-app conversation lights up the daily activity, only claims add points · the two can run separately", "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "Daily sign-in points · claims member activities along the way · guest accounts have no points system (skipped)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "Daily welfare · claim → confirm → read back to verify · resets on the Beijing calendar day", "每日签到": "Daily check-in", "每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签": "Each schedule has its own time and scope · same-day dedup per entry · missed runs are made up after startup", "每账号一条明细 · 点此查看「签到记录」": "One row per account · open \"Check-in records\"", "没有可领套餐的账号(ZCode 未添加)。": "No accounts with claimable plans (ZCode not added).", "没有可领福利的账号(CodeArts / ZCode 未添加)。": "No accounts with claimable rewards (CodeArts / ZCode not added).", "没有支持成长任务的账号(需要 WorkBuddy 国内版)。": "No accounts support growth tasks (WorkBuddy CN required).", "没有支持新手福利的账号": "No accounts support starter rewards", "没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。": "No accounts support starter rewards — currently Loomy, Raccoon and CodeArts.", "没有符合筛选条件的签到记录": "No check-in records match the current filters", "没有需要执行的任务": "Nothing to run", "活动福利": "Event rewards", "活动(campaign)领取 · 每天 10:00 刷新": "Campaign claims · refreshed daily at 10:00", "活跃上报与保活均未成功,且活动未开启": "Both the activity report and keep-alive failed, and the campaign is not active", "活跃上报完成,但保活未成功且活动未开启": "Activity reported, but keep-alive failed and the campaign is not active", "活跃上报完成,但活动未开启": "Activity reported, but the campaign is not active", "活跃上报成功": "Activity reported", "活跃保活失败": "Keep-alive failed", "活跃保活完成": "Keep-alive complete", "活跃保活完成,但活动未开启": "Keep-alive complete, but the campaign is not active", "活跃保活完成,但活跃上报失败且活动未开启": "Keep-alive complete, but the activity report failed and the campaign is not active", "添加条目": "Add schedule", "添加模型": "Add model", "清单为空 —— 保存后恢复缺省链": "The list is empty — saving restores the default chain", "清单里已经有「{name}」": "The list already contains \"{name}\"", "清空失败:{error}": "Clear failed: {error}", "清空签到记录": "Clear check-in records", "点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领": "Click \"Plan details\" to see which plans you got and when they take effect; you can claim again every day during the event", "点击行展开账号明细 · 单账号签到不受范围限制": "Click a row to expand account details · single-account check-in is not limited by scope", "独立链路 · 只提供手动入口 · 不进自动签到": "Separate flow · manual entry only · not part of auto check-in", "猫猫旅行": "Cat travel", "猫猫旅行失败:{error}": "Cat travel failed: {error}", "猫猫旅行失败:{message}": "Cat travel failed: {message}", "猫猫旅行:{message}": "Cat travel: {message}", "用免费模型与网页会话维持账号活跃 · 不领取奖励": "Keep the account active with free models and a web-app conversation · no rewards claimed", "确定删除「{name}」?删除后到点不再执行。": "Delete \"{name}\"? It will no longer run at its scheduled time.", "确定清空全部签到记录?清空后无法恢复。": "Clear ALL check-in records? This cannot be undone.", "确定清空当前筛选出的签到记录?清空后无法恢复。": "Clear the filtered check-in records? This cannot be undone.", "移除 {name}": "Remove {name}", "立即全部签到": "Check in all now", "签到": "Check in", "签到中…": "Checking in…", "签到中心分区": "Check-in center sections", "签到失败:{error}": "Check-in failed: {error}", "签到完成:{succeeded}/{total} 成功{active},{failed} 个失败": "Check-in complete: {succeeded}/{total} succeeded{active}, {failed} failed", "签到成功": "Check-in succeeded", "签到提供商": "Check-in providers", "签到范围": "Check-in scope", "签到触发时刻": "Check-in trigger time", "签到记录": "Check-in records", "签到记录时间范围": "Check-in records time range", "签到记录表": "Check-in records table", "累计签到记录": "Total check-in records", "结算于 {time}": "Settled at {time}", "能量 {n}": "Energy {n}", "腾讯每日签到接口 · 仅国内版": "Tencent daily check-in API · China edition only", "至少勾一家才能保存(不勾等于这条条目不签到)": "Select at least one provider to save (an empty scope means this entry checks in nothing)", "读取定时签到数据失败:{error}": "Failed to load schedule data: {error}", "读取签到数据失败:{error}": "Failed to load check-in data: {error}", "读取签到记录失败:{error}": "Failed to load check-in records: {error}", "跳过 {n}": "{n} skipped", "运营活动交付(领取 → 确认 → 回读核实)· 领的是套餐赠送积分": "Campaign delivery (claim → confirm → read back to verify) · these are plan bonus points", "还没执行过": "Never run yet", "还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。": "No schedules yet — click \"Add schedule\" to create one, or check in manually on the \"Check-in center\" tab.", "还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。": "No check-in records yet — details appear here after a manual check-in or a scheduled run.", "连登 {n} 天": "{n}-day streak", "部分": "Partial", "重签": "Check in again", "重试": "Retry", "限时体验套餐 · 领取需滑块验证码 · 逐份查看与领取": "Limited-time trial plan · claim requires a slider CAPTCHA · view and claim one by one", "限时体验套餐(start-plan),活动期内每天一份 · 领取需滑块验证码": "Limited-time trial plan (start-plan), one per day during the campaign · claim requires a slider CAPTCHA", "需真实操作": "Requires real action", "非活动期": "Event inactive", "项": "items", "领取": "Claim", "领取中": "Claiming", "领取中…": "Claiming…", "领取免费 CodingPlan(按需:套餐有效时只回读额度)": "Claim the free CodingPlan (on demand: when the plan is active the quota is only read back)", "领取失败": "Claim failed", "领取失败:{error}": "Claim failed: {error}", "(共 {n} 个账号)": " ({n} accounts total)", "(能量 / 连登读数本次未取到)": "(Energy / streak readings unavailable this time)", ",日活保活 {n} 个": ", {n} kept alive" }); diff --git a/desktop-tauri/ui/i18n/en/requests.js b/desktop-tauri/ui/i18n/en/requests.js index 30e1a77c..15ccb74d 100644 --- a/desktop-tauri/ui/i18n/en/requests.js +++ b/desktop-tauri/ui/i18n/en/requests.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:en/requests —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · {n} 进行中": " · {n} in progress", " · 会话粘住": " · session stuck", "

    无响应原文
    ": "
    No raw response
    ", "
    无法解析响应内容
    ": "
    Unable to parse response content
    ", "
    无法解析请求体(不是含 messages 数组的 JSON)
    ": "
    Unable to parse request body (not JSON with a messages array)
    ", "
    无请求原文
    ": "
    No raw request
    ", "
    调用工具 {name}
    ": "
    Tool call {name}
    ", "响应原文": "Raw response", "思考过程": "Reasoning process", "请求原文": "Raw request", "HTTP {status},但响应体阶段出错": "HTTP {status}, but the response body phase failed", "HTTP {status},但响应体阶段出错:见错误列": "HTTP {status}, but the response body phase failed: see the error column", "{duration} / 首帧 {first}": "{duration} / TTFB {first}", "{m}分{s}秒": "{m}m {s}s", "{n}秒": "{n}s", "{s}秒{ms}ms": "{s}s {ms}ms", "↻ {n} 次": "↻ {n} times", "↻ 重试 {n} 次": "↻ Retried {n} times", "✅ 压缩完成": "✅ Vacuum complete", "「仅看进行中」开启时,状态固定为进行中": "When \"In progress only\" is on, the status is pinned to In progress", "上游模型繁忙,请求已排进上游队列;网关正按上游建议的时长等待后重发(不是登录态或额度问题)": "Upstream model is busy; the request is queued upstream and the Gateway will resend it after the wait suggested by the upstream (not a login-session or quota problem)", "上游状态码: {status}": "Upstream status code: {status}", "上游请求已发出,正在等第一个字节到达(模型的思考时间也在这段)": "Upstream request sent, waiting for the first byte to arrive (model thinking time is included here)", "上游返回错误": "Upstream returned an error", "下游请求的模型:{model}": "Model requested downstream: {model}", "仅清空报文原文": "Clear raw payloads only", "仅看进行中": "In progress only", "令牌": "Tokens", "体积": "Size", "保留统计行与报表,只抹掉请求 / 响应正文。清理后详情弹窗的「预览对话」将无原文可看,统计数字分毫不动。": "Keeps stats rows and reports, and only wipes the request / response bodies. After clearing, \"Conversation preview\" in the detail dialog will have no raw content to show and the stats numbers stay exactly the same.", "全部删除": "Delete all", "全部提供商": "All providers", "全部模型": "All models", "全部状态": "All statuses", "共 {n} 次尝试": "{n} attempts total", "共 {n} 次尝试,最终由 {provider} 承载": "{n} attempts total, finally served by {provider}", "其它": "Other", "内容按原样保存,请求头中的凭据字段已替换为 [redacted]": "Content is saved as-is; credential fields in the request headers have been replaced with [redacted]", "内部重试": "Internal retry", "切换路径:": "Switch path:", "列表数据已刷新,请重试(关闭后重新打开这条详情)": "List data has been refreshed; please try again (close and reopen this detail)", "删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。": "Deletes the log records themselves. With filters active, only matching entries are deleted and daily aggregate reports are unaffected; with no filters, everything is cleared and reports are reset. This cannot be undone.", "压缩中…": "Vacuuming…", "压缩失败:{message}": "Vacuum failed: {message}", "压缩已在后台开始,完成后会自动提示": "Vacuum started in the background; you will be notified when it completes", "压缩已在进行中": "Vacuum already in progress", "压缩已在进行中,接上它的进度等待完成": "Vacuum already in progress; joining it and waiting for it to finish", "压缩数据库": "Vacuum database", "压缩正在后台执行,完成后自动恢复": "Vacuum is running in the background; it resumes automatically when it completes", "原始报文": "Raw payload", "另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)": "{missing} more attempts have no details recorded (only the earliest {kept} are kept)", "另有 {n} 个词命中": "{n} more words hit", "只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)": "Only shows requests currently being forwarded (the status column shows the phase: Connecting / Awaiting response / Streaming / Retrying; the duration column shows elapsed time)", "只看失败": "Failed only", "只看成功": "Success only", "只看进行中": "In progress only", "命中了敏感词": "Sensitive word hit", "命中的敏感词": "Sensitive words hit", "响应中": "Streaming", "响应体": "Response body", "响应头": "Response headers", "回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)": "Reclaims the disk space used by deleted data (checkpoint + VACUUM, runs in the background)", "在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文": "Raw payloads are only saved for requests made after \"Debug mode\" is enabled in Settings → General", "失败请求不记录用量": "Usage is not recorded for failed requests", "失败({status}):{error}": "Failed ({status}): {error}", "失败:{error}": "Failed: {error}", "实际发给上游 {bytes} 字节": "{bytes} bytes actually sent upstream", "客户端": "Client", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "Will delete {count} logs · {raw} of them still carry raw payloads · database size {db}", "尝试 {n} ·": "Attempt {n} ·", "尝试明细": "Attempt details", "工具结果": "Tool result", "工具结果 · {id}": "Tool result · {id}", "工具结果图片": "Tool result image", "已删除 {count} 条": "{count} logs deleted", "已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」": "Abort accepted: the upstream connection is closed; the list will refresh to \"Request manually aborted\" shortly", "已抹掉 {count} 条报文原文": "Raw payloads wiped for {count} logs", "已用时(请求仍在转发中)": "Elapsed time (request is still being forwarded)", "成功": "Success", "成功({status})": "Success ({status})", "执行清理": "Run clear", "报文超过单条上限,请求体 / 响应体已按上限截断": "Payload exceeds the per-record limit; the request / response bodies have been truncated to the limit", "抹掉原文": "Wipe raw payloads", "按提供商筛选": "Filter by provider", "按提供商筛选(只列日志里出现过的)": "Filter by provider (only lists providers seen in the logs)", "按时间倒序,全量保留": "Reverse chronological order; kept in full", "按模型筛选": "Filter by model", "按模型筛选(只列日志里出现过的;精确匹配模型名)": "Filter by model (only lists models seen in the logs; matches the exact model name)", "按请求结果筛选": "Filter by request result", "排队中": "Queued", "提供商 / 账号": "Provider / Account", "提供商: {provider}": "Provider: {provider}", "提示": "Notice", "敏": "SW", "敏感词": "Sensitive words", "断开上游连接,明细记为「请求已被手动终止」": "Disconnects the upstream connection and records the detail as \"Request manually aborted\"", "无报文原文(未开启「调试模式」,或已被清理)": "No raw payload (debug mode was off, or it has been cleared)", "无结果记录": "No result recorded", "时间": "Time", "暂无请求日志,网关还没有转发过请求": "No request logs yet; the Gateway hasn't forwarded any requests", "最终承载:{picked}": "Finally served by: {picked}", "本轮尝试失败,正在退避等待或切换到下一个账号": "This attempt failed; backing off or switching to the next account", "来源客户端:{client}": "Source client: {client}", "查看该请求的上游原始报文": "View the raw upstream payload for this request", "模型": "Model", "模型测试": "Model test", "模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计": "Requests started by the \"Test\" button in the action column of the Model Management page: the same forwarding path as real requests, but not counted in reports", "正在加载请求日志…": "Loading request logs…", "正在统计…": "Calculating…", "正文超过单条上限,可能已被截断": "Body exceeds the per-record limit and may have been truncated", "没有符合筛选条件的请求": "No requests match the current filters", "清理": "Clear", "清理中…": "Clearing…", "清理失败:{message}": "Clear failed: {message}", "清理方式": "Clear mode", "清理请求日志": "Clear request logs", "生成速度:输出 token ÷ 流式段用时(总用时 − 首响);流式段不足总用时一半时按总用时计": "Generation speed: output tokens ÷ streaming time (total duration − TTFB); falls back to total duration when the streaming window is under half the request", "用时": "Duration", "用量": "Usage", "确定对全部请求日志执行「{label}」?将处理 {count} 条,此操作无法恢复。": "Run \"{label}\" on all request logs? This will process {count} entries and cannot be undone.", "确定按当前筛选执行「{label}」?将处理 {count} 条,此操作无法恢复。": "Run \"{label}\" on the current filter results? This will process {count} entries and cannot be undone.", "确定终止这条正在转发的请求?
    上游连接会立即断开,明细将记为「请求已被手动终止」。": "Abort this request that is being forwarded?
    The upstream connection will be closed immediately and the detail will be recorded as \"Request manually aborted\".", "等待响应": "Awaiting response", "策略 {strategy} · 会话粘性 {sticky}{hit}": "Strategy {strategy} · Stickiness {sticky}{hit}", "终止": "Abort", "终止失败:{error}": "Abort failed: {error}", "终止请求": "Abort request", "结果": "Result", "缓存读取: - / 命中率: -": "Cache reads: - / Hit rate: -", "缓存读取: {cache} / 命中率: {rate}": "Cache reads: {cache} / Hit rate: {rate}", "耗时": "Duration", "被跳过:": "Skipped: ", "识别到 User-Agent,但不属于已知的客户端类型:{ua}": "User-Agent present but not a known client type: {ua}", "详情": "Details", "详情内容": "Detail content", "请求体": "Request body", "请求图片": "Request image", "请求在任何账号接手之前就失败了": "The request failed before any account picked it up", "请求头": "Request headers", "请求已受理,正在选路、取凭证、建立上游连接": "Request accepted; selecting a route, fetching credentials, and establishing the upstream connection", "请求日志列表": "Request Logs", "请求日志时间范围": "Request Logs time range", "请求日志表": "Request Logs table", "请求正在转发中,用时列显示的是已用时": "The request is being forwarded; the duration column shows elapsed time", "请求详情": "Request details", "读取请求日志失败,详见控制台": "Failed to load request logs; see the console for details", "路由回放": "Routing replay", "转发到上游的模型:{model}": "Model forwarded to the upstream: {model}", "轮次": "Round", "输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}": "Input {input} · Output {output} · Total {total} · Cache reads {cache}", "这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计": "This detail row is a model test (manually started): it takes the same forwarding path as real requests but is not counted in reports", "这条明细没有记录提供商(旧数据或请求未走到转发)": "No provider is recorded for this detail row (old data, or the request never reached forwarding)", "这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)": "No source client recorded for this entry (legacy data, model test, or request without User-Agent)", "这条记录命中了敏感词,但没有留下命中明细": "This record hit a sensitive word, but no hit details were kept", "这条记录的尝试明细未采集(该字段上线前的旧数据,或请求在转发前就失败)": "Attempt details were not collected for this record (old data from before this field existed, or the request failed before forwarding)", "这条请求没有保存这一段报文": "This request did not save this payload section", "进入「{phase}」阶段已持续 {time}": "In \"{phase}\" phase for {time}", "进行中": "In progress", "进行中…": "In progress…", "连接中": "Connecting", "重试 {n}": "Retry {n}", "重试中": "Retrying", "重试次数": "Retries", "预览对话": "Conversation preview", "预览读取失败": "Failed to load preview", "预览读取失败:{error}": "Failed to load preview: {error}", "首响 {time}": "TTFB {time}", "首响:上游首帧到达的耗时": "TTFB: time to first byte from the upstream", "首响:上游首帧到达的耗时(请求仍在转发中)": "TTFB: time to first byte from the upstream (request is still being forwarded)", "首帧已到,上游内容正在下发": "First byte received; upstream content is streaming in", "(请求仍在转发中)": "(request is still being forwarded)", "(账号:{account})": "(Account: {account})", ",{n}毫秒后重试": ", retry in {n}ms", ",{n}秒后重试": ", retry in {n}s" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · {n} 进行中": " · {n} in progress", " · 会话粘住": " · session stuck", "
    无响应原文
    ": "
    No raw response
    ", "
    无法解析响应内容
    ": "
    Unable to parse response content
    ", "
    无法解析请求体(不是含 messages 数组的 JSON)
    ": "
    Unable to parse request body (not JSON with a messages array)
    ", "
    无请求原文
    ": "
    No raw request
    ", "
    调用工具 {name}
    ": "
    Tool call {name}
    ", "响应原文": "Raw response", "思考过程": "Reasoning process", "请求原文": "Raw request", "HTTP {status},但响应体阶段出错": "HTTP {status}, but the response body phase failed", "HTTP {status},但响应体阶段出错:见错误列": "HTTP {status}, but the response body phase failed: see the error column", "{duration} / 首帧 {first}": "{duration} / TTFB {first}", "{m}分{s}秒": "{m}m {s}s", "{n}秒": "{n}s", "{s}秒{ms}ms": "{s}s {ms}ms", "↻ {n} 次": "↻ {n} times", "↻ 重试 {n} 次": "↻ Retried {n} times", "✅ 压缩完成": "✅ Vacuum complete", "「仅看进行中」开启时,状态固定为进行中": "When \"In progress only\" is on, the status is pinned to In progress", "上游模型繁忙,请求已排进上游队列;网关正按上游建议的时长等待后重发(不是登录态或额度问题)": "Upstream model is busy; the request is queued upstream and the Gateway will resend it after the wait suggested by the upstream (not a login-session or quota problem)", "上游状态码: {status}": "Upstream status code: {status}", "上游请求已发出,正在等第一个字节到达(模型的思考时间也在这段)": "Upstream request sent, waiting for the first byte to arrive (model thinking time is included here)", "上游返回错误": "Upstream returned an error", "下游请求的模型:{model}": "Model requested downstream: {model}", "仅清空报文原文": "Clear raw payloads only", "仅看进行中": "In progress only", "令牌": "Tokens", "体积": "Size", "保留统计行与报表,只抹掉请求 / 响应正文。清理后详情弹窗的「预览对话」将无原文可看,统计数字分毫不动。": "Keeps stats rows and reports, and only wipes the request / response bodies. After clearing, \"Conversation preview\" in the detail dialog will have no raw content to show and the stats numbers stay exactly the same.", "全部删除": "Delete all", "全部提供商": "All providers", "全部模型": "All models", "全部状态": "All statuses", "共 {n} 次尝试": "{n} attempts total", "共 {n} 次尝试,最终由 {provider} 承载": "{n} attempts total, finally served by {provider}", "其它": "Other", "内容按原样保存,请求头中的凭据字段已替换为 [redacted]": "Content is saved as-is; credential fields in the request headers have been replaced with [redacted]", "内部重试": "Internal retry", "切换路径:": "Switch path:", "列表数据已刷新,请重试(关闭后重新打开这条详情)": "List data has been refreshed; please try again (close and reopen this detail)", "删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。": "Deletes the log records themselves. With filters active, only matching entries are deleted and daily aggregate reports are unaffected; with no filters, everything is cleared and reports are reset. This cannot be undone.", "压缩中…": "Vacuuming…", "压缩失败:{message}": "Vacuum failed: {message}", "压缩已在后台开始,完成后会自动提示": "Vacuum started in the background; you will be notified when it completes", "压缩已在进行中": "Vacuum already in progress", "压缩已在进行中,接上它的进度等待完成": "Vacuum already in progress; joining it and waiting for it to finish", "压缩数据库": "Vacuum database", "压缩正在后台执行,完成后自动恢复": "Vacuum is running in the background; it resumes automatically when it completes", "原始报文": "Raw payload", "另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)": "{missing} more attempts have no details recorded (only the earliest {kept} are kept)", "另有 {n} 个词命中": "{n} more words hit", "只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)": "Only shows requests currently being forwarded (the status column shows the phase: Connecting / Awaiting response / Streaming / Retrying; the duration column shows elapsed time)", "只看失败": "Failed only", "只看成功": "Success only", "只看进行中": "In progress only", "命中了敏感词": "Sensitive word hit", "命中的敏感词": "Sensitive words hit", "响应中": "Streaming", "响应体": "Response body", "响应头": "Response headers", "回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)": "Reclaims the disk space used by deleted data (checkpoint + VACUUM, runs in the background)", "在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文": "Raw payloads are only saved for requests made after \"Debug mode\" is enabled in Settings → General", "失败请求不记录用量": "Usage is not recorded for failed requests", "失败({status}):{error}": "Failed ({status}): {error}", "失败:{error}": "Failed: {error}", "实际下发给客户端 {bytes} 字节": "{bytes} bytes actually delivered to the client", "实际发给上游 {bytes} 字节": "{bytes} bytes actually sent upstream", "客户端": "Client", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "Will delete {count} logs · {raw} of them still carry raw payloads · database size {db}", "尝试 {n} ·": "Attempt {n} ·", "尝试明细": "Attempt details", "工具结果": "Tool result", "工具结果 · {id}": "Tool result · {id}", "工具结果图片": "Tool result image", "已下发": "Delivered", "已删除 {count} 条": "{count} logs deleted", "已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」": "Abort accepted: the upstream connection is closed; the list will refresh to \"Request manually aborted\" shortly", "已抹掉 {count} 条报文原文": "Raw payloads wiped for {count} logs", "已用时(请求仍在转发中)": "Elapsed time (request is still being forwarded)", "成功": "Success", "成功({status})": "Success ({status})", "执行清理": "Run clear", "报文超过单条上限,请求体 / 响应体已按上限截断": "Payload exceeds the per-record limit; the request / response bodies have been truncated to the limit", "抹掉原文": "Wipe raw payloads", "按提供商筛选": "Filter by provider", "按提供商筛选(只列日志里出现过的)": "Filter by provider (only lists providers seen in the logs)", "按时间倒序,全量保留": "Reverse chronological order; kept in full", "按模型筛选": "Filter by model", "按模型筛选(只列日志里出现过的;精确匹配模型名)": "Filter by model (only lists models seen in the logs; matches the exact model name)", "按请求结果筛选": "Filter by request result", "排队中": "Queued", "提供商 / 账号": "Provider / Account", "提供商: {provider}": "Provider: {provider}", "提示": "Notice", "敏": "SW", "敏感词": "Sensitive words", "断开上游连接,明细记为「请求已被手动终止」": "Disconnects the upstream connection and records the detail as \"Request manually aborted\"", "无报文原文(未开启「调试模式」,或已被清理)": "No raw payload (debug mode was off, or it has been cleared)", "无结果记录": "No result recorded", "时间": "Time", "暂无请求日志,网关还没有转发过请求": "No request logs yet; the Gateway hasn't forwarded any requests", "最终承载:{picked}": "Finally served by: {picked}", "本轮尝试失败,正在退避等待或切换到下一个账号": "This attempt failed; backing off or switching to the next account", "来源客户端:{client}": "Source client: {client}", "查看该请求的上游原始报文": "View the raw upstream payload for this request", "模型": "Model", "模型测试": "Model test", "模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计": "Requests started by the \"Test\" button in the action column of the Model Management page: the same forwarding path as real requests, but not counted in reports", "正在加载请求日志…": "Loading request logs…", "正在统计…": "Calculating…", "正文超过单条上限,可能已被截断": "Body exceeds the per-record limit and may have been truncated", "没有符合筛选条件的请求": "No requests match the current filters", "清理": "Clear", "清理中…": "Clearing…", "清理失败:{message}": "Clear failed: {message}", "清理方式": "Clear mode", "清理请求日志": "Clear request logs", "生成速度:输出 token ÷ 流式段用时(总用时 − 首响);流式段不足总用时一半时按总用时计": "Generation speed: output tokens ÷ streaming time (total duration − TTFB); falls back to total duration when the streaming window is under half the request", "用时": "Duration", "用量": "Usage", "确定对全部请求日志执行「{label}」?将处理 {count} 条,此操作无法恢复。": "Run \"{label}\" on all request logs? This will process {count} entries and cannot be undone.", "确定按当前筛选执行「{label}」?将处理 {count} 条,此操作无法恢复。": "Run \"{label}\" on the current filter results? This will process {count} entries and cannot be undone.", "确定终止这条正在转发的请求?
    上游连接会立即断开,明细将记为「请求已被手动终止」。": "Abort this request that is being forwarded?
    The upstream connection will be closed immediately and the detail will be recorded as \"Request manually aborted\".", "等待响应": "Awaiting response", "策略 {strategy} · 会话粘性 {sticky}{hit}": "Strategy {strategy} · Stickiness {sticky}{hit}", "终止": "Abort", "终止失败:{error}": "Abort failed: {error}", "终止请求": "Abort request", "结果": "Result", "缓存读取: - / 命中率: -": "Cache reads: - / Hit rate: -", "缓存读取: {cache} / 命中率: {rate}": "Cache reads: {cache} / Hit rate: {rate}", "耗时": "Duration", "被跳过:": "Skipped: ", "识别到 User-Agent,但不属于已知的客户端类型:{ua}": "User-Agent present but not a known client type: {ua}", "详情": "Details", "详情内容": "Detail content", "请求体": "Request body", "请求图片": "Request image", "请求在任何账号接手之前就失败了": "The request failed before any account picked it up", "请求头": "Request headers", "请求已受理,正在选路、取凭证、建立上游连接": "Request accepted; selecting a route, fetching credentials, and establishing the upstream connection", "请求日志列表": "Request Logs", "请求日志时间范围": "Request Logs time range", "请求日志表": "Request Logs table", "请求正在转发中,用时列显示的是已用时": "The request is being forwarded; the duration column shows elapsed time", "请求详情": "Request details", "读取请求日志失败,详见控制台": "Failed to load request logs; see the console for details", "路由回放": "Routing replay", "转发到上游的模型:{model}": "Model forwarded to the upstream: {model}", "轮次": "Round", "输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}": "Input {input} · Output {output} · Total {total} · Cache reads {cache}", "这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计": "This detail row is a model test (manually started): it takes the same forwarding path as real requests but is not counted in reports", "这条明细没有记录提供商(旧数据或请求未走到转发)": "No provider is recorded for this detail row (old data, or the request never reached forwarding)", "这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)": "No source client recorded for this entry (legacy data, model test, or request without User-Agent)", "这条记录命中了敏感词,但没有留下命中明细": "This record hit a sensitive word, but no hit details were kept", "这条记录的尝试明细未采集(该字段上线前的旧数据,或请求在转发前就失败)": "Attempt details were not collected for this record (old data from before this field existed, or the request failed before forwarding)", "这条请求没有保存这一段报文": "This request did not save this payload section", "进入「{phase}」阶段已持续 {time}": "In \"{phase}\" phase for {time}", "进行中": "In progress", "进行中…": "In progress…", "连接中": "Connecting", "重试 {n}": "Retry {n}", "重试中": "Retrying", "重试次数": "Retries", "预览对话": "Conversation preview", "预览读取失败": "Failed to load preview", "预览读取失败:{error}": "Failed to load preview: {error}", "首响 {time}": "TTFB {time}", "首响:上游首帧到达的耗时": "TTFB: time to first byte from the upstream", "首响:上游首帧到达的耗时(请求仍在转发中)": "TTFB: time to first byte from the upstream (request is still being forwarded)", "首帧已到,上游内容正在下发": "First byte received; upstream content is streaming in", "(请求仍在转发中)": "(request is still being forwarded)", "(账号:{account})": "(Account: {account})", ",{n}毫秒后重试": ", retry in {n}ms", ",{n}秒后重试": ", retry in {n}s" }); diff --git a/desktop-tauri/ui/i18n/ja/add-account.js b/desktop-tauri/ui/i18n/ja/add-account.js index 25ce3406..efeff9c8 100644 --- a/desktop-tauri/ui/i18n/ja/add-account.js +++ b/desktop-tauri/ui/i18n/ja/add-account.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:ja/add-account —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · 模型 {head}{rest}": " · モデル {head}{rest}", " 等 {n} 个": " など {n} 個", "1// 开头的 Google refresh token(也可直接粘贴 refresh_token=… 整行)": "1// で始まる Google refresh token(refresh_token=… の行をそのまま貼り付けても可)", "11 位大陆手机号": "11 桁の中国本土の携帯番号", "1~64 个字符,账号列表里按它分组显示": "1~64 文字、アカウント一覧ではこれでグループ表示されます", "6 位数字": "6 桁の数字", "
  • 没有可自动领的活动
  • ": "
  • 自動で受け取れるイベントはありません
  • ", "

    领到的是套餐赠送积分,不会增加福利模型的 token 池;": "

    受け取るのはプラン特典ポイントで、特典モデルの token プールは増えません;", "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "Anthropic 互換エンドポイントは https://api.deepseek.com/anthropic です(プロトコルを anthropic に切り替えるときにこれを入力)", "Anthropic 协议填根地址,不要带 /v1": "Anthropic プロトコルではルートアドレスを入力し、/v1 は付けないでください", "Antigravity": "Antigravity", "AutoClaw 国内版": "AutoClaw 中国版", "AutoClaw 国际版": "AutoClaw 国際版", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS は必須です;**完全な Cookie** を貼り付けてください(Cookie エディターのワンクリック書き出しで構いません)—— このうち **PTOKEN は必ず残してください**。ゲートウェイはこれを使って Baidu から KukuAI の業務セッショントークンを発行してもらうため、欠けるとモデル更新時に「未ログイン」と表示されます。Cookie ヘッダー全体、Cookie エディターが書き出した JSON 配列、または {BDUSS, STOKEN} オブジェクトに対応しています。KukuAI には更新の仕組みがないため、ログイン状態の期限が切れたら貼り直せば済みます。", "BDUSS=…; STOKEN=…(或 Cookie 编辑器导出的 JSON)": "BDUSS=…; STOKEN=…(または Cookie エディターが書き出した JSON)", "CatPaw 的 X-Passport-Token(登录态 Cookie)": "CatPaw の X-Passport-Token(ログイン状態 Cookie)", "Cline Pass 的 API Key 在 app.cline.bot 获取(订阅制);端点为 OpenAI 兼容,模型清单以 Cline 控制台为准,模型名在模型管理里按裸名添加(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客户端形态校验,所需的客户端请求头已随卡预置": "Cline Pass の API Key は app.cline.bot で取得します(サブスクリプション制)。エンドポイントは OpenAI 互換で、モデル一覧は Cline コンソールが正となります。モデル名はモデル管理で接頭辞なしの名前のまま追加します(例:glm-5.2 / kimi-k2.7-code / deepseek-v4-pro)。上流はクライアントの形態を検証するため、必要なクライアントリクエストヘッダーはこのカードにあらかじめ設定されています", "Cloud-IDE-JWT(三段点分)": "Cloud-IDE-JWT(3 段のドット区切り)", "Command Code": "Command Code", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT(ドット区切り 3 パート。Bearer 行全体や data.bin の中身を貼っても可)", "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT(3 つのドット区切り部分。chatglm_token=… の行をそのまま貼っても可)", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode 国際版", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "NVIDIA NIM のマネージドエンドポイント。Key は integrate.api.nvidia.com で申請します", "OfficeAce 果办": "OfficeAce", "accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)": "accessToken は Accio のログイン認証情報です(長い不透明な token 文字列で、JWT ではありません);refreshToken は任意で、入力すると期限切れ時に自動更新できます。{label}アカウントの認証情報を入力してください —— {siteNote}(最も簡単な方法:上の「ウェブログイン」を使えば token を手動で探す必要はありません。)", "accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
    手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
    没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。": "accessToken は Trae の Cloud-IDE-JWT(3 段のドット区切り)です。refreshToken は期限切れ時の自動更新に使用します(本家の refreshToken は 1 回ごとに交換され、一度更新すると古いものは無効になります。そのため 2 つのプログラムで同じアカウントを同時に更新しないでください)。
    手動で貼り付ける場合は machineId / deviceId も一緒に入力してください:アップストリームはこれらをログイン時にアップロードされたデバイス公開鍵と紐付けてデバイスを判定するため、根拠なく別のペアにすると 2xxxx 系のデバイスバインディング拒否に当たります。
    この 3 つがない場合の正道は上の「ウェブログイン」を使うことです;コンテナ / リモートデプロイ形態では案内に従ってアドレスバーのコールバックアドレスを貼り付けてください。", "monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)": "monkeycode_ai_session の値(Cookie 全体をそのまま貼り付けても可)", "refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。": "refreshToken は任意で、入力すると期限切れ時に自動更新できます;どちらも小浣熊クライアントのログイン状態ファイルから取得できます。", "refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。": "refreshToken は任意で、入力すると自動更新できます;どちらもクライアントのログイン状態ファイルから取得できます。同じ Cline アカウントはどちらのプールでも使え、ここでは {label} に分類されます。", "token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。": "token は enc: プレフィックスに対応しています(Windows では自動復号)。refreshToken を入力しないと自動更新できません。", "token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。": "token はログイン状態 Cookie です;CatPaw には更新の仕組みがないため、期限切れ後はクライアントで再ログインが必要です。", "user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)": "user_ で始まる API Key(Authorization: Bearer … の行をそのまま貼り付けても可)", "{label} 网页登录的打开方式": "{label} のウェブログインの開き方", "{label} 账号地区": "{label} アカウントの地域", "{label} 账号的添加方式": "{label} アカウントの追加方法", "{name} 不是合法 JSON:{reason}": "{name} は有効な JSON ではありません:{reason}", "{name}要粘一个 JSON 对象(形如 {sample})": "{name} には JSON オブジェクトを貼り付けてください(例:{sample})", "{n} 个账号": "{n} 個のアカウント", "{n} 个账号,点击添加": "{n} 個のアカウント、クリックして追加", "{seconds}s 后重发": "{seconds} 秒後に再送", "✅ {label}账号已添加": "✅ {label} アカウントを追加しました", "✅ {label}账号已添加{name}": "✅ {label} アカウントを追加しました{name}", "✅ 已从 cc-switch 导入 {n} 个供应商": "✅ cc-switch から {n} 個のプロバイダーをインポートしました", "✅ 已创建自定义提供商「{name}」并添加账号": "✅ カスタムプロバイダー「{name}」を作成し、アカウントを追加しました", "✅ 已删除自定义提供商「{name}」{accounts}{keys}": "✅ カスタムプロバイダー「{name}」を削除しました{accounts}{keys}", "✅ 已更新已有账号{name}(同一把 API Key 只保留一条,重复添加会合并)": "✅ 既存アカウント{name}を更新しました(同じ API Key は 1 件のみ保持し、再追加はマージされます)", "✅ 登录成功,{edition}账号已加入列表": "✅ ログインに成功し、{edition}アカウントをリストに追加しました", "✅ 领取成功:{planId}{period}": "✅ 受け取りに成功しました:{planId}{period}", " 同一家可以放多把 key,按优先级轮换。": " 同じプロバイダーに複数の key を置くことができ、優先度に従ってローテーションされます。", "「{name}」的账号添加功能还在开发中,敬请期待。": "「{name}」のアカウント追加機能は開発中です。ご期待ください。", "一个 Key 用遍多家模型,模型名用「厂商/模型」全称(如 deepseek/deepseek-chat)": "1 つの Key で複数のプロバイダーのモデルを利用できます。モデル名は「ベンダー/モデル」の完全名を指定します(例:deepseek/deepseek-chat)", "上游不要鉴权时留空,并勾选下面一项(留空又不勾会被当成未配置凭证)": "アップストリームが認証不要の場合は空欄にし、下の項目にチェックを入れてください(空欄かつ未チェックでは認証情報未設定として扱われます)", "上游信息": "アップストリーム情報", "不可领({status})": "受け取り不可({status})", "不支持导入": "インポートに対応していません", "不是有效的凭证文件:内容要是一个 JSON 对象": "有効な認証情報ファイルではありません:内容は JSON オブジェクトである必要があります", "不是有效的凭证文件:需要包含 accessToken 与 uid": "有効な認証情報ファイルではありません:accessToken と uid が必要です", "不是自定义提供商": "カスタムプロバイダーではありません", "不需要 Key:转发与拉取模型都不发送鉴权头": "Key は不要:転送とモデル取得のいずれも認証ヘッダーを送信しません", "不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

    注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。": "ログイン不要です。下のボタンを押すと、ゲートウェイが智谱清言からゲスト資格情報を直接取得してアカウント一覧に追加します(先にメモ名を入力しても構いません)。ゲスト資格情報はそのまま会話に使え、無料枠は上流がデバイス単位で発行します。

    注意:ゲストアカウントにはポイント体系がありません。会員ポイント・毎日ログインポイント・アクティビティ受け取りの各インターフェースはすべてログインが必要なため、これらの機能はゲストではスキップされます(アカウントページのポイントや签到センターには数値が表示されません)。ポイントが必要な場合は「資格情報を入力」でログイン済みアカウントのトークンを貼り付けるか、「デスクトップのログイン状態をインポート」してください。", "两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。": "両版のアカウントは同時に保存でき、それぞれ独自のモデルカタログを持ちます(中国版と国際版は別々のプロバイダーで、個別に有効化とマッピングができます)。", "中国版": "中国版", "今天没有需要领的活动(都已到账)": "本日受け取る必要のあるイベントはありません(すべて付与済み)", "今天(北京时间 {day})已试 {attempts} 次,还能试 {left} 次{next}
    ": "本日(北京時間 {day})は {attempts} 回試行済み、あと {left} 回試せます{next}
    ", "从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。": "別のマシン(または OfficeAce デスクトップ版の models.json)から次の 3 項目を取り出して貼り付けてください:モデルゲートウェイのベース URL(baseUrl、https://modelgw-…/v2 の形式)と modelAppKey / modelAppSecret。この 3 項目だけで転送できます(ゲートウェイ認証情報は無期限)。コントロールプレーンの一時認証情報(accessKeyId など)は任意で、空欄の場合は残高照会と自動更新が使えませんが、転送には影響しません。", "从本机导入桌面端登录态": "本機からデスクトップ版のログイン状態をインポート", "任务镜像 id": "タスクイメージ id", "余额查询": "残高照会", "使用 Google 账号登录": "Google アカウントでログイン", "使用 Zai 账号登录": "Zai アカウントでログイン", "使用个人访问令牌(PAT)": "個人アクセストークン(PAT)を使用", "免费模型不用填 Key(默认勾上「该上游无需鉴权」,网关自动用匿名凭证);付费模型则填 Zen 的 API Key。免费档上游只认官方客户端形态,已默认开启「伪装 OpenCode 官方客户端」": "無料モデルでは Key の入力は不要です(既定で「このアップストリームは認証不要」がチェックされ、ゲートウェイは自動的に匿名認証情報を使用します);有料モデルでは Zen の API Key を入力します。無料枠のアップストリームは公式クライアントの形態しか受け付けないため、「OpenCode 公式クライアントの偽装」が既定で有効になっています", "内嵌窗口": "内蔵ウィンドウ", "内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)": "内蔵ウィンドウで開きます;ウィンドウを閉じると待機をキャンセルします(Google アカウントが拒否された場合はシステムブラウザをお使いください)", "内嵌窗口打开;完成后自动加入列表,关窗即取消等待": "内蔵ウィンドウで開きます;完了後は自動でリストに追加され、ウィンドウを閉じると待機をキャンセルします", "内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待": "内蔵ウィンドウで開きます。読み取りが完了すると自動で一覧に追加され、ウィンドウを閉じると待機を中止します", "内嵌窗口用全新环境,多账号互不影响;关窗即取消等待": "内蔵ウィンドウは新しい環境を使うため、複数のアカウントが互いに影響しません;ウィンドウを閉じると待機をキャンセルします", "内嵌窗口(推荐)": "内蔵ウィンドウ(推奨)", "准备验证…": "認証を準備中…", "凭据 JSON": "認証情報 JSON", "凭证文件在哪?": "認証情報ファイルの場所は?", "创建并添加账号": "作成してアカウントを追加", "创建这个提供商,并同时建立它的第一个账号。": "このプロバイダーを作成し、同時にその最初のアカウントを作成します。", "删除功能不可用(脚本未就绪)": "削除機能は利用できません(スクリプト未準備)", "删除失败:{reason}": "削除に失敗しました:{reason}", "删除此提供商": "このプロバイダーを削除", "及 {n} 个账号": "、および {n} 個のアカウント", "发送中…": "送信中…", "发送失败:{message}": "送信に失敗しました:{message}", "发送未确认:登录窗口已弹出,请在弹出的窗口中手动完成登录": "送信は未確認です:ログインウィンドウが開きました。表示されたウィンドウで手動でログインを完了してください", "取消失败:{message}": "キャンセルに失敗しました:{message}", "取消等待": "待機をキャンセル", "只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页": "「内蔵ウィンドウ」でのみサードパーティの入り口を復元できます:システムブラウザではログインページを変更できないためです", "可选": "任意", "可选(cloudaicompanionProject):留空由网关自动发现": "任意(cloudaicompanionProject):空欄の場合はゲートウェイが自動検出します", "可选,展示用": "任意、表示用", "可选,没有则无法自动续期": "任意、ないと自動更新できません", "可选,留空使用凭据里的 user_name": "任意、空欄の場合は認証情報の user_name を使用", "可选,留空使用账号姓名、邮箱或令牌指纹": "任意、空欄の場合はアカウントの氏名、メールアドレス、またはトークンフィンガープリントを使用", "可选,留空使用账号昵称或邮箱": "任意、空欄の場合はアカウントのニックネームまたはメールアドレスを使用", "可选,留空则用 key 尾号": "任意、空欄の場合は key の末尾を使用", "可选,留空则用 userId": "任意、空欄の場合は userId を使用", "可选,留空则用上游返回的账号名或账号 id": "任意、空欄の場合はアップストリームが返したアカウント名またはアカウント id を使用", "可选,留空则用凭证里的账号名": "任意、空欄の場合は認証情報のアカウント名を使用", "可选,留空则用文件里的昵称": "任意。空欄の場合はファイル内のニックネームを使用", "可选,留空则用登录名或 uid": "任意、空欄の場合はログイン名または uid を使用", "可选,留空则用脱敏手机号": "任意、空欄の場合はマスク済み電話番号を使用", "可选,留空则用脱敏手机号或 userId": "任意、空欄の場合はマスク済み電話番号または userId を使用", "可选,留空则用账号 uk": "任意、空欄の場合はアカウントの uk を使用", "可选,留空则用账号昵称": "任意。空欄ならアカウントのニックネームを使用", "可选,留空则用账号昵称或 userId": "任意。空欄の場合はアカウントのニックネームまたは userId を使用", "可选,留空则用邮箱或令牌尾号": "任意、空欄の場合はメールアドレスまたはトークンの末尾を使用", "可选,留空用「OfficeAce 果办」": "任意、空欄の場合は「OfficeAce」を使用", "可选,留空自动生成": "任意、空欄の場合は自動生成", "可选,留空自动用昵称": "任意、空欄の場合はニックネームを自動使用", "可选,给这个访客账号起个备注名": "任意。このゲストアカウントのメモ名", "可选,续期时带上": "任意、更新時に一緒に送信されます", "可选,讯飞侧 userid": "任意、iFlytek 側の userid", "可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补": "任意;会話の開始に必須です。空欄の場合はゲートウェイがこのアカウントの既存タスクから自動検出します。見つからない場合は後で手動で補ってください。", "可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)": "任意;アカウントのメールアドレスで、表示と身元識別に使用します(新しいトークンに替えても同じアカウントとして認識されます)", "可选;UUID 形态,与凭据同生共死": "任意;UUID 形式で、認証情報と一体で管理されます", "可选;填了才能到期自动续期": "任意;入力すると期限切れ時に自動更新できます", "可选;用于去重与展示名(留空时网关会问一次上游)": "任意;重複排除と表示名に使用します(空欄の場合、ゲートウェイがアップストリームに一度問い合わせます)", "可选;用于生成账号 id 与展示名": "任意;アカウント id と表示名の生成に使用します", "可选;纯数字串(上游只收 8–24 位数字)": "任意;数字のみの文字列(アップストリームは 8–24 桁の数字のみ受け付けます)", "可领取": "受け取り可能", "同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)": "同じプロバイダーに API Key ありのアカウントと認証不要アカウントを混在でき、それぞれの規則でルーティングされます。認証不要アカウントも複数併存でき、それぞれ別の送信プロキシを設定できます", "同地址换 anthropic 协议即走它的 Claude 兼容端点。转发必须填 OpenCode Go 的 API Key(订阅制;获取模型清单不需要 Key,但发请求上游会回 401「Missing API key」)": "同じアドレスでプロトコルを anthropic に変えると、その Claude 互換エンドポイントを使用します。転送には OpenCode Go の API Key が必須です(サブスクリプション制;モデルカタログの取得に Key は不要ですが、リクエストを送るとアップストリームが 401「Missing API key」を返します)", "响应为空": "応答が空です", "商汤日日新的 OpenAI 兼容端点;余额查询走控制台账号密码(见下方「余额查询」一段)": "SenseNova の OpenAI 互換エンドポイント。残高照会はコンソールのアカウント / パスワードを使います(下の「残高照会」の節を参照)", "国内版": "中国版", "国内版与**国际版是两套独立的账号与套餐**,凭证与领取的套餐都不通用。": "中国版と**国際版は 2 組の独立したアカウントとプラン**で、認証情報と受け取ったプランはどちらも共用できません。", "国内版登录页没有 Google / GitHub 入口(它用微信 / 手机号 / 邮箱登录)": "中国版のログインページには Google / GitHub の入り口がありません(WeChat / 電話番号 / メールアドレスでログインします)", "国内版的登录站点是 www.accio-ai.com,与国际版不是同一站。": "中国版のログインサイトは www.accio-ai.com で、国際版とは別のサイトです。", "国内版(WorkBuddy)": "中国版(WorkBuddy)", "国内站为 https://api.siliconflow.cn/v1": "中国版サイトは https://api.siliconflow.cn/v1", "国内站(minimaxi.com);OpenAI 兼容地址为 https://api.minimaxi.com/v1": "中国版サイト(minimaxi.com);OpenAI 互換アドレスは https://api.minimaxi.com/v1", "国际版": "国際版", "国际版 z.ai 的 Coding 套餐;OpenAI 兼容地址为 https://api.z.ai/api/coding/paas/v4": "国際版 z.ai の Coding プラン;OpenAI 互換アドレスは https://api.z.ai/api/coding/paas/v4", "国际版与国内版是**两套独立的账号**(同一账号体系的两个站点),凭证不通用。": "国際版と中国版は**2 組の独立したアカウント**です(同じアカウント体系の 2 つのサイト)。認証情報は共用できません。", "国际版与国内版账号体系独立,请填国际版的凭证。未填 refreshToken 无法自动续期。": "国際版と中国版はアカウント体系が独立しています。国際版の認証情報を入力してください。refreshToken を入力しないと自動更新できません。", "国际版登录页默认只显示邮箱登录,勾选后恢复 Google / GitHub / X 入口": "国際版のログインページは既定でメールアドレスログインのみ表示されます。チェックすると Google / GitHub / X の入り口が復元されます", "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "国際版の推論サイトは api.z.ai で、中国版とは別のサイトです;プランもそれぞれ独立しています。", "国际版(WorkBuddy AI)": "国際版(WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "国際版サイトの Coding プラン;OpenAI 互換アドレスは https://api.minimax.io/v1", "在 cc-switch 里找到": "cc-switch で", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "内蔵ウィンドウで公式ログインを完了してください。成功すると自動でアカウント一覧に追加されます。", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "{label} の Qoder アカウント設定 → Integrations で PAT を生成してください(Google / GitHub のトークンは入力しないでください)。2 つの地域のアカウントと認証情報は共用できません。", "在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。": "開いた公式認可ページで WeChat の QR コードを読み取ってログインします(ページに QR コードが直接表示されます)。読み取りに成功すると自動でアカウント一覧に追加されます。", "在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "開いたウィンドウで Baidu アカウントを使って KukuAI 本体サイトにログインしてください(SMS 認証コード / QR コードのいずれも可)。ログインに成功すると自動でアカウント一覧に追加されます。ゲートウェイは Baidu から KukuAI の業務セッショントークンを発行してもらいます(本機に KukuAI クライアントがインストールされている必要があります)。", "在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "開いたウィンドウで Google アカウントにログインしてください(Antigravity の認可ページ)。認可が完了すると自動でアカウントリストに追加されます。", "在浏览器中打开登录页": "ブラウザでログインページを開く", "地区": "地域", "填写凭证": "認証情報を入力", "填写凭证添加": "認証情報を入力して追加", "备注名": "ラベル名", "套餐": "プラン", "如:智谱 GLM": "例:Zhipu GLM", "完成登录后自动加入账号列表,昵称取自上游。": "ログイン完了後は自動でアカウント一覧に追加されます。ニックネームはアップストリームから取得します。", "官方客户端凭证目录:Windows 是 C:\\Users\\<你的用户名>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth,macOS 是 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth": "公式クライアントの認証情報ディレクトリ:Windows は C:\\Users\\<ユーザー名>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth、macOS は ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth", "官方已确认到账": "公式が付与を確認済み", "导入": "インポート", "导入中…": "インポート中…", "导入凭证文件": "認証情報ファイルのインポート", "导入失败": "インポートに失敗しました", "导入失败:{reason}": "インポートに失敗しました:{reason}", "导入所选": "選択項目をインポート", "导入所选({n})": "選択項目をインポート({n})", "导入桌面端登录态": "デスクトップ版のログイン状態をインポート", "将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下": "内蔵ウィンドウを開きます;認可が完了すると自動でアカウント一覧に追加されます。ウィンドウを閉じると待機をキャンセルします。認可ページの最後に zcode:// リンクを開けない旨の表示が出ることがありますが、これは正常です —— 結果はサーバー側で記録済みです", "将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)": "内蔵ウィンドウを開きます;認可が完了すると自動でアカウント一覧に追加されます。リモートパネルではコールバックアドレスを貼り付けられます。ウィンドウを閉じると待機をキャンセルします。リンクの有効期限は 5 分で、タイムアウトまたは未クリックの場合は無効になります(再発行できます)", "将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待": "内蔵ウィンドウを開きます;ログイン完了後は自動でアカウント一覧に追加されます。ウィンドウを閉じると待機をキャンセルします", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 Trae 账号);同机完成后自动加入,Docker / 远程面板按提示粘贴地址栏回调地址。": "システムの既定ブラウザで認可ページを開きます(ブラウザでログイン済みの Trae アカウントが再利用されます);同一マシンでは完了後に自動追加され、Docker / リモートパネルでは案内に従ってアドレスバーのコールバックアドレスを貼り付けてください。", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 ZCode 账号);授权完成后自动加入账号列表。页面最后可能提示无法打开 zcode:// 链接,属正常现象": "システムの既定ブラウザで認可ページを開きます(ブラウザでログイン済みの ZCode アカウントが再利用されます);認可完了後は自動でアカウント一覧に追加されます。ページの最後に zcode:// リンクを開けない旨の表示が出ることがありますが、正常な動作です", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "システムの既定ブラウザで認可ページを開きます(ブラウザでログイン済みの Huawei Cloud アカウントが再利用されます);認可コードはゲートウェイがクラウドをポーリングして取得するため、ブラウザがどのマシンで動いていても関係ありません。完了後は自動でアカウント一覧に追加されます", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "システムの既定ブラウザで認可ページを開きます(ブラウザでログイン済みの Huawei Cloud アカウントが再利用されます);ブラウザとゲートウェイが別のマシンにある場合は、上の説明に従って最終的なコールバックアドレスをパネルに貼り付けてください", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "システムの既定ブラウザでログインページを開きます(ブラウザでログイン済みの Accio アカウントが再利用されます);ログイン完了後は自動でアカウント一覧に追加され、ポップアップを閉じると待機をキャンセルします", "小米 MiMo Token Plan 套餐(新加坡站)": "小米 MiMo Token Plan プラン(シンガポールサイト)", "已到账": "付与済み", "已取消登录等待": "ログイン待ちをキャンセルしました", "已取消领取": "受け取りをキャンセルしました", "已取消验证码": "認証をキャンセルしました", "已填写手机号,正在发送验证码…": "電話番号を入力しました。認証コードを送信中…", "已声明无需鉴权:账号会被正常选路,出网时不带任何鉴权头": "認証不要を宣言済み:アカウントは通常どおりルーティングされ、送信時に認証ヘッダーは一切付きません", "已导入 {ok} 个,失败 {fail} 个(失败项已标注在列表里)": "{ok} 個をインポートしました。失敗 {fail} 個(失敗した項目にはリスト内でマークが付いています)", "已建过 · {n} 个账号,点击继续添加": "作成済み · {n} 個のアカウント、クリックして追加", "已建过,点击继续添加账号": "作成済み、クリックしてアカウントを追加", "已打开官方登录页,请在窗口中完成登录…": "公式ログインページを開きました。ウィンドウでログインを完了してください…", "已点发送,等待短信…": "送信しました。SMS を待っています…", "已用系统默认浏览器打开登录页,请在浏览器中完成登录…": "システムの既定ブラウザでログインページを開きました。ブラウザでログインを完了してください…", "已领取,等官方确认": "受け取り済み、公式の確認待ち", "当前壳版本不支持 AutoClaw 网页登录,请更新应用": "現在のシェルバージョンは AutoClaw のウェブログインに対応していません。アプリを更新してください", "当前没有可领取的套餐(活动尚未开始)": "現在受け取り可能なプランはありません(イベントはまだ開始されていません)", "当前环境不支持领取(桥接方法缺失)": "現在の環境では受け取りに対応していません(ブリッジメソッドがありません)", "必填,CatPaw 账号标识": "必須、CatPaw アカウントの識別子", "恢复 Google / GitHub 入口": "Google / GitHub の入り口を復元", "手机号": "電話番号", "手机验证码登录": "SMS 認証コードログイン", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Trae SOLO の公式認可ページ(trae.cn)を開き、あなたの Trae アカウントでログインしてください:認可が完了すると公式ページから本機の一時ポートにリダイレクトされます;同一マシンでのデプロイでは自動受信し、Docker / リモートパネルでは案内に従ってアドレスバーのコールバックアドレスをコピーしてください。", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Accio {label}の公式ログインページ({site})を開き、あなたの Accio アカウントでログインしてください:ログインに成功すると公式ページから本機にリダイレクトされます;同一マシンでは自動完了し、Docker / リモートパネルでは案内に従ってコールバックアドレスを貼り付けてください(認可コードは本機からゲートウェイに渡されるだけで、画面に平文 token は表示されません)。", "打开 Accio {label}登录页": "Accio {label} のログインページを開く", "打开 Antigravity 网页登录": "Antigravity のウェブログインを開く", "打开 CatPaw 网页登录": "CatPaw のウェブログインを開く", "打开 Cline 授权页": "Cline の認可ページを開く", "打开 CodeArts 授权页": "CodeArts の認可ページを開く", "打开 Qoder 网页登录": "Qoder のウェブログインを開く", "打开 Trae 授权页": "Trae の認可ページを開く", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "ZCode {label}の公式認可ページを開き、あなたのアカウントでログインしてください。認可結果は ZCode サーバー側で記録され、ゲートウェイがバックグラウンドで自動的に認証情報を取得してアカウント一覧に追加します。", "打开 ZCode {label}授权页": "ZCode {label} の認可ページを開く", "打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
    网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。": "Huawei Cloud CodeArts の公式認可ページを開いてログインします:ログイン完了後、公式ページからブラウザがゲートウェイ自身の /oauth/callback に戻され、ゲートウェイはワンタイム認可コードで一時認証情報を取得し、アカウント一覧に追加します。
    ゲートウェイが別のマシンで動作している場合は、ウェブ画面でアドレスバーの最終的なコールバックアドレスをそのままパネルに貼り付けるよう案内されます。", "打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。": "Huawei Cloud の認可ページを開いてログインしてください:完了すると、ゲートウェイがクラウドをポーリングして認可コードを取得し、アカウント一覧に追加します(OfficeAce デスクトップ版は不要で、コールバック URL を手動で貼り戻す必要もありません)。", "打开官方授权页完成设备码授权,登录的是{label}({site})的账号。": "公式認可ページを開いてデバイスコード認可を完了します。ログインするのは {label}({site})のアカウントです。", "打开授权页": "認可ページを開く", "打开授权页完成确认,账号自动加入 {label}{note}": "認可ページを開いて確認を完了すると、アカウントが自動で {label} に追加されます{note}", "打开方式": "開き方", "打开网页登录": "ウェブログインを開く", "探测失败:{message}": "検出に失敗しました:{message}", "接入一个兼容上游": "互換アップストリームを接続", "提交中…": "送信中…", "搜索提供商": "プロバイダーを検索", "搜索提供商…": "プロバイダーを検索…", "整份粘贴官方插件 / CLIProxyAPI 落盘的凭据 JSON(形如 {\"codearts_provider_credential\":{…}},铺平的也行)。
    必填:access_key_id、secret_access_key、security_token。要能自动续期,必须连 refresh_token 与 oauth_context 一起粘 —— 临时凭据约一小时到期,缺这半块就续不回来,只能重新登录。": "公式プラグイン / CLIProxyAPI が保存した認証情報 JSON をまるごと貼り付けてください({\"codearts_provider_credential\":{…}} の形。フラットなものでも可)。
    必須:access_key_id、secret_access_key、security_token。自動更新を効かせるには refresh_token と oauth_context も一緒に貼り付ける必要があります —— 一時認証情報は約 1 時間で期限切れになり、この半分が欠けると更新できず、再ログインするしかありません。", "文件是{edition}凭证,已按该版本导入": "ファイルは{edition}の認証情報のため、そのバージョンでインポートしました", "新建自定义提供商": "カスタムプロバイダーを新規作成", "方案:{label}": "方式:{label}", "明文 JWT 或 auth.json 里的 enc: 加密值(自动解密)": "平文 JWT または auth.json 内の enc: 暗号化値(自動復号)", "明文 JWT(国际版账号的 access token)": "平文 JWT(国際版アカウントの access token)", "智谱 bigmodel.cn 的 Coding 套餐,Key 在 open.bigmodel.cn/usercenter/apikeys": "Zhipu bigmodel.cn の Coding プラン。Key は open.bigmodel.cn/usercenter/apikeys で取得します", "未知错误": "不明なエラー", "未能获取授权地址,请重试": "認可アドレスを取得できませんでした。再試行してください", "未能读取 cc-switch 数据": "cc-switch のデータを読み取れませんでした", "未领取成功": "受け取りに失敗しました", "本地 Ollama、OpenCode Zen 免费档这类上游要勾上,否则账号不可用": "ローカル Ollama、OpenCode Zen の無料枠などのアップストリームではチェックを入れてください。入れないとアカウントを使用できません", "本家有两个**互不替代**的凭证,按你要用的功能填,至少填一个:编码套餐 API Key 用于转发推理(打 {site})与查询窗口限额(每 N 小时 / 每周的剩余比例);jwt 用于领取套餐与查询余额(打 zcode.z.ai 的 billing 网关,官方叫 Coding Plan JWT,是一串三段点分的字符串)。余额两把都能查、但读到的东西不同:jwt 读到余额桶与套餐到期,API Key 读到窗口限额;而领取只认 jwt、转发只认 API Key。请填写 {label}账号的凭证 —— {plan}(最容易拿到的办法:直接用上方的「网页登录」—— 它会替你把这个 API Key 换好,两个凭证一起拿到。)": "本家には**互いに代替できない** 2 つの認証情報があり、使いたい機能に応じて入力してください。少なくともどちらか一方は必須です:コーディングプラン API Key は推論の転送用({site} を叩きます)とウィンドウクォータの照会用(N 時間ごと / 週ごとの残り割合);jwt はプランの受け取りと残高照会用(zcode.z.ai の billing ゲートウェイを叩きます。公式名称は Coding Plan JWT で、3 段のドット区切り文字列です)。残高はどちらでも照会できますが、読み取れる内容が異なります:jwt は残高バケットとプランの有効期限、API Key はウィンドウクォータを読み取ります;受け取りは jwt のみ、転送は API Key のみです。{label}アカウントの認証情報を入力してください —— {plan}(最も簡単な方法:上の「ウェブログイン」を使うこと —— この API Key を代わりに交換してくれ、2 つの認証情報をまとめて取得できます。)", "本机 Ollama 的 OpenAI 兼容端点,无需 API Key(默认已勾上「该上游无需鉴权」,鉴权头一个都不发)": "本機の Ollama の OpenAI 互換エンドポイント。API Key は不要です(既定で「このアップストリームは認証不要」がチェック済みで、認証ヘッダーは一切送信されません)", "本次未确认到账": "今回は付与が確認できませんでした", "条可导入": "件がインポート可能", "条配置,其中": "件の設定、うち", "桌面运行时不可用(Tauri 未初始化)": "デスクトップランタイムが利用できません(Tauri が未初期化)", "模型网关基址": "モデルゲートウェイのベース URL", "正在打开百度登录页…": "Baidu のログインページを開いています…", "正在扫描本机 cc-switch…": "本機の cc-switch をスキャンしています…", "正在换发访客凭证…": "ゲスト資格情報を発行中…", "正在添加…": "追加中…", "正在等待{name}登录完成;完成或取消后才能发起新的登录": "{name} のログイン完了を待っています;完了またはキャンセルしないと新しいログインを開始できません", "每周": "毎週", "每日": "毎日", "每月": "毎月", "没有则无法自动续期": "ないと自動更新できません", "没有匹配「{keyword}」的提供商": "「{keyword}」に一致するプロバイダーはありません", "添加 {name} 账号": "{name} アカウントを追加", "添加中…": "追加中…", "添加到 ": "追加先 ", "添加失败:{reason}": "追加に失敗しました:{reason}", "添加小浣熊账号": "小浣熊アカウントを追加", "添加方式": "追加方法", "添加访客账号": "ゲストアカウントを追加", "添加账号": "アカウントを追加", "火山引擎 Coding 套餐;通用推理接入点为 https://ark.cn-beijing.volces.com/api/v3": "Volcano Engine Coding プラン;汎用推論エンドポイントは https://ark.cn-beijing.volces.com/api/v3", "点击添加账号": "クリックしてアカウントを追加", "点开即预填,填 Key 接入": "開くと自動入力済み、Key を入力して接続", "点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
    这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。": "ボタンを押すとまずスライダー認証(公式の不正対策)を 1 回行い、その後ログインページが開きます。ログイン完了で自動的にアカウントが追加されます。
    これが国際版公式の唯一のログイン方式です。すでにクライアントでログイン済みの場合は、「デスクトップ版のログイン状態をインポート」を使う方が速いです。", "用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。": "AutoClaw アカウントに紐付けられた電話番号でログインします:「コードを取得」をクリックし、SMS を受け取ったら下の欄に入力してログインしてください。認証コードは本機から直接公式 API に送信され、画面に token は表示されません。", "用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。": "CatPaw アカウントでログインを完了します(Meituan passport)。成功すると自動でアカウント一覧に追加されます。", "用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。": "Loomy アカウントに紐付けられた電話番号でログインします:「コードを取得」をクリックして入力するだけです。本家で最も手軽な入り口です。", "用于转发;形如 apiKey.secret 两段点分(不填则这个账号不能转发)": "転送用。apiKey.secret の 2 段ドット区切り形式です(未入力の場合、このアカウントは転送できません)", "用于领取套餐与查询余额;不填则这个账号不能领取与查余额": "プランの受け取りと残高照会用。未入力の場合、このアカウントは受け取りと残高照会ができません", "用户 ID": "ユーザー ID", "用绑定的手机号登录:点「获取验证码」后填入即可。这是国内版官方唯一的登录方式。": "紐付けられた電話番号でログインします:「コードを取得」をクリックして入力するだけです。これが中国版公式の唯一のログイン方式です。", "留空则用提供商名称": "空欄の場合はプロバイダー名を使用", "登录 / 添加 {name} 账号": "ログイン / {name} アカウントを追加", "登录 session(一长串)": "ログイン session(長い文字列)", "登录中…": "ログイン中…", "登录失败:{message}": "ログインに失敗しました:{message}", "登录已结束": "ログインは終了しました", "登录并添加": "ログインして追加", "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "ログイン状態ファイルのパス:~/.box-agent/config/auth.json(Windows:C:\\Users\\<ユーザー名>\\.box-agent\\config\\auth.json)", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "カスタムプロバイダー「{name}」を削除しますか?このプロバイダー配下の {count} 個のアカウントも同時に削除され、削除後は元に戻せません。", "等待 Accio {label}登录完成…": "Accio {label} のログイン完了を待っています…", "等待 CatPaw 登录完成…": "CatPaw のログイン完了を待っています…", "等待 Cline 授权确认…": "Cline の認可確認を待っています…", "等待 CodeArts 登录完成…": "CodeArts のログイン完了を待っています…", "等待 Google 授权完成…": "Google の認可完了を待っています…", "等待 KukuAI 登录完成…": "KukuAI のログイン完了を待っています…", "等待 OfficeAce 登录完成…": "OfficeAce のログイン完了を待っています…", "等待 OrcaTerm 登录完成…": "OrcaTerm のログイン完了を待っています…", "等待 Qoder 授权完成…": "Qoder の認可完了を待っています…", "等待 Trae 授权完成…": "Trae の認可完了を待っています…", "等待 ZCode {label}授权完成…": "ZCode {label} の認可完了を待っています…", "等待小浣熊登录完成…": "小浣熊のログイン完了を待っています…", "等待登录完成…": "ログインの完了を待っています…", "等待网页登录…": "ウェブログインを待っています…", "粘贴 API Key": "API Key を貼り付け", "粘贴 Accio 的 accessToken": "Accio の accessToken を貼り付け", "粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
    这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。": "Antigravity の Google refresh token を貼り付け(1// で始まる;refresh_token= を含む行全体をそのまま貼り付けても可)。
    これはウェブログイン以外の手動入り口です(例:ログイン済みの公式 IDE からエクスポート)。refresh token は期限切れにならず、access token の期限切れはゲートウェイが自動で更新します;トークンを紛失すると Google の認可をやり直すしかないため、大切に保管してください。", "粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。": "Command Code の API Key を貼り付け(user_ で始まる;公式 CLI の ~/.commandcode/auth.json またはウェブの studio から取得)。行全体 / 引用符付き / Bearer プレフィックス付きのまま貼り付けても構いません。ゲートウェイが自動で裸の key に剥がします。key は期限切れにならず、更新用 API もありません。無効になったら取得し直してください;追加時にゲートウェイが軽量な死活確認を 1 回行いますが、失敗しても追加には影響しません。", "粘贴 Cookie": "Cookie を貼り付け", "粘贴 Google refresh token": "Google refresh token を貼り付け", "粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。": "OrcaTerm の OAuth accessToken(ドット区切り 3 パートの JWT。デスクトップクライアントの data.bin か Web 版の Cookie ot_session から取得)を貼り付けます。Bearer / ot_session= を含む行をそのまま貼ってもよく、ゲートウェイが裸のトークンに剥がします。追加後はゲートウェイが自動で更新するため(上流に更新 API があります)、2 時間の有効期限を手動で追う必要はありません。", "粘贴 Qoder 个人访问令牌(pt-…)": "Qoder の個人アクセストークン(pt-…)を貼り付け", "粘贴 accessToken": "accessToken を貼り付け", "粘贴 access_token(一长串 JWT)": "access_token(長い JWT)を貼り付け", "粘贴 session cookie": "session cookie を貼り付け", "粘贴 token / refreshToken": "token / refreshToken を貼り付け", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "workos:… で始まるトークンを貼り付け(プレフィックスがなくても自動で補われます)", "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "智谱清言のアクセストークン(3 つのドット区切り部分からなる JWT)を貼り付けてください。取得方法:ブラウザで chatglm.cn を開いてログインし、開発者ツールの Application → Cookies から chatglm_token の値をコピーします(chatglm_token=…; の行をそのまま貼っても可)。貼り付けたあと上流に対して一度検証し、通った場合のみアカウントを保存します。ログイン済みアカウントではポイントと签到の機能が使え、以降はゲートウェイが自動で更新します(上流に更新インターフェースがあります)。", "粘贴登录凭据": "ログイン認証情報を貼り付け", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "貼り付けるのはログイン session です(同時にモデルゲートウェイの token でもあります)。Loomy には更新用 API がなく、session の有効期間は約 14 日です。期限切れ後は再ログインしてください。", "粘贴网关凭据": "ゲートウェイ認証情報を貼り付け", "粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
    {siteNote}
    MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。": "ウェブのログイン状態 cookie monkeycode_ai_session の値を貼り付け(ブラウザーで Cookie 全体をコピーしてそのまま貼り付けても可。ゲートウェイが値を自動で取り出します)。
    {siteNote}
    MonkeyCode には更新用 API がありません:session の有効期間は約 30 日(ハードリミット)で、期限切れ後はウェブに再ログインして貼り付け直せば済みます。", "粘贴访问令牌": "アクセストークンを貼り付け", "系统浏览器": "システムブラウザ", "系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。": "システムブラウザは既存のログイン状態を再利用できます(国際版で推奨);内蔵ウィンドウの方がクリーンです。", "系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表": "システムブラウザで開く(ログイン済みの Cline アカウントを再利用);完了後は自動でリストに追加されます", "系统浏览器打开(复用已登录的 Google 账号);完成后自动加入列表": "システムブラウザで開きます(ログイン済みの Google アカウントを再利用);完了後は自動でリストに追加されます", "系统浏览器打开(复用已登录的 Zai / Google 账号);完成后自动加入列表": "システムブラウザで開く(ログイン済みの Zai / Google アカウントを再利用);完了後は自動でリストに追加されます", "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "システムブラウザで開く(ログイン済みの Meituan アカウントを再利用);完了後は自動でリストに追加されます", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "システムブラウザで開く(ログイン済みアカウントを再利用);完了後は自動でリストに追加されます", "系统默认浏览器": "システムの既定ブラウザ", "编码套餐 API Key": "コーディングプラン API Key", "网关 Secret": "ゲートウェイシークレット", "网页登录": "ウェブログイン", "网页登录的打开方式": "ウェブログインの開き方", "网页登录(Zai / Google)": "ウェブログイン(Zai / Google)", "自定义": "カスタム", "自建聚合面板:填你自己的站点地址。余额查询用该账号的 API Key 打 {base}/v1/usage,不需要额外配置": "自前のアグリゲーションパネル:自分のサイトアドレスを入力。残高照会はこのアカウントの API キーで {base}/v1/usage を叩きます。追加設定不要", "自建聚合面板:填你自己的站点地址;余额查询要另填 NewAPI 的个人访问令牌(见下方「余额查询」一段)": "自前のアグリゲーションパネル:自分のサイトアドレスを入力。残高照会には別途 NewAPI の個人アクセストークンが必要です(下の「残高照会」の節を参照)", "获取风控配置失败,将直接尝试领取:{message}": "不正対策設定の取得に失敗しました。そのまま受け取りを試みます:{message}", "获取验证码": "コードを取得", "访客模式": "ゲストモード", "访客模式(免登录)": "ゲストモード(ログイン不要)", "该上游无需鉴权(不发送鉴权头)": "このアップストリームは認証不要(認証ヘッダーを送信しません)", "该提供商": "このプロバイダー", "该提供商账号添加功能即将上线": "このプロバイダーのアカウント追加機能は近日公開予定です", "该自定义提供商已不存在(可能已被删除),请刷新后重试": "このカスタムプロバイダーは存在しません(削除された可能性があります)。更新してから再試行してください", "请先选择一个自定义提供商": "先にカスタムプロバイダーを選択してください", "请在弹出的滑块中完成验证(官方要求的风控步骤)": "表示されたスライダーで認証を完了してください(公式が求める不正対策ステップ)", "请填写 11 位大陆手机号": "11 桁の中国本土の携帯番号を入力してください", "请填写 6 位数字验证码": "6 桁の認証コードを入力してください", "请填写 Base URL": "Base URL を入力してください", "请填写 {label}": "{label} を入力してください", "请填写名称": "名前を入力してください", "请完成验证…": "認証を完了してください…", "读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows": "%APPDATA%/AutoClaw/auth.json を読み込んで復号します。Windows のみ", "读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录": "~/.cline/data/settings/providers.json を読み込みます。Cline クライアントでログイン済みである必要があります", "读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录": "~/.meituan-catpaw/auth.json を読み込みます。CatPaw クライアントでログイン済みである必要があります", "读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录": "本機の KukuAI クライアントのログイン状態を読み込みます(%APPDATA%\\baidugenflowpro\\Network\\Cookies)。KukuAI クライアントでログイン済みである必要があります", "读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录": "ローカルの OrcaTerm クライアントのログイン状態(%APPDATA%\\com.orcaterm-desktop.app\\data.bin)を読み取ります。OrcaTerm クライアントでのログインが必要です", "读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录": "本機の智谱清言クライアントのログイン状態を読み取ります(%APPDATA%\\chatglm\\Network\\Cookies)。クライアントでログイン済みである必要があります", "读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。": "このマシンの公式クライアント(Tencent WorkBuddy / WorkBuddy AI)で現在ログイン中のアカウントを読み取ります:認証情報ディレクトリ内の最新のログイン状態ファイルを自動で特定します(このマシンの公式クライアントにログイン済みである必要があります)。インポート後はゲートウェイが自動で更新するため、2 時間の有効期限に合わせて手動でトークンを交換する必要はありません。", "读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。": "本機クライアントの現在のログイン状態を読み込みます(平文 Cookie、復号不要)。クライアントの実行中は Cookies ファイルが排他占有されるため、インポート前に KukuAI クライアントを終了してください。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。": "本機クライアントの現在のログイン状態を読み込みます。毎回リアルタイムで読み取ります(この記録を削除してもクライアントのログイン状態には影響しません)。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后点「刷新 Token」同步。": "本機クライアントの現在のログイン状態を読み込みます。毎回リアルタイムで読み取ります(この記録を削除してもクライアントのログイン状態には影響しません)。クライアントで再ログインしたら「Token を更新」をクリックして同期してください。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后重新导入即可。": "本機クライアントの現在のログイン状態を読み込みます。毎回リアルタイムで読み取ります(この記録を削除してもクライアントのログイン状態には影響しません)。クライアントで再ログインしたら再インポートしてください。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。": "本機クライアントの現在のログイン状態を読み込みます。毎回リアルタイムで読み取ります(この記録を削除してもクライアントのログイン状態には影響しません)。Zai / Google でクライアントにログインしているユーザーはこちらを使ってください。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。": "本機クライアントの現在のログイン状態を読み込みます。毎回リアルタイムで読み取ります(この記録を削除してもクライアントのログイン状態には影響しません)。これは {label} に分類されます。", "读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。": "ローカルクライアントが現在ログインしている OAuth トークンを読み取ります(平文 JSON、復号不要)。インポート後はゲートウェイが自動で更新するため、クライアントの 2 時間の有効期限に合わせて手動で切り替える必要はありません。", "读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。": "本機クライアントの現在のログイン状態を読み取ります。クライアントの Cookies は DPAPI 暗号化(Chromium os_crypt)されているため、Windows でのみ利用できます。クライアントの起動中はこのファイルを読めないことがあるので、クライアントを終了してから再試行してください(「資格情報を入力」でトークンを貼り付ける方法でも構いません)。", "账号信息": "アカウント情報", "账号信息不完整,请刷新后重试": "アカウント情報が不完全です。更新してから再試行してください", "账号已添加,但请留意:{warning}": "アカウントを追加しましたが、ご注意ください:{warning}", "账号版本": "アカウントバージョン", "账号类型": "アカウント種別", "账号:{name}
    ": "アカウント:{name}
    ", "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 创建": "Inference Providers ルートを使用します。Key は hf.co/settings/tokens で作成します", "还没有账号": "アカウントはまだありません", "这一家当前不支持网页登录,请改用填写凭证": "このプロバイダーは現在ウェブログインに対応していません。認証情報の入力に切り替えてください", "这个账号当前没有可自动领的活动(只自动领每日登录赠送积分)": "このアカウントには現在自動で受け取れるイベントがありません(自動で受け取るのは毎日ログインで付与されるポイントのみです)", "这份额度走「活动套餐」通道:若转发时提示编码套餐已到期,": "このクォータは「イベントプラン」の経路を使用します:転送時にコーディングプランの期限切れが表示された場合は、", "这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。": "ここには中国サイト(monkeycode-ai.com)のアカウントを入力します:中国サイトの session を貼り付けてください。国際サイト(monkeycode-ai.net)は別のプロバイダーで、アカウントも別です。両者の認証情報は共用できません。", "这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。": "ここには国際サイト(monkeycode-ai.net)のアカウントを入力します:国際サイトの session を貼り付けてください。中国サイト(monkeycode-ai.com)は別のプロバイダーで、アカウントも別です。両者の認証情報は共用できません。", "选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "CatPaw クライアントのログイン状態ファイル auth.json を選択します(Windows:C:\\Users\\<ユーザー名>\\.meituan-catpaw\\auth.json、macOS / Linux:~/.meituan-catpaw/auth.json)。インポート後は認証情報がゲートウェイ側に保存され、単独で転送できるようになります(本機クライアントに依存しません)。", "选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。": "Cline クライアントのログイン状態ファイル providers.json を選択します(Windows:C:\\Users\\<ユーザー名>\\.cline\\data\\settings\\providers.json、macOS / Linux:~/.cline/data/settings/providers.json)。インポート後は認証情報がゲートウェイ側に保存され、単独で転送できるようになります(本機クライアントに依存しません)。この項目は {label} に登録されます。", "选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。": "KukuAI クライアントの Cookies ファイルを選択します(%APPDATA%\\baidugenflowpro\\Network\\Cookies):これは**バイナリ**の SQLite データベースです(拡張子なし、JSON ではありません)。インポート時は選択したファイルのコピーを読み、実行中のクライアントと占有を争いません。したがって**クライアントを終了する必要はありません**。インポート後は認証情報がゲートウェイ側に保存され、単独で転送できるようになります。", "选择凭证文件导入": "認証情報ファイルからインポート", "选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。": "公式クライアントのログイン状態の認証情報ファイル(workbuddy-desktop.info / workbuddy-desktop-ai.info)または wb2api がエクスポートしたアカウント JSON(ネスト / フラット形式どちらも可)を選択してください。", "选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "小浣熊クライアントのログイン状態ファイル auth.json を選択します(Windows:C:\\Users\\<ユーザー名>\\.box-agent\\config\\auth.json、macOS / Linux:~/.box-agent/config/auth.json)。インポート後は認証情報がゲートウェイ側に保存され、単独で転送できるようになります(本機クライアントに依存しません)。", "选择提供商": "プロバイダーを選択", "选择的文件不是合法 JSON": "選択したファイルは有効な JSON ではありません", "选择要添加账号的提供商": "アカウントを追加するプロバイダーを選択", "重新扫描": "再スキャン", "鉴权": "認証", "阿里云 Token Plan 套餐(国际站)": "Alibaba Cloud Token Plan プラン(国際版サイト)", "阿里云百炼 Coding 套餐(国内站)": "Alibaba Cloud Model Studio Coding プラン(中国版サイト)", "阿里云百炼 Coding 套餐(国际站)": "Alibaba Cloud Model Studio Coding プラン(国際版サイト)", "阿里云百炼模型服务(国际站,OpenAI 兼容模式)": "Alibaba Cloud Model Studio モデルサービス(国際版サイト、OpenAI 互換モード)", "需要图形验证码:登录窗口已弹出,请填写后继续": "画像認証が必要です:ログインウィンドウが開きました。入力して続行してください", "预置 API": "プリセット API", "领取 CodeArts 每日福利": "CodeArts の毎日特典を受け取る", "领取失败:{message}": "受け取りに失敗しました:{message}", "领取并让官方确认": "受け取って公式の確認を待つ", "风控验证配置不完整,请稍后重试": "不正対策認証の設定が不完全です。しばらくしてから再試行してください", "验证码": "認証コード", "验证码已发送。收到后填入下方并点「登录并添加」": "認証コードを送信しました。受け取ったら下の欄に入力し、「ログインして追加」をクリックしてください", "验证码已发送,请查看手机短信": "認証コードを送信しました。携帯の SMS をご確認ください", "验证码已发送,请查看手机短信。填入下方并点「登录并添加」即完成登录": "認証コードを送信しました。携帯の SMS をご確認ください。下の欄に入力して「ログインして追加」をクリックすると、ログインが完了します", "验证码已发送,请查看短信": "認証コードを送信しました。SMS をご確認ください", "验证码校验失败,请重试": "認証コードの検証に失敗しました。再試行してください", "验证码校验超时,请重试": "認証コードの検証がタイムアウトしました。再試行してください", "验证码组件不可用,请重试": "認証コンポーネントが利用できません。再試行してください", "验证码组件初始化超时,请重试": "認証コンポーネントの初期化がタイムアウトしました。再試行してください", "验证码组件加载失败,请检查网络后重试": "認証コンポーネントの読み込みに失敗しました。ネットワークを確認して再試行してください", "验证码组件加载异常,请重试": "認証コンポーネントの読み込みでエラーが発生しました。再試行してください", "验证码组件加载超时,请检查网络后重试": "認証コンポーネントの読み込みがタイムアウトしました。ネットワークを確認して再試行してください", "验证码组件未加载,请重启应用后重试": "認証コンポーネントが読み込まれていません。アプリを再起動してから再試行してください", "(免费额度池,模型名带 cline-free/ 前缀)。": "(無料枠プール、モデル名に cline-free/ プレフィックスが付きます)。", "(其中 {n} 把恢复为不限制)": "(うち {n} 個は無制限に戻ります)", "(必填)": "(必須)", "(有效期 {window})": "(有効期間 {window})", "(订阅池,模型名带 cline-pass/ 前缀,需要账号有对应订阅)。": "(サブスクプール、モデル名に cline-pass/ プレフィックスが付きます。アカウントに対応するサブスクリプションが必要です)。", ",下一次最快 {minutes} 分钟后": "、次回は最短 {minutes} 分後", ",并从 {n} 把 Key 的可用提供商里移除": "、Key {n} 個を利用可能なプロバイダーからも除外しました" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · 模型 {head}{rest}": " · モデル {head}{rest}", " 等 {n} 个": " など {n} 個", "1// 开头的 Google refresh token(也可直接粘贴 refresh_token=… 整行)": "1// で始まる Google refresh token(refresh_token=… の行をそのまま貼り付けても可)", "11 位大陆手机号": "11 桁の中国本土の携帯番号", "1~64 个字符,账号列表里按它分组显示": "1~64 文字、アカウント一覧ではこれでグループ表示されます", "6 位数字": "6 桁の数字", "

  • 没有可自动领的活动
  • ": "
  • 自動で受け取れるイベントはありません
  • ", "

    领到的是套餐赠送积分,不会增加福利模型的 token 池;": "

    受け取るのはプラン特典ポイントで、特典モデルの token プールは増えません;", "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "Anthropic 互換エンドポイントは https://api.deepseek.com/anthropic です(プロトコルを anthropic に切り替えるときにこれを入力)", "Anthropic 协议填根地址,不要带 /v1": "Anthropic プロトコルではルートアドレスを入力し、/v1 は付けないでください", "Antigravity": "Antigravity", "AtomCode": "AtomCode", "AutoClaw 国内版": "AutoClaw 中国版", "AutoClaw 国际版": "AutoClaw 国際版", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS は必須です;**完全な Cookie** を貼り付けてください(Cookie エディターのワンクリック書き出しで構いません)—— このうち **PTOKEN は必ず残してください**。ゲートウェイはこれを使って Baidu から KukuAI の業務セッショントークンを発行してもらうため、欠けるとモデル更新時に「未ログイン」と表示されます。Cookie ヘッダー全体、Cookie エディターが書き出した JSON 配列、または {BDUSS, STOKEN} オブジェクトに対応しています。KukuAI には更新の仕組みがないため、ログイン状態の期限が切れたら貼り直せば済みます。", "BDUSS=…; STOKEN=…(或 Cookie 编辑器导出的 JSON)": "BDUSS=…; STOKEN=…(または Cookie エディターが書き出した JSON)", "CatPaw 的 X-Passport-Token(登录态 Cookie)": "CatPaw の X-Passport-Token(ログイン状態 Cookie)", "Cline Pass 的 API Key 在 app.cline.bot 获取(订阅制);端点为 OpenAI 兼容,模型清单以 Cline 控制台为准,模型名在模型管理里按裸名添加(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客户端形态校验,所需的客户端请求头已随卡预置": "Cline Pass の API Key は app.cline.bot で取得します(サブスクリプション制)。エンドポイントは OpenAI 互換で、モデル一覧は Cline コンソールが正となります。モデル名はモデル管理で接頭辞なしの名前のまま追加します(例:glm-5.2 / kimi-k2.7-code / deepseek-v4-pro)。上流はクライアントの形態を検証するため、必要なクライアントリクエストヘッダーはこのカードにあらかじめ設定されています", "Cloud-IDE-JWT(三段点分)": "Cloud-IDE-JWT(3 段のドット区切り)", "Command Code": "Command Code", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT(ドット区切り 3 パート。Bearer 行全体や data.bin の中身を貼っても可)", "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT(3 つのドット区切り部分。chatglm_token=… の行をそのまま貼っても可)", "LobsterAI": "LobsterAI", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode 国際版", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "NVIDIA NIM のマネージドエンドポイント。Key は integrate.api.nvidia.com で申請します", "OfficeAce 果办": "OfficeAce", "accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)": "accessToken は Accio のログイン認証情報です(長い不透明な token 文字列で、JWT ではありません);refreshToken は任意で、入力すると期限切れ時に自動更新できます。{label}アカウントの認証情報を入力してください —— {siteNote}(最も簡単な方法:上の「ウェブログイン」を使えば token を手動で探す必要はありません。)", "accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
    手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
    没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。": "accessToken は Trae の Cloud-IDE-JWT(3 段のドット区切り)です。refreshToken は期限切れ時の自動更新に使用します(本家の refreshToken は 1 回ごとに交換され、一度更新すると古いものは無効になります。そのため 2 つのプログラムで同じアカウントを同時に更新しないでください)。
    手動で貼り付ける場合は machineId / deviceId も一緒に入力してください:アップストリームはこれらをログイン時にアップロードされたデバイス公開鍵と紐付けてデバイスを判定するため、根拠なく別のペアにすると 2xxxx 系のデバイスバインディング拒否に当たります。
    この 3 つがない場合の正道は上の「ウェブログイン」を使うことです;コンテナ / リモートデプロイ形態では案内に従ってアドレスバーのコールバックアドレスを貼り付けてください。", "monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)": "monkeycode_ai_session の値(Cookie 全体をそのまま貼り付けても可)", "refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。": "refreshToken は任意で、入力すると期限切れ時に自動更新できます;どちらも小浣熊クライアントのログイン状態ファイルから取得できます。", "refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。": "refreshToken は任意で、入力すると自動更新できます;どちらもクライアントのログイン状態ファイルから取得できます。同じ Cline アカウントはどちらのプールでも使え、ここでは {label} に分類されます。", "token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。": "token は enc: プレフィックスに対応しています(Windows では自動復号)。refreshToken を入力しないと自動更新できません。", "token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。": "token はログイン状態 Cookie です;CatPaw には更新の仕組みがないため、期限切れ後はクライアントで再ログインが必要です。", "user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)": "user_ で始まる API Key(Authorization: Bearer … の行をそのまま貼り付けても可)", "{label} 网页登录的打开方式": "{label} のウェブログインの開き方", "{label} 账号地区": "{label} アカウントの地域", "{label} 账号的添加方式": "{label} アカウントの追加方法", "{name} 不是合法 JSON:{reason}": "{name} は有効な JSON ではありません:{reason}", "{name}要粘一个 JSON 对象(形如 {sample})": "{name} には JSON オブジェクトを貼り付けてください(例:{sample})", "{n} 个账号": "{n} 個のアカウント", "{n} 个账号,点击添加": "{n} 個のアカウント、クリックして追加", "{seconds}s 后重发": "{seconds} 秒後に再送", "✅ {label}账号已添加": "✅ {label} アカウントを追加しました", "✅ {label}账号已添加{name}": "✅ {label} アカウントを追加しました{name}", "✅ 已从 cc-switch 导入 {n} 个供应商": "✅ cc-switch から {n} 個のプロバイダーをインポートしました", "✅ 已创建自定义提供商「{name}」并添加账号": "✅ カスタムプロバイダー「{name}」を作成し、アカウントを追加しました", "✅ 已删除自定义提供商「{name}」{accounts}{keys}": "✅ カスタムプロバイダー「{name}」を削除しました{accounts}{keys}", "✅ 已更新已有账号{name}(同一把 API Key 只保留一条,重复添加会合并)": "✅ 既存アカウント{name}を更新しました(同じ API Key は 1 件のみ保持し、再追加はマージされます)", "✅ 登录成功,{edition}账号已加入列表": "✅ ログインに成功し、{edition}アカウントをリストに追加しました", "✅ 领取成功:{planId}{period}": "✅ 受け取りに成功しました:{planId}{period}", " 同一家可以放多把 key,按优先级轮换。": " 同じプロバイダーに複数の key を置くことができ、優先度に従ってローテーションされます。", "「{name}」的账号添加功能还在开发中,敬请期待。": "「{name}」のアカウント追加機能は開発中です。ご期待ください。", "一个 Key 用遍多家模型,模型名用「厂商/模型」全称(如 deepseek/deepseek-chat)": "1 つの Key で複数のプロバイダーのモデルを利用できます。モデル名は「ベンダー/モデル」の完全名を指定します(例:deepseek/deepseek-chat)", "上游不要鉴权时留空,并勾选下面一项(留空又不勾会被当成未配置凭证)": "アップストリームが認証不要の場合は空欄にし、下の項目にチェックを入れてください(空欄かつ未チェックでは認証情報未設定として扱われます)", "上游信息": "アップストリーム情報", "不可领({status})": "受け取り不可({status})", "不支持导入": "インポートに対応していません", "不是有效的凭证文件:内容要是一个 JSON 对象": "有効な認証情報ファイルではありません:内容は JSON オブジェクトである必要があります", "不是有效的凭证文件:需要包含 accessToken 与 uid": "有効な認証情報ファイルではありません:accessToken と uid が必要です", "不是自定义提供商": "カスタムプロバイダーではありません", "不需要 Key:转发与拉取模型都不发送鉴权头": "Key は不要:転送とモデル取得のいずれも認証ヘッダーを送信しません", "不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

    注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。": "ログイン不要です。下のボタンを押すと、ゲートウェイが智谱清言からゲスト資格情報を直接取得してアカウント一覧に追加します(先にメモ名を入力しても構いません)。ゲスト資格情報はそのまま会話に使え、無料枠は上流がデバイス単位で発行します。

    注意:ゲストアカウントにはポイント体系がありません。会員ポイント・毎日ログインポイント・アクティビティ受け取りの各インターフェースはすべてログインが必要なため、これらの機能はゲストではスキップされます(アカウントページのポイントや签到センターには数値が表示されません)。ポイントが必要な場合は「資格情報を入力」でログイン済みアカウントのトークンを貼り付けるか、「デスクトップのログイン状態をインポート」してください。", "两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。": "両版のアカウントは同時に保存でき、それぞれ独自のモデルカタログを持ちます(中国版と国際版は別々のプロバイダーで、個別に有効化とマッピングができます)。", "中国版": "中国版", "今天没有需要领的活动(都已到账)": "本日受け取る必要のあるイベントはありません(すべて付与済み)", "今天(北京时间 {day})已试 {attempts} 次,还能试 {left} 次{next}
    ": "本日(北京時間 {day})は {attempts} 回試行済み、あと {left} 回試せます{next}
    ", "从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。": "別のマシン(または OfficeAce デスクトップ版の models.json)から次の 3 項目を取り出して貼り付けてください:モデルゲートウェイのベース URL(baseUrl、https://modelgw-…/v2 の形式)と modelAppKey / modelAppSecret。この 3 項目だけで転送できます(ゲートウェイ認証情報は無期限)。コントロールプレーンの一時認証情報(accessKeyId など)は任意で、空欄の場合は残高照会と自動更新が使えませんが、転送には影響しません。", "从本机导入桌面端登录态": "本機からデスクトップ版のログイン状態をインポート", "任务镜像 id": "タスクイメージ id", "余额查询": "残高照会", "使用 Google 账号登录": "Google アカウントでログイン", "使用 Zai 账号登录": "Zai アカウントでログイン", "使用个人访问令牌(PAT)": "個人アクセストークン(PAT)を使用", "免费模型不用填 Key(默认勾上「该上游无需鉴权」,网关自动用匿名凭证);付费模型则填 Zen 的 API Key。免费档上游只认官方客户端形态,已默认开启「伪装 OpenCode 官方客户端」": "無料モデルでは Key の入力は不要です(既定で「このアップストリームは認証不要」がチェックされ、ゲートウェイは自動的に匿名認証情報を使用します);有料モデルでは Zen の API Key を入力します。無料枠のアップストリームは公式クライアントの形態しか受け付けないため、「OpenCode 公式クライアントの偽装」が既定で有効になっています", "内嵌窗口": "内蔵ウィンドウ", "内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)": "内蔵ウィンドウで開きます;ウィンドウを閉じると待機をキャンセルします(Google アカウントが拒否された場合はシステムブラウザをお使いください)", "内嵌窗口打开;完成后自动加入列表,关窗即取消等待": "内蔵ウィンドウで開きます;完了後は自動でリストに追加され、ウィンドウを閉じると待機をキャンセルします", "内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待": "内蔵ウィンドウで開きます。読み取りが完了すると自動で一覧に追加され、ウィンドウを閉じると待機を中止します", "内嵌窗口用全新环境,多账号互不影响;关窗即取消等待": "内蔵ウィンドウは新しい環境を使うため、複数のアカウントが互いに影響しません;ウィンドウを閉じると待機をキャンセルします", "内嵌窗口(推荐)": "内蔵ウィンドウ(推奨)", "准备验证…": "認証を準備中…", "凭据 JSON": "認証情報 JSON", "凭证文件在哪?": "認証情報ファイルの場所は?", "创建并添加账号": "作成してアカウントを追加", "创建这个提供商,并同时建立它的第一个账号。": "このプロバイダーを作成し、同時にその最初のアカウントを作成します。", "删除功能不可用(脚本未就绪)": "削除機能は利用できません(スクリプト未準備)", "删除失败:{reason}": "削除に失敗しました:{reason}", "删除此提供商": "このプロバイダーを削除", "及 {n} 个账号": "、および {n} 個のアカウント", "发送中…": "送信中…", "发送失败:{message}": "送信に失敗しました:{message}", "发送未确认:登录窗口已弹出,请在弹出的窗口中手动完成登录": "送信は未確認です:ログインウィンドウが開きました。表示されたウィンドウで手動でログインを完了してください", "取消失败:{message}": "キャンセルに失敗しました:{message}", "取消等待": "待機をキャンセル", "只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页": "「内蔵ウィンドウ」でのみサードパーティの入り口を復元できます:システムブラウザではログインページを変更できないためです", "可选": "任意", "可选(cloudaicompanionProject):留空由网关自动发现": "任意(cloudaicompanionProject):空欄の場合はゲートウェイが自動検出します", "可选,展示用": "任意、表示用", "可选,没有则无法自动续期": "任意、ないと自動更新できません", "可选,用于生成账号标识": "任意。アカウント識別子の生成に使用", "可选,留空使用凭据里的 user_name": "任意、空欄の場合は認証情報の user_name を使用", "可选,留空使用账号姓名、邮箱或令牌指纹": "任意、空欄の場合はアカウントの氏名、メールアドレス、またはトークンフィンガープリントを使用", "可选,留空使用账号昵称或邮箱": "任意、空欄の場合はアカウントのニックネームまたはメールアドレスを使用", "可选,留空则用 key 尾号": "任意、空欄の場合は key の末尾を使用", "可选,留空则用 userId": "任意、空欄の場合は userId を使用", "可选,留空则用上游返回的账号名或账号 id": "任意、空欄の場合はアップストリームが返したアカウント名またはアカウント id を使用", "可选,留空则用凭证里的账号名": "任意、空欄の場合は認証情報のアカウント名を使用", "可选,留空则用文件里的昵称": "任意。空欄の場合はファイル内のニックネームを使用", "可选,留空则用登录名或 uid": "任意、空欄の場合はログイン名または uid を使用", "可选,留空则用脱敏手机号": "任意、空欄の場合はマスク済み電話番号を使用", "可选,留空则用脱敏手机号或 userId": "任意、空欄の場合はマスク済み電話番号または userId を使用", "可选,留空则用账号 uk": "任意、空欄の場合はアカウントの uk を使用", "可选,留空则用账号昵称": "任意。空欄ならアカウントのニックネームを使用", "可选,留空则用账号昵称或 userId": "任意。空欄の場合はアカウントのニックネームまたは userId を使用", "可选,留空则用邮箱或令牌尾号": "任意、空欄の場合はメールアドレスまたはトークンの末尾を使用", "可选,留空用「AtomCode」": "任意。空欄なら「AtomCode」", "可选,留空用「OfficeAce 果办」": "任意、空欄の場合は「OfficeAce」を使用", "可选,留空自动生成": "任意、空欄の場合は自動生成", "可选,留空自动用昵称": "任意、空欄の場合はニックネームを自動使用", "可选,给这个访客账号起个备注名": "任意。このゲストアカウントのメモ名", "可选,续期时带上": "任意、更新時に一緒に送信されます", "可选,讯飞侧 userid": "任意、iFlytek 側の userid", "可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补": "任意;会話の開始に必須です。空欄の場合はゲートウェイがこのアカウントの既存タスクから自動検出します。見つからない場合は後で手動で補ってください。", "可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)": "任意;アカウントのメールアドレスで、表示と身元識別に使用します(新しいトークンに替えても同じアカウントとして認識されます)", "可选;UUID 形态,与凭据同生共死": "任意;UUID 形式で、認証情報と一体で管理されます", "可选;填了才能到期自动续期": "任意;入力すると期限切れ時に自動更新できます", "可选;用于去重与展示名(留空时网关会问一次上游)": "任意;重複排除と表示名に使用します(空欄の場合、ゲートウェイがアップストリームに一度問い合わせます)", "可选;用于生成账号 id 与展示名": "任意;アカウント id と表示名の生成に使用します", "可选;纯数字串(上游只收 8–24 位数字)": "任意;数字のみの文字列(アップストリームは 8–24 桁の数字のみ受け付けます)", "可领取": "受け取り可能", "同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)": "同じプロバイダーに API Key ありのアカウントと認証不要アカウントを混在でき、それぞれの規則でルーティングされます。認証不要アカウントも複数併存でき、それぞれ別の送信プロキシを設定できます", "同地址换 anthropic 协议即走它的 Claude 兼容端点。转发必须填 OpenCode Go 的 API Key(订阅制;获取模型清单不需要 Key,但发请求上游会回 401「Missing API key」)": "同じアドレスでプロトコルを anthropic に変えると、その Claude 互換エンドポイントを使用します。転送には OpenCode Go の API Key が必須です(サブスクリプション制;モデルカタログの取得に Key は不要ですが、リクエストを送るとアップストリームが 401「Missing API key」を返します)", "响应为空": "応答が空です", "商汤日日新的 OpenAI 兼容端点;余额查询走控制台账号密码(见下方「余额查询」一段)": "SenseNova の OpenAI 互換エンドポイント。残高照会はコンソールのアカウント / パスワードを使います(下の「残高照会」の節を参照)", "国内版": "中国版", "国内版与**国际版是两套独立的账号与套餐**,凭证与领取的套餐都不通用。": "中国版と**国際版は 2 組の独立したアカウントとプラン**で、認証情報と受け取ったプランはどちらも共用できません。", "国内版登录页没有 Google / GitHub 入口(它用微信 / 手机号 / 邮箱登录)": "中国版のログインページには Google / GitHub の入り口がありません(WeChat / 電話番号 / メールアドレスでログインします)", "国内版的登录站点是 www.accio-ai.com,与国际版不是同一站。": "中国版のログインサイトは www.accio-ai.com で、国際版とは別のサイトです。", "国内版(WorkBuddy)": "中国版(WorkBuddy)", "国内站为 https://api.siliconflow.cn/v1": "中国版サイトは https://api.siliconflow.cn/v1", "国内站(minimaxi.com);OpenAI 兼容地址为 https://api.minimaxi.com/v1": "中国版サイト(minimaxi.com);OpenAI 互換アドレスは https://api.minimaxi.com/v1", "国际版": "国際版", "国际版 z.ai 的 Coding 套餐;OpenAI 兼容地址为 https://api.z.ai/api/coding/paas/v4": "国際版 z.ai の Coding プラン;OpenAI 互換アドレスは https://api.z.ai/api/coding/paas/v4", "国际版与国内版是**两套独立的账号**(同一账号体系的两个站点),凭证不通用。": "国際版と中国版は**2 組の独立したアカウント**です(同じアカウント体系の 2 つのサイト)。認証情報は共用できません。", "国际版与国内版账号体系独立,请填国际版的凭证。未填 refreshToken 无法自动续期。": "国際版と中国版はアカウント体系が独立しています。国際版の認証情報を入力してください。refreshToken を入力しないと自動更新できません。", "国际版登录页默认只显示邮箱登录,勾选后恢复 Google / GitHub / X 入口": "国際版のログインページは既定でメールアドレスログインのみ表示されます。チェックすると Google / GitHub / X の入り口が復元されます", "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "国際版の推論サイトは api.z.ai で、中国版とは別のサイトです;プランもそれぞれ独立しています。", "国际版(WorkBuddy AI)": "国際版(WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "国際版サイトの Coding プラン;OpenAI 互換アドレスは https://api.minimax.io/v1", "在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。": "AtomGit の AtomCode ページから accessToken と refreshToken の 2 つを取得して貼り付けてください。リフレッシュトークンの入力推奨 —— このプロバイダーのトークンは JWT ではなく(約 7 日で失効し、失効時刻も読み取れません)、これが無いと自動更新できず、失効後は再ログインが必要になります。", "在 cc-switch 里找到": "cc-switch で", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "内蔵ウィンドウで公式ログインを完了してください。成功すると自動でアカウント一覧に追加されます。", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "{label} の Qoder アカウント設定 → Integrations で PAT を生成してください(Google / GitHub のトークンは入力しないでください)。2 つの地域のアカウントと認証情報は共用できません。", "在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。": "開いた公式認可ページで WeChat の QR コードを読み取ってログインします(ページに QR コードが直接表示されます)。読み取りに成功すると自動でアカウント一覧に追加されます。", "在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "開いたウィンドウで Baidu アカウントを使って KukuAI 本体サイトにログインしてください(SMS 認証コード / QR コードのいずれも可)。ログインに成功すると自動でアカウント一覧に追加されます。ゲートウェイは Baidu から KukuAI の業務セッショントークンを発行してもらいます(本機に KukuAI クライアントがインストールされている必要があります)。", "在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "開いたウィンドウで Google アカウントにログインしてください(Antigravity の認可ページ)。認可が完了すると自動でアカウントリストに追加されます。", "在浏览器中打开登录页": "ブラウザでログインページを開く", "地区": "地域", "填写凭证": "認証情報を入力", "填写凭证添加": "認証情報を入力して追加", "备注名": "ラベル名", "套餐": "プラン", "如:智谱 GLM": "例:Zhipu GLM", "完成登录后自动加入账号列表,昵称取自上游。": "ログイン完了後は自動でアカウント一覧に追加されます。ニックネームはアップストリームから取得します。", "官方客户端凭证目录:Windows 是 C:\\Users\\<你的用户名>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth,macOS 是 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth": "公式クライアントの認証情報ディレクトリ:Windows は C:\\Users\\<ユーザー名>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth、macOS は ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth", "官方已确认到账": "公式が付与を確認済み", "导入": "インポート", "导入中…": "インポート中…", "导入凭证文件": "認証情報ファイルのインポート", "导入失败": "インポートに失敗しました", "导入失败:{reason}": "インポートに失敗しました:{reason}", "导入所选": "選択項目をインポート", "导入所选({n})": "選択項目をインポート({n})", "导入桌面端登录态": "デスクトップ版のログイン状態をインポート", "将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下": "内蔵ウィンドウを開きます;認可が完了すると自動でアカウント一覧に追加されます。ウィンドウを閉じると待機をキャンセルします。認可ページの最後に zcode:// リンクを開けない旨の表示が出ることがありますが、これは正常です —— 結果はサーバー側で記録済みです", "将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)": "内蔵ウィンドウを開きます;認可が完了すると自動でアカウント一覧に追加されます。リモートパネルではコールバックアドレスを貼り付けられます。ウィンドウを閉じると待機をキャンセルします。リンクの有効期限は 5 分で、タイムアウトまたは未クリックの場合は無効になります(再発行できます)", "将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待": "内蔵ウィンドウを開きます;ログイン完了後は自動でアカウント一覧に追加されます。ウィンドウを閉じると待機をキャンセルします", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 Trae 账号);同机完成后自动加入,Docker / 远程面板按提示粘贴地址栏回调地址。": "システムの既定ブラウザで認可ページを開きます(ブラウザでログイン済みの Trae アカウントが再利用されます);同一マシンでは完了後に自動追加され、Docker / リモートパネルでは案内に従ってアドレスバーのコールバックアドレスを貼り付けてください。", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 ZCode 账号);授权完成后自动加入账号列表。页面最后可能提示无法打开 zcode:// 链接,属正常现象": "システムの既定ブラウザで認可ページを開きます(ブラウザでログイン済みの ZCode アカウントが再利用されます);認可完了後は自動でアカウント一覧に追加されます。ページの最後に zcode:// リンクを開けない旨の表示が出ることがありますが、正常な動作です", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "システムの既定ブラウザで認可ページを開きます(ブラウザでログイン済みの Huawei Cloud アカウントが再利用されます);認可コードはゲートウェイがクラウドをポーリングして取得するため、ブラウザがどのマシンで動いていても関係ありません。完了後は自動でアカウント一覧に追加されます", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "システムの既定ブラウザで認可ページを開きます(ブラウザでログイン済みの Huawei Cloud アカウントが再利用されます);ブラウザとゲートウェイが別のマシンにある場合は、上の説明に従って最終的なコールバックアドレスをパネルに貼り付けてください", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "システムの既定ブラウザでログインページを開きます(ブラウザでログイン済みの Accio アカウントが再利用されます);ログイン完了後は自動でアカウント一覧に追加され、ポップアップを閉じると待機をキャンセルします", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "システムの既定ブラウザでログインページを開きます(ブラウザでログイン済みの LobsterAI アカウントが再利用されます);ログイン完了後は自動でアカウント一覧に追加され、ポップアップを閉じると待機をキャンセルします", "小米 MiMo Token Plan 套餐(新加坡站)": "小米 MiMo Token Plan プラン(シンガポールサイト)", "已到账": "付与済み", "已取消登录等待": "ログイン待ちをキャンセルしました", "已取消领取": "受け取りをキャンセルしました", "已取消验证码": "認証をキャンセルしました", "已填写手机号,正在发送验证码…": "電話番号を入力しました。認証コードを送信中…", "已声明无需鉴权:账号会被正常选路,出网时不带任何鉴权头": "認証不要を宣言済み:アカウントは通常どおりルーティングされ、送信時に認証ヘッダーは一切付きません", "已导入 {ok} 个,失败 {fail} 个(失败项已标注在列表里)": "{ok} 個をインポートしました。失敗 {fail} 個(失敗した項目にはリスト内でマークが付いています)", "已建过 · {n} 个账号,点击继续添加": "作成済み · {n} 個のアカウント、クリックして追加", "已建过,点击继续添加账号": "作成済み、クリックしてアカウントを追加", "已打开官方登录页,请在窗口中完成登录…": "公式ログインページを開きました。ウィンドウでログインを完了してください…", "已点发送,等待短信…": "送信しました。SMS を待っています…", "已用系统默认浏览器打开登录页,请在浏览器中完成登录…": "システムの既定ブラウザでログインページを開きました。ブラウザでログインを完了してください…", "已领取,等官方确认": "受け取り済み、公式の確認待ち", "当前壳版本不支持 AutoClaw 网页登录,请更新应用": "現在のシェルバージョンは AutoClaw のウェブログインに対応していません。アプリを更新してください", "当前没有可领取的套餐(活动尚未开始)": "現在受け取り可能なプランはありません(イベントはまだ開始されていません)", "当前环境不支持领取(桥接方法缺失)": "現在の環境では受け取りに対応していません(ブリッジメソッドがありません)", "必填,CatPaw 账号标识": "必須、CatPaw アカウントの識別子", "恢复 Google / GitHub 入口": "Google / GitHub の入り口を復元", "手机号": "電話番号", "手机验证码登录": "SMS 認証コードログイン", "打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。": "公式の LobsterAI ログインページ(lobsterai.youdao.com)を開き、LobsterAI アカウントでログインしてください:ログインに成功すると公式ページが本機に戻り、自動でアカウント一覧に追加されます —— コールバックはゲートウェイ自身のポートに届くため、ブラウザはゲートウェイと同じマシンで開く必要があります。認証コードは本機内でゲートウェイに渡されるだけで、画面に平文トークンは表示されません。", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Trae SOLO の公式認可ページ(trae.cn)を開き、あなたの Trae アカウントでログインしてください:認可が完了すると公式ページから本機の一時ポートにリダイレクトされます;同一マシンでのデプロイでは自動受信し、Docker / リモートパネルでは案内に従ってアドレスバーのコールバックアドレスをコピーしてください。", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Accio {label}の公式ログインページ({site})を開き、あなたの Accio アカウントでログインしてください:ログインに成功すると公式ページから本機にリダイレクトされます;同一マシンでは自動完了し、Docker / リモートパネルでは案内に従ってコールバックアドレスを貼り付けてください(認可コードは本機からゲートウェイに渡されるだけで、画面に平文 token は表示されません)。", "打开 Accio {label}登录页": "Accio {label} のログインページを開く", "打开 Antigravity 网页登录": "Antigravity のウェブログインを開く", "打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。": "AtomGit の認可ページを開いてログインしてください。完了後、ゲートウェイが自動的にブローカーをポーリングしてトークンを取得し、アカウントを追加します(コールバックはブローカー側で終端され本機には戻らないため、コールバック URL の手入力は不要です)。", "打开 CatPaw 网页登录": "CatPaw のウェブログインを開く", "打开 Cline 授权页": "Cline の認可ページを開く", "打开 CodeArts 授权页": "CodeArts の認可ページを開く", "打开 LobsterAI 登录页": "LobsterAI のログインページを開く", "打开 Qoder 网页登录": "Qoder のウェブログインを開く", "打开 Trae 授权页": "Trae の認可ページを開く", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "ZCode {label}の公式認可ページを開き、あなたのアカウントでログインしてください。認可結果は ZCode サーバー側で記録され、ゲートウェイがバックグラウンドで自動的に認証情報を取得してアカウント一覧に追加します。", "打开 ZCode {label}授权页": "ZCode {label} の認可ページを開く", "打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
    网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。": "Huawei Cloud CodeArts の公式認可ページを開いてログインします:ログイン完了後、公式ページからブラウザがゲートウェイ自身の /oauth/callback に戻され、ゲートウェイはワンタイム認可コードで一時認証情報を取得し、アカウント一覧に追加します。
    ゲートウェイが別のマシンで動作している場合は、ウェブ画面でアドレスバーの最終的なコールバックアドレスをそのままパネルに貼り付けるよう案内されます。", "打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。": "Huawei Cloud の認可ページを開いてログインしてください:完了すると、ゲートウェイがクラウドをポーリングして認可コードを取得し、アカウント一覧に追加します(OfficeAce デスクトップ版は不要で、コールバック URL を手動で貼り戻す必要もありません)。", "打开官方授权页完成设备码授权,登录的是{label}({site})的账号。": "公式認可ページを開いてデバイスコード認可を完了します。ログインするのは {label}({site})のアカウントです。", "打开授权页": "認可ページを開く", "打开授权页完成确认,账号自动加入 {label}{note}": "認可ページを開いて確認を完了すると、アカウントが自動で {label} に追加されます{note}", "打开方式": "開き方", "打开网页登录": "ウェブログインを開く", "探测失败:{message}": "検出に失敗しました:{message}", "接入一个兼容上游": "互換アップストリームを接続", "提交中…": "送信中…", "搜索提供商": "プロバイダーを検索", "搜索提供商…": "プロバイダーを検索…", "整份粘贴官方插件 / CLIProxyAPI 落盘的凭据 JSON(形如 {\"codearts_provider_credential\":{…}},铺平的也行)。
    必填:access_key_id、secret_access_key、security_token。要能自动续期,必须连 refresh_token 与 oauth_context 一起粘 —— 临时凭据约一小时到期,缺这半块就续不回来,只能重新登录。": "公式プラグイン / CLIProxyAPI が保存した認証情報 JSON をまるごと貼り付けてください({\"codearts_provider_credential\":{…}} の形。フラットなものでも可)。
    必須:access_key_id、secret_access_key、security_token。自動更新を効かせるには refresh_token と oauth_context も一緒に貼り付ける必要があります —— 一時認証情報は約 1 時間で期限切れになり、この半分が欠けると更新できず、再ログインするしかありません。", "文件是{edition}凭证,已按该版本导入": "ファイルは{edition}の認証情報のため、そのバージョンでインポートしました", "新建自定义提供商": "カスタムプロバイダーを新規作成", "方案:{label}": "方式:{label}", "明文 JWT 或 auth.json 里的 enc: 加密值(自动解密)": "平文 JWT または auth.json 内の enc: 暗号化値(自動復号)", "明文 JWT(国际版账号的 access token)": "平文 JWT(国際版アカウントの access token)", "智谱 bigmodel.cn 的 Coding 套餐,Key 在 open.bigmodel.cn/usercenter/apikeys": "Zhipu bigmodel.cn の Coding プラン。Key は open.bigmodel.cn/usercenter/apikeys で取得します", "未知错误": "不明なエラー", "未能获取授权地址,请重试": "認可アドレスを取得できませんでした。再試行してください", "未能读取 cc-switch 数据": "cc-switch のデータを読み取れませんでした", "未领取成功": "受け取りに失敗しました", "本地 Ollama、OpenCode Zen 免费档这类上游要勾上,否则账号不可用": "ローカル Ollama、OpenCode Zen の無料枠などのアップストリームではチェックを入れてください。入れないとアカウントを使用できません", "本家有两个**互不替代**的凭证,按你要用的功能填,至少填一个:编码套餐 API Key 用于转发推理(打 {site})与查询窗口限额(每 N 小时 / 每周的剩余比例);jwt 用于领取套餐与查询余额(打 zcode.z.ai 的 billing 网关,官方叫 Coding Plan JWT,是一串三段点分的字符串)。余额两把都能查、但读到的东西不同:jwt 读到余额桶与套餐到期,API Key 读到窗口限额;而领取只认 jwt、转发只认 API Key。请填写 {label}账号的凭证 —— {plan}(最容易拿到的办法:直接用上方的「网页登录」—— 它会替你把这个 API Key 换好,两个凭证一起拿到。)": "本家には**互いに代替できない** 2 つの認証情報があり、使いたい機能に応じて入力してください。少なくともどちらか一方は必須です:コーディングプラン API Key は推論の転送用({site} を叩きます)とウィンドウクォータの照会用(N 時間ごと / 週ごとの残り割合);jwt はプランの受け取りと残高照会用(zcode.z.ai の billing ゲートウェイを叩きます。公式名称は Coding Plan JWT で、3 段のドット区切り文字列です)。残高はどちらでも照会できますが、読み取れる内容が異なります:jwt は残高バケットとプランの有効期限、API Key はウィンドウクォータを読み取ります;受け取りは jwt のみ、転送は API Key のみです。{label}アカウントの認証情報を入力してください —— {plan}(最も簡単な方法:上の「ウェブログイン」を使うこと —— この API Key を代わりに交換してくれ、2 つの認証情報をまとめて取得できます。)", "本机 Ollama 的 OpenAI 兼容端点,无需 API Key(默认已勾上「该上游无需鉴权」,鉴权头一个都不发)": "本機の Ollama の OpenAI 互換エンドポイント。API Key は不要です(既定で「このアップストリームは認証不要」がチェック済みで、認証ヘッダーは一切送信されません)", "本次未确认到账": "今回は付与が確認できませんでした", "条可导入": "件がインポート可能", "条配置,其中": "件の設定、うち", "桌面运行时不可用(Tauri 未初始化)": "デスクトップランタイムが利用できません(Tauri が未初期化)", "模型网关基址": "モデルゲートウェイのベース URL", "正在打开百度登录页…": "Baidu のログインページを開いています…", "正在扫描本机 cc-switch…": "本機の cc-switch をスキャンしています…", "正在换发访客凭证…": "ゲスト資格情報を発行中…", "正在添加…": "追加中…", "正在等待{name}登录完成;完成或取消后才能发起新的登录": "{name} のログイン完了を待っています;完了またはキャンセルしないと新しいログインを開始できません", "每周": "毎週", "每日": "毎日", "每月": "毎月", "没有则无法自动续期": "ないと自動更新できません", "没有匹配「{keyword}」的提供商": "「{keyword}」に一致するプロバイダーはありません", "添加 {name} 账号": "{name} アカウントを追加", "添加中…": "追加中…", "添加到 ": "追加先 ", "添加失败:{reason}": "追加に失敗しました:{reason}", "添加小浣熊账号": "小浣熊アカウントを追加", "添加方式": "追加方法", "添加访客账号": "ゲストアカウントを追加", "添加账号": "アカウントを追加", "火山引擎 Coding 套餐;通用推理接入点为 https://ark.cn-beijing.volces.com/api/v3": "Volcano Engine Coding プラン;汎用推論エンドポイントは https://ark.cn-beijing.volces.com/api/v3", "点击添加账号": "クリックしてアカウントを追加", "点开即预填,填 Key 接入": "開くと自動入力済み、Key を入力して接続", "点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
    这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。": "ボタンを押すとまずスライダー認証(公式の不正対策)を 1 回行い、その後ログインページが開きます。ログイン完了で自動的にアカウントが追加されます。
    これが国際版公式の唯一のログイン方式です。すでにクライアントでログイン済みの場合は、「デスクトップ版のログイン状態をインポート」を使う方が速いです。", "用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。": "AutoClaw アカウントに紐付けられた電話番号でログインします:「コードを取得」をクリックし、SMS を受け取ったら下の欄に入力してログインしてください。認証コードは本機から直接公式 API に送信され、画面に token は表示されません。", "用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。": "CatPaw アカウントでログインを完了します(Meituan passport)。成功すると自動でアカウント一覧に追加されます。", "用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。": "Loomy アカウントに紐付けられた電話番号でログインします:「コードを取得」をクリックして入力するだけです。本家で最も手軽な入り口です。", "用于转发;形如 apiKey.secret 两段点分(不填则这个账号不能转发)": "転送用。apiKey.secret の 2 段ドット区切り形式です(未入力の場合、このアカウントは転送できません)", "用于领取套餐与查询余额;不填则这个账号不能领取与查余额": "プランの受け取りと残高照会用。未入力の場合、このアカウントは受け取りと残高照会ができません", "用户 ID": "ユーザー ID", "用绑定的手机号登录:点「获取验证码」后填入即可。这是国内版官方唯一的登录方式。": "紐付けられた電話番号でログインします:「コードを取得」をクリックして入力するだけです。これが中国版公式の唯一のログイン方式です。", "留空则用提供商名称": "空欄の場合はプロバイダー名を使用", "登录 / 添加 {name} 账号": "ログイン / {name} アカウントを追加", "登录 session(一长串)": "ログイン session(長い文字列)", "登录中…": "ログイン中…", "登录失败:{message}": "ログインに失敗しました:{message}", "登录已结束": "ログインは終了しました", "登录并添加": "ログインして追加", "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "ログイン状態ファイルのパス:~/.box-agent/config/auth.json(Windows:C:\\Users\\<ユーザー名>\\.box-agent\\config\\auth.json)", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "カスタムプロバイダー「{name}」を削除しますか?このプロバイダー配下の {count} 個のアカウントも同時に削除され、削除後は元に戻せません。", "等待 Accio {label}登录完成…": "Accio {label} のログイン完了を待っています…", "等待 AtomCode 登录完成…": "AtomCode のログイン完了を待機中…", "等待 CatPaw 登录完成…": "CatPaw のログイン完了を待っています…", "等待 Cline 授权确认…": "Cline の認可確認を待っています…", "等待 CodeArts 登录完成…": "CodeArts のログイン完了を待っています…", "等待 Google 授权完成…": "Google の認可完了を待っています…", "等待 KukuAI 登录完成…": "KukuAI のログイン完了を待っています…", "等待 LobsterAI 登录完成…": "LobsterAI のログイン完了を待っています…", "等待 OfficeAce 登录完成…": "OfficeAce のログイン完了を待っています…", "等待 OrcaTerm 登录完成…": "OrcaTerm のログイン完了を待っています…", "等待 Qoder 授权完成…": "Qoder の認可完了を待っています…", "等待 Trae 授权完成…": "Trae の認可完了を待っています…", "等待 ZCode {label}授权完成…": "ZCode {label} の認可完了を待っています…", "等待小浣熊登录完成…": "小浣熊のログイン完了を待っています…", "等待登录完成…": "ログインの完了を待っています…", "等待网页登录…": "ウェブログインを待っています…", "粘贴 API Key": "API Key を貼り付け", "粘贴 Accio 的 accessToken": "Accio の accessToken を貼り付け", "粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
    这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。": "Antigravity の Google refresh token を貼り付け(1// で始まる;refresh_token= を含む行全体をそのまま貼り付けても可)。
    これはウェブログイン以外の手動入り口です(例:ログイン済みの公式 IDE からエクスポート)。refresh token は期限切れにならず、access token の期限切れはゲートウェイが自動で更新します;トークンを紛失すると Google の認可をやり直すしかないため、大切に保管してください。", "粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。": "Command Code の API Key を貼り付け(user_ で始まる;公式 CLI の ~/.commandcode/auth.json またはウェブの studio から取得)。行全体 / 引用符付き / Bearer プレフィックス付きのまま貼り付けても構いません。ゲートウェイが自動で裸の key に剥がします。key は期限切れにならず、更新用 API もありません。無効になったら取得し直してください;追加時にゲートウェイが軽量な死活確認を 1 回行いますが、失敗しても追加には影響しません。", "粘贴 Cookie": "Cookie を貼り付け", "粘贴 Google refresh token": "Google refresh token を貼り付け", "粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。": "OrcaTerm の OAuth accessToken(ドット区切り 3 パートの JWT。デスクトップクライアントの data.bin か Web 版の Cookie ot_session から取得)を貼り付けます。Bearer / ot_session= を含む行をそのまま貼ってもよく、ゲートウェイが裸のトークンに剥がします。追加後はゲートウェイが自動で更新するため(上流に更新 API があります)、2 時間の有効期限を手動で追う必要はありません。", "粘贴 Qoder 个人访问令牌(pt-…)": "Qoder の個人アクセストークン(pt-…)を貼り付け", "粘贴 accessToken": "accessToken を貼り付け", "粘贴 access_token(一长串 JWT)": "access_token(長い JWT)を貼り付け", "粘贴 session cookie": "session cookie を貼り付け", "粘贴 token / refreshToken": "token / refreshToken を貼り付け", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "workos:… で始まるトークンを貼り付け(プレフィックスがなくても自動で補われます)", "粘贴令牌对": "トークンペアを貼り付け", "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "智谱清言のアクセストークン(3 つのドット区切り部分からなる JWT)を貼り付けてください。取得方法:ブラウザで chatglm.cn を開いてログインし、開発者ツールの Application → Cookies から chatglm_token の値をコピーします(chatglm_token=…; の行をそのまま貼っても可)。貼り付けたあと上流に対して一度検証し、通った場合のみアカウントを保存します。ログイン済みアカウントではポイントと签到の機能が使え、以降はゲートウェイが自動で更新します(上流に更新インターフェースがあります)。", "粘贴登录凭据": "ログイン認証情報を貼り付け", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "貼り付けるのはログイン session です(同時にモデルゲートウェイの token でもあります)。Loomy には更新用 API がなく、session の有効期間は約 14 日です。期限切れ後は再ログインしてください。", "粘贴网关凭据": "ゲートウェイ認証情報を貼り付け", "粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
    {siteNote}
    MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。": "ウェブのログイン状態 cookie monkeycode_ai_session の値を貼り付け(ブラウザーで Cookie 全体をコピーしてそのまま貼り付けても可。ゲートウェイが値を自動で取り出します)。
    {siteNote}
    MonkeyCode には更新用 API がありません:session の有効期間は約 30 日(ハードリミット)で、期限切れ後はウェブに再ログインして貼り付け直せば済みます。", "粘贴访问令牌": "アクセストークンを貼り付け", "系统浏览器": "システムブラウザ", "系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。": "システムブラウザは既存のログイン状態を再利用できます(国際版で推奨);内蔵ウィンドウの方がクリーンです。", "系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表": "システムブラウザで開く(ログイン済みの Cline アカウントを再利用);完了後は自動でリストに追加されます", "系统浏览器打开(复用已登录的 Google 账号);完成后自动加入列表": "システムブラウザで開きます(ログイン済みの Google アカウントを再利用);完了後は自動でリストに追加されます", "系统浏览器打开(复用已登录的 Zai / Google 账号);完成后自动加入列表": "システムブラウザで開く(ログイン済みの Zai / Google アカウントを再利用);完了後は自動でリストに追加されます", "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "システムブラウザで開く(ログイン済みの Meituan アカウントを再利用);完了後は自動でリストに追加されます", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "システムブラウザで開く(ログイン済みアカウントを再利用);完了後は自動でリストに追加されます", "系统默认浏览器": "システムの既定ブラウザ", "续期令牌": "リフレッシュトークン", "编码套餐 API Key": "コーディングプラン API Key", "网关 Secret": "ゲートウェイシークレット", "网页登录": "ウェブログイン", "网页登录的打开方式": "ウェブログインの開き方", "网页登录(Zai / Google)": "ウェブログイン(Zai / Google)", "自定义": "カスタム", "自建聚合面板:填你自己的站点地址。余额查询用该账号的 API Key 打 {base}/v1/usage,不需要额外配置": "自前のアグリゲーションパネル:自分のサイトアドレスを入力。残高照会はこのアカウントの API キーで {base}/v1/usage を叩きます。追加設定不要", "自建聚合面板:填你自己的站点地址;余额查询要另填 NewAPI 的个人访问令牌(见下方「余额查询」一段)": "自前のアグリゲーションパネル:自分のサイトアドレスを入力。残高照会には別途 NewAPI の個人アクセストークンが必要です(下の「残高照会」の節を参照)", "获取风控配置失败,将直接尝试领取:{message}": "不正対策設定の取得に失敗しました。そのまま受け取りを試みます:{message}", "获取验证码": "コードを取得", "访客模式": "ゲストモード", "访客模式(免登录)": "ゲストモード(ログイン不要)", "访问令牌": "アクセストークン", "该上游无需鉴权(不发送鉴权头)": "このアップストリームは認証不要(認証ヘッダーを送信しません)", "该提供商": "このプロバイダー", "该提供商账号添加功能即将上线": "このプロバイダーのアカウント追加機能は近日公開予定です", "该自定义提供商已不存在(可能已被删除),请刷新后重试": "このカスタムプロバイダーは存在しません(削除された可能性があります)。更新してから再試行してください", "请先选择一个自定义提供商": "先にカスタムプロバイダーを選択してください", "请在弹出的滑块中完成验证(官方要求的风控步骤)": "表示されたスライダーで認証を完了してください(公式が求める不正対策ステップ)", "请填写 11 位大陆手机号": "11 桁の中国本土の携帯番号を入力してください", "请填写 6 位数字验证码": "6 桁の認証コードを入力してください", "请填写 Base URL": "Base URL を入力してください", "请填写 {label}": "{label} を入力してください", "请填写名称": "名前を入力してください", "请完成验证…": "認証を完了してください…", "读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows": "%APPDATA%/AutoClaw/auth.json を読み込んで復号します。Windows のみ", "读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录": "~/.cline/data/settings/providers.json を読み込みます。Cline クライアントでログイン済みである必要があります", "读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录": "~/.meituan-catpaw/auth.json を読み込みます。CatPaw クライアントでログイン済みである必要があります", "读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录": "本機の KukuAI クライアントのログイン状態を読み込みます(%APPDATA%\\baidugenflowpro\\Network\\Cookies)。KukuAI クライアントでログイン済みである必要があります", "读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录": "ローカルの OrcaTerm クライアントのログイン状態(%APPDATA%\\com.orcaterm-desktop.app\\data.bin)を読み取ります。OrcaTerm クライアントでのログインが必要です", "读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录": "本機の智谱清言クライアントのログイン状態を読み取ります(%APPDATA%\\chatglm\\Network\\Cookies)。クライアントでログイン済みである必要があります", "读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。": "このマシンの公式クライアント(Tencent WorkBuddy / WorkBuddy AI)で現在ログイン中のアカウントを読み取ります:認証情報ディレクトリ内の最新のログイン状態ファイルを自動で特定します(このマシンの公式クライアントにログイン済みである必要があります)。インポート後はゲートウェイが自動で更新するため、2 時間の有効期限に合わせて手動でトークンを交換する必要はありません。", "读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。": "本機クライアントの現在のログイン状態を読み込みます(平文 Cookie、復号不要)。クライアントの実行中は Cookies ファイルが排他占有されるため、インポート前に KukuAI クライアントを終了してください。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。": "本機クライアントの現在のログイン状態を読み込みます。毎回リアルタイムで読み取ります(この記録を削除してもクライアントのログイン状態には影響しません)。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后点「刷新 Token」同步。": "本機クライアントの現在のログイン状態を読み込みます。毎回リアルタイムで読み取ります(この記録を削除してもクライアントのログイン状態には影響しません)。クライアントで再ログインしたら「Token を更新」をクリックして同期してください。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后重新导入即可。": "本機クライアントの現在のログイン状態を読み込みます。毎回リアルタイムで読み取ります(この記録を削除してもクライアントのログイン状態には影響しません)。クライアントで再ログインしたら再インポートしてください。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。": "本機クライアントの現在のログイン状態を読み込みます。毎回リアルタイムで読み取ります(この記録を削除してもクライアントのログイン状態には影響しません)。Zai / Google でクライアントにログインしているユーザーはこちらを使ってください。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。": "本機クライアントの現在のログイン状態を読み込みます。毎回リアルタイムで読み取ります(この記録を削除してもクライアントのログイン状態には影響しません)。これは {label} に分類されます。", "读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。": "ローカルクライアントが現在ログインしている OAuth トークンを読み取ります(平文 JSON、復号不要)。インポート後はゲートウェイが自動で更新するため、クライアントの 2 時間の有効期限に合わせて手動で切り替える必要はありません。", "读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。": "本機クライアントの現在のログイン状態を読み取ります。クライアントの Cookies は DPAPI 暗号化(Chromium os_crypt)されているため、Windows でのみ利用できます。クライアントの起動中はこのファイルを読めないことがあるので、クライアントを終了してから再試行してください(「資格情報を入力」でトークンを貼り付ける方法でも構いません)。", "账号信息": "アカウント情報", "账号信息不完整,请刷新后重试": "アカウント情報が不完全です。更新してから再試行してください", "账号已添加,但请留意:{warning}": "アカウントを追加しましたが、ご注意ください:{warning}", "账号版本": "アカウントバージョン", "账号类型": "アカウント種別", "账号:{name}
    ": "アカウント:{name}
    ", "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 创建": "Inference Providers ルートを使用します。Key は hf.co/settings/tokens で作成します", "还没有账号": "アカウントはまだありません", "这一家当前不支持网页登录,请改用填写凭证": "このプロバイダーは現在ウェブログインに対応していません。認証情報の入力に切り替えてください", "这个账号当前没有可自动领的活动(只自动领每日登录赠送积分)": "このアカウントには現在自動で受け取れるイベントがありません(自動で受け取るのは毎日ログインで付与されるポイントのみです)", "这份额度走「活动套餐」通道:若转发时提示编码套餐已到期,": "このクォータは「イベントプラン」の経路を使用します:転送時にコーディングプランの期限切れが表示された場合は、", "这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。": "ここには中国サイト(monkeycode-ai.com)のアカウントを入力します:中国サイトの session を貼り付けてください。国際サイト(monkeycode-ai.net)は別のプロバイダーで、アカウントも別です。両者の認証情報は共用できません。", "这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。": "ここには国際サイト(monkeycode-ai.net)のアカウントを入力します:国際サイトの session を貼り付けてください。中国サイト(monkeycode-ai.com)は別のプロバイダーで、アカウントも別です。両者の認証情報は共用できません。", "选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "CatPaw クライアントのログイン状態ファイル auth.json を選択します(Windows:C:\\Users\\<ユーザー名>\\.meituan-catpaw\\auth.json、macOS / Linux:~/.meituan-catpaw/auth.json)。インポート後は認証情報がゲートウェイ側に保存され、単独で転送できるようになります(本機クライアントに依存しません)。", "选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。": "Cline クライアントのログイン状態ファイル providers.json を選択します(Windows:C:\\Users\\<ユーザー名>\\.cline\\data\\settings\\providers.json、macOS / Linux:~/.cline/data/settings/providers.json)。インポート後は認証情報がゲートウェイ側に保存され、単独で転送できるようになります(本機クライアントに依存しません)。この項目は {label} に登録されます。", "选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。": "KukuAI クライアントの Cookies ファイルを選択します(%APPDATA%\\baidugenflowpro\\Network\\Cookies):これは**バイナリ**の SQLite データベースです(拡張子なし、JSON ではありません)。インポート時は選択したファイルのコピーを読み、実行中のクライアントと占有を争いません。したがって**クライアントを終了する必要はありません**。インポート後は認証情報がゲートウェイ側に保存され、単独で転送できるようになります。", "选择凭证文件导入": "認証情報ファイルからインポート", "选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。": "公式クライアントのログイン状態の認証情報ファイル(workbuddy-desktop.info / workbuddy-desktop-ai.info)または wb2api がエクスポートしたアカウント JSON(ネスト / フラット形式どちらも可)を選択してください。", "选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "小浣熊クライアントのログイン状態ファイル auth.json を選択します(Windows:C:\\Users\\<ユーザー名>\\.box-agent\\config\\auth.json、macOS / Linux:~/.box-agent/config/auth.json)。インポート後は認証情報がゲートウェイ側に保存され、単独で転送できるようになります(本機クライアントに依存しません)。", "选择提供商": "プロバイダーを選択", "选择的文件不是合法 JSON": "選択したファイルは有効な JSON ではありません", "选择要添加账号的提供商": "アカウントを追加するプロバイダーを選択", "重新扫描": "再スキャン", "鉴权": "認証", "阿里云 Token Plan 套餐(国际站)": "Alibaba Cloud Token Plan プラン(国際版サイト)", "阿里云百炼 Coding 套餐(国内站)": "Alibaba Cloud Model Studio Coding プラン(中国版サイト)", "阿里云百炼 Coding 套餐(国际站)": "Alibaba Cloud Model Studio Coding プラン(国際版サイト)", "阿里云百炼模型服务(国际站,OpenAI 兼容模式)": "Alibaba Cloud Model Studio モデルサービス(国際版サイト、OpenAI 互換モード)", "需要图形验证码:登录窗口已弹出,请填写后继续": "画像認証が必要です:ログインウィンドウが開きました。入力して続行してください", "预置 API": "プリセット API", "领取 CodeArts 每日福利": "CodeArts の毎日特典を受け取る", "领取失败:{message}": "受け取りに失敗しました:{message}", "领取并让官方确认": "受け取って公式の確認を待つ", "风控验证配置不完整,请稍后重试": "不正対策認証の設定が不完全です。しばらくしてから再試行してください", "验证码": "認証コード", "验证码已发送。收到后填入下方并点「登录并添加」": "認証コードを送信しました。受け取ったら下の欄に入力し、「ログインして追加」をクリックしてください", "验证码已发送,请查看手机短信": "認証コードを送信しました。携帯の SMS をご確認ください", "验证码已发送,请查看手机短信。填入下方并点「登录并添加」即完成登录": "認証コードを送信しました。携帯の SMS をご確認ください。下の欄に入力して「ログインして追加」をクリックすると、ログインが完了します", "验证码已发送,请查看短信": "認証コードを送信しました。SMS をご確認ください", "验证码校验失败,请重试": "認証コードの検証に失敗しました。再試行してください", "验证码校验超时,请重试": "認証コードの検証がタイムアウトしました。再試行してください", "验证码组件不可用,请重试": "認証コンポーネントが利用できません。再試行してください", "验证码组件初始化超时,请重试": "認証コンポーネントの初期化がタイムアウトしました。再試行してください", "验证码组件加载失败,请检查网络后重试": "認証コンポーネントの読み込みに失敗しました。ネットワークを確認して再試行してください", "验证码组件加载异常,请重试": "認証コンポーネントの読み込みでエラーが発生しました。再試行してください", "验证码组件加载超时,请检查网络后重试": "認証コンポーネントの読み込みがタイムアウトしました。ネットワークを確認して再試行してください", "验证码组件未加载,请重启应用后重试": "認証コンポーネントが読み込まれていません。アプリを再起動してから再試行してください", "(免费额度池,模型名带 cline-free/ 前缀)。": "(無料枠プール、モデル名に cline-free/ プレフィックスが付きます)。", "(其中 {n} 把恢复为不限制)": "(うち {n} 個は無制限に戻ります)", "(必填)": "(必須)", "(有效期 {window})": "(有効期間 {window})", "(订阅池,模型名带 cline-pass/ 前缀,需要账号有对应订阅)。": "(サブスクプール、モデル名に cline-pass/ プレフィックスが付きます。アカウントに対応するサブスクリプションが必要です)。", ",下一次最快 {minutes} 分钟后": "、次回は最短 {minutes} 分後", ",并从 {n} 把 Key 的可用提供商里移除": "、Key {n} 個を利用可能なプロバイダーからも除外しました" }); diff --git a/desktop-tauri/ui/i18n/ja/checkin.js b/desktop-tauri/ui/i18n/ja/checkin.js index 2b2ba23f..1ec5edc0 100644 --- a/desktop-tauri/ui/i18n/ja/checkin.js +++ b/desktop-tauri/ui/i18n/ja/checkin.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:ja/checkin —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · ": " · ", "SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新": "SOLO の checkin_credits 受け取り · 自然日 0 時に更新", "UMID 组件安装失败:{error}": "UMID コンポーネントのインストールに失敗しました:{error}", "WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细": "WorkBuddy 中国版 · タスクツリーを照会してワンクリックで点灯/受け取り · 新学期イベントとねこ旅行を含む · 行をクリックで詳細", "WorkBuddy 国际版保活模型,按顺序回退": "WorkBuddy 国際版のキープアライブ用モデル(順番にフォールバック)", "WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关": "WorkBuddy国際版のデイリーアクティビティ用の無料モデル · 全体で1つの設定、スケジュールとは無関係", "{date}(每天 {time})": "{date}(毎日 {time})", "{label} ×{count}({reason})": "{label} ×{count}({reason})", "{n} 项可完成": "完了可能 {n} 件", "{n} 项待领": "{n} 件受け取り待ち", "{total} 账号 · {done} 已签": "{total} アカウント · {done} チェックイン済み", "{total} 项任务 · {pending} 项可完成": "タスク {total} 件 · 完了可能 {pending} 件", "✅ UMID 组件已安装,下轮签到即可领取国际版积分": "✅ UMID コンポーネントをインストールしました。次回のチェックインで国際版の積分を受け取れます", "✅ {message}": "✅ {message}", "✅ 定时签到已开启": "✅ 定期チェックインを有効にしました", "✅ 开学季任务执行完成": "✅ 新学期タスクの実行が完了しました", "✅ 成长任务执行完成:积分 +{credit} 能量 +{energy}": "✅ 成長タスク完了:クレジット +{credit} エネルギー +{energy}", "✅ 新手福利领取完成,累计 {n} 积分": "✅ 初心者ボーナスの受け取りが完了しました。累計 {n} 積分", "✅ 签到完成:{succeeded}/{total} 个账号成功领取{active}": "✅ チェックイン完了:{succeeded}/{total} 件のアカウントが受け取りに成功{active}", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空提交恢复缺省({defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "「キープアライブモデルチェーン」は、WorkBuddy国際版のデイリーアクティビティでアカウントを活性化し続けるための無料モデルです。順に試し、最初の成功で止まります。空で送信すると既定({defaults})に戻します。当日の重複排除+冪等な受け取り:再実行しても「受取済み」が返るだけで、ポイントが二重に加算されることはありません。", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "「キープアライブモデルチェーン」は、WorkBuddy国際版のデイリーアクティビティでアカウントを活性化し続けるための無料モデルです。順に試し、最初の成功で止まります。リストを空にして保存すると既定のチェーン(現在の既定:{defaults})に戻ります。当日の重複排除+冪等な受け取り:再実行しても「受取済み」が返るだけで、ポイントが二重に加算されることはありません。", "「免费领积分」的每日任务 · 逐个领取": "「無料積分受け取り」のデイリータスク · 1 つずつ受け取る", "一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单": "一度きりの特典 · チェックイン後に自動で照会して受け取り、受け取り済みは本機に記録し以降はアップストリームに問い合わせません · 行をクリックしてタスク一覧を展開", "一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证": "その日の報酬を一度にまとめて受け取ります(毎日チェックイン+新規特典) · 冪等性はアップストリームが保証", "一键安装组件": "ワンクリックでインストール", "一键完成": "一括完了", "一键领取({n})": "一括受け取り({n})", "上次执行:{line}": "前回実行:{line}", "上次签到时间": "前回のチェックイン時刻", "上次领取 {time} · 领取需通过滑块验证码": "前回の受け取り {time} · 受け取りにはスライダー認証が必要", "下次 {date}": "次回 {date}", "下次 {date} · 点此管理条目": "次回 {date} · クリックしてスケジュールを管理", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它与 ZCode 的每日/套餐福利领取见「活动福利」。": "デイリーチェックインの対象外:{list}。CodeArts の一度きりの特典は下の「初心者ボーナス」に、CodeArts と ZCode の日次/プラン特典の受け取りは「アクティビティ特典」にあります。", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。": "デイリーチェックインの対象外:{list}。CodeArts の一度きりの特典は下の「初心者ボーナス」、CodeArts のデイリー特典はすぐ上の「デイリーチェックイン」にあります。ZCode のプラン受け取りは「アクティビティ特典」です。", "与国内版同一套任务接口 · 站点不同": "中国版と同じタスク API · サイトが異なる", "今天已领 {n} 份": "本日 {n} 件受け取り済み", "今天未领": "本日は未受取", "今天(北京时间 {day})已领取 · 官方确认到账 {n} 项 · 明天可再领": "本日(北京時間 {day})受け取り済み · 公式に {n} 件の到着を確認 · 明日また受け取れます", "今日已执行": "本日実行済み", "今日已派出": "本日は出発済み", "今日已领取": "本日受け取り済み", "今日签到": "本日のチェックイン", "今日签到进度": "本日のチェックイン進捗", "从清单里移除": "リストから削除", "保活": "キープアライブ", "保活 {n}": "キープアライブ {n}", "保活+领取": "キープアライブ+受け取り", "保活中…": "キープアライブ中…", "保活模型链": "キープアライブモデルチェーン", "全选": "すべて選択", "全部 {n} 家": "全 {n} 社", "全部完成": "すべて完了", "全部查询": "一括クエリ", "全部结果": "すべての結果", "全部账号均可签到": "すべてのアカウントがチェックイン可能", "全部领取": "すべて受け取り済み", "关着:所有条目都不执行": "オフ:どのスケジュールも実行されません", "删除失败:{error}": "削除に失敗しました:{error}", "删除定时条目": "スケジュールを削除", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活并在网页通道建会话点亮日活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "時刻になると、チェックを入れたプロバイダーの利用可能なアカウントを自動でチェックインします(WorkBuddy 国内版は毎日チェックイン API、国際版はデイリーアクティブクエスト:イベントを探し、デイリーアクティブ報酬を受け取り、無料モデルでキープアライブし、Web チャネルでセッションを張って日次アクティブを点灯。小浣熊はデスクトップの日次ポイント経路。AutoClaw は公式クライアントの毎日チェックインタスク。Qoder 中国版はイベント受領で、イベントを付与されていないアカウントは中立的な通知を受け取ります。Qoder 国際版はデバイスのリスク管理 ID(ローカル Qoder クライアント / qodercli の UMID コンポーネントが生成)を持たないと「毎日 100 Credits」イベントが表示されず、コンポーネントがなければエラーではなく明確な説明が返ります。Trae は SOLO の毎日チェックインポイントを受け取り(0 時リセット)、上流がイベントを用意していない、または認証情報にデバイス ID がない場合は「未受領 + 理由」が返ります。Loomy は毎日 1 回初回ログインポイントを付け、KukuAI は「無料ポイント」の日次タスクを受け取り、CodeArts は当日の運営特典を受け取ります(受領 → 確認 → 再読で検証。自動実行は 1 日 6 回 / 10 分間隔の書き込みレート制限内)。OfficeAce はその日の特典を一括で受け取ります(毎日チェックイン + 新規特典))。時刻を逃しても起動時に自動で追補するため、電源が入っていなくても取りこぼしません。各社のチェックイン API は冪等で、繰り返しても二重受け取りにはなりません。", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "時刻になると、チェックしたプロバイダーの利用可能なアカウントを自動でチェックインします(WorkBuddy 中国版はデイリーチェックイン API、国際版はデイリーアクティブタスク:イベントを検出して日活報酬を受け取り、無料モデルでキープアライブ。小浣熊はデスクトップ版のデイリー積分経路。AutoClaw は公式クライアントのデイリーチェックインタスク。Qoder 中国版はイベント受け取りで、イベントが配信されていないアカウントには中立的な案内が出ます。Qoder 国際版はデバイスリスク制御 ID(本機の Qoder クライアント / qodercli の UMID コンポーネントで生成)を付けて初めて「毎日 100 Credits」イベントが見えるようになり、コンポーネントが欠けている場合はエラーではなく明確な説明が出ます。Trae は SOLO のデイリーチェックイン積分を受け取り(自然日 0 時にリセット)、アップストリームがそのアカウントにイベントを開いていない場合や認証情報にデバイス ID がない場合は、エラーではなく「未受け取り + 具体的な理由」が返ります。Loomy は毎日アカウントの初回ログイン積分を 1 回付与します。KukuAI は「無料積分受け取り」のデイリータスクを消化します;CodeArts はその日の運営特典を受け取ります(受け取り → 確認 → 読み戻し検証。自動実行は 1 日 6 回・間隔 10 分の書き込み制限を受けます);OfficeAce はその日の報酬を一度の操作でまとめて受け取ります(毎日チェックイン+新規特典))。時刻を逃しても、起動後に自動で補完チェックインされ、当時電源が入っていなかったことを理由に漏れることはありません。各社のチェックイン API はすべて冪等なので、繰り返し実行しても二重に受け取ることはありません。", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "時刻になると、チェックしたプロバイダーの利用可能なアカウントを自動でチェックインします(WorkBuddy 中国版はデイリーチェックイン API、国際版はデイリーアクティブタスク:イベントを検出して日活報酬を受け取り、無料モデルでキープアライブ。小浣熊はデスクトップ版のデイリー積分経路。AutoClaw は公式クライアントのデイリーチェックインタスク。Qoder 中国版はイベント受け取りで、イベントが配信されていないアカウントには中立的な案内が出ます。Qoder 国際版はデバイスリスク制御 ID(本機の Qoder クライアント / qodercli の UMID コンポーネントで生成)を付けて初めて「毎日 100 Credits」イベントが見えるようになり、コンポーネントが欠けている場合はエラーではなく明確な説明が出ます。Trae は SOLO のデイリーチェックイン積分を受け取り(自然日 0 時にリセット)、アップストリームがそのアカウントにイベントを開いていない場合や認証情報にデバイス ID がない場合は、エラーではなく「未受け取り + 具体的な理由」が返ります。Loomy は毎日アカウントの初回ログイン積分を 1 回付与します。KukuAI は「無料積分受け取り」のデイリータスクを消化します;OfficeAce はその日の報酬を一度の操作でまとめて受け取ります(毎日チェックイン+新規特典))。時刻を逃しても、起動後に自動で補完チェックインされ、当時電源が入っていなかったことを理由に漏れることはありません。各社のチェックイン API はすべて冪等なので、繰り返し実行しても二重に受け取ることはありません。", "到点时逐个签到,已勾 {n} / {total} 家": "定刻に 1 社ずつチェックインします。選択 {n} / {total} 社", "北京时间 · 错过时点开机后会自动补签": "北京時間 · 起動時に自動で補完します", "去领取": "受け取りへ", "另有 {n} 个账号不参与每日签到": "ほかにデイリーチェックインの対象外のアカウントが {n} 件", "只领取已完成任务的奖励(幂等,重复点不会重复入账)": "完了済みタスクの報酬のみ受け取り(冪等・連打しても二重加算されません)", "可完成": "完了可能", "可派出": "出発可能", "可选 · 缺省显示时刻": "任意 · 未指定では時刻を表示", "可领奖": "受け取り可能", "后端未返回定时签到数据": "バックエンドがスケジュールデータを返しませんでした", "后端未返回签到中心数据": "バックエンドがチェックインセンターのデータを返しませんでした", "国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。": "国際版のチェックインには、UMID コンポーネントで生成するデバイスリスク制御 ID が必要です(本機では検出されませんでした)。", "在途": "旅行中", "填入缺省链": "既定のチェーンを入力", "填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)": "時刻と範囲を決めて「保存」を押してください。名前は空欄でも構いません(空欄なら時刻を表示)", "失败 {n}": "失敗 {n}", "套餐明细": "プラン明細", "安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。": "Qoder クライアントまたは qodercli をインストールすれば利用できます(ゲートウェイの再起動は不要)。", "安装中…": "インストール中…", "官方客户端的每日签到任务": "公式クライアントのデイリーチェックインタスク", "定时签到": "定期チェックイン", "定时签到已关闭": "定期チェックインを無効にしました", "尚未查询": "未照会", "尚未查询任务状态": "タスク状態は未クエリ", "巡检一次": "1 回実行", "已保存:{label}": "保存しました:{label}", "已保存:保活模型链": "保存しました:キープアライブモデルチェーン", "已保存:定时条目": "保存しました:スケジュール", "已删除定时条目": "スケジュールを削除しました", "已完成活跃上报(连登已点亮),但活动未开启(本次没有可领的奖励)": "アクティビティを報告しました(連続ログイン達成)が、キャンペーンは未実施です(今回の報酬はなし)", "已抽 {n} 次": "{n} 回抽選しました", "已有结果 {checked} / 共 {total} 个账号": "結果あり {checked} / 全 {total} アカウント", "已签": "チェックイン済み", "已领 {claimed}/{total} · 累计 {earned} 积分": "受け取り済み {claimed}/{total} · 累計 {earned} 積分", "已领养": "飼い主になりました", "已领取": "受け取り済み", "带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件": "デバイスリスク制御 ID を付けて「毎日 100 Credits」を受け取り · UMID コンポーネントが必要", "开学季执行失败:{error}": "新学期の実行に失敗:{error}", "开学季执行完成:{message}": "新学期の実行が完了:{message}", "开学季活动": "新学期イベント", "开着:启用的条目按各自时刻执行": "オン:有効なスケジュールがそれぞれの時刻に実行されます", "待签": "未チェックイン", "待领新手福利": "受け取り待ちの初心者ボーナス", "总开关": "マスタースイッチ", "总开关已关闭 · 在「定时签到」页打开": "マスタースイッチがオフです · 「定期チェックイン」タブで有効化してください", "成功 {n}": "成功 {n}", "成长任务": "成長タスク", "成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}": "成長タスク完了(成功 {done} 件・失敗 {failed} 件):クレジット +{credit} エネルギー +{energy}", "成长任务执行失败:{error}": "成長タスクの実行に失敗:{error}", "执行失败:{error}": "実行に失敗しました:{error}", "执行并抽奖": "実行して抽選", "执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)": "新学期タスクを実行し、抽選回数を使い切る(シェア/会話/エキスパートイベント)", "把缺省链填进清单,再点「保存」生效": "既定のチェーンを一覧に入れます。「保存」で反映されます", "抽奖机会 {n} 次": "抽選チャンス {n} 回", "按住拖动调整顺序": "押したままドラッグで並べ替え", "按住拖动,调整这个模型在链里的顺序": "押したままドラッグして、チェーン内でのこのモデルの順序を変更します", "按序逐个尝试,共 {n} 个": "順番に試行、計 {n} 件", "按结果筛选": "結果で絞り込み", "探测 + 领取 + 保活 · 与定时签到同一套组合": "検出 + 受け取り + キープアライブ · 定期チェックインと同じ組み合わせ", "探测活动并领取日活奖励 · 不做保活": "イベントを検出して日活報酬を受け取り · キープアライブは行わない", "接单 + 点亮 + 领奖(只处理已实测沉淀的任务)": "受注+点灯+受け取り(実測で検証済みのタスクのみ処理)", "搜账号 / 收益 / 错误 / 触发原因": "アカウント / 収益 / エラー / トリガー理由を検索", "收起新建": "新規入力を閉じる", "新建定时条目": "新しい定時エントリ", "新手福利": "初心者ボーナス", "新手福利有 {n} 项领取失败,可重试": "初心者ボーナスで {n} 件の受け取りに失敗しました。再試行できます", "无": "なし", "无独立签到接口 · 每天替账号打一次首次登录积分": "専用のチェックイン API なし · 毎日アカウントの初回ログイン積分を 1 回付与", "无账号": "アカウントなし", "暂不可领": "受け取り不可", "暂不支持": "未対応", "暂无启用的条目 · 点此添加": "有効なスケジュールがありません · クリックして追加", "有未保存的改动": "未保存の変更があります", "未勾选提供商": "プロバイダー未選択", "未开启": "未有効", "未解锁": "未解禁", "未领养(领养需先完成当日对话量)": "未飼育(飼育には当日の会話量の達成が先に必要)", "未领取": "未受け取り", "本地台账里今天还没领过;活动期内每天发一份新套餐": "ローカルの記録では本日まだ受け取っていません。キャンペーン期間中は毎日新しいプランが 1 件配布されます", "本地台账里今天(北京时间)领过的套餐:{plans}": "ローカル記録で本日(北京時間)受け取ったプラン:{plans}", "条启用": "件有効", "查询于 {time}": "{time} にクエリ", "查询任务": "タスクをクエリ", "查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)": "タスクツリーを照会してワンクリックで点灯/受け取り(新学期・ねこ旅行を含む)", "查询失败:{error}": "クエリに失敗しました:{error}", "桌面端每日积分链路": "デスクトップ版のデイリー積分経路", "正在读取定时签到数据…": "定期チェックインデータを読み込み中…", "正在读取签到数据…": "チェックインデータを読み込み中…", "每天 {time}": "毎日 {time}", "每天 {time} · {scope}": "毎日 {time} · {scope}", "每天时刻": "毎日の時刻", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "チェックイン1回ごとに · アカウントごとに1行 · 結果と収益はバックエンドの記録が正", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "毎日のアクティブタスク · Webチャネルの会話で日活を達成、ポイントは受け取りで加算 · 両方は別々に実行可能", "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "毎日ログインポイント · あわせて会員アクティビティを受け取り · ゲストアカウントはポイント体系なし(スキップ)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "デイリー特典 · 受け取り → 確認 → 読み戻し検証 · 北京時間の自然日でリセット", "每日签到": "デイリーチェックイン", "每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签": "スケジュールごとに時刻と範囲 · 当日はそれぞれ重複排除 · 起動時に自動補完", "每账号一条明细 · 点此查看「签到记录」": "アカウントごとに1行 · 「チェックイン記録」を開く", "没有可领套餐的账号(ZCode 未添加)。": "受け取れるプランのあるアカウントがありません(ZCode 未追加)。", "没有可领福利的账号(CodeArts / ZCode 未添加)。": "受け取れる特典のあるアカウントがありません(CodeArts / ZCode が未追加)。", "没有支持成长任务的账号(需要 WorkBuddy 国内版)。": "成長タスクに対応するアカウントがありません(WorkBuddy 中国版が必要)。", "没有支持新手福利的账号": "初心者ボーナスに対応するアカウントがありません", "没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。": "初心者ボーナスに対応するアカウントがありません —— 現在は Loomy、小浣熊、CodeArts の 3 社です。", "没有符合筛选条件的签到记录": "絞り込み条件に一致するチェックイン記録がありません", "没有需要执行的任务": "実行するタスクはありません", "活动福利": "イベント特典", "活动(campaign)领取 · 每天 10:00 刷新": "イベント(campaign)の受け取り · 毎日 10:00 に更新", "活跃上报与保活均未成功,且活动未开启": "アクティビティの報告とキープアライブの両方に失敗し、キャンペーンも未実施です", "活跃上报完成,但保活未成功且活动未开启": "アクティビティの報告は完了しましたが、キープアライブに失敗し、キャンペーンも未実施です", "活跃上报完成,但活动未开启": "アクティビティを報告しましたが、キャンペーンは未実施です", "活跃上报成功": "アクティビティの報告に成功しました", "活跃保活失败": "アクティブのキープアライブに失敗しました", "活跃保活完成": "アクティブのキープアライブが完了しました", "活跃保活完成,但活动未开启": "アクティブのキープアライブは完了しましたが、イベントが有効ではありません", "活跃保活完成,但活跃上报失败且活动未开启": "キープアライブは完了しましたが、アクティビティの報告に失敗し、キャンペーンも未実施です", "添加条目": "スケジュールを追加", "添加模型": "モデルを追加", "清单为空 —— 保存后恢复缺省链": "リストが空です — 保存すると既定のチェーンに戻ります", "清单里已经有「{name}」": "リストに「{name}」はすでにあります", "清空失败:{error}": "クリアに失敗しました:{error}", "清空签到记录": "チェックイン記録をクリア", "点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领": "「プラン明細」で受け取ったプランと有効になる時期を確認できます。キャンペーン期間中は毎日また受け取れます", "点击行展开账号明细 · 单账号签到不受范围限制": "行をクリックしてアカウントの詳細を展開 · 単一アカウントのチェックインは範囲の制限を受けません", "独立链路 · 只提供手动入口 · 不进自动签到": "独立した経路 · 手動入口のみ · 自動チェックインには含まれない", "猫猫旅行": "ねこ旅行", "猫猫旅行失败:{error}": "ねこ旅行に失敗:{error}", "猫猫旅行失败:{message}": "ねこ旅行に失敗:{message}", "猫猫旅行:{message}": "ねこ旅行:{message}", "用免费模型与网页会话维持账号活跃 · 不领取奖励": "無料モデルとWebチャネルの会話でアカウントのアクティブ状態を維持 · 報酬は受け取らない", "确定删除「{name}」?删除后到点不再执行。": "「{name}」を削除しますか?削除後は予定時刻に実行されません。", "确定清空全部签到记录?清空后无法恢复。": "すべてのチェックイン記録をクリアしますか?クリア後は復元できません。", "确定清空当前筛选出的签到记录?清空后无法恢复。": "現在の絞り込みに一致するチェックイン記録をクリアしますか?クリア後は復元できません。", "移除 {name}": "{name} を削除", "立即全部签到": "今すぐ一括チェックイン", "签到": "チェックイン", "签到中…": "チェックイン中…", "签到中心分区": "チェックインセンターのセクション", "签到失败:{error}": "チェックインに失敗しました:{error}", "签到完成:{succeeded}/{total} 成功{active},{failed} 个失败": "チェックイン完了:{succeeded}/{total} 成功{active}、{failed} 件失敗", "签到成功": "チェックインに成功しました", "签到提供商": "チェックインプロバイダー", "签到范围": "チェックイン範囲", "签到触发时刻": "チェックイン実行時刻", "签到记录": "チェックイン記録", "签到记录时间范围": "チェックイン記録の時間範囲", "签到记录表": "チェックイン記録テーブル", "累计签到记录": "累計チェックイン記録", "结算于 {time}": "{time} に確定", "能量 {n}": "エネルギー {n}", "腾讯每日签到接口 · 仅国内版": "Tencent のデイリーチェックイン API · 中国版のみ", "至少勾一家才能保存(不勾等于这条条目不签到)": "保存するには 1 社以上選択してください(未選択=このエントリは何もチェックインしません)", "读取定时签到数据失败:{error}": "定期チェックインデータの読み込みに失敗しました:{error}", "读取签到数据失败:{error}": "チェックインデータの読み込みに失敗しました:{error}", "读取签到记录失败:{error}": "チェックイン記録の読み込みに失敗しました:{error}", "跳过 {n}": "スキップ {n}", "运营活动交付(领取 → 确认 → 回读核实)· 领的是套餐赠送积分": "運営イベントの受け渡し(受け取り → 確認 → 読み戻しで検証)· 受け取るのはプラン特典積分", "还没执行过": "まだ実行されていません", "还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。": "スケジュールがまだありません —— 「スケジュールを追加」で作成するか、「チェックインセンター」で手動チェックインしてください。", "还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。": "チェックイン記録はまだありません —— 手動チェックインまたは定期実行の後に、明細がここに表示されます。", "连登 {n} 天": "連続ログイン {n} 日", "部分": "一部", "重签": "再チェックイン", "重试": "再試行", "限时体验套餐 · 领取需滑块验证码 · 逐份查看与领取": "期間限定お試しプラン · 受け取りにはスライダー認証が必要 · 1 件ずつ確認して受け取り", "限时体验套餐(start-plan),活动期内每天一份 · 领取需滑块验证码": "期間限定体験プラン(start-plan)。開催期間中は毎日 1 件 · 受け取りにはスライダー認証が必要", "需真实操作": "実際の操作が必要", "非活动期": "イベント期間外", "项": "件", "领取": "受け取る", "领取中": "受け取り中", "领取中…": "受け取り中…", "领取失败": "受け取りに失敗", "领取失败:{error}": "受け取りに失敗しました:{error}", "(共 {n} 个账号)": "(全 {n} アカウント)", "(能量 / 连登读数本次未取到)": "(エネルギー/連続ログインの数値は今回取得できませんでした)", ",日活保活 {n} 个": "、日活キープアライブ {n} 件" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · ": " · ", "SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新": "SOLO の checkin_credits 受け取り · 自然日 0 時に更新", "UMID 组件安装失败:{error}": "UMID コンポーネントのインストールに失敗しました:{error}", "WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细": "WorkBuddy 中国版 · タスクツリーを照会してワンクリックで点灯/受け取り · 新学期イベントとねこ旅行を含む · 行をクリックで詳細", "WorkBuddy 国际版保活模型,按顺序回退": "WorkBuddy 国際版のキープアライブ用モデル(順番にフォールバック)", "WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关": "WorkBuddy国際版のデイリーアクティビティ用の無料モデル · 全体で1つの設定、スケジュールとは無関係", "{date}(每天 {time})": "{date}(毎日 {time})", "{label} ×{count}({reason})": "{label} ×{count}({reason})", "{n} 项可完成": "完了可能 {n} 件", "{n} 项待领": "{n} 件受け取り待ち", "{total} 账号 · {done} 已签": "{total} アカウント · {done} チェックイン済み", "{total} 项任务 · {pending} 项可完成": "タスク {total} 件 · 完了可能 {pending} 件", "✅ UMID 组件已安装,下轮签到即可领取国际版积分": "✅ UMID コンポーネントをインストールしました。次回のチェックインで国際版の積分を受け取れます", "✅ {message}": "✅ {message}", "✅ 定时签到已开启": "✅ 定期チェックインを有効にしました", "✅ 开学季任务执行完成": "✅ 新学期タスクの実行が完了しました", "✅ 成长任务执行完成:积分 +{credit} 能量 +{energy}": "✅ 成長タスク完了:クレジット +{credit} エネルギー +{energy}", "✅ 新手福利领取完成,累计 {n} 积分": "✅ 初心者ボーナスの受け取りが完了しました。累計 {n} 積分", "✅ 签到完成:{succeeded}/{total} 个账号成功领取{active}": "✅ チェックイン完了:{succeeded}/{total} 件のアカウントが受け取りに成功{active}", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空提交恢复缺省({defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "「キープアライブモデルチェーン」は、WorkBuddy国際版のデイリーアクティビティでアカウントを活性化し続けるための無料モデルです。順に試し、最初の成功で止まります。空で送信すると既定({defaults})に戻します。当日の重複排除+冪等な受け取り:再実行しても「受取済み」が返るだけで、ポイントが二重に加算されることはありません。", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "「キープアライブモデルチェーン」は、WorkBuddy国際版のデイリーアクティビティでアカウントを活性化し続けるための無料モデルです。順に試し、最初の成功で止まります。リストを空にして保存すると既定のチェーン(現在の既定:{defaults})に戻ります。当日の重複排除+冪等な受け取り:再実行しても「受取済み」が返るだけで、ポイントが二重に加算されることはありません。", "「免费领积分」的每日任务 · 逐个领取": "「無料積分受け取り」のデイリータスク · 1 つずつ受け取る", "一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单": "一度きりの特典 · チェックイン後に自動で照会して受け取り、受け取り済みは本機に記録し以降はアップストリームに問い合わせません · 行をクリックしてタスク一覧を展開", "一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证": "その日の報酬を一度にまとめて受け取ります(毎日チェックイン+新規特典) · 冪等性はアップストリームが保証", "一键安装组件": "ワンクリックでインストール", "一键完成": "一括完了", "一键领取({n})": "一括受け取り({n})", "上次执行:{line}": "前回実行:{line}", "上次签到时间": "前回のチェックイン時刻", "上次领取 {time} · 领取需通过滑块验证码": "前回の受け取り {time} · 受け取りにはスライダー認証が必要", "下次 {date}": "次回 {date}", "下次 {date} · 点此管理条目": "次回 {date} · クリックしてスケジュールを管理", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它与 ZCode 的每日/套餐福利领取见「活动福利」。": "デイリーチェックインの対象外:{list}。CodeArts の一度きりの特典は下の「初心者ボーナス」に、CodeArts と ZCode の日次/プラン特典の受け取りは「アクティビティ特典」にあります。", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。": "デイリーチェックインの対象外:{list}。CodeArts の一度きりの特典は下の「初心者ボーナス」、CodeArts のデイリー特典はすぐ上の「デイリーチェックイン」にあります。ZCode のプラン受け取りは「アクティビティ特典」です。", "与国内版同一套任务接口 · 站点不同": "中国版と同じタスク API · サイトが異なる", "今天已领 {n} 份": "本日 {n} 件受け取り済み", "今天未领": "本日は未受取", "今天(北京时间 {day})已领取 · 官方确认到账 {n} 项 · 明天可再领": "本日(北京時間 {day})受け取り済み · 公式に {n} 件の到着を確認 · 明日また受け取れます", "今日已执行": "本日実行済み", "今日已派出": "本日は出発済み", "今日已领取": "本日受け取り済み", "今日签到": "本日のチェックイン", "今日签到进度": "本日のチェックイン進捗", "从清单里移除": "リストから削除", "保活": "キープアライブ", "保活 {n}": "キープアライブ {n}", "保活+领取": "キープアライブ+受け取り", "保活中…": "キープアライブ中…", "保活模型链": "キープアライブモデルチェーン", "全选": "すべて選択", "全部 {n} 家": "全 {n} 社", "全部完成": "すべて完了", "全部查询": "一括クエリ", "全部结果": "すべての結果", "全部账号均可签到": "すべてのアカウントがチェックイン可能", "全部领取": "すべて受け取り済み", "关着:所有条目都不执行": "オフ:どのスケジュールも実行されません", "删除失败:{error}": "削除に失敗しました:{error}", "删除定时条目": "スケジュールを削除", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活并在网页通道建会话点亮日活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "時刻になると、チェックを入れたプロバイダーの利用可能なアカウントを自動でチェックインします(WorkBuddy 国内版は毎日チェックイン API、国際版はデイリーアクティブクエスト:イベントを探し、デイリーアクティブ報酬を受け取り、無料モデルでキープアライブし、Web チャネルでセッションを張って日次アクティブを点灯。小浣熊はデスクトップの日次ポイント経路。AutoClaw は公式クライアントの毎日チェックインタスク。Qoder 中国版はイベント受領で、イベントを付与されていないアカウントは中立的な通知を受け取ります。Qoder 国際版はデバイスのリスク管理 ID(ローカル Qoder クライアント / qodercli の UMID コンポーネントが生成)を持たないと「毎日 100 Credits」イベントが表示されず、コンポーネントがなければエラーではなく明確な説明が返ります。Trae は SOLO の毎日チェックインポイントを受け取り(0 時リセット)、上流がイベントを用意していない、または認証情報にデバイス ID がない場合は「未受領 + 理由」が返ります。Loomy は毎日 1 回初回ログインポイントを付け、KukuAI は「無料ポイント」の日次タスクを受け取り、CodeArts は当日の運営特典を受け取ります(受領 → 確認 → 再読で検証。自動実行は 1 日 6 回 / 10 分間隔の書き込みレート制限内)。OfficeAce はその日の特典を一括で受け取ります(毎日チェックイン + 新規特典))。時刻を逃しても起動時に自動で追補するため、電源が入っていなくても取りこぼしません。各社のチェックイン API は冪等で、繰り返しても二重受け取りにはなりません。", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "時刻になると、チェックしたプロバイダーの利用可能なアカウントを自動でチェックインします(WorkBuddy 中国版はデイリーチェックイン API、国際版はデイリーアクティブタスク:イベントを検出して日活報酬を受け取り、無料モデルでキープアライブ。小浣熊はデスクトップ版のデイリー積分経路。AutoClaw は公式クライアントのデイリーチェックインタスク。Qoder 中国版はイベント受け取りで、イベントが配信されていないアカウントには中立的な案内が出ます。Qoder 国際版はデバイスリスク制御 ID(本機の Qoder クライアント / qodercli の UMID コンポーネントで生成)を付けて初めて「毎日 100 Credits」イベントが見えるようになり、コンポーネントが欠けている場合はエラーではなく明確な説明が出ます。Trae は SOLO のデイリーチェックイン積分を受け取り(自然日 0 時にリセット)、アップストリームがそのアカウントにイベントを開いていない場合や認証情報にデバイス ID がない場合は、エラーではなく「未受け取り + 具体的な理由」が返ります。Loomy は毎日アカウントの初回ログイン積分を 1 回付与します。KukuAI は「無料積分受け取り」のデイリータスクを消化します;CodeArts はその日の運営特典を受け取ります(受け取り → 確認 → 読み戻し検証。自動実行は 1 日 6 回・間隔 10 分の書き込み制限を受けます);OfficeAce はその日の報酬を一度の操作でまとめて受け取ります(毎日チェックイン+新規特典))。時刻を逃しても、起動後に自動で補完チェックインされ、当時電源が入っていなかったことを理由に漏れることはありません。各社のチェックイン API はすべて冪等なので、繰り返し実行しても二重に受け取ることはありません。", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "時刻になると、チェックしたプロバイダーの利用可能なアカウントを自動でチェックインします(WorkBuddy 中国版はデイリーチェックイン API、国際版はデイリーアクティブタスク:イベントを検出して日活報酬を受け取り、無料モデルでキープアライブ。小浣熊はデスクトップ版のデイリー積分経路。AutoClaw は公式クライアントのデイリーチェックインタスク。Qoder 中国版はイベント受け取りで、イベントが配信されていないアカウントには中立的な案内が出ます。Qoder 国際版はデバイスリスク制御 ID(本機の Qoder クライアント / qodercli の UMID コンポーネントで生成)を付けて初めて「毎日 100 Credits」イベントが見えるようになり、コンポーネントが欠けている場合はエラーではなく明確な説明が出ます。Trae は SOLO のデイリーチェックイン積分を受け取り(自然日 0 時にリセット)、アップストリームがそのアカウントにイベントを開いていない場合や認証情報にデバイス ID がない場合は、エラーではなく「未受け取り + 具体的な理由」が返ります。Loomy は毎日アカウントの初回ログイン積分を 1 回付与します。KukuAI は「無料積分受け取り」のデイリータスクを消化します;OfficeAce はその日の報酬を一度の操作でまとめて受け取ります(毎日チェックイン+新規特典))。時刻を逃しても、起動後に自動で補完チェックインされ、当時電源が入っていなかったことを理由に漏れることはありません。各社のチェックイン API はすべて冪等なので、繰り返し実行しても二重に受け取ることはありません。", "到点时逐个签到,已勾 {n} / {total} 家": "定刻に 1 社ずつチェックインします。選択 {n} / {total} 社", "北京时间 · 错过时点开机后会自动补签": "北京時間 · 起動時に自動で補完します", "去领取": "受け取りへ", "另有 {n} 个账号不参与每日签到": "ほかにデイリーチェックインの対象外のアカウントが {n} 件", "只领取已完成任务的奖励(幂等,重复点不会重复入账)": "完了済みタスクの報酬のみ受け取り(冪等・連打しても二重加算されません)", "可完成": "完了可能", "可派出": "出発可能", "可选 · 缺省显示时刻": "任意 · 未指定では時刻を表示", "可领奖": "受け取り可能", "后端未返回定时签到数据": "バックエンドがスケジュールデータを返しませんでした", "后端未返回签到中心数据": "バックエンドがチェックインセンターのデータを返しませんでした", "国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。": "国際版のチェックインには、UMID コンポーネントで生成するデバイスリスク制御 ID が必要です(本機では検出されませんでした)。", "在途": "旅行中", "填入缺省链": "既定のチェーンを入力", "填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)": "時刻と範囲を決めて「保存」を押してください。名前は空欄でも構いません(空欄なら時刻を表示)", "失败 {n}": "失敗 {n}", "套餐明细": "プラン明細", "安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。": "Qoder クライアントまたは qodercli をインストールすれば利用できます(ゲートウェイの再起動は不要)。", "安装中…": "インストール中…", "官方客户端的每日签到任务": "公式クライアントのデイリーチェックインタスク", "定时签到": "定期チェックイン", "定时签到已关闭": "定期チェックインを無効にしました", "尚未查询": "未照会", "尚未查询任务状态": "タスク状態は未クエリ", "巡检一次": "1 回実行", "已保存:{label}": "保存しました:{label}", "已保存:保活模型链": "保存しました:キープアライブモデルチェーン", "已保存:定时条目": "保存しました:スケジュール", "已删除定时条目": "スケジュールを削除しました", "已完成活跃上报(连登已点亮),但活动未开启(本次没有可领的奖励)": "アクティビティを報告しました(連続ログイン達成)が、キャンペーンは未実施です(今回の報酬はなし)", "已抽 {n} 次": "{n} 回抽選しました", "已有结果 {checked} / 共 {total} 个账号": "結果あり {checked} / 全 {total} アカウント", "已签": "チェックイン済み", "已领 {claimed}/{total} · 累计 {earned} 积分": "受け取り済み {claimed}/{total} · 累計 {earned} 積分", "已领养": "飼い主になりました", "已领取": "受け取り済み", "带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件": "デバイスリスク制御 ID を付けて「毎日 100 Credits」を受け取り · UMID コンポーネントが必要", "开学季执行失败:{error}": "新学期の実行に失敗:{error}", "开学季执行完成:{message}": "新学期の実行が完了:{message}", "开学季活动": "新学期イベント", "开着:启用的条目按各自时刻执行": "オン:有効なスケジュールがそれぞれの時刻に実行されます", "待签": "未チェックイン", "待领新手福利": "受け取り待ちの初心者ボーナス", "总开关": "マスタースイッチ", "总开关已关闭 · 在「定时签到」页打开": "マスタースイッチがオフです · 「定期チェックイン」タブで有効化してください", "成功 {n}": "成功 {n}", "成长任务": "成長タスク", "成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}": "成長タスク完了(成功 {done} 件・失敗 {failed} 件):クレジット +{credit} エネルギー +{energy}", "成长任务执行失败:{error}": "成長タスクの実行に失敗:{error}", "执行失败:{error}": "実行に失敗しました:{error}", "执行并抽奖": "実行して抽選", "执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)": "新学期タスクを実行し、抽選回数を使い切る(シェア/会話/エキスパートイベント)", "把缺省链填进清单,再点「保存」生效": "既定のチェーンを一覧に入れます。「保存」で反映されます", "抽奖机会 {n} 次": "抽選チャンス {n} 回", "按住拖动调整顺序": "押したままドラッグで並べ替え", "按住拖动,调整这个模型在链里的顺序": "押したままドラッグして、チェーン内でのこのモデルの順序を変更します", "按序逐个尝试,共 {n} 个": "順番に試行、計 {n} 件", "按结果筛选": "結果で絞り込み", "探测 + 领取 + 保活 · 与定时签到同一套组合": "検出 + 受け取り + キープアライブ · 定期チェックインと同じ組み合わせ", "探测活动并领取日活奖励 · 不做保活": "イベントを検出して日活報酬を受け取り · キープアライブは行わない", "接单 + 点亮 + 领奖(只处理已实测沉淀的任务)": "受注+点灯+受け取り(実測で検証済みのタスクのみ処理)", "搜账号 / 收益 / 错误 / 触发原因": "アカウント / 収益 / エラー / トリガー理由を検索", "收起新建": "新規入力を閉じる", "新建定时条目": "新しい定時エントリ", "新手福利": "初心者ボーナス", "新手福利有 {n} 项领取失败,可重试": "初心者ボーナスで {n} 件の受け取りに失敗しました。再試行できます", "无": "なし", "无独立签到接口 · 每天替账号打一次首次登录积分": "専用のチェックイン API なし · 毎日アカウントの初回ログイン積分を 1 回付与", "无账号": "アカウントなし", "暂不可领": "受け取り不可", "暂不支持": "未対応", "暂无启用的条目 · 点此添加": "有効なスケジュールがありません · クリックして追加", "有未保存的改动": "未保存の変更があります", "未勾选提供商": "プロバイダー未選択", "未开启": "未有効", "未解锁": "未解禁", "未领养(领养需先完成当日对话量)": "未飼育(飼育には当日の会話量の達成が先に必要)", "未领取": "未受け取り", "本地台账里今天还没领过;活动期内每天发一份新套餐": "ローカルの記録では本日まだ受け取っていません。キャンペーン期間中は毎日新しいプランが 1 件配布されます", "本地台账里今天(北京时间)领过的套餐:{plans}": "ローカル記録で本日(北京時間)受け取ったプラン:{plans}", "条启用": "件有効", "查询于 {time}": "{time} にクエリ", "查询任务": "タスクをクエリ", "查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)": "タスクツリーを照会してワンクリックで点灯/受け取り(新学期・ねこ旅行を含む)", "查询失败:{error}": "クエリに失敗しました:{error}", "桌面端每日积分链路": "デスクトップ版のデイリー積分経路", "正在读取定时签到数据…": "定期チェックインデータを読み込み中…", "正在读取签到数据…": "チェックインデータを読み込み中…", "每天 {time}": "毎日 {time}", "每天 {time} · {scope}": "毎日 {time} · {scope}", "每天时刻": "毎日の時刻", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "チェックイン1回ごとに · アカウントごとに1行 · 結果と収益はバックエンドの記録が正", "每日活动签到(+100 积分,已领则中性提示)": "毎日のアクティビティ签到(+100 クレジット、受領済みなら中立の案内)", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "毎日のアクティブタスク · Webチャネルの会話で日活を達成、ポイントは受け取りで加算 · 両方は別々に実行可能", "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "毎日ログインポイント · あわせて会員アクティビティを受け取り · ゲストアカウントはポイント体系なし(スキップ)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "デイリー特典 · 受け取り → 確認 → 読み戻し検証 · 北京時間の自然日でリセット", "每日签到": "デイリーチェックイン", "每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签": "スケジュールごとに時刻と範囲 · 当日はそれぞれ重複排除 · 起動時に自動補完", "每账号一条明细 · 点此查看「签到记录」": "アカウントごとに1行 · 「チェックイン記録」を開く", "没有可领套餐的账号(ZCode 未添加)。": "受け取れるプランのあるアカウントがありません(ZCode 未追加)。", "没有可领福利的账号(CodeArts / ZCode 未添加)。": "受け取れる特典のあるアカウントがありません(CodeArts / ZCode が未追加)。", "没有支持成长任务的账号(需要 WorkBuddy 国内版)。": "成長タスクに対応するアカウントがありません(WorkBuddy 中国版が必要)。", "没有支持新手福利的账号": "初心者ボーナスに対応するアカウントがありません", "没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。": "初心者ボーナスに対応するアカウントがありません —— 現在は Loomy、小浣熊、CodeArts の 3 社です。", "没有符合筛选条件的签到记录": "絞り込み条件に一致するチェックイン記録がありません", "没有需要执行的任务": "実行するタスクはありません", "活动福利": "イベント特典", "活动(campaign)领取 · 每天 10:00 刷新": "イベント(campaign)の受け取り · 毎日 10:00 に更新", "活跃上报与保活均未成功,且活动未开启": "アクティビティの報告とキープアライブの両方に失敗し、キャンペーンも未実施です", "活跃上报完成,但保活未成功且活动未开启": "アクティビティの報告は完了しましたが、キープアライブに失敗し、キャンペーンも未実施です", "活跃上报完成,但活动未开启": "アクティビティを報告しましたが、キャンペーンは未実施です", "活跃上报成功": "アクティビティの報告に成功しました", "活跃保活失败": "アクティブのキープアライブに失敗しました", "活跃保活完成": "アクティブのキープアライブが完了しました", "活跃保活完成,但活动未开启": "アクティブのキープアライブは完了しましたが、イベントが有効ではありません", "活跃保活完成,但活跃上报失败且活动未开启": "キープアライブは完了しましたが、アクティビティの報告に失敗し、キャンペーンも未実施です", "添加条目": "スケジュールを追加", "添加模型": "モデルを追加", "清单为空 —— 保存后恢复缺省链": "リストが空です — 保存すると既定のチェーンに戻ります", "清单里已经有「{name}」": "リストに「{name}」はすでにあります", "清空失败:{error}": "クリアに失敗しました:{error}", "清空签到记录": "チェックイン記録をクリア", "点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领": "「プラン明細」で受け取ったプランと有効になる時期を確認できます。キャンペーン期間中は毎日また受け取れます", "点击行展开账号明细 · 单账号签到不受范围限制": "行をクリックしてアカウントの詳細を展開 · 単一アカウントのチェックインは範囲の制限を受けません", "独立链路 · 只提供手动入口 · 不进自动签到": "独立した経路 · 手動入口のみ · 自動チェックインには含まれない", "猫猫旅行": "ねこ旅行", "猫猫旅行失败:{error}": "ねこ旅行に失敗:{error}", "猫猫旅行失败:{message}": "ねこ旅行に失敗:{message}", "猫猫旅行:{message}": "ねこ旅行:{message}", "用免费模型与网页会话维持账号活跃 · 不领取奖励": "無料モデルとWebチャネルの会話でアカウントのアクティブ状態を維持 · 報酬は受け取らない", "确定删除「{name}」?删除后到点不再执行。": "「{name}」を削除しますか?削除後は予定時刻に実行されません。", "确定清空全部签到记录?清空后无法恢复。": "すべてのチェックイン記録をクリアしますか?クリア後は復元できません。", "确定清空当前筛选出的签到记录?清空后无法恢复。": "現在の絞り込みに一致するチェックイン記録をクリアしますか?クリア後は復元できません。", "移除 {name}": "{name} を削除", "立即全部签到": "今すぐ一括チェックイン", "签到": "チェックイン", "签到中…": "チェックイン中…", "签到中心分区": "チェックインセンターのセクション", "签到失败:{error}": "チェックインに失敗しました:{error}", "签到完成:{succeeded}/{total} 成功{active},{failed} 个失败": "チェックイン完了:{succeeded}/{total} 成功{active}、{failed} 件失敗", "签到成功": "チェックインに成功しました", "签到提供商": "チェックインプロバイダー", "签到范围": "チェックイン範囲", "签到触发时刻": "チェックイン実行時刻", "签到记录": "チェックイン記録", "签到记录时间范围": "チェックイン記録の時間範囲", "签到记录表": "チェックイン記録テーブル", "累计签到记录": "累計チェックイン記録", "结算于 {time}": "{time} に確定", "能量 {n}": "エネルギー {n}", "腾讯每日签到接口 · 仅国内版": "Tencent のデイリーチェックイン API · 中国版のみ", "至少勾一家才能保存(不勾等于这条条目不签到)": "保存するには 1 社以上選択してください(未選択=このエントリは何もチェックインしません)", "读取定时签到数据失败:{error}": "定期チェックインデータの読み込みに失敗しました:{error}", "读取签到数据失败:{error}": "チェックインデータの読み込みに失敗しました:{error}", "读取签到记录失败:{error}": "チェックイン記録の読み込みに失敗しました:{error}", "跳过 {n}": "スキップ {n}", "运营活动交付(领取 → 确认 → 回读核实)· 领的是套餐赠送积分": "運営イベントの受け渡し(受け取り → 確認 → 読み戻しで検証)· 受け取るのはプラン特典積分", "还没执行过": "まだ実行されていません", "还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。": "スケジュールがまだありません —— 「スケジュールを追加」で作成するか、「チェックインセンター」で手動チェックインしてください。", "还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。": "チェックイン記録はまだありません —— 手動チェックインまたは定期実行の後に、明細がここに表示されます。", "连登 {n} 天": "連続ログイン {n} 日", "部分": "一部", "重签": "再チェックイン", "重试": "再試行", "限时体验套餐 · 领取需滑块验证码 · 逐份查看与领取": "期間限定お試しプラン · 受け取りにはスライダー認証が必要 · 1 件ずつ確認して受け取り", "限时体验套餐(start-plan),活动期内每天一份 · 领取需滑块验证码": "期間限定体験プラン(start-plan)。開催期間中は毎日 1 件 · 受け取りにはスライダー認証が必要", "需真实操作": "実際の操作が必要", "非活动期": "イベント期間外", "项": "件", "领取": "受け取る", "领取中": "受け取り中", "领取中…": "受け取り中…", "领取免费 CodingPlan(按需:套餐有效时只回读额度)": "無料 CodingPlan を取得(オンデマンド:プラン有効時はクォータを読むだけ)", "领取失败": "受け取りに失敗", "领取失败:{error}": "受け取りに失敗しました:{error}", "(共 {n} 个账号)": "(全 {n} アカウント)", "(能量 / 连登读数本次未取到)": "(エネルギー/連続ログインの数値は今回取得できませんでした)", ",日活保活 {n} 个": "、日活キープアライブ {n} 件" }); diff --git a/desktop-tauri/ui/i18n/ja/requests.js b/desktop-tauri/ui/i18n/ja/requests.js index 019950d2..cdb0b162 100644 --- a/desktop-tauri/ui/i18n/ja/requests.js +++ b/desktop-tauri/ui/i18n/ja/requests.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:ja/requests —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · {n} 进行中": " · 処理中 {n} 件", " · 会话粘住": " · セッション粘着", "

    无响应原文
    ": "
    レスポンス原文がありません
    ", "
    无法解析响应内容
    ": "
    レスポンスの内容を解析できません
    ", "
    无法解析请求体(不是含 messages 数组的 JSON)
    ": "
    リクエストボディを解析できません(messages 配列を含む JSON ではありません)
    ", "
    无请求原文
    ": "
    リクエスト原文がありません
    ", "
    调用工具 {name}
    ": "
    ツール呼び出し {name}
    ", "响应原文": "レスポンス原文", "思考过程": "思考プロセス", "请求原文": "リクエスト原文", "HTTP {status},但响应体阶段出错": "HTTP {status} ですが、レスポンスボディ段階でエラー", "HTTP {status},但响应体阶段出错:见错误列": "HTTP {status} ですが、レスポンスボディ段階でエラー:エラー列を参照", "{duration} / 首帧 {first}": "{duration} / 初回応答 {first}", "{m}分{s}秒": "{m}分{s}秒", "{n}秒": "{n}秒", "{s}秒{ms}ms": "{s}秒{ms}ms", "↻ {n} 次": "↻ {n} 回", "↻ 重试 {n} 次": "↻ リトライ {n} 回", "✅ 压缩完成": "✅ 圧縮が完了しました", "「仅看进行中」开启时,状态固定为进行中": "「処理中のみ表示」が有効な間、ステータスは処理中に固定されます", "上游模型繁忙,请求已排进上游队列;网关正按上游建议的时长等待后重发(不是登录态或额度问题)": "アップストリームのモデルが混雑しているため、リクエストはアップストリームのキューに入りました。ゲートウェイはアップストリームが推奨した時間だけ待ってから再送信します(ログイン状態やクォータの問題ではありません)", "上游状态码: {status}": "アップストリームのステータスコード:{status}", "上游请求已发出,正在等第一个字节到达(模型的思考时间也在这段)": "アップストリームへのリクエストは送信済みで、最初のバイトの到着を待っています(モデルの思考時間もこの段階に含まれます)", "上游返回错误": "アップストリームがエラーを返しました", "下游请求的模型:{model}": "ダウンストリームが要求したモデル:{model}", "仅清空报文原文": "原文のみクリア", "仅看进行中": "処理中のみ表示", "令牌": "トークン", "体积": "サイズ", "保留统计行与报表,只抹掉请求 / 响应正文。清理后详情弹窗的「预览对话」将无原文可看,统计数字分毫不动。": "統計行とレポートはそのままに、リクエスト / レスポンス本文だけを消去します。クリア後は詳細ダイアログの「会話プレビュー」で原文を確認できなくなりますが、統計数値は一切変わりません。", "全部删除": "すべて削除", "全部提供商": "すべてのプロバイダー", "全部模型": "すべてのモデル", "全部状态": "すべてのステータス", "共 {n} 次尝试": "計 {n} 回の試行", "共 {n} 次尝试,最终由 {provider} 承载": "計 {n} 回の試行、最終的に {provider} が処理しました", "其它": "その他", "内容按原样保存,请求头中的凭据字段已替换为 [redacted]": "内容はそのまま保存され、リクエストヘッダー内の認証情報フィールドは [redacted] に置き換えられています", "内部重试": "内部リトライ", "切换路径:": "切替経路:", "列表数据已刷新,请重试(关闭后重新打开这条详情)": "リストのデータが更新されました。もう一度お試しください(閉じてからこの詳細を開き直してください)", "删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。": "ログレコード自体を削除します。フィルター適用中はヒットした項目だけを削除し、日別集計レポートには影響しません。フィルターなしの場合はすべてをクリアしてレポートをリセットします。この方法では元に戻せません。", "压缩中…": "圧縮中…", "压缩失败:{message}": "圧縮に失敗しました:{message}", "压缩已在后台开始,完成后会自动提示": "圧縮はバックグラウンドで開始されました。完了すると自動で通知されます", "压缩已在进行中": "圧縮はすでに実行中です", "压缩已在进行中,接上它的进度等待完成": "圧縮はすでに実行中です。既存の進捗を引き継いで完了を待ちます", "压缩数据库": "データベース圧縮", "压缩正在后台执行,完成后自动恢复": "圧縮はバックグラウンドで実行中です。完了すると自動的に復帰します", "原始报文": "生ペイロード", "另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)": "ほかに {missing} 回の試行は明細が記録されていません(古い方から {kept} 件のみ保持)", "另有 {n} 个词命中": "ほかに {n} 語がヒット", "只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)": "転送中のリクエストのみ表示します(ステータス列は段階ごとに表示:接続中 / 応答待ち / 応答中 / リトライ中、所要時間列は経過時間を表示)", "只看失败": "失敗のみ", "只看成功": "成功のみ", "只看进行中": "処理中のみ", "命中了敏感词": "センシティブワードにヒット", "命中的敏感词": "ヒットしたセンシティブワード", "响应中": "応答中", "响应体": "レスポンスボディ", "响应头": "レスポンスヘッダー", "回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)": "削除済みデータが占有していたディスク領域を回収します(checkpoint + VACUUM、バックグラウンドで実行)", "在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文": "設定 → 一般で「デバッグモード」を有効にすると、以降の新しいリクエストでのみペイロードが保存されます", "失败请求不记录用量": "失敗したリクエストは使用量を記録しません", "失败({status}):{error}": "失敗({status}):{error}", "失败:{error}": "失敗:{error}", "实际发给上游 {bytes} 字节": "実際にアップストリームへ送信した {bytes} バイト", "客户端": "クライアント", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "{count} 件のログを削除 · うち {raw} 件はペイロード原文あり · DB サイズ {db}", "尝试 {n} ·": "試行 {n} ·", "尝试明细": "試行明細", "工具结果": "ツール結果", "工具结果 · {id}": "ツール結果 · {id}", "工具结果图片": "ツール結果の画像", "已删除 {count} 条": "{count} 件を削除しました", "已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」": "中断を受け付けました:アップストリーム接続は切断されました。リストはまもなく「リクエストは手動で中断されました」に更新されます", "已抹掉 {count} 条报文原文": "{count} 件のペイロード原文を消去しました", "已用时(请求仍在转发中)": "経過時間(リクエストは転送中)", "成功": "成功", "成功({status})": "成功({status})", "执行清理": "クリアを実行", "报文超过单条上限,请求体 / 响应体已按上限截断": "ペイロードが 1 件あたりの上限を超えたため、リクエストボディ / レスポンスボディは上限で切り詰められています", "抹掉原文": "原文を消去", "按提供商筛选": "プロバイダーで絞り込み", "按提供商筛选(只列日志里出现过的)": "プロバイダーで絞り込み(ログに出現したもののみ表示)", "按时间倒序,全量保留": "時系列の新しい順、全量保持", "按模型筛选": "モデルで絞り込み", "按模型筛选(只列日志里出现过的;精确匹配模型名)": "モデルで絞り込み(ログに出現したもののみ表示;モデル名は完全一致)", "按请求结果筛选": "リクエスト結果で絞り込み", "排队中": "キュー中", "提供商 / 账号": "プロバイダー / アカウント", "提供商: {provider}": "プロバイダー:{provider}", "提示": "備考", "敏": "敏", "敏感词": "センシティブワード", "断开上游连接,明细记为「请求已被手动终止」": "アップストリーム接続を切断します。明細には「リクエストは手動で中断されました」と記録されます", "无报文原文(未开启「调试模式」,或已被清理)": "原文ペイロードがありません(デバッグモードが無効だったか、クリア済みです)", "无结果记录": "結果の記録なし", "时间": "時刻", "暂无请求日志,网关还没有转发过请求": "リクエストログはまだありません。ゲートウェイはまだリクエストを転送したことがありません", "最终承载:{picked}": "最終的に処理:{picked}", "本轮尝试失败,正在退避等待或切换到下一个账号": "今回の試行は失敗し、バックオフ待機中または次のアカウントへの切り替え中です", "来源客户端:{client}": "接続元クライアント:{client}", "查看该请求的上游原始报文": "このリクエストのアップストリーム生ペイロードを表示", "模型": "モデル", "模型测试": "モデルテスト", "模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计": "モデル管理ページの操作列にある「テスト」から発行されたリクエスト:実際のリクエストと同じ転送経路を通りますが、レポート統計には含まれません", "正在加载请求日志…": "リクエストログを読み込んでいます…", "正在统计…": "統計中…", "正文超过单条上限,可能已被截断": "本文が 1 件あたりの上限を超えているため、切り詰められている可能性があります", "没有符合筛选条件的请求": "絞り込み条件に一致するリクエストがありません", "清理": "クリア", "清理中…": "クリア中…", "清理失败:{message}": "クリアに失敗しました:{message}", "清理方式": "クリア方法", "清理请求日志": "リクエストログをクリア", "生成速度:输出 token ÷ 流式段用时(总用时 − 首响);流式段不足总用时一半时按总用时计": "生成速度:出力トークン ÷ ストリーミング時間(所要時間 − 初回応答)。ストリーミング時間が所要時間の半分未満の場合は所要時間で計算", "用时": "所要時間", "用量": "使用量", "确定对全部请求日志执行「{label}」?将处理 {count} 条,此操作无法恢复。": "すべてのリクエストログに対して「{label}」を実行しますか?{count} 件を処理します。この操作は元に戻せません。", "确定按当前筛选执行「{label}」?将处理 {count} 条,此操作无法恢复。": "現在の絞り込み条件で「{label}」を実行しますか?{count} 件を処理します。この操作は元に戻せません。", "确定终止这条正在转发的请求?
    上游连接会立即断开,明细将记为「请求已被手动终止」。": "この転送中のリクエストを中断しますか?
    アップストリーム接続はすぐに切断され、明細には「リクエストは手動で中断されました」と記録されます。", "等待响应": "応答待ち", "策略 {strategy} · 会话粘性 {sticky}{hit}": "戦略 {strategy} · セッション粘着 {sticky}{hit}", "终止": "中断", "终止失败:{error}": "中断に失敗しました:{error}", "终止请求": "リクエストを中断", "结果": "結果", "缓存读取: - / 命中率: -": "キャッシュ読み取り:- / ヒット率:-", "缓存读取: {cache} / 命中率: {rate}": "キャッシュ読み取り:{cache} / ヒット率:{rate}", "耗时": "所要時間", "被跳过:": "スキップ:", "识别到 User-Agent,但不属于已知的客户端类型:{ua}": "User-Agent はありますが、既知のクライアント種別ではありません:{ua}", "详情": "詳細", "详情内容": "詳細の内容", "请求体": "リクエストボディ", "请求图片": "リクエストの画像", "请求在任何账号接手之前就失败了": "リクエストはどのアカウントが引き継ぐ前に失敗しました", "请求头": "リクエストヘッダー", "请求已受理,正在选路、取凭证、建立上游连接": "リクエストを受け付けました。経路の選択、認証情報の取得、アップストリーム接続の確立を進めています", "请求日志列表": "リクエストログ一覧", "请求日志时间范围": "リクエストログの時間範囲", "请求日志表": "リクエストログテーブル", "请求正在转发中,用时列显示的是已用时": "リクエストは転送中です。所要時間列には経過時間が表示されます", "请求详情": "リクエスト詳細", "读取请求日志失败,详见控制台": "リクエストログの読み込みに失敗しました。詳細はコンソールを確認してください", "路由回放": "ルーティングリプレイ", "转发到上游的模型:{model}": "アップストリームへ転送するモデル:{model}", "轮次": "回目", "输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}": "入力 {input} · 出力 {output} · 合計 {total} · キャッシュ読み取り {cache}", "这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计": "この明細はモデルテスト(手動実行)です:実際の転送経路を通りますが、レポート統計には含まれません", "这条明细没有记录提供商(旧数据或请求未走到转发)": "この明細にはプロバイダーが記録されていません(旧データか、リクエストが転送まで到達しなかったため)", "这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)": "この記録には接続元クライアントがありません(旧データ、モデルテスト、または User-Agent なしのリクエスト)", "这条记录命中了敏感词,但没有留下命中明细": "この記録はセンシティブワードにヒットしましたが、ヒット明細が残っていません", "这条记录的尝试明细未采集(该字段上线前的旧数据,或请求在转发前就失败)": "この記録の試行明細は収集されていません(このフィールドの実装前の旧データか、リクエストが転送前に失敗したため)", "这条请求没有保存这一段报文": "このリクエストではこの区間のペイロードが保存されていません", "进入「{phase}」阶段已持续 {time}": "「{phase}」段階に入ってから {time} 経過", "进行中": "処理中", "进行中…": "処理中…", "连接中": "接続中", "重试 {n}": "リトライ {n}", "重试中": "リトライ中", "重试次数": "リトライ回数", "预览对话": "会話プレビュー", "预览读取失败": "プレビューの読み込みに失敗しました", "预览读取失败:{error}": "プレビューの読み込みに失敗しました:{error}", "首响 {time}": "初回応答 {time}", "首响:上游首帧到达的耗时": "初回応答:アップストリームの初回フレーム到達までの所要時間", "首响:上游首帧到达的耗时(请求仍在转发中)": "初回応答:アップストリームの初回フレーム到達までの所要時間(リクエストは転送中)", "首帧已到,上游内容正在下发": "初回フレームが到着し、アップストリームの内容を受信しています", "(请求仍在转发中)": "(リクエストは転送中)", "(账号:{account})": "(アカウント:{account})", ",{n}毫秒后重试": "、{n}ミリ秒後にリトライ", ",{n}秒后重试": "、{n}秒後にリトライ" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · {n} 进行中": " · 処理中 {n} 件", " · 会话粘住": " · セッション粘着", "
    无响应原文
    ": "
    レスポンス原文がありません
    ", "
    无法解析响应内容
    ": "
    レスポンスの内容を解析できません
    ", "
    无法解析请求体(不是含 messages 数组的 JSON)
    ": "
    リクエストボディを解析できません(messages 配列を含む JSON ではありません)
    ", "
    无请求原文
    ": "
    リクエスト原文がありません
    ", "
    调用工具 {name}
    ": "
    ツール呼び出し {name}
    ", "响应原文": "レスポンス原文", "思考过程": "思考プロセス", "请求原文": "リクエスト原文", "HTTP {status},但响应体阶段出错": "HTTP {status} ですが、レスポンスボディ段階でエラー", "HTTP {status},但响应体阶段出错:见错误列": "HTTP {status} ですが、レスポンスボディ段階でエラー:エラー列を参照", "{duration} / 首帧 {first}": "{duration} / 初回応答 {first}", "{m}分{s}秒": "{m}分{s}秒", "{n}秒": "{n}秒", "{s}秒{ms}ms": "{s}秒{ms}ms", "↻ {n} 次": "↻ {n} 回", "↻ 重试 {n} 次": "↻ リトライ {n} 回", "✅ 压缩完成": "✅ 圧縮が完了しました", "「仅看进行中」开启时,状态固定为进行中": "「処理中のみ表示」が有効な間、ステータスは処理中に固定されます", "上游模型繁忙,请求已排进上游队列;网关正按上游建议的时长等待后重发(不是登录态或额度问题)": "アップストリームのモデルが混雑しているため、リクエストはアップストリームのキューに入りました。ゲートウェイはアップストリームが推奨した時間だけ待ってから再送信します(ログイン状態やクォータの問題ではありません)", "上游状态码: {status}": "アップストリームのステータスコード:{status}", "上游请求已发出,正在等第一个字节到达(模型的思考时间也在这段)": "アップストリームへのリクエストは送信済みで、最初のバイトの到着を待っています(モデルの思考時間もこの段階に含まれます)", "上游返回错误": "アップストリームがエラーを返しました", "下游请求的模型:{model}": "ダウンストリームが要求したモデル:{model}", "仅清空报文原文": "原文のみクリア", "仅看进行中": "処理中のみ表示", "令牌": "トークン", "体积": "サイズ", "保留统计行与报表,只抹掉请求 / 响应正文。清理后详情弹窗的「预览对话」将无原文可看,统计数字分毫不动。": "統計行とレポートはそのままに、リクエスト / レスポンス本文だけを消去します。クリア後は詳細ダイアログの「会話プレビュー」で原文を確認できなくなりますが、統計数値は一切変わりません。", "全部删除": "すべて削除", "全部提供商": "すべてのプロバイダー", "全部模型": "すべてのモデル", "全部状态": "すべてのステータス", "共 {n} 次尝试": "計 {n} 回の試行", "共 {n} 次尝试,最终由 {provider} 承载": "計 {n} 回の試行、最終的に {provider} が処理しました", "其它": "その他", "内容按原样保存,请求头中的凭据字段已替换为 [redacted]": "内容はそのまま保存され、リクエストヘッダー内の認証情報フィールドは [redacted] に置き換えられています", "内部重试": "内部リトライ", "切换路径:": "切替経路:", "列表数据已刷新,请重试(关闭后重新打开这条详情)": "リストのデータが更新されました。もう一度お試しください(閉じてからこの詳細を開き直してください)", "删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。": "ログレコード自体を削除します。フィルター適用中はヒットした項目だけを削除し、日別集計レポートには影響しません。フィルターなしの場合はすべてをクリアしてレポートをリセットします。この方法では元に戻せません。", "压缩中…": "圧縮中…", "压缩失败:{message}": "圧縮に失敗しました:{message}", "压缩已在后台开始,完成后会自动提示": "圧縮はバックグラウンドで開始されました。完了すると自動で通知されます", "压缩已在进行中": "圧縮はすでに実行中です", "压缩已在进行中,接上它的进度等待完成": "圧縮はすでに実行中です。既存の進捗を引き継いで完了を待ちます", "压缩数据库": "データベース圧縮", "压缩正在后台执行,完成后自动恢复": "圧縮はバックグラウンドで実行中です。完了すると自動的に復帰します", "原始报文": "生ペイロード", "另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)": "ほかに {missing} 回の試行は明細が記録されていません(古い方から {kept} 件のみ保持)", "另有 {n} 个词命中": "ほかに {n} 語がヒット", "只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)": "転送中のリクエストのみ表示します(ステータス列は段階ごとに表示:接続中 / 応答待ち / 応答中 / リトライ中、所要時間列は経過時間を表示)", "只看失败": "失敗のみ", "只看成功": "成功のみ", "只看进行中": "処理中のみ", "命中了敏感词": "センシティブワードにヒット", "命中的敏感词": "ヒットしたセンシティブワード", "响应中": "応答中", "响应体": "レスポンスボディ", "响应头": "レスポンスヘッダー", "回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)": "削除済みデータが占有していたディスク領域を回収します(checkpoint + VACUUM、バックグラウンドで実行)", "在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文": "設定 → 一般で「デバッグモード」を有効にすると、以降の新しいリクエストでのみペイロードが保存されます", "失败请求不记录用量": "失敗したリクエストは使用量を記録しません", "失败({status}):{error}": "失敗({status}):{error}", "失败:{error}": "失敗:{error}", "实际下发给客户端 {bytes} 字节": "クライアントに実際に配信した {bytes} バイト", "实际发给上游 {bytes} 字节": "実際にアップストリームへ送信した {bytes} バイト", "客户端": "クライアント", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "{count} 件のログを削除 · うち {raw} 件はペイロード原文あり · DB サイズ {db}", "尝试 {n} ·": "試行 {n} ·", "尝试明细": "試行明細", "工具结果": "ツール結果", "工具结果 · {id}": "ツール結果 · {id}", "工具结果图片": "ツール結果の画像", "已下发": "配信済み", "已删除 {count} 条": "{count} 件を削除しました", "已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」": "中断を受け付けました:アップストリーム接続は切断されました。リストはまもなく「リクエストは手動で中断されました」に更新されます", "已抹掉 {count} 条报文原文": "{count} 件のペイロード原文を消去しました", "已用时(请求仍在转发中)": "経過時間(リクエストは転送中)", "成功": "成功", "成功({status})": "成功({status})", "执行清理": "クリアを実行", "报文超过单条上限,请求体 / 响应体已按上限截断": "ペイロードが 1 件あたりの上限を超えたため、リクエストボディ / レスポンスボディは上限で切り詰められています", "抹掉原文": "原文を消去", "按提供商筛选": "プロバイダーで絞り込み", "按提供商筛选(只列日志里出现过的)": "プロバイダーで絞り込み(ログに出現したもののみ表示)", "按时间倒序,全量保留": "時系列の新しい順、全量保持", "按模型筛选": "モデルで絞り込み", "按模型筛选(只列日志里出现过的;精确匹配模型名)": "モデルで絞り込み(ログに出現したもののみ表示;モデル名は完全一致)", "按请求结果筛选": "リクエスト結果で絞り込み", "排队中": "キュー中", "提供商 / 账号": "プロバイダー / アカウント", "提供商: {provider}": "プロバイダー:{provider}", "提示": "備考", "敏": "敏", "敏感词": "センシティブワード", "断开上游连接,明细记为「请求已被手动终止」": "アップストリーム接続を切断します。明細には「リクエストは手動で中断されました」と記録されます", "无报文原文(未开启「调试模式」,或已被清理)": "原文ペイロードがありません(デバッグモードが無効だったか、クリア済みです)", "无结果记录": "結果の記録なし", "时间": "時刻", "暂无请求日志,网关还没有转发过请求": "リクエストログはまだありません。ゲートウェイはまだリクエストを転送したことがありません", "最终承载:{picked}": "最終的に処理:{picked}", "本轮尝试失败,正在退避等待或切换到下一个账号": "今回の試行は失敗し、バックオフ待機中または次のアカウントへの切り替え中です", "来源客户端:{client}": "接続元クライアント:{client}", "查看该请求的上游原始报文": "このリクエストのアップストリーム生ペイロードを表示", "模型": "モデル", "模型测试": "モデルテスト", "模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计": "モデル管理ページの操作列にある「テスト」から発行されたリクエスト:実際のリクエストと同じ転送経路を通りますが、レポート統計には含まれません", "正在加载请求日志…": "リクエストログを読み込んでいます…", "正在统计…": "統計中…", "正文超过单条上限,可能已被截断": "本文が 1 件あたりの上限を超えているため、切り詰められている可能性があります", "没有符合筛选条件的请求": "絞り込み条件に一致するリクエストがありません", "清理": "クリア", "清理中…": "クリア中…", "清理失败:{message}": "クリアに失敗しました:{message}", "清理方式": "クリア方法", "清理请求日志": "リクエストログをクリア", "生成速度:输出 token ÷ 流式段用时(总用时 − 首响);流式段不足总用时一半时按总用时计": "生成速度:出力トークン ÷ ストリーミング時間(所要時間 − 初回応答)。ストリーミング時間が所要時間の半分未満の場合は所要時間で計算", "用时": "所要時間", "用量": "使用量", "确定对全部请求日志执行「{label}」?将处理 {count} 条,此操作无法恢复。": "すべてのリクエストログに対して「{label}」を実行しますか?{count} 件を処理します。この操作は元に戻せません。", "确定按当前筛选执行「{label}」?将处理 {count} 条,此操作无法恢复。": "現在の絞り込み条件で「{label}」を実行しますか?{count} 件を処理します。この操作は元に戻せません。", "确定终止这条正在转发的请求?
    上游连接会立即断开,明细将记为「请求已被手动终止」。": "この転送中のリクエストを中断しますか?
    アップストリーム接続はすぐに切断され、明細には「リクエストは手動で中断されました」と記録されます。", "等待响应": "応答待ち", "策略 {strategy} · 会话粘性 {sticky}{hit}": "戦略 {strategy} · セッション粘着 {sticky}{hit}", "终止": "中断", "终止失败:{error}": "中断に失敗しました:{error}", "终止请求": "リクエストを中断", "结果": "結果", "缓存读取: - / 命中率: -": "キャッシュ読み取り:- / ヒット率:-", "缓存读取: {cache} / 命中率: {rate}": "キャッシュ読み取り:{cache} / ヒット率:{rate}", "耗时": "所要時間", "被跳过:": "スキップ:", "识别到 User-Agent,但不属于已知的客户端类型:{ua}": "User-Agent はありますが、既知のクライアント種別ではありません:{ua}", "详情": "詳細", "详情内容": "詳細の内容", "请求体": "リクエストボディ", "请求图片": "リクエストの画像", "请求在任何账号接手之前就失败了": "リクエストはどのアカウントが引き継ぐ前に失敗しました", "请求头": "リクエストヘッダー", "请求已受理,正在选路、取凭证、建立上游连接": "リクエストを受け付けました。経路の選択、認証情報の取得、アップストリーム接続の確立を進めています", "请求日志列表": "リクエストログ一覧", "请求日志时间范围": "リクエストログの時間範囲", "请求日志表": "リクエストログテーブル", "请求正在转发中,用时列显示的是已用时": "リクエストは転送中です。所要時間列には経過時間が表示されます", "请求详情": "リクエスト詳細", "读取请求日志失败,详见控制台": "リクエストログの読み込みに失敗しました。詳細はコンソールを確認してください", "路由回放": "ルーティングリプレイ", "转发到上游的模型:{model}": "アップストリームへ転送するモデル:{model}", "轮次": "回目", "输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}": "入力 {input} · 出力 {output} · 合計 {total} · キャッシュ読み取り {cache}", "这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计": "この明細はモデルテスト(手動実行)です:実際の転送経路を通りますが、レポート統計には含まれません", "这条明细没有记录提供商(旧数据或请求未走到转发)": "この明細にはプロバイダーが記録されていません(旧データか、リクエストが転送まで到達しなかったため)", "这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)": "この記録には接続元クライアントがありません(旧データ、モデルテスト、または User-Agent なしのリクエスト)", "这条记录命中了敏感词,但没有留下命中明细": "この記録はセンシティブワードにヒットしましたが、ヒット明細が残っていません", "这条记录的尝试明细未采集(该字段上线前的旧数据,或请求在转发前就失败)": "この記録の試行明細は収集されていません(このフィールドの実装前の旧データか、リクエストが転送前に失敗したため)", "这条请求没有保存这一段报文": "このリクエストではこの区間のペイロードが保存されていません", "进入「{phase}」阶段已持续 {time}": "「{phase}」段階に入ってから {time} 経過", "进行中": "処理中", "进行中…": "処理中…", "连接中": "接続中", "重试 {n}": "リトライ {n}", "重试中": "リトライ中", "重试次数": "リトライ回数", "预览对话": "会話プレビュー", "预览读取失败": "プレビューの読み込みに失敗しました", "预览读取失败:{error}": "プレビューの読み込みに失敗しました:{error}", "首响 {time}": "初回応答 {time}", "首响:上游首帧到达的耗时": "初回応答:アップストリームの初回フレーム到達までの所要時間", "首响:上游首帧到达的耗时(请求仍在转发中)": "初回応答:アップストリームの初回フレーム到達までの所要時間(リクエストは転送中)", "首帧已到,上游内容正在下发": "初回フレームが到着し、アップストリームの内容を受信しています", "(请求仍在转发中)": "(リクエストは転送中)", "(账号:{account})": "(アカウント:{account})", ",{n}毫秒后重试": "、{n}ミリ秒後にリトライ", ",{n}秒后重试": "、{n}秒後にリトライ" }); diff --git a/desktop-tauri/ui/i18n/ko/add-account.js b/desktop-tauri/ui/i18n/ko/add-account.js index ef34db34..81040221 100644 --- a/desktop-tauri/ui/i18n/ko/add-account.js +++ b/desktop-tauri/ui/i18n/ko/add-account.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:ko/add-account —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · 模型 {head}{rest}": " · 모델 {head}{rest}", " 等 {n} 个": " 등 {n}개", "1// 开头的 Google refresh token(也可直接粘贴 refresh_token=… 整行)": "1//로 시작하는 Google refresh token (refresh_token=… 줄 전체를 붙여넣어도 됨)", "11 位大陆手机号": "11자리 중국 본토 휴대폰 번호", "1~64 个字符,账号列表里按它分组显示": "1~64자, 계정 목록에서 이 값을 기준으로 그룹화되어 표시됩니다", "6 位数字": "6자리 숫자", "
  • 没有可自动领的活动
  • ": "
  • 자동으로 수령할 수 있는 이벤트가 없습니다
  • ", "

    领到的是套餐赠送积分,不会增加福利模型的 token 池;": "

    수령하는 것은 플랜 증정 포인트이며, 혜택 모델의 token 풀은 늘지 않습니다;", "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "Anthropic 호환 엔드포인트는 https://api.deepseek.com/anthropic입니다 (프로토콜을 anthropic으로 바꿀 때 입력합니다)", "Anthropic 协议填根地址,不要带 /v1": "Anthropic 프로토콜은 루트 주소를 입력하고 /v1은 붙이지 마세요", "Antigravity": "Antigravity", "AutoClaw 国内版": "AutoClaw 중국판", "AutoClaw 国际版": "AutoClaw 국제판", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS는 필수입니다; **전체 Cookie**를 붙여넣으세요 (Cookie 편집기에서 원클릭 내보내기 가능) — 이 중 **PTOKEN은 반드시 남겨 두세요**. 게이트웨이가 이것을 사용해 Baidu에서 KukuAI 비즈니스 세션 토큰을 교환 발급받으며, 없으면 모델 갱신 시 「미로그인」 오류가 발생합니다. 전체 Cookie 헤더, Cookie 편집기에서 내보낸 JSON 배열, 또는 {BDUSS, STOKEN} 객체를 지원합니다. KukuAI에는 갱신 메커니즘이 없으므로 로그인 상태가 만료되면 다시 붙여넣으면 됩니다.", "BDUSS=…; STOKEN=…(或 Cookie 编辑器导出的 JSON)": "BDUSS=…; STOKEN=… (또는 Cookie 편집기에서 내보낸 JSON)", "CatPaw 的 X-Passport-Token(登录态 Cookie)": "CatPaw의 X-Passport-Token (로그인 상태 Cookie)", "Cline Pass 的 API Key 在 app.cline.bot 获取(订阅制);端点为 OpenAI 兼容,模型清单以 Cline 控制台为准,模型名在模型管理里按裸名添加(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客户端形态校验,所需的客户端请求头已随卡预置": "Cline Pass의 API Key는 app.cline.bot에서 발급받습니다(구독제). 엔드포인트는 OpenAI 호환이며, 모델 목록은 Cline 콘솔을 기준으로 하고, 모델 이름은 모델 관리에서 접두사 없는 이름 그대로 추가합니다(예: glm-5.2 / kimi-k2.7-code / deepseek-v4-pro). 업스트림이 클라이언트 형태를 검증하므로 필요한 클라이언트 요청 헤더는 이 카드에 미리 설정되어 있습니다", "Cloud-IDE-JWT(三段点分)": "Cloud-IDE-JWT (3단 점 구분)", "Command Code": "Command Code", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT(점 3개 구분. Bearer 전체 줄 또는 data.bin 내용을 붙여넣어도 됨)", "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT(점 3개로 구분된 형식. chatglm_token=… 줄을 그대로 붙여도 됩니다)", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode 국제판", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "NVIDIA NIM 관리형 엔드포인트, Key는 integrate.api.nvidia.com에서 신청합니다", "OfficeAce 果办": "OfficeAce", "accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)": "accessToken은 Accio의 로그인 자격 증명입니다 (길고 불투명한 token 문자열이며 JWT가 아닙니다); refreshToken은 선택이며, 입력하면 만료 시 자동 갱신됩니다. {label} 계정의 자격 증명을 입력하세요 — {siteNote} (가장 쉽게 얻는 방법: 위의 「웹 로그인」을 바로 사용하세요. token을 직접 찾을 필요가 없습니다.)", "accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
    手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
    没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。": "accessToken은 Trae의 Cloud-IDE-JWT이며(3단 점 구분), refreshToken은 만료 시 자동 갱신에 사용됩니다 (이 제공자는 refreshToken이 교환할 때마다 바뀝니다: 한 번 교환하면 이전 것은 무효가 되므로 두 프로그램에서 같은 계정을 동시에 갱신하지 마세요).
    수동으로 붙여넣을 때는 machineId / deviceId도 함께 입력하세요: 업스트림은 이를 로그인 시 업로드한 기기 공개 키와 묶어 기기를 판별하므로, 갑자기 새로운 값을 넣으면 2xxxx 계열 기기 바인딩 거부가 발생합니다.
    이 세 가지가 없을 때의 올바른 방법은 위의 「웹 로그인」이며, 컨테이너 / 원격 배포 환경에서는 안내에 따라 주소 표시줄의 콜백 주소를 붙여넣으면 됩니다.", "monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)": "monkeycode_ai_session의 값 (전체 Cookie를 붙여넣어도 됨)", "refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。": "refreshToken은 선택이며, 입력하면 만료 시 자동 갱신됩니다; 둘 다 小浣熊 클라이언트 로그인 상태 파일에서 얻을 수 있습니다.", "refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。": "refreshToken은 선택이며, 입력하면 자동 갱신됩니다; 둘 다 클라이언트 로그인 상태 파일에서 얻을 수 있습니다. 같은 Cline 계정은 두 풀 모두에서 사용할 수 있으며, 여기서는 {label}로 분류됩니다.", "token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。": "token은 enc: 접두사를 지원합니다 (Windows에서는 자동 복호화). refreshToken을 입력하지 않으면 자동 갱신할 수 없습니다.", "token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。": "token은 로그인 상태 Cookie입니다; CatPaw에는 갱신 메커니즘이 없어 만료 후 클라이언트에서 다시 로그인해야 합니다.", "user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)": "user_로 시작하는 API Key (Authorization: Bearer … 줄 전체를 붙여넣어도 됨)", "{label} 网页登录的打开方式": "{label} 웹 로그인을 여는 방식", "{label} 账号地区": "{label} 계정 지역", "{label} 账号的添加方式": "{label} 계정 추가 방식", "{name} 不是合法 JSON:{reason}": "{name}은(는) 올바른 JSON이 아닙니다: {reason}", "{name}要粘一个 JSON 对象(形如 {sample})": "{name}에는 JSON 객체를 붙여넣어야 합니다 (예: {sample})", "{n} 个账号": "계정 {n}개", "{n} 个账号,点击添加": "계정 {n}개, 클릭하여 추가", "{seconds}s 后重发": "{seconds}초 후 재발송", "✅ {label}账号已添加": "✅ {label} 계정을 추가했습니다", "✅ {label}账号已添加{name}": "✅ {label} 계정을 추가했습니다{name}", "✅ 已从 cc-switch 导入 {n} 个供应商": "✅ cc-switch에서 제공자 {n}개를 가져왔습니다", "✅ 已创建自定义提供商「{name}」并添加账号": "✅ 커스텀 제공자 「{name}」을(를) 만들고 계정을 추가했습니다", "✅ 已删除自定义提供商「{name}」{accounts}{keys}": "✅ 커스텀 제공자 「{name}」을(를) 삭제했습니다{accounts}{keys}", "✅ 已更新已有账号{name}(同一把 API Key 只保留一条,重复添加会合并)": "✅ 기존 계정{name}을(를) 업데이트했습니다(동일한 API Key는 하나만 유지되며 다시 추가하면 병합됩니다)", "✅ 登录成功,{edition}账号已加入列表": "✅ 로그인 성공, {edition} 계정을 목록에 추가했습니다", "✅ 领取成功:{planId}{period}": "✅ 수령 성공: {planId}{period}", " 同一家可以放多把 key,按优先级轮换。": " 같은 제공자에 key를 여러 개 둘 수 있으며, 우선순위에 따라 로테이션됩니다.", "「{name}」的账号添加功能还在开发中,敬请期待。": "「{name}」의 계정 추가 기능은 아직 개발 중입니다. 기대해 주세요.", "一个 Key 用遍多家模型,模型名用「厂商/模型」全称(如 deepseek/deepseek-chat)": "하나의 Key로 여러 제공자의 모델을 사용할 수 있으며, 모델 이름은 「벤더/모델」 전체 이름을 씁니다 (예: deepseek/deepseek-chat)", "上游不要鉴权时留空,并勾选下面一项(留空又不勾会被当成未配置凭证)": "업스트림이 인증을 요구하지 않으면 비워 두고 아래 항목을 체크하세요 (비워 두고 체크하지 않으면 자격 증명 미설정으로 간주됩니다)", "上游信息": "업스트림 정보", "不可领({status})": "수령 불가 ({status})", "不支持导入": "가져오기 미지원", "不是有效的凭证文件:内容要是一个 JSON 对象": "유효한 자격 증명 파일이 아닙니다. 내용은 JSON 객체여야 합니다", "不是有效的凭证文件:需要包含 accessToken 与 uid": "유효한 자격 증명 파일이 아닙니다. accessToken과 uid가 필요합니다", "不是自定义提供商": "커스텀 제공자가 아닙니다", "不需要 Key:转发与拉取模型都不发送鉴权头": "Key 불필요: 포워딩과 모델 조회 모두 인증 헤더를 보내지 않습니다", "不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

    注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。": "로그인이 필요 없습니다. 아래 버튼을 누르면 게이트웨이가 智谱清言에서 게스트 자격 증명을 직접 발급받아 계정 목록에 추가합니다(먼저 메모 이름을 입력해도 됩니다). 게스트 자격 증명은 바로 대화에 쓸 수 있고, 무료 할당량은 업스트림이 기기 단위로 발급합니다.

    주의: 게스트 계정에는 포인트 체계가 없습니다. 회원 포인트, 매일 로그인 포인트, 활동 수령 관련 인터페이스는 모두 로그인이 필요하므로 이러한 기능은 게스트에서 건너뜁니다(계정 페이지의 포인트와 签到 센터에 수치가 표시되지 않습니다). 포인트가 필요하면 \"자격 증명 입력\"으로 로그인된 계정 토큰을 붙여넣거나 \"데스크톱 로그인 상태 가져오기\"를 사용하세요.", "两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。": "두 판의 계정을 동시에 저장할 수 있으며 각각 별도의 모델 목록을 가집니다 (중국판과 국제판은 서로 다른 두 제공자로, 각각 활성화하고 매핑할 수 있습니다).", "中国版": "중국판", "今天没有需要领的活动(都已到账)": "오늘 수령할 이벤트가 없습니다 (모두 적립됨)", "今天(北京时间 {day})已试 {attempts} 次,还能试 {left} 次{next}
    ": "오늘(베이징 시간 {day}) {attempts}회 시도했고, {left}회 더 시도할 수 있습니다{next}
    ", "从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。": "다른 컴퓨터(또는 OfficeAce 데스크톱 앱의 models.json)에서 다음 세 항목을 가져와 붙여넣으세요: 모델 게이트웨이 기본 주소(baseUrl, https://modelgw-…/v2 형태)와 modelAppKey / modelAppSecret. 이 세 항목만 있으면 전달할 수 있습니다(게이트웨이 자격 증명은 만료되지 않음). 제어 영역 임시 자격 증명(accessKeyId 등)은 선택이며, 비워 두면 잔액 조회와 자동 갱신을 쓸 수 없지만 전달에는 영향이 없습니다.", "从本机导入桌面端登录态": "이 컴퓨터에서 데스크톱 로그인 상태 가져오기", "任务镜像 id": "작업 이미지 id", "余额查询": "잔액 조회", "使用 Google 账号登录": "Google 계정으로 로그인", "使用 Zai 账号登录": "Zai 계정으로 로그인", "使用个人访问令牌(PAT)": "개인 액세스 토큰(PAT) 사용", "免费模型不用填 Key(默认勾上「该上游无需鉴权」,网关自动用匿名凭证);付费模型则填 Zen 的 API Key。免费档上游只认官方客户端形态,已默认开启「伪装 OpenCode 官方客户端」": "무료 모델은 Key를 입력하지 않아도 됩니다 (기본적으로 「이 업스트림은 인증이 필요하지 않음」이 체크되어 있고, 게이트웨이가 자동으로 익명 자격 증명을 사용합니다); 유료 모델은 Zen의 API Key를 입력하세요. 무료 등급 업스트림은 공식 클라이언트 형태만 허용하므로 「OpenCode 공식 클라이언트로 위장」이 기본으로 켜져 있습니다", "内嵌窗口": "내장 창", "内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)": "내장 창으로 엽니다; 창을 닫으면 대기가 취소됩니다 (Google 계정이 거부되면 시스템 브라우저를 사용하세요)", "内嵌窗口打开;完成后自动加入列表,关窗即取消等待": "내장 창으로 엽니다; 완료하면 자동으로 목록에 추가되고, 창을 닫으면 대기가 취소됩니다", "内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待": "내장 창으로 열립니다. 스캔이 끝나면 자동으로 목록에 추가되며, 창을 닫으면 대기가 취소됩니다", "内嵌窗口用全新环境,多账号互不影响;关窗即取消等待": "내장 창은 새 환경을 사용하므로 여러 계정이 서로 영향을 주지 않습니다; 창을 닫으면 대기가 취소됩니다", "内嵌窗口(推荐)": "내장 창(권장)", "准备验证…": "인증 준비 중…", "凭据 JSON": "자격 증명 JSON", "凭证文件在哪?": "자격 증명 파일 위치는?", "创建并添加账号": "생성하고 계정 추가", "创建这个提供商,并同时建立它的第一个账号。": "이 제공자를 만들고 동시에 첫 계정을 생성합니다.", "删除功能不可用(脚本未就绪)": "삭제 기능을 사용할 수 없습니다 (스크립트가 준비되지 않음)", "删除失败:{reason}": "삭제 실패: {reason}", "删除此提供商": "이 제공자 삭제", "及 {n} 个账号": " 및 계정 {n}개", "发送中…": "전송 중…", "发送失败:{message}": "전송 실패: {message}", "发送未确认:登录窗口已弹出,请在弹出的窗口中手动完成登录": "전송 미확인: 로그인 창이 열렸습니다. 열린 창에서 직접 로그인을 완료하세요", "取消失败:{message}": "취소 실패: {message}", "取消等待": "대기 취소", "只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页": "「내장 창」에서만 제3자 로그인 항목을 복원할 수 있습니다: 시스템 브라우저에서는 로그인 페이지를 수정할 수 없습니다", "可选": "선택", "可选(cloudaicompanionProject):留空由网关自动发现": "선택 (cloudaicompanionProject): 비워 두면 게이트웨이가 자동으로 찾습니다", "可选,展示用": "선택, 표시용", "可选,没有则无法自动续期": "선택, 없으면 자동 갱신 불가", "可选,留空使用凭据里的 user_name": "선택, 비워 두면 자격 증명의 user_name 사용", "可选,留空使用账号姓名、邮箱或令牌指纹": "선택, 비워 두면 계정 이름, 이메일 또는 토큰 지문 사용", "可选,留空使用账号昵称或邮箱": "선택, 비워 두면 계정 닉네임 또는 이메일 사용", "可选,留空则用 key 尾号": "선택, 비워 두면 key 끝자리 사용", "可选,留空则用 userId": "선택, 비워 두면 userId 사용", "可选,留空则用上游返回的账号名或账号 id": "선택, 비워 두면 업스트림이 반환한 계정 이름 또는 계정 id 사용", "可选,留空则用凭证里的账号名": "선택, 비워 두면 자격 증명의 계정 이름 사용", "可选,留空则用文件里的昵称": "선택 사항, 비워 두면 파일의 닉네임 사용", "可选,留空则用登录名或 uid": "선택, 비워 두면 로그인 이름 또는 uid 사용", "可选,留空则用脱敏手机号": "선택, 비워 두면 마스킹된 휴대폰 번호 사용", "可选,留空则用脱敏手机号或 userId": "선택, 비워 두면 마스킹된 휴대폰 번호 또는 userId 사용", "可选,留空则用账号 uk": "선택, 비워 두면 계정 uk 사용", "可选,留空则用账号昵称": "선택 사항. 비워 두면 계정 닉네임을 사용합니다", "可选,留空则用账号昵称或 userId": "선택 사항. 비워 두면 계정 닉네임 또는 userId 사용", "可选,留空则用邮箱或令牌尾号": "선택, 비워 두면 이메일 또는 토큰 끝자리 사용", "可选,留空用「OfficeAce 果办」": "선택, 비워 두면 「OfficeAce」 사용", "可选,留空自动生成": "선택, 비워 두면 자동 생성", "可选,留空自动用昵称": "선택, 비워 두면 닉네임 자동 사용", "可选,给这个访客账号起个备注名": "선택 사항. 이 게스트 계정의 메모 이름", "可选,续期时带上": "선택, 갱신 시 함께 전송", "可选,讯飞侧 userid": "선택, iFlytek 측 userid", "可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补": "선택; 대화 시작에 필요합니다. 비워 두면 게이트웨이가 이 계정의 기존 작업에서 자동으로 찾습니다. 찾지 못하면 나중에 직접 입력하면 됩니다.", "可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)": "선택; 계정 이메일로, 표시와 신원 식별에 사용됩니다 (새 토큰으로 바꿔도 같은 계정으로 인식됩니다)", "可选;UUID 形态,与凭据同生共死": "선택; UUID 형식이며 자격 증명과 함께 생성되고 함께 사라집니다", "可选;填了才能到期自动续期": "선택; 입력해야 만료 시 자동 갱신할 수 있습니다", "可选;用于去重与展示名(留空时网关会问一次上游)": "선택; 중복 제거와 표시 이름에 사용됩니다 (비워 두면 게이트웨이가 업스트림에 한 번 물어봅니다)", "可选;用于生成账号 id 与展示名": "선택; 계정 id와 표시 이름 생성에 사용됩니다", "可选;纯数字串(上游只收 8–24 位数字)": "선택; 숫자만 입력 (업스트림은 8–24자리 숫자만 허용)", "可领取": "수령 가능", "同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)": "한 제공사에 API Key 있는 계정과 인증 불필요 계정을 섞어 둘 수 있으며 각각의 규칙으로 라우팅됩니다. 인증 불필요 계정도 여러 개 둘 수 있고 각각 다른 아웃바운드 프록시를 설정할 수 있습니다", "同地址换 anthropic 协议即走它的 Claude 兼容端点。转发必须填 OpenCode Go 的 API Key(订阅制;获取模型清单不需要 Key,但发请求上游会回 401「Missing API key」)": "같은 주소에서 anthropic 프로토콜로 바꾸면 해당 Claude 호환 엔드포인트를 사용합니다. 포워딩에는 OpenCode Go의 API Key가 반드시 필요합니다 (구독제; 모델 목록 조회에는 Key가 필요 없지만, 요청을 보내면 업스트림이 401 「Missing API key」를 반환합니다)", "响应为空": "응답이 비어 있습니다", "商汤日日新的 OpenAI 兼容端点;余额查询走控制台账号密码(见下方「余额查询」一段)": "SenseNova 의 OpenAI 호환 엔드포인트. 잔액 조회는 콘솔 계정 / 비밀번호를 사용합니다 (아래 \"잔액 조회\" 단락 참조)", "国内版": "중국판", "国内版与**国际版是两套独立的账号与套餐**,凭证与领取的套餐都不通用。": "중국판과 **국제판은 서로 독립된 두 세트의 계정과 플랜**입니다. 자격 증명과 수령한 플랜은 서로 호환되지 않습니다.", "国内版登录页没有 Google / GitHub 入口(它用微信 / 手机号 / 邮箱登录)": "중국판 로그인 페이지에는 Google / GitHub 항목이 없습니다 (WeChat / 휴대폰 번호 / 이메일로 로그인합니다)", "国内版的登录站点是 www.accio-ai.com,与国际版不是同一站。": "중국판의 로그인 사이트는 www.accio-ai.com이며, 국제판과 같은 사이트가 아닙니다.", "国内版(WorkBuddy)": "중국판 (WorkBuddy)", "国内站为 https://api.siliconflow.cn/v1": "중국 사이트는 https://api.siliconflow.cn/v1입니다", "国内站(minimaxi.com);OpenAI 兼容地址为 https://api.minimaxi.com/v1": "중국 사이트 (minimaxi.com); OpenAI 호환 주소는 https://api.minimaxi.com/v1입니다", "国际版": "국제판", "国际版 z.ai 的 Coding 套餐;OpenAI 兼容地址为 https://api.z.ai/api/coding/paas/v4": "국제판 z.ai의 Coding 플랜; OpenAI 호환 주소는 https://api.z.ai/api/coding/paas/v4입니다", "国际版与国内版是**两套独立的账号**(同一账号体系的两个站点),凭证不通用。": "국제판과 중국판은 **서로 독립된 두 세트의 계정**입니다 (같은 계정 체계의 두 사이트). 자격 증명은 호환되지 않습니다.", "国际版与国内版账号体系独立,请填国际版的凭证。未填 refreshToken 无法自动续期。": "국제판과 중국판은 계정 체계가 독립적입니다. 국제판의 자격 증명을 입력하세요. refreshToken을 입력하지 않으면 자동 갱신할 수 없습니다.", "国际版登录页默认只显示邮箱登录,勾选后恢复 Google / GitHub / X 入口": "국제판 로그인 페이지는 기본적으로 이메일 로그인만 표시하며, 체크하면 Google / GitHub / X 항목이 복원됩니다", "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "국제판의 추론 사이트는 api.z.ai이며 중국판과 다른 사이트입니다; 플랜도 각각 독립적입니다.", "国际版(WorkBuddy AI)": "국제판 (WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "국제 사이트 Coding 플랜; OpenAI 호환 주소는 https://api.minimax.io/v1입니다", "在 cc-switch 里找到": "cc-switch에서 찾은", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "내장 창에서 공식 로그인을 완료하면 성공 시 자동으로 계정 목록에 추가됩니다.", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "{label}의 Qoder 계정 설정 → Integrations에서 PAT를 생성하세요 (Google / GitHub 토큰을 입력하지 마세요). 두 지역의 계정과 자격 증명은 호환되지 않습니다.", "在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。": "열린 공식 인증 페이지에서 위챗 QR 코드를 스캔하여 로그인하세요(페이지에 QR 코드가 바로 표시됩니다). 스캔에 성공하면 계정이 자동으로 목록에 추가됩니다.", "在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "열린 창에서 Baidu 계정으로 KukuAI 메인 사이트에 로그인하세요 (SMS 인증 코드 / QR 스캔 모두 가능). 로그인에 성공하면 자동으로 계정 목록에 추가됩니다. 게이트웨이는 Baidu에서 KukuAI 비즈니스 세션 토큰을 교환 발급받습니다 (이 컴퓨터에 KukuAI 클라이언트가 설치되어 있어야 합니다).", "在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "열린 창에서 Google 계정으로 로그인하세요(Antigravity의 인가 페이지). 인가가 완료되면 자동으로 계정 목록에 추가됩니다.", "在浏览器中打开登录页": "브라우저에서 로그인 페이지 열기", "地区": "지역", "填写凭证": "자격 증명 입력", "填写凭证添加": "자격 증명 입력 후 추가", "备注名": "라벨명", "套餐": "플랜", "如:智谱 GLM": "예: Zhipu GLM", "完成登录后自动加入账号列表,昵称取自上游。": "로그인을 완료하면 자동으로 계정 목록에 추가되며, 닉네임은 업스트림에서 가져옵니다.", "官方客户端凭证目录:Windows 是 C:\\Users\\<你的用户名>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth,macOS 是 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth": "공식 클라이언트 자격 증명 디렉터리: Windows는 C:\\Users\\<사용자명>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth, macOS는 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth", "官方已确认到账": "공식 적립 확인 완료", "导入": "가져오기", "导入中…": "가져오는 중…", "导入凭证文件": "자격 증명 파일 가져오기", "导入失败": "가져오기 실패", "导入失败:{reason}": "가져오기 실패: {reason}", "导入所选": "선택 항목 가져오기", "导入所选({n})": "선택 항목 가져오기({n})", "导入桌面端登录态": "데스크톱 로그인 상태 가져오기", "将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下": "내장 창이 열립니다; 인가가 완료되면 자동으로 계정 목록에 추가됩니다. 창을 닫으면 대기가 취소됩니다. 인가 페이지 마지막에 zcode:// 링크를 열 수 없다는 안내가 나올 수 있는데, 정상입니다 — 결과는 이미 서버 측에 기록되었습니다", "将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)": "내장 창이 열립니다; 인가가 완료되면 자동으로 계정 목록에 추가됩니다. 원격 패널에서는 콜백 주소를 붙여넣을 수 있습니다. 창을 닫으면 대기가 취소됩니다. 링크는 5분간 유효하며, 시간이 초과되거나 클릭하지 않으면 무효가 됩니다 (다시 시작할 수 있습니다)", "将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待": "내장 창이 열립니다; 로그인이 완료되면 자동으로 계정 목록에 추가됩니다. 창을 닫으면 대기가 취소됩니다", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 Trae 账号);同机完成后自动加入,Docker / 远程面板按提示粘贴地址栏回调地址。": "시스템 기본 브라우저로 인가 페이지를 엽니다 (브라우저에 로그인된 Trae 계정을 재사용합니다); 같은 컴퓨터에서는 완료 시 자동으로 추가되며, Docker / 원격 패널에서는 안내에 따라 주소 표시줄의 콜백 주소를 붙여넣으세요.", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 ZCode 账号);授权完成后自动加入账号列表。页面最后可能提示无法打开 zcode:// 链接,属正常现象": "시스템 기본 브라우저로 인가 페이지를 엽니다 (브라우저에 로그인된 ZCode 계정을 재사용합니다); 인가가 완료되면 자동으로 계정 목록에 추가됩니다. 페이지 마지막에 zcode:// 링크를 열 수 없다는 안내가 나올 수 있는데 정상입니다", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "시스템 기본 브라우저로 인가 페이지를 엽니다 (브라우저에 로그인된 Huawei Cloud 계정을 재사용합니다); 인가 코드는 게이트웨이가 클라우드를 폴링해 가져오므로 브라우저가 어느 컴퓨터에서 실행되는지는 상관없습니다. 완료하면 자동으로 계정 목록에 추가됩니다", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "시스템 기본 브라우저로 인가 페이지를 엽니다 (브라우저에 로그인된 Huawei Cloud 계정을 재사용합니다); 브라우저와 게이트웨이가 같은 컴퓨터에 있지 않다면 위 설명대로 최종 콜백 주소를 패널에 다시 붙여넣으세요", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "시스템 기본 브라우저로 로그인 페이지를 엽니다 (브라우저에 로그인된 Accio 계정을 재사용합니다); 로그인을 완료하면 자동으로 계정 목록에 추가되며, 팝업을 닫으면 대기가 취소됩니다", "小米 MiMo Token Plan 套餐(新加坡站)": "小米 MiMo Token Plan 플랜 (싱가포르 사이트)", "已到账": "적립 완료", "已取消登录等待": "로그인 대기를 취소했습니다", "已取消领取": "수령을 취소했습니다", "已取消验证码": "인증 코드를 취소했습니다", "已填写手机号,正在发送验证码…": "휴대폰 번호 입력 완료, 인증 코드 전송 중…", "已声明无需鉴权:账号会被正常选路,出网时不带任何鉴权头": "인증 필요 없음으로 선언됨: 계정이 정상적으로 라우팅되며, 외부로 나갈 때 인증 헤더를 전혀 보내지 않습니다", "已导入 {ok} 个,失败 {fail} 个(失败项已标注在列表里)": "가져오기 성공 {ok}개, 실패 {fail}개 (실패 항목은 목록에 표시해 두었습니다)", "已建过 · {n} 个账号,点击继续添加": "이미 생성됨 · 계정 {n}개, 클릭하여 추가", "已建过,点击继续添加账号": "이미 생성됨, 클릭하여 계정 추가", "已打开官方登录页,请在窗口中完成登录…": "공식 로그인 페이지를 열었습니다. 창에서 로그인을 완료하세요…", "已点发送,等待短信…": "전송을 눌렀습니다. SMS 대기 중…", "已用系统默认浏览器打开登录页,请在浏览器中完成登录…": "시스템 기본 브라우저로 로그인 페이지를 열었습니다. 브라우저에서 로그인을 완료하세요…", "已领取,等官方确认": "수령 완료, 공식 확인 대기 중", "当前壳版本不支持 AutoClaw 网页登录,请更新应用": "현재 앱 버전에서는 AutoClaw 웹 로그인을 지원하지 않습니다. 앱을 업데이트하세요", "当前没有可领取的套餐(活动尚未开始)": "현재 수령 가능한 플랜이 없습니다 (이벤트가 아직 시작되지 않았습니다)", "当前环境不支持领取(桥接方法缺失)": "현재 환경에서는 수령을 지원하지 않습니다 (브리지 메서드 없음)", "必填,CatPaw 账号标识": "필수, CatPaw 계정 식별자", "恢复 Google / GitHub 入口": "Google / GitHub 항목 복원", "手机号": "휴대폰 번호", "手机验证码登录": "SMS 인증 코드 로그인", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Trae SOLO의 공식 인가 페이지(trae.cn)를 열고 Trae 계정으로 로그인하세요: 인가가 완료되면 공식 페이지가 이 컴퓨터의 임시 포트로 되돌아옵니다; 같은 컴퓨터 배포에서는 자동으로 수신하며, Docker / 원격 패널에서는 안내에 따라 주소 표시줄의 콜백 주소를 복사하세요.", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Accio {label}의 공식 로그인 페이지({site})를 열고 Accio 계정으로 로그인하세요: 로그인에 성공하면 공식 페이지가 이 컴퓨터로 되돌아옵니다; 같은 컴퓨터에서는 자동으로 완료되며, Docker / 원격 패널에서는 안내에 따라 콜백 주소를 붙여넣으면 됩니다 (인가 코드는 이 컴퓨터에서만 게이트웨이에 전달되며, 화면에 평문 token이 표시되지 않습니다).", "打开 Accio {label}登录页": "Accio {label} 로그인 페이지 열기", "打开 Antigravity 网页登录": "Antigravity 웹 로그인 열기", "打开 CatPaw 网页登录": "CatPaw 웹 로그인 열기", "打开 Cline 授权页": "Cline 인가 페이지 열기", "打开 CodeArts 授权页": "CodeArts 인가 페이지 열기", "打开 Qoder 网页登录": "Qoder 웹 로그인 열기", "打开 Trae 授权页": "Trae 인가 페이지 열기", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "ZCode {label}의 공식 인가 페이지를 열고 계정으로 로그인하세요. 인가 결과는 ZCode 서버에 기록되며, 게이트웨이가 백그라운드에서 자격 증명을 자동으로 가져와 계정 목록에 추가합니다.", "打开 ZCode {label}授权页": "ZCode {label} 인가 페이지 열기", "打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
    网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。": "Huawei Cloud CodeArts의 공식 인가 페이지를 열어 로그인하세요: 로그인이 완료되면 공식 페이지가 브라우저를 게이트웨이 자체의 /oauth/callback으로 되돌립니다. 게이트웨이는 일회성 인가 코드를 임시 자격 증명으로 교환하고 계정 목록에 추가합니다.
    게이트웨이가 다른 컴퓨터에서 실행 중이면 웹 UI에서 주소 표시줄의 최종 콜백 주소를 패널에 그대로 붙여넣으라는 안내가 표시됩니다.", "打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。": "Huawei Cloud 인가 페이지를 열고 로그인하세요: 완료되면 게이트웨이가 클라우드를 폴링해 인가 코드를 가져와 계정 목록에 추가합니다 (OfficeAce 데스크톱 앱이 필요 없고, 콜백 URL을 수동으로 다시 붙여넣을 필요도 없습니다).", "打开官方授权页完成设备码授权,登录的是{label}({site})的账号。": "공식 인가 페이지를 열어 기기 코드 인가를 완료하세요. 로그인되는 계정은 {label} ({site}) 계정입니다.", "打开授权页": "인가 페이지 열기", "打开授权页完成确认,账号自动加入 {label}{note}": "인가 페이지를 열어 확인을 완료하면 계정이 {label}에 자동으로 추가됩니다{note}", "打开方式": "여는 방식", "打开网页登录": "웹 로그인 열기", "探测失败:{message}": "탐지 실패: {message}", "接入一个兼容上游": "호환 업스트림 연결", "提交中…": "제출 중…", "搜索提供商": "제공자 검색", "搜索提供商…": "제공자 검색…", "整份粘贴官方插件 / CLIProxyAPI 落盘的凭据 JSON(形如 {\"codearts_provider_credential\":{…}},铺平的也行)。
    必填:access_key_id、secret_access_key、security_token。要能自动续期,必须连 refresh_token 与 oauth_context 一起粘 —— 临时凭据约一小时到期,缺这半块就续不回来,只能重新登录。": "공식 플러그인 / CLIProxyAPI가 디스크에 기록한 자격 증명 JSON을 통째로 붙여넣으세요 (형식: {\"codearts_provider_credential\":{…}}, 평탄화된 형태도 가능).
    필수: access_key_id, secret_access_key, security_token.자동 갱신을 원하면 refresh_token과 oauth_context도 함께 붙여넣어야 합니다 — 임시 자격 증명은 약 1시간 후 만료되며, 이 부분이 없으면 갱신할 수 없어 다시 로그인해야 합니다.", "文件是{edition}凭证,已按该版本导入": "파일은 {edition} 자격 증명이며 해당 버전으로 가져왔습니다", "新建自定义提供商": "커스텀 제공자 새로 만들기", "方案:{label}": "방식: {label}", "明文 JWT 或 auth.json 里的 enc: 加密值(自动解密)": "평문 JWT 또는 auth.json의 enc: 암호화 값 (자동 복호화)", "明文 JWT(国际版账号的 access token)": "평문 JWT (국제판 계정의 access token)", "智谱 bigmodel.cn 的 Coding 套餐,Key 在 open.bigmodel.cn/usercenter/apikeys": "Zhipu bigmodel.cn의 Coding 플랜, Key는 open.bigmodel.cn/usercenter/apikeys에서 발급합니다", "未知错误": "알 수 없는 오류", "未能获取授权地址,请重试": "인가 주소를 가져오지 못했습니다. 다시 시도하세요", "未能读取 cc-switch 数据": "cc-switch 데이터를 읽지 못했습니다", "未领取成功": "수령하지 못했습니다", "本地 Ollama、OpenCode Zen 免费档这类上游要勾上,否则账号不可用": "로컬 Ollama, OpenCode Zen 무료 등급 같은 업스트림에는 체크해야 하며, 그렇지 않으면 계정을 사용할 수 없습니다", "本家有两个**互不替代**的凭证,按你要用的功能填,至少填一个:编码套餐 API Key 用于转发推理(打 {site})与查询窗口限额(每 N 小时 / 每周的剩余比例);jwt 用于领取套餐与查询余额(打 zcode.z.ai 的 billing 网关,官方叫 Coding Plan JWT,是一串三段点分的字符串)。余额两把都能查、但读到的东西不同:jwt 读到余额桶与套餐到期,API Key 读到窗口限额;而领取只认 jwt、转发只认 API Key。请填写 {label}账号的凭证 —— {plan}(最容易拿到的办法:直接用上方的「网页登录」—— 它会替你把这个 API Key 换好,两个凭证一起拿到。)": "이 제공자에는 **서로 대체할 수 없는** 자격 증명이 두 개 있습니다. 사용할 기능에 따라 입력하고 최소 하나는 입력하세요: 코딩 플랜 API Key는 추론 포워딩({site} 호출)과 윈도우 할당량 조회(N시간마다 / 주간 잔여 비율)에 사용하고, jwt는 플랜 수령과 잔액 조회에 사용합니다(zcode.z.ai의 billing 게이트웨이 호출, 공식 명칭 Coding Plan JWT, 3단 점 구분 문자열). 잔액은 둘 다 조회할 수 있지만 읽어 오는 내용이 다릅니다: jwt는 잔액 버킷과 플랜 만료를, API Key는 윈도우 할당량을 읽습니다. 수령은 jwt만, 포워딩은 API Key만 인정합니다. {label} 계정의 자격 증명을 입력하세요 — {plan} (가장 쉽게 얻는 방법: 위의 「웹 로그인」을 바로 사용하세요 — 이 API Key를 대신 교환해 두 자격 증명을 함께 받아 줍니다.)", "本机 Ollama 的 OpenAI 兼容端点,无需 API Key(默认已勾上「该上游无需鉴权」,鉴权头一个都不发)": "이 컴퓨터의 Ollama OpenAI 호환 엔드포인트, API Key 불필요 (기본적으로 「이 업스트림은 인증이 필요하지 않음」이 체크되어 있고, 인증 헤더를 전혀 보내지 않습니다)", "本次未确认到账": "이번에는 적립이 확인되지 않았습니다", "条可导入": "건을 가져올 수 있습니다", "条配置,其中": "건의 설정 중", "桌面运行时不可用(Tauri 未初始化)": "데스크톱 런타임을 사용할 수 없습니다 (Tauri 미초기화)", "模型网关基址": "모델 게이트웨이 기본 주소", "正在打开百度登录页…": "Baidu 로그인 페이지를 여는 중…", "正在扫描本机 cc-switch…": "이 컴퓨터의 cc-switch를 스캔하는 중…", "正在换发访客凭证…": "게스트 자격 증명을 발급하는 중…", "正在添加…": "추가하는 중…", "正在等待{name}登录完成;完成或取消后才能发起新的登录": "{name} 로그인 완료를 기다리는 중입니다; 완료하거나 취소한 후에 새로운 로그인을 시작할 수 있습니다", "每周": "매주", "每日": "매일", "每月": "매월", "没有则无法自动续期": "없으면 자동 갱신할 수 없습니다", "没有匹配「{keyword}」的提供商": "「{keyword}」와 일치하는 제공자가 없습니다", "添加 {name} 账号": "{name} 계정 추가", "添加中…": "추가 중…", "添加到 ": "추가 대상: ", "添加失败:{reason}": "추가 실패: {reason}", "添加小浣熊账号": "小浣熊 계정 추가", "添加方式": "추가 방식", "添加访客账号": "게스트 계정 추가", "添加账号": "계정 추가", "火山引擎 Coding 套餐;通用推理接入点为 https://ark.cn-beijing.volces.com/api/v3": "Volcano Engine Coding 플랜; 범용 추론 엔드포인트는 https://ark.cn-beijing.volces.com/api/v3입니다", "点击添加账号": "클릭하여 계정 추가", "点开即预填,填 Key 接入": "열면 값이 미리 채워져 있으며, Key를 입력하면 연결됩니다", "点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
    这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。": "버튼을 누르면 먼저 슬라이더 인증을 한 번 통과하고(공식 리스크 관리), 이어서 로그인 페이지가 열리며 로그인이 완료되면 계정이 자동으로 추가됩니다.
    이것이 국제판의 공식 유일한 로그인 방식입니다; 이미 클라이언트에 로그인한 적이 있다면 「데스크톱 로그인 상태 가져오기」가 더 빠릅니다.", "用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。": "AutoClaw 계정에 연결된 휴대폰 번호로 로그인합니다: 「코드 받기」를 클릭하고 SMS를 받은 뒤 아래에 입력하여 로그인하세요. 인증 코드는 이 컴퓨터에서 공식 API로 직접 제출되며, 화면에 token이 표시되지 않습니다.", "用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。": "CatPaw 계정으로 로그인합니다 (Meituan passport), 성공하면 자동으로 계정 목록에 추가됩니다.", "用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。": "Loomy 계정에 연결된 휴대폰 번호로 로그인합니다: 「코드 받기」를 클릭한 뒤 입력하면 됩니다. 이 제공자에서 가장 간편한 방법입니다.", "用于转发;形如 apiKey.secret 两段点分(不填则这个账号不能转发)": "포워딩에 사용합니다; apiKey.secret 형태의 2단 점 구분 (입력하지 않으면 이 계정은 포워딩할 수 없습니다)", "用于领取套餐与查询余额;不填则这个账号不能领取与查余额": "플랜 수령과 잔액 조회에 사용합니다; 입력하지 않으면 이 계정은 수령과 잔액 조회를 할 수 없습니다", "用户 ID": "사용자 ID", "用绑定的手机号登录:点「获取验证码」后填入即可。这是国内版官方唯一的登录方式。": "연결된 휴대폰 번호로 로그인합니다: 「코드 받기」를 클릭한 뒤 입력하면 됩니다. 이것이 중국판의 공식 유일한 로그인 방식입니다.", "留空则用提供商名称": "비워 두면 제공자 이름 사용", "登录 / 添加 {name} 账号": "로그인 / {name} 계정 추가", "登录 session(一长串)": "로그인 session (긴 문자열)", "登录中…": "로그인 중…", "登录失败:{message}": "로그인 실패: {message}", "登录已结束": "로그인이 종료되었습니다", "登录并添加": "로그인 후 추가", "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "로그인 상태 파일 경로: ~/.box-agent/config/auth.json (Windows: C:\\Users\\<사용자 이름>\\.box-agent\\config\\auth.json)", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "커스텀 제공자 「{name}」을(를) 삭제할까요? 이 제공자 아래의 계정 {count}개도 함께 삭제되며, 삭제 후에는 복구할 수 없습니다.", "等待 Accio {label}登录完成…": "Accio {label} 로그인 대기 중…", "等待 CatPaw 登录完成…": "CatPaw 로그인 대기 중…", "等待 Cline 授权确认…": "Cline 인가 확인 대기 중…", "等待 CodeArts 登录完成…": "CodeArts 로그인 대기 중…", "等待 Google 授权完成…": "Google 인가 대기 중…", "等待 KukuAI 登录完成…": "KukuAI 로그인 대기 중…", "等待 OfficeAce 登录完成…": "OfficeAce 로그인 대기 중…", "等待 OrcaTerm 登录完成…": "OrcaTerm 로그인 대기 중…", "等待 Qoder 授权完成…": "Qoder 인가 대기 중…", "等待 Trae 授权完成…": "Trae 인가 대기 중…", "等待 ZCode {label}授权完成…": "ZCode {label} 인가 대기 중…", "等待小浣熊登录完成…": "小浣熊 로그인 대기 중…", "等待登录完成…": "로그인 완료 대기 중…", "等待网页登录…": "웹 로그인 대기 중…", "粘贴 API Key": "API Key 붙여넣기", "粘贴 Accio 的 accessToken": "Accio의 accessToken 붙여넣기", "粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
    这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。": "Antigravity의 Google refresh token을 붙여넣으세요 (1//로 시작; refresh_token=이 포함된 줄 전체를 붙여넣어도 됨).
    웹 로그인 외의 수동 입구입니다(예: 로그인된 공식 IDE에서 내보내기). refresh token은 만료되지 않으며, access token 만료 시 게이트웨이가 자동으로 갱신합니다; 토큰을 잃어버리면 Google 인가를 다시 받아야 하므로 잘 보관하세요.", "粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。": "Command Code의 API Key를 붙여넣으세요 (user_로 시작; 공식 CLI의 ~/.commandcode/auth.json 또는 웹 studio에서 획득). 줄 전체 / 따옴표 포함 / Bearer 접두사 포함 그대로 붙여넣어도 됩니다. 게이트웨이가 알아서 순수한 key만 추출합니다. key는 만료되지 않고 갱신 인터페이스도 없으므로, 무효가 되면 다시 받으면 됩니다; 추가 시 게이트웨이가 가벼운 활성 확인을 한 번 수행하며, 실패해도 추가에는 영향을 주지 않습니다.", "粘贴 Cookie": "Cookie 붙여넣기", "粘贴 Google refresh token": "Google refresh token 붙여넣기", "粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。": "OrcaTerm의 OAuth accessToken(점 3개로 구분된 JWT. 데스크톱 클라이언트의 data.bin 또는 웹 버전 Cookie ot_session에서 확인)을 붙여넣으세요. Bearer / ot_session=이 포함된 줄을 그대로 붙여넣어도 되며, 게이트웨이가 토큰만 추출합니다. 추가 후에는 게이트웨이가 자동으로 갱신하므로(업스트림에 갱신 API가 있음) 2시간 만료를 직접 따라갈 필요가 없습니다.", "粘贴 Qoder 个人访问令牌(pt-…)": "Qoder 개인 액세스 토큰(pt-…) 붙여넣기", "粘贴 accessToken": "accessToken 붙여넣기", "粘贴 access_token(一长串 JWT)": "access_token 붙여넣기 (긴 JWT)", "粘贴 session cookie": "session cookie 붙여넣기", "粘贴 token / refreshToken": "token / refreshToken 붙여넣기", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "workos:…로 시작하는 토큰 붙여넣기 (접두사가 없어도 자동으로 붙습니다)", "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "智谱清言 액세스 토큰(점 3개로 구분된 JWT)을 붙여넣으세요. 발급 방법: 브라우저에서 chatglm.cn을 열고 로그인한 뒤 개발자 도구의 Application → Cookies에서 chatglm_token 값을 복사합니다(chatglm_token=…; 줄을 그대로 붙여도 됩니다). 붙여넣은 뒤 업스트림에 한 번 검증하며, 통과한 경우에만 계정을 저장합니다. 로그인된 계정은 포인트와 签到 기능을 쓸 수 있고, 이후 게이트웨이가 자동으로 갱신합니다(업스트림에 갱신 인터페이스가 있습니다).", "粘贴登录凭据": "로그인 자격 증명 붙여넣기", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "붙여넣는 것은 로그인 session입니다 (동시에 모델 게이트웨이의 token입니다). Loomy에는 갱신 인터페이스가 없으며 session 유효 기간은 약 14일이므로, 만료되면 다시 로그인하면 됩니다.", "粘贴网关凭据": "게이트웨이 자격 증명 붙여넣기", "粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
    {siteNote}
    MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。": "웹 로그인 상태 cookie monkeycode_ai_session의 값을 붙여넣으세요 (브라우저에서 전체 Cookie를 복사해 그대로 붙여넣어도 됩니다. 게이트웨이가 값을 알아서 추출합니다).
    {siteNote}
    MonkeyCode에는 갱신 인터페이스가 없습니다: session 유효 기간은 약 30일(하드 제한)이며, 만료되면 웹에 다시 로그인해 한 번 더 붙여넣으면 됩니다.", "粘贴访问令牌": "액세스 토큰 붙여넣기", "系统浏览器": "시스템 브라우저", "系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。": "시스템 브라우저는 기존 로그인 상태를 재사용할 수 있습니다 (국제판 권장); 내장 창이 더 깔끔합니다.", "系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表": "시스템 브라우저로 엽니다 (로그인된 Cline 계정 재사용); 완료하면 자동으로 목록에 추가됩니다", "系统浏览器打开(复用已登录的 Google 账号);完成后自动加入列表": "시스템 브라우저로 엽니다(로그인된 Google 계정 재사용); 완료하면 자동으로 목록에 추가됩니다", "系统浏览器打开(复用已登录的 Zai / Google 账号);完成后自动加入列表": "시스템 브라우저로 엽니다 (로그인된 Zai / Google 계정 재사용); 완료하면 자동으로 목록에 추가됩니다", "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "시스템 브라우저로 엽니다 (로그인된 Meituan 계정 재사용); 완료하면 자동으로 목록에 추가됩니다", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "시스템 브라우저로 엽니다 (로그인된 계정 재사용); 완료하면 자동으로 목록에 추가됩니다", "系统默认浏览器": "시스템 기본 브라우저", "编码套餐 API Key": "코딩 플랜 API Key", "网关 Secret": "게이트웨이 시크릿", "网页登录": "웹 로그인", "网页登录的打开方式": "웹 로그인을 여는 방식", "网页登录(Zai / Google)": "웹 로그인 (Zai / Google)", "自定义": "커스텀", "自建聚合面板:填你自己的站点地址。余额查询用该账号的 API Key 打 {base}/v1/usage,不需要额外配置": "자체 구축 어그리게이션 패널: 자신의 사이트 주소를 입력하세요. 잔액 조회는 이 계정의 API 키로 {base}/v1/usage 를 호출하며 추가 설정이 필요 없습니다", "自建聚合面板:填你自己的站点地址;余额查询要另填 NewAPI 的个人访问令牌(见下方「余额查询」一段)": "자체 구축 어그리게이션 패널: 자신의 사이트 주소를 입력하세요. 잔액 조회에는 별도로 NewAPI 개인 액세스 토큰이 필요합니다 (아래 \"잔액 조회\" 단락 참조)", "获取风控配置失败,将直接尝试领取:{message}": "리스크 관리 구성을 가져오지 못했습니다. 바로 수령을 시도합니다: {message}", "获取验证码": "코드 받기", "访客模式": "게스트 모드", "访客模式(免登录)": "게스트 모드(로그인 불필요)", "该上游无需鉴权(不发送鉴权头)": "이 업스트림은 인증이 필요하지 않음 (인증 헤더를 보내지 않음)", "该提供商": "이 제공자", "该提供商账号添加功能即将上线": "이 제공자의 계정 추가 기능이 곧 출시됩니다", "该自定义提供商已不存在(可能已被删除),请刷新后重试": "이 커스텀 제공자가 더 이상 존재하지 않습니다(삭제되었을 수 있습니다). 새로 고친 후 다시 시도하세요", "请先选择一个自定义提供商": "먼저 커스텀 제공자를 선택하세요", "请在弹出的滑块中完成验证(官方要求的风控步骤)": "표시된 슬라이더에서 인증을 완료하세요 (공식에서 요구하는 리스크 관리 단계)", "请填写 11 位大陆手机号": "11자리 중국 본토 휴대폰 번호를 입력하세요", "请填写 6 位数字验证码": "6자리 숫자 인증 코드를 입력하세요", "请填写 Base URL": "Base URL을 입력하세요", "请填写 {label}": "{label}을(를) 입력하세요", "请填写名称": "이름을 입력하세요", "请完成验证…": "인증을 완료하세요…", "读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows": "%APPDATA%/AutoClaw/auth.json을 읽어 복호화합니다. Windows 전용", "读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录": "~/.cline/data/settings/providers.json을 읽습니다. Cline 클라이언트에 로그인되어 있어야 합니다", "读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录": "~/.meituan-catpaw/auth.json을 읽습니다. CatPaw 클라이언트에 로그인되어 있어야 합니다", "读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录": "이 컴퓨터의 KukuAI 클라이언트 로그인 상태를 읽습니다 (%APPDATA%\\baidugenflowpro\\Network\\Cookies). KukuAI 클라이언트에 로그인되어 있어야 합니다", "读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录": "로컬 OrcaTerm 클라이언트 로그인 상태(%APPDATA%\\com.orcaterm-desktop.app\\data.bin)를 읽습니다. OrcaTerm 클라이언트에 로그인되어 있어야 합니다", "读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录": "이 PC의 智谱清言 클라이언트 로그인 상태를 읽습니다(%APPDATA%\\chatglm\\Network\\Cookies). 클라이언트에 로그인되어 있어야 합니다", "读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。": "이 컴퓨터의 공식 클라이언트(Tencent WorkBuddy / WorkBuddy AI)에 현재 로그인된 계정을 읽습니다. 자격 증명 디렉터리에서 최신 로그인 상태 파일을 자동으로 찾습니다(이 컴퓨터의 공식 클라이언트에 로그인되어 있어야 함). 가져온 후에는 게이트웨이가 자동 갱신하므로 2시간 유효 기간에 맞춰 토큰을 수동으로 교체할 필요가 없습니다.", "读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 읽습니다 (평문 Cookie, 복호화 불필요). 클라이언트 실행 중에는 Cookies 파일이 독점 점유되므로, 가져오기 전에 KukuAI 클라이언트를 먼저 종료하세요.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 매번 실시간으로 읽습니다 (이 기록을 삭제해도 클라이언트 로그인 상태에는 영향을 주지 않습니다).", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后点「刷新 Token」同步。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 매번 실시간으로 읽습니다 (이 기록을 삭제해도 클라이언트 로그인 상태에는 영향을 주지 않습니다). 클라이언트에서 다시 로그인한 후 「Token 갱신」을 눌러 동기화하세요.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后重新导入即可。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 매번 실시간으로 읽습니다 (이 기록을 삭제해도 클라이언트 로그인 상태에는 영향을 주지 않습니다). 클라이언트에서 다시 로그인한 후 다시 가져오면 됩니다.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 매번 실시간으로 읽습니다 (이 기록을 삭제해도 클라이언트 로그인 상태에는 영향을 주지 않습니다). Zai / Google로 클라이언트에 로그인하는 사용자는 이 항목을 사용하세요.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 매번 실시간으로 읽습니다 (이 기록을 삭제해도 클라이언트 로그인 상태에는 영향을 주지 않습니다). 이 항목은 {label}로 분류됩니다.", "读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。": "로컬 클라이언트가 현재 로그인한 OAuth 토큰을 읽습니다(평문 JSON, 복호화 불필요). 가져온 뒤에는 게이트웨이가 자동으로 갱신하므로 클라이언트의 2시간 만료에 맞춰 수동으로 교체할 필요가 없습니다.", "读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。": "이 PC 클라이언트의 현재 로그인 상태를 읽습니다. 클라이언트의 Cookies는 DPAPI로 암호화되어 있어(Chromium os_crypt) Windows에서만 사용할 수 있습니다. 클라이언트가 실행 중이면 이 파일을 읽지 못할 수 있으니 클라이언트를 종료한 뒤 다시 시도하세요(\"자격 증명 입력\"으로 토큰을 붙여넣어도 됩니다).", "账号信息": "계정 정보", "账号信息不完整,请刷新后重试": "계정 정보가 불완전합니다. 새로 고친 후 다시 시도하세요", "账号已添加,但请留意:{warning}": "계정은 추가되었지만 확인해 주세요: {warning}", "账号版本": "계정 버전", "账号类型": "계정 유형", "账号:{name}
    ": "계정: {name}
    ", "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 创建": "Inference Providers 라우팅을 사용하며, Key는 hf.co/settings/tokens에서 만듭니다", "还没有账号": "아직 계정이 없습니다", "这一家当前不支持网页登录,请改用填写凭证": "이 제공자는 현재 웹 로그인을 지원하지 않습니다. 대신 자격 증명 입력을 사용하세요", "这个账号当前没有可自动领的活动(只自动领每日登录赠送积分)": "이 계정에는 현재 자동으로 수령할 수 있는 이벤트가 없습니다 (매일 로그인 보너스 포인트만 자동 수령합니다)", "这份额度走「活动套餐」通道:若转发时提示编码套餐已到期,": "이 할당량은 「이벤트 플랜」 채널을 사용합니다: 포워딩 시 코딩 플랜이 만료되었다는 안내가 나오면, ", "这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。": "여기에는 중국 사이트(monkeycode-ai.com)의 계정을 입력합니다: 중국 사이트의 session을 붙여넣으세요. 국제 사이트(monkeycode-ai.net)는 다른 제공자이며 별도의 계정입니다. 양쪽 자격 증명은 호환되지 않습니다.", "这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。": "여기에는 국제 사이트(monkeycode-ai.net)의 계정을 입력합니다: 국제 사이트의 session을 붙여넣으세요. 중국 사이트(monkeycode-ai.com)는 다른 제공자이며 별도의 계정입니다. 양쪽 자격 증명은 호환되지 않습니다.", "选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "CatPaw 클라이언트의 로그인 상태 파일 auth.json을 선택하세요 (Windows: C:\\Users\\<사용자 이름>\\.meituan-catpaw\\auth.json, macOS / Linux: ~/.meituan-catpaw/auth.json). 가져오기 후에는 자격 증명이 게이트웨이에 저장되어 독립적으로 전달할 수 있습니다 — 더 이상 이 컴퓨터의 클라이언트에 의존하지 않습니다.", "选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。": "Cline 클라이언트의 로그인 상태 파일 providers.json을 선택하세요 (Windows: C:\\Users\\<사용자 이름>\\.cline\\data\\settings\\providers.json, macOS / Linux: ~/.cline/data/settings/providers.json). 가져오기 후에는 자격 증명이 게이트웨이에 저장되어 독립적으로 전달할 수 있습니다 — 더 이상 이 컴퓨터의 클라이언트에 의존하지 않습니다. 이 항목은 {label}에 등록됩니다.", "选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。": "KukuAI 클라이언트의 Cookies 파일을 선택하세요 (%APPDATA%\\baidugenflowpro\\Network\\Cookies): **바이너리** SQLite 데이터베이스입니다 (확장자 없음, JSON 아님). 가져오기는 선택한 파일의 복사본을 읽어 실행 중인 클라이언트와 점유를 다투지 않으므로 **클라이언트를 먼저 종료할 필요가 없습니다**. 가져오기 후에는 자격 증명이 게이트웨이에 저장되어 독립적으로 전달할 수 있습니다.", "选择凭证文件导入": "자격 증명 파일에서 가져오기", "选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。": "공식 클라이언트의 로그인 자격 증명 파일(workbuddy-desktop.info / workbuddy-desktop-ai.info) 또는 wb2api에서 내보낸 계정 JSON(중첩/평면 형식 모두 가능)을 선택하세요.", "选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "小浣熊 클라이언트의 로그인 상태 파일 auth.json을 선택하세요 (Windows: C:\\Users\\<사용자 이름>\\.box-agent\\config\\auth.json, macOS / Linux: ~/.box-agent/config/auth.json). 가져오기 후에는 자격 증명이 게이트웨이에 저장되어 독립적으로 전달할 수 있습니다 — 더 이상 이 컴퓨터의 클라이언트에 의존하지 않습니다.", "选择提供商": "제공자 선택", "选择的文件不是合法 JSON": "선택한 파일이 유효한 JSON이 아닙니다", "选择要添加账号的提供商": "계정을 추가할 제공자 선택", "重新扫描": "다시 스캔", "鉴权": "인증", "阿里云 Token Plan 套餐(国际站)": "Alibaba Cloud Token Plan (국제 사이트)", "阿里云百炼 Coding 套餐(国内站)": "Alibaba Cloud Model Studio Coding 플랜 (중국 사이트)", "阿里云百炼 Coding 套餐(国际站)": "Alibaba Cloud Model Studio Coding 플랜 (국제 사이트)", "阿里云百炼模型服务(国际站,OpenAI 兼容模式)": "Alibaba Cloud Model Studio 모델 서비스 (국제 사이트, OpenAI 호환 모드)", "需要图形验证码:登录窗口已弹出,请填写后继续": "그래픽 캡차가 필요합니다: 로그인 창이 열렸으니 입력한 후 계속하세요", "预置 API": "프리셋 API", "领取 CodeArts 每日福利": "CodeArts 일일 혜택 수령", "领取失败:{message}": "수령 실패: {message}", "领取并让官方确认": "수령하고 공식 확인 받기", "风控验证配置不完整,请稍后重试": "리스크 관리 인증 구성이 불완전합니다. 잠시 후 다시 시도하세요", "验证码": "인증 코드", "验证码已发送。收到后填入下方并点「登录并添加」": "인증 코드를 전송했습니다. 받은 후 아래에 입력하고 「로그인 후 추가」를 클릭하세요", "验证码已发送,请查看手机短信": "인증 코드를 전송했습니다. 휴대폰 SMS를 확인하세요", "验证码已发送,请查看手机短信。填入下方并点「登录并添加」即完成登录": "인증 코드를 전송했습니다. 휴대폰 SMS를 확인하세요. 아래에 입력하고 「로그인 후 추가」를 클릭하면 로그인이 완료됩니다", "验证码已发送,请查看短信": "인증 코드를 전송했습니다. 문자 메시지를 확인하세요", "验证码校验失败,请重试": "인증 코드 확인에 실패했습니다. 다시 시도하세요", "验证码校验超时,请重试": "인증 코드 확인 시간이 초과되었습니다. 다시 시도하세요", "验证码组件不可用,请重试": "캡차 컴포넌트를 사용할 수 없습니다. 다시 시도하세요", "验证码组件初始化超时,请重试": "캡차 컴포넌트 초기화 시간이 초과되었습니다. 다시 시도하세요", "验证码组件加载失败,请检查网络后重试": "캡차 컴포넌트 로드에 실패했습니다. 네트워크를 확인한 후 다시 시도하세요", "验证码组件加载异常,请重试": "캡차 컴포넌트 로드 중 오류가 발생했습니다. 다시 시도하세요", "验证码组件加载超时,请检查网络后重试": "캡차 컴포넌트 로드 시간이 초과되었습니다. 네트워크를 확인한 후 다시 시도하세요", "验证码组件未加载,请重启应用后重试": "캡차 컴포넌트가 로드되지 않았습니다. 앱을 다시 시작한 후 다시 시도하세요", "(免费额度池,模型名带 cline-free/ 前缀)。": " (무료 할당량 풀, 모델 이름에 cline-free/ 접두사 포함).", "(其中 {n} 把恢复为不限制)": " (이 중 {n}개는 제한 없음으로 복원)", "(必填)": " (필수)", "(有效期 {window})": "(유효 기간 {window})", "(订阅池,模型名带 cline-pass/ 前缀,需要账号有对应订阅)。": " (구독 풀, 모델 이름에 cline-pass/ 접두사 포함, 계정에 해당 구독이 있어야 합니다).", ",下一次最快 {minutes} 分钟后": ", 다음 시도는 최소 {minutes}분 후", ",并从 {n} 把 Key 的可用提供商里移除": ", Key {n}개의 사용 가능한 제공자 목록에서 이 제공자를 제거했습니다" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · 模型 {head}{rest}": " · 모델 {head}{rest}", " 等 {n} 个": " 등 {n}개", "1// 开头的 Google refresh token(也可直接粘贴 refresh_token=… 整行)": "1//로 시작하는 Google refresh token (refresh_token=… 줄 전체를 붙여넣어도 됨)", "11 位大陆手机号": "11자리 중국 본토 휴대폰 번호", "1~64 个字符,账号列表里按它分组显示": "1~64자, 계정 목록에서 이 값을 기준으로 그룹화되어 표시됩니다", "6 位数字": "6자리 숫자", "

  • 没有可自动领的活动
  • ": "
  • 자동으로 수령할 수 있는 이벤트가 없습니다
  • ", "

    领到的是套餐赠送积分,不会增加福利模型的 token 池;": "

    수령하는 것은 플랜 증정 포인트이며, 혜택 모델의 token 풀은 늘지 않습니다;", "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "Anthropic 호환 엔드포인트는 https://api.deepseek.com/anthropic입니다 (프로토콜을 anthropic으로 바꿀 때 입력합니다)", "Anthropic 协议填根地址,不要带 /v1": "Anthropic 프로토콜은 루트 주소를 입력하고 /v1은 붙이지 마세요", "Antigravity": "Antigravity", "AtomCode": "AtomCode", "AutoClaw 国内版": "AutoClaw 중국판", "AutoClaw 国际版": "AutoClaw 국제판", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS는 필수입니다; **전체 Cookie**를 붙여넣으세요 (Cookie 편집기에서 원클릭 내보내기 가능) — 이 중 **PTOKEN은 반드시 남겨 두세요**. 게이트웨이가 이것을 사용해 Baidu에서 KukuAI 비즈니스 세션 토큰을 교환 발급받으며, 없으면 모델 갱신 시 「미로그인」 오류가 발생합니다. 전체 Cookie 헤더, Cookie 편집기에서 내보낸 JSON 배열, 또는 {BDUSS, STOKEN} 객체를 지원합니다. KukuAI에는 갱신 메커니즘이 없으므로 로그인 상태가 만료되면 다시 붙여넣으면 됩니다.", "BDUSS=…; STOKEN=…(或 Cookie 编辑器导出的 JSON)": "BDUSS=…; STOKEN=… (또는 Cookie 편집기에서 내보낸 JSON)", "CatPaw 的 X-Passport-Token(登录态 Cookie)": "CatPaw의 X-Passport-Token (로그인 상태 Cookie)", "Cline Pass 的 API Key 在 app.cline.bot 获取(订阅制);端点为 OpenAI 兼容,模型清单以 Cline 控制台为准,模型名在模型管理里按裸名添加(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客户端形态校验,所需的客户端请求头已随卡预置": "Cline Pass의 API Key는 app.cline.bot에서 발급받습니다(구독제). 엔드포인트는 OpenAI 호환이며, 모델 목록은 Cline 콘솔을 기준으로 하고, 모델 이름은 모델 관리에서 접두사 없는 이름 그대로 추가합니다(예: glm-5.2 / kimi-k2.7-code / deepseek-v4-pro). 업스트림이 클라이언트 형태를 검증하므로 필요한 클라이언트 요청 헤더는 이 카드에 미리 설정되어 있습니다", "Cloud-IDE-JWT(三段点分)": "Cloud-IDE-JWT (3단 점 구분)", "Command Code": "Command Code", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT(점 3개 구분. Bearer 전체 줄 또는 data.bin 내용을 붙여넣어도 됨)", "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT(점 3개로 구분된 형식. chatglm_token=… 줄을 그대로 붙여도 됩니다)", "LobsterAI": "LobsterAI", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode 국제판", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "NVIDIA NIM 관리형 엔드포인트, Key는 integrate.api.nvidia.com에서 신청합니다", "OfficeAce 果办": "OfficeAce", "accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)": "accessToken은 Accio의 로그인 자격 증명입니다 (길고 불투명한 token 문자열이며 JWT가 아닙니다); refreshToken은 선택이며, 입력하면 만료 시 자동 갱신됩니다. {label} 계정의 자격 증명을 입력하세요 — {siteNote} (가장 쉽게 얻는 방법: 위의 「웹 로그인」을 바로 사용하세요. token을 직접 찾을 필요가 없습니다.)", "accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
    手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
    没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。": "accessToken은 Trae의 Cloud-IDE-JWT이며(3단 점 구분), refreshToken은 만료 시 자동 갱신에 사용됩니다 (이 제공자는 refreshToken이 교환할 때마다 바뀝니다: 한 번 교환하면 이전 것은 무효가 되므로 두 프로그램에서 같은 계정을 동시에 갱신하지 마세요).
    수동으로 붙여넣을 때는 machineId / deviceId도 함께 입력하세요: 업스트림은 이를 로그인 시 업로드한 기기 공개 키와 묶어 기기를 판별하므로, 갑자기 새로운 값을 넣으면 2xxxx 계열 기기 바인딩 거부가 발생합니다.
    이 세 가지가 없을 때의 올바른 방법은 위의 「웹 로그인」이며, 컨테이너 / 원격 배포 환경에서는 안내에 따라 주소 표시줄의 콜백 주소를 붙여넣으면 됩니다.", "monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)": "monkeycode_ai_session의 값 (전체 Cookie를 붙여넣어도 됨)", "refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。": "refreshToken은 선택이며, 입력하면 만료 시 자동 갱신됩니다; 둘 다 小浣熊 클라이언트 로그인 상태 파일에서 얻을 수 있습니다.", "refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。": "refreshToken은 선택이며, 입력하면 자동 갱신됩니다; 둘 다 클라이언트 로그인 상태 파일에서 얻을 수 있습니다. 같은 Cline 계정은 두 풀 모두에서 사용할 수 있으며, 여기서는 {label}로 분류됩니다.", "token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。": "token은 enc: 접두사를 지원합니다 (Windows에서는 자동 복호화). refreshToken을 입력하지 않으면 자동 갱신할 수 없습니다.", "token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。": "token은 로그인 상태 Cookie입니다; CatPaw에는 갱신 메커니즘이 없어 만료 후 클라이언트에서 다시 로그인해야 합니다.", "user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)": "user_로 시작하는 API Key (Authorization: Bearer … 줄 전체를 붙여넣어도 됨)", "{label} 网页登录的打开方式": "{label} 웹 로그인을 여는 방식", "{label} 账号地区": "{label} 계정 지역", "{label} 账号的添加方式": "{label} 계정 추가 방식", "{name} 不是合法 JSON:{reason}": "{name}은(는) 올바른 JSON이 아닙니다: {reason}", "{name}要粘一个 JSON 对象(形如 {sample})": "{name}에는 JSON 객체를 붙여넣어야 합니다 (예: {sample})", "{n} 个账号": "계정 {n}개", "{n} 个账号,点击添加": "계정 {n}개, 클릭하여 추가", "{seconds}s 后重发": "{seconds}초 후 재발송", "✅ {label}账号已添加": "✅ {label} 계정을 추가했습니다", "✅ {label}账号已添加{name}": "✅ {label} 계정을 추가했습니다{name}", "✅ 已从 cc-switch 导入 {n} 个供应商": "✅ cc-switch에서 제공자 {n}개를 가져왔습니다", "✅ 已创建自定义提供商「{name}」并添加账号": "✅ 커스텀 제공자 「{name}」을(를) 만들고 계정을 추가했습니다", "✅ 已删除自定义提供商「{name}」{accounts}{keys}": "✅ 커스텀 제공자 「{name}」을(를) 삭제했습니다{accounts}{keys}", "✅ 已更新已有账号{name}(同一把 API Key 只保留一条,重复添加会合并)": "✅ 기존 계정{name}을(를) 업데이트했습니다(동일한 API Key는 하나만 유지되며 다시 추가하면 병합됩니다)", "✅ 登录成功,{edition}账号已加入列表": "✅ 로그인 성공, {edition} 계정을 목록에 추가했습니다", "✅ 领取成功:{planId}{period}": "✅ 수령 성공: {planId}{period}", " 同一家可以放多把 key,按优先级轮换。": " 같은 제공자에 key를 여러 개 둘 수 있으며, 우선순위에 따라 로테이션됩니다.", "「{name}」的账号添加功能还在开发中,敬请期待。": "「{name}」의 계정 추가 기능은 아직 개발 중입니다. 기대해 주세요.", "一个 Key 用遍多家模型,模型名用「厂商/模型」全称(如 deepseek/deepseek-chat)": "하나의 Key로 여러 제공자의 모델을 사용할 수 있으며, 모델 이름은 「벤더/모델」 전체 이름을 씁니다 (예: deepseek/deepseek-chat)", "上游不要鉴权时留空,并勾选下面一项(留空又不勾会被当成未配置凭证)": "업스트림이 인증을 요구하지 않으면 비워 두고 아래 항목을 체크하세요 (비워 두고 체크하지 않으면 자격 증명 미설정으로 간주됩니다)", "上游信息": "업스트림 정보", "不可领({status})": "수령 불가 ({status})", "不支持导入": "가져오기 미지원", "不是有效的凭证文件:内容要是一个 JSON 对象": "유효한 자격 증명 파일이 아닙니다. 내용은 JSON 객체여야 합니다", "不是有效的凭证文件:需要包含 accessToken 与 uid": "유효한 자격 증명 파일이 아닙니다. accessToken과 uid가 필요합니다", "不是自定义提供商": "커스텀 제공자가 아닙니다", "不需要 Key:转发与拉取模型都不发送鉴权头": "Key 불필요: 포워딩과 모델 조회 모두 인증 헤더를 보내지 않습니다", "不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

    注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。": "로그인이 필요 없습니다. 아래 버튼을 누르면 게이트웨이가 智谱清言에서 게스트 자격 증명을 직접 발급받아 계정 목록에 추가합니다(먼저 메모 이름을 입력해도 됩니다). 게스트 자격 증명은 바로 대화에 쓸 수 있고, 무료 할당량은 업스트림이 기기 단위로 발급합니다.

    주의: 게스트 계정에는 포인트 체계가 없습니다. 회원 포인트, 매일 로그인 포인트, 활동 수령 관련 인터페이스는 모두 로그인이 필요하므로 이러한 기능은 게스트에서 건너뜁니다(계정 페이지의 포인트와 签到 센터에 수치가 표시되지 않습니다). 포인트가 필요하면 \"자격 증명 입력\"으로 로그인된 계정 토큰을 붙여넣거나 \"데스크톱 로그인 상태 가져오기\"를 사용하세요.", "两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。": "두 판의 계정을 동시에 저장할 수 있으며 각각 별도의 모델 목록을 가집니다 (중국판과 국제판은 서로 다른 두 제공자로, 각각 활성화하고 매핑할 수 있습니다).", "中国版": "중국판", "今天没有需要领的活动(都已到账)": "오늘 수령할 이벤트가 없습니다 (모두 적립됨)", "今天(北京时间 {day})已试 {attempts} 次,还能试 {left} 次{next}
    ": "오늘(베이징 시간 {day}) {attempts}회 시도했고, {left}회 더 시도할 수 있습니다{next}
    ", "从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。": "다른 컴퓨터(또는 OfficeAce 데스크톱 앱의 models.json)에서 다음 세 항목을 가져와 붙여넣으세요: 모델 게이트웨이 기본 주소(baseUrl, https://modelgw-…/v2 형태)와 modelAppKey / modelAppSecret. 이 세 항목만 있으면 전달할 수 있습니다(게이트웨이 자격 증명은 만료되지 않음). 제어 영역 임시 자격 증명(accessKeyId 등)은 선택이며, 비워 두면 잔액 조회와 자동 갱신을 쓸 수 없지만 전달에는 영향이 없습니다.", "从本机导入桌面端登录态": "이 컴퓨터에서 데스크톱 로그인 상태 가져오기", "任务镜像 id": "작업 이미지 id", "余额查询": "잔액 조회", "使用 Google 账号登录": "Google 계정으로 로그인", "使用 Zai 账号登录": "Zai 계정으로 로그인", "使用个人访问令牌(PAT)": "개인 액세스 토큰(PAT) 사용", "免费模型不用填 Key(默认勾上「该上游无需鉴权」,网关自动用匿名凭证);付费模型则填 Zen 的 API Key。免费档上游只认官方客户端形态,已默认开启「伪装 OpenCode 官方客户端」": "무료 모델은 Key를 입력하지 않아도 됩니다 (기본적으로 「이 업스트림은 인증이 필요하지 않음」이 체크되어 있고, 게이트웨이가 자동으로 익명 자격 증명을 사용합니다); 유료 모델은 Zen의 API Key를 입력하세요. 무료 등급 업스트림은 공식 클라이언트 형태만 허용하므로 「OpenCode 공식 클라이언트로 위장」이 기본으로 켜져 있습니다", "内嵌窗口": "내장 창", "内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)": "내장 창으로 엽니다; 창을 닫으면 대기가 취소됩니다 (Google 계정이 거부되면 시스템 브라우저를 사용하세요)", "内嵌窗口打开;完成后自动加入列表,关窗即取消等待": "내장 창으로 엽니다; 완료하면 자동으로 목록에 추가되고, 창을 닫으면 대기가 취소됩니다", "内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待": "내장 창으로 열립니다. 스캔이 끝나면 자동으로 목록에 추가되며, 창을 닫으면 대기가 취소됩니다", "内嵌窗口用全新环境,多账号互不影响;关窗即取消等待": "내장 창은 새 환경을 사용하므로 여러 계정이 서로 영향을 주지 않습니다; 창을 닫으면 대기가 취소됩니다", "内嵌窗口(推荐)": "내장 창(권장)", "准备验证…": "인증 준비 중…", "凭据 JSON": "자격 증명 JSON", "凭证文件在哪?": "자격 증명 파일 위치는?", "创建并添加账号": "생성하고 계정 추가", "创建这个提供商,并同时建立它的第一个账号。": "이 제공자를 만들고 동시에 첫 계정을 생성합니다.", "删除功能不可用(脚本未就绪)": "삭제 기능을 사용할 수 없습니다 (스크립트가 준비되지 않음)", "删除失败:{reason}": "삭제 실패: {reason}", "删除此提供商": "이 제공자 삭제", "及 {n} 个账号": " 및 계정 {n}개", "发送中…": "전송 중…", "发送失败:{message}": "전송 실패: {message}", "发送未确认:登录窗口已弹出,请在弹出的窗口中手动完成登录": "전송 미확인: 로그인 창이 열렸습니다. 열린 창에서 직접 로그인을 완료하세요", "取消失败:{message}": "취소 실패: {message}", "取消等待": "대기 취소", "只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页": "「내장 창」에서만 제3자 로그인 항목을 복원할 수 있습니다: 시스템 브라우저에서는 로그인 페이지를 수정할 수 없습니다", "可选": "선택", "可选(cloudaicompanionProject):留空由网关自动发现": "선택 (cloudaicompanionProject): 비워 두면 게이트웨이가 자동으로 찾습니다", "可选,展示用": "선택, 표시용", "可选,没有则无法自动续期": "선택, 없으면 자동 갱신 불가", "可选,用于生成账号标识": "선택 사항이며 계정 식별자 생성에 사용", "可选,留空使用凭据里的 user_name": "선택, 비워 두면 자격 증명의 user_name 사용", "可选,留空使用账号姓名、邮箱或令牌指纹": "선택, 비워 두면 계정 이름, 이메일 또는 토큰 지문 사용", "可选,留空使用账号昵称或邮箱": "선택, 비워 두면 계정 닉네임 또는 이메일 사용", "可选,留空则用 key 尾号": "선택, 비워 두면 key 끝자리 사용", "可选,留空则用 userId": "선택, 비워 두면 userId 사용", "可选,留空则用上游返回的账号名或账号 id": "선택, 비워 두면 업스트림이 반환한 계정 이름 또는 계정 id 사용", "可选,留空则用凭证里的账号名": "선택, 비워 두면 자격 증명의 계정 이름 사용", "可选,留空则用文件里的昵称": "선택 사항, 비워 두면 파일의 닉네임 사용", "可选,留空则用登录名或 uid": "선택, 비워 두면 로그인 이름 또는 uid 사용", "可选,留空则用脱敏手机号": "선택, 비워 두면 마스킹된 휴대폰 번호 사용", "可选,留空则用脱敏手机号或 userId": "선택, 비워 두면 마스킹된 휴대폰 번호 또는 userId 사용", "可选,留空则用账号 uk": "선택, 비워 두면 계정 uk 사용", "可选,留空则用账号昵称": "선택 사항. 비워 두면 계정 닉네임을 사용합니다", "可选,留空则用账号昵称或 userId": "선택 사항. 비워 두면 계정 닉네임 또는 userId 사용", "可选,留空则用邮箱或令牌尾号": "선택, 비워 두면 이메일 또는 토큰 끝자리 사용", "可选,留空用「AtomCode」": "선택 사항이며 비워 두면 “AtomCode”", "可选,留空用「OfficeAce 果办」": "선택, 비워 두면 「OfficeAce」 사용", "可选,留空自动生成": "선택, 비워 두면 자동 생성", "可选,留空自动用昵称": "선택, 비워 두면 닉네임 자동 사용", "可选,给这个访客账号起个备注名": "선택 사항. 이 게스트 계정의 메모 이름", "可选,续期时带上": "선택, 갱신 시 함께 전송", "可选,讯飞侧 userid": "선택, iFlytek 측 userid", "可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补": "선택; 대화 시작에 필요합니다. 비워 두면 게이트웨이가 이 계정의 기존 작업에서 자동으로 찾습니다. 찾지 못하면 나중에 직접 입력하면 됩니다.", "可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)": "선택; 계정 이메일로, 표시와 신원 식별에 사용됩니다 (새 토큰으로 바꿔도 같은 계정으로 인식됩니다)", "可选;UUID 形态,与凭据同生共死": "선택; UUID 형식이며 자격 증명과 함께 생성되고 함께 사라집니다", "可选;填了才能到期自动续期": "선택; 입력해야 만료 시 자동 갱신할 수 있습니다", "可选;用于去重与展示名(留空时网关会问一次上游)": "선택; 중복 제거와 표시 이름에 사용됩니다 (비워 두면 게이트웨이가 업스트림에 한 번 물어봅니다)", "可选;用于生成账号 id 与展示名": "선택; 계정 id와 표시 이름 생성에 사용됩니다", "可选;纯数字串(上游只收 8–24 位数字)": "선택; 숫자만 입력 (업스트림은 8–24자리 숫자만 허용)", "可领取": "수령 가능", "同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)": "한 제공사에 API Key 있는 계정과 인증 불필요 계정을 섞어 둘 수 있으며 각각의 규칙으로 라우팅됩니다. 인증 불필요 계정도 여러 개 둘 수 있고 각각 다른 아웃바운드 프록시를 설정할 수 있습니다", "同地址换 anthropic 协议即走它的 Claude 兼容端点。转发必须填 OpenCode Go 的 API Key(订阅制;获取模型清单不需要 Key,但发请求上游会回 401「Missing API key」)": "같은 주소에서 anthropic 프로토콜로 바꾸면 해당 Claude 호환 엔드포인트를 사용합니다. 포워딩에는 OpenCode Go의 API Key가 반드시 필요합니다 (구독제; 모델 목록 조회에는 Key가 필요 없지만, 요청을 보내면 업스트림이 401 「Missing API key」를 반환합니다)", "响应为空": "응답이 비어 있습니다", "商汤日日新的 OpenAI 兼容端点;余额查询走控制台账号密码(见下方「余额查询」一段)": "SenseNova 의 OpenAI 호환 엔드포인트. 잔액 조회는 콘솔 계정 / 비밀번호를 사용합니다 (아래 \"잔액 조회\" 단락 참조)", "国内版": "중국판", "国内版与**国际版是两套独立的账号与套餐**,凭证与领取的套餐都不通用。": "중국판과 **국제판은 서로 독립된 두 세트의 계정과 플랜**입니다. 자격 증명과 수령한 플랜은 서로 호환되지 않습니다.", "国内版登录页没有 Google / GitHub 入口(它用微信 / 手机号 / 邮箱登录)": "중국판 로그인 페이지에는 Google / GitHub 항목이 없습니다 (WeChat / 휴대폰 번호 / 이메일로 로그인합니다)", "国内版的登录站点是 www.accio-ai.com,与国际版不是同一站。": "중국판의 로그인 사이트는 www.accio-ai.com이며, 국제판과 같은 사이트가 아닙니다.", "国内版(WorkBuddy)": "중국판 (WorkBuddy)", "国内站为 https://api.siliconflow.cn/v1": "중국 사이트는 https://api.siliconflow.cn/v1입니다", "国内站(minimaxi.com);OpenAI 兼容地址为 https://api.minimaxi.com/v1": "중국 사이트 (minimaxi.com); OpenAI 호환 주소는 https://api.minimaxi.com/v1입니다", "国际版": "국제판", "国际版 z.ai 的 Coding 套餐;OpenAI 兼容地址为 https://api.z.ai/api/coding/paas/v4": "국제판 z.ai의 Coding 플랜; OpenAI 호환 주소는 https://api.z.ai/api/coding/paas/v4입니다", "国际版与国内版是**两套独立的账号**(同一账号体系的两个站点),凭证不通用。": "국제판과 중국판은 **서로 독립된 두 세트의 계정**입니다 (같은 계정 체계의 두 사이트). 자격 증명은 호환되지 않습니다.", "国际版与国内版账号体系独立,请填国际版的凭证。未填 refreshToken 无法自动续期。": "국제판과 중국판은 계정 체계가 독립적입니다. 국제판의 자격 증명을 입력하세요. refreshToken을 입력하지 않으면 자동 갱신할 수 없습니다.", "国际版登录页默认只显示邮箱登录,勾选后恢复 Google / GitHub / X 入口": "국제판 로그인 페이지는 기본적으로 이메일 로그인만 표시하며, 체크하면 Google / GitHub / X 항목이 복원됩니다", "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "국제판의 추론 사이트는 api.z.ai이며 중국판과 다른 사이트입니다; 플랜도 각각 독립적입니다.", "国际版(WorkBuddy AI)": "국제판 (WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "국제 사이트 Coding 플랜; OpenAI 호환 주소는 https://api.minimax.io/v1입니다", "在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。": "AtomGit의 AtomCode 페이지에서 accessToken과 refreshToken 두 값을 가져와 붙여넣으세요. 리프레시 토큰 입력을 권장합니다 —— 이 제공자의 토큰은 JWT가 아니며(약 7일 후 만료되고 만료 시각을 읽을 수 없습니다), 없으면 자동 갱신이 불가능해 만료 후 다시 로그인해야 합니다.", "在 cc-switch 里找到": "cc-switch에서 찾은", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "내장 창에서 공식 로그인을 완료하면 성공 시 자동으로 계정 목록에 추가됩니다.", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "{label}의 Qoder 계정 설정 → Integrations에서 PAT를 생성하세요 (Google / GitHub 토큰을 입력하지 마세요). 두 지역의 계정과 자격 증명은 호환되지 않습니다.", "在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。": "열린 공식 인증 페이지에서 위챗 QR 코드를 스캔하여 로그인하세요(페이지에 QR 코드가 바로 표시됩니다). 스캔에 성공하면 계정이 자동으로 목록에 추가됩니다.", "在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "열린 창에서 Baidu 계정으로 KukuAI 메인 사이트에 로그인하세요 (SMS 인증 코드 / QR 스캔 모두 가능). 로그인에 성공하면 자동으로 계정 목록에 추가됩니다. 게이트웨이는 Baidu에서 KukuAI 비즈니스 세션 토큰을 교환 발급받습니다 (이 컴퓨터에 KukuAI 클라이언트가 설치되어 있어야 합니다).", "在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "열린 창에서 Google 계정으로 로그인하세요(Antigravity의 인가 페이지). 인가가 완료되면 자동으로 계정 목록에 추가됩니다.", "在浏览器中打开登录页": "브라우저에서 로그인 페이지 열기", "地区": "지역", "填写凭证": "자격 증명 입력", "填写凭证添加": "자격 증명 입력 후 추가", "备注名": "라벨명", "套餐": "플랜", "如:智谱 GLM": "예: Zhipu GLM", "完成登录后自动加入账号列表,昵称取自上游。": "로그인을 완료하면 자동으로 계정 목록에 추가되며, 닉네임은 업스트림에서 가져옵니다.", "官方客户端凭证目录:Windows 是 C:\\Users\\<你的用户名>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth,macOS 是 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth": "공식 클라이언트 자격 증명 디렉터리: Windows는 C:\\Users\\<사용자명>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth, macOS는 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth", "官方已确认到账": "공식 적립 확인 완료", "导入": "가져오기", "导入中…": "가져오는 중…", "导入凭证文件": "자격 증명 파일 가져오기", "导入失败": "가져오기 실패", "导入失败:{reason}": "가져오기 실패: {reason}", "导入所选": "선택 항목 가져오기", "导入所选({n})": "선택 항목 가져오기({n})", "导入桌面端登录态": "데스크톱 로그인 상태 가져오기", "将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下": "내장 창이 열립니다; 인가가 완료되면 자동으로 계정 목록에 추가됩니다. 창을 닫으면 대기가 취소됩니다. 인가 페이지 마지막에 zcode:// 링크를 열 수 없다는 안내가 나올 수 있는데, 정상입니다 — 결과는 이미 서버 측에 기록되었습니다", "将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)": "내장 창이 열립니다; 인가가 완료되면 자동으로 계정 목록에 추가됩니다. 원격 패널에서는 콜백 주소를 붙여넣을 수 있습니다. 창을 닫으면 대기가 취소됩니다. 링크는 5분간 유효하며, 시간이 초과되거나 클릭하지 않으면 무효가 됩니다 (다시 시작할 수 있습니다)", "将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待": "내장 창이 열립니다; 로그인이 완료되면 자동으로 계정 목록에 추가됩니다. 창을 닫으면 대기가 취소됩니다", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 Trae 账号);同机完成后自动加入,Docker / 远程面板按提示粘贴地址栏回调地址。": "시스템 기본 브라우저로 인가 페이지를 엽니다 (브라우저에 로그인된 Trae 계정을 재사용합니다); 같은 컴퓨터에서는 완료 시 자동으로 추가되며, Docker / 원격 패널에서는 안내에 따라 주소 표시줄의 콜백 주소를 붙여넣으세요.", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 ZCode 账号);授权完成后自动加入账号列表。页面最后可能提示无法打开 zcode:// 链接,属正常现象": "시스템 기본 브라우저로 인가 페이지를 엽니다 (브라우저에 로그인된 ZCode 계정을 재사용합니다); 인가가 완료되면 자동으로 계정 목록에 추가됩니다. 페이지 마지막에 zcode:// 링크를 열 수 없다는 안내가 나올 수 있는데 정상입니다", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "시스템 기본 브라우저로 인가 페이지를 엽니다 (브라우저에 로그인된 Huawei Cloud 계정을 재사용합니다); 인가 코드는 게이트웨이가 클라우드를 폴링해 가져오므로 브라우저가 어느 컴퓨터에서 실행되는지는 상관없습니다. 완료하면 자동으로 계정 목록에 추가됩니다", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "시스템 기본 브라우저로 인가 페이지를 엽니다 (브라우저에 로그인된 Huawei Cloud 계정을 재사용합니다); 브라우저와 게이트웨이가 같은 컴퓨터에 있지 않다면 위 설명대로 최종 콜백 주소를 패널에 다시 붙여넣으세요", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "시스템 기본 브라우저로 로그인 페이지를 엽니다 (브라우저에 로그인된 Accio 계정을 재사용합니다); 로그인을 완료하면 자동으로 계정 목록에 추가되며, 팝업을 닫으면 대기가 취소됩니다", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "시스템 기본 브라우저로 로그인 페이지를 엽니다 (브라우저에 로그인된 LobsterAI 계정을 재사용합니다); 로그인을 완료하면 자동으로 계정 목록에 추가되며, 팝업을 닫으면 대기가 취소됩니다", "小米 MiMo Token Plan 套餐(新加坡站)": "小米 MiMo Token Plan 플랜 (싱가포르 사이트)", "已到账": "적립 완료", "已取消登录等待": "로그인 대기를 취소했습니다", "已取消领取": "수령을 취소했습니다", "已取消验证码": "인증 코드를 취소했습니다", "已填写手机号,正在发送验证码…": "휴대폰 번호 입력 완료, 인증 코드 전송 중…", "已声明无需鉴权:账号会被正常选路,出网时不带任何鉴权头": "인증 필요 없음으로 선언됨: 계정이 정상적으로 라우팅되며, 외부로 나갈 때 인증 헤더를 전혀 보내지 않습니다", "已导入 {ok} 个,失败 {fail} 个(失败项已标注在列表里)": "가져오기 성공 {ok}개, 실패 {fail}개 (실패 항목은 목록에 표시해 두었습니다)", "已建过 · {n} 个账号,点击继续添加": "이미 생성됨 · 계정 {n}개, 클릭하여 추가", "已建过,点击继续添加账号": "이미 생성됨, 클릭하여 계정 추가", "已打开官方登录页,请在窗口中完成登录…": "공식 로그인 페이지를 열었습니다. 창에서 로그인을 완료하세요…", "已点发送,等待短信…": "전송을 눌렀습니다. SMS 대기 중…", "已用系统默认浏览器打开登录页,请在浏览器中完成登录…": "시스템 기본 브라우저로 로그인 페이지를 열었습니다. 브라우저에서 로그인을 완료하세요…", "已领取,等官方确认": "수령 완료, 공식 확인 대기 중", "当前壳版本不支持 AutoClaw 网页登录,请更新应用": "현재 앱 버전에서는 AutoClaw 웹 로그인을 지원하지 않습니다. 앱을 업데이트하세요", "当前没有可领取的套餐(活动尚未开始)": "현재 수령 가능한 플랜이 없습니다 (이벤트가 아직 시작되지 않았습니다)", "当前环境不支持领取(桥接方法缺失)": "현재 환경에서는 수령을 지원하지 않습니다 (브리지 메서드 없음)", "必填,CatPaw 账号标识": "필수, CatPaw 계정 식별자", "恢复 Google / GitHub 入口": "Google / GitHub 항목 복원", "手机号": "휴대폰 번호", "手机验证码登录": "SMS 인증 코드 로그인", "打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。": "공식 LobsterAI 로그인 페이지(lobsterai.youdao.com)를 열고 LobsterAI 계정으로 로그인하세요: 로그인이 완료되면 공식 페이지가 이 컴퓨터로 되돌아오고 계정이 자동으로 목록에 추가됩니다 —— 콜백은 게이트웨이 자체 포트로 들어오므로 브라우저는 게이트웨이와 같은 컴퓨터에서 열어야 합니다. 인증 코드는 이 컴퓨터에서만 게이트웨이로 전달되며 화면에 평문 토큰은 표시되지 않습니다.", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Trae SOLO의 공식 인가 페이지(trae.cn)를 열고 Trae 계정으로 로그인하세요: 인가가 완료되면 공식 페이지가 이 컴퓨터의 임시 포트로 되돌아옵니다; 같은 컴퓨터 배포에서는 자동으로 수신하며, Docker / 원격 패널에서는 안내에 따라 주소 표시줄의 콜백 주소를 복사하세요.", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Accio {label}의 공식 로그인 페이지({site})를 열고 Accio 계정으로 로그인하세요: 로그인에 성공하면 공식 페이지가 이 컴퓨터로 되돌아옵니다; 같은 컴퓨터에서는 자동으로 완료되며, Docker / 원격 패널에서는 안내에 따라 콜백 주소를 붙여넣으면 됩니다 (인가 코드는 이 컴퓨터에서만 게이트웨이에 전달되며, 화면에 평문 token이 표시되지 않습니다).", "打开 Accio {label}登录页": "Accio {label} 로그인 페이지 열기", "打开 Antigravity 网页登录": "Antigravity 웹 로그인 열기", "打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。": "AtomGit 승인 페이지를 열고 로그인하세요. 완료되면 게이트웨이가 자동으로 브로커를 폴링해 토큰을 가져오고 계정을 추가합니다(콜백은 브로커에서 종료되며 이 기기로 돌아오지 않으므로 콜백 URL을 직접 입력할 필요가 없습니다).", "打开 CatPaw 网页登录": "CatPaw 웹 로그인 열기", "打开 Cline 授权页": "Cline 인가 페이지 열기", "打开 CodeArts 授权页": "CodeArts 인가 페이지 열기", "打开 LobsterAI 登录页": "LobsterAI 로그인 페이지 열기", "打开 Qoder 网页登录": "Qoder 웹 로그인 열기", "打开 Trae 授权页": "Trae 인가 페이지 열기", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "ZCode {label}의 공식 인가 페이지를 열고 계정으로 로그인하세요. 인가 결과는 ZCode 서버에 기록되며, 게이트웨이가 백그라운드에서 자격 증명을 자동으로 가져와 계정 목록에 추가합니다.", "打开 ZCode {label}授权页": "ZCode {label} 인가 페이지 열기", "打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
    网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。": "Huawei Cloud CodeArts의 공식 인가 페이지를 열어 로그인하세요: 로그인이 완료되면 공식 페이지가 브라우저를 게이트웨이 자체의 /oauth/callback으로 되돌립니다. 게이트웨이는 일회성 인가 코드를 임시 자격 증명으로 교환하고 계정 목록에 추가합니다.
    게이트웨이가 다른 컴퓨터에서 실행 중이면 웹 UI에서 주소 표시줄의 최종 콜백 주소를 패널에 그대로 붙여넣으라는 안내가 표시됩니다.", "打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。": "Huawei Cloud 인가 페이지를 열고 로그인하세요: 완료되면 게이트웨이가 클라우드를 폴링해 인가 코드를 가져와 계정 목록에 추가합니다 (OfficeAce 데스크톱 앱이 필요 없고, 콜백 URL을 수동으로 다시 붙여넣을 필요도 없습니다).", "打开官方授权页完成设备码授权,登录的是{label}({site})的账号。": "공식 인가 페이지를 열어 기기 코드 인가를 완료하세요. 로그인되는 계정은 {label} ({site}) 계정입니다.", "打开授权页": "인가 페이지 열기", "打开授权页完成确认,账号自动加入 {label}{note}": "인가 페이지를 열어 확인을 완료하면 계정이 {label}에 자동으로 추가됩니다{note}", "打开方式": "여는 방식", "打开网页登录": "웹 로그인 열기", "探测失败:{message}": "탐지 실패: {message}", "接入一个兼容上游": "호환 업스트림 연결", "提交中…": "제출 중…", "搜索提供商": "제공자 검색", "搜索提供商…": "제공자 검색…", "整份粘贴官方插件 / CLIProxyAPI 落盘的凭据 JSON(形如 {\"codearts_provider_credential\":{…}},铺平的也行)。
    必填:access_key_id、secret_access_key、security_token。要能自动续期,必须连 refresh_token 与 oauth_context 一起粘 —— 临时凭据约一小时到期,缺这半块就续不回来,只能重新登录。": "공식 플러그인 / CLIProxyAPI가 디스크에 기록한 자격 증명 JSON을 통째로 붙여넣으세요 (형식: {\"codearts_provider_credential\":{…}}, 평탄화된 형태도 가능).
    필수: access_key_id, secret_access_key, security_token.자동 갱신을 원하면 refresh_token과 oauth_context도 함께 붙여넣어야 합니다 — 임시 자격 증명은 약 1시간 후 만료되며, 이 부분이 없으면 갱신할 수 없어 다시 로그인해야 합니다.", "文件是{edition}凭证,已按该版本导入": "파일은 {edition} 자격 증명이며 해당 버전으로 가져왔습니다", "新建自定义提供商": "커스텀 제공자 새로 만들기", "方案:{label}": "방식: {label}", "明文 JWT 或 auth.json 里的 enc: 加密值(自动解密)": "평문 JWT 또는 auth.json의 enc: 암호화 값 (자동 복호화)", "明文 JWT(国际版账号的 access token)": "평문 JWT (국제판 계정의 access token)", "智谱 bigmodel.cn 的 Coding 套餐,Key 在 open.bigmodel.cn/usercenter/apikeys": "Zhipu bigmodel.cn의 Coding 플랜, Key는 open.bigmodel.cn/usercenter/apikeys에서 발급합니다", "未知错误": "알 수 없는 오류", "未能获取授权地址,请重试": "인가 주소를 가져오지 못했습니다. 다시 시도하세요", "未能读取 cc-switch 数据": "cc-switch 데이터를 읽지 못했습니다", "未领取成功": "수령하지 못했습니다", "本地 Ollama、OpenCode Zen 免费档这类上游要勾上,否则账号不可用": "로컬 Ollama, OpenCode Zen 무료 등급 같은 업스트림에는 체크해야 하며, 그렇지 않으면 계정을 사용할 수 없습니다", "本家有两个**互不替代**的凭证,按你要用的功能填,至少填一个:编码套餐 API Key 用于转发推理(打 {site})与查询窗口限额(每 N 小时 / 每周的剩余比例);jwt 用于领取套餐与查询余额(打 zcode.z.ai 的 billing 网关,官方叫 Coding Plan JWT,是一串三段点分的字符串)。余额两把都能查、但读到的东西不同:jwt 读到余额桶与套餐到期,API Key 读到窗口限额;而领取只认 jwt、转发只认 API Key。请填写 {label}账号的凭证 —— {plan}(最容易拿到的办法:直接用上方的「网页登录」—— 它会替你把这个 API Key 换好,两个凭证一起拿到。)": "이 제공자에는 **서로 대체할 수 없는** 자격 증명이 두 개 있습니다. 사용할 기능에 따라 입력하고 최소 하나는 입력하세요: 코딩 플랜 API Key는 추론 포워딩({site} 호출)과 윈도우 할당량 조회(N시간마다 / 주간 잔여 비율)에 사용하고, jwt는 플랜 수령과 잔액 조회에 사용합니다(zcode.z.ai의 billing 게이트웨이 호출, 공식 명칭 Coding Plan JWT, 3단 점 구분 문자열). 잔액은 둘 다 조회할 수 있지만 읽어 오는 내용이 다릅니다: jwt는 잔액 버킷과 플랜 만료를, API Key는 윈도우 할당량을 읽습니다. 수령은 jwt만, 포워딩은 API Key만 인정합니다. {label} 계정의 자격 증명을 입력하세요 — {plan} (가장 쉽게 얻는 방법: 위의 「웹 로그인」을 바로 사용하세요 — 이 API Key를 대신 교환해 두 자격 증명을 함께 받아 줍니다.)", "本机 Ollama 的 OpenAI 兼容端点,无需 API Key(默认已勾上「该上游无需鉴权」,鉴权头一个都不发)": "이 컴퓨터의 Ollama OpenAI 호환 엔드포인트, API Key 불필요 (기본적으로 「이 업스트림은 인증이 필요하지 않음」이 체크되어 있고, 인증 헤더를 전혀 보내지 않습니다)", "本次未确认到账": "이번에는 적립이 확인되지 않았습니다", "条可导入": "건을 가져올 수 있습니다", "条配置,其中": "건의 설정 중", "桌面运行时不可用(Tauri 未初始化)": "데스크톱 런타임을 사용할 수 없습니다 (Tauri 미초기화)", "模型网关基址": "모델 게이트웨이 기본 주소", "正在打开百度登录页…": "Baidu 로그인 페이지를 여는 중…", "正在扫描本机 cc-switch…": "이 컴퓨터의 cc-switch를 스캔하는 중…", "正在换发访客凭证…": "게스트 자격 증명을 발급하는 중…", "正在添加…": "추가하는 중…", "正在等待{name}登录完成;完成或取消后才能发起新的登录": "{name} 로그인 완료를 기다리는 중입니다; 완료하거나 취소한 후에 새로운 로그인을 시작할 수 있습니다", "每周": "매주", "每日": "매일", "每月": "매월", "没有则无法自动续期": "없으면 자동 갱신할 수 없습니다", "没有匹配「{keyword}」的提供商": "「{keyword}」와 일치하는 제공자가 없습니다", "添加 {name} 账号": "{name} 계정 추가", "添加中…": "추가 중…", "添加到 ": "추가 대상: ", "添加失败:{reason}": "추가 실패: {reason}", "添加小浣熊账号": "小浣熊 계정 추가", "添加方式": "추가 방식", "添加访客账号": "게스트 계정 추가", "添加账号": "계정 추가", "火山引擎 Coding 套餐;通用推理接入点为 https://ark.cn-beijing.volces.com/api/v3": "Volcano Engine Coding 플랜; 범용 추론 엔드포인트는 https://ark.cn-beijing.volces.com/api/v3입니다", "点击添加账号": "클릭하여 계정 추가", "点开即预填,填 Key 接入": "열면 값이 미리 채워져 있으며, Key를 입력하면 연결됩니다", "点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
    这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。": "버튼을 누르면 먼저 슬라이더 인증을 한 번 통과하고(공식 리스크 관리), 이어서 로그인 페이지가 열리며 로그인이 완료되면 계정이 자동으로 추가됩니다.
    이것이 국제판의 공식 유일한 로그인 방식입니다; 이미 클라이언트에 로그인한 적이 있다면 「데스크톱 로그인 상태 가져오기」가 더 빠릅니다.", "用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。": "AutoClaw 계정에 연결된 휴대폰 번호로 로그인합니다: 「코드 받기」를 클릭하고 SMS를 받은 뒤 아래에 입력하여 로그인하세요. 인증 코드는 이 컴퓨터에서 공식 API로 직접 제출되며, 화면에 token이 표시되지 않습니다.", "用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。": "CatPaw 계정으로 로그인합니다 (Meituan passport), 성공하면 자동으로 계정 목록에 추가됩니다.", "用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。": "Loomy 계정에 연결된 휴대폰 번호로 로그인합니다: 「코드 받기」를 클릭한 뒤 입력하면 됩니다. 이 제공자에서 가장 간편한 방법입니다.", "用于转发;形如 apiKey.secret 两段点分(不填则这个账号不能转发)": "포워딩에 사용합니다; apiKey.secret 형태의 2단 점 구분 (입력하지 않으면 이 계정은 포워딩할 수 없습니다)", "用于领取套餐与查询余额;不填则这个账号不能领取与查余额": "플랜 수령과 잔액 조회에 사용합니다; 입력하지 않으면 이 계정은 수령과 잔액 조회를 할 수 없습니다", "用户 ID": "사용자 ID", "用绑定的手机号登录:点「获取验证码」后填入即可。这是国内版官方唯一的登录方式。": "연결된 휴대폰 번호로 로그인합니다: 「코드 받기」를 클릭한 뒤 입력하면 됩니다. 이것이 중국판의 공식 유일한 로그인 방식입니다.", "留空则用提供商名称": "비워 두면 제공자 이름 사용", "登录 / 添加 {name} 账号": "로그인 / {name} 계정 추가", "登录 session(一长串)": "로그인 session (긴 문자열)", "登录中…": "로그인 중…", "登录失败:{message}": "로그인 실패: {message}", "登录已结束": "로그인이 종료되었습니다", "登录并添加": "로그인 후 추가", "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "로그인 상태 파일 경로: ~/.box-agent/config/auth.json (Windows: C:\\Users\\<사용자 이름>\\.box-agent\\config\\auth.json)", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "커스텀 제공자 「{name}」을(를) 삭제할까요? 이 제공자 아래의 계정 {count}개도 함께 삭제되며, 삭제 후에는 복구할 수 없습니다.", "等待 Accio {label}登录完成…": "Accio {label} 로그인 대기 중…", "等待 AtomCode 登录完成…": "AtomCode 로그인 완료 대기 중…", "等待 CatPaw 登录完成…": "CatPaw 로그인 대기 중…", "等待 Cline 授权确认…": "Cline 인가 확인 대기 중…", "等待 CodeArts 登录完成…": "CodeArts 로그인 대기 중…", "等待 Google 授权完成…": "Google 인가 대기 중…", "等待 KukuAI 登录完成…": "KukuAI 로그인 대기 중…", "等待 LobsterAI 登录完成…": "LobsterAI 로그인 대기 중…", "等待 OfficeAce 登录完成…": "OfficeAce 로그인 대기 중…", "等待 OrcaTerm 登录完成…": "OrcaTerm 로그인 대기 중…", "等待 Qoder 授权完成…": "Qoder 인가 대기 중…", "等待 Trae 授权完成…": "Trae 인가 대기 중…", "等待 ZCode {label}授权完成…": "ZCode {label} 인가 대기 중…", "等待小浣熊登录完成…": "小浣熊 로그인 대기 중…", "等待登录完成…": "로그인 완료 대기 중…", "等待网页登录…": "웹 로그인 대기 중…", "粘贴 API Key": "API Key 붙여넣기", "粘贴 Accio 的 accessToken": "Accio의 accessToken 붙여넣기", "粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
    这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。": "Antigravity의 Google refresh token을 붙여넣으세요 (1//로 시작; refresh_token=이 포함된 줄 전체를 붙여넣어도 됨).
    웹 로그인 외의 수동 입구입니다(예: 로그인된 공식 IDE에서 내보내기). refresh token은 만료되지 않으며, access token 만료 시 게이트웨이가 자동으로 갱신합니다; 토큰을 잃어버리면 Google 인가를 다시 받아야 하므로 잘 보관하세요.", "粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。": "Command Code의 API Key를 붙여넣으세요 (user_로 시작; 공식 CLI의 ~/.commandcode/auth.json 또는 웹 studio에서 획득). 줄 전체 / 따옴표 포함 / Bearer 접두사 포함 그대로 붙여넣어도 됩니다. 게이트웨이가 알아서 순수한 key만 추출합니다. key는 만료되지 않고 갱신 인터페이스도 없으므로, 무효가 되면 다시 받으면 됩니다; 추가 시 게이트웨이가 가벼운 활성 확인을 한 번 수행하며, 실패해도 추가에는 영향을 주지 않습니다.", "粘贴 Cookie": "Cookie 붙여넣기", "粘贴 Google refresh token": "Google refresh token 붙여넣기", "粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。": "OrcaTerm의 OAuth accessToken(점 3개로 구분된 JWT. 데스크톱 클라이언트의 data.bin 또는 웹 버전 Cookie ot_session에서 확인)을 붙여넣으세요. Bearer / ot_session=이 포함된 줄을 그대로 붙여넣어도 되며, 게이트웨이가 토큰만 추출합니다. 추가 후에는 게이트웨이가 자동으로 갱신하므로(업스트림에 갱신 API가 있음) 2시간 만료를 직접 따라갈 필요가 없습니다.", "粘贴 Qoder 个人访问令牌(pt-…)": "Qoder 개인 액세스 토큰(pt-…) 붙여넣기", "粘贴 accessToken": "accessToken 붙여넣기", "粘贴 access_token(一长串 JWT)": "access_token 붙여넣기 (긴 JWT)", "粘贴 session cookie": "session cookie 붙여넣기", "粘贴 token / refreshToken": "token / refreshToken 붙여넣기", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "workos:…로 시작하는 토큰 붙여넣기 (접두사가 없어도 자동으로 붙습니다)", "粘贴令牌对": "토큰 쌍 붙여넣기", "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "智谱清言 액세스 토큰(점 3개로 구분된 JWT)을 붙여넣으세요. 발급 방법: 브라우저에서 chatglm.cn을 열고 로그인한 뒤 개발자 도구의 Application → Cookies에서 chatglm_token 값을 복사합니다(chatglm_token=…; 줄을 그대로 붙여도 됩니다). 붙여넣은 뒤 업스트림에 한 번 검증하며, 통과한 경우에만 계정을 저장합니다. 로그인된 계정은 포인트와 签到 기능을 쓸 수 있고, 이후 게이트웨이가 자동으로 갱신합니다(업스트림에 갱신 인터페이스가 있습니다).", "粘贴登录凭据": "로그인 자격 증명 붙여넣기", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "붙여넣는 것은 로그인 session입니다 (동시에 모델 게이트웨이의 token입니다). Loomy에는 갱신 인터페이스가 없으며 session 유효 기간은 약 14일이므로, 만료되면 다시 로그인하면 됩니다.", "粘贴网关凭据": "게이트웨이 자격 증명 붙여넣기", "粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
    {siteNote}
    MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。": "웹 로그인 상태 cookie monkeycode_ai_session의 값을 붙여넣으세요 (브라우저에서 전체 Cookie를 복사해 그대로 붙여넣어도 됩니다. 게이트웨이가 값을 알아서 추출합니다).
    {siteNote}
    MonkeyCode에는 갱신 인터페이스가 없습니다: session 유효 기간은 약 30일(하드 제한)이며, 만료되면 웹에 다시 로그인해 한 번 더 붙여넣으면 됩니다.", "粘贴访问令牌": "액세스 토큰 붙여넣기", "系统浏览器": "시스템 브라우저", "系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。": "시스템 브라우저는 기존 로그인 상태를 재사용할 수 있습니다 (국제판 권장); 내장 창이 더 깔끔합니다.", "系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表": "시스템 브라우저로 엽니다 (로그인된 Cline 계정 재사용); 완료하면 자동으로 목록에 추가됩니다", "系统浏览器打开(复用已登录的 Google 账号);完成后自动加入列表": "시스템 브라우저로 엽니다(로그인된 Google 계정 재사용); 완료하면 자동으로 목록에 추가됩니다", "系统浏览器打开(复用已登录的 Zai / Google 账号);完成后自动加入列表": "시스템 브라우저로 엽니다 (로그인된 Zai / Google 계정 재사용); 완료하면 자동으로 목록에 추가됩니다", "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "시스템 브라우저로 엽니다 (로그인된 Meituan 계정 재사용); 완료하면 자동으로 목록에 추가됩니다", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "시스템 브라우저로 엽니다 (로그인된 계정 재사용); 완료하면 자동으로 목록에 추가됩니다", "系统默认浏览器": "시스템 기본 브라우저", "续期令牌": "리프레시 토큰", "编码套餐 API Key": "코딩 플랜 API Key", "网关 Secret": "게이트웨이 시크릿", "网页登录": "웹 로그인", "网页登录的打开方式": "웹 로그인을 여는 방식", "网页登录(Zai / Google)": "웹 로그인 (Zai / Google)", "自定义": "커스텀", "自建聚合面板:填你自己的站点地址。余额查询用该账号的 API Key 打 {base}/v1/usage,不需要额外配置": "자체 구축 어그리게이션 패널: 자신의 사이트 주소를 입력하세요. 잔액 조회는 이 계정의 API 키로 {base}/v1/usage 를 호출하며 추가 설정이 필요 없습니다", "自建聚合面板:填你自己的站点地址;余额查询要另填 NewAPI 的个人访问令牌(见下方「余额查询」一段)": "자체 구축 어그리게이션 패널: 자신의 사이트 주소를 입력하세요. 잔액 조회에는 별도로 NewAPI 개인 액세스 토큰이 필요합니다 (아래 \"잔액 조회\" 단락 참조)", "获取风控配置失败,将直接尝试领取:{message}": "리스크 관리 구성을 가져오지 못했습니다. 바로 수령을 시도합니다: {message}", "获取验证码": "코드 받기", "访客模式": "게스트 모드", "访客模式(免登录)": "게스트 모드(로그인 불필요)", "访问令牌": "액세스 토큰", "该上游无需鉴权(不发送鉴权头)": "이 업스트림은 인증이 필요하지 않음 (인증 헤더를 보내지 않음)", "该提供商": "이 제공자", "该提供商账号添加功能即将上线": "이 제공자의 계정 추가 기능이 곧 출시됩니다", "该自定义提供商已不存在(可能已被删除),请刷新后重试": "이 커스텀 제공자가 더 이상 존재하지 않습니다(삭제되었을 수 있습니다). 새로 고친 후 다시 시도하세요", "请先选择一个自定义提供商": "먼저 커스텀 제공자를 선택하세요", "请在弹出的滑块中完成验证(官方要求的风控步骤)": "표시된 슬라이더에서 인증을 완료하세요 (공식에서 요구하는 리스크 관리 단계)", "请填写 11 位大陆手机号": "11자리 중국 본토 휴대폰 번호를 입력하세요", "请填写 6 位数字验证码": "6자리 숫자 인증 코드를 입력하세요", "请填写 Base URL": "Base URL을 입력하세요", "请填写 {label}": "{label}을(를) 입력하세요", "请填写名称": "이름을 입력하세요", "请完成验证…": "인증을 완료하세요…", "读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows": "%APPDATA%/AutoClaw/auth.json을 읽어 복호화합니다. Windows 전용", "读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录": "~/.cline/data/settings/providers.json을 읽습니다. Cline 클라이언트에 로그인되어 있어야 합니다", "读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录": "~/.meituan-catpaw/auth.json을 읽습니다. CatPaw 클라이언트에 로그인되어 있어야 합니다", "读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录": "이 컴퓨터의 KukuAI 클라이언트 로그인 상태를 읽습니다 (%APPDATA%\\baidugenflowpro\\Network\\Cookies). KukuAI 클라이언트에 로그인되어 있어야 합니다", "读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录": "로컬 OrcaTerm 클라이언트 로그인 상태(%APPDATA%\\com.orcaterm-desktop.app\\data.bin)를 읽습니다. OrcaTerm 클라이언트에 로그인되어 있어야 합니다", "读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录": "이 PC의 智谱清言 클라이언트 로그인 상태를 읽습니다(%APPDATA%\\chatglm\\Network\\Cookies). 클라이언트에 로그인되어 있어야 합니다", "读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。": "이 컴퓨터의 공식 클라이언트(Tencent WorkBuddy / WorkBuddy AI)에 현재 로그인된 계정을 읽습니다. 자격 증명 디렉터리에서 최신 로그인 상태 파일을 자동으로 찾습니다(이 컴퓨터의 공식 클라이언트에 로그인되어 있어야 함). 가져온 후에는 게이트웨이가 자동 갱신하므로 2시간 유효 기간에 맞춰 토큰을 수동으로 교체할 필요가 없습니다.", "读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 읽습니다 (평문 Cookie, 복호화 불필요). 클라이언트 실행 중에는 Cookies 파일이 독점 점유되므로, 가져오기 전에 KukuAI 클라이언트를 먼저 종료하세요.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 매번 실시간으로 읽습니다 (이 기록을 삭제해도 클라이언트 로그인 상태에는 영향을 주지 않습니다).", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后点「刷新 Token」同步。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 매번 실시간으로 읽습니다 (이 기록을 삭제해도 클라이언트 로그인 상태에는 영향을 주지 않습니다). 클라이언트에서 다시 로그인한 후 「Token 갱신」을 눌러 동기화하세요.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后重新导入即可。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 매번 실시간으로 읽습니다 (이 기록을 삭제해도 클라이언트 로그인 상태에는 영향을 주지 않습니다). 클라이언트에서 다시 로그인한 후 다시 가져오면 됩니다.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 매번 실시간으로 읽습니다 (이 기록을 삭제해도 클라이언트 로그인 상태에는 영향을 주지 않습니다). Zai / Google로 클라이언트에 로그인하는 사용자는 이 항목을 사용하세요.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。": "이 컴퓨터 클라이언트의 현재 로그인 상태를 매번 실시간으로 읽습니다 (이 기록을 삭제해도 클라이언트 로그인 상태에는 영향을 주지 않습니다). 이 항목은 {label}로 분류됩니다.", "读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。": "로컬 클라이언트가 현재 로그인한 OAuth 토큰을 읽습니다(평문 JSON, 복호화 불필요). 가져온 뒤에는 게이트웨이가 자동으로 갱신하므로 클라이언트의 2시간 만료에 맞춰 수동으로 교체할 필요가 없습니다.", "读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。": "이 PC 클라이언트의 현재 로그인 상태를 읽습니다. 클라이언트의 Cookies는 DPAPI로 암호화되어 있어(Chromium os_crypt) Windows에서만 사용할 수 있습니다. 클라이언트가 실행 중이면 이 파일을 읽지 못할 수 있으니 클라이언트를 종료한 뒤 다시 시도하세요(\"자격 증명 입력\"으로 토큰을 붙여넣어도 됩니다).", "账号信息": "계정 정보", "账号信息不完整,请刷新后重试": "계정 정보가 불완전합니다. 새로 고친 후 다시 시도하세요", "账号已添加,但请留意:{warning}": "계정은 추가되었지만 확인해 주세요: {warning}", "账号版本": "계정 버전", "账号类型": "계정 유형", "账号:{name}
    ": "계정: {name}
    ", "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 创建": "Inference Providers 라우팅을 사용하며, Key는 hf.co/settings/tokens에서 만듭니다", "还没有账号": "아직 계정이 없습니다", "这一家当前不支持网页登录,请改用填写凭证": "이 제공자는 현재 웹 로그인을 지원하지 않습니다. 대신 자격 증명 입력을 사용하세요", "这个账号当前没有可自动领的活动(只自动领每日登录赠送积分)": "이 계정에는 현재 자동으로 수령할 수 있는 이벤트가 없습니다 (매일 로그인 보너스 포인트만 자동 수령합니다)", "这份额度走「活动套餐」通道:若转发时提示编码套餐已到期,": "이 할당량은 「이벤트 플랜」 채널을 사용합니다: 포워딩 시 코딩 플랜이 만료되었다는 안내가 나오면, ", "这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。": "여기에는 중국 사이트(monkeycode-ai.com)의 계정을 입력합니다: 중국 사이트의 session을 붙여넣으세요. 국제 사이트(monkeycode-ai.net)는 다른 제공자이며 별도의 계정입니다. 양쪽 자격 증명은 호환되지 않습니다.", "这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。": "여기에는 국제 사이트(monkeycode-ai.net)의 계정을 입력합니다: 국제 사이트의 session을 붙여넣으세요. 중국 사이트(monkeycode-ai.com)는 다른 제공자이며 별도의 계정입니다. 양쪽 자격 증명은 호환되지 않습니다.", "选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "CatPaw 클라이언트의 로그인 상태 파일 auth.json을 선택하세요 (Windows: C:\\Users\\<사용자 이름>\\.meituan-catpaw\\auth.json, macOS / Linux: ~/.meituan-catpaw/auth.json). 가져오기 후에는 자격 증명이 게이트웨이에 저장되어 독립적으로 전달할 수 있습니다 — 더 이상 이 컴퓨터의 클라이언트에 의존하지 않습니다.", "选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。": "Cline 클라이언트의 로그인 상태 파일 providers.json을 선택하세요 (Windows: C:\\Users\\<사용자 이름>\\.cline\\data\\settings\\providers.json, macOS / Linux: ~/.cline/data/settings/providers.json). 가져오기 후에는 자격 증명이 게이트웨이에 저장되어 독립적으로 전달할 수 있습니다 — 더 이상 이 컴퓨터의 클라이언트에 의존하지 않습니다. 이 항목은 {label}에 등록됩니다.", "选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。": "KukuAI 클라이언트의 Cookies 파일을 선택하세요 (%APPDATA%\\baidugenflowpro\\Network\\Cookies): **바이너리** SQLite 데이터베이스입니다 (확장자 없음, JSON 아님). 가져오기는 선택한 파일의 복사본을 읽어 실행 중인 클라이언트와 점유를 다투지 않으므로 **클라이언트를 먼저 종료할 필요가 없습니다**. 가져오기 후에는 자격 증명이 게이트웨이에 저장되어 독립적으로 전달할 수 있습니다.", "选择凭证文件导入": "자격 증명 파일에서 가져오기", "选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。": "공식 클라이언트의 로그인 자격 증명 파일(workbuddy-desktop.info / workbuddy-desktop-ai.info) 또는 wb2api에서 내보낸 계정 JSON(중첩/평면 형식 모두 가능)을 선택하세요.", "选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "小浣熊 클라이언트의 로그인 상태 파일 auth.json을 선택하세요 (Windows: C:\\Users\\<사용자 이름>\\.box-agent\\config\\auth.json, macOS / Linux: ~/.box-agent/config/auth.json). 가져오기 후에는 자격 증명이 게이트웨이에 저장되어 독립적으로 전달할 수 있습니다 — 더 이상 이 컴퓨터의 클라이언트에 의존하지 않습니다.", "选择提供商": "제공자 선택", "选择的文件不是合法 JSON": "선택한 파일이 유효한 JSON이 아닙니다", "选择要添加账号的提供商": "계정을 추가할 제공자 선택", "重新扫描": "다시 스캔", "鉴权": "인증", "阿里云 Token Plan 套餐(国际站)": "Alibaba Cloud Token Plan (국제 사이트)", "阿里云百炼 Coding 套餐(国内站)": "Alibaba Cloud Model Studio Coding 플랜 (중국 사이트)", "阿里云百炼 Coding 套餐(国际站)": "Alibaba Cloud Model Studio Coding 플랜 (국제 사이트)", "阿里云百炼模型服务(国际站,OpenAI 兼容模式)": "Alibaba Cloud Model Studio 모델 서비스 (국제 사이트, OpenAI 호환 모드)", "需要图形验证码:登录窗口已弹出,请填写后继续": "그래픽 캡차가 필요합니다: 로그인 창이 열렸으니 입력한 후 계속하세요", "预置 API": "프리셋 API", "领取 CodeArts 每日福利": "CodeArts 일일 혜택 수령", "领取失败:{message}": "수령 실패: {message}", "领取并让官方确认": "수령하고 공식 확인 받기", "风控验证配置不完整,请稍后重试": "리스크 관리 인증 구성이 불완전합니다. 잠시 후 다시 시도하세요", "验证码": "인증 코드", "验证码已发送。收到后填入下方并点「登录并添加」": "인증 코드를 전송했습니다. 받은 후 아래에 입력하고 「로그인 후 추가」를 클릭하세요", "验证码已发送,请查看手机短信": "인증 코드를 전송했습니다. 휴대폰 SMS를 확인하세요", "验证码已发送,请查看手机短信。填入下方并点「登录并添加」即完成登录": "인증 코드를 전송했습니다. 휴대폰 SMS를 확인하세요. 아래에 입력하고 「로그인 후 추가」를 클릭하면 로그인이 완료됩니다", "验证码已发送,请查看短信": "인증 코드를 전송했습니다. 문자 메시지를 확인하세요", "验证码校验失败,请重试": "인증 코드 확인에 실패했습니다. 다시 시도하세요", "验证码校验超时,请重试": "인증 코드 확인 시간이 초과되었습니다. 다시 시도하세요", "验证码组件不可用,请重试": "캡차 컴포넌트를 사용할 수 없습니다. 다시 시도하세요", "验证码组件初始化超时,请重试": "캡차 컴포넌트 초기화 시간이 초과되었습니다. 다시 시도하세요", "验证码组件加载失败,请检查网络后重试": "캡차 컴포넌트 로드에 실패했습니다. 네트워크를 확인한 후 다시 시도하세요", "验证码组件加载异常,请重试": "캡차 컴포넌트 로드 중 오류가 발생했습니다. 다시 시도하세요", "验证码组件加载超时,请检查网络后重试": "캡차 컴포넌트 로드 시간이 초과되었습니다. 네트워크를 확인한 후 다시 시도하세요", "验证码组件未加载,请重启应用后重试": "캡차 컴포넌트가 로드되지 않았습니다. 앱을 다시 시작한 후 다시 시도하세요", "(免费额度池,模型名带 cline-free/ 前缀)。": " (무료 할당량 풀, 모델 이름에 cline-free/ 접두사 포함).", "(其中 {n} 把恢复为不限制)": " (이 중 {n}개는 제한 없음으로 복원)", "(必填)": " (필수)", "(有效期 {window})": "(유효 기간 {window})", "(订阅池,模型名带 cline-pass/ 前缀,需要账号有对应订阅)。": " (구독 풀, 모델 이름에 cline-pass/ 접두사 포함, 계정에 해당 구독이 있어야 합니다).", ",下一次最快 {minutes} 分钟后": ", 다음 시도는 최소 {minutes}분 후", ",并从 {n} 把 Key 的可用提供商里移除": ", Key {n}개의 사용 가능한 제공자 목록에서 이 제공자를 제거했습니다" }); diff --git a/desktop-tauri/ui/i18n/ko/checkin.js b/desktop-tauri/ui/i18n/ko/checkin.js index 1a3f2ee1..5914d3d8 100644 --- a/desktop-tauri/ui/i18n/ko/checkin.js +++ b/desktop-tauri/ui/i18n/ko/checkin.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:ko/checkin —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · ": " · ", "SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新": "SOLO의 checkin_credits 수령 · 자연일 0시 기준 새로고침", "UMID 组件安装失败:{error}": "UMID 컴포넌트 설치 실패: {error}", "WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细": "WorkBuddy 중국판 · 작업 트리를 조회한 뒤 한 번에 점등/수령 · 개학 시즌 이벤트와 고양이 여행 포함 · 행을 클릭하면 상세", "WorkBuddy 国际版保活模型,按顺序回退": "WorkBuddy 국제판 키핑 모델, 순서대로 폴백", "WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关": "WorkBuddy 국제판 일일 활동용 무료 모델 · 전역 단일 설정, 항목과 무관", "{date}(每天 {time})": "{date} (매일 {time})", "{label} ×{count}({reason})": "{label} ×{count}({reason})", "{n} 项可完成": "{n}개 완료 가능", "{n} 项待领": "수령 대기 {n}건", "{total} 账号 · {done} 已签": "계정 {total}개 · 출석 완료 {done}개", "{total} 项任务 · {pending} 项可完成": "작업 {total}개 · 완료 가능 {pending}개", "✅ UMID 组件已安装,下轮签到即可领取国际版积分": "✅ UMID 컴포넌트를 설치했습니다. 다음 출석부터 국제판 포인트를 수령할 수 있습니다", "✅ {message}": "✅ {message}", "✅ 定时签到已开启": "✅ 예약 출석이 켜졌습니다", "✅ 开学季任务执行完成": "✅ 개학 시즌 작업 실행 완료", "✅ 成长任务执行完成:积分 +{credit} 能量 +{energy}": "✅ 성장 과제 완료: 크레딧 +{credit} 에너지 +{energy}", "✅ 新手福利领取完成,累计 {n} 积分": "✅ 입문 혜택 수령 완료, 누적 {n} 포인트", "✅ 签到完成:{succeeded}/{total} 个账号成功领取{active}": "✅ 출석 완료: {succeeded}/{total}개 계정 수령 성공{active}", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空提交恢复缺省({defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "\"키핑 모델 체인\"은 WorkBuddy 국제판 일일 활동에서 계정을 활성 상태로 유지하는 무료 모델입니다. 순서대로 시도하며 첫 성공에서 멈춥니다. 비운 채로 제출하면 기본값({defaults})으로 복원됩니다. 당일 중복 제거 + 멱등한 수령: 반복 실행해도 \"이미 수령됨\"만 반환되며 포인트가 이중으로 적립되지 않습니다.", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "\"키핑 모델 체인\"은 WorkBuddy 국제판 일일 활동에서 계정을 활성 상태로 유지하는 무료 모델입니다. 순서대로 시도하며 첫 성공에서 멈춥니다. 목록을 비우고 저장하면 기본 체인(현재 기본값: {defaults})으로 복원됩니다. 당일 중복 제거 + 멱등한 수령: 반복 실행해도 \"이미 수령됨\"만 반환되며 포인트가 이중으로 적립되지 않습니다.", "「免费领积分」的每日任务 · 逐个领取": "「무료 포인트 받기」 일일 작업 · 하나씩 수령", "一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单": "일회성 혜택 · 출석 후 자동으로 조회하고 수령하며, 수령을 마치면 로컬에 기록해 업스트림을 더 조회하지 않음 · 행을 클릭해 작업 목록 펼치기", "一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证": "그날의 보상을 한 번에 일괄 수령합니다(매일 출석 + 신규 혜택) · 멱등성은 업스트림이 보장", "一键安装组件": "컴포넌트 한 번에 설치", "一键完成": "한 번에 완료", "一键领取({n})": "전체 수령({n})", "上次执行:{line}": "마지막 실행: {line}", "上次签到时间": "최근 출석 시간", "上次领取 {time} · 领取需通过滑块验证码": "최근 수령 {time} · 수령 시 슬라이더 캡차 필요", "下次 {date}": "다음 {date}", "下次 {date} · 点此管理条目": "다음 {date} · 클릭하여 항목 관리", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它与 ZCode 的每日/套餐福利领取见「活动福利」。": "일일 출석 제외: {list}. CodeArts의 일회성 보상은 아래 \"입문 혜택\"에 있으며, CodeArts와 ZCode의 일일/플랜 혜택 수령은 \"활동 혜택\"에서 볼 수 있습니다.", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。": "일일 출석 제외: {list}. CodeArts의 일회성 보상은 아래 \"입문 혜택\"에, CodeArts의 일일 혜택은 바로 위 \"일일 출석\"에 있습니다. ZCode의 플랜 수령은 \"활동 혜택\"에서 볼 수 있습니다.", "与国内版同一套任务接口 · 站点不同": "중국판과 동일한 작업 인터페이스 · 사이트만 다름", "今天已领 {n} 份": "오늘 {n}건 받음", "今天未领": "오늘 미수령", "今天(北京时间 {day})已领取 · 官方确认到账 {n} 项 · 明天可再领": "오늘(베이징 시간 {day}) 수령 완료 · 공식 확인 반영 {n}건 · 내일 다시 수령 가능", "今日已执行": "오늘 실행됨", "今日已派出": "오늘 이미 출발", "今日已领取": "오늘 수령 완료", "今日签到": "오늘 출석", "今日签到进度": "오늘 출석 진행률", "从清单里移除": "목록에서 제거", "保活": "키핑", "保活 {n}": "키핑 {n}", "保活+领取": "키핑+수령", "保活中…": "키핑 중…", "保活模型链": "키핑 모델 체인", "全选": "전체 선택", "全部 {n} 家": "전체 {n}곳", "全部完成": "모두 완료", "全部查询": "전체 조회", "全部结果": "모든 결과", "全部账号均可签到": "모든 계정이 출석할 수 있습니다", "全部领取": "전체 수령 완료", "关着:所有条目都不执行": "꺼짐: 어떤 항목도 실행되지 않습니다", "删除失败:{error}": "삭제 실패: {error}", "删除定时条目": "예약 항목 삭제", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活并在网页通道建会话点亮日活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "예정 시각에 체크한 제공자의 사용 가능한 계정을 자동으로 출석 체크합니다 (WorkBuddy 국내판은 매일 출석 API, 국제판은 데일리 액티비티 퀘스트: 이벤트 탐색 → 데일리 액티브 보상 수령 → 무료 모델로 킵얼라이브하고 웹 채널로 세션을 열어 일일 활성 점등. 소浣熊(샤오환슝)은 데스크톱 일일 포인트 경로, AutoClaw 는 공식 클라이언트의 매일 출석 작업. Qoder 중국판은 이벤트 수령 방식이며 이벤트를 배정받지 못한 계정은 중립적 안내를 받습니다. Qoder 국제판은 기기 리스크 관리 신원(로컬 Qoder 클라이언트 / qodercli 의 UMID 컴포넌트가 생성)을 먼저 갖춰야 \"매일 100 Credits\" 이벤트가 보이며, 컴포넌트가 없으면 오류 대신 명확한 설명을 받습니다. Trae 는 SOLO 의 매일 출석 포인트를 수령하고(자정 리셋), 업스트림이 이벤트를 열지 않았거나 자격 증명에 기기 번호가 없으면 오류 대신 \"미수령 + 사유\"를 받습니다. Loomy 는 매일 계정별 첫 로그인 포인트를 한 번 채우고, KukuAI 는 \"무료 포인트\" 일일 작업을 수령하며, CodeArts 는 당일 운영 혜택을 수령합니다(수령 → 확인 → 재조회 검증. 자동 실행은 하루 6 회 / 10 분 간격 쓰기 요청 제한 내). OfficeAce 는 당일 모든 보상을 한 번에 수령합니다 (매일 출석 + 신규 혜택)). 예정 시각을 놓쳐도 부팅 후 자동으로 보충하므로 전원이 꺼져 있어도 누락되지 않습니다. 각사 출석 API 는 멱등하여 반복 실행해도 중복 수령되지 않습니다.", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "정해진 시각이 되면 선택한 제공자의 사용 가능한 계정으로 자동 출석합니다 (WorkBuddy 중국판은 매일 출석 체크 인터페이스, 국제판은 일일 활성 작업으로 진행합니다: 이벤트 탐지, 일일 활성 보상 수령, 무료 모델로 키핑; 小浣熊은 데스크톱 앱 일일 포인트 경로를 사용하고; AutoClaw는 공식 클라이언트의 매일 출석 작업을 사용합니다; Qoder 중국판은 이벤트 수령으로 진행하며, 이벤트가 배정되지 않은 계정은 중립적인 안내를 받습니다; Qoder 국제판은 기기 리스크 관리 신원(로컬 Qoder 클라이언트 / qodercli의 UMID 컴포넌트로 생성)을 먼저 갖춰야 「매일 100 Credits」 이벤트가 보이며, 컴포넌트가 없으면 오류 대신 명확한 설명이 표시됩니다; Trae는 SOLO의 매일 출석 포인트를 수령하며(자연일 0시 기준 초기화), 업스트림이 해당 계정에 이벤트를 열지 않았거나 자격 증명에 기기 번호가 없으면 오류 대신 「미수령 + 구체적 사유」가 표시됩니다; Loomy는 매일 계정당 최초 로그인 포인트를 한 번 적립합니다; KukuAI는 「무료 포인트 받기」 일일 작업을 수령합니다; CodeArts는 당일 운영 혜택을 수령합니다(수령 → 확인 → 재조회 검증, 자동 실행은 하루 6회 / 간격 10분의 쓰기 요청 제한이 적용됩니다); OfficeAce 는 그날의 보상을 한 번의 동작으로 일괄 수령합니다(매일 출석 + 신규 혜택)). 시각을 놓치면 부팅 후 자동으로 보충 출석하며, 당시 컴퓨터가 꺼져 있었다고 해서 빠지지 않습니다. 각 사의 출석 인터페이스는 멱등이므로 반복 실행해도 중복 수령되지 않습니다.", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "정해진 시각이 되면 선택한 제공자의 사용 가능한 계정으로 자동 출석합니다 (WorkBuddy 중국판은 매일 출석 체크 인터페이스, 국제판은 일일 활성 작업으로 진행합니다: 이벤트 탐지, 일일 활성 보상 수령, 무료 모델로 키핑; 小浣熊은 데스크톱 앱 일일 포인트 경로를 사용하고; AutoClaw는 공식 클라이언트의 매일 출석 작업을 사용합니다; Qoder 중국판은 이벤트 수령으로 진행하며, 이벤트가 배정되지 않은 계정은 중립적인 안내를 받습니다; Qoder 국제판은 기기 리스크 관리 신원(로컬 Qoder 클라이언트 / qodercli의 UMID 컴포넌트로 생성)을 먼저 갖춰야 「매일 100 Credits」 이벤트가 보이며, 컴포넌트가 없으면 오류 대신 명확한 설명이 표시됩니다; Trae는 SOLO의 매일 출석 포인트를 수령하며(자연일 0시 기준 초기화), 업스트림이 해당 계정에 이벤트를 열지 않았거나 자격 증명에 기기 번호가 없으면 오류 대신 「미수령 + 구체적 사유」가 표시됩니다; Loomy는 매일 계정당 최초 로그인 포인트를 한 번 적립합니다; KukuAI는 「무료 포인트 받기」 일일 작업을 수령합니다; OfficeAce 는 그날의 보상을 한 번의 동작으로 일괄 수령합니다(매일 출석 + 신규 혜택)). 시각을 놓치면 부팅 후 자동으로 보충 출석하며, 당시 컴퓨터가 꺼져 있었다고 해서 빠지지 않습니다. 각 사의 출석 인터페이스는 멱등이므로 반복 실행해도 중복 수령되지 않습니다.", "到点时逐个签到,已勾 {n} / {total} 家": "정해진 시각에 하나씩 출석합니다. 선택 {n} / {total}개", "北京时间 · 错过时点开机后会自动补签": "베이징 시간 · 시간을 놓쳐도 시작 후 자동으로 보완됩니다", "去领取": "수령하러 가기", "另有 {n} 个账号不参与每日签到": "그 외 {n}개 계정은 매일 출석 체크에 참여하지 않습니다", "只领取已完成任务的奖励(幂等,重复点不会重复入账)": "완료된 작업의 보상만 수령(멱등, 반복 클릭해도 중복 적립되지 않음)", "可完成": "완료 가능", "可派出": "출발 가능", "可选 · 缺省显示时刻": "선택 · 기본값은 시간 표시", "可领奖": "수령 가능", "后端未返回定时签到数据": "백엔드가 예약 데이터를 반환하지 않았습니다", "后端未返回签到中心数据": "백엔드가 출석 체크 센터 데이터를 반환하지 않았습니다", "国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。": "국제판 출석에는 기기 리스크 관리 신원을 생성할 UMID 컴포넌트가 필요합니다 (이 기기에서 감지되지 않음).", "在途": "여행 중", "填入缺省链": "기본 체인 채우기", "填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)": "시각과 출석 범위를 정한 뒤 「저장」을 누르세요. 이름은 비워도 됩니다(비우면 시각이 표시됩니다)", "失败 {n}": "실패 {n}", "套餐明细": "플랜 상세", "安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。": "Qoder 클라이언트 또는 qodercli를 설치하면 됩니다 (게이트웨이 재시작 불필요).", "安装中…": "설치 중…", "官方客户端的每日签到任务": "공식 클라이언트의 매일 출석 작업", "定时签到": "예약 출석", "定时签到已关闭": "예약 출석이 꺼졌습니다", "尚未查询": "아직 조회하지 않음", "尚未查询任务状态": "작업 상태를 아직 조회하지 않았습니다", "巡检一次": "한 번 점검", "已保存:{label}": "저장됨: {label}", "已保存:保活模型链": "저장됨: 키핑 모델 체인", "已保存:定时条目": "저장됨: 예약 항목", "已删除定时条目": "예약 항목이 삭제되었습니다", "已完成活跃上报(连登已点亮),但活动未开启(本次没有可领的奖励)": "활동 보고 완료(연속 출석 점등), but 캠페인이 진행 중이 아닙니다(이번에 받을 보상 없음)", "已抽 {n} 次": "{n}회 추첨함", "已有结果 {checked} / 共 {total} 个账号": "결과 있음 {checked} / 전체 {total}개 계정", "已签": "출석 완료", "已领 {claimed}/{total} · 累计 {earned} 积分": "수령 {claimed}/{total} · 누적 {earned} 포인트", "已领养": "입양 완료", "已领取": "수령 완료", "带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件": "기기 리스크 관리 신원을 갖춰 「매일 100 Credits」 수령 · UMID 컴포넌트 필요", "开学季执行失败:{error}": "개학 시즌 실행 실패: {error}", "开学季执行完成:{message}": "개학 시즌 실행 완료: {message}", "开学季活动": "개학 시즌 이벤트", "开着:启用的条目按各自时刻执行": "켜짐: 활성화된 항목이 각자의 시간에 실행됩니다", "待签": "출석 대기", "待领新手福利": "수령 대기 입문 혜택", "总开关": "메인 스위치", "总开关已关闭 · 在「定时签到」页打开": "메인 스위치가 꺼져 있습니다 · \"예약 출석\" 탭에서 켜세요", "成功 {n}": "성공 {n}", "成长任务": "성장 과제", "成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}": "성장 과제 완료(성공 {done}개, 실패 {failed}개): 크레딧 +{credit} 에너지 +{energy}", "成长任务执行失败:{error}": "성장 과제 실행 실패: {error}", "执行失败:{error}": "실행 실패: {error}", "执行并抽奖": "실행 및 추첨", "执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)": "개학 시즌 작업을 실행하고 추첨 기회를 모두 사용(공유/대화/전문가 이벤트)", "把缺省链填进清单,再点「保存」生效": "기본 체인을 목록에 채웁니다. 「저장」을 눌러야 반영됩니다", "抽奖机会 {n} 次": "추첨 기회 {n}회", "按住拖动调整顺序": "길게 눌러 드래그해 순서 변경", "按住拖动,调整这个模型在链里的顺序": "길게 눌러 드래그해 체인에서 이 모델의 순서를 조정합니다", "按序逐个尝试,共 {n} 个": "순서대로 시도, 총 {n}개", "按结果筛选": "결과로 필터", "探测 + 领取 + 保活 · 与定时签到同一套组合": "탐지 + 수령 + 키핑 · 예약 출석과 동일한 조합", "探测活动并领取日活奖励 · 不做保活": "이벤트를 탐지해 일일 활성 보상을 수령 · 키핑은 하지 않음", "接单 + 点亮 + 领奖(只处理已实测沉淀的任务)": "수락 + 점등 + 수령(실측으로 검증된 작업만 처리)", "搜账号 / 收益 / 错误 / 触发原因": "계정 / 수익 / 오류 / 트리거 사유 검색", "收起新建": "새 항목 닫기", "新建定时条目": "새 예약 항목", "新手福利": "입문 혜택", "新手福利有 {n} 项领取失败,可重试": "입문 혜택 {n}건의 수령에 실패했습니다. 재시도할 수 있습니다", "无": "없음", "无独立签到接口 · 每天替账号打一次首次登录积分": "독립 출석 인터페이스 없음 · 매일 계정당 최초 로그인 포인트를 한 번 적립", "无账号": "계정 없음", "暂不可领": "지금은 수령 불가", "暂不支持": "아직 미지원", "暂无启用的条目 · 点此添加": "활성화된 항목이 없습니다 · 클릭하여 추가", "有未保存的改动": "저장되지 않은 변경 사항이 있습니다", "未勾选提供商": "공급자 미선택", "未开启": "꺼짐", "未解锁": "잠김", "未领养(领养需先完成当日对话量)": "미입양(입양하려면 먼저 당일 대화량을 채워야 함)", "未领取": "미수령", "本地台账里今天还没领过;活动期内每天发一份新套餐": "로컬 기록상 오늘은 아직 받지 않았습니다. 이벤트 기간에는 매일 새 플랜이 한 건씩 지급됩니다", "本地台账里今天(北京时间)领过的套餐:{plans}": "로컬 기록상 오늘(베이징 시간) 받은 플랜: {plans}", "条启用": "개 활성화", "查询于 {time}": "조회 시각 {time}", "查询任务": "작업 조회", "查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)": "작업 트리를 조회하고 한 번에 점등/수령(개학 시즌과 고양이 여행 포함)", "查询失败:{error}": "조회 실패: {error}", "桌面端每日积分链路": "데스크톱 앱 일일 포인트 경로", "正在读取定时签到数据…": "예약 출석 데이터를 읽는 중…", "正在读取签到数据…": "출석 데이터를 읽는 중…", "每天 {time}": "매일 {time}", "每天 {time} · {scope}": "매일 {time} · {scope}", "每天时刻": "매일 시각", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "출석 실행마다 · 계정별 한 행 · 결과와 수익은 백엔드 기록 기준", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "매일 활성 작업 · 웹 채널 세션으로 일일 활성 달성, 포인트는 수령 시 적립 · 두 가지는 따로 실행 가능", "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "매일 로그인 포인트 · 회원 활동 함께 수령 · 게스트 계정은 포인트 체계 없음(건너뜀)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "일일 혜택 · 수령 → 확인 → 재조회 검증 · 베이징 시간 자연일 기준 초기화", "每日签到": "매일 출석 체크", "每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签": "항목별 시간과 범위 · 항목별 당일 중복 제거 · 시작 시 자동 보완", "每账号一条明细 · 点此查看「签到记录」": "계정별 한 행 · \"출석 기록\" 열기", "没有可领套餐的账号(ZCode 未添加)。": "수령할 플랜이 있는 계정이 없습니다 (ZCode 미추가).", "没有可领福利的账号(CodeArts / ZCode 未添加)。": "혜택을 수령할 수 있는 계정이 없습니다 (CodeArts / ZCode 미추가).", "没有支持成长任务的账号(需要 WorkBuddy 国内版)。": "성장 과제를 지원하는 계정이 없습니다(WorkBuddy 중국판 필요).", "没有支持新手福利的账号": "입문 혜택을 지원하는 계정이 없습니다", "没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。": "입문 혜택을 지원하는 계정이 없습니다 — 현재 Loomy, 小浣熊, CodeArts 세 곳입니다.", "没有符合筛选条件的签到记录": "필터 조건에 맞는 출석 기록이 없습니다", "没有需要执行的任务": "실행할 작업이 없습니다", "活动福利": "이벤트 혜택", "活动(campaign)领取 · 每天 10:00 刷新": "캠페인(campaign) 수령 · 매일 10:00 새로고침", "活跃上报与保活均未成功,且活动未开启": "활동 보고와 keep-alive 모두 실패했고 캠페인도 진행 중이 아닙니다", "活跃上报完成,但保活未成功且活动未开启": "활동 보고는 완료되었지만 keep-alive에 실패했고 캠페인도 진행 중이 아닙니다", "活跃上报完成,但活动未开启": "활동 보고 완료, but 캠페인이 진행 중이 아닙니다", "活跃上报成功": "활동 보고 성공", "活跃保活失败": "활성 키핑 실패", "活跃保活完成": "활성 키핑 완료", "活跃保活完成,但活动未开启": "활성 키핑 완료, 다만 이벤트는 열리지 않았습니다", "活跃保活完成,但活跃上报失败且活动未开启": "keep-alive는 완료되었지만 활동 보고에 실패했고 캠페인도 진행 중이 아닙니다", "添加条目": "항목 추가", "添加模型": "모델 추가", "清单为空 —— 保存后恢复缺省链": "목록이 비어 있습니다 — 저장하면 기본 체인으로 복원됩니다", "清单里已经有「{name}」": "목록에 \"{name}\"이(가) 이미 있습니다", "清空失败:{error}": "비우기 실패: {error}", "清空签到记录": "출석 기록 비우기", "点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领": "\"플랜 상세\"에서 어떤 플랜을 받았고 언제 적용되는지 확인하세요. 이벤트 기간에는 매일 다시 받을 수 있습니다", "点击行展开账号明细 · 单账号签到不受范围限制": "행을 클릭해 계정 상세 펼치기 · 개별 계정 출석은 범위 제한을 받지 않습니다", "独立链路 · 只提供手动入口 · 不进自动签到": "독립 경로 · 수동 진입점만 제공 · 자동 출석에는 포함되지 않음", "猫猫旅行": "고양이 여행", "猫猫旅行失败:{error}": "고양이 여행 실패: {error}", "猫猫旅行失败:{message}": "고양이 여행 실패: {message}", "猫猫旅行:{message}": "고양이 여행: {message}", "用免费模型与网页会话维持账号活跃 · 不领取奖励": "무료 모델과 웹 채널 세션으로 계정 활성 유지 · 보상은 수령하지 않음", "确定删除「{name}」?删除后到点不再执行。": "\"{name}\"을(를) 삭제할까요? 삭제 후 예약 시간에 실행되지 않습니다.", "确定清空全部签到记录?清空后无法恢复。": "모든 출석 기록을 비울까요? 비운 후에는 복구할 수 없습니다.", "确定清空当前筛选出的签到记录?清空后无法恢复。": "현재 필터된 출석 기록을 비울까요? 비운 후에는 복구할 수 없습니다.", "移除 {name}": "{name} 제거", "立即全部签到": "지금 전체 출석", "签到": "출석", "签到中…": "출석 중…", "签到中心分区": "출석 센터 구역", "签到失败:{error}": "출석 실패: {error}", "签到完成:{succeeded}/{total} 成功{active},{failed} 个失败": "출석 완료: {succeeded}/{total} 성공{active}, {failed}개 실패", "签到成功": "출석 성공", "签到提供商": "출석 제공자", "签到范围": "출석 범위", "签到触发时刻": "출석 실행 시각", "签到记录": "출석 기록", "签到记录时间范围": "출석 기록 시간 범위", "签到记录表": "출석 기록 표", "累计签到记录": "누적 출석 기록", "结算于 {time}": "결산 시각 {time}", "能量 {n}": "에너지 {n}", "腾讯每日签到接口 · 仅国内版": "텐센트 매일 출석 인터페이스 · 중국판 전용", "至少勾一家才能保存(不勾等于这条条目不签到)": "저장하려면 최소 한 곳을 선택하세요(선택하지 않으면 이 항목은 아무것도 출석하지 않습니다)", "读取定时签到数据失败:{error}": "예약 출석 데이터 로드 실패: {error}", "读取签到数据失败:{error}": "출석 데이터 읽기 실패: {error}", "读取签到记录失败:{error}": "출석 기록 로드 실패: {error}", "跳过 {n}": "건너뜀 {n}", "运营活动交付(领取 → 确认 → 回读核实)· 领的是套餐赠送积分": "운영 이벤트 전달 (수령 → 확인 → 재조회 검증) · 플랜 증정 포인트를 수령합니다", "还没执行过": "아직 실행된 적 없음", "还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。": "예약 항목이 아직 없습니다 — \"항목 추가\"로 만들거나 \"출석 센터\"에서 수동으로 출석하세요.", "还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。": "출석 기록이 아직 없습니다 — 수동 출석 또는 예약 실행 후 여기에 상세 내역이 표시됩니다.", "连登 {n} 天": "연속 로그인 {n}일", "部分": "일부", "重签": "재출석", "重试": "다시 시도", "限时体验套餐 · 领取需滑块验证码 · 逐份查看与领取": "기간 한정 체험 플랜 · 수령 시 슬라이더 인증 필요 · 항목별로 확인하고 수령", "限时体验套餐(start-plan),活动期内每天一份 · 领取需滑块验证码": "기간 한정 체험 플랜(start-plan), 이벤트 기간 내 매일 1회 · 수령 시 슬라이더 캡차 필요", "需真实操作": "실제 조작 필요", "非活动期": "이벤트 기간 아님", "项": "건", "领取": "수령", "领取中": "수령 중", "领取中…": "수령 중…", "领取失败": "수령 실패", "领取失败:{error}": "수령 실패: {error}", "(共 {n} 个账号)": "(전체 {n}개 계정)", "(能量 / 连登读数本次未取到)": "(에너지/연속 로그인 수치는 이번에 가져오지 못했습니다)", ",日活保活 {n} 个": ", 일일 활성 키핑 {n}개" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · ": " · ", "SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新": "SOLO의 checkin_credits 수령 · 자연일 0시 기준 새로고침", "UMID 组件安装失败:{error}": "UMID 컴포넌트 설치 실패: {error}", "WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细": "WorkBuddy 중국판 · 작업 트리를 조회한 뒤 한 번에 점등/수령 · 개학 시즌 이벤트와 고양이 여행 포함 · 행을 클릭하면 상세", "WorkBuddy 国际版保活模型,按顺序回退": "WorkBuddy 국제판 키핑 모델, 순서대로 폴백", "WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关": "WorkBuddy 국제판 일일 활동용 무료 모델 · 전역 단일 설정, 항목과 무관", "{date}(每天 {time})": "{date} (매일 {time})", "{label} ×{count}({reason})": "{label} ×{count}({reason})", "{n} 项可完成": "{n}개 완료 가능", "{n} 项待领": "수령 대기 {n}건", "{total} 账号 · {done} 已签": "계정 {total}개 · 출석 완료 {done}개", "{total} 项任务 · {pending} 项可完成": "작업 {total}개 · 완료 가능 {pending}개", "✅ UMID 组件已安装,下轮签到即可领取国际版积分": "✅ UMID 컴포넌트를 설치했습니다. 다음 출석부터 국제판 포인트를 수령할 수 있습니다", "✅ {message}": "✅ {message}", "✅ 定时签到已开启": "✅ 예약 출석이 켜졌습니다", "✅ 开学季任务执行完成": "✅ 개학 시즌 작업 실행 완료", "✅ 成长任务执行完成:积分 +{credit} 能量 +{energy}": "✅ 성장 과제 완료: 크레딧 +{credit} 에너지 +{energy}", "✅ 新手福利领取完成,累计 {n} 积分": "✅ 입문 혜택 수령 완료, 누적 {n} 포인트", "✅ 签到完成:{succeeded}/{total} 个账号成功领取{active}": "✅ 출석 완료: {succeeded}/{total}개 계정 수령 성공{active}", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空提交恢复缺省({defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "\"키핑 모델 체인\"은 WorkBuddy 국제판 일일 활동에서 계정을 활성 상태로 유지하는 무료 모델입니다. 순서대로 시도하며 첫 성공에서 멈춥니다. 비운 채로 제출하면 기본값({defaults})으로 복원됩니다. 당일 중복 제거 + 멱등한 수령: 반복 실행해도 \"이미 수령됨\"만 반환되며 포인트가 이중으로 적립되지 않습니다.", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "\"키핑 모델 체인\"은 WorkBuddy 국제판 일일 활동에서 계정을 활성 상태로 유지하는 무료 모델입니다. 순서대로 시도하며 첫 성공에서 멈춥니다. 목록을 비우고 저장하면 기본 체인(현재 기본값: {defaults})으로 복원됩니다. 당일 중복 제거 + 멱등한 수령: 반복 실행해도 \"이미 수령됨\"만 반환되며 포인트가 이중으로 적립되지 않습니다.", "「免费领积分」的每日任务 · 逐个领取": "「무료 포인트 받기」 일일 작업 · 하나씩 수령", "一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单": "일회성 혜택 · 출석 후 자동으로 조회하고 수령하며, 수령을 마치면 로컬에 기록해 업스트림을 더 조회하지 않음 · 행을 클릭해 작업 목록 펼치기", "一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证": "그날의 보상을 한 번에 일괄 수령합니다(매일 출석 + 신규 혜택) · 멱등성은 업스트림이 보장", "一键安装组件": "컴포넌트 한 번에 설치", "一键完成": "한 번에 완료", "一键领取({n})": "전체 수령({n})", "上次执行:{line}": "마지막 실행: {line}", "上次签到时间": "최근 출석 시간", "上次领取 {time} · 领取需通过滑块验证码": "최근 수령 {time} · 수령 시 슬라이더 캡차 필요", "下次 {date}": "다음 {date}", "下次 {date} · 点此管理条目": "다음 {date} · 클릭하여 항목 관리", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它与 ZCode 的每日/套餐福利领取见「活动福利」。": "일일 출석 제외: {list}. CodeArts의 일회성 보상은 아래 \"입문 혜택\"에 있으며, CodeArts와 ZCode의 일일/플랜 혜택 수령은 \"활동 혜택\"에서 볼 수 있습니다.", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。": "일일 출석 제외: {list}. CodeArts의 일회성 보상은 아래 \"입문 혜택\"에, CodeArts의 일일 혜택은 바로 위 \"일일 출석\"에 있습니다. ZCode의 플랜 수령은 \"활동 혜택\"에서 볼 수 있습니다.", "与国内版同一套任务接口 · 站点不同": "중국판과 동일한 작업 인터페이스 · 사이트만 다름", "今天已领 {n} 份": "오늘 {n}건 받음", "今天未领": "오늘 미수령", "今天(北京时间 {day})已领取 · 官方确认到账 {n} 项 · 明天可再领": "오늘(베이징 시간 {day}) 수령 완료 · 공식 확인 반영 {n}건 · 내일 다시 수령 가능", "今日已执行": "오늘 실행됨", "今日已派出": "오늘 이미 출발", "今日已领取": "오늘 수령 완료", "今日签到": "오늘 출석", "今日签到进度": "오늘 출석 진행률", "从清单里移除": "목록에서 제거", "保活": "키핑", "保活 {n}": "키핑 {n}", "保活+领取": "키핑+수령", "保活中…": "키핑 중…", "保活模型链": "키핑 모델 체인", "全选": "전체 선택", "全部 {n} 家": "전체 {n}곳", "全部完成": "모두 완료", "全部查询": "전체 조회", "全部结果": "모든 결과", "全部账号均可签到": "모든 계정이 출석할 수 있습니다", "全部领取": "전체 수령 완료", "关着:所有条目都不执行": "꺼짐: 어떤 항목도 실행되지 않습니다", "删除失败:{error}": "삭제 실패: {error}", "删除定时条目": "예약 항목 삭제", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活并在网页通道建会话点亮日活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "예정 시각에 체크한 제공자의 사용 가능한 계정을 자동으로 출석 체크합니다 (WorkBuddy 국내판은 매일 출석 API, 국제판은 데일리 액티비티 퀘스트: 이벤트 탐색 → 데일리 액티브 보상 수령 → 무료 모델로 킵얼라이브하고 웹 채널로 세션을 열어 일일 활성 점등. 소浣熊(샤오환슝)은 데스크톱 일일 포인트 경로, AutoClaw 는 공식 클라이언트의 매일 출석 작업. Qoder 중국판은 이벤트 수령 방식이며 이벤트를 배정받지 못한 계정은 중립적 안내를 받습니다. Qoder 국제판은 기기 리스크 관리 신원(로컬 Qoder 클라이언트 / qodercli 의 UMID 컴포넌트가 생성)을 먼저 갖춰야 \"매일 100 Credits\" 이벤트가 보이며, 컴포넌트가 없으면 오류 대신 명확한 설명을 받습니다. Trae 는 SOLO 의 매일 출석 포인트를 수령하고(자정 리셋), 업스트림이 이벤트를 열지 않았거나 자격 증명에 기기 번호가 없으면 오류 대신 \"미수령 + 사유\"를 받습니다. Loomy 는 매일 계정별 첫 로그인 포인트를 한 번 채우고, KukuAI 는 \"무료 포인트\" 일일 작업을 수령하며, CodeArts 는 당일 운영 혜택을 수령합니다(수령 → 확인 → 재조회 검증. 자동 실행은 하루 6 회 / 10 분 간격 쓰기 요청 제한 내). OfficeAce 는 당일 모든 보상을 한 번에 수령합니다 (매일 출석 + 신규 혜택)). 예정 시각을 놓쳐도 부팅 후 자동으로 보충하므로 전원이 꺼져 있어도 누락되지 않습니다. 각사 출석 API 는 멱등하여 반복 실행해도 중복 수령되지 않습니다.", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "정해진 시각이 되면 선택한 제공자의 사용 가능한 계정으로 자동 출석합니다 (WorkBuddy 중국판은 매일 출석 체크 인터페이스, 국제판은 일일 활성 작업으로 진행합니다: 이벤트 탐지, 일일 활성 보상 수령, 무료 모델로 키핑; 小浣熊은 데스크톱 앱 일일 포인트 경로를 사용하고; AutoClaw는 공식 클라이언트의 매일 출석 작업을 사용합니다; Qoder 중국판은 이벤트 수령으로 진행하며, 이벤트가 배정되지 않은 계정은 중립적인 안내를 받습니다; Qoder 국제판은 기기 리스크 관리 신원(로컬 Qoder 클라이언트 / qodercli의 UMID 컴포넌트로 생성)을 먼저 갖춰야 「매일 100 Credits」 이벤트가 보이며, 컴포넌트가 없으면 오류 대신 명확한 설명이 표시됩니다; Trae는 SOLO의 매일 출석 포인트를 수령하며(자연일 0시 기준 초기화), 업스트림이 해당 계정에 이벤트를 열지 않았거나 자격 증명에 기기 번호가 없으면 오류 대신 「미수령 + 구체적 사유」가 표시됩니다; Loomy는 매일 계정당 최초 로그인 포인트를 한 번 적립합니다; KukuAI는 「무료 포인트 받기」 일일 작업을 수령합니다; CodeArts는 당일 운영 혜택을 수령합니다(수령 → 확인 → 재조회 검증, 자동 실행은 하루 6회 / 간격 10분의 쓰기 요청 제한이 적용됩니다); OfficeAce 는 그날의 보상을 한 번의 동작으로 일괄 수령합니다(매일 출석 + 신규 혜택)). 시각을 놓치면 부팅 후 자동으로 보충 출석하며, 당시 컴퓨터가 꺼져 있었다고 해서 빠지지 않습니다. 각 사의 출석 인터페이스는 멱등이므로 반복 실행해도 중복 수령되지 않습니다.", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "정해진 시각이 되면 선택한 제공자의 사용 가능한 계정으로 자동 출석합니다 (WorkBuddy 중국판은 매일 출석 체크 인터페이스, 국제판은 일일 활성 작업으로 진행합니다: 이벤트 탐지, 일일 활성 보상 수령, 무료 모델로 키핑; 小浣熊은 데스크톱 앱 일일 포인트 경로를 사용하고; AutoClaw는 공식 클라이언트의 매일 출석 작업을 사용합니다; Qoder 중국판은 이벤트 수령으로 진행하며, 이벤트가 배정되지 않은 계정은 중립적인 안내를 받습니다; Qoder 국제판은 기기 리스크 관리 신원(로컬 Qoder 클라이언트 / qodercli의 UMID 컴포넌트로 생성)을 먼저 갖춰야 「매일 100 Credits」 이벤트가 보이며, 컴포넌트가 없으면 오류 대신 명확한 설명이 표시됩니다; Trae는 SOLO의 매일 출석 포인트를 수령하며(자연일 0시 기준 초기화), 업스트림이 해당 계정에 이벤트를 열지 않았거나 자격 증명에 기기 번호가 없으면 오류 대신 「미수령 + 구체적 사유」가 표시됩니다; Loomy는 매일 계정당 최초 로그인 포인트를 한 번 적립합니다; KukuAI는 「무료 포인트 받기」 일일 작업을 수령합니다; OfficeAce 는 그날의 보상을 한 번의 동작으로 일괄 수령합니다(매일 출석 + 신규 혜택)). 시각을 놓치면 부팅 후 자동으로 보충 출석하며, 당시 컴퓨터가 꺼져 있었다고 해서 빠지지 않습니다. 각 사의 출석 인터페이스는 멱등이므로 반복 실행해도 중복 수령되지 않습니다.", "到点时逐个签到,已勾 {n} / {total} 家": "정해진 시각에 하나씩 출석합니다. 선택 {n} / {total}개", "北京时间 · 错过时点开机后会自动补签": "베이징 시간 · 시간을 놓쳐도 시작 후 자동으로 보완됩니다", "去领取": "수령하러 가기", "另有 {n} 个账号不参与每日签到": "그 외 {n}개 계정은 매일 출석 체크에 참여하지 않습니다", "只领取已完成任务的奖励(幂等,重复点不会重复入账)": "완료된 작업의 보상만 수령(멱등, 반복 클릭해도 중복 적립되지 않음)", "可完成": "완료 가능", "可派出": "출발 가능", "可选 · 缺省显示时刻": "선택 · 기본값은 시간 표시", "可领奖": "수령 가능", "后端未返回定时签到数据": "백엔드가 예약 데이터를 반환하지 않았습니다", "后端未返回签到中心数据": "백엔드가 출석 체크 센터 데이터를 반환하지 않았습니다", "国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。": "국제판 출석에는 기기 리스크 관리 신원을 생성할 UMID 컴포넌트가 필요합니다 (이 기기에서 감지되지 않음).", "在途": "여행 중", "填入缺省链": "기본 체인 채우기", "填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)": "시각과 출석 범위를 정한 뒤 「저장」을 누르세요. 이름은 비워도 됩니다(비우면 시각이 표시됩니다)", "失败 {n}": "실패 {n}", "套餐明细": "플랜 상세", "安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。": "Qoder 클라이언트 또는 qodercli를 설치하면 됩니다 (게이트웨이 재시작 불필요).", "安装中…": "설치 중…", "官方客户端的每日签到任务": "공식 클라이언트의 매일 출석 작업", "定时签到": "예약 출석", "定时签到已关闭": "예약 출석이 꺼졌습니다", "尚未查询": "아직 조회하지 않음", "尚未查询任务状态": "작업 상태를 아직 조회하지 않았습니다", "巡检一次": "한 번 점검", "已保存:{label}": "저장됨: {label}", "已保存:保活模型链": "저장됨: 키핑 모델 체인", "已保存:定时条目": "저장됨: 예약 항목", "已删除定时条目": "예약 항목이 삭제되었습니다", "已完成活跃上报(连登已点亮),但活动未开启(本次没有可领的奖励)": "활동 보고 완료(연속 출석 점등), but 캠페인이 진행 중이 아닙니다(이번에 받을 보상 없음)", "已抽 {n} 次": "{n}회 추첨함", "已有结果 {checked} / 共 {total} 个账号": "결과 있음 {checked} / 전체 {total}개 계정", "已签": "출석 완료", "已领 {claimed}/{total} · 累计 {earned} 积分": "수령 {claimed}/{total} · 누적 {earned} 포인트", "已领养": "입양 완료", "已领取": "수령 완료", "带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件": "기기 리스크 관리 신원을 갖춰 「매일 100 Credits」 수령 · UMID 컴포넌트 필요", "开学季执行失败:{error}": "개학 시즌 실행 실패: {error}", "开学季执行完成:{message}": "개학 시즌 실행 완료: {message}", "开学季活动": "개학 시즌 이벤트", "开着:启用的条目按各自时刻执行": "켜짐: 활성화된 항목이 각자의 시간에 실행됩니다", "待签": "출석 대기", "待领新手福利": "수령 대기 입문 혜택", "总开关": "메인 스위치", "总开关已关闭 · 在「定时签到」页打开": "메인 스위치가 꺼져 있습니다 · \"예약 출석\" 탭에서 켜세요", "成功 {n}": "성공 {n}", "成长任务": "성장 과제", "成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}": "성장 과제 완료(성공 {done}개, 실패 {failed}개): 크레딧 +{credit} 에너지 +{energy}", "成长任务执行失败:{error}": "성장 과제 실행 실패: {error}", "执行失败:{error}": "실행 실패: {error}", "执行并抽奖": "실행 및 추첨", "执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)": "개학 시즌 작업을 실행하고 추첨 기회를 모두 사용(공유/대화/전문가 이벤트)", "把缺省链填进清单,再点「保存」生效": "기본 체인을 목록에 채웁니다. 「저장」을 눌러야 반영됩니다", "抽奖机会 {n} 次": "추첨 기회 {n}회", "按住拖动调整顺序": "길게 눌러 드래그해 순서 변경", "按住拖动,调整这个模型在链里的顺序": "길게 눌러 드래그해 체인에서 이 모델의 순서를 조정합니다", "按序逐个尝试,共 {n} 个": "순서대로 시도, 총 {n}개", "按结果筛选": "결과로 필터", "探测 + 领取 + 保活 · 与定时签到同一套组合": "탐지 + 수령 + 키핑 · 예약 출석과 동일한 조합", "探测活动并领取日活奖励 · 不做保活": "이벤트를 탐지해 일일 활성 보상을 수령 · 키핑은 하지 않음", "接单 + 点亮 + 领奖(只处理已实测沉淀的任务)": "수락 + 점등 + 수령(실측으로 검증된 작업만 처리)", "搜账号 / 收益 / 错误 / 触发原因": "계정 / 수익 / 오류 / 트리거 사유 검색", "收起新建": "새 항목 닫기", "新建定时条目": "새 예약 항목", "新手福利": "입문 혜택", "新手福利有 {n} 项领取失败,可重试": "입문 혜택 {n}건의 수령에 실패했습니다. 재시도할 수 있습니다", "无": "없음", "无独立签到接口 · 每天替账号打一次首次登录积分": "독립 출석 인터페이스 없음 · 매일 계정당 최초 로그인 포인트를 한 번 적립", "无账号": "계정 없음", "暂不可领": "지금은 수령 불가", "暂不支持": "아직 미지원", "暂无启用的条目 · 点此添加": "활성화된 항목이 없습니다 · 클릭하여 추가", "有未保存的改动": "저장되지 않은 변경 사항이 있습니다", "未勾选提供商": "공급자 미선택", "未开启": "꺼짐", "未解锁": "잠김", "未领养(领养需先完成当日对话量)": "미입양(입양하려면 먼저 당일 대화량을 채워야 함)", "未领取": "미수령", "本地台账里今天还没领过;活动期内每天发一份新套餐": "로컬 기록상 오늘은 아직 받지 않았습니다. 이벤트 기간에는 매일 새 플랜이 한 건씩 지급됩니다", "本地台账里今天(北京时间)领过的套餐:{plans}": "로컬 기록상 오늘(베이징 시간) 받은 플랜: {plans}", "条启用": "개 활성화", "查询于 {time}": "조회 시각 {time}", "查询任务": "작업 조회", "查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)": "작업 트리를 조회하고 한 번에 점등/수령(개학 시즌과 고양이 여행 포함)", "查询失败:{error}": "조회 실패: {error}", "桌面端每日积分链路": "데스크톱 앱 일일 포인트 경로", "正在读取定时签到数据…": "예약 출석 데이터를 읽는 중…", "正在读取签到数据…": "출석 데이터를 읽는 중…", "每天 {time}": "매일 {time}", "每天 {time} · {scope}": "매일 {time} · {scope}", "每天时刻": "매일 시각", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "출석 실행마다 · 계정별 한 행 · 결과와 수익은 백엔드 기록 기준", "每日活动签到(+100 积分,已领则中性提示)": "일일 활동 체크인(+100 크레딧, 이미 받았으면 중립 안내)", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "매일 활성 작업 · 웹 채널 세션으로 일일 활성 달성, 포인트는 수령 시 적립 · 두 가지는 따로 실행 가능", "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "매일 로그인 포인트 · 회원 활동 함께 수령 · 게스트 계정은 포인트 체계 없음(건너뜀)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "일일 혜택 · 수령 → 확인 → 재조회 검증 · 베이징 시간 자연일 기준 초기화", "每日签到": "매일 출석 체크", "每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签": "항목별 시간과 범위 · 항목별 당일 중복 제거 · 시작 시 자동 보완", "每账号一条明细 · 点此查看「签到记录」": "계정별 한 행 · \"출석 기록\" 열기", "没有可领套餐的账号(ZCode 未添加)。": "수령할 플랜이 있는 계정이 없습니다 (ZCode 미추가).", "没有可领福利的账号(CodeArts / ZCode 未添加)。": "혜택을 수령할 수 있는 계정이 없습니다 (CodeArts / ZCode 미추가).", "没有支持成长任务的账号(需要 WorkBuddy 国内版)。": "성장 과제를 지원하는 계정이 없습니다(WorkBuddy 중국판 필요).", "没有支持新手福利的账号": "입문 혜택을 지원하는 계정이 없습니다", "没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。": "입문 혜택을 지원하는 계정이 없습니다 — 현재 Loomy, 小浣熊, CodeArts 세 곳입니다.", "没有符合筛选条件的签到记录": "필터 조건에 맞는 출석 기록이 없습니다", "没有需要执行的任务": "실행할 작업이 없습니다", "活动福利": "이벤트 혜택", "活动(campaign)领取 · 每天 10:00 刷新": "캠페인(campaign) 수령 · 매일 10:00 새로고침", "活跃上报与保活均未成功,且活动未开启": "활동 보고와 keep-alive 모두 실패했고 캠페인도 진행 중이 아닙니다", "活跃上报完成,但保活未成功且活动未开启": "활동 보고는 완료되었지만 keep-alive에 실패했고 캠페인도 진행 중이 아닙니다", "活跃上报完成,但活动未开启": "활동 보고 완료, but 캠페인이 진행 중이 아닙니다", "活跃上报成功": "활동 보고 성공", "活跃保活失败": "활성 키핑 실패", "活跃保活完成": "활성 키핑 완료", "活跃保活完成,但活动未开启": "활성 키핑 완료, 다만 이벤트는 열리지 않았습니다", "活跃保活完成,但活跃上报失败且活动未开启": "keep-alive는 완료되었지만 활동 보고에 실패했고 캠페인도 진행 중이 아닙니다", "添加条目": "항목 추가", "添加模型": "모델 추가", "清单为空 —— 保存后恢复缺省链": "목록이 비어 있습니다 — 저장하면 기본 체인으로 복원됩니다", "清单里已经有「{name}」": "목록에 \"{name}\"이(가) 이미 있습니다", "清空失败:{error}": "비우기 실패: {error}", "清空签到记录": "출석 기록 비우기", "点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领": "\"플랜 상세\"에서 어떤 플랜을 받았고 언제 적용되는지 확인하세요. 이벤트 기간에는 매일 다시 받을 수 있습니다", "点击行展开账号明细 · 单账号签到不受范围限制": "행을 클릭해 계정 상세 펼치기 · 개별 계정 출석은 범위 제한을 받지 않습니다", "独立链路 · 只提供手动入口 · 不进自动签到": "독립 경로 · 수동 진입점만 제공 · 자동 출석에는 포함되지 않음", "猫猫旅行": "고양이 여행", "猫猫旅行失败:{error}": "고양이 여행 실패: {error}", "猫猫旅行失败:{message}": "고양이 여행 실패: {message}", "猫猫旅行:{message}": "고양이 여행: {message}", "用免费模型与网页会话维持账号活跃 · 不领取奖励": "무료 모델과 웹 채널 세션으로 계정 활성 유지 · 보상은 수령하지 않음", "确定删除「{name}」?删除后到点不再执行。": "\"{name}\"을(를) 삭제할까요? 삭제 후 예약 시간에 실행되지 않습니다.", "确定清空全部签到记录?清空后无法恢复。": "모든 출석 기록을 비울까요? 비운 후에는 복구할 수 없습니다.", "确定清空当前筛选出的签到记录?清空后无法恢复。": "현재 필터된 출석 기록을 비울까요? 비운 후에는 복구할 수 없습니다.", "移除 {name}": "{name} 제거", "立即全部签到": "지금 전체 출석", "签到": "출석", "签到中…": "출석 중…", "签到中心分区": "출석 센터 구역", "签到失败:{error}": "출석 실패: {error}", "签到完成:{succeeded}/{total} 成功{active},{failed} 个失败": "출석 완료: {succeeded}/{total} 성공{active}, {failed}개 실패", "签到成功": "출석 성공", "签到提供商": "출석 제공자", "签到范围": "출석 범위", "签到触发时刻": "출석 실행 시각", "签到记录": "출석 기록", "签到记录时间范围": "출석 기록 시간 범위", "签到记录表": "출석 기록 표", "累计签到记录": "누적 출석 기록", "结算于 {time}": "결산 시각 {time}", "能量 {n}": "에너지 {n}", "腾讯每日签到接口 · 仅国内版": "텐센트 매일 출석 인터페이스 · 중국판 전용", "至少勾一家才能保存(不勾等于这条条目不签到)": "저장하려면 최소 한 곳을 선택하세요(선택하지 않으면 이 항목은 아무것도 출석하지 않습니다)", "读取定时签到数据失败:{error}": "예약 출석 데이터 로드 실패: {error}", "读取签到数据失败:{error}": "출석 데이터 읽기 실패: {error}", "读取签到记录失败:{error}": "출석 기록 로드 실패: {error}", "跳过 {n}": "건너뜀 {n}", "运营活动交付(领取 → 确认 → 回读核实)· 领的是套餐赠送积分": "운영 이벤트 전달 (수령 → 확인 → 재조회 검증) · 플랜 증정 포인트를 수령합니다", "还没执行过": "아직 실행된 적 없음", "还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。": "예약 항목이 아직 없습니다 — \"항목 추가\"로 만들거나 \"출석 센터\"에서 수동으로 출석하세요.", "还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。": "출석 기록이 아직 없습니다 — 수동 출석 또는 예약 실행 후 여기에 상세 내역이 표시됩니다.", "连登 {n} 天": "연속 로그인 {n}일", "部分": "일부", "重签": "재출석", "重试": "다시 시도", "限时体验套餐 · 领取需滑块验证码 · 逐份查看与领取": "기간 한정 체험 플랜 · 수령 시 슬라이더 인증 필요 · 항목별로 확인하고 수령", "限时体验套餐(start-plan),活动期内每天一份 · 领取需滑块验证码": "기간 한정 체험 플랜(start-plan), 이벤트 기간 내 매일 1회 · 수령 시 슬라이더 캡차 필요", "需真实操作": "실제 조작 필요", "非活动期": "이벤트 기간 아님", "项": "건", "领取": "수령", "领取中": "수령 중", "领取中…": "수령 중…", "领取免费 CodingPlan(按需:套餐有效时只回读额度)": "무료 CodingPlan 받기(필요 시에만: 플랜이 유효하면 할당량만 조회)", "领取失败": "수령 실패", "领取失败:{error}": "수령 실패: {error}", "(共 {n} 个账号)": "(전체 {n}개 계정)", "(能量 / 连登读数本次未取到)": "(에너지/연속 로그인 수치는 이번에 가져오지 못했습니다)", ",日活保活 {n} 个": ", 일일 활성 키핑 {n}개" }); diff --git a/desktop-tauri/ui/i18n/ko/requests.js b/desktop-tauri/ui/i18n/ko/requests.js index 2b39f018..68bdfde2 100644 --- a/desktop-tauri/ui/i18n/ko/requests.js +++ b/desktop-tauri/ui/i18n/ko/requests.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:ko/requests —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · {n} 进行中": " · {n} 처리 중", " · 会话粘住": " · 세션 고정", "

    无响应原文
    ": "
    응답 원문 없음
    ", "
    无法解析响应内容
    ": "
    응답 내용을 파싱할 수 없음
    ", "
    无法解析请求体(不是含 messages 数组的 JSON)
    ": "
    요청 본문을 파싱할 수 없음(messages 배열을 포함한 JSON이 아님)
    ", "
    无请求原文
    ": "
    요청 원문 없음
    ", "
    调用工具 {name}
    ": "
    도구 호출 {name}
    ", "响应原文": "응답 원문", "思考过程": "사고 과정", "请求原文": "요청 원문", "HTTP {status},但响应体阶段出错": "HTTP {status}이지만 응답 본문 단계에서 오류 발생", "HTTP {status},但响应体阶段出错:见错误列": "HTTP {status}이지만 응답 본문 단계에서 오류 발생: 오류 열 참조", "{duration} / 首帧 {first}": "{duration} / 첫 프레임 {first}", "{m}分{s}秒": "{m}분 {s}초", "{n}秒": "{n}초", "{s}秒{ms}ms": "{s}초{ms}ms", "↻ {n} 次": "↻ {n}회", "↻ 重试 {n} 次": "↻ 재시도 {n}회", "✅ 压缩完成": "✅ 압축 완료", "「仅看进行中」开启时,状态固定为进行中": "「처리 중만 보기」가 켜져 있으면 상태는 처리 중으로 고정됩니다", "上游模型繁忙,请求已排进上游队列;网关正按上游建议的时长等待后重发(不是登录态或额度问题)": "업스트림 모델이 혼잡하여 요청이 업스트림 대기열에 들어갔습니다. 게이트웨이는 업스트림이 권장한 시간만큼 기다린 뒤 다시 전송합니다(로그인 상태나 할당량 문제가 아닙니다)", "上游状态码: {status}": "업스트림 상태 코드: {status}", "上游请求已发出,正在等第一个字节到达(模型的思考时间也在这段)": "업스트림 요청을 보냈고 첫 바이트가 도착하기를 기다리는 중입니다(모델의 사고 시간도 이 구간에 포함됩니다)", "上游返回错误": "업스트림이 오류를 반환했습니다", "下游请求的模型:{model}": "다운스트림 요청 모델: {model}", "仅清空报文原文": "원문만 비우기", "仅看进行中": "처리 중만 보기", "令牌": "Token", "体积": "크기", "保留统计行与报表,只抹掉请求 / 响应正文。清理后详情弹窗的「预览对话」将无原文可看,统计数字分毫不动。": "통계 행과 리포트는 유지하고 요청 / 응답 본문만 지웁니다. 정리 후 상세 대화상자의 「대화 미리보기」에서는 볼 원문이 없어지고, 통계 수치는 전혀 변하지 않습니다.", "全部删除": "모두 삭제", "全部提供商": "모든 제공자", "全部模型": "모든 모델", "全部状态": "모든 상태", "共 {n} 次尝试": "총 {n}회 시도", "共 {n} 次尝试,最终由 {provider} 承载": "총 {n}회 시도, 최종적으로 {provider}에서 처리됨", "其它": "기타", "内容按原样保存,请求头中的凭据字段已替换为 [redacted]": "내용은 원본 그대로 저장되며, 요청 헤더의 자격 증명 필드는 [redacted]로 대체되었습니다", "内部重试": "내부 재시도", "切换路径:": "전환 경로: ", "列表数据已刷新,请重试(关闭后重新打开这条详情)": "목록 데이터가 갱신되었습니다. 다시 시도하세요(닫은 후 이 상세를 다시 열어 주세요)", "删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。": "로그 레코드 자체를 삭제합니다. 현재 필터가 있으면 적중한 항목만 삭제하며 일별 집계 리포트는 영향을 받지 않습니다. 필터가 없으면 전부 비우고 리포트를 초기화합니다. 이 방식은 복구할 수 없습니다.", "压缩中…": "압축 중…", "压缩失败:{message}": "압축 실패: {message}", "压缩已在后台开始,完成后会自动提示": "압축이 백그라운드에서 시작되었습니다. 완료되면 자동으로 알려드립니다", "压缩已在进行中": "압축이 이미 진행 중입니다", "压缩已在进行中,接上它的进度等待完成": "압축이 이미 진행 중입니다. 기존 진행 상황에 이어 완료를 기다립니다", "压缩数据库": "DB 압축", "压缩正在后台执行,完成后自动恢复": "압축이 백그라운드에서 실행 중입니다. 완료되면 자동으로 복원됩니다", "原始报文": "원시 페이로드", "另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)": "그 외 {missing}회 시도는 상세가 기록되지 않았습니다(가장 이른 {kept}건만 보관)", "另有 {n} 个词命中": "그 외 {n}개 단어 적중", "只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)": "포워딩 중인 요청만 표시합니다(상태 열은 단계별로 표시: 연결 중 / 응답 대기 / 응답 중 / 재시도 중, 소요 시간 열은 경과 시간 표시)", "只看失败": "실패만 보기", "只看成功": "성공만 보기", "只看进行中": "처리 중만 보기", "命中了敏感词": "민감 단어 적중", "命中的敏感词": "적중한 민감 단어", "响应中": "응답 중", "响应体": "응답 본문", "响应头": "응답 헤더", "回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)": "삭제된 데이터가 차지한 디스크 공간 회수(checkpoint + VACUUM, 백그라운드 실행)", "在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文": "설정 → 일반에서 「디버그 모드」를 켜야 새로 발생하는 요청부터 페이로드가 저장됩니다", "失败请求不记录用量": "실패한 요청은 사용량을 기록하지 않음", "失败({status}):{error}": "실패({status}): {error}", "失败:{error}": "실패: {error}", "实际发给上游 {bytes} 字节": "업스트림에 실제 전송한 {bytes} 바이트", "客户端": "클라이언트", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "{count}건의 로그를 삭제합니다 · 그중 {raw}건은 페이로드 원문이 남아 있음 · DB 크기 {db}", "尝试 {n} ·": "시도 {n} ·", "尝试明细": "시도 상세", "工具结果": "도구 결과", "工具结果 · {id}": "도구 결과 · {id}", "工具结果图片": "도구 결과 이미지", "已删除 {count} 条": "{count}건 삭제됨", "已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」": "중단 요청 접수됨: 업스트림 연결이 끊어졌으며, 목록은 잠시 후 「요청이 수동으로 중단됨」으로 갱신됩니다", "已抹掉 {count} 条报文原文": "{count}건의 페이로드 원문을 지웠습니다", "已用时(请求仍在转发中)": "경과 시간(요청이 아직 포워딩 중)", "成功": "성공", "成功({status})": "성공({status})", "执行清理": "정리 실행", "报文超过单条上限,请求体 / 响应体已按上限截断": "페이로드가 단일 항목 상한을 초과하여 요청 본문 / 응답 본문이 상한에 맞춰 잘렸습니다", "抹掉原文": "원문 지우기", "按提供商筛选": "제공자별 필터", "按提供商筛选(只列日志里出现过的)": "제공자별 필터(로그에 나타난 제공자만 표시)", "按时间倒序,全量保留": "최신순 정렬, 전량 보존", "按模型筛选": "모델별 필터", "按模型筛选(只列日志里出现过的;精确匹配模型名)": "모델별 필터(로그에 나타난 모델만 표시; 모델 이름 정확히 일치)", "按请求结果筛选": "요청 결과별 필터", "排队中": "대기 중", "提供商 / 账号": "제공자 / 계정", "提供商: {provider}": "제공자: {provider}", "提示": "비고", "敏": "민", "敏感词": "민감 단어", "断开上游连接,明细记为「请求已被手动终止」": "업스트림 연결을 끊고 상세에 「요청이 수동으로 중단됨」으로 기록됩니다", "无报文原文(未开启「调试模式」,或已被清理)": "원문 페이로드 없음(디버그 모드가 꺼져 있었거나 정리되었음)", "无结果记录": "결과 미기록", "时间": "시각", "暂无请求日志,网关还没有转发过请求": "아직 요청 로그가 없습니다. 게이트웨이가 아직 요청을 포워딩한 적이 없습니다", "最终承载:{picked}": "최종 처리: {picked}", "本轮尝试失败,正在退避等待或切换到下一个账号": "이번 시도가 실패하여 백오프 대기 중이거나 다음 계정으로 전환 중입니다", "来源客户端:{client}": "요청 클라이언트: {client}", "查看该请求的上游原始报文": "이 요청의 업스트림 원시 페이로드 보기", "模型": "모델", "模型测试": "모델 테스트", "模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计": "모델 관리 페이지 작업 열의 「테스트」로 시작된 요청: 실제 요청과 같은 포워딩 경로를 사용하지만 리포트 통계에는 포함되지 않습니다", "正在加载请求日志…": "요청 로그 불러오는 중…", "正在统计…": "집계 중…", "正文超过单条上限,可能已被截断": "본문이 단일 항목 상한을 초과하여 잘렸을 수 있습니다", "没有符合筛选条件的请求": "필터 조건에 맞는 요청이 없습니다", "清理": "정리", "清理中…": "정리 중…", "清理失败:{message}": "정리 실패: {message}", "清理方式": "정리 방식", "清理请求日志": "요청 로그 정리", "生成速度:输出 token ÷ 流式段用时(总用时 − 首响);流式段不足总用时一半时按总用时计": "생성 속도: 출력 토큰 ÷ 스트리밍 시간(소요 시간 − 첫 응답). 스트리밍 시간이 소요 시간의 절반 미만이면 소요 시간으로 계산", "用时": "소요 시간", "用量": "사용량", "确定对全部请求日志执行「{label}」?将处理 {count} 条,此操作无法恢复。": "전체 요청 로그에 「{label}」 작업을 실행할까요? {count}건을 처리하며, 이 작업은 복구할 수 없습니다.", "确定按当前筛选执行「{label}」?将处理 {count} 条,此操作无法恢复。": "현재 필터 기준으로 「{label}」 작업을 실행할까요? {count}건을 처리하며, 이 작업은 복구할 수 없습니다.", "确定终止这条正在转发的请求?
    上游连接会立即断开,明细将记为「请求已被手动终止」。": "포워딩 중인 이 요청을 중단할까요?
    업스트림 연결이 즉시 끊어지며, 상세에 「요청이 수동으로 중단됨」으로 기록됩니다.", "等待响应": "응답 대기", "策略 {strategy} · 会话粘性 {sticky}{hit}": "전략 {strategy} · 세션 고정 {sticky}{hit}", "终止": "중단", "终止失败:{error}": "중단 실패: {error}", "终止请求": "요청 중단", "结果": "결과", "缓存读取: - / 命中率: -": "캐시 읽기: - / 적중률: -", "缓存读取: {cache} / 命中率: {rate}": "캐시 읽기: {cache} / 적중률: {rate}", "耗时": "소요 시간", "被跳过:": "건너뜀: ", "识别到 User-Agent,但不属于已知的客户端类型:{ua}": "User-Agent가 있지만 알려진 클라이언트 유형이 아닙니다: {ua}", "详情": "상세", "详情内容": "상세 내용", "请求体": "요청 본문", "请求图片": "요청 이미지", "请求在任何账号接手之前就失败了": "요청이 어떤 계정도 넘겨받기 전에 실패했습니다", "请求头": "요청 헤더", "请求已受理,正在选路、取凭证、建立上游连接": "요청이 접수되었습니다. 경로를 선택하고 자격 증명을 가져와 업스트림 연결을 수립하는 중입니다", "请求日志列表": "요청 로그 목록", "请求日志时间范围": "요청 로그 시간 범위", "请求日志表": "요청 로그 표", "请求正在转发中,用时列显示的是已用时": "요청이 포워딩 중이며, 소요 시간 열에는 경과 시간이 표시됩니다", "请求详情": "요청 상세", "读取请求日志失败,详见控制台": "요청 로그를 읽지 못했습니다. 자세한 내용은 콘솔을 확인하세요", "路由回放": "라우팅 리플레이", "转发到上游的模型:{model}": "업스트림으로 포워딩된 모델: {model}", "轮次": "회차", "输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}": "입력 {input} · 출력 {output} · 합계 {total} · 캐시 읽기 {cache}", "这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计": "이 상세는 모델 테스트(수동 실행)입니다: 실제 포워딩 경로를 사용하지만 리포트 통계에는 포함되지 않습니다", "这条明细没有记录提供商(旧数据或请求未走到转发)": "이 상세에는 제공자가 기록되지 않았습니다(오래된 데이터이거나 요청이 포워딩 단계까지 가지 못함)", "这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)": "이 기록에는 요청 클라이언트가 없습니다(이전 데이터, 모델 테스트 또는 User-Agent 없는 요청)", "这条记录命中了敏感词,但没有留下命中明细": "이 레코드는 민감 단어에 적중했지만 적중 상세가 남아 있지 않습니다", "这条记录的尝试明细未采集(该字段上线前的旧数据,或请求在转发前就失败)": "이 레코드의 시도 상세가 수집되지 않았습니다(이 필드 도입 전의 오래된 데이터이거나 요청이 포워딩 전에 실패함)", "这条请求没有保存这一段报文": "이 요청은 이 구간의 페이로드를 저장하지 않았습니다", "进入「{phase}」阶段已持续 {time}": "「{phase}」 단계에 진입한 뒤 {time} 경과", "进行中": "처리 중", "进行中…": "처리 중…", "连接中": "연결 중", "重试 {n}": "재시도 {n}", "重试中": "재시도 중", "重试次数": "재시도 횟수", "预览对话": "대화 미리보기", "预览读取失败": "미리보기 읽기 실패", "预览读取失败:{error}": "미리보기 읽기 실패: {error}", "首响 {time}": "첫 응답 {time}", "首响:上游首帧到达的耗时": "첫 응답: 업스트림 첫 프레임 도착까지 걸린 시간", "首响:上游首帧到达的耗时(请求仍在转发中)": "첫 응답: 업스트림 첫 프레임 도착까지 걸린 시간(요청이 아직 포워딩 중)", "首帧已到,上游内容正在下发": "첫 프레임이 도착하여 업스트림 콘텐츠를 내려보내는 중입니다", "(请求仍在转发中)": "(요청이 아직 포워딩 중)", "(账号:{account})": "(계정: {account})", ",{n}毫秒后重试": ", {n}밀리초 후 재시도", ",{n}秒后重试": ", {n}초 후 재시도" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · {n} 进行中": " · {n} 처리 중", " · 会话粘住": " · 세션 고정", "
    无响应原文
    ": "
    응답 원문 없음
    ", "
    无法解析响应内容
    ": "
    응답 내용을 파싱할 수 없음
    ", "
    无法解析请求体(不是含 messages 数组的 JSON)
    ": "
    요청 본문을 파싱할 수 없음(messages 배열을 포함한 JSON이 아님)
    ", "
    无请求原文
    ": "
    요청 원문 없음
    ", "
    调用工具 {name}
    ": "
    도구 호출 {name}
    ", "响应原文": "응답 원문", "思考过程": "사고 과정", "请求原文": "요청 원문", "HTTP {status},但响应体阶段出错": "HTTP {status}이지만 응답 본문 단계에서 오류 발생", "HTTP {status},但响应体阶段出错:见错误列": "HTTP {status}이지만 응답 본문 단계에서 오류 발생: 오류 열 참조", "{duration} / 首帧 {first}": "{duration} / 첫 프레임 {first}", "{m}分{s}秒": "{m}분 {s}초", "{n}秒": "{n}초", "{s}秒{ms}ms": "{s}초{ms}ms", "↻ {n} 次": "↻ {n}회", "↻ 重试 {n} 次": "↻ 재시도 {n}회", "✅ 压缩完成": "✅ 압축 완료", "「仅看进行中」开启时,状态固定为进行中": "「처리 중만 보기」가 켜져 있으면 상태는 처리 중으로 고정됩니다", "上游模型繁忙,请求已排进上游队列;网关正按上游建议的时长等待后重发(不是登录态或额度问题)": "업스트림 모델이 혼잡하여 요청이 업스트림 대기열에 들어갔습니다. 게이트웨이는 업스트림이 권장한 시간만큼 기다린 뒤 다시 전송합니다(로그인 상태나 할당량 문제가 아닙니다)", "上游状态码: {status}": "업스트림 상태 코드: {status}", "上游请求已发出,正在等第一个字节到达(模型的思考时间也在这段)": "업스트림 요청을 보냈고 첫 바이트가 도착하기를 기다리는 중입니다(모델의 사고 시간도 이 구간에 포함됩니다)", "上游返回错误": "업스트림이 오류를 반환했습니다", "下游请求的模型:{model}": "다운스트림 요청 모델: {model}", "仅清空报文原文": "원문만 비우기", "仅看进行中": "처리 중만 보기", "令牌": "Token", "体积": "크기", "保留统计行与报表,只抹掉请求 / 响应正文。清理后详情弹窗的「预览对话」将无原文可看,统计数字分毫不动。": "통계 행과 리포트는 유지하고 요청 / 응답 본문만 지웁니다. 정리 후 상세 대화상자의 「대화 미리보기」에서는 볼 원문이 없어지고, 통계 수치는 전혀 변하지 않습니다.", "全部删除": "모두 삭제", "全部提供商": "모든 제공자", "全部模型": "모든 모델", "全部状态": "모든 상태", "共 {n} 次尝试": "총 {n}회 시도", "共 {n} 次尝试,最终由 {provider} 承载": "총 {n}회 시도, 최종적으로 {provider}에서 처리됨", "其它": "기타", "内容按原样保存,请求头中的凭据字段已替换为 [redacted]": "내용은 원본 그대로 저장되며, 요청 헤더의 자격 증명 필드는 [redacted]로 대체되었습니다", "内部重试": "내부 재시도", "切换路径:": "전환 경로: ", "列表数据已刷新,请重试(关闭后重新打开这条详情)": "목록 데이터가 갱신되었습니다. 다시 시도하세요(닫은 후 이 상세를 다시 열어 주세요)", "删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。": "로그 레코드 자체를 삭제합니다. 현재 필터가 있으면 적중한 항목만 삭제하며 일별 집계 리포트는 영향을 받지 않습니다. 필터가 없으면 전부 비우고 리포트를 초기화합니다. 이 방식은 복구할 수 없습니다.", "压缩中…": "압축 중…", "压缩失败:{message}": "압축 실패: {message}", "压缩已在后台开始,完成后会自动提示": "압축이 백그라운드에서 시작되었습니다. 완료되면 자동으로 알려드립니다", "压缩已在进行中": "압축이 이미 진행 중입니다", "压缩已在进行中,接上它的进度等待完成": "압축이 이미 진행 중입니다. 기존 진행 상황에 이어 완료를 기다립니다", "压缩数据库": "DB 압축", "压缩正在后台执行,完成后自动恢复": "압축이 백그라운드에서 실행 중입니다. 완료되면 자동으로 복원됩니다", "原始报文": "원시 페이로드", "另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)": "그 외 {missing}회 시도는 상세가 기록되지 않았습니다(가장 이른 {kept}건만 보관)", "另有 {n} 个词命中": "그 외 {n}개 단어 적중", "只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)": "포워딩 중인 요청만 표시합니다(상태 열은 단계별로 표시: 연결 중 / 응답 대기 / 응답 중 / 재시도 중, 소요 시간 열은 경과 시간 표시)", "只看失败": "실패만 보기", "只看成功": "성공만 보기", "只看进行中": "처리 중만 보기", "命中了敏感词": "민감 단어 적중", "命中的敏感词": "적중한 민감 단어", "响应中": "응답 중", "响应体": "응답 본문", "响应头": "응답 헤더", "回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)": "삭제된 데이터가 차지한 디스크 공간 회수(checkpoint + VACUUM, 백그라운드 실행)", "在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文": "설정 → 일반에서 「디버그 모드」를 켜야 새로 발생하는 요청부터 페이로드가 저장됩니다", "失败请求不记录用量": "실패한 요청은 사용량을 기록하지 않음", "失败({status}):{error}": "실패({status}): {error}", "失败:{error}": "실패: {error}", "实际下发给客户端 {bytes} 字节": "클라이언트에 실제로 전송한 {bytes} 바이트", "实际发给上游 {bytes} 字节": "업스트림에 실제 전송한 {bytes} 바이트", "客户端": "클라이언트", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "{count}건의 로그를 삭제합니다 · 그중 {raw}건은 페이로드 원문이 남아 있음 · DB 크기 {db}", "尝试 {n} ·": "시도 {n} ·", "尝试明细": "시도 상세", "工具结果": "도구 결과", "工具结果 · {id}": "도구 결과 · {id}", "工具结果图片": "도구 결과 이미지", "已下发": "전송됨", "已删除 {count} 条": "{count}건 삭제됨", "已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」": "중단 요청 접수됨: 업스트림 연결이 끊어졌으며, 목록은 잠시 후 「요청이 수동으로 중단됨」으로 갱신됩니다", "已抹掉 {count} 条报文原文": "{count}건의 페이로드 원문을 지웠습니다", "已用时(请求仍在转发中)": "경과 시간(요청이 아직 포워딩 중)", "成功": "성공", "成功({status})": "성공({status})", "执行清理": "정리 실행", "报文超过单条上限,请求体 / 响应体已按上限截断": "페이로드가 단일 항목 상한을 초과하여 요청 본문 / 응답 본문이 상한에 맞춰 잘렸습니다", "抹掉原文": "원문 지우기", "按提供商筛选": "제공자별 필터", "按提供商筛选(只列日志里出现过的)": "제공자별 필터(로그에 나타난 제공자만 표시)", "按时间倒序,全量保留": "최신순 정렬, 전량 보존", "按模型筛选": "모델별 필터", "按模型筛选(只列日志里出现过的;精确匹配模型名)": "모델별 필터(로그에 나타난 모델만 표시; 모델 이름 정확히 일치)", "按请求结果筛选": "요청 결과별 필터", "排队中": "대기 중", "提供商 / 账号": "제공자 / 계정", "提供商: {provider}": "제공자: {provider}", "提示": "비고", "敏": "민", "敏感词": "민감 단어", "断开上游连接,明细记为「请求已被手动终止」": "업스트림 연결을 끊고 상세에 「요청이 수동으로 중단됨」으로 기록됩니다", "无报文原文(未开启「调试模式」,或已被清理)": "원문 페이로드 없음(디버그 모드가 꺼져 있었거나 정리되었음)", "无结果记录": "결과 미기록", "时间": "시각", "暂无请求日志,网关还没有转发过请求": "아직 요청 로그가 없습니다. 게이트웨이가 아직 요청을 포워딩한 적이 없습니다", "最终承载:{picked}": "최종 처리: {picked}", "本轮尝试失败,正在退避等待或切换到下一个账号": "이번 시도가 실패하여 백오프 대기 중이거나 다음 계정으로 전환 중입니다", "来源客户端:{client}": "요청 클라이언트: {client}", "查看该请求的上游原始报文": "이 요청의 업스트림 원시 페이로드 보기", "模型": "모델", "模型测试": "모델 테스트", "模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计": "모델 관리 페이지 작업 열의 「테스트」로 시작된 요청: 실제 요청과 같은 포워딩 경로를 사용하지만 리포트 통계에는 포함되지 않습니다", "正在加载请求日志…": "요청 로그 불러오는 중…", "正在统计…": "집계 중…", "正文超过单条上限,可能已被截断": "본문이 단일 항목 상한을 초과하여 잘렸을 수 있습니다", "没有符合筛选条件的请求": "필터 조건에 맞는 요청이 없습니다", "清理": "정리", "清理中…": "정리 중…", "清理失败:{message}": "정리 실패: {message}", "清理方式": "정리 방식", "清理请求日志": "요청 로그 정리", "生成速度:输出 token ÷ 流式段用时(总用时 − 首响);流式段不足总用时一半时按总用时计": "생성 속도: 출력 토큰 ÷ 스트리밍 시간(소요 시간 − 첫 응답). 스트리밍 시간이 소요 시간의 절반 미만이면 소요 시간으로 계산", "用时": "소요 시간", "用量": "사용량", "确定对全部请求日志执行「{label}」?将处理 {count} 条,此操作无法恢复。": "전체 요청 로그에 「{label}」 작업을 실행할까요? {count}건을 처리하며, 이 작업은 복구할 수 없습니다.", "确定按当前筛选执行「{label}」?将处理 {count} 条,此操作无法恢复。": "현재 필터 기준으로 「{label}」 작업을 실행할까요? {count}건을 처리하며, 이 작업은 복구할 수 없습니다.", "确定终止这条正在转发的请求?
    上游连接会立即断开,明细将记为「请求已被手动终止」。": "포워딩 중인 이 요청을 중단할까요?
    업스트림 연결이 즉시 끊어지며, 상세에 「요청이 수동으로 중단됨」으로 기록됩니다.", "等待响应": "응답 대기", "策略 {strategy} · 会话粘性 {sticky}{hit}": "전략 {strategy} · 세션 고정 {sticky}{hit}", "终止": "중단", "终止失败:{error}": "중단 실패: {error}", "终止请求": "요청 중단", "结果": "결과", "缓存读取: - / 命中率: -": "캐시 읽기: - / 적중률: -", "缓存读取: {cache} / 命中率: {rate}": "캐시 읽기: {cache} / 적중률: {rate}", "耗时": "소요 시간", "被跳过:": "건너뜀: ", "识别到 User-Agent,但不属于已知的客户端类型:{ua}": "User-Agent가 있지만 알려진 클라이언트 유형이 아닙니다: {ua}", "详情": "상세", "详情内容": "상세 내용", "请求体": "요청 본문", "请求图片": "요청 이미지", "请求在任何账号接手之前就失败了": "요청이 어떤 계정도 넘겨받기 전에 실패했습니다", "请求头": "요청 헤더", "请求已受理,正在选路、取凭证、建立上游连接": "요청이 접수되었습니다. 경로를 선택하고 자격 증명을 가져와 업스트림 연결을 수립하는 중입니다", "请求日志列表": "요청 로그 목록", "请求日志时间范围": "요청 로그 시간 범위", "请求日志表": "요청 로그 표", "请求正在转发中,用时列显示的是已用时": "요청이 포워딩 중이며, 소요 시간 열에는 경과 시간이 표시됩니다", "请求详情": "요청 상세", "读取请求日志失败,详见控制台": "요청 로그를 읽지 못했습니다. 자세한 내용은 콘솔을 확인하세요", "路由回放": "라우팅 리플레이", "转发到上游的模型:{model}": "업스트림으로 포워딩된 모델: {model}", "轮次": "회차", "输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}": "입력 {input} · 출력 {output} · 합계 {total} · 캐시 읽기 {cache}", "这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计": "이 상세는 모델 테스트(수동 실행)입니다: 실제 포워딩 경로를 사용하지만 리포트 통계에는 포함되지 않습니다", "这条明细没有记录提供商(旧数据或请求未走到转发)": "이 상세에는 제공자가 기록되지 않았습니다(오래된 데이터이거나 요청이 포워딩 단계까지 가지 못함)", "这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)": "이 기록에는 요청 클라이언트가 없습니다(이전 데이터, 모델 테스트 또는 User-Agent 없는 요청)", "这条记录命中了敏感词,但没有留下命中明细": "이 레코드는 민감 단어에 적중했지만 적중 상세가 남아 있지 않습니다", "这条记录的尝试明细未采集(该字段上线前的旧数据,或请求在转发前就失败)": "이 레코드의 시도 상세가 수집되지 않았습니다(이 필드 도입 전의 오래된 데이터이거나 요청이 포워딩 전에 실패함)", "这条请求没有保存这一段报文": "이 요청은 이 구간의 페이로드를 저장하지 않았습니다", "进入「{phase}」阶段已持续 {time}": "「{phase}」 단계에 진입한 뒤 {time} 경과", "进行中": "처리 중", "进行中…": "처리 중…", "连接中": "연결 중", "重试 {n}": "재시도 {n}", "重试中": "재시도 중", "重试次数": "재시도 횟수", "预览对话": "대화 미리보기", "预览读取失败": "미리보기 읽기 실패", "预览读取失败:{error}": "미리보기 읽기 실패: {error}", "首响 {time}": "첫 응답 {time}", "首响:上游首帧到达的耗时": "첫 응답: 업스트림 첫 프레임 도착까지 걸린 시간", "首响:上游首帧到达的耗时(请求仍在转发中)": "첫 응답: 업스트림 첫 프레임 도착까지 걸린 시간(요청이 아직 포워딩 중)", "首帧已到,上游内容正在下发": "첫 프레임이 도착하여 업스트림 콘텐츠를 내려보내는 중입니다", "(请求仍在转发中)": "(요청이 아직 포워딩 중)", "(账号:{account})": "(계정: {account})", ",{n}毫秒后重试": ", {n}밀리초 후 재시도", ",{n}秒后重试": ", {n}초 후 재시도" }); diff --git a/desktop-tauri/ui/i18n/pt-BR/add-account.js b/desktop-tauri/ui/i18n/pt-BR/add-account.js index e1a4972c..00529c10 100644 --- a/desktop-tauri/ui/i18n/pt-BR/add-account.js +++ b/desktop-tauri/ui/i18n/pt-BR/add-account.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:pt-BR/add-account —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · 模型 {head}{rest}": " · Modelos: {head}{rest}", " 等 {n} 个": " ({n} no total)", "1// 开头的 Google refresh token(也可直接粘贴 refresh_token=… 整行)": "Google refresh token começando com 1// (também pode colar a linha refresh_token=… inteira)", "11 位大陆手机号": "Celular da China continental (11 dígitos)", "1~64 个字符,账号列表里按它分组显示": "1 a 64 caracteres; na lista de contas, as contas são agrupadas por ele", "6 位数字": "6 dígitos", "
  • 没有可自动领的活动
  • ": "
  • Nenhuma campanha pode ser resgatada automaticamente
  • ", "

    领到的是套餐赠送积分,不会增加福利模型的 token 池;": "

    O que você recebe são pontos de brinde do plano, que não aumentam o pool de tokens dos modelos de benefício; ", "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "O endpoint compatível com Anthropic é https://api.deepseek.com/anthropic (preencha-o ao trocar o protocolo para anthropic)", "Anthropic 协议填根地址,不要带 /v1": "No protocolo Anthropic, preencha o endereço raiz, sem /v1", "Antigravity": "Antigravity", "AutoClaw 国内版": "AutoClaw edição da China", "AutoClaw 国际版": "AutoClaw edição internacional", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS é obrigatório; cole o **Cookie completo** (um editor de Cookie exporta com um clique) — o **PTOKEN deve ser mantido**, pois o gateway o usa para trocar com o Baidu por um token de sessão de negócio do KukuAI; sem ele, a atualização de modelos reporta \"não conectado\". São aceitos o cabeçalho Cookie inteiro, um array JSON exportado por um editor de Cookie, ou um objeto {BDUSS, STOKEN}. O KukuAI não tem mecanismo de atualização; quando a sessão de login expirar, basta colar de novo.", "BDUSS=…; STOKEN=…(或 Cookie 编辑器导出的 JSON)": "BDUSS=…; STOKEN=… (ou o JSON exportado por um editor de Cookie)", "CatPaw 的 X-Passport-Token(登录态 Cookie)": "X-Passport-Token do CatPaw (Cookie da sessão de login)", "Cline Pass 的 API Key 在 app.cline.bot 获取(订阅制);端点为 OpenAI 兼容,模型清单以 Cline 控制台为准,模型名在模型管理里按裸名添加(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客户端形态校验,所需的客户端请求头已随卡预置": "Obtenha a API Key do Cline Pass em app.cline.bot (por assinatura); o endpoint é compatível com OpenAI, o catálogo de modelos segue o console da Cline e os nomes dos modelos são adicionados no gerenciamento de modelos como nomes simples, sem prefixo (ex.: glm-5.2 / kimi-k2.7-code / deepseek-v4-pro); o upstream valida o formato do cliente, e os cabeçalhos de requisição do cliente necessários já vêm predefinidos neste cartão", "Cloud-IDE-JWT(三段点分)": "Cloud-IDE-JWT (três segmentos separados por ponto)", "Command Code": "Command Code", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT (três partes separadas por pontos; também pode colar a linha Bearer inteira ou o conteúdo do data.bin)", "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT (três partes separadas por ponto; colar a linha chatglm_token=… inteira também funciona)", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode edição internacional", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "Endpoint gerenciado do NVIDIA NIM; solicite a Key em integrate.api.nvidia.com", "OfficeAce 果办": "OfficeAce", "accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)": "accessToken é a credencial de login do Accio (um token opaco bem longo, não é JWT); refreshToken é opcional e, se preenchido, permite renovação automática ao expirar. Preencha a credencial da conta {label} — {siteNote} (a forma mais fácil de obter: use o \"Login web\" acima; não é preciso procurar o token manualmente.)", "accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
    手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
    没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。": "accessToken é o Cloud-IDE-JWT do Trae (três segmentos separados por ponto) e o refreshToken serve para renovação automática ao expirar (este provedor troca o refreshToken a cada uso: assim que é trocado, o antigo é invalidado, então não atualize a mesma conta em dois programas ao mesmo tempo).
    Ao colar manualmente, preencha também machineId / deviceId: o upstream os vincula à chave pública do dispositivo enviada no login para identificar o dispositivo, e inventar um novo par dispara a família de rejeições de vínculo de dispositivo 2xxxx.
    Sem esses três itens, o caminho correto é o \"Login web\" acima; em contêiner / implantação remota, basta colar o endereço de callback da barra de endereços conforme as instruções.", "monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)": "O valor de monkeycode_ai_session (também pode colar o Cookie inteiro)", "refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。": "refreshToken é opcional e, se preenchido, permite renovação automática ao expirar; ambos podem ser obtidos do arquivo de sessão de login do cliente Raccoon.", "refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。": "refreshToken é opcional e, se preenchido, permite renovação automática; ambos podem ser obtidos do arquivo de sessão de login do cliente. A mesma conta Cline funciona nos dois pools; aqui ela entra em {label}.", "token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。": "O token aceita o prefixo enc: (descriptografado automaticamente no Windows). Sem preencher o refreshToken, não é possível renovar automaticamente.", "token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。": "O token é o Cookie da sessão de login; o CatPaw não tem mecanismo de atualização, então faça login novamente no cliente após expirar.", "user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)": "API Key começando com user_ (também pode colar a linha Authorization: Bearer … inteira)", "{label} 网页登录的打开方式": "Como abrir o login web de {label}", "{label} 账号地区": "Região da conta {label}", "{label} 账号的添加方式": "Como adicionar uma conta {label}", "{name} 不是合法 JSON:{reason}": "{name} não é um JSON válido: {reason}", "{name}要粘一个 JSON 对象(形如 {sample})": "{name}: cole um objeto JSON (no formato {sample})", "{n} 个账号": "{n} contas", "{n} 个账号,点击添加": "{n} contas, clique para adicionar", "{seconds}s 后重发": "Reenviar em {seconds}s", "✅ {label}账号已添加": "✅ Conta {label} adicionada", "✅ {label}账号已添加{name}": "✅ Conta {label} adicionada{name}", "✅ 已从 cc-switch 导入 {n} 个供应商": "✅ {n} provedores importados do cc-switch", "✅ 已创建自定义提供商「{name}」并添加账号": "✅ Provedor personalizado \"{name}\" criado e conta adicionada", "✅ 已删除自定义提供商「{name}」{accounts}{keys}": "✅ Provedor personalizado \"{name}\" excluído{accounts}{keys}", "✅ 已更新已有账号{name}(同一把 API Key 只保留一条,重复添加会合并)": "✅ Conta existente{name} atualizada (apenas uma conta por chave de API; adições repetidas são mescladas)", "✅ 登录成功,{edition}账号已加入列表": "✅ Login concluído; conta {edition} adicionada à lista", "✅ 领取成功:{planId}{period}": "✅ Resgate concluído: {planId}{period}", " 同一家可以放多把 key,按优先级轮换。": " Um mesmo provedor pode ter várias keys, rotacionadas por prioridade.", "「{name}」的账号添加功能还在开发中,敬请期待。": "A adição de contas para \"{name}\" ainda está em desenvolvimento; aguarde novidades.", "一个 Key 用遍多家模型,模型名用「厂商/模型」全称(如 deepseek/deepseek-chat)": "Uma única Key serve para vários fornecedores; use o nome completo \"fornecedor/modelo\" (ex.: deepseek/deepseek-chat)", "上游不要鉴权时留空,并勾选下面一项(留空又不勾会被当成未配置凭证)": "Deixe em branco quando o upstream não exigir autenticação e marque a opção abaixo (em branco e sem marcar é tratado como credencial não configurada)", "上游信息": "Informações do upstream", "不可领({status})": "Não resgatável ({status})", "不支持导入": "Importação não suportada", "不是有效的凭证文件:内容要是一个 JSON 对象": "Não é um arquivo de credenciais válido: o conteúdo deve ser um objeto JSON", "不是有效的凭证文件:需要包含 accessToken 与 uid": "Não é um arquivo de credenciais válido: accessToken e uid são obrigatórios", "不是自定义提供商": "Não é um provedor personalizado", "不需要 Key:转发与拉取模型都不发送鉴权头": "Não precisa de Key: nem o encaminhamento nem a busca de modelos enviam cabeçalhos de autenticação", "不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

    注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。": "Não é preciso fazer login: clique no botão abaixo e o gateway vai obter uma credencial de visitante diretamente do Zhipu Qingyan e adicioná-la à lista de contas (você pode preencher um nome de observação antes). A credencial de visitante pode ser usada para conversar imediatamente; a cota gratuita é emitida pela origem por dispositivo.

    Atenção: uma conta de visitante não tem sistema de pontos — as interfaces de pontos de membro, pontos de login diário e resgate de atividades exigem login, então esses recursos são ignorados para visitantes (nenhuma leitura aparece na página de contas nem na central de check-in). Para obter pontos, use \"Preencher credenciais\" para colar o token de uma conta conectada, ou \"Importar estado de login do desktop\".", "两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。": "As contas das duas edições podem ser salvas ao mesmo tempo, cada uma com sua própria lista de modelos (as edições da China e internacional são dois provedores, que podem ser ativados e mapeados separadamente).", "中国版": "Edição da China", "今天没有需要领的活动(都已到账)": "Hoje não há campanhas para resgatar (todas já creditadas)", "今天(北京时间 {day})已试 {attempts} 次,还能试 {left} 次{next}
    ": "Hoje (horário de Pequim, {day}) você já tentou {attempts} vezes; restam {left} tentativas{next}
    ", "从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。": "Pegue três itens de outra máquina (ou do models.json do aplicativo de desktop do OfficeAce) e cole aqui: o endereço base do gateway de modelos (baseUrl, no formato https://modelgw-…/v2) e modelAppKey / modelAppSecret. Esses três já bastam para encaminhar requisições (a credencial do gateway não expira); as credenciais temporárias do plano de controle (accessKeyId etc.) são opcionais — se ficarem em branco, a consulta de saldo e a renovação automática ficam indisponíveis, mas o encaminhamento não é afetado.", "从本机导入桌面端登录态": "Importar sessão de login do desktop desta máquina", "任务镜像 id": "id da imagem da tarefa", "余额查询": "Consulta de saldo", "使用 Google 账号登录": "Entrar com conta Google", "使用 Zai 账号登录": "Entrar com conta Zai", "使用个人访问令牌(PAT)": "Usar token de acesso pessoal (PAT)", "免费模型不用填 Key(默认勾上「该上游无需鉴权」,网关自动用匿名凭证);付费模型则填 Zen 的 API Key。免费档上游只认官方客户端形态,已默认开启「伪装 OpenCode 官方客户端」": "Modelos gratuitos não precisam de Key (a opção \"Este upstream não exige autenticação\" já vem marcada e o gateway usa credenciais anônimas automaticamente); para modelos pagos, preencha a API Key do Zen. O nível gratuito do upstream só aceita o formato do cliente oficial, e a opção \"Simular o cliente oficial do OpenCode\" já vem ativada", "内嵌窗口": "Janela embutida", "内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)": "Abre na janela embutida; fechar a janela cancela a espera (se a conta Google for recusada, use o navegador do sistema)", "内嵌窗口打开;完成后自动加入列表,关窗即取消等待": "Abre na janela embutida; ao concluir, entra automaticamente na lista; fechar a janela cancela a espera", "内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待": "Abre em uma janela incorporada; a conta é adicionada automaticamente após o escaneamento — fechar a janela cancela a espera", "内嵌窗口用全新环境,多账号互不影响;关窗即取消等待": "A janela embutida usa um ambiente totalmente novo, então várias contas não interferem entre si; fechar a janela cancela a espera", "内嵌窗口(推荐)": "Janela embutida (recomendado)", "准备验证…": "Preparando a verificação…", "凭据 JSON": "JSON da credencial", "凭证文件在哪?": "Onde fica o arquivo de credenciais?", "创建并添加账号": "Criar e adicionar conta", "创建这个提供商,并同时建立它的第一个账号。": "Crie este provedor e, ao mesmo tempo, sua primeira conta.", "删除功能不可用(脚本未就绪)": "Exclusão indisponível (script não está pronto)", "删除失败:{reason}": "Falha ao excluir: {reason}", "删除此提供商": "Excluir este provedor", "及 {n} 个账号": " e {n} contas", "发送中…": "Enviando…", "发送失败:{message}": "Falha ao enviar: {message}", "发送未确认:登录窗口已弹出,请在弹出的窗口中手动完成登录": "Envio não confirmado: a janela de login foi aberta; conclua o login manualmente nessa janela", "取消失败:{message}": "Falha ao cancelar: {message}", "取消等待": "Cancelar espera", "只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页": "Somente a \"Janela embutida\" pode restaurar os acessos de terceiros: no navegador do sistema não podemos alterar a página de login", "可选": "Opcional", "可选(cloudaicompanionProject):留空由网关自动发现": "Opcional (cloudaicompanionProject); se vazio, o gateway descobre automaticamente", "可选,展示用": "Opcional, para exibição", "可选,没有则无法自动续期": "Opcional; sem ele, não é possível renovar automaticamente", "可选,留空使用凭据里的 user_name": "Opcional; se vazio, usa o user_name da credencial", "可选,留空使用账号姓名、邮箱或令牌指纹": "Opcional; se vazio, usa o nome, o e-mail ou a impressão digital do token", "可选,留空使用账号昵称或邮箱": "Opcional; se vazio, usa o apelido ou o e-mail da conta", "可选,留空则用 key 尾号": "Opcional; se vazio, usa o final da key", "可选,留空则用 userId": "Opcional; se vazio, usa o userId", "可选,留空则用上游返回的账号名或账号 id": "Opcional; se vazio, usa o nome ou o id da conta retornados pelo upstream", "可选,留空则用凭证里的账号名": "Opcional; se vazio, usa o nome da conta na credencial", "可选,留空则用文件里的昵称": "Opcional; deixe vazio para usar o apelido do arquivo", "可选,留空则用登录名或 uid": "Opcional; se vazio, usa o nome de login ou o uid", "可选,留空则用脱敏手机号": "Opcional; se vazio, usa o celular mascarado", "可选,留空则用脱敏手机号或 userId": "Opcional; se vazio, usa o celular mascarado ou o userId", "可选,留空则用账号 uk": "Opcional; se vazio, usa o uk da conta", "可选,留空则用账号昵称": "Opcional; em branco usa o apelido da conta", "可选,留空则用账号昵称或 userId": "Opcional; deixe em branco para usar o apelido da conta ou o userId", "可选,留空则用邮箱或令牌尾号": "Opcional; se vazio, usa o e-mail ou o final do token", "可选,留空用「OfficeAce 果办」": "Opcional; se vazio, usa “OfficeAce”", "可选,留空自动生成": "Opcional; se vazio, gerado automaticamente", "可选,留空自动用昵称": "Opcional; se vazio, usa o apelido", "可选,给这个访客账号起个备注名": "Opcional; um nome de observação para esta conta de visitante", "可选,续期时带上": "Opcional; enviado na renovação", "可选,讯飞侧 userid": "Opcional; userid do lado iFlytek", "可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补": "Opcional; necessário para iniciar uma conversa. Se vazio, o gateway descobre automaticamente nas tarefas existentes da conta; se não encontrar, preencha manualmente depois.", "可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)": "Opcional; e-mail da conta, usado para exibição e identificação (a mesma conta é reconhecida mesmo ao trocar por um token novo)", "可选;UUID 形态,与凭据同生共死": "Opcional; formato UUID, vive e morre com a credencial", "可选;填了才能到期自动续期": "Opcional; preencha para renovar automaticamente ao expirar", "可选;用于去重与展示名(留空时网关会问一次上游)": "Opcional; usado para deduplicação e nome de exibição (se vazio, o gateway consulta o upstream uma vez)", "可选;用于生成账号 id 与展示名": "Opcional; usado para gerar o id e o nome de exibição da conta", "可选;纯数字串(上游只收 8–24 位数字)": "Opcional; apenas dígitos (o upstream aceita somente 8–24 dígitos)", "可领取": "Resgatável", "同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)": "Contas com chave e sem autenticação podem coexistir no mesmo provedor, cada uma roteada pelas próprias regras; várias contas sem autenticação também são permitidas (cada uma com seu próprio proxy de saída)", "同地址换 anthropic 协议即走它的 Claude 兼容端点。转发必须填 OpenCode Go 的 API Key(订阅制;获取模型清单不需要 Key,但发请求上游会回 401「Missing API key」)": "No mesmo endereço, troque o protocolo para anthropic e ele usará o endpoint compatível com Claude. Para encaminhar, é obrigatório preencher a API Key do OpenCode Go (por assinatura; obter a lista de modelos não exige Key, mas as requisições recebem 401 \"Missing API key\" do upstream)", "响应为空": "Resposta vazia", "商汤日日新的 OpenAI 兼容端点;余额查询走控制台账号密码(见下方「余额查询」一段)": "Endpoint compatível com OpenAI do SenseNova; a consulta de saldo usa a conta e a senha do console (veja \"Consulta de saldo\" abaixo)", "国内版": "Edição da China", "国内版与**国际版是两套独立的账号与套餐**,凭证与领取的套餐都不通用。": "A edição da China e **a edição internacional são dois conjuntos independentes de contas e planos**; as credenciais e os planos resgatados não são compartilhados.", "国内版登录页没有 Google / GitHub 入口(它用微信 / 手机号 / 邮箱登录)": "A página de login da edição da China não tem acessos Google / GitHub (o login é por WeChat / celular / e-mail)", "国内版的登录站点是 www.accio-ai.com,与国际版不是同一站。": "O site de login da edição da China é www.accio-ai.com, diferente do site da edição internacional.", "国内版(WorkBuddy)": "Edição da China (WorkBuddy)", "国内站为 https://api.siliconflow.cn/v1": "O site da China é https://api.siliconflow.cn/v1", "国内站(minimaxi.com);OpenAI 兼容地址为 https://api.minimaxi.com/v1": "Site da China (minimaxi.com); o endereço compatível com OpenAI é https://api.minimaxi.com/v1", "国际版": "Edição internacional", "国际版 z.ai 的 Coding 套餐;OpenAI 兼容地址为 https://api.z.ai/api/coding/paas/v4": "Plano Coding do z.ai da edição internacional; o endereço compatível com OpenAI é https://api.z.ai/api/coding/paas/v4", "国际版与国内版是**两套独立的账号**(同一账号体系的两个站点),凭证不通用。": "As edições internacional e da China são **dois conjuntos independentes de contas** (dois sites do mesmo sistema de contas); as credenciais não são compartilhadas.", "国际版与国内版账号体系独立,请填国际版的凭证。未填 refreshToken 无法自动续期。": "As edições internacional e da China têm sistemas de contas independentes; preencha a credencial da edição internacional. Sem preencher o refreshToken, não é possível renovar automaticamente.", "国际版登录页默认只显示邮箱登录,勾选后恢复 Google / GitHub / X 入口": "A página de login da edição internacional mostra por padrão apenas o login por e-mail; marcar a opção restaura os acessos Google / GitHub / X", "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "O site de inferência da edição internacional é api.z.ai, diferente do site da edição da China; os planos também são independentes.", "国际版(WorkBuddy AI)": "Edição internacional (WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "Plano Coding do site internacional; o endereço compatível com OpenAI é https://api.minimax.io/v1", "在 cc-switch 里找到": "Encontradas", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "Conclua o login oficial na janela embutida; em caso de sucesso, a conta entra automaticamente na lista de contas.", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "Gere um PAT nas configurações da conta Qoder do {label} → Integrations (não use um token do Google / GitHub). Contas e credenciais não são compartilhadas entre as duas regiões.", "在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。": "Escaneie o QR code do WeChat na página de autorização oficial que abrir (o QR code aparece imediatamente). A conta é adicionada à lista automaticamente após a leitura bem-sucedida.", "在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "Na janela aberta, entre no site principal do KukuAI com sua conta Baidu (código SMS ou QR code, tanto faz); após o login, a conta entra automaticamente na lista. O gateway troca com o Baidu um token de sessão de negócio do KukuAI (requer o cliente KukuAI instalado nesta máquina).", "在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "Faça login com sua conta Google na janela que abrir (página de autorização do Antigravity); a conta é adicionada à lista automaticamente quando a autorização for concluída.", "在浏览器中打开登录页": "Abrir a página de login no navegador", "地区": "Região", "填写凭证": "Preencher credenciais", "填写凭证添加": "Preencher credenciais para adicionar", "备注名": "Rótulo", "套餐": "Plano", "如:智谱 GLM": "ex.: Zhipu GLM", "完成登录后自动加入账号列表,昵称取自上游。": "Após concluir o login, a conta entra automaticamente na lista; o apelido vem do upstream.", "官方客户端凭证目录:Windows 是 C:\\Users\\<你的用户名>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth,macOS 是 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth": "Diretório de credenciais do cliente oficial: Windows — C:\\Users\\\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth; macOS — ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth", "官方已确认到账": "Crédito confirmado oficialmente", "导入": "Importar", "导入中…": "Importando…", "导入凭证文件": "Importar arquivo de credenciais", "导入失败": "Falha na importação", "导入失败:{reason}": "Falha na importação: {reason}", "导入所选": "Importar selecionados", "导入所选({n})": "Importar selecionados ({n})", "导入桌面端登录态": "Importar sessão de login do desktop", "将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下": "A janela embutida será aberta; após a autorização, a conta entra automaticamente na lista de contas. Fechar a janela cancela a espera. No final, a página de autorização pode avisar que não conseguiu abrir o link zcode:// — isso é normal; o resultado já foi registrado no servidor", "将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)": "A janela embutida será aberta; após a autorização, a conta entra automaticamente na lista de contas. No painel remoto, é possível colar o endereço de callback. Fechar a janela cancela a espera. O link é válido por 5 minutos; se expirar ou não for clicado, ele é invalidado (você pode iniciar de novo)", "将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待": "A janela embutida será aberta; após o login, a conta entra automaticamente na lista. Fechar a janela cancela a espera", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 Trae 账号);同机完成后自动加入,Docker / 远程面板按提示粘贴地址栏回调地址。": "A página de autorização será aberta no navegador padrão do sistema (reaproveitando a conta Trae já conectada nele); na mesma máquina, a conta entra automaticamente ao concluir. Para Docker / painel remoto, cole o endereço de callback da barra de endereços conforme as instruções.", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 ZCode 账号);授权完成后自动加入账号列表。页面最后可能提示无法打开 zcode:// 链接,属正常现象": "A página de autorização será aberta no navegador padrão do sistema (reaproveitando a conta ZCode já conectada nele); após a autorização, a conta entra automaticamente na lista. No final, a página pode avisar que não conseguiu abrir o link zcode://; isso é normal", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "A página de autorização será aberta no navegador padrão do sistema (reaproveitando a conta Huawei Cloud já conectada nele); o gateway consulta a nuvem em polling para obter o código de autorização, então não importa em qual máquina o navegador roda. Ao concluir, a conta entra automaticamente na lista", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "A página de autorização será aberta no navegador padrão do sistema (reaproveitando a conta Huawei Cloud já conectada nele); se o navegador e o gateway não estiverem na mesma máquina, cole o endereço de callback final de volta no painel conforme as instruções acima", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "A página de login será aberta no navegador padrão do sistema (reaproveitando a conta Accio já conectada nele); após o login, a conta entra automaticamente na lista; fechar a janela cancela a espera", "小米 MiMo Token Plan 套餐(新加坡站)": "Plano Token Plan do Xiaomi MiMo (site de Singapura)", "已到账": "Creditado", "已取消登录等待": "Espera de login cancelada", "已取消领取": "Resgate cancelado", "已取消验证码": "Captcha cancelado", "已填写手机号,正在发送验证码…": "Celular preenchido; enviando o código…", "已声明无需鉴权:账号会被正常选路,出网时不带任何鉴权头": "Marcada como sem necessidade de autenticação: a conta é roteada normalmente e nenhum cabeçalho de autenticação é enviado nas requisições de saída", "已导入 {ok} 个,失败 {fail} 个(失败项已标注在列表里)": "{ok} importados, {fail} com falha (os itens com falha estão marcados na lista)", "已建过 · {n} 个账号,点击继续添加": "Já criado · {n} contas, clique para adicionar mais", "已建过,点击继续添加账号": "Já criado, clique para adicionar outra conta", "已打开官方登录页,请在窗口中完成登录…": "Página de login oficial aberta; conclua o login na janela…", "已点发送,等待短信…": "Enviado; aguardando o SMS…", "已用系统默认浏览器打开登录页,请在浏览器中完成登录…": "Página de login aberta no navegador padrão do sistema; conclua o login no navegador…", "已领取,等官方确认": "Resgatado; aguardando confirmação oficial", "当前壳版本不支持 AutoClaw 网页登录,请更新应用": "Esta versão do aplicativo não suporta o login web do AutoClaw; atualize o aplicativo", "当前没有可领取的套餐(活动尚未开始)": "Nenhum plano disponível para resgate no momento (a campanha ainda não começou)", "当前环境不支持领取(桥接方法缺失)": "O resgate não é suportado neste ambiente (método de ponte ausente)", "必填,CatPaw 账号标识": "Obrigatório; identificador da conta CatPaw", "恢复 Google / GitHub 入口": "Restaurar acessos Google / GitHub", "手机号": "Celular", "手机验证码登录": "Login com código SMS", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Abra a página de autorização oficial do Trae SOLO (trae.cn) e entre com sua conta Trae: ao concluir a autorização, a página oficial redireciona de volta para uma porta temporária desta máquina; implantações na mesma máquina recebem automaticamente, e no Docker / painel remoto copie o endereço de callback da barra de endereços conforme as instruções.", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Abra a página de login oficial do Accio {label} ({site}) e entre com sua conta Accio: após o login, a página oficial redireciona de volta para esta máquina; na mesma máquina tudo se completa automaticamente, e no Docker / painel remoto basta colar o endereço de callback conforme as instruções (o código de autorização é passado ao gateway apenas nesta máquina, e nenhum token em texto claro é exibido na interface).", "打开 Accio {label}登录页": "Abrir a página de login do Accio {label}", "打开 Antigravity 网页登录": "Abrir login web do Antigravity", "打开 CatPaw 网页登录": "Abrir o login web do CatPaw", "打开 Cline 授权页": "Abrir a página de autorização do Cline", "打开 CodeArts 授权页": "Abrir a página de autorização do CodeArts", "打开 Qoder 网页登录": "Abrir o login web do Qoder", "打开 Trae 授权页": "Abrir a página de autorização do Trae", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "Abra a página de autorização oficial do ZCode {label} e entre com sua conta. O resultado da autorização é registrado pelo servidor do ZCode; o gateway busca a credencial em segundo plano e a adiciona à lista de contas.", "打开 ZCode {label}授权页": "Abrir a página de autorização do ZCode {label}", "打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
    网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。": "Abra a página de autorização oficial do Huawei Cloud CodeArts para entrar: após o login, a página oficial leva o navegador de volta ao /oauth/callback do próprio gateway, e o gateway troca o código de autorização de uso único por credenciais temporárias e adiciona a conta à lista.
    Quando o gateway roda em outra máquina, a interface web pede para colar o endereço de callback final da barra de endereços de volta no painel.", "打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。": "Abra a página de autorização do Huawei Cloud e faça login: ao concluir, o gateway consulta a nuvem automaticamente para obter o código de autorização e adiciona a conta à lista (não é preciso instalar o aplicativo de desktop do OfficeAce nem colar manualmente uma URL de retorno).", "打开官方授权页完成设备码授权,登录的是{label}({site})的账号。": "Abra a página de autorização oficial e conclua a autorização por código de dispositivo; a conta usada é do {label} ({site}).", "打开授权页": "Abrir a página de autorização", "打开授权页完成确认,账号自动加入 {label}{note}": "Abra a página de autorização e confirme; a conta entra automaticamente em {label}{note}", "打开方式": "Como abrir", "打开网页登录": "Abrir login web", "探测失败:{message}": "Falha na detecção: {message}", "接入一个兼容上游": "Conectar um upstream compatível", "提交中…": "Enviando…", "搜索提供商": "Buscar provedores", "搜索提供商…": "Buscar provedores…", "整份粘贴官方插件 / CLIProxyAPI 落盘的凭据 JSON(形如 {\"codearts_provider_credential\":{…}},铺平的也行)。
    必填:access_key_id、secret_access_key、security_token。要能自动续期,必须连 refresh_token 与 oauth_context 一起粘 —— 临时凭据约一小时到期,缺这半块就续不回来,只能重新登录。": "Cole o JSON de credencial inteiro gravado pelo plugin oficial / CLIProxyAPI (no formato {\"codearts_provider_credential\":{…}}; a versão achatada também serve).
    Obrigatórios: access_key_id, secret_access_key, security_token.Para renovar automaticamente, é preciso colar também refresh_token e oauth_context — as credenciais temporárias expiram em cerca de uma hora, e sem essa parte não há como renovar; será preciso fazer login de novo.", "文件是{edition}凭证,已按该版本导入": "O arquivo contém credenciais da {edition} e foi importado como tal", "新建自定义提供商": "Novo provedor personalizado", "方案:{label}": "Esquema: {label}", "明文 JWT 或 auth.json 里的 enc: 加密值(自动解密)": "JWT em texto claro ou o valor criptografado enc: do auth.json (descriptografado automaticamente)", "明文 JWT(国际版账号的 access token)": "JWT em texto claro (o access token da conta da edição internacional)", "智谱 bigmodel.cn 的 Coding 套餐,Key 在 open.bigmodel.cn/usercenter/apikeys": "Plano Coding do bigmodel.cn da Zhipu; obtenha a Key em open.bigmodel.cn/usercenter/apikeys", "未知错误": "Erro desconhecido", "未能获取授权地址,请重试": "Não foi possível obter o endereço de autorização; tente novamente", "未能读取 cc-switch 数据": "Não foi possível ler os dados do cc-switch", "未领取成功": "Resgate não concluído", "本地 Ollama、OpenCode Zen 免费档这类上游要勾上,否则账号不可用": "Marque esta opção para upstreams como o Ollama local ou o nível gratuito do OpenCode Zen; caso contrário, a conta não funcionará", "本家有两个**互不替代**的凭证,按你要用的功能填,至少填一个:编码套餐 API Key 用于转发推理(打 {site})与查询窗口限额(每 N 小时 / 每周的剩余比例);jwt 用于领取套餐与查询余额(打 zcode.z.ai 的 billing 网关,官方叫 Coding Plan JWT,是一串三段点分的字符串)。余额两把都能查、但读到的东西不同:jwt 读到余额桶与套餐到期,API Key 读到窗口限额;而领取只认 jwt、转发只认 API Key。请填写 {label}账号的凭证 —— {plan}(最容易拿到的办法:直接用上方的「网页登录」—— 它会替你把这个 API Key 换好,两个凭证一起拿到。)": "Este provedor tem duas credenciais que **não se substituem**; preencha as que correspondem aos recursos que você vai usar, pelo menos uma: a API Key do Coding Plan serve para encaminhar inferência (acessa {site}) e para consultar os limites de janela (a porcentagem restante de cada janela de N horas / semanal); o jwt serve para resgatar planos e consultar saldo (acessa o gateway de billing em zcode.z.ai; oficialmente chamado Coding Plan JWT, uma string de três segmentos separados por ponto). As duas consultam saldo, mas leem coisas diferentes: o jwt lê os baldes de saldo e a expiração do plano, enquanto a API Key lê os limites de janela; e o resgate só aceita o jwt, o encaminhamento só aceita a API Key. Preencha a credencial da conta {label} — {plan} (a forma mais fácil: use o \"Login web\" acima — ele troca essa API Key por você e obtém as duas credenciais de uma vez.)", "本机 Ollama 的 OpenAI 兼容端点,无需 API Key(默认已勾上「该上游无需鉴权」,鉴权头一个都不发)": "Endpoint compatível com OpenAI do Ollama local; não precisa de API Key (a opção \"Este upstream não exige autenticação\" já vem marcada e nenhum cabeçalho de autenticação é enviado)", "本次未确认到账": "Crédito não confirmado desta vez", "条可导入": "importáveis", "条配置,其中": "configurações no cc-switch, das quais", "桌面运行时不可用(Tauri 未初始化)": "Runtime do desktop indisponível (Tauri não inicializado)", "模型网关基址": "Endereço base do gateway de modelos", "正在打开百度登录页…": "Abrindo a página de login do Baidu…", "正在扫描本机 cc-switch…": "Escaneando o cc-switch desta máquina…", "正在换发访客凭证…": "Solicitando uma credencial de visitante…", "正在添加…": "Adicionando…", "正在等待{name}登录完成;完成或取消后才能发起新的登录": "Aguardando a conclusão do login de {name}; só após concluir ou cancelar você pode iniciar um novo login", "每周": "Semanal", "每日": "Diário", "每月": "Mensal", "没有则无法自动续期": "Sem ele, não é possível renovar automaticamente", "没有匹配「{keyword}」的提供商": "Nenhum provedor corresponde a \"{keyword}\"", "添加 {name} 账号": "Adicionar conta {name}", "添加中…": "Adicionando…", "添加到 ": "Adicionar a ", "添加失败:{reason}": "Falha ao adicionar: {reason}", "添加小浣熊账号": "Adicionar conta Raccoon", "添加方式": "Forma de adicionar", "添加访客账号": "Adicionar uma conta de visitante", "添加账号": "Adicionar conta", "火山引擎 Coding 套餐;通用推理接入点为 https://ark.cn-beijing.volces.com/api/v3": "Plano Coding do Volcano Engine; o endpoint de inferência geral é https://ark.cn-beijing.volces.com/api/v3", "点击添加账号": "Clique para adicionar uma conta", "点开即预填,填 Key 接入": "Abre já preenchido; informe a Key para conectar", "点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
    这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。": "Após clicar no botão, primeiro passe por uma verificação de slider (controle de risco do provedor); em seguida a página de login abre e a conta é adicionada automaticamente ao concluir o login.
    Esta é a única forma oficial de login da edição internacional; se você já entrou no cliente, \"Importar sessão de login do desktop\" é mais rápido.", "用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。": "Entre com o celular vinculado à conta AutoClaw: clique em \"Obter código\", informe o código recebido por SMS abaixo e entre. O código é enviado desta máquina diretamente à API oficial, e nenhum token é exibido na interface.", "用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。": "Conclua o login com sua conta CatPaw (Meituan Passport); em caso de sucesso, a conta entra automaticamente na lista.", "用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。": "Entre com o celular vinculado à conta Loomy: clique em \"Obter código\" e informe o código. Esta é a forma mais simples neste provedor.", "用于转发;形如 apiKey.secret 两段点分(不填则这个账号不能转发)": "Usado para encaminhamento; no formato apiKey.secret, dois segmentos separados por ponto (se vazio, esta conta não pode encaminhar)", "用于领取套餐与查询余额;不填则这个账号不能领取与查余额": "Usado para resgatar planos e consultar saldo; se vazio, esta conta não pode resgatar nem consultar saldo", "用户 ID": "ID do usuário", "用绑定的手机号登录:点「获取验证码」后填入即可。这是国内版官方唯一的登录方式。": "Entre com o celular vinculado: clique em \"Obter código\" e informe o código. Esta é a única forma oficial de login da edição da China.", "留空则用提供商名称": "Se vazio, usa o nome do provedor", "登录 / 添加 {name} 账号": "Entrar / adicionar conta {name}", "登录 session(一长串)": "Sessão de login (uma string longa)", "登录中…": "Entrando…", "登录失败:{message}": "Falha no login: {message}", "登录已结束": "Login encerrado", "登录并添加": "Entrar e adicionar", "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "Caminho do arquivo de sessão de login: ~/.box-agent/config/auth.json (Windows: C:\\Users\\\\.box-agent\\config\\auth.json)", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "Excluir o provedor personalizado \"{name}\"? Isso também excluirá {count} contas desse provedor; a exclusão não pode ser desfeita.", "等待 Accio {label}登录完成…": "Aguardando a conclusão do login do Accio {label}…", "等待 CatPaw 登录完成…": "Aguardando a conclusão do login do CatPaw…", "等待 Cline 授权确认…": "Aguardando a confirmação de autorização do Cline…", "等待 CodeArts 登录完成…": "Aguardando a conclusão do login do CodeArts…", "等待 Google 授权完成…": "Aguardando a conclusão da autorização do Google…", "等待 KukuAI 登录完成…": "Aguardando a conclusão do login do KukuAI…", "等待 OfficeAce 登录完成…": "Aguardando a conclusão do login do OfficeAce…", "等待 OrcaTerm 登录完成…": "Aguardando o login no OrcaTerm…", "等待 Qoder 授权完成…": "Aguardando a conclusão da autorização do Qoder…", "等待 Trae 授权完成…": "Aguardando a conclusão da autorização do Trae…", "等待 ZCode {label}授权完成…": "Aguardando a conclusão da autorização do ZCode {label}…", "等待小浣熊登录完成…": "Aguardando a conclusão do login do Raccoon…", "等待登录完成…": "Aguardando a conclusão do login…", "等待网页登录…": "Aguardando o login web…", "粘贴 API Key": "Colar API Key", "粘贴 Accio 的 accessToken": "Colar o accessToken do Accio", "粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
    这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。": "Cole o Google refresh token do Antigravity (começa com 1//; também pode colar a linha inteira que contém refresh_token=).
    Esta é uma entrada manual além do login web (por exemplo, exportada do IDE oficial em que você já está conectado). O refresh token não expira, e o gateway renova o access token automaticamente ao expirar; se o token for perdido, só resta refazer a autorização do Google, então guarde-o bem.", "粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。": "Cole a API Key do Command Code (começa com user_; obtenha no ~/.commandcode/auth.json do CLI oficial ou no studio web). Colar a linha inteira / com aspas / com o prefixo Bearer funciona; o gateway extrai a key pura sozinho. A key não expira e não há API de renovação, então basta obter uma nova se ela deixar de funcionar. Ao adicionar, o gateway faz uma verificação leve de atividade; uma falha não impede a adição.", "粘贴 Cookie": "Colar Cookie", "粘贴 Google refresh token": "Colar Google refresh token", "粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。": "Cole o accessToken OAuth do OrcaTerm (um JWT com três partes separadas por pontos; obtido do data.bin do cliente desktop ou do cookie ot_session da versão web). Também pode colar a linha inteira contendo Bearer / ot_session= — o gateway extrai apenas o token. Após adicionar, o gateway renova automaticamente (o upstream tem API de renovação), sem precisar acompanhar a expiração de 2 horas manualmente.", "粘贴 Qoder 个人访问令牌(pt-…)": "Colar o token de acesso pessoal do Qoder (pt-…)", "粘贴 accessToken": "Colar accessToken", "粘贴 access_token(一长串 JWT)": "Colar o access_token (um JWT longo)", "粘贴 session cookie": "Colar session cookie", "粘贴 token / refreshToken": "Colar token / refreshToken", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "Cole um token começando com workos:… (o prefixo é adicionado automaticamente se faltar)", "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "Cole um token de acesso do Zhipu Qingyan (um JWT com três partes separadas por ponto). Como obter: abra chatglm.cn no navegador, faça login e copie o valor do cookie chatglm_token em Application → Cookies nas ferramentas de desenvolvedor (colar a linha chatglm_token=…; inteira também funciona). O token é verificado uma vez na origem após a colagem, e a conta só é salva se passar; contas conectadas têm pontos e check-in, e o gateway renova automaticamente depois (a origem tem API de renovação).", "粘贴登录凭据": "Colar credenciais de login", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "O que você cola é a sessão de login (que também é o token do gateway de modelos). O Loomy não tem API de renovação; a sessão vale cerca de 14 dias, então basta entrar de novo após expirar.", "粘贴网关凭据": "Colar credenciais do gateway", "粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
    {siteNote}
    MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。": "Cole o valor do cookie de sessão de login web monkeycode_ai_session (copiar o Cookie inteiro no navegador e colar diretamente também funciona; o gateway extrai o valor sozinho).
    {siteNote}
    O MonkeyCode não tem API de renovação: a sessão vale cerca de 30 dias (limite rígido); quando expirar, entre novamente no site e cole mais uma vez.", "粘贴访问令牌": "Cole o token de acesso", "系统浏览器": "Navegador do sistema", "系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。": "O navegador do sistema pode reaproveitar a sessão de login existente (recomendado para a edição internacional); a janela embutida é mais limpa.", "系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表": "Abre no navegador do sistema (reaproveita a conta Cline já conectada); ao concluir, entra automaticamente na lista", "系统浏览器打开(复用已登录的 Google 账号);完成后自动加入列表": "Abre no navegador do sistema (reutiliza a conta Google já conectada); ao concluir, entra automaticamente na lista", "系统浏览器打开(复用已登录的 Zai / Google 账号);完成后自动加入列表": "Abre no navegador do sistema (reaproveita a conta Zai / Google já conectada); ao concluir, entra automaticamente na lista", "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "Abre no navegador do sistema (reaproveita a conta Meituan já conectada); ao concluir, entra automaticamente na lista", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "Abre no navegador do sistema (reaproveita a conta já conectada); ao concluir, entra automaticamente na lista", "系统默认浏览器": "Navegador padrão do sistema", "编码套餐 API Key": "API Key do Coding Plan", "网关 Secret": "Segredo do gateway", "网页登录": "Login web", "网页登录的打开方式": "Como abrir o login web", "网页登录(Zai / Google)": "Login web (Zai / Google)", "自定义": "Personalizado", "自建聚合面板:填你自己的站点地址。余额查询用该账号的 API Key 打 {base}/v1/usage,不需要额外配置": "Painel de agregação próprio: preencha o endereço do seu site. A consulta de saldo chama {base}/v1/usage com a chave de API desta conta; sem configuração extra", "自建聚合面板:填你自己的站点地址;余额查询要另填 NewAPI 的个人访问令牌(见下方「余额查询」一段)": "Painel de agregação próprio: preencha o endereço do seu site; a consulta de saldo precisa também de um token de acesso pessoal do NewAPI (veja \"Consulta de saldo\" abaixo)", "获取风控配置失败,将直接尝试领取:{message}": "Falha ao obter a configuração de controle de risco; tentando resgatar diretamente: {message}", "获取验证码": "Obter código", "访客模式": "Modo visitante", "访客模式(免登录)": "Modo visitante (sem login)", "该上游无需鉴权(不发送鉴权头)": "Este upstream não exige autenticação (nenhum cabeçalho de autenticação é enviado)", "该提供商": "Este provedor", "该提供商账号添加功能即将上线": "A adição de contas deste provedor chega em breve", "该自定义提供商已不存在(可能已被删除),请刷新后重试": "Este provedor personalizado não existe mais (pode ter sido excluído); atualize e tente novamente", "请先选择一个自定义提供商": "Selecione primeiro um provedor personalizado", "请在弹出的滑块中完成验证(官方要求的风控步骤)": "Conclua a verificação no slider exibido (etapa de controle de risco exigida pelo provedor)", "请填写 11 位大陆手机号": "Informe um número de celular da China continental com 11 dígitos", "请填写 6 位数字验证码": "Informe o código de 6 dígitos", "请填写 Base URL": "Preencha a Base URL", "请填写 {label}": "Preencha {label}", "请填写名称": "Preencha o nome", "请完成验证…": "Conclua a verificação…", "读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows": "Lê e descriptografa %APPDATA%/AutoClaw/auth.json; somente no Windows", "读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录": "Lê ~/.cline/data/settings/providers.json; requer login no cliente Cline", "读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录": "Lê ~/.meituan-catpaw/auth.json; requer login no cliente CatPaw", "读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录": "Lê a sessão de login do cliente KukuAI desta máquina (%APPDATA%\\baidugenflowpro\\Network\\Cookies); requer login no cliente KukuAI", "读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录": "Lê o estado de login do cliente OrcaTerm local (%APPDATA%\\com.orcaterm-desktop.app\\data.bin); é preciso estar logado no cliente OrcaTerm", "读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录": "Lê o estado de login do cliente 智谱清言 local (%APPDATA%\\chatglm\\Network\\Cookies); o cliente precisa estar conectado", "读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。": "Lê a conta atualmente conectada no cliente oficial (Tencent WorkBuddy / WorkBuddy AI) nesta máquina: o arquivo de sessão mais recente no diretório de credenciais é localizado automaticamente (é preciso estar conectado ao cliente oficial nesta máquina). Após a importação, o gateway renova o token automaticamente — não é preciso trocar tokens manualmente conforme a validade de 2 horas.", "读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。": "Lê a sessão de login atual do cliente desta máquina (Cookie em texto claro, sem descriptografia). Com o cliente em execução, o arquivo Cookies fica em uso exclusivo; feche o cliente KukuAI antes de importar.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。": "Lê a sessão de login atual do cliente desta máquina, em tempo real a cada vez (excluir este registro não afeta a sessão de login do cliente).", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后点「刷新 Token」同步。": "Lê a sessão de login atual do cliente desta máquina, em tempo real a cada vez (excluir este registro não afeta a sessão de login do cliente). Após entrar novamente no cliente, clique em \"Atualizar Token\" para sincronizar.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后重新导入即可。": "Lê a sessão de login atual do cliente desta máquina, em tempo real a cada vez (excluir este registro não afeta a sessão de login do cliente). Após entrar novamente no cliente, basta importar de novo.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。": "Lê a sessão de login atual do cliente desta máquina, em tempo real a cada vez (excluir este registro não afeta a sessão de login do cliente). Usuários que entram no cliente com Zai / Google devem usar esta opção.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。": "Lê a sessão de login atual do cliente desta máquina, em tempo real a cada vez (excluir este registro não afeta a sessão de login do cliente). Esta opção fica em {label}.", "读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。": "Lê o token OAuth com que o cliente local está logado (JSON em texto claro, sem descriptografia). Após importar, o gateway renova automaticamente — não é preciso acompanhar a expiração de 2 horas do cliente.", "读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。": "Lê o estado de login atual do cliente local. Os cookies do cliente são criptografados com DPAPI (Chromium os_crypt), portanto isto funciona somente no Windows; o arquivo pode ficar ilegível enquanto o cliente está aberto — feche o cliente e tente de novo (ou use \"Preencher credenciais\" para colar um token).", "账号信息": "Informações da conta", "账号信息不完整,请刷新后重试": "Informações da conta incompletas; atualize e tente novamente", "账号已添加,但请留意:{warning}": "Conta adicionada, mas atenção: {warning}", "账号版本": "Edição da conta", "账号类型": "Tipo de conta", "账号:{name}
    ": "Conta: {name}
    ", "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 创建": "Usa a rota Inference Providers; crie a Key em hf.co/settings/tokens", "还没有账号": "Ainda não há contas", "这一家当前不支持网页登录,请改用填写凭证": "Este provedor não suporta login web no momento; use \"Preencher credenciais\"", "这个账号当前没有可自动领的活动(只自动领每日登录赠送积分)": "Esta conta não tem campanhas com resgate automático no momento (apenas os pontos de brinde do login diário são resgatados automaticamente)", "这份额度走「活动套餐」通道:若转发时提示编码套餐已到期,": "Esta cota passa pelo canal \"Start Plan\": se o encaminhamento avisar que o Coding Plan expirou, ", "这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。": "Aqui é a conta do site da China (monkeycode-ai.com): cole a sessão do site da China. O site internacional (monkeycode-ai.net) é outro provedor, com conta própria; as credenciais não são compartilhadas entre os dois.", "这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。": "Aqui é a conta do site internacional (monkeycode-ai.net): cole a sessão do site internacional. O site da China (monkeycode-ai.com) é outro provedor, com conta própria; as credenciais não são compartilhadas entre os dois.", "选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "Selecione o arquivo de sessão de login auth.json do cliente CatPaw (Windows: C:\\Users\\\\.meituan-catpaw\\auth.json; macOS / Linux: ~/.meituan-catpaw/auth.json). Após a importação, as credenciais ficam no gateway e podem ser encaminhadas de forma independente — sem depender mais do cliente desta máquina.", "选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。": "Selecione o arquivo de sessão de login providers.json do cliente Cline (Windows: C:\\Users\\\\.cline\\data\\settings\\providers.json; macOS / Linux: ~/.cline/data/settings/providers.json). Após a importação, as credenciais ficam no gateway e podem ser encaminhadas de forma independente — sem depender mais do cliente desta máquina; este registro entra em {label}.", "选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。": "Selecione o arquivo Cookies do cliente KukuAI (%APPDATA%\\baidugenflowpro\\Network\\Cookies): é um banco SQLite **binário** (sem extensão, não é JSON). A importação lê uma cópia do arquivo selecionado e não disputa o bloqueio com o cliente em execução, então **não é preciso fechar o cliente antes**. Após a importação, as credenciais ficam no gateway e podem ser encaminhadas de forma independente.", "选择凭证文件导入": "Importar de arquivo de credenciais", "选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。": "Selecione o arquivo de credenciais de sessão do cliente oficial (workbuddy-desktop.info / workbuddy-desktop-ai.info) ou um JSON de conta exportado pelo wb2api (formato aninhado ou plano).", "选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "Selecione o arquivo de sessão de login auth.json do cliente Raccoon (Windows: C:\\Users\\\\.box-agent\\config\\auth.json; macOS / Linux: ~/.box-agent/config/auth.json). Após a importação, as credenciais ficam no gateway e podem ser encaminhadas de forma independente — sem depender mais do cliente desta máquina.", "选择提供商": "Selecionar provedor", "选择的文件不是合法 JSON": "O arquivo selecionado não é um JSON válido", "选择要添加账号的提供商": "Selecione o provedor para adicionar uma conta", "重新扫描": "Escanear novamente", "鉴权": "Autenticação", "阿里云 Token Plan 套餐(国际站)": "Plano Token Plan do Alibaba Cloud (site internacional)", "阿里云百炼 Coding 套餐(国内站)": "Plano Coding do Alibaba Cloud Model Studio (site da China)", "阿里云百炼 Coding 套餐(国际站)": "Plano Coding do Alibaba Cloud Model Studio (site internacional)", "阿里云百炼模型服务(国际站,OpenAI 兼容模式)": "Serviço de modelos Alibaba Cloud Model Studio (site internacional, modo compatível com OpenAI)", "需要图形验证码:登录窗口已弹出,请填写后继续": "Captcha necessário: a janela de login foi aberta; preencha para continuar", "预置 API": "API predefinida", "领取 CodeArts 每日福利": "Resgatar benefício diário do CodeArts", "领取失败:{message}": "Falha ao resgatar: {message}", "领取并让官方确认": "Resgatar e aguardar confirmação oficial", "风控验证配置不完整,请稍后重试": "Configuração de verificação de controle de risco incompleta; tente novamente mais tarde", "验证码": "Código de verificação", "验证码已发送。收到后填入下方并点「登录并添加」": "Código enviado. Informe-o abaixo e clique em \"Entrar e adicionar\"", "验证码已发送,请查看手机短信": "Código enviado; verifique o SMS", "验证码已发送,请查看手机短信。填入下方并点「登录并添加」即完成登录": "Código enviado; verifique o SMS. Informe-o abaixo e clique em \"Entrar e adicionar\" para concluir o login", "验证码已发送,请查看短信": "Código enviado; verifique as mensagens", "验证码校验失败,请重试": "Falha na verificação do captcha; tente novamente", "验证码校验超时,请重试": "Tempo esgotado na verificação do captcha; tente novamente", "验证码组件不可用,请重试": "Componente de captcha indisponível; tente novamente", "验证码组件初始化超时,请重试": "Tempo esgotado ao inicializar o componente de captcha; tente novamente", "验证码组件加载失败,请检查网络后重试": "Falha ao carregar o componente de captcha; verifique a rede e tente novamente", "验证码组件加载异常,请重试": "Erro ao carregar o componente de captcha; tente novamente", "验证码组件加载超时,请检查网络后重试": "Tempo esgotado ao carregar o componente de captcha; verifique a rede e tente novamente", "验证码组件未加载,请重启应用后重试": "O componente de captcha não foi carregado; reinicie o aplicativo e tente novamente", "(免费额度池,模型名带 cline-free/ 前缀)。": " (pool de cota gratuita; nomes de modelo com o prefixo cline-free/).", "(其中 {n} 把恢复为不限制)": " ({n} das quais voltaram a ser ilimitadas)", "(必填)": " (obrigatório)", "(有效期 {window})": "(validade {window})", "(订阅池,模型名带 cline-pass/ 前缀,需要账号有对应订阅)。": " (pool de assinatura; nomes de modelo com o prefixo cline-pass/, e a conta precisa da assinatura correspondente).", ",下一次最快 {minutes} 分钟后": ", próxima tentativa em pelo menos {minutes} min", ",并从 {n} 把 Key 的可用提供商里移除": ", e removido dos provedores permitidos de {n} keys" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · 模型 {head}{rest}": " · Modelos: {head}{rest}", " 等 {n} 个": " ({n} no total)", "1// 开头的 Google refresh token(也可直接粘贴 refresh_token=… 整行)": "Google refresh token começando com 1// (também pode colar a linha refresh_token=… inteira)", "11 位大陆手机号": "Celular da China continental (11 dígitos)", "1~64 个字符,账号列表里按它分组显示": "1 a 64 caracteres; na lista de contas, as contas são agrupadas por ele", "6 位数字": "6 dígitos", "

  • 没有可自动领的活动
  • ": "
  • Nenhuma campanha pode ser resgatada automaticamente
  • ", "

    领到的是套餐赠送积分,不会增加福利模型的 token 池;": "

    O que você recebe são pontos de brinde do plano, que não aumentam o pool de tokens dos modelos de benefício; ", "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "O endpoint compatível com Anthropic é https://api.deepseek.com/anthropic (preencha-o ao trocar o protocolo para anthropic)", "Anthropic 协议填根地址,不要带 /v1": "No protocolo Anthropic, preencha o endereço raiz, sem /v1", "Antigravity": "Antigravity", "AtomCode": "AtomCode", "AutoClaw 国内版": "AutoClaw edição da China", "AutoClaw 国际版": "AutoClaw edição internacional", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS é obrigatório; cole o **Cookie completo** (um editor de Cookie exporta com um clique) — o **PTOKEN deve ser mantido**, pois o gateway o usa para trocar com o Baidu por um token de sessão de negócio do KukuAI; sem ele, a atualização de modelos reporta \"não conectado\". São aceitos o cabeçalho Cookie inteiro, um array JSON exportado por um editor de Cookie, ou um objeto {BDUSS, STOKEN}. O KukuAI não tem mecanismo de atualização; quando a sessão de login expirar, basta colar de novo.", "BDUSS=…; STOKEN=…(或 Cookie 编辑器导出的 JSON)": "BDUSS=…; STOKEN=… (ou o JSON exportado por um editor de Cookie)", "CatPaw 的 X-Passport-Token(登录态 Cookie)": "X-Passport-Token do CatPaw (Cookie da sessão de login)", "Cline Pass 的 API Key 在 app.cline.bot 获取(订阅制);端点为 OpenAI 兼容,模型清单以 Cline 控制台为准,模型名在模型管理里按裸名添加(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客户端形态校验,所需的客户端请求头已随卡预置": "Obtenha a API Key do Cline Pass em app.cline.bot (por assinatura); o endpoint é compatível com OpenAI, o catálogo de modelos segue o console da Cline e os nomes dos modelos são adicionados no gerenciamento de modelos como nomes simples, sem prefixo (ex.: glm-5.2 / kimi-k2.7-code / deepseek-v4-pro); o upstream valida o formato do cliente, e os cabeçalhos de requisição do cliente necessários já vêm predefinidos neste cartão", "Cloud-IDE-JWT(三段点分)": "Cloud-IDE-JWT (três segmentos separados por ponto)", "Command Code": "Command Code", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT (três partes separadas por pontos; também pode colar a linha Bearer inteira ou o conteúdo do data.bin)", "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT (três partes separadas por ponto; colar a linha chatglm_token=… inteira também funciona)", "LobsterAI": "LobsterAI", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode edição internacional", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "Endpoint gerenciado do NVIDIA NIM; solicite a Key em integrate.api.nvidia.com", "OfficeAce 果办": "OfficeAce", "accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)": "accessToken é a credencial de login do Accio (um token opaco bem longo, não é JWT); refreshToken é opcional e, se preenchido, permite renovação automática ao expirar. Preencha a credencial da conta {label} — {siteNote} (a forma mais fácil de obter: use o \"Login web\" acima; não é preciso procurar o token manualmente.)", "accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
    手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
    没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。": "accessToken é o Cloud-IDE-JWT do Trae (três segmentos separados por ponto) e o refreshToken serve para renovação automática ao expirar (este provedor troca o refreshToken a cada uso: assim que é trocado, o antigo é invalidado, então não atualize a mesma conta em dois programas ao mesmo tempo).
    Ao colar manualmente, preencha também machineId / deviceId: o upstream os vincula à chave pública do dispositivo enviada no login para identificar o dispositivo, e inventar um novo par dispara a família de rejeições de vínculo de dispositivo 2xxxx.
    Sem esses três itens, o caminho correto é o \"Login web\" acima; em contêiner / implantação remota, basta colar o endereço de callback da barra de endereços conforme as instruções.", "monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)": "O valor de monkeycode_ai_session (também pode colar o Cookie inteiro)", "refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。": "refreshToken é opcional e, se preenchido, permite renovação automática ao expirar; ambos podem ser obtidos do arquivo de sessão de login do cliente Raccoon.", "refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。": "refreshToken é opcional e, se preenchido, permite renovação automática; ambos podem ser obtidos do arquivo de sessão de login do cliente. A mesma conta Cline funciona nos dois pools; aqui ela entra em {label}.", "token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。": "O token aceita o prefixo enc: (descriptografado automaticamente no Windows). Sem preencher o refreshToken, não é possível renovar automaticamente.", "token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。": "O token é o Cookie da sessão de login; o CatPaw não tem mecanismo de atualização, então faça login novamente no cliente após expirar.", "user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)": "API Key começando com user_ (também pode colar a linha Authorization: Bearer … inteira)", "{label} 网页登录的打开方式": "Como abrir o login web de {label}", "{label} 账号地区": "Região da conta {label}", "{label} 账号的添加方式": "Como adicionar uma conta {label}", "{name} 不是合法 JSON:{reason}": "{name} não é um JSON válido: {reason}", "{name}要粘一个 JSON 对象(形如 {sample})": "{name}: cole um objeto JSON (no formato {sample})", "{n} 个账号": "{n} contas", "{n} 个账号,点击添加": "{n} contas, clique para adicionar", "{seconds}s 后重发": "Reenviar em {seconds}s", "✅ {label}账号已添加": "✅ Conta {label} adicionada", "✅ {label}账号已添加{name}": "✅ Conta {label} adicionada{name}", "✅ 已从 cc-switch 导入 {n} 个供应商": "✅ {n} provedores importados do cc-switch", "✅ 已创建自定义提供商「{name}」并添加账号": "✅ Provedor personalizado \"{name}\" criado e conta adicionada", "✅ 已删除自定义提供商「{name}」{accounts}{keys}": "✅ Provedor personalizado \"{name}\" excluído{accounts}{keys}", "✅ 已更新已有账号{name}(同一把 API Key 只保留一条,重复添加会合并)": "✅ Conta existente{name} atualizada (apenas uma conta por chave de API; adições repetidas são mescladas)", "✅ 登录成功,{edition}账号已加入列表": "✅ Login concluído; conta {edition} adicionada à lista", "✅ 领取成功:{planId}{period}": "✅ Resgate concluído: {planId}{period}", " 同一家可以放多把 key,按优先级轮换。": " Um mesmo provedor pode ter várias keys, rotacionadas por prioridade.", "「{name}」的账号添加功能还在开发中,敬请期待。": "A adição de contas para \"{name}\" ainda está em desenvolvimento; aguarde novidades.", "一个 Key 用遍多家模型,模型名用「厂商/模型」全称(如 deepseek/deepseek-chat)": "Uma única Key serve para vários fornecedores; use o nome completo \"fornecedor/modelo\" (ex.: deepseek/deepseek-chat)", "上游不要鉴权时留空,并勾选下面一项(留空又不勾会被当成未配置凭证)": "Deixe em branco quando o upstream não exigir autenticação e marque a opção abaixo (em branco e sem marcar é tratado como credencial não configurada)", "上游信息": "Informações do upstream", "不可领({status})": "Não resgatável ({status})", "不支持导入": "Importação não suportada", "不是有效的凭证文件:内容要是一个 JSON 对象": "Não é um arquivo de credenciais válido: o conteúdo deve ser um objeto JSON", "不是有效的凭证文件:需要包含 accessToken 与 uid": "Não é um arquivo de credenciais válido: accessToken e uid são obrigatórios", "不是自定义提供商": "Não é um provedor personalizado", "不需要 Key:转发与拉取模型都不发送鉴权头": "Não precisa de Key: nem o encaminhamento nem a busca de modelos enviam cabeçalhos de autenticação", "不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

    注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。": "Não é preciso fazer login: clique no botão abaixo e o gateway vai obter uma credencial de visitante diretamente do Zhipu Qingyan e adicioná-la à lista de contas (você pode preencher um nome de observação antes). A credencial de visitante pode ser usada para conversar imediatamente; a cota gratuita é emitida pela origem por dispositivo.

    Atenção: uma conta de visitante não tem sistema de pontos — as interfaces de pontos de membro, pontos de login diário e resgate de atividades exigem login, então esses recursos são ignorados para visitantes (nenhuma leitura aparece na página de contas nem na central de check-in). Para obter pontos, use \"Preencher credenciais\" para colar o token de uma conta conectada, ou \"Importar estado de login do desktop\".", "两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。": "As contas das duas edições podem ser salvas ao mesmo tempo, cada uma com sua própria lista de modelos (as edições da China e internacional são dois provedores, que podem ser ativados e mapeados separadamente).", "中国版": "Edição da China", "今天没有需要领的活动(都已到账)": "Hoje não há campanhas para resgatar (todas já creditadas)", "今天(北京时间 {day})已试 {attempts} 次,还能试 {left} 次{next}
    ": "Hoje (horário de Pequim, {day}) você já tentou {attempts} vezes; restam {left} tentativas{next}
    ", "从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。": "Pegue três itens de outra máquina (ou do models.json do aplicativo de desktop do OfficeAce) e cole aqui: o endereço base do gateway de modelos (baseUrl, no formato https://modelgw-…/v2) e modelAppKey / modelAppSecret. Esses três já bastam para encaminhar requisições (a credencial do gateway não expira); as credenciais temporárias do plano de controle (accessKeyId etc.) são opcionais — se ficarem em branco, a consulta de saldo e a renovação automática ficam indisponíveis, mas o encaminhamento não é afetado.", "从本机导入桌面端登录态": "Importar sessão de login do desktop desta máquina", "任务镜像 id": "id da imagem da tarefa", "余额查询": "Consulta de saldo", "使用 Google 账号登录": "Entrar com conta Google", "使用 Zai 账号登录": "Entrar com conta Zai", "使用个人访问令牌(PAT)": "Usar token de acesso pessoal (PAT)", "免费模型不用填 Key(默认勾上「该上游无需鉴权」,网关自动用匿名凭证);付费模型则填 Zen 的 API Key。免费档上游只认官方客户端形态,已默认开启「伪装 OpenCode 官方客户端」": "Modelos gratuitos não precisam de Key (a opção \"Este upstream não exige autenticação\" já vem marcada e o gateway usa credenciais anônimas automaticamente); para modelos pagos, preencha a API Key do Zen. O nível gratuito do upstream só aceita o formato do cliente oficial, e a opção \"Simular o cliente oficial do OpenCode\" já vem ativada", "内嵌窗口": "Janela embutida", "内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)": "Abre na janela embutida; fechar a janela cancela a espera (se a conta Google for recusada, use o navegador do sistema)", "内嵌窗口打开;完成后自动加入列表,关窗即取消等待": "Abre na janela embutida; ao concluir, entra automaticamente na lista; fechar a janela cancela a espera", "内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待": "Abre em uma janela incorporada; a conta é adicionada automaticamente após o escaneamento — fechar a janela cancela a espera", "内嵌窗口用全新环境,多账号互不影响;关窗即取消等待": "A janela embutida usa um ambiente totalmente novo, então várias contas não interferem entre si; fechar a janela cancela a espera", "内嵌窗口(推荐)": "Janela embutida (recomendado)", "准备验证…": "Preparando a verificação…", "凭据 JSON": "JSON da credencial", "凭证文件在哪?": "Onde fica o arquivo de credenciais?", "创建并添加账号": "Criar e adicionar conta", "创建这个提供商,并同时建立它的第一个账号。": "Crie este provedor e, ao mesmo tempo, sua primeira conta.", "删除功能不可用(脚本未就绪)": "Exclusão indisponível (script não está pronto)", "删除失败:{reason}": "Falha ao excluir: {reason}", "删除此提供商": "Excluir este provedor", "及 {n} 个账号": " e {n} contas", "发送中…": "Enviando…", "发送失败:{message}": "Falha ao enviar: {message}", "发送未确认:登录窗口已弹出,请在弹出的窗口中手动完成登录": "Envio não confirmado: a janela de login foi aberta; conclua o login manualmente nessa janela", "取消失败:{message}": "Falha ao cancelar: {message}", "取消等待": "Cancelar espera", "只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页": "Somente a \"Janela embutida\" pode restaurar os acessos de terceiros: no navegador do sistema não podemos alterar a página de login", "可选": "Opcional", "可选(cloudaicompanionProject):留空由网关自动发现": "Opcional (cloudaicompanionProject); se vazio, o gateway descobre automaticamente", "可选,展示用": "Opcional, para exibição", "可选,没有则无法自动续期": "Opcional; sem ele, não é possível renovar automaticamente", "可选,用于生成账号标识": "Opcional; usado para gerar o identificador da conta", "可选,留空使用凭据里的 user_name": "Opcional; se vazio, usa o user_name da credencial", "可选,留空使用账号姓名、邮箱或令牌指纹": "Opcional; se vazio, usa o nome, o e-mail ou a impressão digital do token", "可选,留空使用账号昵称或邮箱": "Opcional; se vazio, usa o apelido ou o e-mail da conta", "可选,留空则用 key 尾号": "Opcional; se vazio, usa o final da key", "可选,留空则用 userId": "Opcional; se vazio, usa o userId", "可选,留空则用上游返回的账号名或账号 id": "Opcional; se vazio, usa o nome ou o id da conta retornados pelo upstream", "可选,留空则用凭证里的账号名": "Opcional; se vazio, usa o nome da conta na credencial", "可选,留空则用文件里的昵称": "Opcional; deixe vazio para usar o apelido do arquivo", "可选,留空则用登录名或 uid": "Opcional; se vazio, usa o nome de login ou o uid", "可选,留空则用脱敏手机号": "Opcional; se vazio, usa o celular mascarado", "可选,留空则用脱敏手机号或 userId": "Opcional; se vazio, usa o celular mascarado ou o userId", "可选,留空则用账号 uk": "Opcional; se vazio, usa o uk da conta", "可选,留空则用账号昵称": "Opcional; em branco usa o apelido da conta", "可选,留空则用账号昵称或 userId": "Opcional; deixe em branco para usar o apelido da conta ou o userId", "可选,留空则用邮箱或令牌尾号": "Opcional; se vazio, usa o e-mail ou o final do token", "可选,留空用「AtomCode」": "Opcional; padrão “AtomCode”", "可选,留空用「OfficeAce 果办」": "Opcional; se vazio, usa “OfficeAce”", "可选,留空自动生成": "Opcional; se vazio, gerado automaticamente", "可选,留空自动用昵称": "Opcional; se vazio, usa o apelido", "可选,给这个访客账号起个备注名": "Opcional; um nome de observação para esta conta de visitante", "可选,续期时带上": "Opcional; enviado na renovação", "可选,讯飞侧 userid": "Opcional; userid do lado iFlytek", "可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补": "Opcional; necessário para iniciar uma conversa. Se vazio, o gateway descobre automaticamente nas tarefas existentes da conta; se não encontrar, preencha manualmente depois.", "可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)": "Opcional; e-mail da conta, usado para exibição e identificação (a mesma conta é reconhecida mesmo ao trocar por um token novo)", "可选;UUID 形态,与凭据同生共死": "Opcional; formato UUID, vive e morre com a credencial", "可选;填了才能到期自动续期": "Opcional; preencha para renovar automaticamente ao expirar", "可选;用于去重与展示名(留空时网关会问一次上游)": "Opcional; usado para deduplicação e nome de exibição (se vazio, o gateway consulta o upstream uma vez)", "可选;用于生成账号 id 与展示名": "Opcional; usado para gerar o id e o nome de exibição da conta", "可选;纯数字串(上游只收 8–24 位数字)": "Opcional; apenas dígitos (o upstream aceita somente 8–24 dígitos)", "可领取": "Resgatável", "同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)": "Contas com chave e sem autenticação podem coexistir no mesmo provedor, cada uma roteada pelas próprias regras; várias contas sem autenticação também são permitidas (cada uma com seu próprio proxy de saída)", "同地址换 anthropic 协议即走它的 Claude 兼容端点。转发必须填 OpenCode Go 的 API Key(订阅制;获取模型清单不需要 Key,但发请求上游会回 401「Missing API key」)": "No mesmo endereço, troque o protocolo para anthropic e ele usará o endpoint compatível com Claude. Para encaminhar, é obrigatório preencher a API Key do OpenCode Go (por assinatura; obter a lista de modelos não exige Key, mas as requisições recebem 401 \"Missing API key\" do upstream)", "响应为空": "Resposta vazia", "商汤日日新的 OpenAI 兼容端点;余额查询走控制台账号密码(见下方「余额查询」一段)": "Endpoint compatível com OpenAI do SenseNova; a consulta de saldo usa a conta e a senha do console (veja \"Consulta de saldo\" abaixo)", "国内版": "Edição da China", "国内版与**国际版是两套独立的账号与套餐**,凭证与领取的套餐都不通用。": "A edição da China e **a edição internacional são dois conjuntos independentes de contas e planos**; as credenciais e os planos resgatados não são compartilhados.", "国内版登录页没有 Google / GitHub 入口(它用微信 / 手机号 / 邮箱登录)": "A página de login da edição da China não tem acessos Google / GitHub (o login é por WeChat / celular / e-mail)", "国内版的登录站点是 www.accio-ai.com,与国际版不是同一站。": "O site de login da edição da China é www.accio-ai.com, diferente do site da edição internacional.", "国内版(WorkBuddy)": "Edição da China (WorkBuddy)", "国内站为 https://api.siliconflow.cn/v1": "O site da China é https://api.siliconflow.cn/v1", "国内站(minimaxi.com);OpenAI 兼容地址为 https://api.minimaxi.com/v1": "Site da China (minimaxi.com); o endereço compatível com OpenAI é https://api.minimaxi.com/v1", "国际版": "Edição internacional", "国际版 z.ai 的 Coding 套餐;OpenAI 兼容地址为 https://api.z.ai/api/coding/paas/v4": "Plano Coding do z.ai da edição internacional; o endereço compatível com OpenAI é https://api.z.ai/api/coding/paas/v4", "国际版与国内版是**两套独立的账号**(同一账号体系的两个站点),凭证不通用。": "As edições internacional e da China são **dois conjuntos independentes de contas** (dois sites do mesmo sistema de contas); as credenciais não são compartilhadas.", "国际版与国内版账号体系独立,请填国际版的凭证。未填 refreshToken 无法自动续期。": "As edições internacional e da China têm sistemas de contas independentes; preencha a credencial da edição internacional. Sem preencher o refreshToken, não é possível renovar automaticamente.", "国际版登录页默认只显示邮箱登录,勾选后恢复 Google / GitHub / X 入口": "A página de login da edição internacional mostra por padrão apenas o login por e-mail; marcar a opção restaura os acessos Google / GitHub / X", "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "O site de inferência da edição internacional é api.z.ai, diferente do site da edição da China; os planos também são independentes.", "国际版(WorkBuddy AI)": "Edição internacional (WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "Plano Coding do site internacional; o endereço compatível com OpenAI é https://api.minimax.io/v1", "在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。": "Obtenha os dois valores na página do AtomCode no AtomGit: accessToken e refreshToken. O refresh token é fortemente recomendado — o token deste provedor não é um JWT (expira em cerca de 7 dias e não traz expiração legível); sem ele a conta não consegue se renovar e precisará ser autorizada de novo ao expirar.", "在 cc-switch 里找到": "Encontradas", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "Conclua o login oficial na janela embutida; em caso de sucesso, a conta entra automaticamente na lista de contas.", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "Gere um PAT nas configurações da conta Qoder do {label} → Integrations (não use um token do Google / GitHub). Contas e credenciais não são compartilhadas entre as duas regiões.", "在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。": "Escaneie o QR code do WeChat na página de autorização oficial que abrir (o QR code aparece imediatamente). A conta é adicionada à lista automaticamente após a leitura bem-sucedida.", "在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "Na janela aberta, entre no site principal do KukuAI com sua conta Baidu (código SMS ou QR code, tanto faz); após o login, a conta entra automaticamente na lista. O gateway troca com o Baidu um token de sessão de negócio do KukuAI (requer o cliente KukuAI instalado nesta máquina).", "在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "Faça login com sua conta Google na janela que abrir (página de autorização do Antigravity); a conta é adicionada à lista automaticamente quando a autorização for concluída.", "在浏览器中打开登录页": "Abrir a página de login no navegador", "地区": "Região", "填写凭证": "Preencher credenciais", "填写凭证添加": "Preencher credenciais para adicionar", "备注名": "Rótulo", "套餐": "Plano", "如:智谱 GLM": "ex.: Zhipu GLM", "完成登录后自动加入账号列表,昵称取自上游。": "Após concluir o login, a conta entra automaticamente na lista; o apelido vem do upstream.", "官方客户端凭证目录:Windows 是 C:\\Users\\<你的用户名>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth,macOS 是 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth": "Diretório de credenciais do cliente oficial: Windows — C:\\Users\\\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth; macOS — ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth", "官方已确认到账": "Crédito confirmado oficialmente", "导入": "Importar", "导入中…": "Importando…", "导入凭证文件": "Importar arquivo de credenciais", "导入失败": "Falha na importação", "导入失败:{reason}": "Falha na importação: {reason}", "导入所选": "Importar selecionados", "导入所选({n})": "Importar selecionados ({n})", "导入桌面端登录态": "Importar sessão de login do desktop", "将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下": "A janela embutida será aberta; após a autorização, a conta entra automaticamente na lista de contas. Fechar a janela cancela a espera. No final, a página de autorização pode avisar que não conseguiu abrir o link zcode:// — isso é normal; o resultado já foi registrado no servidor", "将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)": "A janela embutida será aberta; após a autorização, a conta entra automaticamente na lista de contas. No painel remoto, é possível colar o endereço de callback. Fechar a janela cancela a espera. O link é válido por 5 minutos; se expirar ou não for clicado, ele é invalidado (você pode iniciar de novo)", "将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待": "A janela embutida será aberta; após o login, a conta entra automaticamente na lista. Fechar a janela cancela a espera", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 Trae 账号);同机完成后自动加入,Docker / 远程面板按提示粘贴地址栏回调地址。": "A página de autorização será aberta no navegador padrão do sistema (reaproveitando a conta Trae já conectada nele); na mesma máquina, a conta entra automaticamente ao concluir. Para Docker / painel remoto, cole o endereço de callback da barra de endereços conforme as instruções.", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 ZCode 账号);授权完成后自动加入账号列表。页面最后可能提示无法打开 zcode:// 链接,属正常现象": "A página de autorização será aberta no navegador padrão do sistema (reaproveitando a conta ZCode já conectada nele); após a autorização, a conta entra automaticamente na lista. No final, a página pode avisar que não conseguiu abrir o link zcode://; isso é normal", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "A página de autorização será aberta no navegador padrão do sistema (reaproveitando a conta Huawei Cloud já conectada nele); o gateway consulta a nuvem em polling para obter o código de autorização, então não importa em qual máquina o navegador roda. Ao concluir, a conta entra automaticamente na lista", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "A página de autorização será aberta no navegador padrão do sistema (reaproveitando a conta Huawei Cloud já conectada nele); se o navegador e o gateway não estiverem na mesma máquina, cole o endereço de callback final de volta no painel conforme as instruções acima", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "A página de login será aberta no navegador padrão do sistema (reaproveitando a conta Accio já conectada nele); após o login, a conta entra automaticamente na lista; fechar a janela cancela a espera", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "A página de login será aberta no navegador padrão do sistema (reaproveitando a conta LobsterAI já conectada nele); após o login, a conta entra automaticamente na lista; fechar a janela cancela a espera", "小米 MiMo Token Plan 套餐(新加坡站)": "Plano Token Plan do Xiaomi MiMo (site de Singapura)", "已到账": "Creditado", "已取消登录等待": "Espera de login cancelada", "已取消领取": "Resgate cancelado", "已取消验证码": "Captcha cancelado", "已填写手机号,正在发送验证码…": "Celular preenchido; enviando o código…", "已声明无需鉴权:账号会被正常选路,出网时不带任何鉴权头": "Marcada como sem necessidade de autenticação: a conta é roteada normalmente e nenhum cabeçalho de autenticação é enviado nas requisições de saída", "已导入 {ok} 个,失败 {fail} 个(失败项已标注在列表里)": "{ok} importados, {fail} com falha (os itens com falha estão marcados na lista)", "已建过 · {n} 个账号,点击继续添加": "Já criado · {n} contas, clique para adicionar mais", "已建过,点击继续添加账号": "Já criado, clique para adicionar outra conta", "已打开官方登录页,请在窗口中完成登录…": "Página de login oficial aberta; conclua o login na janela…", "已点发送,等待短信…": "Enviado; aguardando o SMS…", "已用系统默认浏览器打开登录页,请在浏览器中完成登录…": "Página de login aberta no navegador padrão do sistema; conclua o login no navegador…", "已领取,等官方确认": "Resgatado; aguardando confirmação oficial", "当前壳版本不支持 AutoClaw 网页登录,请更新应用": "Esta versão do aplicativo não suporta o login web do AutoClaw; atualize o aplicativo", "当前没有可领取的套餐(活动尚未开始)": "Nenhum plano disponível para resgate no momento (a campanha ainda não começou)", "当前环境不支持领取(桥接方法缺失)": "O resgate não é suportado neste ambiente (método de ponte ausente)", "必填,CatPaw 账号标识": "Obrigatório; identificador da conta CatPaw", "恢复 Google / GitHub 入口": "Restaurar acessos Google / GitHub", "手机号": "Celular", "手机验证码登录": "Login com código SMS", "打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。": "Abra a página de login oficial do LobsterAI (lobsterai.youdao.com) e entre com sua conta LobsterAI: após o login, a página oficial retorna a esta máquina e a conta entra automaticamente na lista — o callback chega à porta do próprio gateway, portanto o navegador precisa estar na mesma máquina que o gateway; o código de autorização só é entregue ao gateway localmente e nenhum token em texto claro é exibido na interface.", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "Abra a página de autorização oficial do Trae SOLO (trae.cn) e entre com sua conta Trae: ao concluir a autorização, a página oficial redireciona de volta para uma porta temporária desta máquina; implantações na mesma máquina recebem automaticamente, e no Docker / painel remoto copie o endereço de callback da barra de endereços conforme as instruções.", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "Abra a página de login oficial do Accio {label} ({site}) e entre com sua conta Accio: após o login, a página oficial redireciona de volta para esta máquina; na mesma máquina tudo se completa automaticamente, e no Docker / painel remoto basta colar o endereço de callback conforme as instruções (o código de autorização é passado ao gateway apenas nesta máquina, e nenhum token em texto claro é exibido na interface).", "打开 Accio {label}登录页": "Abrir a página de login do Accio {label}", "打开 Antigravity 网页登录": "Abrir login web do Antigravity", "打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。": "Abra a página de autorização do AtomGit e entre: ao concluir, o gateway consulta o broker automaticamente para obter o token e adiciona a conta (o callback é encerrado no broker e nunca volta para esta máquina, então não é preciso preencher a URL de callback).", "打开 CatPaw 网页登录": "Abrir o login web do CatPaw", "打开 Cline 授权页": "Abrir a página de autorização do Cline", "打开 CodeArts 授权页": "Abrir a página de autorização do CodeArts", "打开 LobsterAI 登录页": "Abrir a página de login do LobsterAI", "打开 Qoder 网页登录": "Abrir o login web do Qoder", "打开 Trae 授权页": "Abrir a página de autorização do Trae", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "Abra a página de autorização oficial do ZCode {label} e entre com sua conta. O resultado da autorização é registrado pelo servidor do ZCode; o gateway busca a credencial em segundo plano e a adiciona à lista de contas.", "打开 ZCode {label}授权页": "Abrir a página de autorização do ZCode {label}", "打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
    网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。": "Abra a página de autorização oficial do Huawei Cloud CodeArts para entrar: após o login, a página oficial leva o navegador de volta ao /oauth/callback do próprio gateway, e o gateway troca o código de autorização de uso único por credenciais temporárias e adiciona a conta à lista.
    Quando o gateway roda em outra máquina, a interface web pede para colar o endereço de callback final da barra de endereços de volta no painel.", "打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。": "Abra a página de autorização do Huawei Cloud e faça login: ao concluir, o gateway consulta a nuvem automaticamente para obter o código de autorização e adiciona a conta à lista (não é preciso instalar o aplicativo de desktop do OfficeAce nem colar manualmente uma URL de retorno).", "打开官方授权页完成设备码授权,登录的是{label}({site})的账号。": "Abra a página de autorização oficial e conclua a autorização por código de dispositivo; a conta usada é do {label} ({site}).", "打开授权页": "Abrir a página de autorização", "打开授权页完成确认,账号自动加入 {label}{note}": "Abra a página de autorização e confirme; a conta entra automaticamente em {label}{note}", "打开方式": "Como abrir", "打开网页登录": "Abrir login web", "探测失败:{message}": "Falha na detecção: {message}", "接入一个兼容上游": "Conectar um upstream compatível", "提交中…": "Enviando…", "搜索提供商": "Buscar provedores", "搜索提供商…": "Buscar provedores…", "整份粘贴官方插件 / CLIProxyAPI 落盘的凭据 JSON(形如 {\"codearts_provider_credential\":{…}},铺平的也行)。
    必填:access_key_id、secret_access_key、security_token。要能自动续期,必须连 refresh_token 与 oauth_context 一起粘 —— 临时凭据约一小时到期,缺这半块就续不回来,只能重新登录。": "Cole o JSON de credencial inteiro gravado pelo plugin oficial / CLIProxyAPI (no formato {\"codearts_provider_credential\":{…}}; a versão achatada também serve).
    Obrigatórios: access_key_id, secret_access_key, security_token.Para renovar automaticamente, é preciso colar também refresh_token e oauth_context — as credenciais temporárias expiram em cerca de uma hora, e sem essa parte não há como renovar; será preciso fazer login de novo.", "文件是{edition}凭证,已按该版本导入": "O arquivo contém credenciais da {edition} e foi importado como tal", "新建自定义提供商": "Novo provedor personalizado", "方案:{label}": "Esquema: {label}", "明文 JWT 或 auth.json 里的 enc: 加密值(自动解密)": "JWT em texto claro ou o valor criptografado enc: do auth.json (descriptografado automaticamente)", "明文 JWT(国际版账号的 access token)": "JWT em texto claro (o access token da conta da edição internacional)", "智谱 bigmodel.cn 的 Coding 套餐,Key 在 open.bigmodel.cn/usercenter/apikeys": "Plano Coding do bigmodel.cn da Zhipu; obtenha a Key em open.bigmodel.cn/usercenter/apikeys", "未知错误": "Erro desconhecido", "未能获取授权地址,请重试": "Não foi possível obter o endereço de autorização; tente novamente", "未能读取 cc-switch 数据": "Não foi possível ler os dados do cc-switch", "未领取成功": "Resgate não concluído", "本地 Ollama、OpenCode Zen 免费档这类上游要勾上,否则账号不可用": "Marque esta opção para upstreams como o Ollama local ou o nível gratuito do OpenCode Zen; caso contrário, a conta não funcionará", "本家有两个**互不替代**的凭证,按你要用的功能填,至少填一个:编码套餐 API Key 用于转发推理(打 {site})与查询窗口限额(每 N 小时 / 每周的剩余比例);jwt 用于领取套餐与查询余额(打 zcode.z.ai 的 billing 网关,官方叫 Coding Plan JWT,是一串三段点分的字符串)。余额两把都能查、但读到的东西不同:jwt 读到余额桶与套餐到期,API Key 读到窗口限额;而领取只认 jwt、转发只认 API Key。请填写 {label}账号的凭证 —— {plan}(最容易拿到的办法:直接用上方的「网页登录」—— 它会替你把这个 API Key 换好,两个凭证一起拿到。)": "Este provedor tem duas credenciais que **não se substituem**; preencha as que correspondem aos recursos que você vai usar, pelo menos uma: a API Key do Coding Plan serve para encaminhar inferência (acessa {site}) e para consultar os limites de janela (a porcentagem restante de cada janela de N horas / semanal); o jwt serve para resgatar planos e consultar saldo (acessa o gateway de billing em zcode.z.ai; oficialmente chamado Coding Plan JWT, uma string de três segmentos separados por ponto). As duas consultam saldo, mas leem coisas diferentes: o jwt lê os baldes de saldo e a expiração do plano, enquanto a API Key lê os limites de janela; e o resgate só aceita o jwt, o encaminhamento só aceita a API Key. Preencha a credencial da conta {label} — {plan} (a forma mais fácil: use o \"Login web\" acima — ele troca essa API Key por você e obtém as duas credenciais de uma vez.)", "本机 Ollama 的 OpenAI 兼容端点,无需 API Key(默认已勾上「该上游无需鉴权」,鉴权头一个都不发)": "Endpoint compatível com OpenAI do Ollama local; não precisa de API Key (a opção \"Este upstream não exige autenticação\" já vem marcada e nenhum cabeçalho de autenticação é enviado)", "本次未确认到账": "Crédito não confirmado desta vez", "条可导入": "importáveis", "条配置,其中": "configurações no cc-switch, das quais", "桌面运行时不可用(Tauri 未初始化)": "Runtime do desktop indisponível (Tauri não inicializado)", "模型网关基址": "Endereço base do gateway de modelos", "正在打开百度登录页…": "Abrindo a página de login do Baidu…", "正在扫描本机 cc-switch…": "Escaneando o cc-switch desta máquina…", "正在换发访客凭证…": "Solicitando uma credencial de visitante…", "正在添加…": "Adicionando…", "正在等待{name}登录完成;完成或取消后才能发起新的登录": "Aguardando a conclusão do login de {name}; só após concluir ou cancelar você pode iniciar um novo login", "每周": "Semanal", "每日": "Diário", "每月": "Mensal", "没有则无法自动续期": "Sem ele, não é possível renovar automaticamente", "没有匹配「{keyword}」的提供商": "Nenhum provedor corresponde a \"{keyword}\"", "添加 {name} 账号": "Adicionar conta {name}", "添加中…": "Adicionando…", "添加到 ": "Adicionar a ", "添加失败:{reason}": "Falha ao adicionar: {reason}", "添加小浣熊账号": "Adicionar conta Raccoon", "添加方式": "Forma de adicionar", "添加访客账号": "Adicionar uma conta de visitante", "添加账号": "Adicionar conta", "火山引擎 Coding 套餐;通用推理接入点为 https://ark.cn-beijing.volces.com/api/v3": "Plano Coding do Volcano Engine; o endpoint de inferência geral é https://ark.cn-beijing.volces.com/api/v3", "点击添加账号": "Clique para adicionar uma conta", "点开即预填,填 Key 接入": "Abre já preenchido; informe a Key para conectar", "点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
    这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。": "Após clicar no botão, primeiro passe por uma verificação de slider (controle de risco do provedor); em seguida a página de login abre e a conta é adicionada automaticamente ao concluir o login.
    Esta é a única forma oficial de login da edição internacional; se você já entrou no cliente, \"Importar sessão de login do desktop\" é mais rápido.", "用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。": "Entre com o celular vinculado à conta AutoClaw: clique em \"Obter código\", informe o código recebido por SMS abaixo e entre. O código é enviado desta máquina diretamente à API oficial, e nenhum token é exibido na interface.", "用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。": "Conclua o login com sua conta CatPaw (Meituan Passport); em caso de sucesso, a conta entra automaticamente na lista.", "用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。": "Entre com o celular vinculado à conta Loomy: clique em \"Obter código\" e informe o código. Esta é a forma mais simples neste provedor.", "用于转发;形如 apiKey.secret 两段点分(不填则这个账号不能转发)": "Usado para encaminhamento; no formato apiKey.secret, dois segmentos separados por ponto (se vazio, esta conta não pode encaminhar)", "用于领取套餐与查询余额;不填则这个账号不能领取与查余额": "Usado para resgatar planos e consultar saldo; se vazio, esta conta não pode resgatar nem consultar saldo", "用户 ID": "ID do usuário", "用绑定的手机号登录:点「获取验证码」后填入即可。这是国内版官方唯一的登录方式。": "Entre com o celular vinculado: clique em \"Obter código\" e informe o código. Esta é a única forma oficial de login da edição da China.", "留空则用提供商名称": "Se vazio, usa o nome do provedor", "登录 / 添加 {name} 账号": "Entrar / adicionar conta {name}", "登录 session(一长串)": "Sessão de login (uma string longa)", "登录中…": "Entrando…", "登录失败:{message}": "Falha no login: {message}", "登录已结束": "Login encerrado", "登录并添加": "Entrar e adicionar", "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "Caminho do arquivo de sessão de login: ~/.box-agent/config/auth.json (Windows: C:\\Users\\\\.box-agent\\config\\auth.json)", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "Excluir o provedor personalizado \"{name}\"? Isso também excluirá {count} contas desse provedor; a exclusão não pode ser desfeita.", "等待 Accio {label}登录完成…": "Aguardando a conclusão do login do Accio {label}…", "等待 AtomCode 登录完成…": "Aguardando o login do AtomCode…", "等待 CatPaw 登录完成…": "Aguardando a conclusão do login do CatPaw…", "等待 Cline 授权确认…": "Aguardando a confirmação de autorização do Cline…", "等待 CodeArts 登录完成…": "Aguardando a conclusão do login do CodeArts…", "等待 Google 授权完成…": "Aguardando a conclusão da autorização do Google…", "等待 KukuAI 登录完成…": "Aguardando a conclusão do login do KukuAI…", "等待 LobsterAI 登录完成…": "Aguardando a conclusão do login do LobsterAI…", "等待 OfficeAce 登录完成…": "Aguardando a conclusão do login do OfficeAce…", "等待 OrcaTerm 登录完成…": "Aguardando o login no OrcaTerm…", "等待 Qoder 授权完成…": "Aguardando a conclusão da autorização do Qoder…", "等待 Trae 授权完成…": "Aguardando a conclusão da autorização do Trae…", "等待 ZCode {label}授权完成…": "Aguardando a conclusão da autorização do ZCode {label}…", "等待小浣熊登录完成…": "Aguardando a conclusão do login do Raccoon…", "等待登录完成…": "Aguardando a conclusão do login…", "等待网页登录…": "Aguardando o login web…", "粘贴 API Key": "Colar API Key", "粘贴 Accio 的 accessToken": "Colar o accessToken do Accio", "粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
    这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。": "Cole o Google refresh token do Antigravity (começa com 1//; também pode colar a linha inteira que contém refresh_token=).
    Esta é uma entrada manual além do login web (por exemplo, exportada do IDE oficial em que você já está conectado). O refresh token não expira, e o gateway renova o access token automaticamente ao expirar; se o token for perdido, só resta refazer a autorização do Google, então guarde-o bem.", "粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。": "Cole a API Key do Command Code (começa com user_; obtenha no ~/.commandcode/auth.json do CLI oficial ou no studio web). Colar a linha inteira / com aspas / com o prefixo Bearer funciona; o gateway extrai a key pura sozinho. A key não expira e não há API de renovação, então basta obter uma nova se ela deixar de funcionar. Ao adicionar, o gateway faz uma verificação leve de atividade; uma falha não impede a adição.", "粘贴 Cookie": "Colar Cookie", "粘贴 Google refresh token": "Colar Google refresh token", "粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。": "Cole o accessToken OAuth do OrcaTerm (um JWT com três partes separadas por pontos; obtido do data.bin do cliente desktop ou do cookie ot_session da versão web). Também pode colar a linha inteira contendo Bearer / ot_session= — o gateway extrai apenas o token. Após adicionar, o gateway renova automaticamente (o upstream tem API de renovação), sem precisar acompanhar a expiração de 2 horas manualmente.", "粘贴 Qoder 个人访问令牌(pt-…)": "Colar o token de acesso pessoal do Qoder (pt-…)", "粘贴 accessToken": "Colar accessToken", "粘贴 access_token(一长串 JWT)": "Colar o access_token (um JWT longo)", "粘贴 session cookie": "Colar session cookie", "粘贴 token / refreshToken": "Colar token / refreshToken", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "Cole um token começando com workos:… (o prefixo é adicionado automaticamente se faltar)", "粘贴令牌对": "Colar par de tokens", "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "Cole um token de acesso do Zhipu Qingyan (um JWT com três partes separadas por ponto). Como obter: abra chatglm.cn no navegador, faça login e copie o valor do cookie chatglm_token em Application → Cookies nas ferramentas de desenvolvedor (colar a linha chatglm_token=…; inteira também funciona). O token é verificado uma vez na origem após a colagem, e a conta só é salva se passar; contas conectadas têm pontos e check-in, e o gateway renova automaticamente depois (a origem tem API de renovação).", "粘贴登录凭据": "Colar credenciais de login", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "O que você cola é a sessão de login (que também é o token do gateway de modelos). O Loomy não tem API de renovação; a sessão vale cerca de 14 dias, então basta entrar de novo após expirar.", "粘贴网关凭据": "Colar credenciais do gateway", "粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
    {siteNote}
    MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。": "Cole o valor do cookie de sessão de login web monkeycode_ai_session (copiar o Cookie inteiro no navegador e colar diretamente também funciona; o gateway extrai o valor sozinho).
    {siteNote}
    O MonkeyCode não tem API de renovação: a sessão vale cerca de 30 dias (limite rígido); quando expirar, entre novamente no site e cole mais uma vez.", "粘贴访问令牌": "Cole o token de acesso", "系统浏览器": "Navegador do sistema", "系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。": "O navegador do sistema pode reaproveitar a sessão de login existente (recomendado para a edição internacional); a janela embutida é mais limpa.", "系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表": "Abre no navegador do sistema (reaproveita a conta Cline já conectada); ao concluir, entra automaticamente na lista", "系统浏览器打开(复用已登录的 Google 账号);完成后自动加入列表": "Abre no navegador do sistema (reutiliza a conta Google já conectada); ao concluir, entra automaticamente na lista", "系统浏览器打开(复用已登录的 Zai / Google 账号);完成后自动加入列表": "Abre no navegador do sistema (reaproveita a conta Zai / Google já conectada); ao concluir, entra automaticamente na lista", "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "Abre no navegador do sistema (reaproveita a conta Meituan já conectada); ao concluir, entra automaticamente na lista", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "Abre no navegador do sistema (reaproveita a conta já conectada); ao concluir, entra automaticamente na lista", "系统默认浏览器": "Navegador padrão do sistema", "续期令牌": "Token de atualização", "编码套餐 API Key": "API Key do Coding Plan", "网关 Secret": "Segredo do gateway", "网页登录": "Login web", "网页登录的打开方式": "Como abrir o login web", "网页登录(Zai / Google)": "Login web (Zai / Google)", "自定义": "Personalizado", "自建聚合面板:填你自己的站点地址。余额查询用该账号的 API Key 打 {base}/v1/usage,不需要额外配置": "Painel de agregação próprio: preencha o endereço do seu site. A consulta de saldo chama {base}/v1/usage com a chave de API desta conta; sem configuração extra", "自建聚合面板:填你自己的站点地址;余额查询要另填 NewAPI 的个人访问令牌(见下方「余额查询」一段)": "Painel de agregação próprio: preencha o endereço do seu site; a consulta de saldo precisa também de um token de acesso pessoal do NewAPI (veja \"Consulta de saldo\" abaixo)", "获取风控配置失败,将直接尝试领取:{message}": "Falha ao obter a configuração de controle de risco; tentando resgatar diretamente: {message}", "获取验证码": "Obter código", "访客模式": "Modo visitante", "访客模式(免登录)": "Modo visitante (sem login)", "访问令牌": "Token de acesso", "该上游无需鉴权(不发送鉴权头)": "Este upstream não exige autenticação (nenhum cabeçalho de autenticação é enviado)", "该提供商": "Este provedor", "该提供商账号添加功能即将上线": "A adição de contas deste provedor chega em breve", "该自定义提供商已不存在(可能已被删除),请刷新后重试": "Este provedor personalizado não existe mais (pode ter sido excluído); atualize e tente novamente", "请先选择一个自定义提供商": "Selecione primeiro um provedor personalizado", "请在弹出的滑块中完成验证(官方要求的风控步骤)": "Conclua a verificação no slider exibido (etapa de controle de risco exigida pelo provedor)", "请填写 11 位大陆手机号": "Informe um número de celular da China continental com 11 dígitos", "请填写 6 位数字验证码": "Informe o código de 6 dígitos", "请填写 Base URL": "Preencha a Base URL", "请填写 {label}": "Preencha {label}", "请填写名称": "Preencha o nome", "请完成验证…": "Conclua a verificação…", "读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows": "Lê e descriptografa %APPDATA%/AutoClaw/auth.json; somente no Windows", "读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录": "Lê ~/.cline/data/settings/providers.json; requer login no cliente Cline", "读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录": "Lê ~/.meituan-catpaw/auth.json; requer login no cliente CatPaw", "读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录": "Lê a sessão de login do cliente KukuAI desta máquina (%APPDATA%\\baidugenflowpro\\Network\\Cookies); requer login no cliente KukuAI", "读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录": "Lê o estado de login do cliente OrcaTerm local (%APPDATA%\\com.orcaterm-desktop.app\\data.bin); é preciso estar logado no cliente OrcaTerm", "读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录": "Lê o estado de login do cliente 智谱清言 local (%APPDATA%\\chatglm\\Network\\Cookies); o cliente precisa estar conectado", "读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。": "Lê a conta atualmente conectada no cliente oficial (Tencent WorkBuddy / WorkBuddy AI) nesta máquina: o arquivo de sessão mais recente no diretório de credenciais é localizado automaticamente (é preciso estar conectado ao cliente oficial nesta máquina). Após a importação, o gateway renova o token automaticamente — não é preciso trocar tokens manualmente conforme a validade de 2 horas.", "读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。": "Lê a sessão de login atual do cliente desta máquina (Cookie em texto claro, sem descriptografia). Com o cliente em execução, o arquivo Cookies fica em uso exclusivo; feche o cliente KukuAI antes de importar.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。": "Lê a sessão de login atual do cliente desta máquina, em tempo real a cada vez (excluir este registro não afeta a sessão de login do cliente).", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后点「刷新 Token」同步。": "Lê a sessão de login atual do cliente desta máquina, em tempo real a cada vez (excluir este registro não afeta a sessão de login do cliente). Após entrar novamente no cliente, clique em \"Atualizar Token\" para sincronizar.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后重新导入即可。": "Lê a sessão de login atual do cliente desta máquina, em tempo real a cada vez (excluir este registro não afeta a sessão de login do cliente). Após entrar novamente no cliente, basta importar de novo.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。": "Lê a sessão de login atual do cliente desta máquina, em tempo real a cada vez (excluir este registro não afeta a sessão de login do cliente). Usuários que entram no cliente com Zai / Google devem usar esta opção.", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。": "Lê a sessão de login atual do cliente desta máquina, em tempo real a cada vez (excluir este registro não afeta a sessão de login do cliente). Esta opção fica em {label}.", "读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。": "Lê o token OAuth com que o cliente local está logado (JSON em texto claro, sem descriptografia). Após importar, o gateway renova automaticamente — não é preciso acompanhar a expiração de 2 horas do cliente.", "读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。": "Lê o estado de login atual do cliente local. Os cookies do cliente são criptografados com DPAPI (Chromium os_crypt), portanto isto funciona somente no Windows; o arquivo pode ficar ilegível enquanto o cliente está aberto — feche o cliente e tente de novo (ou use \"Preencher credenciais\" para colar um token).", "账号信息": "Informações da conta", "账号信息不完整,请刷新后重试": "Informações da conta incompletas; atualize e tente novamente", "账号已添加,但请留意:{warning}": "Conta adicionada, mas atenção: {warning}", "账号版本": "Edição da conta", "账号类型": "Tipo de conta", "账号:{name}
    ": "Conta: {name}
    ", "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 创建": "Usa a rota Inference Providers; crie a Key em hf.co/settings/tokens", "还没有账号": "Ainda não há contas", "这一家当前不支持网页登录,请改用填写凭证": "Este provedor não suporta login web no momento; use \"Preencher credenciais\"", "这个账号当前没有可自动领的活动(只自动领每日登录赠送积分)": "Esta conta não tem campanhas com resgate automático no momento (apenas os pontos de brinde do login diário são resgatados automaticamente)", "这份额度走「活动套餐」通道:若转发时提示编码套餐已到期,": "Esta cota passa pelo canal \"Start Plan\": se o encaminhamento avisar que o Coding Plan expirou, ", "这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。": "Aqui é a conta do site da China (monkeycode-ai.com): cole a sessão do site da China. O site internacional (monkeycode-ai.net) é outro provedor, com conta própria; as credenciais não são compartilhadas entre os dois.", "这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。": "Aqui é a conta do site internacional (monkeycode-ai.net): cole a sessão do site internacional. O site da China (monkeycode-ai.com) é outro provedor, com conta própria; as credenciais não são compartilhadas entre os dois.", "选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "Selecione o arquivo de sessão de login auth.json do cliente CatPaw (Windows: C:\\Users\\\\.meituan-catpaw\\auth.json; macOS / Linux: ~/.meituan-catpaw/auth.json). Após a importação, as credenciais ficam no gateway e podem ser encaminhadas de forma independente — sem depender mais do cliente desta máquina.", "选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。": "Selecione o arquivo de sessão de login providers.json do cliente Cline (Windows: C:\\Users\\\\.cline\\data\\settings\\providers.json; macOS / Linux: ~/.cline/data/settings/providers.json). Após a importação, as credenciais ficam no gateway e podem ser encaminhadas de forma independente — sem depender mais do cliente desta máquina; este registro entra em {label}.", "选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。": "Selecione o arquivo Cookies do cliente KukuAI (%APPDATA%\\baidugenflowpro\\Network\\Cookies): é um banco SQLite **binário** (sem extensão, não é JSON). A importação lê uma cópia do arquivo selecionado e não disputa o bloqueio com o cliente em execução, então **não é preciso fechar o cliente antes**. Após a importação, as credenciais ficam no gateway e podem ser encaminhadas de forma independente.", "选择凭证文件导入": "Importar de arquivo de credenciais", "选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。": "Selecione o arquivo de credenciais de sessão do cliente oficial (workbuddy-desktop.info / workbuddy-desktop-ai.info) ou um JSON de conta exportado pelo wb2api (formato aninhado ou plano).", "选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "Selecione o arquivo de sessão de login auth.json do cliente Raccoon (Windows: C:\\Users\\\\.box-agent\\config\\auth.json; macOS / Linux: ~/.box-agent/config/auth.json). Após a importação, as credenciais ficam no gateway e podem ser encaminhadas de forma independente — sem depender mais do cliente desta máquina.", "选择提供商": "Selecionar provedor", "选择的文件不是合法 JSON": "O arquivo selecionado não é um JSON válido", "选择要添加账号的提供商": "Selecione o provedor para adicionar uma conta", "重新扫描": "Escanear novamente", "鉴权": "Autenticação", "阿里云 Token Plan 套餐(国际站)": "Plano Token Plan do Alibaba Cloud (site internacional)", "阿里云百炼 Coding 套餐(国内站)": "Plano Coding do Alibaba Cloud Model Studio (site da China)", "阿里云百炼 Coding 套餐(国际站)": "Plano Coding do Alibaba Cloud Model Studio (site internacional)", "阿里云百炼模型服务(国际站,OpenAI 兼容模式)": "Serviço de modelos Alibaba Cloud Model Studio (site internacional, modo compatível com OpenAI)", "需要图形验证码:登录窗口已弹出,请填写后继续": "Captcha necessário: a janela de login foi aberta; preencha para continuar", "预置 API": "API predefinida", "领取 CodeArts 每日福利": "Resgatar benefício diário do CodeArts", "领取失败:{message}": "Falha ao resgatar: {message}", "领取并让官方确认": "Resgatar e aguardar confirmação oficial", "风控验证配置不完整,请稍后重试": "Configuração de verificação de controle de risco incompleta; tente novamente mais tarde", "验证码": "Código de verificação", "验证码已发送。收到后填入下方并点「登录并添加」": "Código enviado. Informe-o abaixo e clique em \"Entrar e adicionar\"", "验证码已发送,请查看手机短信": "Código enviado; verifique o SMS", "验证码已发送,请查看手机短信。填入下方并点「登录并添加」即完成登录": "Código enviado; verifique o SMS. Informe-o abaixo e clique em \"Entrar e adicionar\" para concluir o login", "验证码已发送,请查看短信": "Código enviado; verifique as mensagens", "验证码校验失败,请重试": "Falha na verificação do captcha; tente novamente", "验证码校验超时,请重试": "Tempo esgotado na verificação do captcha; tente novamente", "验证码组件不可用,请重试": "Componente de captcha indisponível; tente novamente", "验证码组件初始化超时,请重试": "Tempo esgotado ao inicializar o componente de captcha; tente novamente", "验证码组件加载失败,请检查网络后重试": "Falha ao carregar o componente de captcha; verifique a rede e tente novamente", "验证码组件加载异常,请重试": "Erro ao carregar o componente de captcha; tente novamente", "验证码组件加载超时,请检查网络后重试": "Tempo esgotado ao carregar o componente de captcha; verifique a rede e tente novamente", "验证码组件未加载,请重启应用后重试": "O componente de captcha não foi carregado; reinicie o aplicativo e tente novamente", "(免费额度池,模型名带 cline-free/ 前缀)。": " (pool de cota gratuita; nomes de modelo com o prefixo cline-free/).", "(其中 {n} 把恢复为不限制)": " ({n} das quais voltaram a ser ilimitadas)", "(必填)": " (obrigatório)", "(有效期 {window})": "(validade {window})", "(订阅池,模型名带 cline-pass/ 前缀,需要账号有对应订阅)。": " (pool de assinatura; nomes de modelo com o prefixo cline-pass/, e a conta precisa da assinatura correspondente).", ",下一次最快 {minutes} 分钟后": ", próxima tentativa em pelo menos {minutes} min", ",并从 {n} 把 Key 的可用提供商里移除": ", e removido dos provedores permitidos de {n} keys" }); diff --git a/desktop-tauri/ui/i18n/pt-BR/checkin.js b/desktop-tauri/ui/i18n/pt-BR/checkin.js index 0de0c780..66d5fd7f 100644 --- a/desktop-tauri/ui/i18n/pt-BR/checkin.js +++ b/desktop-tauri/ui/i18n/pt-BR/checkin.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:pt-BR/checkin —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · ": " · ", "SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新": "Resgate de checkin_credits do SOLO · redefinido às 0h do dia natural", "UMID 组件安装失败:{error}": "Falha ao instalar o componente UMID: {error}", "WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细": "WorkBuddy CN · Consulte a árvore de tarefas e acenda / resgate com um clique · inclui o evento de volta às aulas e a viagem do gato · clique na linha para detalhes", "WorkBuddy 国际版保活模型,按顺序回退": "Modelos do WorkBuddy internacional para manter ativo, com fallback em ordem", "WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关": "Modelos gratuitos da atividade diária do WorkBuddy Internacional · uma configuração global, independente dos agendamentos", "{date}(每天 {time})": "{date} (todos os dias às {time})", "{label} ×{count}({reason})": "{label} ×{count} ({reason})", "{n} 项可完成": "{n} para concluir", "{n} 项待领": "{n} itens a resgatar", "{total} 账号 · {done} 已签": "{total} contas · {done} com check-in", "{total} 项任务 · {pending} 项可完成": "{total} tarefas · {pending} para concluir", "✅ UMID 组件已安装,下轮签到即可领取国际版积分": "✅ Componente UMID instalado; no próximo check-in já dá para resgatar os pontos da edição internacional", "✅ {message}": "✅ {message}", "✅ 定时签到已开启": "✅ Check-in agendado ativado", "✅ 开学季任务执行完成": "✅ Tarefas de volta às aulas concluídas", "✅ 成长任务执行完成:积分 +{credit} 能量 +{energy}": "✅ Tarefas de crescimento concluídas: créditos +{credit}, energia +{energy}", "✅ 新手福利领取完成,累计 {n} 积分": "✅ Benefícios iniciais resgatados, {n} pontos no total", "✅ 签到完成:{succeeded}/{total} 个账号成功领取{active}": "✅ Check-in concluído: {succeeded}/{total} contas resgataram com sucesso{active}", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空提交恢复缺省({defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "A \"cadeia de modelos de keep-alive\" são os modelos gratuitos usados para manter contas do WorkBuddy Internacional ativas na atividade diária — tentados em ordem, parando no primeiro sucesso; envie vazio para restaurar o padrão ({defaults}). Deduplicação no dia + resgate idempotente: repetir a execução só retorna \"já resgatado\" e nunca pontua duas vezes.", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "A \"cadeia de modelos de keep-alive\" são os modelos gratuitos usados para manter contas do WorkBuddy Internacional ativas na atividade diária — tentados em ordem, parando no primeiro sucesso; limpar a lista e salvar restaura a cadeia padrão (padrão atual: {defaults}). Deduplicação no dia + resgate idempotente: repetir a execução só retorna \"já resgatado\" e nunca pontua duas vezes.", "「免费领积分」的每日任务 · 逐个领取": "Tarefa diária do “Ganhe pontos grátis” · resgate um a um", "一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单": "Benefício único · consultado e resgatado automaticamente após o check-in; depois de resgatado, fica registrado localmente e o upstream não é mais consultado · clique na linha para expandir a lista de tarefas", "一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证": "Resgata todas as recompensas do dia em uma única ação (check-in diário + presente de novos usuários) · idempotência garantida pelo upstream", "一键安装组件": "Instalar componente", "一键完成": "Concluir tudo", "一键领取({n})": "Resgatar todos ({n})", "上次执行:{line}": "Última execução: {line}", "上次签到时间": "Último check-in", "上次领取 {time} · 领取需通过滑块验证码": "Último resgate: {time} · o resgate exige captcha deslizante", "下次 {date}": "Próxima {date}", "下次 {date} · 点此管理条目": "Próxima {date} · clique para gerenciar os agendamentos", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它与 ZCode 的每日/套餐福利领取见「活动福利」。": "Fora do check-in diário: {list}. As recompensas únicas do CodeArts ficam em \"Benefícios iniciais\" abaixo; o resgate diário/de planos dele e do ZCode fica em \"Benefícios de atividade\".", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。": "Fora do check-in diário: {list}. As recompensas únicas do CodeArts ficam em \"Benefícios iniciais\" abaixo, e o benefício diário dele fica logo acima em \"Check-in diário\"; o resgate de planos do ZCode fica em \"Benefícios de atividade\".", "与国内版同一套任务接口 · 站点不同": "Mesma interface de tarefas da edição da China · site diferente", "今天已领 {n} 份": "{n} resgatado(s) hoje", "今天未领": "Não resgatado hoje", "今天(北京时间 {day})已领取 · 官方确认到账 {n} 项 · 明天可再领": "Resgatado hoje (horário de Pequim, {day}) · {n} itens confirmados oficialmente · amanhã é possível resgatar novamente", "今日已执行": "Executado hoje", "今日已派出": "Já enviado hoje", "今日已领取": "Resgatado hoje", "今日签到": "Check-in de hoje", "今日签到进度": "Progresso do check-in de hoje", "从清单里移除": "Remover da lista", "保活": "Manter ativo", "保活 {n}": "{n} mantidos ativos", "保活+领取": "Manter ativo + Resgatar", "保活中…": "Mantendo ativo…", "保活模型链": "Cadeia de modelos para manter ativo", "全选": "Selecionar tudo", "全部 {n} 家": "Todos os {n} provedores", "全部完成": "Tudo concluído", "全部查询": "Consultar todos", "全部结果": "Todos os resultados", "全部账号均可签到": "Todas as contas podem fazer check-in", "全部领取": "Resgatar todos", "关着:所有条目都不执行": "Desligado: nenhum agendamento é executado", "删除失败:{error}": "Falha ao excluir: {error}", "删除定时条目": "Excluir agendamento", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活并在网页通道建会话点亮日活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "No horário marcado, faz o check-in automático das contas habilitadas dos provedores marcados (WorkBuddy CN usa a API de check-in diário; a versão internacional usa a tarefa de atividade diária: detecta o evento, recebe a recompensa de atividade diária, mantém-se ativo com o modelo gratuito e abre uma sessão pelo canal web para acender a atividade do dia; o Raccoon usa o fluxo de pontos diários do desktop; o AutoClaw usa a tarefa de check-in diário do cliente oficial; o Qoder CN reivindica via evento, e contas sem evento atribuído recebem um aviso neutro; o Qoder internacional precisa primeiro de uma identidade de risco do dispositivo (gerada pelo componente UMID do cliente Qoder local / qodercli) para sequer ver o evento \"100 Credits diários\" — sem o componente, chega uma explicação clara em vez de um erro; o Trae recebe os pontos de check-in diário do SOLO (reiniciam à meia-noite), e quando o upstream não tem evento para a conta ou as credenciais não têm ID de dispositivo, chega \"não recebido + motivo\" em vez de erro; o Loomy credita os pontos de primeiro login uma vez por dia para a conta; o KukuAI recebe a tarefa diária \"Pontos grátis\"; o CodeArts recebe as vantagens operacionais do dia (receber → confirmar → verificar relendo; execuções automáticas limitadas a 6 requisições de escrita por dia, com 10 minutos de intervalo); o OfficeAce recebe todas as recompensas do dia de uma vez (check-in diário + brinde de novato)). Se a máquina estava desligada no horário, um check-in de reposição roda ao ligar — nada fica para trás. A API de check-in de cada provedor é idempotente: repetir nunca recebe duas vezes.", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "No horário definido, o check-in automático é feito nas contas disponíveis dos provedores marcados (o WorkBuddy edição da China usa a interface de check-in diário; a edição internacional usa as tarefas de atividade diária: detectar o evento, resgatar a recompensa de atividade diária e então manter a conta ativa com modelos gratuitos; o Raccoon usa o fluxo diário de pontos do aplicativo desktop; o AutoClaw usa a tarefa de check-in diário do cliente oficial; o Qoder China usa o resgate de evento, e contas sem evento distribuído recebem um aviso neutro; o Qoder internacional precisa primeiro da identidade de controle de risco do dispositivo (gerada pelo componente UMID do cliente Qoder / qodercli local) para enxergar o evento “100 Credits diários”, e sem o componente recebe uma explicação clara em vez de um erro; o Trae resgata os pontos de check-in diário do SOLO (redefinidos às 0h do dia natural), e quando o upstream não abriu o evento para a conta ou a credencial não tem o ID do dispositivo, o resultado é “não resgatado + motivo específico” em vez de um erro; o Loomy credita uma vez por dia os pontos de primeiro login da conta; o KukuAI resgata a tarefa diária do “Ganhe pontos grátis”); o CodeArts resgata o benefício de campanha do dia (resgatar → confirmar → reler para verificar; execuções automáticas têm limite de 6 gravações por dia com intervalo de 10 minutos); o OfficeAce resgata todas as recompensas do dia em uma única ação (check-in diário + presente de novos usuários)). Se o horário for perdido, o check-in é compensado automaticamente após o computador ser ligado, sem que nada se perca por o computador estar desligado no horário. As interfaces de check-in de cada provedor são idempotentes; execuções repetidas não resgatam duas vezes.", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "No horário definido, o check-in automático é feito nas contas disponíveis dos provedores marcados (o WorkBuddy edição da China usa a interface de check-in diário; a edição internacional usa as tarefas de atividade diária: detectar o evento, resgatar a recompensa de atividade diária e então manter a conta ativa com modelos gratuitos; o Raccoon usa o fluxo diário de pontos do aplicativo desktop; o AutoClaw usa a tarefa de check-in diário do cliente oficial; o Qoder China usa o resgate de evento, e contas sem evento distribuído recebem um aviso neutro; o Qoder internacional precisa primeiro da identidade de controle de risco do dispositivo (gerada pelo componente UMID do cliente Qoder / qodercli local) para enxergar o evento “100 Credits diários”, e sem o componente recebe uma explicação clara em vez de um erro; o Trae resgata os pontos de check-in diário do SOLO (redefinidos às 0h do dia natural), e quando o upstream não abriu o evento para a conta ou a credencial não tem o ID do dispositivo, o resultado é “não resgatado + motivo específico” em vez de um erro; o Loomy credita uma vez por dia os pontos de primeiro login da conta; o KukuAI resgata a tarefa diária do “Ganhe pontos grátis”); o OfficeAce resgata todas as recompensas do dia em uma única ação (check-in diário + presente de novos usuários)). Se o horário for perdido, o check-in é compensado automaticamente após o computador ser ligado, sem que nada se perca por o computador estar desligado no horário. As interfaces de check-in de cada provedor são idempotentes; execuções repetidas não resgatam duas vezes.", "到点时逐个签到,已勾 {n} / {total} 家": "{n} de {total} marcados — o check-in é feito um por um no horário", "北京时间 · 错过时点开机后会自动补签": "Horário de Pequim · execuções perdidas são compensadas automaticamente ao iniciar", "去领取": "Resgatar", "另有 {n} 个账号不参与每日签到": "Outras {n} contas não participam do check-in diário", "只领取已完成任务的奖励(幂等,重复点不会重复入账)": "Resgatar apenas recompensas de tarefas concluídas (idempotente; cliques repetidos não creditam em dobro)", "可完成": "Pode concluir", "可派出": "Pode enviar", "可选 · 缺省显示时刻": "Opcional · usa o horário como padrão", "可领奖": "Pode resgatar", "后端未返回定时签到数据": "O backend não retornou dados de agendamento", "后端未返回签到中心数据": "O backend não retornou os dados da central de check-in", "国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。": "O check-in da edição internacional exige o componente UMID para gerar a identidade de controle de risco do dispositivo (não detectado nesta máquina).", "在途": "Em viagem", "填入缺省链": "Preencher cadeia padrão", "填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)": "Escolha o horário e os provedores e clique em Salvar; o nome é opcional (quando vazio, mostra-se o horário)", "失败 {n}": "{n} com falha", "套餐明细": "Detalhes dos planos", "安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。": "Basta instalar o cliente Qoder ou o qodercli (não é preciso reiniciar o gateway).", "安装中…": "Instalando…", "官方客户端的每日签到任务": "Tarefa de check-in diário do cliente oficial", "定时签到": "Check-in agendado", "定时签到已关闭": "Check-in agendado desativado", "尚未查询": "Ainda não consultado", "尚未查询任务状态": "Status das tarefas ainda não consultado", "巡检一次": "Executar uma rodada", "已保存:{label}": "Salvo: {label}", "已保存:保活模型链": "Salvo: cadeia de modelos para manter ativo", "已保存:定时条目": "Salvo: agendamento", "已删除定时条目": "Agendamento excluído", "已完成活跃上报(连登已点亮),但活动未开启(本次没有可领的奖励)": "Atividade reportada (ofensiva acesa), mas a campanha não está ativa (nada para resgatar desta vez)", "已抽 {n} 次": "Sorteou {n} vezes", "已有结果 {checked} / 共 {total} 个账号": "Já com resultado: {checked} / {total} contas", "已签": "Com check-in", "已领 {claimed}/{total} · 累计 {earned} 积分": "Resgatado {claimed}/{total} · {earned} pontos acumulados", "已领养": "Adotado", "已领取": "Resgatado", "带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件": "Resgatar “100 Credits diários” com a identidade de controle de risco do dispositivo · requer o componente UMID", "开学季执行失败:{error}": "Falha na execução de volta às aulas: {error}", "开学季执行完成:{message}": "Execução de volta às aulas concluída: {message}", "开学季活动": "Evento de volta às aulas", "开着:启用的条目按各自时刻执行": "Ligado: agendamentos habilitados rodam em seus próprios horários", "待签": "Pendente", "待领新手福利": "Benefícios iniciais pendentes", "总开关": "Interruptor principal", "总开关已关闭 · 在「定时签到」页打开": "Interruptor principal desligado · ative na aba \"Check-in agendado\"", "成功 {n}": "{n} com sucesso", "成长任务": "Tarefas de crescimento", "成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}": "Tarefas de crescimento concluídas ({done} com sucesso, {failed} com falha): créditos +{credit}, energia +{energy}", "成长任务执行失败:{error}": "Falha na execução das tarefas de crescimento: {error}", "执行失败:{error}": "Falha na execução: {error}", "执行并抽奖": "Executar e sortear", "执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)": "Executar as tarefas de volta às aulas e gastar todas as chances de sorteio (compartilhar / conversar / eventos de especialista)", "把缺省链填进清单,再点「保存」生效": "Coloca a cadeia padrão na lista; clique em Salvar para aplicar", "抽奖机会 {n} 次": "{n} chances de sorteio", "按住拖动调整顺序": "Pressione e arraste para reordenar", "按住拖动,调整这个模型在链里的顺序": "Pressione e arraste para mudar a posição deste modelo na cadeia", "按序逐个尝试,共 {n} 个": "{n} modelos, tentados em ordem", "按结果筛选": "Filtrar por resultado", "探测 + 领取 + 保活 · 与定时签到同一套组合": "Detectar + resgatar + manter ativo · mesma combinação do check-in agendado", "探测活动并领取日活奖励 · 不做保活": "Detectar o evento e resgatar a recompensa de atividade diária · sem manter ativo", "接单 + 点亮 + 领奖(只处理已实测沉淀的任务)": "Aceitar + acender + resgatar (apenas tarefas com receita verificada)", "搜账号 / 收益 / 错误 / 触发原因": "Pesquisar conta / ganho / erro / motivo do disparo", "收起新建": "Fechar novo item", "新建定时条目": "Nova entrada agendada", "新手福利": "Benefícios iniciais", "新手福利有 {n} 项领取失败,可重试": "{n} item(ns) dos benefícios iniciais falharam no resgate — é possível tentar novamente", "无": "Nenhum", "无独立签到接口 · 每天替账号打一次首次登录积分": "Sem interface de check-in própria · credita uma vez por dia os pontos de primeiro login da conta", "无账号": "Sem contas", "暂不可领": "Indisponível", "暂不支持": "Ainda não suportado", "暂无启用的条目 · 点此添加": "Nenhum agendamento habilitado · clique para adicionar", "有未保存的改动": "Alterações não salvas", "未勾选提供商": "Nenhum provedor selecionado", "未开启": "Desativado", "未解锁": "Bloqueado", "未领养(领养需先完成当日对话量)": "Não adotado (a adoção exige cumprir a cota diária de conversas primeiro)", "未领取": "Não resgatado", "本地台账里今天还没领过;活动期内每天发一份新套餐": "O registro local ainda não tem resgate nenhum hoje; um novo plano é emitido todo dia durante o evento", "本地台账里今天(北京时间)领过的套餐:{plans}": "Planos resgatados hoje (horário de Pequim) conforme o registro local: {plans}", "条启用": "habilitado(s)", "查询于 {time}": "Consultado em {time}", "查询任务": "Consultar tarefas", "查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)": "Consulte a árvore de tarefas e acenda / resgate com um clique (inclui volta às aulas e viagem do gato)", "查询失败:{error}": "Falha na consulta: {error}", "桌面端每日积分链路": "Fluxo diário de pontos do aplicativo desktop", "正在读取定时签到数据…": "Carregando dados de agendamento…", "正在读取签到数据…": "Lendo os dados de check-in…", "每天 {time}": "Todos os dias às {time}", "每天 {time} · {scope}": "Diário {time} · {scope}", "每天时刻": "Horário diário", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "Uma linha por conta a cada execução · resultados e ganhos conforme registrado pelo backend", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "Tarefas de atividade diária · uma conversa no canal web acende a atividade diária, só o resgate adiciona pontos · os dois podem ser executados separadamente", "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "Pontos de login diário · resgata atividades de membro junto · contas de visitante não têm sistema de pontos (ignorado)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "Benefício diário · resgatar → confirmar → reler para verificar · redefinido no dia natural (horário de Pequim)", "每日签到": "Check-in diário", "每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签": "Cada agendamento tem horário e escopo próprios · deduplicação diária por item · execuções perdidas são compensadas ao iniciar", "每账号一条明细 · 点此查看「签到记录」": "Uma linha por conta · abra \"Registros de check-in\"", "没有可领套餐的账号(ZCode 未添加)。": "Nenhuma conta com planos resgatáveis (ZCode não adicionado).", "没有可领福利的账号(CodeArts / ZCode 未添加)。": "Nenhuma conta com benefícios para resgatar (CodeArts / ZCode não foram adicionados).", "没有支持成长任务的账号(需要 WorkBuddy 国内版)。": "Nenhuma conta suporta tarefas de crescimento (requer WorkBuddy CN).", "没有支持新手福利的账号": "Nenhuma conta suporta benefícios iniciais", "没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。": "Nenhuma conta suporta benefícios iniciais — atualmente Loomy, Raccoon e CodeArts.", "没有符合筛选条件的签到记录": "Nenhum registro de check-in corresponde aos filtros", "没有需要执行的任务": "Nada para executar", "活动福利": "Recompensas de evento", "活动(campaign)领取 · 每天 10:00 刷新": "Resgate de eventos (campaign) · atualizado todos os dias às 10:00", "活跃上报与保活均未成功,且活动未开启": "Tanto o relatório de atividade quanto o keep-alive falharam, e a campanha não está ativa", "活跃上报完成,但保活未成功且活动未开启": "Atividade reportada, mas o keep-alive falhou e a campanha não está ativa", "活跃上报完成,但活动未开启": "Atividade reportada, mas a campanha não está ativa", "活跃上报成功": "Atividade reportada com sucesso", "活跃保活失败": "Falha ao manter a atividade", "活跃保活完成": "Atividade mantida", "活跃保活完成,但活动未开启": "Atividade mantida, mas o evento não está aberto", "活跃保活完成,但活跃上报失败且活动未开启": "Keep-alive concluído, mas o relatório de atividade falhou e a campanha não está ativa", "添加条目": "Adicionar agendamento", "添加模型": "Adicionar modelo", "清单为空 —— 保存后恢复缺省链": "A lista está vazia — salvar restaura a cadeia padrão", "清单里已经有「{name}」": "A lista já contém \"{name}\"", "清空失败:{error}": "Falha ao limpar: {error}", "清空签到记录": "Limpar registros de check-in", "点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领": "Clique em \"Detalhes dos planos\" para ver quais você recebeu e quando entram em vigor; dá para resgatar de novo todo dia durante o evento", "点击行展开账号明细 · 单账号签到不受范围限制": "Clique na linha para expandir os detalhes das contas · o check-in individual não é limitado pelo escopo", "独立链路 · 只提供手动入口 · 不进自动签到": "Fluxo independente · apenas entrada manual · não participa do check-in automático", "猫猫旅行": "Viagem do gato", "猫猫旅行失败:{error}": "Falha na viagem do gato: {error}", "猫猫旅行失败:{message}": "Falha na viagem do gato: {message}", "猫猫旅行:{message}": "Viagem do gato: {message}", "用免费模型与网页会话维持账号活跃 · 不领取奖励": "Manter a conta ativa com modelos gratuitos e uma conversa no canal web · sem resgatar recompensas", "确定删除「{name}」?删除后到点不再执行。": "Excluir \"{name}\"? Ele não será mais executado no horário agendado.", "确定清空全部签到记录?清空后无法恢复。": "Limpar todos os registros de check-in? Isso não pode ser desfeito.", "确定清空当前筛选出的签到记录?清空后无法恢复。": "Limpar os registros de check-in filtrados? Isso não pode ser desfeito.", "移除 {name}": "Remover {name}", "立即全部签到": "Fazer check-in de todos agora", "签到": "Check-in", "签到中…": "Fazendo check-in…", "签到中心分区": "Seções do centro de check-in", "签到失败:{error}": "Falha no check-in: {error}", "签到完成:{succeeded}/{total} 成功{active},{failed} 个失败": "Check-in concluído: {succeeded}/{total} com sucesso{active}; {failed} com falha", "签到成功": "Check-in bem-sucedido", "签到提供商": "Provedores de check-in", "签到范围": "Escopo do check-in", "签到触发时刻": "Horário de disparo do check-in", "签到记录": "Registros de check-in", "签到记录时间范围": "Período dos registros de check-in", "签到记录表": "Tabela de registros de check-in", "累计签到记录": "Total de registros de check-in", "结算于 {time}": "Liquidado em {time}", "能量 {n}": "Energia {n}", "腾讯每日签到接口 · 仅国内版": "Interface de check-in diário da Tencent · apenas edição da China", "至少勾一家才能保存(不勾等于这条条目不签到)": "Selecione ao menos um provedor para salvar (sem seleção, esta entrada não faz check-in)", "读取定时签到数据失败:{error}": "Falha ao carregar dados de agendamento: {error}", "读取签到数据失败:{error}": "Falha ao ler os dados de check-in: {error}", "读取签到记录失败:{error}": "Falha ao carregar registros de check-in: {error}", "跳过 {n}": "{n} ignorados", "运营活动交付(领取 → 确认 → 回读核实)· 领的是套餐赠送积分": "Entrega de eventos oficiais (resgatar → confirmar → reler para verificar) · resgate dos pontos de bônus do plano", "还没执行过": "Ainda não executado", "还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。": "Nenhum agendamento ainda — clique em \"Adicionar agendamento\" para criar um, ou faça check-in manualmente no \"Centro de check-in\".", "还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。": "Nenhum registro de check-in ainda — os detalhes aparecem aqui após um check-in manual ou uma execução agendada.", "连登 {n} 天": "Sequência de {n} dias", "部分": "Parcial", "重签": "Refazer check-in", "重试": "Tentar de novo", "限时体验套餐 · 领取需滑块验证码 · 逐份查看与领取": "Plano de teste por tempo limitado · o resgate exige CAPTCHA de arrastar · veja e resgate um a um", "限时体验套餐(start-plan),活动期内每天一份 · 领取需滑块验证码": "Plano de teste por tempo limitado (start-plan), um por dia durante o evento · o resgate exige captcha deslizante", "需真实操作": "Requer ação real", "非活动期": "Evento inativo", "项": "itens", "领取": "Resgatar", "领取中": "Resgatando", "领取中…": "Resgatando…", "领取失败": "Falha no resgate", "领取失败:{error}": "Falha no resgate: {error}", "(共 {n} 个账号)": "({n} contas no total)", "(能量 / 连登读数本次未取到)": "(Leituras de energia / sequência indisponíveis desta vez)", ",日活保活 {n} 个": ", {n} mantidas ativas" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · ": " · ", "SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新": "Resgate de checkin_credits do SOLO · redefinido às 0h do dia natural", "UMID 组件安装失败:{error}": "Falha ao instalar o componente UMID: {error}", "WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细": "WorkBuddy CN · Consulte a árvore de tarefas e acenda / resgate com um clique · inclui o evento de volta às aulas e a viagem do gato · clique na linha para detalhes", "WorkBuddy 国际版保活模型,按顺序回退": "Modelos do WorkBuddy internacional para manter ativo, com fallback em ordem", "WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关": "Modelos gratuitos da atividade diária do WorkBuddy Internacional · uma configuração global, independente dos agendamentos", "{date}(每天 {time})": "{date} (todos os dias às {time})", "{label} ×{count}({reason})": "{label} ×{count} ({reason})", "{n} 项可完成": "{n} para concluir", "{n} 项待领": "{n} itens a resgatar", "{total} 账号 · {done} 已签": "{total} contas · {done} com check-in", "{total} 项任务 · {pending} 项可完成": "{total} tarefas · {pending} para concluir", "✅ UMID 组件已安装,下轮签到即可领取国际版积分": "✅ Componente UMID instalado; no próximo check-in já dá para resgatar os pontos da edição internacional", "✅ {message}": "✅ {message}", "✅ 定时签到已开启": "✅ Check-in agendado ativado", "✅ 开学季任务执行完成": "✅ Tarefas de volta às aulas concluídas", "✅ 成长任务执行完成:积分 +{credit} 能量 +{energy}": "✅ Tarefas de crescimento concluídas: créditos +{credit}, energia +{energy}", "✅ 新手福利领取完成,累计 {n} 积分": "✅ Benefícios iniciais resgatados, {n} pontos no total", "✅ 签到完成:{succeeded}/{total} 个账号成功领取{active}": "✅ Check-in concluído: {succeeded}/{total} contas resgataram com sucesso{active}", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空提交恢复缺省({defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "A \"cadeia de modelos de keep-alive\" são os modelos gratuitos usados para manter contas do WorkBuddy Internacional ativas na atividade diária — tentados em ordem, parando no primeiro sucesso; envie vazio para restaurar o padrão ({defaults}). Deduplicação no dia + resgate idempotente: repetir a execução só retorna \"já resgatado\" e nunca pontua duas vezes.", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "A \"cadeia de modelos de keep-alive\" são os modelos gratuitos usados para manter contas do WorkBuddy Internacional ativas na atividade diária — tentados em ordem, parando no primeiro sucesso; limpar a lista e salvar restaura a cadeia padrão (padrão atual: {defaults}). Deduplicação no dia + resgate idempotente: repetir a execução só retorna \"já resgatado\" e nunca pontua duas vezes.", "「免费领积分」的每日任务 · 逐个领取": "Tarefa diária do “Ganhe pontos grátis” · resgate um a um", "一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单": "Benefício único · consultado e resgatado automaticamente após o check-in; depois de resgatado, fica registrado localmente e o upstream não é mais consultado · clique na linha para expandir a lista de tarefas", "一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证": "Resgata todas as recompensas do dia em uma única ação (check-in diário + presente de novos usuários) · idempotência garantida pelo upstream", "一键安装组件": "Instalar componente", "一键完成": "Concluir tudo", "一键领取({n})": "Resgatar todos ({n})", "上次执行:{line}": "Última execução: {line}", "上次签到时间": "Último check-in", "上次领取 {time} · 领取需通过滑块验证码": "Último resgate: {time} · o resgate exige captcha deslizante", "下次 {date}": "Próxima {date}", "下次 {date} · 点此管理条目": "Próxima {date} · clique para gerenciar os agendamentos", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它与 ZCode 的每日/套餐福利领取见「活动福利」。": "Fora do check-in diário: {list}. As recompensas únicas do CodeArts ficam em \"Benefícios iniciais\" abaixo; o resgate diário/de planos dele e do ZCode fica em \"Benefícios de atividade\".", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。": "Fora do check-in diário: {list}. As recompensas únicas do CodeArts ficam em \"Benefícios iniciais\" abaixo, e o benefício diário dele fica logo acima em \"Check-in diário\"; o resgate de planos do ZCode fica em \"Benefícios de atividade\".", "与国内版同一套任务接口 · 站点不同": "Mesma interface de tarefas da edição da China · site diferente", "今天已领 {n} 份": "{n} resgatado(s) hoje", "今天未领": "Não resgatado hoje", "今天(北京时间 {day})已领取 · 官方确认到账 {n} 项 · 明天可再领": "Resgatado hoje (horário de Pequim, {day}) · {n} itens confirmados oficialmente · amanhã é possível resgatar novamente", "今日已执行": "Executado hoje", "今日已派出": "Já enviado hoje", "今日已领取": "Resgatado hoje", "今日签到": "Check-in de hoje", "今日签到进度": "Progresso do check-in de hoje", "从清单里移除": "Remover da lista", "保活": "Manter ativo", "保活 {n}": "{n} mantidos ativos", "保活+领取": "Manter ativo + Resgatar", "保活中…": "Mantendo ativo…", "保活模型链": "Cadeia de modelos para manter ativo", "全选": "Selecionar tudo", "全部 {n} 家": "Todos os {n} provedores", "全部完成": "Tudo concluído", "全部查询": "Consultar todos", "全部结果": "Todos os resultados", "全部账号均可签到": "Todas as contas podem fazer check-in", "全部领取": "Resgatar todos", "关着:所有条目都不执行": "Desligado: nenhum agendamento é executado", "删除失败:{error}": "Falha ao excluir: {error}", "删除定时条目": "Excluir agendamento", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活并在网页通道建会话点亮日活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "No horário marcado, faz o check-in automático das contas habilitadas dos provedores marcados (WorkBuddy CN usa a API de check-in diário; a versão internacional usa a tarefa de atividade diária: detecta o evento, recebe a recompensa de atividade diária, mantém-se ativo com o modelo gratuito e abre uma sessão pelo canal web para acender a atividade do dia; o Raccoon usa o fluxo de pontos diários do desktop; o AutoClaw usa a tarefa de check-in diário do cliente oficial; o Qoder CN reivindica via evento, e contas sem evento atribuído recebem um aviso neutro; o Qoder internacional precisa primeiro de uma identidade de risco do dispositivo (gerada pelo componente UMID do cliente Qoder local / qodercli) para sequer ver o evento \"100 Credits diários\" — sem o componente, chega uma explicação clara em vez de um erro; o Trae recebe os pontos de check-in diário do SOLO (reiniciam à meia-noite), e quando o upstream não tem evento para a conta ou as credenciais não têm ID de dispositivo, chega \"não recebido + motivo\" em vez de erro; o Loomy credita os pontos de primeiro login uma vez por dia para a conta; o KukuAI recebe a tarefa diária \"Pontos grátis\"; o CodeArts recebe as vantagens operacionais do dia (receber → confirmar → verificar relendo; execuções automáticas limitadas a 6 requisições de escrita por dia, com 10 minutos de intervalo); o OfficeAce recebe todas as recompensas do dia de uma vez (check-in diário + brinde de novato)). Se a máquina estava desligada no horário, um check-in de reposição roda ao ligar — nada fica para trás. A API de check-in de cada provedor é idempotente: repetir nunca recebe duas vezes.", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "No horário definido, o check-in automático é feito nas contas disponíveis dos provedores marcados (o WorkBuddy edição da China usa a interface de check-in diário; a edição internacional usa as tarefas de atividade diária: detectar o evento, resgatar a recompensa de atividade diária e então manter a conta ativa com modelos gratuitos; o Raccoon usa o fluxo diário de pontos do aplicativo desktop; o AutoClaw usa a tarefa de check-in diário do cliente oficial; o Qoder China usa o resgate de evento, e contas sem evento distribuído recebem um aviso neutro; o Qoder internacional precisa primeiro da identidade de controle de risco do dispositivo (gerada pelo componente UMID do cliente Qoder / qodercli local) para enxergar o evento “100 Credits diários”, e sem o componente recebe uma explicação clara em vez de um erro; o Trae resgata os pontos de check-in diário do SOLO (redefinidos às 0h do dia natural), e quando o upstream não abriu o evento para a conta ou a credencial não tem o ID do dispositivo, o resultado é “não resgatado + motivo específico” em vez de um erro; o Loomy credita uma vez por dia os pontos de primeiro login da conta; o KukuAI resgata a tarefa diária do “Ganhe pontos grátis”); o CodeArts resgata o benefício de campanha do dia (resgatar → confirmar → reler para verificar; execuções automáticas têm limite de 6 gravações por dia com intervalo de 10 minutos); o OfficeAce resgata todas as recompensas do dia em uma única ação (check-in diário + presente de novos usuários)). Se o horário for perdido, o check-in é compensado automaticamente após o computador ser ligado, sem que nada se perca por o computador estar desligado no horário. As interfaces de check-in de cada provedor são idempotentes; execuções repetidas não resgatam duas vezes.", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "No horário definido, o check-in automático é feito nas contas disponíveis dos provedores marcados (o WorkBuddy edição da China usa a interface de check-in diário; a edição internacional usa as tarefas de atividade diária: detectar o evento, resgatar a recompensa de atividade diária e então manter a conta ativa com modelos gratuitos; o Raccoon usa o fluxo diário de pontos do aplicativo desktop; o AutoClaw usa a tarefa de check-in diário do cliente oficial; o Qoder China usa o resgate de evento, e contas sem evento distribuído recebem um aviso neutro; o Qoder internacional precisa primeiro da identidade de controle de risco do dispositivo (gerada pelo componente UMID do cliente Qoder / qodercli local) para enxergar o evento “100 Credits diários”, e sem o componente recebe uma explicação clara em vez de um erro; o Trae resgata os pontos de check-in diário do SOLO (redefinidos às 0h do dia natural), e quando o upstream não abriu o evento para a conta ou a credencial não tem o ID do dispositivo, o resultado é “não resgatado + motivo específico” em vez de um erro; o Loomy credita uma vez por dia os pontos de primeiro login da conta; o KukuAI resgata a tarefa diária do “Ganhe pontos grátis”); o OfficeAce resgata todas as recompensas do dia em uma única ação (check-in diário + presente de novos usuários)). Se o horário for perdido, o check-in é compensado automaticamente após o computador ser ligado, sem que nada se perca por o computador estar desligado no horário. As interfaces de check-in de cada provedor são idempotentes; execuções repetidas não resgatam duas vezes.", "到点时逐个签到,已勾 {n} / {total} 家": "{n} de {total} marcados — o check-in é feito um por um no horário", "北京时间 · 错过时点开机后会自动补签": "Horário de Pequim · execuções perdidas são compensadas automaticamente ao iniciar", "去领取": "Resgatar", "另有 {n} 个账号不参与每日签到": "Outras {n} contas não participam do check-in diário", "只领取已完成任务的奖励(幂等,重复点不会重复入账)": "Resgatar apenas recompensas de tarefas concluídas (idempotente; cliques repetidos não creditam em dobro)", "可完成": "Pode concluir", "可派出": "Pode enviar", "可选 · 缺省显示时刻": "Opcional · usa o horário como padrão", "可领奖": "Pode resgatar", "后端未返回定时签到数据": "O backend não retornou dados de agendamento", "后端未返回签到中心数据": "O backend não retornou os dados da central de check-in", "国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。": "O check-in da edição internacional exige o componente UMID para gerar a identidade de controle de risco do dispositivo (não detectado nesta máquina).", "在途": "Em viagem", "填入缺省链": "Preencher cadeia padrão", "填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)": "Escolha o horário e os provedores e clique em Salvar; o nome é opcional (quando vazio, mostra-se o horário)", "失败 {n}": "{n} com falha", "套餐明细": "Detalhes dos planos", "安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。": "Basta instalar o cliente Qoder ou o qodercli (não é preciso reiniciar o gateway).", "安装中…": "Instalando…", "官方客户端的每日签到任务": "Tarefa de check-in diário do cliente oficial", "定时签到": "Check-in agendado", "定时签到已关闭": "Check-in agendado desativado", "尚未查询": "Ainda não consultado", "尚未查询任务状态": "Status das tarefas ainda não consultado", "巡检一次": "Executar uma rodada", "已保存:{label}": "Salvo: {label}", "已保存:保活模型链": "Salvo: cadeia de modelos para manter ativo", "已保存:定时条目": "Salvo: agendamento", "已删除定时条目": "Agendamento excluído", "已完成活跃上报(连登已点亮),但活动未开启(本次没有可领的奖励)": "Atividade reportada (ofensiva acesa), mas a campanha não está ativa (nada para resgatar desta vez)", "已抽 {n} 次": "Sorteou {n} vezes", "已有结果 {checked} / 共 {total} 个账号": "Já com resultado: {checked} / {total} contas", "已签": "Com check-in", "已领 {claimed}/{total} · 累计 {earned} 积分": "Resgatado {claimed}/{total} · {earned} pontos acumulados", "已领养": "Adotado", "已领取": "Resgatado", "带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件": "Resgatar “100 Credits diários” com a identidade de controle de risco do dispositivo · requer o componente UMID", "开学季执行失败:{error}": "Falha na execução de volta às aulas: {error}", "开学季执行完成:{message}": "Execução de volta às aulas concluída: {message}", "开学季活动": "Evento de volta às aulas", "开着:启用的条目按各自时刻执行": "Ligado: agendamentos habilitados rodam em seus próprios horários", "待签": "Pendente", "待领新手福利": "Benefícios iniciais pendentes", "总开关": "Interruptor principal", "总开关已关闭 · 在「定时签到」页打开": "Interruptor principal desligado · ative na aba \"Check-in agendado\"", "成功 {n}": "{n} com sucesso", "成长任务": "Tarefas de crescimento", "成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}": "Tarefas de crescimento concluídas ({done} com sucesso, {failed} com falha): créditos +{credit}, energia +{energy}", "成长任务执行失败:{error}": "Falha na execução das tarefas de crescimento: {error}", "执行失败:{error}": "Falha na execução: {error}", "执行并抽奖": "Executar e sortear", "执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)": "Executar as tarefas de volta às aulas e gastar todas as chances de sorteio (compartilhar / conversar / eventos de especialista)", "把缺省链填进清单,再点「保存」生效": "Coloca a cadeia padrão na lista; clique em Salvar para aplicar", "抽奖机会 {n} 次": "{n} chances de sorteio", "按住拖动调整顺序": "Pressione e arraste para reordenar", "按住拖动,调整这个模型在链里的顺序": "Pressione e arraste para mudar a posição deste modelo na cadeia", "按序逐个尝试,共 {n} 个": "{n} modelos, tentados em ordem", "按结果筛选": "Filtrar por resultado", "探测 + 领取 + 保活 · 与定时签到同一套组合": "Detectar + resgatar + manter ativo · mesma combinação do check-in agendado", "探测活动并领取日活奖励 · 不做保活": "Detectar o evento e resgatar a recompensa de atividade diária · sem manter ativo", "接单 + 点亮 + 领奖(只处理已实测沉淀的任务)": "Aceitar + acender + resgatar (apenas tarefas com receita verificada)", "搜账号 / 收益 / 错误 / 触发原因": "Pesquisar conta / ganho / erro / motivo do disparo", "收起新建": "Fechar novo item", "新建定时条目": "Nova entrada agendada", "新手福利": "Benefícios iniciais", "新手福利有 {n} 项领取失败,可重试": "{n} item(ns) dos benefícios iniciais falharam no resgate — é possível tentar novamente", "无": "Nenhum", "无独立签到接口 · 每天替账号打一次首次登录积分": "Sem interface de check-in própria · credita uma vez por dia os pontos de primeiro login da conta", "无账号": "Sem contas", "暂不可领": "Indisponível", "暂不支持": "Ainda não suportado", "暂无启用的条目 · 点此添加": "Nenhum agendamento habilitado · clique para adicionar", "有未保存的改动": "Alterações não salvas", "未勾选提供商": "Nenhum provedor selecionado", "未开启": "Desativado", "未解锁": "Bloqueado", "未领养(领养需先完成当日对话量)": "Não adotado (a adoção exige cumprir a cota diária de conversas primeiro)", "未领取": "Não resgatado", "本地台账里今天还没领过;活动期内每天发一份新套餐": "O registro local ainda não tem resgate nenhum hoje; um novo plano é emitido todo dia durante o evento", "本地台账里今天(北京时间)领过的套餐:{plans}": "Planos resgatados hoje (horário de Pequim) conforme o registro local: {plans}", "条启用": "habilitado(s)", "查询于 {time}": "Consultado em {time}", "查询任务": "Consultar tarefas", "查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)": "Consulte a árvore de tarefas e acenda / resgate com um clique (inclui volta às aulas e viagem do gato)", "查询失败:{error}": "Falha na consulta: {error}", "桌面端每日积分链路": "Fluxo diário de pontos do aplicativo desktop", "正在读取定时签到数据…": "Carregando dados de agendamento…", "正在读取签到数据…": "Lendo os dados de check-in…", "每天 {time}": "Todos os dias às {time}", "每天 {time} · {scope}": "Diário {time} · {scope}", "每天时刻": "Horário diário", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "Uma linha por conta a cada execução · resultados e ganhos conforme registrado pelo backend", "每日活动签到(+100 积分,已领则中性提示)": "Check-in de atividade diária (+100 créditos; aviso neutro se já resgatado)", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "Tarefas de atividade diária · uma conversa no canal web acende a atividade diária, só o resgate adiciona pontos · os dois podem ser executados separadamente", "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "Pontos de login diário · resgata atividades de membro junto · contas de visitante não têm sistema de pontos (ignorado)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "Benefício diário · resgatar → confirmar → reler para verificar · redefinido no dia natural (horário de Pequim)", "每日签到": "Check-in diário", "每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签": "Cada agendamento tem horário e escopo próprios · deduplicação diária por item · execuções perdidas são compensadas ao iniciar", "每账号一条明细 · 点此查看「签到记录」": "Uma linha por conta · abra \"Registros de check-in\"", "没有可领套餐的账号(ZCode 未添加)。": "Nenhuma conta com planos resgatáveis (ZCode não adicionado).", "没有可领福利的账号(CodeArts / ZCode 未添加)。": "Nenhuma conta com benefícios para resgatar (CodeArts / ZCode não foram adicionados).", "没有支持成长任务的账号(需要 WorkBuddy 国内版)。": "Nenhuma conta suporta tarefas de crescimento (requer WorkBuddy CN).", "没有支持新手福利的账号": "Nenhuma conta suporta benefícios iniciais", "没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。": "Nenhuma conta suporta benefícios iniciais — atualmente Loomy, Raccoon e CodeArts.", "没有符合筛选条件的签到记录": "Nenhum registro de check-in corresponde aos filtros", "没有需要执行的任务": "Nada para executar", "活动福利": "Recompensas de evento", "活动(campaign)领取 · 每天 10:00 刷新": "Resgate de eventos (campaign) · atualizado todos os dias às 10:00", "活跃上报与保活均未成功,且活动未开启": "Tanto o relatório de atividade quanto o keep-alive falharam, e a campanha não está ativa", "活跃上报完成,但保活未成功且活动未开启": "Atividade reportada, mas o keep-alive falhou e a campanha não está ativa", "活跃上报完成,但活动未开启": "Atividade reportada, mas a campanha não está ativa", "活跃上报成功": "Atividade reportada com sucesso", "活跃保活失败": "Falha ao manter a atividade", "活跃保活完成": "Atividade mantida", "活跃保活完成,但活动未开启": "Atividade mantida, mas o evento não está aberto", "活跃保活完成,但活跃上报失败且活动未开启": "Keep-alive concluído, mas o relatório de atividade falhou e a campanha não está ativa", "添加条目": "Adicionar agendamento", "添加模型": "Adicionar modelo", "清单为空 —— 保存后恢复缺省链": "A lista está vazia — salvar restaura a cadeia padrão", "清单里已经有「{name}」": "A lista já contém \"{name}\"", "清空失败:{error}": "Falha ao limpar: {error}", "清空签到记录": "Limpar registros de check-in", "点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领": "Clique em \"Detalhes dos planos\" para ver quais você recebeu e quando entram em vigor; dá para resgatar de novo todo dia durante o evento", "点击行展开账号明细 · 单账号签到不受范围限制": "Clique na linha para expandir os detalhes das contas · o check-in individual não é limitado pelo escopo", "独立链路 · 只提供手动入口 · 不进自动签到": "Fluxo independente · apenas entrada manual · não participa do check-in automático", "猫猫旅行": "Viagem do gato", "猫猫旅行失败:{error}": "Falha na viagem do gato: {error}", "猫猫旅行失败:{message}": "Falha na viagem do gato: {message}", "猫猫旅行:{message}": "Viagem do gato: {message}", "用免费模型与网页会话维持账号活跃 · 不领取奖励": "Manter a conta ativa com modelos gratuitos e uma conversa no canal web · sem resgatar recompensas", "确定删除「{name}」?删除后到点不再执行。": "Excluir \"{name}\"? Ele não será mais executado no horário agendado.", "确定清空全部签到记录?清空后无法恢复。": "Limpar todos os registros de check-in? Isso não pode ser desfeito.", "确定清空当前筛选出的签到记录?清空后无法恢复。": "Limpar os registros de check-in filtrados? Isso não pode ser desfeito.", "移除 {name}": "Remover {name}", "立即全部签到": "Fazer check-in de todos agora", "签到": "Check-in", "签到中…": "Fazendo check-in…", "签到中心分区": "Seções do centro de check-in", "签到失败:{error}": "Falha no check-in: {error}", "签到完成:{succeeded}/{total} 成功{active},{failed} 个失败": "Check-in concluído: {succeeded}/{total} com sucesso{active}; {failed} com falha", "签到成功": "Check-in bem-sucedido", "签到提供商": "Provedores de check-in", "签到范围": "Escopo do check-in", "签到触发时刻": "Horário de disparo do check-in", "签到记录": "Registros de check-in", "签到记录时间范围": "Período dos registros de check-in", "签到记录表": "Tabela de registros de check-in", "累计签到记录": "Total de registros de check-in", "结算于 {time}": "Liquidado em {time}", "能量 {n}": "Energia {n}", "腾讯每日签到接口 · 仅国内版": "Interface de check-in diário da Tencent · apenas edição da China", "至少勾一家才能保存(不勾等于这条条目不签到)": "Selecione ao menos um provedor para salvar (sem seleção, esta entrada não faz check-in)", "读取定时签到数据失败:{error}": "Falha ao carregar dados de agendamento: {error}", "读取签到数据失败:{error}": "Falha ao ler os dados de check-in: {error}", "读取签到记录失败:{error}": "Falha ao carregar registros de check-in: {error}", "跳过 {n}": "{n} ignorados", "运营活动交付(领取 → 确认 → 回读核实)· 领的是套餐赠送积分": "Entrega de eventos oficiais (resgatar → confirmar → reler para verificar) · resgate dos pontos de bônus do plano", "还没执行过": "Ainda não executado", "还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。": "Nenhum agendamento ainda — clique em \"Adicionar agendamento\" para criar um, ou faça check-in manualmente no \"Centro de check-in\".", "还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。": "Nenhum registro de check-in ainda — os detalhes aparecem aqui após um check-in manual ou uma execução agendada.", "连登 {n} 天": "Sequência de {n} dias", "部分": "Parcial", "重签": "Refazer check-in", "重试": "Tentar de novo", "限时体验套餐 · 领取需滑块验证码 · 逐份查看与领取": "Plano de teste por tempo limitado · o resgate exige CAPTCHA de arrastar · veja e resgate um a um", "限时体验套餐(start-plan),活动期内每天一份 · 领取需滑块验证码": "Plano de teste por tempo limitado (start-plan), um por dia durante o evento · o resgate exige captcha deslizante", "需真实操作": "Requer ação real", "非活动期": "Evento inativo", "项": "itens", "领取": "Resgatar", "领取中": "Resgatando", "领取中…": "Resgatando…", "领取免费 CodingPlan(按需:套餐有效时只回读额度)": "Resgatar o CodingPlan gratuito (sob demanda: com o plano ativo apenas lê a cota)", "领取失败": "Falha no resgate", "领取失败:{error}": "Falha no resgate: {error}", "(共 {n} 个账号)": "({n} contas no total)", "(能量 / 连登读数本次未取到)": "(Leituras de energia / sequência indisponíveis desta vez)", ",日活保活 {n} 个": ", {n} mantidas ativas" }); diff --git a/desktop-tauri/ui/i18n/pt-BR/requests.js b/desktop-tauri/ui/i18n/pt-BR/requests.js index 7b5715c7..7ea658d9 100644 --- a/desktop-tauri/ui/i18n/pt-BR/requests.js +++ b/desktop-tauri/ui/i18n/pt-BR/requests.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:pt-BR/requests —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · {n} 进行中": " · {n} em andamento", " · 会话粘住": " · sessão presa", "

    无响应原文
    ": "
    Sem resposta original
    ", "
    无法解析响应内容
    ": "
    Não foi possível interpretar o conteúdo da resposta
    ", "
    无法解析请求体(不是含 messages 数组的 JSON)
    ": "
    Não foi possível interpretar o corpo da requisição (não é um JSON com um array messages)
    ", "
    无请求原文
    ": "
    Sem requisição original
    ", "
    调用工具 {name}
    ": "
    Chamada de ferramenta {name}
    ", "响应原文": "Resposta original", "思考过程": "Processo de raciocínio", "请求原文": "Requisição original", "HTTP {status},但响应体阶段出错": "HTTP {status}, mas o erro ocorreu na fase do corpo da resposta", "HTTP {status},但响应体阶段出错:见错误列": "HTTP {status}, mas o erro ocorreu na fase do corpo da resposta: ver a coluna Erro", "{duration} / 首帧 {first}": "{duration} / 1ª resposta {first}", "{m}分{s}秒": "{m}min {s}s", "{n}秒": "{n}s", "{s}秒{ms}ms": "{s}s{ms}ms", "↻ {n} 次": "↻ {n}x", "↻ 重试 {n} 次": "↻ {n} retries", "✅ 压缩完成": "✅ Compactação concluída", "「仅看进行中」开启时,状态固定为进行中": "Com “Somente em andamento” ativado, o status fica fixo em Em andamento", "上游模型繁忙,请求已排进上游队列;网关正按上游建议的时长等待后重发(不是登录态或额度问题)": "O modelo do upstream está ocupado e a requisição entrou na fila do upstream; o Gateway aguarda o tempo sugerido pelo upstream antes de reenviar (não é problema de estado de login nem de cota)", "上游状态码: {status}": "Status do upstream: {status}", "上游请求已发出,正在等第一个字节到达(模型的思考时间也在这段)": "A requisição foi enviada ao upstream; aguardando a chegada do primeiro byte (o tempo de raciocínio do modelo também faz parte disso)", "上游返回错误": "O upstream retornou um erro", "下游请求的模型:{model}": "Modelo solicitado pelo downstream: {model}", "仅清空报文原文": "Limpar apenas os payloads", "仅看进行中": "Somente em andamento", "令牌": "Tokens", "体积": "Tamanho", "保留统计行与报表,只抹掉请求 / 响应正文。清理后详情弹窗的「预览对话」将无原文可看,统计数字分毫不动。": "Mantém as linhas de estatísticas e os relatórios e apaga apenas o corpo da requisição / da resposta. Após a limpeza, a “Prévia da conversa” no diálogo de detalhes não terá mais o texto original; os números das estatísticas não mudam em nada.", "全部删除": "Excluir tudo", "全部提供商": "Todos os provedores", "全部模型": "Todos os modelos", "全部状态": "Todos os status", "共 {n} 次尝试": "{n} tentativas no total", "共 {n} 次尝试,最终由 {provider} 承载": "{n} tentativas no total; a última foi atendida por {provider}", "其它": "Outro", "内容按原样保存,请求头中的凭据字段已替换为 [redacted]": "Conteúdo salvo como está; os campos de credenciais nos cabeçalhos da requisição foram substituídos por [redacted]", "内部重试": "Retries internos", "切换路径:": "Caminho de troca: ", "列表数据已刷新,请重试(关闭后重新打开这条详情)": "Os dados da lista foram atualizados; tente novamente (feche e reabra este detalhe)", "删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。": "Exclui os próprios registros de log. Com filtros ativos, exclui apenas os itens correspondentes e os relatórios agregados por dia não são afetados; sem filtros, limpa tudo e redefine os relatórios. Esta ação não pode ser desfeita.", "压缩中…": "Compactando…", "压缩失败:{message}": "Falha na compactação: {message}", "压缩已在后台开始,完成后会自动提示": "A compactação começou em segundo plano; você será avisado quando ela terminar", "压缩已在进行中": "A compactação já está em andamento", "压缩已在进行中,接上它的进度等待完成": "A compactação já está em andamento; retomando o progresso dela e aguardando a conclusão", "压缩数据库": "Compactar banco de dados", "压缩正在后台执行,完成后自动恢复": "A compactação está em execução em segundo plano; tudo volta ao normal automaticamente quando ela terminar", "原始报文": "Payload bruto", "另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)": "Mais {missing} tentativas sem detalhes registrados (mantidas apenas as {kept} primeiras)", "另有 {n} 个词命中": "Mais {n} palavras atingidas", "只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)": "Mostra apenas requisições em encaminhamento (a coluna Status mostra a fase: Conectando / Aguardando resposta / Respondendo / Retentando; a coluna Duração mostra o tempo decorrido)", "只看失败": "Somente falhas", "只看成功": "Somente sucessos", "只看进行中": "Somente em andamento", "命中了敏感词": "Atingiu uma palavra sensível", "命中的敏感词": "Palavras sensíveis atingidas", "响应中": "Respondendo", "响应体": "Corpo da resposta", "响应头": "Cabeçalhos da resposta", "回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)": "Recupera o espaço em disco ocupado por dados excluídos (checkpoint + VACUUM, em segundo plano)", "在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文": "Ative o “Modo debug” em Configurações → Geral para que novas requisições passem a salvar o payload", "失败请求不记录用量": "Requisições com falha não registram uso", "失败({status}):{error}": "Falhou ({status}): {error}", "失败:{error}": "Falhou: {error}", "实际发给上游 {bytes} 字节": "{bytes} bytes enviados de fato ao upstream", "客户端": "Cliente", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "Serão excluídos {count} logs · {raw} ainda com payload · tamanho do banco: {db}", "尝试 {n} ·": "Tentativa {n} ·", "尝试明细": "Detalhes das tentativas", "工具结果": "Resultado da ferramenta", "工具结果 · {id}": "Resultado da ferramenta · {id}", "工具结果图片": "Imagem do resultado da ferramenta", "已删除 {count} 条": "{count} registros excluídos", "已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」": "Abortagem aceita: a conexão com o upstream foi encerrada; a lista será atualizada em instantes como “Requisição abortada manualmente”", "已抹掉 {count} 条报文原文": "{count} payloads removidos", "已用时(请求仍在转发中)": "Tempo decorrido (a requisição ainda está sendo encaminhada)", "成功": "Sucesso", "成功({status})": "Sucesso ({status})", "执行清理": "Executar limpeza", "报文超过单条上限,请求体 / 响应体已按上限截断": "O payload ultrapassou o limite por registro; o corpo da requisição / da resposta foi truncado no limite", "抹掉原文": "Remover payloads", "按提供商筛选": "Filtrar por provedor", "按提供商筛选(只列日志里出现过的)": "Filtrar por provedor (lista apenas os que aparecem nos logs)", "按时间倒序,全量保留": "Ordem cronológica inversa; mantido integralmente", "按模型筛选": "Filtrar por modelo", "按模型筛选(只列日志里出现过的;精确匹配模型名)": "Filtrar por modelo (lista apenas os que aparecem nos logs; correspondência exata com o nome do modelo)", "按请求结果筛选": "Filtrar pelo resultado da requisição", "排队中": "Na fila", "提供商 / 账号": "Provedor / Conta", "提供商: {provider}": "Provedor: {provider}", "提示": "Aviso", "敏": "PS", "敏感词": "Palavras sensíveis", "断开上游连接,明细记为「请求已被手动终止」": "Encerra a conexão com o upstream e registra nos detalhes como “Requisição abortada manualmente”", "无报文原文(未开启「调试模式」,或已被清理)": "Sem payload original (o modo de depuração estava desativado, ou o conteúdo foi limpo)", "无结果记录": "Sem resultado registrado", "时间": "Data/hora", "暂无请求日志,网关还没有转发过请求": "Ainda não há logs de requisições; o Gateway ainda não encaminhou nenhuma requisição", "最终承载:{picked}": "Atendida por fim por: {picked}", "本轮尝试失败,正在退避等待或切换到下一个账号": "Esta rodada falhou; aguardando o backoff ou trocando para a próxima conta", "来源客户端:{client}": "Cliente de origem: {client}", "查看该请求的上游原始报文": "Ver o payload bruto do upstream desta requisição", "模型": "Modelo", "模型测试": "Teste de modelo", "模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计": "Requisição iniciada pelo “Teste” na coluna de ações da página de gerenciamento de modelos: usa a mesma rota de encaminhamento das requisições reais, mas não entra nas estatísticas dos relatórios", "正在加载请求日志…": "Carregando logs de requisições…", "正在统计…": "Calculando…", "正文超过单条上限,可能已被截断": "O texto ultrapassou o limite por registro e pode ter sido truncado", "没有符合筛选条件的请求": "Nenhuma requisição corresponde aos filtros", "清理": "Limpar", "清理中…": "Limpando…", "清理失败:{message}": "Falha na limpeza: {message}", "清理方式": "Modo de limpeza", "清理请求日志": "Limpar logs de requisições", "生成速度:输出 token ÷ 流式段用时(总用时 − 首响);流式段不足总用时一半时按总用时计": "Velocidade de geração: tokens de saída ÷ tempo de streaming (duração total − 1ª resposta); usa a duração total quando a janela de streaming é menor que metade da requisição", "用时": "Duração", "用量": "Uso", "确定对全部请求日志执行「{label}」?将处理 {count} 条,此操作无法恢复。": "Confirmar “{label}” em todos os logs de requisições? Serão processados {count} registros. Esta ação não pode ser desfeita.", "确定按当前筛选执行「{label}」?将处理 {count} 条,此操作无法恢复。": "Confirmar “{label}” com os filtros atuais? Serão processados {count} registros. Esta ação não pode ser desfeita.", "确定终止这条正在转发的请求?
    上游连接会立即断开,明细将记为「请求已被手动终止」。": "Tem certeza de que deseja abortar esta requisição em encaminhamento?
    A conexão com o upstream será encerrada imediatamente e os detalhes serão registrados como “Requisição abortada manualmente”.", "等待响应": "Aguardando resposta", "策略 {strategy} · 会话粘性 {sticky}{hit}": "Estratégia {strategy} · Aderência {sticky}{hit}", "终止": "Abortar", "终止失败:{error}": "Falha ao abortar: {error}", "终止请求": "Abortar requisição", "结果": "Resultado", "缓存读取: - / 命中率: -": "Leituras de cache: - / taxa de acerto: -", "缓存读取: {cache} / 命中率: {rate}": "Leituras de cache: {cache} / taxa de acerto: {rate}", "耗时": "Duração", "被跳过:": "Puladas: ", "识别到 User-Agent,但不属于已知的客户端类型:{ua}": "User-Agent detectado, mas não é um tipo de cliente conhecido: {ua}", "详情": "Detalhes", "详情内容": "Conteúdo dos detalhes", "请求体": "Corpo da requisição", "请求图片": "Imagem da requisição", "请求在任何账号接手之前就失败了": "A requisição falhou antes de qualquer conta assumi-la", "请求头": "Cabeçalhos da requisição", "请求已受理,正在选路、取凭证、建立上游连接": "Requisição aceita; selecionando rota, obtendo credenciais e estabelecendo a conexão com o upstream", "请求日志列表": "Lista de logs de requisições", "请求日志时间范围": "Período dos logs de requisições", "请求日志表": "Tabela de logs de requisições", "请求正在转发中,用时列显示的是已用时": "A requisição está sendo encaminhada; a coluna Duração mostra o tempo decorrido", "请求详情": "Detalhes da requisição", "读取请求日志失败,详见控制台": "Falha ao ler os logs de requisições; veja o console", "路由回放": "Replay de roteamento", "转发到上游的模型:{model}": "Modelo encaminhado ao upstream: {model}", "轮次": "Rodada", "输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}": "Entrada {input} · Saída {output} · Total {total} · Leitura de cache {cache}", "这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计": "Este detalhe é um teste de modelo (iniciado manualmente): usa a mesma rota de encaminhamento das requisições reais, mas não entra nas estatísticas dos relatórios", "这条明细没有记录提供商(旧数据或请求未走到转发)": "Este detalhe não registrou provedor (dados antigos ou a requisição falhou antes do encaminhamento)", "这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)": "Nenhum cliente de origem registrado nesta entrada (dados antigos, teste de modelo ou solicitação sem User-Agent)", "这条记录命中了敏感词,但没有留下命中明细": "Este registro atingiu palavras sensíveis, mas não deixou detalhes das ocorrências", "这条记录的尝试明细未采集(该字段上线前的旧数据,或请求在转发前就失败)": "Os detalhes das tentativas deste registro não foram coletados (dados antigos, de antes do lançamento deste campo, ou a requisição falhou antes do encaminhamento)", "这条请求没有保存这一段报文": "Esta requisição não salvou este trecho do payload", "进入「{phase}」阶段已持续 {time}": "Na fase “{phase}” há {time}", "进行中": "Em andamento", "进行中…": "Em andamento…", "连接中": "Conectando", "重试 {n}": "Retry {n}", "重试中": "Retentando", "重试次数": "Retries", "预览对话": "Prévia da conversa", "预览读取失败": "Falha ao ler a prévia", "预览读取失败:{error}": "Falha ao ler a prévia: {error}", "首响 {time}": "1ª resposta {time}", "首响:上游首帧到达的耗时": "1ª resposta: tempo até o primeiro frame do upstream chegar", "首响:上游首帧到达的耗时(请求仍在转发中)": "1ª resposta: tempo até o primeiro frame do upstream chegar (a requisição ainda está sendo encaminhada)", "首帧已到,上游内容正在下发": "O primeiro frame chegou; o upstream está enviando o conteúdo", "(请求仍在转发中)": "(a requisição ainda está sendo encaminhada)", "(账号:{account})": "(conta: {account})", ",{n}毫秒后重试": ", retry em {n}ms", ",{n}秒后重试": ", retry em {n}s" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · {n} 进行中": " · {n} em andamento", " · 会话粘住": " · sessão presa", "
    无响应原文
    ": "
    Sem resposta original
    ", "
    无法解析响应内容
    ": "
    Não foi possível interpretar o conteúdo da resposta
    ", "
    无法解析请求体(不是含 messages 数组的 JSON)
    ": "
    Não foi possível interpretar o corpo da requisição (não é um JSON com um array messages)
    ", "
    无请求原文
    ": "
    Sem requisição original
    ", "
    调用工具 {name}
    ": "
    Chamada de ferramenta {name}
    ", "响应原文": "Resposta original", "思考过程": "Processo de raciocínio", "请求原文": "Requisição original", "HTTP {status},但响应体阶段出错": "HTTP {status}, mas o erro ocorreu na fase do corpo da resposta", "HTTP {status},但响应体阶段出错:见错误列": "HTTP {status}, mas o erro ocorreu na fase do corpo da resposta: ver a coluna Erro", "{duration} / 首帧 {first}": "{duration} / 1ª resposta {first}", "{m}分{s}秒": "{m}min {s}s", "{n}秒": "{n}s", "{s}秒{ms}ms": "{s}s{ms}ms", "↻ {n} 次": "↻ {n}x", "↻ 重试 {n} 次": "↻ {n} retries", "✅ 压缩完成": "✅ Compactação concluída", "「仅看进行中」开启时,状态固定为进行中": "Com “Somente em andamento” ativado, o status fica fixo em Em andamento", "上游模型繁忙,请求已排进上游队列;网关正按上游建议的时长等待后重发(不是登录态或额度问题)": "O modelo do upstream está ocupado e a requisição entrou na fila do upstream; o Gateway aguarda o tempo sugerido pelo upstream antes de reenviar (não é problema de estado de login nem de cota)", "上游状态码: {status}": "Status do upstream: {status}", "上游请求已发出,正在等第一个字节到达(模型的思考时间也在这段)": "A requisição foi enviada ao upstream; aguardando a chegada do primeiro byte (o tempo de raciocínio do modelo também faz parte disso)", "上游返回错误": "O upstream retornou um erro", "下游请求的模型:{model}": "Modelo solicitado pelo downstream: {model}", "仅清空报文原文": "Limpar apenas os payloads", "仅看进行中": "Somente em andamento", "令牌": "Tokens", "体积": "Tamanho", "保留统计行与报表,只抹掉请求 / 响应正文。清理后详情弹窗的「预览对话」将无原文可看,统计数字分毫不动。": "Mantém as linhas de estatísticas e os relatórios e apaga apenas o corpo da requisição / da resposta. Após a limpeza, a “Prévia da conversa” no diálogo de detalhes não terá mais o texto original; os números das estatísticas não mudam em nada.", "全部删除": "Excluir tudo", "全部提供商": "Todos os provedores", "全部模型": "Todos os modelos", "全部状态": "Todos os status", "共 {n} 次尝试": "{n} tentativas no total", "共 {n} 次尝试,最终由 {provider} 承载": "{n} tentativas no total; a última foi atendida por {provider}", "其它": "Outro", "内容按原样保存,请求头中的凭据字段已替换为 [redacted]": "Conteúdo salvo como está; os campos de credenciais nos cabeçalhos da requisição foram substituídos por [redacted]", "内部重试": "Retries internos", "切换路径:": "Caminho de troca: ", "列表数据已刷新,请重试(关闭后重新打开这条详情)": "Os dados da lista foram atualizados; tente novamente (feche e reabra este detalhe)", "删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。": "Exclui os próprios registros de log. Com filtros ativos, exclui apenas os itens correspondentes e os relatórios agregados por dia não são afetados; sem filtros, limpa tudo e redefine os relatórios. Esta ação não pode ser desfeita.", "压缩中…": "Compactando…", "压缩失败:{message}": "Falha na compactação: {message}", "压缩已在后台开始,完成后会自动提示": "A compactação começou em segundo plano; você será avisado quando ela terminar", "压缩已在进行中": "A compactação já está em andamento", "压缩已在进行中,接上它的进度等待完成": "A compactação já está em andamento; retomando o progresso dela e aguardando a conclusão", "压缩数据库": "Compactar banco de dados", "压缩正在后台执行,完成后自动恢复": "A compactação está em execução em segundo plano; tudo volta ao normal automaticamente quando ela terminar", "原始报文": "Payload bruto", "另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)": "Mais {missing} tentativas sem detalhes registrados (mantidas apenas as {kept} primeiras)", "另有 {n} 个词命中": "Mais {n} palavras atingidas", "只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)": "Mostra apenas requisições em encaminhamento (a coluna Status mostra a fase: Conectando / Aguardando resposta / Respondendo / Retentando; a coluna Duração mostra o tempo decorrido)", "只看失败": "Somente falhas", "只看成功": "Somente sucessos", "只看进行中": "Somente em andamento", "命中了敏感词": "Atingiu uma palavra sensível", "命中的敏感词": "Palavras sensíveis atingidas", "响应中": "Respondendo", "响应体": "Corpo da resposta", "响应头": "Cabeçalhos da resposta", "回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)": "Recupera o espaço em disco ocupado por dados excluídos (checkpoint + VACUUM, em segundo plano)", "在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文": "Ative o “Modo debug” em Configurações → Geral para que novas requisições passem a salvar o payload", "失败请求不记录用量": "Requisições com falha não registram uso", "失败({status}):{error}": "Falhou ({status}): {error}", "失败:{error}": "Falhou: {error}", "实际下发给客户端 {bytes} 字节": "{bytes} bytes realmente entregues ao cliente", "实际发给上游 {bytes} 字节": "{bytes} bytes enviados de fato ao upstream", "客户端": "Cliente", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "Serão excluídos {count} logs · {raw} ainda com payload · tamanho do banco: {db}", "尝试 {n} ·": "Tentativa {n} ·", "尝试明细": "Detalhes das tentativas", "工具结果": "Resultado da ferramenta", "工具结果 · {id}": "Resultado da ferramenta · {id}", "工具结果图片": "Imagem do resultado da ferramenta", "已下发": "Enviado", "已删除 {count} 条": "{count} registros excluídos", "已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」": "Abortagem aceita: a conexão com o upstream foi encerrada; a lista será atualizada em instantes como “Requisição abortada manualmente”", "已抹掉 {count} 条报文原文": "{count} payloads removidos", "已用时(请求仍在转发中)": "Tempo decorrido (a requisição ainda está sendo encaminhada)", "成功": "Sucesso", "成功({status})": "Sucesso ({status})", "执行清理": "Executar limpeza", "报文超过单条上限,请求体 / 响应体已按上限截断": "O payload ultrapassou o limite por registro; o corpo da requisição / da resposta foi truncado no limite", "抹掉原文": "Remover payloads", "按提供商筛选": "Filtrar por provedor", "按提供商筛选(只列日志里出现过的)": "Filtrar por provedor (lista apenas os que aparecem nos logs)", "按时间倒序,全量保留": "Ordem cronológica inversa; mantido integralmente", "按模型筛选": "Filtrar por modelo", "按模型筛选(只列日志里出现过的;精确匹配模型名)": "Filtrar por modelo (lista apenas os que aparecem nos logs; correspondência exata com o nome do modelo)", "按请求结果筛选": "Filtrar pelo resultado da requisição", "排队中": "Na fila", "提供商 / 账号": "Provedor / Conta", "提供商: {provider}": "Provedor: {provider}", "提示": "Aviso", "敏": "PS", "敏感词": "Palavras sensíveis", "断开上游连接,明细记为「请求已被手动终止」": "Encerra a conexão com o upstream e registra nos detalhes como “Requisição abortada manualmente”", "无报文原文(未开启「调试模式」,或已被清理)": "Sem payload original (o modo de depuração estava desativado, ou o conteúdo foi limpo)", "无结果记录": "Sem resultado registrado", "时间": "Data/hora", "暂无请求日志,网关还没有转发过请求": "Ainda não há logs de requisições; o Gateway ainda não encaminhou nenhuma requisição", "最终承载:{picked}": "Atendida por fim por: {picked}", "本轮尝试失败,正在退避等待或切换到下一个账号": "Esta rodada falhou; aguardando o backoff ou trocando para a próxima conta", "来源客户端:{client}": "Cliente de origem: {client}", "查看该请求的上游原始报文": "Ver o payload bruto do upstream desta requisição", "模型": "Modelo", "模型测试": "Teste de modelo", "模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计": "Requisição iniciada pelo “Teste” na coluna de ações da página de gerenciamento de modelos: usa a mesma rota de encaminhamento das requisições reais, mas não entra nas estatísticas dos relatórios", "正在加载请求日志…": "Carregando logs de requisições…", "正在统计…": "Calculando…", "正文超过单条上限,可能已被截断": "O texto ultrapassou o limite por registro e pode ter sido truncado", "没有符合筛选条件的请求": "Nenhuma requisição corresponde aos filtros", "清理": "Limpar", "清理中…": "Limpando…", "清理失败:{message}": "Falha na limpeza: {message}", "清理方式": "Modo de limpeza", "清理请求日志": "Limpar logs de requisições", "生成速度:输出 token ÷ 流式段用时(总用时 − 首响);流式段不足总用时一半时按总用时计": "Velocidade de geração: tokens de saída ÷ tempo de streaming (duração total − 1ª resposta); usa a duração total quando a janela de streaming é menor que metade da requisição", "用时": "Duração", "用量": "Uso", "确定对全部请求日志执行「{label}」?将处理 {count} 条,此操作无法恢复。": "Confirmar “{label}” em todos os logs de requisições? Serão processados {count} registros. Esta ação não pode ser desfeita.", "确定按当前筛选执行「{label}」?将处理 {count} 条,此操作无法恢复。": "Confirmar “{label}” com os filtros atuais? Serão processados {count} registros. Esta ação não pode ser desfeita.", "确定终止这条正在转发的请求?
    上游连接会立即断开,明细将记为「请求已被手动终止」。": "Tem certeza de que deseja abortar esta requisição em encaminhamento?
    A conexão com o upstream será encerrada imediatamente e os detalhes serão registrados como “Requisição abortada manualmente”.", "等待响应": "Aguardando resposta", "策略 {strategy} · 会话粘性 {sticky}{hit}": "Estratégia {strategy} · Aderência {sticky}{hit}", "终止": "Abortar", "终止失败:{error}": "Falha ao abortar: {error}", "终止请求": "Abortar requisição", "结果": "Resultado", "缓存读取: - / 命中率: -": "Leituras de cache: - / taxa de acerto: -", "缓存读取: {cache} / 命中率: {rate}": "Leituras de cache: {cache} / taxa de acerto: {rate}", "耗时": "Duração", "被跳过:": "Puladas: ", "识别到 User-Agent,但不属于已知的客户端类型:{ua}": "User-Agent detectado, mas não é um tipo de cliente conhecido: {ua}", "详情": "Detalhes", "详情内容": "Conteúdo dos detalhes", "请求体": "Corpo da requisição", "请求图片": "Imagem da requisição", "请求在任何账号接手之前就失败了": "A requisição falhou antes de qualquer conta assumi-la", "请求头": "Cabeçalhos da requisição", "请求已受理,正在选路、取凭证、建立上游连接": "Requisição aceita; selecionando rota, obtendo credenciais e estabelecendo a conexão com o upstream", "请求日志列表": "Lista de logs de requisições", "请求日志时间范围": "Período dos logs de requisições", "请求日志表": "Tabela de logs de requisições", "请求正在转发中,用时列显示的是已用时": "A requisição está sendo encaminhada; a coluna Duração mostra o tempo decorrido", "请求详情": "Detalhes da requisição", "读取请求日志失败,详见控制台": "Falha ao ler os logs de requisições; veja o console", "路由回放": "Replay de roteamento", "转发到上游的模型:{model}": "Modelo encaminhado ao upstream: {model}", "轮次": "Rodada", "输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}": "Entrada {input} · Saída {output} · Total {total} · Leitura de cache {cache}", "这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计": "Este detalhe é um teste de modelo (iniciado manualmente): usa a mesma rota de encaminhamento das requisições reais, mas não entra nas estatísticas dos relatórios", "这条明细没有记录提供商(旧数据或请求未走到转发)": "Este detalhe não registrou provedor (dados antigos ou a requisição falhou antes do encaminhamento)", "这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)": "Nenhum cliente de origem registrado nesta entrada (dados antigos, teste de modelo ou solicitação sem User-Agent)", "这条记录命中了敏感词,但没有留下命中明细": "Este registro atingiu palavras sensíveis, mas não deixou detalhes das ocorrências", "这条记录的尝试明细未采集(该字段上线前的旧数据,或请求在转发前就失败)": "Os detalhes das tentativas deste registro não foram coletados (dados antigos, de antes do lançamento deste campo, ou a requisição falhou antes do encaminhamento)", "这条请求没有保存这一段报文": "Esta requisição não salvou este trecho do payload", "进入「{phase}」阶段已持续 {time}": "Na fase “{phase}” há {time}", "进行中": "Em andamento", "进行中…": "Em andamento…", "连接中": "Conectando", "重试 {n}": "Retry {n}", "重试中": "Retentando", "重试次数": "Retries", "预览对话": "Prévia da conversa", "预览读取失败": "Falha ao ler a prévia", "预览读取失败:{error}": "Falha ao ler a prévia: {error}", "首响 {time}": "1ª resposta {time}", "首响:上游首帧到达的耗时": "1ª resposta: tempo até o primeiro frame do upstream chegar", "首响:上游首帧到达的耗时(请求仍在转发中)": "1ª resposta: tempo até o primeiro frame do upstream chegar (a requisição ainda está sendo encaminhada)", "首帧已到,上游内容正在下发": "O primeiro frame chegou; o upstream está enviando o conteúdo", "(请求仍在转发中)": "(a requisição ainda está sendo encaminhada)", "(账号:{account})": "(conta: {account})", ",{n}毫秒后重试": ", retry em {n}ms", ",{n}秒后重试": ", retry em {n}s" }); diff --git a/desktop-tauri/ui/i18n/zh-Hant/add-account.js b/desktop-tauri/ui/i18n/zh-Hant/add-account.js index 93b9b262..dce24288 100644 --- a/desktop-tauri/ui/i18n/zh-Hant/add-account.js +++ b/desktop-tauri/ui/i18n/zh-Hant/add-account.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:zh-Hant/add-account —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · 模型 {head}{rest}": " · 模型 {head}{rest}", " 等 {n} 个": " 等 {n} 個", "1// 开头的 Google refresh token(也可直接粘贴 refresh_token=… 整行)": "1// 開頭的 Google refresh token(也可直接貼上 refresh_token=… 整行)", "11 位大陆手机号": "11 位大陸手機號", "1~64 个字符,账号列表里按它分组显示": "1~64 個字元,帳號列表裡按它分組顯示", "6 位数字": "6 位數字", "
  • 没有可自动领的活动
  • ": "
  • 沒有可自動領的活動
  • ", "

    领到的是套餐赠送积分,不会增加福利模型的 token 池;": "

    領到的是套餐贈送積分,不會增加福利模型的 token 池;", "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "Anthropic 相容端點在 https://api.deepseek.com/anthropic(協議換 anthropic 時填它)", "Anthropic 协议填根地址,不要带 /v1": "Anthropic 協議填根地址,不要帶 /v1", "Antigravity": "Antigravity", "AutoClaw 国内版": "AutoClaw 國內版", "AutoClaw 国际版": "AutoClaw 國際版", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS 必填;請貼上**完整 Cookie**(Cookie 編輯器一鍵匯出即可)—— 其中 **PTOKEN 必須保留**,閘道器要用它向百度換髮 KukuAI 業務會話權杖,缺了它模型重新整理會報「未登入」。支援整段 Cookie 頭、Cookie 編輯器匯出的 JSON 陣列,或 {BDUSS, STOKEN} 物件。KukuAI 沒有重新整理機制,登入態過期後重新貼上即可。", "BDUSS=…; STOKEN=…(或 Cookie 编辑器导出的 JSON)": "BDUSS=…; STOKEN=…(或 Cookie 編輯器匯出的 JSON)", "CatPaw 的 X-Passport-Token(登录态 Cookie)": "CatPaw 的 X-Passport-Token(登入態 Cookie)", "Cline Pass 的 API Key 在 app.cline.bot 获取(订阅制);端点为 OpenAI 兼容,模型清单以 Cline 控制台为准,模型名在模型管理里按裸名添加(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客户端形态校验,所需的客户端请求头已随卡预置": "Cline Pass 的 API Key 在 app.cline.bot 獲取(訂閱制);端點為 OpenAI 相容,模型清單以 Cline 控制台為準,模型名在模型管理裡按裸名新增(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客戶端形態校驗,所需的客戶端請求頭已隨卡預置", "Cloud-IDE-JWT(三段点分)": "Cloud-IDE-JWT(三段點分)", "Command Code": "Command Code", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT(三段點分;也可直接貼上 Bearer 整行或 data.bin 內容)", "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT(三段點分;也可直接貼上 chatglm_token=… 整行)", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode 國際版", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "NVIDIA NIM 託管端點,Key 在 integrate.api.nvidia.com 申請", "OfficeAce 果办": "OfficeAce 果辦", "accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)": "accessToken 是 Accio 的登入憑證(一長串不透明 token,不是 JWT);refreshToken 可選,填了之後到期能自動續期。請填寫 {label}帳號的憑證 —— {siteNote}(最容易拿到的辦法:直接用上方的「網頁登入」,不需要手工找 token。)", "accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
    手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
    没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。": "accessToken 是 Trae 的 Cloud-IDE-JWT(三段點分),refreshToken 用於到期自動續期(本家 refreshToken 一次一換:換髮一次舊的即作廢,所以兩份程式別同時刷同一個帳號)。
    手工貼上時請連 machineId / deviceId 一起填:上游把它們與登入時上傳的裝置公鑰綁在一起判裝置,憑空換一對會撞 2xxxx 那族裝置繫結拒絕。
    沒有這三樣時的正路是用上方的「網頁登入」;容器 / 遠端部署形態按提示貼上位址列回撥地址即可。", "monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)": "monkeycode_ai_session 的值(也可直接貼上整段 Cookie)", "refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。": "refreshToken 可選,填了到期可自動續期;兩者都能從小浣熊客戶端登入態檔案裡取到。", "refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。": "refreshToken 可選,填了可自動續期;兩者都能從客戶端登入態檔案取到。同一個 Cline 帳號兩個池都能用,這裡歸入 {label}。", "token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。": "token 支援 enc: 字首(Windows 上自動解密)。未填 refreshToken 無法自動續期。", "token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。": "token 是登入態 Cookie;CatPaw 沒有重新整理機制,過期後需在客戶端重新登入。", "user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)": "user_ 開頭的 API Key(也可直接貼上 Authorization: Bearer … 整行)", "{label} 网页登录的打开方式": "{label} 網頁登入的開啟方式", "{label} 账号地区": "{label} 帳號地區", "{label} 账号的添加方式": "{label} 帳號的新增方式", "{name} 不是合法 JSON:{reason}": "{name} 不是合法 JSON:{reason}", "{name}要粘一个 JSON 对象(形如 {sample})": "{name}要粘一個 JSON 物件(形如 {sample})", "{n} 个账号": "{n} 個帳號", "{n} 个账号,点击添加": "{n} 個帳號,點擊新增", "{seconds}s 后重发": "{seconds}s 後重發", "✅ {label}账号已添加": "✅ {label}帳號已新增", "✅ {label}账号已添加{name}": "✅ {label}帳號已新增{name}", "✅ 已从 cc-switch 导入 {n} 个供应商": "✅ 已從 cc-switch 匯入 {n} 個供應商", "✅ 已创建自定义提供商「{name}」并添加账号": "✅ 已建立自定義提供商「{name}」並新增帳號", "✅ 已删除自定义提供商「{name}」{accounts}{keys}": "✅ 已刪除自定義提供商「{name}」{accounts}{keys}", "✅ 已更新已有账号{name}(同一把 API Key 只保留一条,重复添加会合并)": "✅ 已更新已有帳號{name}(同一把 API Key 只保留一條,重複新增會合並)", "✅ 登录成功,{edition}账号已加入列表": "✅ 登入成功,{edition}帳號已加入列表", "✅ 领取成功:{planId}{period}": "✅ 領取成功:{planId}{period}", " 同一家可以放多把 key,按优先级轮换。": " 同一家可以放多把 key,按優先順序輪換。", "「{name}」的账号添加功能还在开发中,敬请期待。": "「{name}」的帳號新增功能還在開發中,敬請期待。", "一个 Key 用遍多家模型,模型名用「厂商/模型」全称(如 deepseek/deepseek-chat)": "一個 Key 用遍多家模型,模型名用「廠商/模型」全稱(如 deepseek/deepseek-chat)", "上游不要鉴权时留空,并勾选下面一项(留空又不勾会被当成未配置凭证)": "上游不要鑑權時留空,並勾選下面一項(留空又不勾會被當成未配置憑證)", "上游信息": "上游資訊", "不可领({status})": "不可領({status})", "不支持导入": "不支援匯入", "不是有效的凭证文件:内容要是一个 JSON 对象": "不是有效的憑證檔案:內容要是一個 JSON 物件", "不是有效的凭证文件:需要包含 accessToken 与 uid": "不是有效的憑證檔案:需要包含 accessToken 與 uid", "不是自定义提供商": "不是自定義提供商", "不需要 Key:转发与拉取模型都不发送鉴权头": "不需要 Key:轉發與拉取模型都不傳送鑑權頭", "不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

    注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。": "不需要登入:點下面的按鈕,閘道器會直接在智譜清言換髮一份訪客憑證並加入帳號列表(也可以先填個備註名)。訪客憑證可以直接用來對話,免費額度由上游按裝置發放。

    注意:訪客帳號沒有積分體系 —— 它與「會員積分」「每日登入積分」「活動領取」相關的介面一律需要登入,因此那些能力對訪客會如實跳過(帳號頁的積分、簽到中心都不顯示讀數)。想要積分就用「填寫憑證」粘一個已登入帳號的權杖,或「匯入桌面版登入態」。", "两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。": "兩版帳號可同時儲存,各自一份模型清單(國內版與國際版是兩家提供商,可分別啟用與對映)。", "中国版": "中國版", "今天没有需要领的活动(都已到账)": "今天沒有需要領的活動(都已到賬)", "今天(北京时间 {day})已试 {attempts} 次,还能试 {left} 次{next}
    ": "今天(北京時間 {day})已試 {attempts} 次,還能試 {left} 次{next}
    ", "从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。": "從別的機器(或 OfficeAce 桌面版 models.json)取三項貼進來:模型閘道器基址(baseUrl,形如 https://modelgw-…/v2)與 modelAppKey / modelAppSecret。這三項就能轉發(閘道器憑據不過期);控制面臨時憑據(accessKeyId 等)可選,留空則額度/自動續期不可用,但不影響轉發。", "从本机导入桌面端登录态": "從本機匯入桌面版登入態", "任务镜像 id": "任務映象 id", "余额查询": "餘額查詢", "使用 Google 账号登录": "使用 Google 帳號登入", "使用 Zai 账号登录": "使用 Zai 帳號登入", "使用个人访问令牌(PAT)": "使用個人存取權杖(PAT)", "免费模型不用填 Key(默认勾上「该上游无需鉴权」,网关自动用匿名凭证);付费模型则填 Zen 的 API Key。免费档上游只认官方客户端形态,已默认开启「伪装 OpenCode 官方客户端」": "免費模型不用填 Key(預設勾上「該上游無需鑑權」,閘道器自動用匿名憑證);付費模型則填 Zen 的 API Key。免費檔上游只認官方客戶端形態,已預設開啟「偽裝 OpenCode 官方客戶端」", "内嵌窗口": "內嵌視窗", "内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)": "內嵌視窗開啟;關窗即取消等待(Google 帳號被拒時改用系統瀏覽器)", "内嵌窗口打开;完成后自动加入列表,关窗即取消等待": "內嵌視窗開啟;完成後自動加入列表,關窗即取消等待", "内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待": "內嵌視窗開啟;掃碼完成後自動加入列表,關窗即取消等待", "内嵌窗口用全新环境,多账号互不影响;关窗即取消等待": "內嵌視窗用全新環境,多帳號互不影響;關窗即取消等待", "内嵌窗口(推荐)": "內嵌視窗(推薦)", "准备验证…": "準備驗證…", "凭据 JSON": "憑據 JSON", "凭证文件在哪?": "憑證檔案在哪?", "创建并添加账号": "建立並新增帳號", "创建这个提供商,并同时建立它的第一个账号。": "建立這個提供商,並同時建立它的第一個帳號。", "删除功能不可用(脚本未就绪)": "刪除功能不可用(指令碼未就緒)", "删除失败:{reason}": "刪除失敗:{reason}", "删除此提供商": "刪除此提供商", "及 {n} 个账号": "及 {n} 個帳號", "发送中…": "傳送中…", "发送失败:{message}": "傳送失敗:{message}", "发送未确认:登录窗口已弹出,请在弹出的窗口中手动完成登录": "傳送未確認:登入視窗已彈出,請在彈出的視窗中手動完成登入", "取消失败:{message}": "取消失敗:{message}", "取消等待": "取消等待", "只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页": "只有「內嵌視窗」能恢復第三方入口:系統瀏覽器裡我們無法改動登入頁", "可选": "可選", "可选(cloudaicompanionProject):留空由网关自动发现": "可選(cloudaicompanionProject):留空由閘道器自動發現", "可选,展示用": "可選,展示用", "可选,没有则无法自动续期": "可選,沒有則無法自動續期", "可选,留空使用凭据里的 user_name": "可選,留空使用憑據裡的 user_name", "可选,留空使用账号姓名、邮箱或令牌指纹": "可選,留空使用帳號姓名、郵箱或權杖指紋", "可选,留空使用账号昵称或邮箱": "可選,留空使用帳號暱稱或郵箱", "可选,留空则用 key 尾号": "可選,留空則用 key 尾號", "可选,留空则用 userId": "可選,留空則用 userId", "可选,留空则用上游返回的账号名或账号 id": "可選,留空則用上游返回的帳號名或帳號 id", "可选,留空则用凭证里的账号名": "可選,留空則用憑證裡的帳號名", "可选,留空则用文件里的昵称": "可選,留空則用檔案裡的暱稱", "可选,留空则用登录名或 uid": "可選,留空則用登入名或 uid", "可选,留空则用脱敏手机号": "可選,留空則用脫敏手機號", "可选,留空则用脱敏手机号或 userId": "可選,留空則用脫敏手機號或 userId", "可选,留空则用账号 uk": "可選,留空則用帳號 uk", "可选,留空则用账号昵称": "可選,留空則用帳號暱稱", "可选,留空则用账号昵称或 userId": "可選,留空則用帳號暱稱或 userId", "可选,留空则用邮箱或令牌尾号": "可選,留空則用郵箱或權杖尾號", "可选,留空用「OfficeAce 果办」": "可選,留空用「OfficeAce 果辦」", "可选,留空自动生成": "可選,留空自動生成", "可选,留空自动用昵称": "可選,留空自動用暱稱", "可选,给这个访客账号起个备注名": "可選,給這個訪客帳號起個備註名", "可选,续期时带上": "可選,續期時帶上", "可选,讯飞侧 userid": "可選,訊飛側 userid", "可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补": "可選:發起對話必需,留空則由閘道器從該帳號已有任務裡自動發現,發現不到時之後手動補", "可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)": "可選:帳號郵箱,用於展示與身份識別(換新權杖也能認回同一帳號)", "可选;UUID 形态,与凭据同生共死": "可選;UUID 形態,與憑據同生共死", "可选;填了才能到期自动续期": "可選;填了才能到期自動續期", "可选;用于去重与展示名(留空时网关会问一次上游)": "可選;用於去重與展示名(留空時閘道器會問一次上游)", "可选;用于生成账号 id 与展示名": "可選;用於生成帳號 id 與展示名", "可选;纯数字串(上游只收 8–24 位数字)": "可選;純數字串(上游只收 8–24 位數字)", "可领取": "可領取", "同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)": "同一家可以混著放:有 Key 的帳號與無鑑權帳號各按各的規則走;無需鑑權的帳號也能並存多條(各自可配不同的出網代理)", "同地址换 anthropic 协议即走它的 Claude 兼容端点。转发必须填 OpenCode Go 的 API Key(订阅制;获取模型清单不需要 Key,但发请求上游会回 401「Missing API key」)": "同地址換 anthropic 協議即走它的 Claude 相容端點。轉發必須填 OpenCode Go 的 API Key(訂閱制;獲取模型清單不需要 Key,但發請求上游會回 401「Missing API key」)", "响应为空": "響應為空", "商汤日日新的 OpenAI 兼容端点;余额查询走控制台账号密码(见下方「余额查询」一段)": "商湯日日新的 OpenAI 相容端點;餘額查詢走控制台帳號密碼(見下方「餘額查詢」一段)", "国内版": "國內版", "国内版与**国际版是两套独立的账号与套餐**,凭证与领取的套餐都不通用。": "國內版與**國際版是兩套獨立的帳號與套餐**,憑證與領取的套餐都不通用。", "国内版登录页没有 Google / GitHub 入口(它用微信 / 手机号 / 邮箱登录)": "國內版登入頁沒有 Google / GitHub 入口(它用微信 / 手機號 / 郵箱登入)", "国内版的登录站点是 www.accio-ai.com,与国际版不是同一站。": "國內版的登入站點是 www.accio-ai.com,與國際版不是同一站。", "国内版(WorkBuddy)": "國內版(WorkBuddy)", "国内站为 https://api.siliconflow.cn/v1": "國內站為 https://api.siliconflow.cn/v1", "国内站(minimaxi.com);OpenAI 兼容地址为 https://api.minimaxi.com/v1": "國內站(minimaxi.com);OpenAI 相容地址為 https://api.minimaxi.com/v1", "国际版": "國際版", "国际版 z.ai 的 Coding 套餐;OpenAI 兼容地址为 https://api.z.ai/api/coding/paas/v4": "國際版 z.ai 的 Coding 套餐;OpenAI 相容地址為 https://api.z.ai/api/coding/paas/v4", "国际版与国内版是**两套独立的账号**(同一账号体系的两个站点),凭证不通用。": "國際版與國內版是**兩套獨立的帳號**(同一帳號體系的兩個站點),憑證不通用。", "国际版与国内版账号体系独立,请填国际版的凭证。未填 refreshToken 无法自动续期。": "國際版與國內版帳號體系獨立,請填國際版的憑證。未填 refreshToken 無法自動續期。", "国际版登录页默认只显示邮箱登录,勾选后恢复 Google / GitHub / X 入口": "國際版登入頁預設只顯示郵箱登入,勾選後恢復 Google / GitHub / X 入口", "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "國際版的推理站點是 api.z.ai,與國內版不是同一站;套餐也各自獨立。", "国际版(WorkBuddy AI)": "國際版(WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "國際站 Coding 套餐;OpenAI 相容地址為 https://api.minimax.io/v1", "在 cc-switch 里找到": "在 cc-switch 裡找到", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "在內嵌視窗裡完成官方登入,成功後自動加入帳號列表。", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "在{label}的 Qoder 帳號設定 → Integrations 生成 PAT(別填 Google / GitHub 的權杖)。兩個地區的帳號與憑證不通用。", "在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。": "在開啟的官方授權頁裡用微信掃碼登入(頁面會直接顯示二維碼)。掃碼成功後自動加入帳號列表。", "在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "在開啟的窗口裡用百度帳號登入 KukuAI 主站(手機驗證碼 / 掃碼均可),登入成功後自動加入帳號列表。閘道器會向百度換髮 KukuAI 業務會話權杖(依賴本機已安裝庫庫AI 客戶端)。", "在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "在開啟的窗口裡登入 Google 帳號(Antigravity 的授權頁),授權完成後自動加入帳號列表。", "在浏览器中打开登录页": "在瀏覽器中開啟登入頁", "地区": "地區", "填写凭证": "填寫憑證", "填写凭证添加": "填寫憑證新增", "备注名": "備註名", "套餐": "套餐", "如:智谱 GLM": "如:智譜 GLM", "完成登录后自动加入账号列表,昵称取自上游。": "完成登入後自動加入帳號列表,暱稱取自上游。", "官方客户端凭证目录:Windows 是 C:\\Users\\<你的用户名>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth,macOS 是 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth": "官方客戶端憑證目錄:Windows 是 C:\\Users\\<你的使用者名稱>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth,macOS 是 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth", "官方已确认到账": "官方已確認到賬", "导入": "匯入", "导入中…": "匯入中…", "导入凭证文件": "匯入憑證檔案", "导入失败": "匯入失敗", "导入失败:{reason}": "匯入失敗:{reason}", "导入所选": "匯入所選", "导入所选({n})": "匯入所選({n})", "导入桌面端登录态": "匯入桌面版登入態", "将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下": "將開啟內嵌視窗;授權完成後自動加入帳號列表。關掉視窗即取消等待。授權頁最後可能提示無法開啟 zcode:// 連結,這是正常的 —— 結果已由服務端記下", "将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)": "將開啟內嵌視窗;授權完成後自動加入帳號列表。遠端面板可貼上回撥地址。關掉視窗即取消等待。連結 5 分鐘內有效,超時或未點就會作廢(可重新發起)", "将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待": "將開啟內嵌視窗;登入完成後自動加入帳號列表。關掉視窗即取消等待", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 Trae 账号);同机完成后自动加入,Docker / 远程面板按提示粘贴地址栏回调地址。": "將用系統預設瀏覽器開啟授權頁(會複用瀏覽器裡已登入的 Trae 帳號);同機完成後自動加入,Docker / 遠端面板按提示貼上位址列回撥地址。", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 ZCode 账号);授权完成后自动加入账号列表。页面最后可能提示无法打开 zcode:// 链接,属正常现象": "將用系統預設瀏覽器開啟授權頁(會複用瀏覽器裡已登入的 ZCode 帳號);授權完成後自動加入帳號列表。頁面最後可能提示無法開啟 zcode:// 連結,屬正常現象", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "將用系統預設瀏覽器開啟授權頁(會複用瀏覽器裡已登入的華為雲帳號);授權碼由閘道器輪詢雲端取回,與瀏覽器跑在哪臺機器無關,完成後自動加入帳號列表", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "將用系統預設瀏覽器開啟授權頁(會複用瀏覽器裡已登入的華為雲帳號);瀏覽器與閘道器不在同一臺機器時,按上方說明把最終回撥地址貼上回面板", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "將用系統預設瀏覽器開啟登入頁(會複用瀏覽器裡已登入的 Accio 帳號);完成登入後自動加入帳號列表,關掉彈窗即取消等待", "小米 MiMo Token Plan 套餐(新加坡站)": "小米 MiMo Token Plan 套餐(新加坡站)", "已到账": "已到賬", "已取消登录等待": "已取消登入等待", "已取消领取": "已取消領取", "已取消验证码": "已取消驗證碼", "已填写手机号,正在发送验证码…": "已填寫手機號,正在傳送驗證碼…", "已声明无需鉴权:账号会被正常选路,出网时不带任何鉴权头": "已宣告無需鑑權:帳號會被正常選路,出網時不帶任何鑑權頭", "已导入 {ok} 个,失败 {fail} 个(失败项已标注在列表里)": "已匯入 {ok} 個,失敗 {fail} 個(失敗項已標註在列表裡)", "已建过 · {n} 个账号,点击继续添加": "已建過 · {n} 個帳號,點擊繼續新增", "已建过,点击继续添加账号": "已建過,點擊繼續新增帳號", "已打开官方登录页,请在窗口中完成登录…": "已開啟官方登入頁,請在視窗中完成登入…", "已点发送,等待短信…": "已點發送,等待簡訊…", "已用系统默认浏览器打开登录页,请在浏览器中完成登录…": "已用系統預設瀏覽器開啟登入頁,請在瀏覽器中完成登入…", "已领取,等官方确认": "已領取,等官方確認", "当前壳版本不支持 AutoClaw 网页登录,请更新应用": "當前殼版本不支援 AutoClaw 網頁登入,請更新應用", "当前没有可领取的套餐(活动尚未开始)": "當前沒有可領取的套餐(活動尚未開始)", "当前环境不支持领取(桥接方法缺失)": "當前環境不支援領取(橋接方法缺失)", "必填,CatPaw 账号标识": "必填,CatPaw 帳號標識", "恢复 Google / GitHub 入口": "恢復 Google / GitHub 入口", "手机号": "手機號", "手机验证码登录": "手機驗證碼登入", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "開啟 Trae SOLO 的官方授權頁(trae.cn)並用你的 Trae 帳號登入:授權完成後官方頁面會跳回本機的一個臨時連接埠;同機部署自動接收,Docker / 遠端面板請按提示覆制位址列回撥地址。", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "開啟 Accio {label}的官方登入頁({site})並用你的 Accio 帳號登入:登入成功後官方頁面會跳回本機;同機自動完成,Docker / 遠端面板按提示貼上回撥地址即可(授權碼只在本機傳給閘道器,介面不顯示明文 token)。", "打开 Accio {label}登录页": "開啟 Accio {label}登入頁", "打开 Antigravity 网页登录": "開啟 Antigravity 網頁登入", "打开 CatPaw 网页登录": "開啟 CatPaw 網頁登入", "打开 Cline 授权页": "開啟 Cline 授權頁", "打开 CodeArts 授权页": "開啟 CodeArts 授權頁", "打开 Qoder 网页登录": "開啟 Qoder 網頁登入", "打开 Trae 授权页": "開啟 Trae 授權頁", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "開啟 ZCode {label}的官方授權頁並用你的帳號登入。授權結果由 ZCode 服務端記錄,閘道器在後臺自動取回憑證並加入帳號列表。", "打开 ZCode {label}授权页": "開啟 ZCode {label}授權頁", "打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
    网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。": "開啟華為雲 CodeArts 的官方授權頁登入:登入完成後官方頁面會把瀏覽器帶回閘道器自己的 /oauth/callback,閘道器用一次性授權碼換取臨時憑據並加入帳號列表。
    閘道器跑在另一臺機器上時,網頁端會提示把位址列中的最終回撥地址直接貼上回面板。", "打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。": "開啟華為雲授權頁登入:登入完成後閘道器會自動輪詢雲端取回授權碼並加入帳號列表(不需要裝 OfficeAce 桌面版,也無需手工回填回撥地址)。", "打开官方授权页完成设备码授权,登录的是{label}({site})的账号。": "開啟官方授權頁完成裝置碼授權,登入的是{label}({site})的帳號。", "打开授权页": "開啟授權頁", "打开授权页完成确认,账号自动加入 {label}{note}": "開啟授權頁完成確認,帳號自動加入 {label}{note}", "打开方式": "開啟方式", "打开网页登录": "開啟網頁登入", "探测失败:{message}": "探測失敗:{message}", "接入一个兼容上游": "接入一個相容上游", "提交中…": "提交中…", "搜索提供商": "搜尋提供商", "搜索提供商…": "搜尋提供商…", "整份粘贴官方插件 / CLIProxyAPI 落盘的凭据 JSON(形如 {\"codearts_provider_credential\":{…}},铺平的也行)。
    必填:access_key_id、secret_access_key、security_token。要能自动续期,必须连 refresh_token 与 oauth_context 一起粘 —— 临时凭据约一小时到期,缺这半块就续不回来,只能重新登录。": "整份貼上官方外掛 / CLIProxyAPI 落盤的憑據 JSON(形如 {\"codearts_provider_credential\":{…}},鋪平的也行)。
    必填:access_key_id、secret_access_key、security_token。要能自動續期,必須連 refresh_token 與 oauth_context 一起粘 —— 臨時憑據約一小時到期,缺這半塊就續不回來,只能重新登入。", "文件是{edition}凭证,已按该版本导入": "檔案是{edition}憑證,已按該版本匯入", "新建自定义提供商": "新建自定義提供商", "方案:{label}": "方案:{label}", "明文 JWT 或 auth.json 里的 enc: 加密值(自动解密)": "明文 JWT 或 auth.json 裡的 enc: 加密值(自動解密)", "明文 JWT(国际版账号的 access token)": "明文 JWT(國際版帳號的 access token)", "智谱 bigmodel.cn 的 Coding 套餐,Key 在 open.bigmodel.cn/usercenter/apikeys": "智譜 bigmodel.cn 的 Coding 套餐,Key 在 open.bigmodel.cn/usercenter/apikeys", "未知错误": "未知錯誤", "未能获取授权地址,请重试": "未能獲取授權地址,請重試", "未能读取 cc-switch 数据": "未能讀取 cc-switch 資料", "未领取成功": "未領取成功", "本地 Ollama、OpenCode Zen 免费档这类上游要勾上,否则账号不可用": "本地 Ollama、OpenCode Zen 免費檔這類上游要勾上,否則帳號不可用", "本家有两个**互不替代**的凭证,按你要用的功能填,至少填一个:编码套餐 API Key 用于转发推理(打 {site})与查询窗口限额(每 N 小时 / 每周的剩余比例);jwt 用于领取套餐与查询余额(打 zcode.z.ai 的 billing 网关,官方叫 Coding Plan JWT,是一串三段点分的字符串)。余额两把都能查、但读到的东西不同:jwt 读到余额桶与套餐到期,API Key 读到窗口限额;而领取只认 jwt、转发只认 API Key。请填写 {label}账号的凭证 —— {plan}(最容易拿到的办法:直接用上方的「网页登录」—— 它会替你把这个 API Key 换好,两个凭证一起拿到。)": "本家有兩個**互不替代**的憑證,按你要用的功能填,至少填一個:編碼套餐 API Key 用於轉發推理(打 {site})與查詢視窗限額(每 N 小時 / 每週的剩餘比例);jwt 用於領取套餐與查詢餘額(打 zcode.z.ai 的 billing 閘道器,官方叫 Coding Plan JWT,是一串三段點分的字串)。餘額兩把都能查、但讀到的東西不同:jwt 讀到餘額桶與套餐到期,API Key 讀到視窗限額;而領取只認 jwt、轉發只認 API Key。請填寫 {label}帳號的憑證 —— {plan}(最容易拿到的辦法:直接用上方的「網頁登入」—— 它會替你把這個 API Key 換好,兩個憑證一起拿到。)", "本机 Ollama 的 OpenAI 兼容端点,无需 API Key(默认已勾上「该上游无需鉴权」,鉴权头一个都不发)": "本機 Ollama 的 OpenAI 相容端點,無需 API Key(預設已勾上「該上游無需鑑權」,鑑權頭一個都不發)", "本次未确认到账": "本次未確認到賬", "条可导入": "條可匯入", "条配置,其中": "條配置,其中", "桌面运行时不可用(Tauri 未初始化)": "桌面執行時不可用(Tauri 未初始化)", "模型网关基址": "模型閘道器基址", "正在打开百度登录页…": "正在開啟百度登入頁…", "正在扫描本机 cc-switch…": "正在掃描本機 cc-switch…", "正在换发访客凭证…": "正在換髮訪客憑證…", "正在添加…": "正在新增…", "正在等待{name}登录完成;完成或取消后才能发起新的登录": "正在等待{name}登入完成;完成或取消後才能發起新的登入", "每周": "每週", "每日": "每日", "每月": "每月", "没有则无法自动续期": "沒有則無法自動續期", "没有匹配「{keyword}」的提供商": "沒有匹配「{keyword}」的提供商", "添加 {name} 账号": "新增 {name} 帳號", "添加中…": "新增中…", "添加到 ": "新增到 ", "添加失败:{reason}": "新增失敗:{reason}", "添加小浣熊账号": "新增小浣熊帳號", "添加方式": "新增方式", "添加访客账号": "新增訪客帳號", "添加账号": "新增帳號", "火山引擎 Coding 套餐;通用推理接入点为 https://ark.cn-beijing.volces.com/api/v3": "火山引擎 Coding 套餐;通用推理接入點為 https://ark.cn-beijing.volces.com/api/v3", "点击添加账号": "點擊新增帳號", "点开即预填,填 Key 接入": "點開即預填,填 Key 接入", "点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
    这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。": "點按鈕後先過一次滑塊驗證(官方風控),隨後開啟登入頁,登入完成即自動新增帳號。
    這是國際版官方唯一的登入方式;已在客戶端登入過的,用「匯入桌面版登入態」更快。", "用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。": "用 AutoClaw 帳號繫結的手機號登入:點擊「獲取驗證碼」,收到簡訊後填入下方並登入。驗證碼由本機直接提交給官方介面,介面不顯示 token。", "用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。": "用 CatPaw 帳號完成登入(美團 passport),成功後自動加入帳號列表。", "用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。": "用 Loomy 帳號繫結的手機號登入:點「獲取驗證碼」後填入即可。這是本家最省事的入口。", "用于转发;形如 apiKey.secret 两段点分(不填则这个账号不能转发)": "用於轉發;形如 apiKey.secret 兩段點分(不填則這個帳號不能轉發)", "用于领取套餐与查询余额;不填则这个账号不能领取与查余额": "用於領取套餐與查詢餘額;不填則這個帳號不能領取與查餘額", "用户 ID": "使用者 ID", "用绑定的手机号登录:点「获取验证码」后填入即可。这是国内版官方唯一的登录方式。": "用繫結的手機號登入:點「獲取驗證碼」後填入即可。這是國內版官方唯一的登入方式。", "留空则用提供商名称": "留空則用提供商名稱", "登录 / 添加 {name} 账号": "登入 / 新增 {name} 帳號", "登录 session(一长串)": "登入 session(一長串)", "登录中…": "登入中…", "登录失败:{message}": "登入失敗:{message}", "登录已结束": "登入已結束", "登录并添加": "登入並新增", "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "登入態檔案路徑:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的使用者名稱>\\.box-agent\\config\\auth.json)", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "確定刪除自定義提供商「{name}」?將同時刪除該提供商下 {count} 個帳號,刪除後無法恢復。", "等待 Accio {label}登录完成…": "等待 Accio {label}登入完成…", "等待 CatPaw 登录完成…": "等待 CatPaw 登入完成…", "等待 Cline 授权确认…": "等待 Cline 授權確認…", "等待 CodeArts 登录完成…": "等待 CodeArts 登入完成…", "等待 Google 授权完成…": "等待 Google 授權完成…", "等待 KukuAI 登录完成…": "等待 KukuAI 登入完成…", "等待 OfficeAce 登录完成…": "等待 OfficeAce 登入完成…", "等待 OrcaTerm 登录完成…": "等待 OrcaTerm 登入完成…", "等待 Qoder 授权完成…": "等待 Qoder 授權完成…", "等待 Trae 授权完成…": "等待 Trae 授權完成…", "等待 ZCode {label}授权完成…": "等待 ZCode {label}授權完成…", "等待小浣熊登录完成…": "等待小浣熊登入完成…", "等待登录完成…": "等待登入完成…", "等待网页登录…": "等待網頁登入…", "粘贴 API Key": "貼上 API Key", "粘贴 Accio 的 accessToken": "貼上 Accio 的 accessToken", "粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
    这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。": "貼上 Antigravity 的 Google refresh token(以 1// 開頭;直接貼上含 refresh_token= 的整行也行)。
    這是網頁登入之外的手工入口(例如從已登入的官方 IDE 裡匯出)。refresh token 不會過期,access token 到期由閘道器自動續期;權杖丟了只能重新走 Google 授權,請妥善儲存。", "粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。": "貼上 Command Code 的 API Key(user_ 開頭;從官方 CLI 的 ~/.commandcode/auth.json 或網頁 studio 獲取)。整行 / 帶引號 / 帶 Bearer 字首地粘都行,閘道器會自己剝成裸 key。key 不過期、也沒有續期介面,失效後重新獲取即可;新增時閘道器會做一次輕量探活,失敗不影響新增。", "粘贴 Cookie": "貼上 Cookie", "粘贴 Google refresh token": "貼上 Google refresh token", "粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。": "貼上 OrcaTerm 的 OAuth accessToken(JWT,三段點分;從桌面客戶端 data.bin 或 Web 版 Cookie ot_session 獲取)。直接貼上含 Bearer / ot_session= 的整行也行,閘道器會自己剝成裸 token。新增後由閘道器自動續期(上游有續期介面),無需跟隨 2 小時時效手動更換。", "粘贴 Qoder 个人访问令牌(pt-…)": "貼上 Qoder 個人存取權杖(pt-…)", "粘贴 accessToken": "貼上 accessToken", "粘贴 access_token(一长串 JWT)": "貼上 access_token(一長串 JWT)", "粘贴 session cookie": "貼上 session cookie", "粘贴 token / refreshToken": "貼上 token / refreshToken", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "貼上 workos:… 開頭的權杖(不帶字首也會自動補上)", "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "貼上智譜清言的存取權杖(JWT,三段點分)。獲取方式:瀏覽器開啟 chatglm.cn 並登入後,在開發者工具的 Application → Cookies 裡複製 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘貼後會先向上游校驗一次,校驗通過才落帳號;登入帳號的積分、簽到能力都能用,之後由閘道器自動續期(上游有續期介面)。", "粘贴登录凭据": "貼上登入憑據", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "貼上的是登入 session(同時是模型閘道器的 token)。Loomy 沒有續期介面,session 有效期約 14 天,過期後重新登入即可。", "粘贴网关凭据": "貼上閘道器憑據", "粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
    {siteNote}
    MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。": "貼上網頁登入態 cookie monkeycode_ai_session 的值(在瀏覽器裡複製整段 Cookie 直接貼上也行,閘道器會自己剝出值)。
    {siteNote}
    MonkeyCode 沒有續期介面:session 有效期約 30 天(硬限制),過期後重新登入網頁、再貼上一次即可。", "粘贴访问令牌": "貼上存取權杖", "系统浏览器": "系統瀏覽器", "系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。": "系統瀏覽器可複用已有登入態(國際版推薦);內嵌視窗更乾淨。", "系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表": "系統瀏覽器開啟(複用已登入的 Cline 帳號);完成後自動加入列表", "系统浏览器打开(复用已登录的 Google 账号);完成后自动加入列表": "系統瀏覽器開啟(複用已登入的 Google 帳號);完成後自動加入列表", "系统浏览器打开(复用已登录的 Zai / Google 账号);完成后自动加入列表": "系統瀏覽器開啟(複用已登入的 Zai / Google 帳號);完成後自動加入列表", "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "系統瀏覽器開啟(複用已登入的美團帳號);完成後自動加入列表", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "系統瀏覽器開啟(複用已登入帳號);完成後自動加入列表", "系统默认浏览器": "系統預設瀏覽器", "编码套餐 API Key": "編碼套餐 API Key", "网关 Secret": "閘道器 Secret", "网页登录": "網頁登入", "网页登录的打开方式": "網頁登入的開啟方式", "网页登录(Zai / Google)": "網頁登入(Zai / Google)", "自定义": "自定義", "自建聚合面板:填你自己的站点地址。余额查询用该账号的 API Key 打 {base}/v1/usage,不需要额外配置": "自建聚合面板:填你自己的站點地址。餘額查詢用該帳號的 API Key 打 {base}/v1/usage,不需要額外配置", "自建聚合面板:填你自己的站点地址;余额查询要另填 NewAPI 的个人访问令牌(见下方「余额查询」一段)": "自建聚合面板:填你自己的站點地址;餘額查詢要另填 NewAPI 的個人存取權杖(見下方「餘額查詢」一段)", "获取风控配置失败,将直接尝试领取:{message}": "獲取風控配置失敗,將直接嘗試領取:{message}", "获取验证码": "獲取驗證碼", "访客模式": "訪客模式", "访客模式(免登录)": "訪客模式(免登入)", "该上游无需鉴权(不发送鉴权头)": "該上游無需鑑權(不傳送鑑權頭)", "该提供商": "該提供商", "该提供商账号添加功能即将上线": "該提供商帳號新增功能即將上線", "该自定义提供商已不存在(可能已被删除),请刷新后重试": "該自定義提供商已不存在(可能已被刪除),請重新整理後重試", "请先选择一个自定义提供商": "請先選擇一個自定義提供商", "请在弹出的滑块中完成验证(官方要求的风控步骤)": "請在彈出的滑塊中完成驗證(官方要求的風控步驟)", "请填写 11 位大陆手机号": "請填寫 11 位大陸手機號", "请填写 6 位数字验证码": "請填寫 6 位數字驗證碼", "请填写 Base URL": "請填寫 Base URL", "请填写 {label}": "請填寫 {label}", "请填写名称": "請填寫名稱", "请完成验证…": "請完成驗證…", "读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows": "讀取 %APPDATA%/AutoClaw/auth.json 並解密,僅 Windows", "读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录": "讀取 ~/.cline/data/settings/providers.json,需已在 Cline 客戶端登入", "读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录": "讀取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客戶端登入", "读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录": "讀取本機 KukuAI 客戶端登入態(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客戶端登入", "读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录": "讀取本機 OrcaTerm 客戶端登入態(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客戶端登入", "读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录": "讀取本機智譜清言客戶端登入態(%APPDATA%\\chatglm\\Network\\Cookies),需已在客戶端登入", "读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。": "讀本機官方客戶端(Tencent WorkBuddy / WorkBuddy AI)當前登入的帳號:自動定位其憑證目錄裡最新的登入態檔案(需已在本機官方客戶端登入)。匯入後由閘道器自動續期,無需跟隨 2 小時時效手動換權杖。", "读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。": "讀本機客戶端當前登入態(明文 Cookie,無需解密)。客戶端執行時 Cookies 檔案被獨佔佔用,匯入前請先關閉 KukuAI 客戶端。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。": "讀本機客戶端當前登入態,每次即時讀取(刪掉這條記錄不影響客戶端登入態)。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后点「刷新 Token」同步。": "讀本機客戶端當前登入態,每次即時讀取(刪掉這條記錄不影響客戶端登入態)。客戶端重新登入後點「重新整理 Token」同步。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后重新导入即可。": "讀本機客戶端當前登入態,每次即時讀取(刪掉這條記錄不影響客戶端登入態)。客戶端重新登入後重新匯入即可。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。": "讀本機客戶端當前登入態,每次即時讀取(刪掉這條記錄不影響客戶端登入態)。用 Zai / Google 登入客戶端的使用者走這一條。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。": "讀本機客戶端當前登入態,每次即時讀取(刪掉這條記錄不影響客戶端登入態)。這一條歸入 {label}。", "读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。": "讀本機客戶端當前登入的 OAuth 權杖(明文 JSON,無需解密)。匯入後由閘道器自動續期,無需跟隨客戶端的 2 小時時效手動換權杖。", "读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。": "讀本機客戶端當前的登入態。客戶端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此僅 Windows 可用;客戶端正在執行時可能讀不到該檔案,先退出客戶端再試即可(也可改用「填寫憑證」貼上權杖)。", "账号信息": "帳號資訊", "账号信息不完整,请刷新后重试": "帳號資訊不完整,請重新整理後重試", "账号已添加,但请留意:{warning}": "帳號已新增,但請留意:{warning}", "账号版本": "帳號版本", "账号类型": "帳號型別", "账号:{name}
    ": "帳號:{name}
    ", "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 创建": "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 建立", "还没有账号": "還沒有帳號", "这一家当前不支持网页登录,请改用填写凭证": "這一家當前不支援網頁登入,請改用填寫憑證", "这个账号当前没有可自动领的活动(只自动领每日登录赠送积分)": "這個帳號當前沒有可自動領的活動(只自動領每日登入贈送積分)", "这份额度走「活动套餐」通道:若转发时提示编码套餐已到期,": "這份額度走「活動套餐」通道:若轉發時提示編碼套餐已到期,", "这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。": "這裡填國內站(monkeycode-ai.com)的帳號:請貼上國內站的 session。國際站(monkeycode-ai.net)是另一家 provider、另一套帳號,兩邊的憑證不通用。", "这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。": "這裡填國際站(monkeycode-ai.net)的帳號:請貼上國際站的 session。國內站(monkeycode-ai.com)是另一家 provider、另一套帳號,兩邊的憑證不通用。", "选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "選擇 CatPaw 客戶端的登入態檔案 auth.json(Windows:C:\\Users\\<你的使用者名稱>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。匯入後憑證直接落在閘道器、可獨立轉發,不再依賴本機客戶端。", "选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。": "選擇 Cline 客戶端的登入態檔案 providers.json(Windows:C:\\Users\\<你的使用者名稱>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。匯入後憑證直接落在閘道器、可獨立轉發,不再依賴本機客戶端;這一條歸入 {label}。", "选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。": "選擇 KukuAI 客戶端的 Cookies 檔案(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二進位制** SQLite 資料庫(無副檔名,不是 JSON)。匯入時讀的是你選中檔案的副本、不和執行中的客戶端搶佔用,因此**無需先關閉客戶端**。匯入後憑證直接落在閘道器、可獨立轉發。", "选择凭证文件导入": "選擇憑證檔案匯入", "选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。": "選擇官方客戶端的登入態憑證檔案(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 匯出的帳號 JSON(巢狀 / 扁平形均可)。", "选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "選擇小浣熊客戶端的登入態檔案 auth.json(Windows:C:\\Users\\<你的使用者名稱>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。匯入後憑證直接落在閘道器、可獨立轉發,不再依賴本機客戶端。", "选择提供商": "選擇提供商", "选择的文件不是合法 JSON": "選擇的檔案不是合法 JSON", "选择要添加账号的提供商": "選擇要新增帳號的提供商", "重新扫描": "重新掃描", "鉴权": "鑑權", "阿里云 Token Plan 套餐(国际站)": "阿里雲 Token Plan 套餐(國際站)", "阿里云百炼 Coding 套餐(国内站)": "阿里雲百鍊 Coding 套餐(國內站)", "阿里云百炼 Coding 套餐(国际站)": "阿里雲百鍊 Coding 套餐(國際站)", "阿里云百炼模型服务(国际站,OpenAI 兼容模式)": "阿里雲百鍊模型服務(國際站,OpenAI 相容模式)", "需要图形验证码:登录窗口已弹出,请填写后继续": "需要圖形驗證碼:登入視窗已彈出,請填寫後繼續", "预置 API": "預置 API", "领取 CodeArts 每日福利": "領取 CodeArts 每日福利", "领取失败:{message}": "領取失敗:{message}", "领取并让官方确认": "領取並讓官方確認", "风控验证配置不完整,请稍后重试": "風控驗證配置不完整,請稍後重試", "验证码": "驗證碼", "验证码已发送。收到后填入下方并点「登录并添加」": "驗證碼已傳送。收到後填入下方並點「登入並新增」", "验证码已发送,请查看手机短信": "驗證碼已傳送,請檢視手機簡訊", "验证码已发送,请查看手机短信。填入下方并点「登录并添加」即完成登录": "驗證碼已傳送,請檢視手機簡訊。填入下方並點「登入並新增」即完成登入", "验证码已发送,请查看短信": "驗證碼已傳送,請檢視簡訊", "验证码校验失败,请重试": "驗證碼校驗失敗,請重試", "验证码校验超时,请重试": "驗證碼校驗超時,請重試", "验证码组件不可用,请重试": "驗證碼元件不可用,請重試", "验证码组件初始化超时,请重试": "驗證碼元件初始化超時,請重試", "验证码组件加载失败,请检查网络后重试": "驗證碼元件載入失敗,請檢查網路後重試", "验证码组件加载异常,请重试": "驗證碼元件載入異常,請重試", "验证码组件加载超时,请检查网络后重试": "驗證碼元件載入超時,請檢查網路後重試", "验证码组件未加载,请重启应用后重试": "驗證碼元件未載入,請重啟應用後重試", "(免费额度池,模型名带 cline-free/ 前缀)。": "(免費額度池,模型名帶 cline-free/ 字首)。", "(其中 {n} 把恢复为不限制)": "(其中 {n} 把恢復為不限制)", "(必填)": "(必填)", "(有效期 {window})": "(有效期 {window})", "(订阅池,模型名带 cline-pass/ 前缀,需要账号有对应订阅)。": "(訂閱池,模型名帶 cline-pass/ 字首,需要帳號有對應訂閱)。", ",下一次最快 {minutes} 分钟后": ",下一次最快 {minutes} 分鐘後", ",并从 {n} 把 Key 的可用提供商里移除": ",並從 {n} 把 Key 的可用提供商裡移除" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · 模型 {head}{rest}": " · 模型 {head}{rest}", " 等 {n} 个": " 等 {n} 個", "1// 开头的 Google refresh token(也可直接粘贴 refresh_token=… 整行)": "1// 開頭的 Google refresh token(也可直接貼上 refresh_token=… 整行)", "11 位大陆手机号": "11 位大陸手機號", "1~64 个字符,账号列表里按它分组显示": "1~64 個字元,帳號列表裡按它分組顯示", "6 位数字": "6 位數字", "

  • 没有可自动领的活动
  • ": "
  • 沒有可自動領的活動
  • ", "

    领到的是套餐赠送积分,不会增加福利模型的 token 池;": "

    領到的是套餐贈送積分,不會增加福利模型的 token 池;", "Anthropic 兼容端点在 https://api.deepseek.com/anthropic(协议换 anthropic 时填它)": "Anthropic 相容端點在 https://api.deepseek.com/anthropic(協議換 anthropic 時填它)", "Anthropic 协议填根地址,不要带 /v1": "Anthropic 協議填根地址,不要帶 /v1", "Antigravity": "Antigravity", "AtomCode": "AtomCode", "AutoClaw 国内版": "AutoClaw 國內版", "AutoClaw 国际版": "AutoClaw 國際版", "BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。": "BDUSS 必填;請貼上**完整 Cookie**(Cookie 編輯器一鍵匯出即可)—— 其中 **PTOKEN 必須保留**,閘道器要用它向百度換髮 KukuAI 業務會話權杖,缺了它模型重新整理會報「未登入」。支援整段 Cookie 頭、Cookie 編輯器匯出的 JSON 陣列,或 {BDUSS, STOKEN} 物件。KukuAI 沒有重新整理機制,登入態過期後重新貼上即可。", "BDUSS=…; STOKEN=…(或 Cookie 编辑器导出的 JSON)": "BDUSS=…; STOKEN=…(或 Cookie 編輯器匯出的 JSON)", "CatPaw 的 X-Passport-Token(登录态 Cookie)": "CatPaw 的 X-Passport-Token(登入態 Cookie)", "Cline Pass 的 API Key 在 app.cline.bot 获取(订阅制);端点为 OpenAI 兼容,模型清单以 Cline 控制台为准,模型名在模型管理里按裸名添加(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客户端形态校验,所需的客户端请求头已随卡预置": "Cline Pass 的 API Key 在 app.cline.bot 獲取(訂閱制);端點為 OpenAI 相容,模型清單以 Cline 控制台為準,模型名在模型管理裡按裸名新增(如 glm-5.2 / kimi-k2.7-code / deepseek-v4-pro);上游按客戶端形態校驗,所需的客戶端請求頭已隨卡預置", "Cloud-IDE-JWT(三段点分)": "Cloud-IDE-JWT(三段點分)", "Command Code": "Command Code", "JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)": "JWT(三段點分;也可直接貼上 Bearer 整行或 data.bin 內容)", "JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)": "JWT(三段點分;也可直接貼上 chatglm_token=… 整行)", "LobsterAI": "LobsterAI", "MonkeyCode": "MonkeyCode", "MonkeyCode 国际版": "MonkeyCode 國際版", "NVIDIA NIM 托管端点,Key 在 integrate.api.nvidia.com 申请": "NVIDIA NIM 託管端點,Key 在 integrate.api.nvidia.com 申請", "OfficeAce 果办": "OfficeAce 果辦", "accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)": "accessToken 是 Accio 的登入憑證(一長串不透明 token,不是 JWT);refreshToken 可選,填了之後到期能自動續期。請填寫 {label}帳號的憑證 —— {siteNote}(最容易拿到的辦法:直接用上方的「網頁登入」,不需要手工找 token。)", "accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
    手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
    没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。": "accessToken 是 Trae 的 Cloud-IDE-JWT(三段點分),refreshToken 用於到期自動續期(本家 refreshToken 一次一換:換髮一次舊的即作廢,所以兩份程式別同時刷同一個帳號)。
    手工貼上時請連 machineId / deviceId 一起填:上游把它們與登入時上傳的裝置公鑰綁在一起判裝置,憑空換一對會撞 2xxxx 那族裝置繫結拒絕。
    沒有這三樣時的正路是用上方的「網頁登入」;容器 / 遠端部署形態按提示貼上位址列回撥地址即可。", "monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)": "monkeycode_ai_session 的值(也可直接貼上整段 Cookie)", "refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。": "refreshToken 可選,填了到期可自動續期;兩者都能從小浣熊客戶端登入態檔案裡取到。", "refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。": "refreshToken 可選,填了可自動續期;兩者都能從客戶端登入態檔案取到。同一個 Cline 帳號兩個池都能用,這裡歸入 {label}。", "token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。": "token 支援 enc: 字首(Windows 上自動解密)。未填 refreshToken 無法自動續期。", "token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。": "token 是登入態 Cookie;CatPaw 沒有重新整理機制,過期後需在客戶端重新登入。", "user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)": "user_ 開頭的 API Key(也可直接貼上 Authorization: Bearer … 整行)", "{label} 网页登录的打开方式": "{label} 網頁登入的開啟方式", "{label} 账号地区": "{label} 帳號地區", "{label} 账号的添加方式": "{label} 帳號的新增方式", "{name} 不是合法 JSON:{reason}": "{name} 不是合法 JSON:{reason}", "{name}要粘一个 JSON 对象(形如 {sample})": "{name}要粘一個 JSON 物件(形如 {sample})", "{n} 个账号": "{n} 個帳號", "{n} 个账号,点击添加": "{n} 個帳號,點擊新增", "{seconds}s 后重发": "{seconds}s 後重發", "✅ {label}账号已添加": "✅ {label}帳號已新增", "✅ {label}账号已添加{name}": "✅ {label}帳號已新增{name}", "✅ 已从 cc-switch 导入 {n} 个供应商": "✅ 已從 cc-switch 匯入 {n} 個供應商", "✅ 已创建自定义提供商「{name}」并添加账号": "✅ 已建立自定義提供商「{name}」並新增帳號", "✅ 已删除自定义提供商「{name}」{accounts}{keys}": "✅ 已刪除自定義提供商「{name}」{accounts}{keys}", "✅ 已更新已有账号{name}(同一把 API Key 只保留一条,重复添加会合并)": "✅ 已更新已有帳號{name}(同一把 API Key 只保留一條,重複新增會合並)", "✅ 登录成功,{edition}账号已加入列表": "✅ 登入成功,{edition}帳號已加入列表", "✅ 领取成功:{planId}{period}": "✅ 領取成功:{planId}{period}", " 同一家可以放多把 key,按优先级轮换。": " 同一家可以放多把 key,按優先順序輪換。", "「{name}」的账号添加功能还在开发中,敬请期待。": "「{name}」的帳號新增功能還在開發中,敬請期待。", "一个 Key 用遍多家模型,模型名用「厂商/模型」全称(如 deepseek/deepseek-chat)": "一個 Key 用遍多家模型,模型名用「廠商/模型」全稱(如 deepseek/deepseek-chat)", "上游不要鉴权时留空,并勾选下面一项(留空又不勾会被当成未配置凭证)": "上游不要鑑權時留空,並勾選下面一項(留空又不勾會被當成未配置憑證)", "上游信息": "上游資訊", "不可领({status})": "不可領({status})", "不支持导入": "不支援匯入", "不是有效的凭证文件:内容要是一个 JSON 对象": "不是有效的憑證檔案:內容要是一個 JSON 物件", "不是有效的凭证文件:需要包含 accessToken 与 uid": "不是有效的憑證檔案:需要包含 accessToken 與 uid", "不是自定义提供商": "不是自定義提供商", "不需要 Key:转发与拉取模型都不发送鉴权头": "不需要 Key:轉發與拉取模型都不傳送鑑權頭", "不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

    注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。": "不需要登入:點下面的按鈕,閘道器會直接在智譜清言換髮一份訪客憑證並加入帳號列表(也可以先填個備註名)。訪客憑證可以直接用來對話,免費額度由上游按裝置發放。

    注意:訪客帳號沒有積分體系 —— 它與「會員積分」「每日登入積分」「活動領取」相關的介面一律需要登入,因此那些能力對訪客會如實跳過(帳號頁的積分、簽到中心都不顯示讀數)。想要積分就用「填寫憑證」粘一個已登入帳號的權杖,或「匯入桌面版登入態」。", "两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。": "兩版帳號可同時儲存,各自一份模型清單(國內版與國際版是兩家提供商,可分別啟用與對映)。", "中国版": "中國版", "今天没有需要领的活动(都已到账)": "今天沒有需要領的活動(都已到賬)", "今天(北京时间 {day})已试 {attempts} 次,还能试 {left} 次{next}
    ": "今天(北京時間 {day})已試 {attempts} 次,還能試 {left} 次{next}
    ", "从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。": "從別的機器(或 OfficeAce 桌面版 models.json)取三項貼進來:模型閘道器基址(baseUrl,形如 https://modelgw-…/v2)與 modelAppKey / modelAppSecret。這三項就能轉發(閘道器憑據不過期);控制面臨時憑據(accessKeyId 等)可選,留空則額度/自動續期不可用,但不影響轉發。", "从本机导入桌面端登录态": "從本機匯入桌面版登入態", "任务镜像 id": "任務映象 id", "余额查询": "餘額查詢", "使用 Google 账号登录": "使用 Google 帳號登入", "使用 Zai 账号登录": "使用 Zai 帳號登入", "使用个人访问令牌(PAT)": "使用個人存取權杖(PAT)", "免费模型不用填 Key(默认勾上「该上游无需鉴权」,网关自动用匿名凭证);付费模型则填 Zen 的 API Key。免费档上游只认官方客户端形态,已默认开启「伪装 OpenCode 官方客户端」": "免費模型不用填 Key(預設勾上「該上游無需鑑權」,閘道器自動用匿名憑證);付費模型則填 Zen 的 API Key。免費檔上游只認官方客戶端形態,已預設開啟「偽裝 OpenCode 官方客戶端」", "内嵌窗口": "內嵌視窗", "内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)": "內嵌視窗開啟;關窗即取消等待(Google 帳號被拒時改用系統瀏覽器)", "内嵌窗口打开;完成后自动加入列表,关窗即取消等待": "內嵌視窗開啟;完成後自動加入列表,關窗即取消等待", "内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待": "內嵌視窗開啟;掃碼完成後自動加入列表,關窗即取消等待", "内嵌窗口用全新环境,多账号互不影响;关窗即取消等待": "內嵌視窗用全新環境,多帳號互不影響;關窗即取消等待", "内嵌窗口(推荐)": "內嵌視窗(推薦)", "准备验证…": "準備驗證…", "凭据 JSON": "憑據 JSON", "凭证文件在哪?": "憑證檔案在哪?", "创建并添加账号": "建立並新增帳號", "创建这个提供商,并同时建立它的第一个账号。": "建立這個提供商,並同時建立它的第一個帳號。", "删除功能不可用(脚本未就绪)": "刪除功能不可用(指令碼未就緒)", "删除失败:{reason}": "刪除失敗:{reason}", "删除此提供商": "刪除此提供商", "及 {n} 个账号": "及 {n} 個帳號", "发送中…": "傳送中…", "发送失败:{message}": "傳送失敗:{message}", "发送未确认:登录窗口已弹出,请在弹出的窗口中手动完成登录": "傳送未確認:登入視窗已彈出,請在彈出的視窗中手動完成登入", "取消失败:{message}": "取消失敗:{message}", "取消等待": "取消等待", "只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页": "只有「內嵌視窗」能恢復第三方入口:系統瀏覽器裡我們無法改動登入頁", "可选": "可選", "可选(cloudaicompanionProject):留空由网关自动发现": "可選(cloudaicompanionProject):留空由閘道器自動發現", "可选,展示用": "可選,展示用", "可选,没有则无法自动续期": "可選,沒有則無法自動續期", "可选,用于生成账号标识": "選填,用於產生帳號識別碼", "可选,留空使用凭据里的 user_name": "可選,留空使用憑據裡的 user_name", "可选,留空使用账号姓名、邮箱或令牌指纹": "可選,留空使用帳號姓名、郵箱或權杖指紋", "可选,留空使用账号昵称或邮箱": "可選,留空使用帳號暱稱或郵箱", "可选,留空则用 key 尾号": "可選,留空則用 key 尾號", "可选,留空则用 userId": "可選,留空則用 userId", "可选,留空则用上游返回的账号名或账号 id": "可選,留空則用上游返回的帳號名或帳號 id", "可选,留空则用凭证里的账号名": "可選,留空則用憑證裡的帳號名", "可选,留空则用文件里的昵称": "可選,留空則用檔案裡的暱稱", "可选,留空则用登录名或 uid": "可選,留空則用登入名或 uid", "可选,留空则用脱敏手机号": "可選,留空則用脫敏手機號", "可选,留空则用脱敏手机号或 userId": "可選,留空則用脫敏手機號或 userId", "可选,留空则用账号 uk": "可選,留空則用帳號 uk", "可选,留空则用账号昵称": "可選,留空則用帳號暱稱", "可选,留空则用账号昵称或 userId": "可選,留空則用帳號暱稱或 userId", "可选,留空则用邮箱或令牌尾号": "可選,留空則用郵箱或權杖尾號", "可选,留空用「AtomCode」": "選填,留空用「AtomCode」", "可选,留空用「OfficeAce 果办」": "可選,留空用「OfficeAce 果辦」", "可选,留空自动生成": "可選,留空自動生成", "可选,留空自动用昵称": "可選,留空自動用暱稱", "可选,给这个访客账号起个备注名": "可選,給這個訪客帳號起個備註名", "可选,续期时带上": "可選,續期時帶上", "可选,讯飞侧 userid": "可選,訊飛側 userid", "可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补": "可選:發起對話必需,留空則由閘道器從該帳號已有任務裡自動發現,發現不到時之後手動補", "可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)": "可選:帳號郵箱,用於展示與身份識別(換新權杖也能認回同一帳號)", "可选;UUID 形态,与凭据同生共死": "可選;UUID 形態,與憑據同生共死", "可选;填了才能到期自动续期": "可選;填了才能到期自動續期", "可选;用于去重与展示名(留空时网关会问一次上游)": "可選;用於去重與展示名(留空時閘道器會問一次上游)", "可选;用于生成账号 id 与展示名": "可選;用於生成帳號 id 與展示名", "可选;纯数字串(上游只收 8–24 位数字)": "可選;純數字串(上游只收 8–24 位數字)", "可领取": "可領取", "同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)": "同一家可以混著放:有 Key 的帳號與無鑑權帳號各按各的規則走;無需鑑權的帳號也能並存多條(各自可配不同的出網代理)", "同地址换 anthropic 协议即走它的 Claude 兼容端点。转发必须填 OpenCode Go 的 API Key(订阅制;获取模型清单不需要 Key,但发请求上游会回 401「Missing API key」)": "同地址換 anthropic 協議即走它的 Claude 相容端點。轉發必須填 OpenCode Go 的 API Key(訂閱制;獲取模型清單不需要 Key,但發請求上游會回 401「Missing API key」)", "响应为空": "響應為空", "商汤日日新的 OpenAI 兼容端点;余额查询走控制台账号密码(见下方「余额查询」一段)": "商湯日日新的 OpenAI 相容端點;餘額查詢走控制台帳號密碼(見下方「餘額查詢」一段)", "国内版": "國內版", "国内版与**国际版是两套独立的账号与套餐**,凭证与领取的套餐都不通用。": "國內版與**國際版是兩套獨立的帳號與套餐**,憑證與領取的套餐都不通用。", "国内版登录页没有 Google / GitHub 入口(它用微信 / 手机号 / 邮箱登录)": "國內版登入頁沒有 Google / GitHub 入口(它用微信 / 手機號 / 郵箱登入)", "国内版的登录站点是 www.accio-ai.com,与国际版不是同一站。": "國內版的登入站點是 www.accio-ai.com,與國際版不是同一站。", "国内版(WorkBuddy)": "國內版(WorkBuddy)", "国内站为 https://api.siliconflow.cn/v1": "國內站為 https://api.siliconflow.cn/v1", "国内站(minimaxi.com);OpenAI 兼容地址为 https://api.minimaxi.com/v1": "國內站(minimaxi.com);OpenAI 相容地址為 https://api.minimaxi.com/v1", "国际版": "國際版", "国际版 z.ai 的 Coding 套餐;OpenAI 兼容地址为 https://api.z.ai/api/coding/paas/v4": "國際版 z.ai 的 Coding 套餐;OpenAI 相容地址為 https://api.z.ai/api/coding/paas/v4", "国际版与国内版是**两套独立的账号**(同一账号体系的两个站点),凭证不通用。": "國際版與國內版是**兩套獨立的帳號**(同一帳號體系的兩個站點),憑證不通用。", "国际版与国内版账号体系独立,请填国际版的凭证。未填 refreshToken 无法自动续期。": "國際版與國內版帳號體系獨立,請填國際版的憑證。未填 refreshToken 無法自動續期。", "国际版登录页默认只显示邮箱登录,勾选后恢复 Google / GitHub / X 入口": "國際版登入頁預設只顯示郵箱登入,勾選後恢復 Google / GitHub / X 入口", "国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。": "國際版的推理站點是 api.z.ai,與國內版不是同一站;套餐也各自獨立。", "国际版(WorkBuddy AI)": "國際版(WorkBuddy AI)", "国际站 Coding 套餐;OpenAI 兼容地址为 https://api.minimax.io/v1": "國際站 Coding 套餐;OpenAI 相容地址為 https://api.minimax.io/v1", "在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。": "在 AtomGit 的 AtomCode 頁面取得兩項貼進來:accessToken 與 refreshToken。續期權杖建議填寫——本家權杖不是 JWT(約 7 天到期,讀不到到期時間),沒有它帳號無法自動續期,到期只能重新登入。", "在 cc-switch 里找到": "在 cc-switch 裡找到", "在内嵌窗口里完成官方登录,成功后自动加入账号列表。": "在內嵌視窗裡完成官方登入,成功後自動加入帳號列表。", "在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。": "在{label}的 Qoder 帳號設定 → Integrations 生成 PAT(別填 Google / GitHub 的權杖)。兩個地區的帳號與憑證不通用。", "在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。": "在開啟的官方授權頁裡用微信掃碼登入(頁面會直接顯示二維碼)。掃碼成功後自動加入帳號列表。", "在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。": "在開啟的窗口裡用百度帳號登入 KukuAI 主站(手機驗證碼 / 掃碼均可),登入成功後自動加入帳號列表。閘道器會向百度換髮 KukuAI 業務會話權杖(依賴本機已安裝庫庫AI 客戶端)。", "在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。": "在開啟的窗口裡登入 Google 帳號(Antigravity 的授權頁),授權完成後自動加入帳號列表。", "在浏览器中打开登录页": "在瀏覽器中開啟登入頁", "地区": "地區", "填写凭证": "填寫憑證", "填写凭证添加": "填寫憑證新增", "备注名": "備註名", "套餐": "套餐", "如:智谱 GLM": "如:智譜 GLM", "完成登录后自动加入账号列表,昵称取自上游。": "完成登入後自動加入帳號列表,暱稱取自上游。", "官方客户端凭证目录:Windows 是 C:\\Users\\<你的用户名>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth,macOS 是 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth": "官方客戶端憑證目錄:Windows 是 C:\\Users\\<你的使用者名稱>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth,macOS 是 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth", "官方已确认到账": "官方已確認到賬", "导入": "匯入", "导入中…": "匯入中…", "导入凭证文件": "匯入憑證檔案", "导入失败": "匯入失敗", "导入失败:{reason}": "匯入失敗:{reason}", "导入所选": "匯入所選", "导入所选({n})": "匯入所選({n})", "导入桌面端登录态": "匯入桌面版登入態", "将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下": "將開啟內嵌視窗;授權完成後自動加入帳號列表。關掉視窗即取消等待。授權頁最後可能提示無法開啟 zcode:// 連結,這是正常的 —— 結果已由服務端記下", "将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)": "將開啟內嵌視窗;授權完成後自動加入帳號列表。遠端面板可貼上回撥地址。關掉視窗即取消等待。連結 5 分鐘內有效,超時或未點就會作廢(可重新發起)", "将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待": "將開啟內嵌視窗;登入完成後自動加入帳號列表。關掉視窗即取消等待", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 Trae 账号);同机完成后自动加入,Docker / 远程面板按提示粘贴地址栏回调地址。": "將用系統預設瀏覽器開啟授權頁(會複用瀏覽器裡已登入的 Trae 帳號);同機完成後自動加入,Docker / 遠端面板按提示貼上位址列回撥地址。", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的 ZCode 账号);授权完成后自动加入账号列表。页面最后可能提示无法打开 zcode:// 链接,属正常现象": "將用系統預設瀏覽器開啟授權頁(會複用瀏覽器裡已登入的 ZCode 帳號);授權完成後自動加入帳號列表。頁面最後可能提示無法開啟 zcode:// 連結,屬正常現象", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表": "將用系統預設瀏覽器開啟授權頁(會複用瀏覽器裡已登入的華為雲帳號);授權碼由閘道器輪詢雲端取回,與瀏覽器跑在哪臺機器無關,完成後自動加入帳號列表", "将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板": "將用系統預設瀏覽器開啟授權頁(會複用瀏覽器裡已登入的華為雲帳號);瀏覽器與閘道器不在同一臺機器時,按上方說明把最終回撥地址貼上回面板", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "將用系統預設瀏覽器開啟登入頁(會複用瀏覽器裡已登入的 Accio 帳號);完成登入後自動加入帳號列表,關掉彈窗即取消等待", "将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待": "將用系統預設瀏覽器開啟登入頁(會重用瀏覽器裡已登入的 LobsterAI 帳號);完成登入後自動加入帳號清單,關掉彈窗即取消等待", "小米 MiMo Token Plan 套餐(新加坡站)": "小米 MiMo Token Plan 套餐(新加坡站)", "已到账": "已到賬", "已取消登录等待": "已取消登入等待", "已取消领取": "已取消領取", "已取消验证码": "已取消驗證碼", "已填写手机号,正在发送验证码…": "已填寫手機號,正在傳送驗證碼…", "已声明无需鉴权:账号会被正常选路,出网时不带任何鉴权头": "已宣告無需鑑權:帳號會被正常選路,出網時不帶任何鑑權頭", "已导入 {ok} 个,失败 {fail} 个(失败项已标注在列表里)": "已匯入 {ok} 個,失敗 {fail} 個(失敗項已標註在列表裡)", "已建过 · {n} 个账号,点击继续添加": "已建過 · {n} 個帳號,點擊繼續新增", "已建过,点击继续添加账号": "已建過,點擊繼續新增帳號", "已打开官方登录页,请在窗口中完成登录…": "已開啟官方登入頁,請在視窗中完成登入…", "已点发送,等待短信…": "已點發送,等待簡訊…", "已用系统默认浏览器打开登录页,请在浏览器中完成登录…": "已用系統預設瀏覽器開啟登入頁,請在瀏覽器中完成登入…", "已领取,等官方确认": "已領取,等官方確認", "当前壳版本不支持 AutoClaw 网页登录,请更新应用": "當前殼版本不支援 AutoClaw 網頁登入,請更新應用", "当前没有可领取的套餐(活动尚未开始)": "當前沒有可領取的套餐(活動尚未開始)", "当前环境不支持领取(桥接方法缺失)": "當前環境不支援領取(橋接方法缺失)", "必填,CatPaw 账号标识": "必填,CatPaw 帳號標識", "恢复 Google / GitHub 入口": "恢復 Google / GitHub 入口", "手机号": "手機號", "手机验证码登录": "手機驗證碼登入", "打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。": "開啟 LobsterAI(有道龍蝦)的官方登入頁(lobsterai.youdao.com)並用你的 LobsterAI 帳號登入:登入成功後官方頁面會跳回本機並自動加入帳號清單 —— 回呼落在閘道自己的連接埠上,所以瀏覽器要與閘道在同一台機器上;授權碼只在本機傳給閘道,介面不顯示明文權杖。", "打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。": "開啟 Trae SOLO 的官方授權頁(trae.cn)並用你的 Trae 帳號登入:授權完成後官方頁面會跳回本機的一個臨時連接埠;同機部署自動接收,Docker / 遠端面板請按提示覆制位址列回撥地址。", "打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。": "開啟 Accio {label}的官方登入頁({site})並用你的 Accio 帳號登入:登入成功後官方頁面會跳回本機;同機自動完成,Docker / 遠端面板按提示貼上回撥地址即可(授權碼只在本機傳給閘道器,介面不顯示明文 token)。", "打开 Accio {label}登录页": "開啟 Accio {label}登入頁", "打开 Antigravity 网页登录": "開啟 Antigravity 網頁登入", "打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。": "開啟 AtomGit 授權頁登入:完成後閘道會自動輪詢代理取回權杖並加入帳號清單(授權回呼由代理終結,不會回到本機,因此無需手動回填回呼位址)。", "打开 CatPaw 网页登录": "開啟 CatPaw 網頁登入", "打开 Cline 授权页": "開啟 Cline 授權頁", "打开 CodeArts 授权页": "開啟 CodeArts 授權頁", "打开 LobsterAI 登录页": "開啟 LobsterAI 登入頁", "打开 Qoder 网页登录": "開啟 Qoder 網頁登入", "打开 Trae 授权页": "開啟 Trae 授權頁", "打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。": "開啟 ZCode {label}的官方授權頁並用你的帳號登入。授權結果由 ZCode 服務端記錄,閘道器在後臺自動取回憑證並加入帳號列表。", "打开 ZCode {label}授权页": "開啟 ZCode {label}授權頁", "打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
    网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。": "開啟華為雲 CodeArts 的官方授權頁登入:登入完成後官方頁面會把瀏覽器帶回閘道器自己的 /oauth/callback,閘道器用一次性授權碼換取臨時憑據並加入帳號列表。
    閘道器跑在另一臺機器上時,網頁端會提示把位址列中的最終回撥地址直接貼上回面板。", "打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。": "開啟華為雲授權頁登入:登入完成後閘道器會自動輪詢雲端取回授權碼並加入帳號列表(不需要裝 OfficeAce 桌面版,也無需手工回填回撥地址)。", "打开官方授权页完成设备码授权,登录的是{label}({site})的账号。": "開啟官方授權頁完成裝置碼授權,登入的是{label}({site})的帳號。", "打开授权页": "開啟授權頁", "打开授权页完成确认,账号自动加入 {label}{note}": "開啟授權頁完成確認,帳號自動加入 {label}{note}", "打开方式": "開啟方式", "打开网页登录": "開啟網頁登入", "探测失败:{message}": "探測失敗:{message}", "接入一个兼容上游": "接入一個相容上游", "提交中…": "提交中…", "搜索提供商": "搜尋提供商", "搜索提供商…": "搜尋提供商…", "整份粘贴官方插件 / CLIProxyAPI 落盘的凭据 JSON(形如 {\"codearts_provider_credential\":{…}},铺平的也行)。
    必填:access_key_id、secret_access_key、security_token。要能自动续期,必须连 refresh_token 与 oauth_context 一起粘 —— 临时凭据约一小时到期,缺这半块就续不回来,只能重新登录。": "整份貼上官方外掛 / CLIProxyAPI 落盤的憑據 JSON(形如 {\"codearts_provider_credential\":{…}},鋪平的也行)。
    必填:access_key_id、secret_access_key、security_token。要能自動續期,必須連 refresh_token 與 oauth_context 一起粘 —— 臨時憑據約一小時到期,缺這半塊就續不回來,只能重新登入。", "文件是{edition}凭证,已按该版本导入": "檔案是{edition}憑證,已按該版本匯入", "新建自定义提供商": "新建自定義提供商", "方案:{label}": "方案:{label}", "明文 JWT 或 auth.json 里的 enc: 加密值(自动解密)": "明文 JWT 或 auth.json 裡的 enc: 加密值(自動解密)", "明文 JWT(国际版账号的 access token)": "明文 JWT(國際版帳號的 access token)", "智谱 bigmodel.cn 的 Coding 套餐,Key 在 open.bigmodel.cn/usercenter/apikeys": "智譜 bigmodel.cn 的 Coding 套餐,Key 在 open.bigmodel.cn/usercenter/apikeys", "未知错误": "未知錯誤", "未能获取授权地址,请重试": "未能獲取授權地址,請重試", "未能读取 cc-switch 数据": "未能讀取 cc-switch 資料", "未领取成功": "未領取成功", "本地 Ollama、OpenCode Zen 免费档这类上游要勾上,否则账号不可用": "本地 Ollama、OpenCode Zen 免費檔這類上游要勾上,否則帳號不可用", "本家有两个**互不替代**的凭证,按你要用的功能填,至少填一个:编码套餐 API Key 用于转发推理(打 {site})与查询窗口限额(每 N 小时 / 每周的剩余比例);jwt 用于领取套餐与查询余额(打 zcode.z.ai 的 billing 网关,官方叫 Coding Plan JWT,是一串三段点分的字符串)。余额两把都能查、但读到的东西不同:jwt 读到余额桶与套餐到期,API Key 读到窗口限额;而领取只认 jwt、转发只认 API Key。请填写 {label}账号的凭证 —— {plan}(最容易拿到的办法:直接用上方的「网页登录」—— 它会替你把这个 API Key 换好,两个凭证一起拿到。)": "本家有兩個**互不替代**的憑證,按你要用的功能填,至少填一個:編碼套餐 API Key 用於轉發推理(打 {site})與查詢視窗限額(每 N 小時 / 每週的剩餘比例);jwt 用於領取套餐與查詢餘額(打 zcode.z.ai 的 billing 閘道器,官方叫 Coding Plan JWT,是一串三段點分的字串)。餘額兩把都能查、但讀到的東西不同:jwt 讀到餘額桶與套餐到期,API Key 讀到視窗限額;而領取只認 jwt、轉發只認 API Key。請填寫 {label}帳號的憑證 —— {plan}(最容易拿到的辦法:直接用上方的「網頁登入」—— 它會替你把這個 API Key 換好,兩個憑證一起拿到。)", "本机 Ollama 的 OpenAI 兼容端点,无需 API Key(默认已勾上「该上游无需鉴权」,鉴权头一个都不发)": "本機 Ollama 的 OpenAI 相容端點,無需 API Key(預設已勾上「該上游無需鑑權」,鑑權頭一個都不發)", "本次未确认到账": "本次未確認到賬", "条可导入": "條可匯入", "条配置,其中": "條配置,其中", "桌面运行时不可用(Tauri 未初始化)": "桌面執行時不可用(Tauri 未初始化)", "模型网关基址": "模型閘道器基址", "正在打开百度登录页…": "正在開啟百度登入頁…", "正在扫描本机 cc-switch…": "正在掃描本機 cc-switch…", "正在换发访客凭证…": "正在換髮訪客憑證…", "正在添加…": "正在新增…", "正在等待{name}登录完成;完成或取消后才能发起新的登录": "正在等待{name}登入完成;完成或取消後才能發起新的登入", "每周": "每週", "每日": "每日", "每月": "每月", "没有则无法自动续期": "沒有則無法自動續期", "没有匹配「{keyword}」的提供商": "沒有匹配「{keyword}」的提供商", "添加 {name} 账号": "新增 {name} 帳號", "添加中…": "新增中…", "添加到 ": "新增到 ", "添加失败:{reason}": "新增失敗:{reason}", "添加小浣熊账号": "新增小浣熊帳號", "添加方式": "新增方式", "添加访客账号": "新增訪客帳號", "添加账号": "新增帳號", "火山引擎 Coding 套餐;通用推理接入点为 https://ark.cn-beijing.volces.com/api/v3": "火山引擎 Coding 套餐;通用推理接入點為 https://ark.cn-beijing.volces.com/api/v3", "点击添加账号": "點擊新增帳號", "点开即预填,填 Key 接入": "點開即預填,填 Key 接入", "点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
    这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。": "點按鈕後先過一次滑塊驗證(官方風控),隨後開啟登入頁,登入完成即自動新增帳號。
    這是國際版官方唯一的登入方式;已在客戶端登入過的,用「匯入桌面版登入態」更快。", "用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。": "用 AutoClaw 帳號繫結的手機號登入:點擊「獲取驗證碼」,收到簡訊後填入下方並登入。驗證碼由本機直接提交給官方介面,介面不顯示 token。", "用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。": "用 CatPaw 帳號完成登入(美團 passport),成功後自動加入帳號列表。", "用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。": "用 Loomy 帳號繫結的手機號登入:點「獲取驗證碼」後填入即可。這是本家最省事的入口。", "用于转发;形如 apiKey.secret 两段点分(不填则这个账号不能转发)": "用於轉發;形如 apiKey.secret 兩段點分(不填則這個帳號不能轉發)", "用于领取套餐与查询余额;不填则这个账号不能领取与查余额": "用於領取套餐與查詢餘額;不填則這個帳號不能領取與查餘額", "用户 ID": "使用者 ID", "用绑定的手机号登录:点「获取验证码」后填入即可。这是国内版官方唯一的登录方式。": "用繫結的手機號登入:點「獲取驗證碼」後填入即可。這是國內版官方唯一的登入方式。", "留空则用提供商名称": "留空則用提供商名稱", "登录 / 添加 {name} 账号": "登入 / 新增 {name} 帳號", "登录 session(一长串)": "登入 session(一長串)", "登录中…": "登入中…", "登录失败:{message}": "登入失敗:{message}", "登录已结束": "登入已結束", "登录并添加": "登入並新增", "登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)": "登入態檔案路徑:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的使用者名稱>\\.box-agent\\config\\auth.json)", "确定删除自定义提供商「{name}」?将同时删除该提供商下 {count} 个账号,删除后无法恢复。": "確定刪除自定義提供商「{name}」?將同時刪除該提供商下 {count} 個帳號,刪除後無法恢復。", "等待 Accio {label}登录完成…": "等待 Accio {label}登入完成…", "等待 AtomCode 登录完成…": "等待 AtomCode 登入完成…", "等待 CatPaw 登录完成…": "等待 CatPaw 登入完成…", "等待 Cline 授权确认…": "等待 Cline 授權確認…", "等待 CodeArts 登录完成…": "等待 CodeArts 登入完成…", "等待 Google 授权完成…": "等待 Google 授權完成…", "等待 KukuAI 登录完成…": "等待 KukuAI 登入完成…", "等待 LobsterAI 登录完成…": "等待 LobsterAI 登入完成…", "等待 OfficeAce 登录完成…": "等待 OfficeAce 登入完成…", "等待 OrcaTerm 登录完成…": "等待 OrcaTerm 登入完成…", "等待 Qoder 授权完成…": "等待 Qoder 授權完成…", "等待 Trae 授权完成…": "等待 Trae 授權完成…", "等待 ZCode {label}授权完成…": "等待 ZCode {label}授權完成…", "等待小浣熊登录完成…": "等待小浣熊登入完成…", "等待登录完成…": "等待登入完成…", "等待网页登录…": "等待網頁登入…", "粘贴 API Key": "貼上 API Key", "粘贴 Accio 的 accessToken": "貼上 Accio 的 accessToken", "粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
    这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。": "貼上 Antigravity 的 Google refresh token(以 1// 開頭;直接貼上含 refresh_token= 的整行也行)。
    這是網頁登入之外的手工入口(例如從已登入的官方 IDE 裡匯出)。refresh token 不會過期,access token 到期由閘道器自動續期;權杖丟了只能重新走 Google 授權,請妥善儲存。", "粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。": "貼上 Command Code 的 API Key(user_ 開頭;從官方 CLI 的 ~/.commandcode/auth.json 或網頁 studio 獲取)。整行 / 帶引號 / 帶 Bearer 字首地粘都行,閘道器會自己剝成裸 key。key 不過期、也沒有續期介面,失效後重新獲取即可;新增時閘道器會做一次輕量探活,失敗不影響新增。", "粘贴 Cookie": "貼上 Cookie", "粘贴 Google refresh token": "貼上 Google refresh token", "粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。": "貼上 OrcaTerm 的 OAuth accessToken(JWT,三段點分;從桌面客戶端 data.bin 或 Web 版 Cookie ot_session 獲取)。直接貼上含 Bearer / ot_session= 的整行也行,閘道器會自己剝成裸 token。新增後由閘道器自動續期(上游有續期介面),無需跟隨 2 小時時效手動更換。", "粘贴 Qoder 个人访问令牌(pt-…)": "貼上 Qoder 個人存取權杖(pt-…)", "粘贴 accessToken": "貼上 accessToken", "粘贴 access_token(一长串 JWT)": "貼上 access_token(一長串 JWT)", "粘贴 session cookie": "貼上 session cookie", "粘贴 token / refreshToken": "貼上 token / refreshToken", "粘贴 workos:… 开头的令牌(不带前缀也会自动补上)": "貼上 workos:… 開頭的權杖(不帶字首也會自動補上)", "粘贴令牌对": "貼上權杖對", "粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。": "貼上智譜清言的存取權杖(JWT,三段點分)。獲取方式:瀏覽器開啟 chatglm.cn 並登入後,在開發者工具的 Application → Cookies 裡複製 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘貼後會先向上游校驗一次,校驗通過才落帳號;登入帳號的積分、簽到能力都能用,之後由閘道器自動續期(上游有續期介面)。", "粘贴登录凭据": "貼上登入憑據", "粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。": "貼上的是登入 session(同時是模型閘道器的 token)。Loomy 沒有續期介面,session 有效期約 14 天,過期後重新登入即可。", "粘贴网关凭据": "貼上閘道器憑據", "粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
    {siteNote}
    MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。": "貼上網頁登入態 cookie monkeycode_ai_session 的值(在瀏覽器裡複製整段 Cookie 直接貼上也行,閘道器會自己剝出值)。
    {siteNote}
    MonkeyCode 沒有續期介面:session 有效期約 30 天(硬限制),過期後重新登入網頁、再貼上一次即可。", "粘贴访问令牌": "貼上存取權杖", "系统浏览器": "系統瀏覽器", "系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。": "系統瀏覽器可複用已有登入態(國際版推薦);內嵌視窗更乾淨。", "系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表": "系統瀏覽器開啟(複用已登入的 Cline 帳號);完成後自動加入列表", "系统浏览器打开(复用已登录的 Google 账号);完成后自动加入列表": "系統瀏覽器開啟(複用已登入的 Google 帳號);完成後自動加入列表", "系统浏览器打开(复用已登录的 Zai / Google 账号);完成后自动加入列表": "系統瀏覽器開啟(複用已登入的 Zai / Google 帳號);完成後自動加入列表", "系统浏览器打开(复用已登录的美团账号);完成后自动加入列表": "系統瀏覽器開啟(複用已登入的美團帳號);完成後自動加入列表", "系统浏览器打开(复用已登录账号);完成后自动加入列表": "系統瀏覽器開啟(複用已登入帳號);完成後自動加入列表", "系统默认浏览器": "系統預設瀏覽器", "续期令牌": "續期權杖", "编码套餐 API Key": "編碼套餐 API Key", "网关 Secret": "閘道器 Secret", "网页登录": "網頁登入", "网页登录的打开方式": "網頁登入的開啟方式", "网页登录(Zai / Google)": "網頁登入(Zai / Google)", "自定义": "自定義", "自建聚合面板:填你自己的站点地址。余额查询用该账号的 API Key 打 {base}/v1/usage,不需要额外配置": "自建聚合面板:填你自己的站點地址。餘額查詢用該帳號的 API Key 打 {base}/v1/usage,不需要額外配置", "自建聚合面板:填你自己的站点地址;余额查询要另填 NewAPI 的个人访问令牌(见下方「余额查询」一段)": "自建聚合面板:填你自己的站點地址;餘額查詢要另填 NewAPI 的個人存取權杖(見下方「餘額查詢」一段)", "获取风控配置失败,将直接尝试领取:{message}": "獲取風控配置失敗,將直接嘗試領取:{message}", "获取验证码": "獲取驗證碼", "访客模式": "訪客模式", "访客模式(免登录)": "訪客模式(免登入)", "访问令牌": "存取權杖", "该上游无需鉴权(不发送鉴权头)": "該上游無需鑑權(不傳送鑑權頭)", "该提供商": "該提供商", "该提供商账号添加功能即将上线": "該提供商帳號新增功能即將上線", "该自定义提供商已不存在(可能已被删除),请刷新后重试": "該自定義提供商已不存在(可能已被刪除),請重新整理後重試", "请先选择一个自定义提供商": "請先選擇一個自定義提供商", "请在弹出的滑块中完成验证(官方要求的风控步骤)": "請在彈出的滑塊中完成驗證(官方要求的風控步驟)", "请填写 11 位大陆手机号": "請填寫 11 位大陸手機號", "请填写 6 位数字验证码": "請填寫 6 位數字驗證碼", "请填写 Base URL": "請填寫 Base URL", "请填写 {label}": "請填寫 {label}", "请填写名称": "請填寫名稱", "请完成验证…": "請完成驗證…", "读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows": "讀取 %APPDATA%/AutoClaw/auth.json 並解密,僅 Windows", "读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录": "讀取 ~/.cline/data/settings/providers.json,需已在 Cline 客戶端登入", "读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录": "讀取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客戶端登入", "读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录": "讀取本機 KukuAI 客戶端登入態(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客戶端登入", "读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录": "讀取本機 OrcaTerm 客戶端登入態(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客戶端登入", "读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录": "讀取本機智譜清言客戶端登入態(%APPDATA%\\chatglm\\Network\\Cookies),需已在客戶端登入", "读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。": "讀本機官方客戶端(Tencent WorkBuddy / WorkBuddy AI)當前登入的帳號:自動定位其憑證目錄裡最新的登入態檔案(需已在本機官方客戶端登入)。匯入後由閘道器自動續期,無需跟隨 2 小時時效手動換權杖。", "读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。": "讀本機客戶端當前登入態(明文 Cookie,無需解密)。客戶端執行時 Cookies 檔案被獨佔佔用,匯入前請先關閉 KukuAI 客戶端。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。": "讀本機客戶端當前登入態,每次即時讀取(刪掉這條記錄不影響客戶端登入態)。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后点「刷新 Token」同步。": "讀本機客戶端當前登入態,每次即時讀取(刪掉這條記錄不影響客戶端登入態)。客戶端重新登入後點「重新整理 Token」同步。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后重新导入即可。": "讀本機客戶端當前登入態,每次即時讀取(刪掉這條記錄不影響客戶端登入態)。客戶端重新登入後重新匯入即可。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。": "讀本機客戶端當前登入態,每次即時讀取(刪掉這條記錄不影響客戶端登入態)。用 Zai / Google 登入客戶端的使用者走這一條。", "读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。": "讀本機客戶端當前登入態,每次即時讀取(刪掉這條記錄不影響客戶端登入態)。這一條歸入 {label}。", "读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。": "讀本機客戶端當前登入的 OAuth 權杖(明文 JSON,無需解密)。匯入後由閘道器自動續期,無需跟隨客戶端的 2 小時時效手動換權杖。", "读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。": "讀本機客戶端當前的登入態。客戶端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此僅 Windows 可用;客戶端正在執行時可能讀不到該檔案,先退出客戶端再試即可(也可改用「填寫憑證」貼上權杖)。", "账号信息": "帳號資訊", "账号信息不完整,请刷新后重试": "帳號資訊不完整,請重新整理後重試", "账号已添加,但请留意:{warning}": "帳號已新增,但請留意:{warning}", "账号版本": "帳號版本", "账号类型": "帳號型別", "账号:{name}
    ": "帳號:{name}
    ", "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 创建": "走 Inference Providers 路由,Key 在 hf.co/settings/tokens 建立", "还没有账号": "還沒有帳號", "这一家当前不支持网页登录,请改用填写凭证": "這一家當前不支援網頁登入,請改用填寫憑證", "这个账号当前没有可自动领的活动(只自动领每日登录赠送积分)": "這個帳號當前沒有可自動領的活動(只自動領每日登入贈送積分)", "这份额度走「活动套餐」通道:若转发时提示编码套餐已到期,": "這份額度走「活動套餐」通道:若轉發時提示編碼套餐已到期,", "这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。": "這裡填國內站(monkeycode-ai.com)的帳號:請貼上國內站的 session。國際站(monkeycode-ai.net)是另一家 provider、另一套帳號,兩邊的憑證不通用。", "这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。": "這裡填國際站(monkeycode-ai.net)的帳號:請貼上國際站的 session。國內站(monkeycode-ai.com)是另一家 provider、另一套帳號,兩邊的憑證不通用。", "选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "選擇 CatPaw 客戶端的登入態檔案 auth.json(Windows:C:\\Users\\<你的使用者名稱>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。匯入後憑證直接落在閘道器、可獨立轉發,不再依賴本機客戶端。", "选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。": "選擇 Cline 客戶端的登入態檔案 providers.json(Windows:C:\\Users\\<你的使用者名稱>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。匯入後憑證直接落在閘道器、可獨立轉發,不再依賴本機客戶端;這一條歸入 {label}。", "选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。": "選擇 KukuAI 客戶端的 Cookies 檔案(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二進位制** SQLite 資料庫(無副檔名,不是 JSON)。匯入時讀的是你選中檔案的副本、不和執行中的客戶端搶佔用,因此**無需先關閉客戶端**。匯入後憑證直接落在閘道器、可獨立轉發。", "选择凭证文件导入": "選擇憑證檔案匯入", "选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。": "選擇官方客戶端的登入態憑證檔案(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 匯出的帳號 JSON(巢狀 / 扁平形均可)。", "选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。": "選擇小浣熊客戶端的登入態檔案 auth.json(Windows:C:\\Users\\<你的使用者名稱>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。匯入後憑證直接落在閘道器、可獨立轉發,不再依賴本機客戶端。", "选择提供商": "選擇提供商", "选择的文件不是合法 JSON": "選擇的檔案不是合法 JSON", "选择要添加账号的提供商": "選擇要新增帳號的提供商", "重新扫描": "重新掃描", "鉴权": "鑑權", "阿里云 Token Plan 套餐(国际站)": "阿里雲 Token Plan 套餐(國際站)", "阿里云百炼 Coding 套餐(国内站)": "阿里雲百鍊 Coding 套餐(國內站)", "阿里云百炼 Coding 套餐(国际站)": "阿里雲百鍊 Coding 套餐(國際站)", "阿里云百炼模型服务(国际站,OpenAI 兼容模式)": "阿里雲百鍊模型服務(國際站,OpenAI 相容模式)", "需要图形验证码:登录窗口已弹出,请填写后继续": "需要圖形驗證碼:登入視窗已彈出,請填寫後繼續", "预置 API": "預置 API", "领取 CodeArts 每日福利": "領取 CodeArts 每日福利", "领取失败:{message}": "領取失敗:{message}", "领取并让官方确认": "領取並讓官方確認", "风控验证配置不完整,请稍后重试": "風控驗證配置不完整,請稍後重試", "验证码": "驗證碼", "验证码已发送。收到后填入下方并点「登录并添加」": "驗證碼已傳送。收到後填入下方並點「登入並新增」", "验证码已发送,请查看手机短信": "驗證碼已傳送,請檢視手機簡訊", "验证码已发送,请查看手机短信。填入下方并点「登录并添加」即完成登录": "驗證碼已傳送,請檢視手機簡訊。填入下方並點「登入並新增」即完成登入", "验证码已发送,请查看短信": "驗證碼已傳送,請檢視簡訊", "验证码校验失败,请重试": "驗證碼校驗失敗,請重試", "验证码校验超时,请重试": "驗證碼校驗超時,請重試", "验证码组件不可用,请重试": "驗證碼元件不可用,請重試", "验证码组件初始化超时,请重试": "驗證碼元件初始化超時,請重試", "验证码组件加载失败,请检查网络后重试": "驗證碼元件載入失敗,請檢查網路後重試", "验证码组件加载异常,请重试": "驗證碼元件載入異常,請重試", "验证码组件加载超时,请检查网络后重试": "驗證碼元件載入超時,請檢查網路後重試", "验证码组件未加载,请重启应用后重试": "驗證碼元件未載入,請重啟應用後重試", "(免费额度池,模型名带 cline-free/ 前缀)。": "(免費額度池,模型名帶 cline-free/ 字首)。", "(其中 {n} 把恢复为不限制)": "(其中 {n} 把恢復為不限制)", "(必填)": "(必填)", "(有效期 {window})": "(有效期 {window})", "(订阅池,模型名带 cline-pass/ 前缀,需要账号有对应订阅)。": "(訂閱池,模型名帶 cline-pass/ 字首,需要帳號有對應訂閱)。", ",下一次最快 {minutes} 分钟后": ",下一次最快 {minutes} 分鐘後", ",并从 {n} 把 Key 的可用提供商里移除": ",並從 {n} 把 Key 的可用提供商裡移除" }); diff --git a/desktop-tauri/ui/i18n/zh-Hant/checkin.js b/desktop-tauri/ui/i18n/zh-Hant/checkin.js index da5289f0..1781a639 100644 --- a/desktop-tauri/ui/i18n/zh-Hant/checkin.js +++ b/desktop-tauri/ui/i18n/zh-Hant/checkin.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:zh-Hant/checkin —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · ": " · ", "SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新": "SOLO 的 checkin_credits 領取 · 按自然日 0 點重新整理", "UMID 组件安装失败:{error}": "UMID 元件安裝失敗:{error}", "WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细": "WorkBuddy 國內版 · 查詢任務樹後一鍵點亮/領獎 · 含開學季與貓貓旅行 · 點擊行展開明細", "WorkBuddy 国际版保活模型,按顺序回退": "WorkBuddy 國際版保活模型,按順序回退", "WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关": "WorkBuddy 國際版日活任務的免費模型 · 全域一份,與條目無關", "{date}(每天 {time})": "{date}(每天 {time})", "{label} ×{count}({reason})": "{label} ×{count}({reason})", "{n} 项可完成": "{n} 項可完成", "{n} 项待领": "{n} 項待領", "{total} 账号 · {done} 已签": "{total} 帳號 · {done} 已籤", "{total} 项任务 · {pending} 项可完成": "{total} 項任務 · {pending} 項可完成", "✅ UMID 组件已安装,下轮签到即可领取国际版积分": "✅ UMID 元件已安裝,下輪簽到即可領取國際版積分", "✅ {message}": "✅ {message}", "✅ 定时签到已开启": "✅ 定時簽到已開啟", "✅ 开学季任务执行完成": "✅ 開學季任務執行完成", "✅ 成长任务执行完成:积分 +{credit} 能量 +{energy}": "✅ 成長任務執行完成:積分 +{credit} 能量 +{energy}", "✅ 新手福利领取完成,累计 {n} 积分": "✅ 新手福利領取完成,累計 {n} 積分", "✅ 签到完成:{succeeded}/{total} 个账号成功领取{active}": "✅ 簽到完成:{succeeded}/{total} 個帳號成功領取{active}", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空提交恢复缺省({defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "「保活模型鏈」是 WorkBuddy 國際版領日活時用來保活的免費模型,按順序逐個嘗試、 第一個成功即止;清空提交恢復預設({defaults})。 當天去重 + 冪等領取:重複執行只會拿到「已領取」,不會重複加分。", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "「保活模型鏈」是 WorkBuddy 國際版領日活時用來保活的免費模型,按順序逐個嘗試、 第一個成功即止;清空清單並儲存 = 恢復預設鏈(當前預設:{defaults})。 當天去重 + 冪等領取:重複執行只會拿到「已領取」,不會重複加分。", "「免费领积分」的每日任务 · 逐个领取": "「免費領積分」的每日任務 · 逐個領取", "一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单": "一次性福利 · 簽到後自動查詢並領取,領完記在本機不再查上游 · 點擊行展開任務清單", "一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证": "一次領當天全部獎勵(每日簽到 + 新人禮) · 冪等由上游保證", "一键安装组件": "一鍵安裝元件", "一键完成": "一鍵完成", "一键领取({n})": "一鍵領取({n})", "上次执行:{line}": "上次執行:{line}", "上次签到时间": "上次簽到時間", "上次领取 {time} · 领取需通过滑块验证码": "上次領取 {time} · 領取需通過滑塊驗證碼", "下次 {date}": "下次 {date}", "下次 {date} · 点此管理条目": "下次 {date} · 點此管理條目", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它与 ZCode 的每日/套餐福利领取见「活动福利」。": "不參與每日簽到:{list}。CodeArts 的一次性獎勵在下方「新手福利」,它與 ZCode 的每日/套餐福利領取見「活動福利」。", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。": "不參與每日簽到:{list}。CodeArts 的一次性獎勵在下方「新手福利」,它的每日福利就在上方「每日簽到」裡;ZCode 的套餐領取見「活動福利」。", "与国内版同一套任务接口 · 站点不同": "與國內版同一套任務介面 · 站點不同", "今天已领 {n} 份": "今天已領 {n} 份", "今天未领": "今天未領", "今天(北京时间 {day})已领取 · 官方确认到账 {n} 项 · 明天可再领": "今天(北京時間 {day})已領取 · 官方確認到賬 {n} 項 · 明天可再領", "今日已执行": "今日已執行", "今日已派出": "今日已派出", "今日已领取": "今日已領取", "今日签到": "今日簽到", "今日签到进度": "今日簽到進度", "从清单里移除": "從清單裡移除", "保活": "保活", "保活 {n}": "保活 {n}", "保活+领取": "保活+領取", "保活中…": "保活中…", "保活模型链": "保活模型鏈", "全选": "全選", "全部 {n} 家": "全部 {n} 家", "全部完成": "全部完成", "全部查询": "全部查詢", "全部结果": "全部結果", "全部账号均可签到": "全部帳號均可簽到", "全部领取": "全部領取", "关着:所有条目都不执行": "關著:所有條目都不執行", "删除失败:{error}": "刪除失敗:{error}", "删除定时条目": "刪除定時條目", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活并在网页通道建会话点亮日活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "到點後自動簽到勾選提供商的可用帳號(WorkBuddy 國內版走每日簽到介面,國際版走每日活躍任務:探測活動、領日活獎勵、再用免費模型保活並在網頁通道建會話點亮日活;小浣熊走桌面版每日積分鏈路;AutoClaw 走官方客戶端的每日簽到任務;Qoder 中國版走活動領取,沒被下發活動的帳號會得到中性提示;Qoder 國際版要先帶裝置風控身份(本機 Qoder 客戶端 / qodercli 的 UMID 元件生成)才能看到「每日 100 Credits」活動,缺元件時會得到明確的說明而不是報錯;Trae 領 SOLO 的每日簽到積分(按自然日 0 點重置),上游沒對該帳號開活動、或憑據裡沒有裝置號時得到的是「未領取 + 具體原因」而不是報錯;Loomy 每天替帳號打一次首次登入積分;KukuAI 領「免費領積分」的每日任務;CodeArts 領當日的運營福利(領取 → 確認 → 回讀核實,自動執行受每天 6 次 / 間隔 10 分鐘的寫請求限流);OfficeAce 果辦一次領當天全部獎勵(每日簽到 + 新人禮))。錯過時點開機後會自動補籤,不會因為當時沒開機而漏掉。各家簽到介面都是冪等的,重複執行不會重複領取。", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "到點後自動簽到勾選提供商的可用帳號(WorkBuddy 國內版走每日簽到介面,國際版走每日活躍任務:探測活動、領日活獎勵、再用免費模型保活;小浣熊走桌面版每日積分鏈路;AutoClaw 走官方客戶端的每日簽到任務;Qoder 中國版走活動領取,沒被下發活動的帳號會得到中性提示;Qoder 國際版要先帶裝置風控身份(本機 Qoder 客戶端 / qodercli 的 UMID 元件生成)才能看到「每日 100 Credits」活動,缺元件時會得到明確的說明而不是報錯;Trae 領 SOLO 的每日簽到積分(按自然日 0 點重置),上游沒對該帳號開活動、或憑據裡沒有裝置號時得到的是「未領取 + 具體原因」而不是報錯;Loomy 每天替帳號打一次首次登入積分;KukuAI 領「免費領積分」的每日任務;CodeArts 領當日的運營福利(領取 → 確認 → 回讀核實,自動執行受每天 6 次 / 間隔 10 分鐘的寫請求限流);OfficeAce 果辦一次領當天全部獎勵(每日簽到 + 新人禮))。錯過時點開機後會自動補籤,不會因為當時沒開機而漏掉。各家簽到介面都是冪等的,重複執行不會重複領取。", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "到點後自動簽到勾選提供商的可用帳號(WorkBuddy 國內版走每日簽到介面,國際版走每日活躍任務:探測活動、領日活獎勵、再用免費模型保活;小浣熊走桌面版每日積分鏈路;AutoClaw 走官方客戶端的每日簽到任務;Qoder 中國版走活動領取,沒被下發活動的帳號會得到中性提示;Qoder 國際版要先帶裝置風控身份(本機 Qoder 客戶端 / qodercli 的 UMID 元件生成)才能看到「每日 100 Credits」活動,缺元件時會得到明確的說明而不是報錯;Trae 領 SOLO 的每日簽到積分(按自然日 0 點重置),上游沒對該帳號開活動、或憑據裡沒有裝置號時得到的是「未領取 + 具體原因」而不是報錯;Loomy 每天替帳號打一次首次登入積分;KukuAI 領「免費領積分」的每日任務;OfficeAce 果辦一次領當天全部獎勵(每日簽到 + 新人禮))。錯過時點開機後會自動補籤,不會因為當時沒開機而漏掉。各家簽到介面都是冪等的,重複執行不會重複領取。", "到点时逐个签到,已勾 {n} / {total} 家": "到點時逐個簽到,已勾 {n} / {total} 家", "北京时间 · 错过时点开机后会自动补签": "北京時間 · 錯過時點開機後會自動補籤", "去领取": "去領取", "另有 {n} 个账号不参与每日签到": "另有 {n} 個帳號不參與每日簽到", "只领取已完成任务的奖励(幂等,重复点不会重复入账)": "只領取已完成任務的獎勵(冪等,重複點不會重複入帳)", "可完成": "可完成", "可派出": "可派出", "可选 · 缺省显示时刻": "可選 · 預設顯示時刻", "可领奖": "可領獎", "后端未返回定时签到数据": "後端未返回定時簽到資料", "后端未返回签到中心数据": "後端未返回簽到中心資料", "国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。": "國際版簽到需要 UMID 元件生成裝置風控身份(本機未檢測到)。", "在途": "在途", "填入缺省链": "填入預設鏈", "填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)": "填好時刻與簽到範圍後點「儲存」;名稱可以留空(介面顯示時刻)", "失败 {n}": "失敗 {n}", "套餐明细": "套餐明細", "安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。": "安裝 Qoder 客戶端或 qodercli 後即可(無需重啟閘道器)。", "安装中…": "安裝中…", "官方客户端的每日签到任务": "官方客戶端的每日簽到任務", "定时签到": "定時簽到", "定时签到已关闭": "定時簽到已關閉", "尚未查询": "尚未查詢", "尚未查询任务状态": "尚未查詢任務狀態", "巡检一次": "巡檢一次", "已保存:{label}": "已儲存:{label}", "已保存:保活模型链": "已儲存:保活模型鏈", "已保存:定时条目": "已儲存:定時條目", "已删除定时条目": "已刪除定時條目", "已完成活跃上报(连登已点亮),但活动未开启(本次没有可领的奖励)": "已完成活躍上報(連登已點亮),但活動未開啟(本次沒有可領的獎勵)", "已抽 {n} 次": "已抽 {n} 次", "已有结果 {checked} / 共 {total} 个账号": "已有結果 {checked} / 共 {total} 個帳號", "已签": "已籤", "已领 {claimed}/{total} · 累计 {earned} 积分": "已領 {claimed}/{total} · 累計 {earned} 積分", "已领养": "已領養", "已领取": "已領取", "带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件": "帶裝置風控身份領取「每日 100 Credits」 · 需要 UMID 元件", "开学季执行失败:{error}": "開學季執行失敗:{error}", "开学季执行完成:{message}": "開學季執行完成:{message}", "开学季活动": "開學季活動", "开着:启用的条目按各自时刻执行": "開著:啟用的條目按各自時刻執行", "待签": "待籤", "待领新手福利": "待領新手福利", "总开关": "總開關", "总开关已关闭 · 在「定时签到」页打开": "總開關已關閉 · 在「定時簽到」頁開啟", "成功 {n}": "成功 {n}", "成长任务": "成長任務", "成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}": "成長任務完成({done} 項成功、{failed} 項失敗),積分 +{credit} 能量 +{energy}", "成长任务执行失败:{error}": "成長任務執行失敗:{error}", "执行失败:{error}": "執行失敗:{error}", "执行并抽奖": "執行並抽獎", "执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)": "執行開學季任務並抽空抽獎機會(分享/對話/專家事件)", "把缺省链填进清单,再点「保存」生效": "把預設鏈填進清單,再點「儲存」生效", "抽奖机会 {n} 次": "抽獎機會 {n} 次", "按住拖动调整顺序": "按住拖動調整順序", "按住拖动,调整这个模型在链里的顺序": "按住拖動,調整這個模型在鏈裡的順序", "按序逐个尝试,共 {n} 个": "按序逐個嘗試,共 {n} 個", "按结果筛选": "按結果篩選", "探测 + 领取 + 保活 · 与定时签到同一套组合": "探測 + 領取 + 保活 · 與定時簽到同一套組合", "探测活动并领取日活奖励 · 不做保活": "探測活動並領取日活獎勵 · 不做保活", "接单 + 点亮 + 领奖(只处理已实测沉淀的任务)": "接單+點亮+領獎(只處理已實測沉澱的任務)", "搜账号 / 收益 / 错误 / 触发原因": "搜帳號 / 收益 / 錯誤 / 觸發原因", "收起新建": "收起新建", "新建定时条目": "新建定時條目", "新手福利": "新手福利", "新手福利有 {n} 项领取失败,可重试": "新手福利有 {n} 項領取失敗,可重試", "无": "無", "无独立签到接口 · 每天替账号打一次首次登录积分": "無獨立簽到介面 · 每天替帳號打一次首次登入積分", "无账号": "無帳號", "暂不可领": "暫不可領", "暂不支持": "暫不支援", "暂无启用的条目 · 点此添加": "暫無啟用的條目 · 點此新增", "有未保存的改动": "有未儲存的改動", "未勾选提供商": "未勾選提供商", "未开启": "未開啟", "未解锁": "未解鎖", "未领养(领养需先完成当日对话量)": "未領養(領養需先完成當日對話量)", "未领取": "未領取", "本地台账里今天还没领过;活动期内每天发一份新套餐": "本地臺賬裡今天還沒領過;活動期內每天發一份新套餐", "本地台账里今天(北京时间)领过的套餐:{plans}": "本地臺賬裡今天(北京時間)領過的套餐:{plans}", "条启用": "條啟用", "查询于 {time}": "查詢於 {time}", "查询任务": "查詢任務", "查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)": "查詢任務樹並一鍵點亮/領獎(含開學季與貓貓旅行)", "查询失败:{error}": "查詢失敗:{error}", "桌面端每日积分链路": "桌面版每日積分鏈路", "正在读取定时签到数据…": "正在讀取定時簽到資料…", "正在读取签到数据…": "正在讀取簽到資料…", "每天 {time}": "每天 {time}", "每天 {time} · {scope}": "每天 {time} · {scope}", "每天时刻": "每天時刻", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "每執行一次簽到 · 每個帳號一條明細 · 結果與收益以後端落賬為準", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "每日活躍任務 · 網頁會話保活點亮日活,領取才加積分 · 兩者可分開執行", "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "每日登入積分 · 順帶領取會員活動 · 訪客帳號無積分體系(跳過)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "每日福利 · 領取 → 確認 → 回讀核實 · 按北京時間自然日重置", "每日签到": "每日簽到", "每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签": "每條條目各有時刻與範圍 · 各自當天去重 · 錯過時點開機自動補籤", "每账号一条明细 · 点此查看「签到记录」": "每帳號一條明細 · 點此檢視「簽到記錄」", "没有可领套餐的账号(ZCode 未添加)。": "沒有可領套餐的帳號(ZCode 未新增)。", "没有可领福利的账号(CodeArts / ZCode 未添加)。": "沒有可領福利的帳號(CodeArts / ZCode 未新增)。", "没有支持成长任务的账号(需要 WorkBuddy 国内版)。": "沒有支援成長任務的帳號(需要 WorkBuddy 國內版)。", "没有支持新手福利的账号": "沒有支援新手福利的帳號", "没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。": "沒有支援新手福利的帳號 —— 目前有 Loomy、小浣熊、CodeArts 三家。", "没有符合筛选条件的签到记录": "沒有符合篩選條件的簽到記錄", "没有需要执行的任务": "沒有需要執行的任務", "活动福利": "活動福利", "活动(campaign)领取 · 每天 10:00 刷新": "活動(campaign)領取 · 每天 10:00 重新整理", "活跃上报与保活均未成功,且活动未开启": "活躍上報與保活均未成功,且活動未開啟", "活跃上报完成,但保活未成功且活动未开启": "活躍上報完成,但保活未成功且活動未開啟", "活跃上报完成,但活动未开启": "活躍上報完成,但活動未開啟", "活跃上报成功": "活躍上報成功", "活跃保活失败": "活躍保活失敗", "活跃保活完成": "活躍保活完成", "活跃保活完成,但活动未开启": "活躍保活完成,但活動未開啟", "活跃保活完成,但活跃上报失败且活动未开启": "活躍保活完成,但活躍上報失敗且活動未開啟", "添加条目": "新增條目", "添加模型": "新增模型", "清单为空 —— 保存后恢复缺省链": "清單為空 —— 儲存後恢復預設鏈", "清单里已经有「{name}」": "清單裡已經有「{name}」", "清空失败:{error}": "清空失敗:{error}", "清空签到记录": "清空簽到記錄", "点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领": "點「套餐明細」看領到了哪幾份、什麼時候生效;活動期內每天可再領", "点击行展开账号明细 · 单账号签到不受范围限制": "點擊行展開帳號明細 · 單帳號簽到不受範圍限制", "独立链路 · 只提供手动入口 · 不进自动签到": "獨立鏈路 · 只提供手動入口 · 不進自動簽到", "猫猫旅行": "貓貓旅行", "猫猫旅行失败:{error}": "貓貓旅行失敗:{error}", "猫猫旅行失败:{message}": "貓貓旅行失敗:{message}", "猫猫旅行:{message}": "貓貓旅行:{message}", "用免费模型与网页会话维持账号活跃 · 不领取奖励": "用免費模型與網頁會話維持帳號活躍 · 不領取獎勵", "确定删除「{name}」?删除后到点不再执行。": "確定刪除「{name}」?刪除後到點不再執行。", "确定清空全部签到记录?清空后无法恢复。": "確定清空全部簽到記錄?清空後無法恢復。", "确定清空当前筛选出的签到记录?清空后无法恢复。": "確定清空當前篩選出的簽到記錄?清空後無法恢復。", "移除 {name}": "移除 {name}", "立即全部签到": "立即全部簽到", "签到": "簽到", "签到中…": "簽到中…", "签到中心分区": "簽到中心分割槽", "签到失败:{error}": "簽到失敗:{error}", "签到完成:{succeeded}/{total} 成功{active},{failed} 个失败": "簽到完成:{succeeded}/{total} 成功{active},{failed} 個失敗", "签到成功": "簽到成功", "签到提供商": "簽到提供商", "签到范围": "簽到範圍", "签到触发时刻": "簽到觸發時刻", "签到记录": "簽到記錄", "签到记录时间范围": "簽到記錄時間範圍", "签到记录表": "簽到記錄表", "累计签到记录": "累計簽到記錄", "结算于 {time}": "結算於 {time}", "能量 {n}": "能量 {n}", "腾讯每日签到接口 · 仅国内版": "騰訊每日簽到介面 · 僅國內版", "至少勾一家才能保存(不勾等于这条条目不签到)": "至少勾一家才能儲存(不勾等於這條條目不簽到)", "读取定时签到数据失败:{error}": "讀取定時簽到資料失敗:{error}", "读取签到数据失败:{error}": "讀取簽到資料失敗:{error}", "读取签到记录失败:{error}": "讀取簽到記錄失敗:{error}", "跳过 {n}": "跳過 {n}", "运营活动交付(领取 → 确认 → 回读核实)· 领的是套餐赠送积分": "運營活動交付(領取 → 確認 → 回讀核實)· 領的是套餐贈送積分", "还没执行过": "還沒執行過", "还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。": "還沒有定時條目 —— 點「新增條目」建一條,或到「簽到中心」手動簽到。", "还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。": "還沒有簽到記錄 —— 手動簽到或等定時條目執行後,明細會出現在這裡。", "连登 {n} 天": "連登 {n} 天", "部分": "部分", "重签": "重籤", "重试": "重試", "限时体验套餐 · 领取需滑块验证码 · 逐份查看与领取": "限時體驗套餐 · 領取需滑塊驗證碼 · 逐份檢視與領取", "限时体验套餐(start-plan),活动期内每天一份 · 领取需滑块验证码": "限時體驗套餐(start-plan),活動期內每天一份 · 領取需滑塊驗證碼", "需真实操作": "需真實操作", "非活动期": "非活動期", "项": "項", "领取": "領取", "领取中": "領取中", "领取中…": "領取中…", "领取失败": "領取失敗", "领取失败:{error}": "領取失敗:{error}", "(共 {n} 个账号)": "(共 {n} 個帳號)", "(能量 / 连登读数本次未取到)": "(能量/連登讀數本次未取到)", ",日活保活 {n} 个": ",日活保活 {n} 個" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · ": " · ", "SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新": "SOLO 的 checkin_credits 領取 · 按自然日 0 點重新整理", "UMID 组件安装失败:{error}": "UMID 元件安裝失敗:{error}", "WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细": "WorkBuddy 國內版 · 查詢任務樹後一鍵點亮/領獎 · 含開學季與貓貓旅行 · 點擊行展開明細", "WorkBuddy 国际版保活模型,按顺序回退": "WorkBuddy 國際版保活模型,按順序回退", "WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关": "WorkBuddy 國際版日活任務的免費模型 · 全域一份,與條目無關", "{date}(每天 {time})": "{date}(每天 {time})", "{label} ×{count}({reason})": "{label} ×{count}({reason})", "{n} 项可完成": "{n} 項可完成", "{n} 项待领": "{n} 項待領", "{total} 账号 · {done} 已签": "{total} 帳號 · {done} 已籤", "{total} 项任务 · {pending} 项可完成": "{total} 項任務 · {pending} 項可完成", "✅ UMID 组件已安装,下轮签到即可领取国际版积分": "✅ UMID 元件已安裝,下輪簽到即可領取國際版積分", "✅ {message}": "✅ {message}", "✅ 定时签到已开启": "✅ 定時簽到已開啟", "✅ 开学季任务执行完成": "✅ 開學季任務執行完成", "✅ 成长任务执行完成:积分 +{credit} 能量 +{energy}": "✅ 成長任務執行完成:積分 +{credit} 能量 +{energy}", "✅ 新手福利领取完成,累计 {n} 积分": "✅ 新手福利領取完成,累計 {n} 積分", "✅ 签到完成:{succeeded}/{total} 个账号成功领取{active}": "✅ 簽到完成:{succeeded}/{total} 個帳號成功領取{active}", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空提交恢复缺省({defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "「保活模型鏈」是 WorkBuddy 國際版領日活時用來保活的免費模型,按順序逐個嘗試、 第一個成功即止;清空提交恢復預設({defaults})。 當天去重 + 冪等領取:重複執行只會拿到「已領取」,不會重複加分。", "「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。": "「保活模型鏈」是 WorkBuddy 國際版領日活時用來保活的免費模型,按順序逐個嘗試、 第一個成功即止;清空清單並儲存 = 恢復預設鏈(當前預設:{defaults})。 當天去重 + 冪等領取:重複執行只會拿到「已領取」,不會重複加分。", "「免费领积分」的每日任务 · 逐个领取": "「免費領積分」的每日任務 · 逐個領取", "一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单": "一次性福利 · 簽到後自動查詢並領取,領完記在本機不再查上游 · 點擊行展開任務清單", "一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证": "一次領當天全部獎勵(每日簽到 + 新人禮) · 冪等由上游保證", "一键安装组件": "一鍵安裝元件", "一键完成": "一鍵完成", "一键领取({n})": "一鍵領取({n})", "上次执行:{line}": "上次執行:{line}", "上次签到时间": "上次簽到時間", "上次领取 {time} · 领取需通过滑块验证码": "上次領取 {time} · 領取需通過滑塊驗證碼", "下次 {date}": "下次 {date}", "下次 {date} · 点此管理条目": "下次 {date} · 點此管理條目", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它与 ZCode 的每日/套餐福利领取见「活动福利」。": "不參與每日簽到:{list}。CodeArts 的一次性獎勵在下方「新手福利」,它與 ZCode 的每日/套餐福利領取見「活動福利」。", "不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。": "不參與每日簽到:{list}。CodeArts 的一次性獎勵在下方「新手福利」,它的每日福利就在上方「每日簽到」裡;ZCode 的套餐領取見「活動福利」。", "与国内版同一套任务接口 · 站点不同": "與國內版同一套任務介面 · 站點不同", "今天已领 {n} 份": "今天已領 {n} 份", "今天未领": "今天未領", "今天(北京时间 {day})已领取 · 官方确认到账 {n} 项 · 明天可再领": "今天(北京時間 {day})已領取 · 官方確認到賬 {n} 項 · 明天可再領", "今日已执行": "今日已執行", "今日已派出": "今日已派出", "今日已领取": "今日已領取", "今日签到": "今日簽到", "今日签到进度": "今日簽到進度", "从清单里移除": "從清單裡移除", "保活": "保活", "保活 {n}": "保活 {n}", "保活+领取": "保活+領取", "保活中…": "保活中…", "保活模型链": "保活模型鏈", "全选": "全選", "全部 {n} 家": "全部 {n} 家", "全部完成": "全部完成", "全部查询": "全部查詢", "全部结果": "全部結果", "全部账号均可签到": "全部帳號均可簽到", "全部领取": "全部領取", "关着:所有条目都不执行": "關著:所有條目都不執行", "删除失败:{error}": "刪除失敗:{error}", "删除定时条目": "刪除定時條目", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活并在网页通道建会话点亮日活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "到點後自動簽到勾選提供商的可用帳號(WorkBuddy 國內版走每日簽到介面,國際版走每日活躍任務:探測活動、領日活獎勵、再用免費模型保活並在網頁通道建會話點亮日活;小浣熊走桌面版每日積分鏈路;AutoClaw 走官方客戶端的每日簽到任務;Qoder 中國版走活動領取,沒被下發活動的帳號會得到中性提示;Qoder 國際版要先帶裝置風控身份(本機 Qoder 客戶端 / qodercli 的 UMID 元件生成)才能看到「每日 100 Credits」活動,缺元件時會得到明確的說明而不是報錯;Trae 領 SOLO 的每日簽到積分(按自然日 0 點重置),上游沒對該帳號開活動、或憑據裡沒有裝置號時得到的是「未領取 + 具體原因」而不是報錯;Loomy 每天替帳號打一次首次登入積分;KukuAI 領「免費領積分」的每日任務;CodeArts 領當日的運營福利(領取 → 確認 → 回讀核實,自動執行受每天 6 次 / 間隔 10 分鐘的寫請求限流);OfficeAce 果辦一次領當天全部獎勵(每日簽到 + 新人禮))。錯過時點開機後會自動補籤,不會因為當時沒開機而漏掉。各家簽到介面都是冪等的,重複執行不會重複領取。", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "到點後自動簽到勾選提供商的可用帳號(WorkBuddy 國內版走每日簽到介面,國際版走每日活躍任務:探測活動、領日活獎勵、再用免費模型保活;小浣熊走桌面版每日積分鏈路;AutoClaw 走官方客戶端的每日簽到任務;Qoder 中國版走活動領取,沒被下發活動的帳號會得到中性提示;Qoder 國際版要先帶裝置風控身份(本機 Qoder 客戶端 / qodercli 的 UMID 元件生成)才能看到「每日 100 Credits」活動,缺元件時會得到明確的說明而不是報錯;Trae 領 SOLO 的每日簽到積分(按自然日 0 點重置),上游沒對該帳號開活動、或憑據裡沒有裝置號時得到的是「未領取 + 具體原因」而不是報錯;Loomy 每天替帳號打一次首次登入積分;KukuAI 領「免費領積分」的每日任務;CodeArts 領當日的運營福利(領取 → 確認 → 回讀核實,自動執行受每天 6 次 / 間隔 10 分鐘的寫請求限流);OfficeAce 果辦一次領當天全部獎勵(每日簽到 + 新人禮))。錯過時點開機後會自動補籤,不會因為當時沒開機而漏掉。各家簽到介面都是冪等的,重複執行不會重複領取。", "到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。": "到點後自動簽到勾選提供商的可用帳號(WorkBuddy 國內版走每日簽到介面,國際版走每日活躍任務:探測活動、領日活獎勵、再用免費模型保活;小浣熊走桌面版每日積分鏈路;AutoClaw 走官方客戶端的每日簽到任務;Qoder 中國版走活動領取,沒被下發活動的帳號會得到中性提示;Qoder 國際版要先帶裝置風控身份(本機 Qoder 客戶端 / qodercli 的 UMID 元件生成)才能看到「每日 100 Credits」活動,缺元件時會得到明確的說明而不是報錯;Trae 領 SOLO 的每日簽到積分(按自然日 0 點重置),上游沒對該帳號開活動、或憑據裡沒有裝置號時得到的是「未領取 + 具體原因」而不是報錯;Loomy 每天替帳號打一次首次登入積分;KukuAI 領「免費領積分」的每日任務;OfficeAce 果辦一次領當天全部獎勵(每日簽到 + 新人禮))。錯過時點開機後會自動補籤,不會因為當時沒開機而漏掉。各家簽到介面都是冪等的,重複執行不會重複領取。", "到点时逐个签到,已勾 {n} / {total} 家": "到點時逐個簽到,已勾 {n} / {total} 家", "北京时间 · 错过时点开机后会自动补签": "北京時間 · 錯過時點開機後會自動補籤", "去领取": "去領取", "另有 {n} 个账号不参与每日签到": "另有 {n} 個帳號不參與每日簽到", "只领取已完成任务的奖励(幂等,重复点不会重复入账)": "只領取已完成任務的獎勵(冪等,重複點不會重複入帳)", "可完成": "可完成", "可派出": "可派出", "可选 · 缺省显示时刻": "可選 · 預設顯示時刻", "可领奖": "可領獎", "后端未返回定时签到数据": "後端未返回定時簽到資料", "后端未返回签到中心数据": "後端未返回簽到中心資料", "国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。": "國際版簽到需要 UMID 元件生成裝置風控身份(本機未檢測到)。", "在途": "在途", "填入缺省链": "填入預設鏈", "填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)": "填好時刻與簽到範圍後點「儲存」;名稱可以留空(介面顯示時刻)", "失败 {n}": "失敗 {n}", "套餐明细": "套餐明細", "安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。": "安裝 Qoder 客戶端或 qodercli 後即可(無需重啟閘道器)。", "安装中…": "安裝中…", "官方客户端的每日签到任务": "官方客戶端的每日簽到任務", "定时签到": "定時簽到", "定时签到已关闭": "定時簽到已關閉", "尚未查询": "尚未查詢", "尚未查询任务状态": "尚未查詢任務狀態", "巡检一次": "巡檢一次", "已保存:{label}": "已儲存:{label}", "已保存:保活模型链": "已儲存:保活模型鏈", "已保存:定时条目": "已儲存:定時條目", "已删除定时条目": "已刪除定時條目", "已完成活跃上报(连登已点亮),但活动未开启(本次没有可领的奖励)": "已完成活躍上報(連登已點亮),但活動未開啟(本次沒有可領的獎勵)", "已抽 {n} 次": "已抽 {n} 次", "已有结果 {checked} / 共 {total} 个账号": "已有結果 {checked} / 共 {total} 個帳號", "已签": "已籤", "已领 {claimed}/{total} · 累计 {earned} 积分": "已領 {claimed}/{total} · 累計 {earned} 積分", "已领养": "已領養", "已领取": "已領取", "带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件": "帶裝置風控身份領取「每日 100 Credits」 · 需要 UMID 元件", "开学季执行失败:{error}": "開學季執行失敗:{error}", "开学季执行完成:{message}": "開學季執行完成:{message}", "开学季活动": "開學季活動", "开着:启用的条目按各自时刻执行": "開著:啟用的條目按各自時刻執行", "待签": "待籤", "待领新手福利": "待領新手福利", "总开关": "總開關", "总开关已关闭 · 在「定时签到」页打开": "總開關已關閉 · 在「定時簽到」頁開啟", "成功 {n}": "成功 {n}", "成长任务": "成長任務", "成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}": "成長任務完成({done} 項成功、{failed} 項失敗),積分 +{credit} 能量 +{energy}", "成长任务执行失败:{error}": "成長任務執行失敗:{error}", "执行失败:{error}": "執行失敗:{error}", "执行并抽奖": "執行並抽獎", "执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)": "執行開學季任務並抽空抽獎機會(分享/對話/專家事件)", "把缺省链填进清单,再点「保存」生效": "把預設鏈填進清單,再點「儲存」生效", "抽奖机会 {n} 次": "抽獎機會 {n} 次", "按住拖动调整顺序": "按住拖動調整順序", "按住拖动,调整这个模型在链里的顺序": "按住拖動,調整這個模型在鏈裡的順序", "按序逐个尝试,共 {n} 个": "按序逐個嘗試,共 {n} 個", "按结果筛选": "按結果篩選", "探测 + 领取 + 保活 · 与定时签到同一套组合": "探測 + 領取 + 保活 · 與定時簽到同一套組合", "探测活动并领取日活奖励 · 不做保活": "探測活動並領取日活獎勵 · 不做保活", "接单 + 点亮 + 领奖(只处理已实测沉淀的任务)": "接單+點亮+領獎(只處理已實測沉澱的任務)", "搜账号 / 收益 / 错误 / 触发原因": "搜帳號 / 收益 / 錯誤 / 觸發原因", "收起新建": "收起新建", "新建定时条目": "新建定時條目", "新手福利": "新手福利", "新手福利有 {n} 项领取失败,可重试": "新手福利有 {n} 項領取失敗,可重試", "无": "無", "无独立签到接口 · 每天替账号打一次首次登录积分": "無獨立簽到介面 · 每天替帳號打一次首次登入積分", "无账号": "無帳號", "暂不可领": "暫不可領", "暂不支持": "暫不支援", "暂无启用的条目 · 点此添加": "暫無啟用的條目 · 點此新增", "有未保存的改动": "有未儲存的改動", "未勾选提供商": "未勾選提供商", "未开启": "未開啟", "未解锁": "未解鎖", "未领养(领养需先完成当日对话量)": "未領養(領養需先完成當日對話量)", "未领取": "未領取", "本地台账里今天还没领过;活动期内每天发一份新套餐": "本地臺賬裡今天還沒領過;活動期內每天發一份新套餐", "本地台账里今天(北京时间)领过的套餐:{plans}": "本地臺賬裡今天(北京時間)領過的套餐:{plans}", "条启用": "條啟用", "查询于 {time}": "查詢於 {time}", "查询任务": "查詢任務", "查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)": "查詢任務樹並一鍵點亮/領獎(含開學季與貓貓旅行)", "查询失败:{error}": "查詢失敗:{error}", "桌面端每日积分链路": "桌面版每日積分鏈路", "正在读取定时签到数据…": "正在讀取定時簽到資料…", "正在读取签到数据…": "正在讀取簽到資料…", "每天 {time}": "每天 {time}", "每天 {time} · {scope}": "每天 {time} · {scope}", "每天时刻": "每天時刻", "每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准": "每執行一次簽到 · 每個帳號一條明細 · 結果與收益以後端落賬為準", "每日活动签到(+100 积分,已领则中性提示)": "每日活動簽到(+100 積分,已領則中性提示)", "每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行": "每日活躍任務 · 網頁會話保活點亮日活,領取才加積分 · 兩者可分開執行", "每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)": "每日登入積分 · 順帶領取會員活動 · 訪客帳號無積分體系(跳過)", "每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置": "每日福利 · 領取 → 確認 → 回讀核實 · 按北京時間自然日重置", "每日签到": "每日簽到", "每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签": "每條條目各有時刻與範圍 · 各自當天去重 · 錯過時點開機自動補籤", "每账号一条明细 · 点此查看「签到记录」": "每帳號一條明細 · 點此檢視「簽到記錄」", "没有可领套餐的账号(ZCode 未添加)。": "沒有可領套餐的帳號(ZCode 未新增)。", "没有可领福利的账号(CodeArts / ZCode 未添加)。": "沒有可領福利的帳號(CodeArts / ZCode 未新增)。", "没有支持成长任务的账号(需要 WorkBuddy 国内版)。": "沒有支援成長任務的帳號(需要 WorkBuddy 國內版)。", "没有支持新手福利的账号": "沒有支援新手福利的帳號", "没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。": "沒有支援新手福利的帳號 —— 目前有 Loomy、小浣熊、CodeArts 三家。", "没有符合筛选条件的签到记录": "沒有符合篩選條件的簽到記錄", "没有需要执行的任务": "沒有需要執行的任務", "活动福利": "活動福利", "活动(campaign)领取 · 每天 10:00 刷新": "活動(campaign)領取 · 每天 10:00 重新整理", "活跃上报与保活均未成功,且活动未开启": "活躍上報與保活均未成功,且活動未開啟", "活跃上报完成,但保活未成功且活动未开启": "活躍上報完成,但保活未成功且活動未開啟", "活跃上报完成,但活动未开启": "活躍上報完成,但活動未開啟", "活跃上报成功": "活躍上報成功", "活跃保活失败": "活躍保活失敗", "活跃保活完成": "活躍保活完成", "活跃保活完成,但活动未开启": "活躍保活完成,但活動未開啟", "活跃保活完成,但活跃上报失败且活动未开启": "活躍保活完成,但活躍上報失敗且活動未開啟", "添加条目": "新增條目", "添加模型": "新增模型", "清单为空 —— 保存后恢复缺省链": "清單為空 —— 儲存後恢復預設鏈", "清单里已经有「{name}」": "清單裡已經有「{name}」", "清空失败:{error}": "清空失敗:{error}", "清空签到记录": "清空簽到記錄", "点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领": "點「套餐明細」看領到了哪幾份、什麼時候生效;活動期內每天可再領", "点击行展开账号明细 · 单账号签到不受范围限制": "點擊行展開帳號明細 · 單帳號簽到不受範圍限制", "独立链路 · 只提供手动入口 · 不进自动签到": "獨立鏈路 · 只提供手動入口 · 不進自動簽到", "猫猫旅行": "貓貓旅行", "猫猫旅行失败:{error}": "貓貓旅行失敗:{error}", "猫猫旅行失败:{message}": "貓貓旅行失敗:{message}", "猫猫旅行:{message}": "貓貓旅行:{message}", "用免费模型与网页会话维持账号活跃 · 不领取奖励": "用免費模型與網頁會話維持帳號活躍 · 不領取獎勵", "确定删除「{name}」?删除后到点不再执行。": "確定刪除「{name}」?刪除後到點不再執行。", "确定清空全部签到记录?清空后无法恢复。": "確定清空全部簽到記錄?清空後無法恢復。", "确定清空当前筛选出的签到记录?清空后无法恢复。": "確定清空當前篩選出的簽到記錄?清空後無法恢復。", "移除 {name}": "移除 {name}", "立即全部签到": "立即全部簽到", "签到": "簽到", "签到中…": "簽到中…", "签到中心分区": "簽到中心分割槽", "签到失败:{error}": "簽到失敗:{error}", "签到完成:{succeeded}/{total} 成功{active},{failed} 个失败": "簽到完成:{succeeded}/{total} 成功{active},{failed} 個失敗", "签到成功": "簽到成功", "签到提供商": "簽到提供商", "签到范围": "簽到範圍", "签到触发时刻": "簽到觸發時刻", "签到记录": "簽到記錄", "签到记录时间范围": "簽到記錄時間範圍", "签到记录表": "簽到記錄表", "累计签到记录": "累計簽到記錄", "结算于 {time}": "結算於 {time}", "能量 {n}": "能量 {n}", "腾讯每日签到接口 · 仅国内版": "騰訊每日簽到介面 · 僅國內版", "至少勾一家才能保存(不勾等于这条条目不签到)": "至少勾一家才能儲存(不勾等於這條條目不簽到)", "读取定时签到数据失败:{error}": "讀取定時簽到資料失敗:{error}", "读取签到数据失败:{error}": "讀取簽到資料失敗:{error}", "读取签到记录失败:{error}": "讀取簽到記錄失敗:{error}", "跳过 {n}": "跳過 {n}", "运营活动交付(领取 → 确认 → 回读核实)· 领的是套餐赠送积分": "運營活動交付(領取 → 確認 → 回讀核實)· 領的是套餐贈送積分", "还没执行过": "還沒執行過", "还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。": "還沒有定時條目 —— 點「新增條目」建一條,或到「簽到中心」手動簽到。", "还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。": "還沒有簽到記錄 —— 手動簽到或等定時條目執行後,明細會出現在這裡。", "连登 {n} 天": "連登 {n} 天", "部分": "部分", "重签": "重籤", "重试": "重試", "限时体验套餐 · 领取需滑块验证码 · 逐份查看与领取": "限時體驗套餐 · 領取需滑塊驗證碼 · 逐份檢視與領取", "限时体验套餐(start-plan),活动期内每天一份 · 领取需滑块验证码": "限時體驗套餐(start-plan),活動期內每天一份 · 領取需滑塊驗證碼", "需真实操作": "需真實操作", "非活动期": "非活動期", "项": "項", "领取": "領取", "领取中": "領取中", "领取中…": "領取中…", "领取免费 CodingPlan(按需:套餐有效时只回读额度)": "領取免費 CodingPlan(按需:方案有效時只回讀額度)", "领取失败": "領取失敗", "领取失败:{error}": "領取失敗:{error}", "(共 {n} 个账号)": "(共 {n} 個帳號)", "(能量 / 连登读数本次未取到)": "(能量/連登讀數本次未取到)", ",日活保活 {n} 个": ",日活保活 {n} 個" }); diff --git a/desktop-tauri/ui/i18n/zh-Hant/requests.js b/desktop-tauri/ui/i18n/zh-Hant/requests.js index cca60236..3dbbefe2 100644 --- a/desktop-tauri/ui/i18n/zh-Hant/requests.js +++ b/desktop-tauri/ui/i18n/zh-Hant/requests.js @@ -1,2 +1,2 @@ /* Agent2API 词典构建产物:zh-Hant/requests —— 由 desktop-tauri/i18n/build.mjs 生成,请勿手改;源头是 i18n-src/*.json */ -window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · {n} 进行中": " · {n} 進行中", " · 会话粘住": " · 會話粘住", "

    无响应原文
    ": "
    無響應原文
    ", "
    无法解析响应内容
    ": "
    無法解析響應內容
    ", "
    无法解析请求体(不是含 messages 数组的 JSON)
    ": "
    無法解析請求體(不是含 messages 陣列的 JSON)
    ", "
    无请求原文
    ": "
    無請求原文
    ", "
    调用工具 {name}
    ": "
    呼叫工具 {name}
    ", "响应原文": "響應原文", "思考过程": "思考過程", "请求原文": "請求原文", "HTTP {status},但响应体阶段出错": "HTTP {status},但響應體階段出錯", "HTTP {status},但响应体阶段出错:见错误列": "HTTP {status},但響應體階段出錯:見錯誤列", "{duration} / 首帧 {first}": "{duration} / 首幀 {first}", "{m}分{s}秒": "{m}分{s}秒", "{n}秒": "{n}秒", "{s}秒{ms}ms": "{s}秒{ms}ms", "↻ {n} 次": "↻ {n} 次", "↻ 重试 {n} 次": "↻ 重試 {n} 次", "✅ 压缩完成": "✅ 壓縮完成", "「仅看进行中」开启时,状态固定为进行中": "「僅看進行中」開啟時,狀態固定為進行中", "上游模型繁忙,请求已排进上游队列;网关正按上游建议的时长等待后重发(不是登录态或额度问题)": "上游模型繁忙,請求已排進上游佇列;閘道器正按上游建議的時長等待後重發(不是登入態或額度問題)", "上游状态码: {status}": "上游狀態碼: {status}", "上游请求已发出,正在等第一个字节到达(模型的思考时间也在这段)": "上游請求已發出,正在等第一個位元組到達(模型的思考時間也在這段)", "上游返回错误": "上游返回錯誤", "下游请求的模型:{model}": "下游請求的模型:{model}", "仅清空报文原文": "僅清空報文原文", "仅看进行中": "僅看進行中", "令牌": "權杖", "体积": "體積", "保留统计行与报表,只抹掉请求 / 响应正文。清理后详情弹窗的「预览对话」将无原文可看,统计数字分毫不动。": "保留統計行與報表,只抹掉請求 / 響應正文。清理後詳情彈窗的「預覽對話」將無原文可看,統計數字分毫不動。", "全部删除": "全部刪除", "全部提供商": "全部提供商", "全部模型": "全部模型", "全部状态": "全部狀態", "共 {n} 次尝试": "共 {n} 次嘗試", "共 {n} 次尝试,最终由 {provider} 承载": "共 {n} 次嘗試,最終由 {provider} 承載", "其它": "其它", "内容按原样保存,请求头中的凭据字段已替换为 [redacted]": "內容按原樣儲存,請求頭中的憑據欄位已替換為 [redacted]", "内部重试": "內部重試", "切换路径:": "切換路徑:", "列表数据已刷新,请重试(关闭后重新打开这条详情)": "列表資料已重新整理,請重試(關閉後重新開啟這條詳情)", "删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。": "刪除日誌記錄本身。當前有篩選時只刪命中的條目,按天聚合報表不受影響;無篩選時清空全部並重置報表。此方式無法恢復。", "压缩中…": "壓縮中…", "压缩失败:{message}": "壓縮失敗:{message}", "压缩已在后台开始,完成后会自动提示": "壓縮已在後臺開始,完成後會自動提示", "压缩已在进行中": "壓縮已在進行中", "压缩已在进行中,接上它的进度等待完成": "壓縮已在進行中,接上它的進度等待完成", "压缩数据库": "壓縮資料庫", "压缩正在后台执行,完成后自动恢复": "壓縮正在後臺執行,完成後自動恢復", "原始报文": "原始報文", "另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)": "另有 {missing} 次嘗試未記錄明細(只保留最早的 {kept} 條)", "另有 {n} 个词命中": "另有 {n} 個詞命中", "只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)": "只顯示正在轉發中的請求(狀態列按階段顯示:連線中 / 等待響應 / 響應中 / 重試中,用時列顯示已用時)", "只看失败": "只看失敗", "只看成功": "只看成功", "只看进行中": "只看進行中", "命中了敏感词": "命中了敏感詞", "命中的敏感词": "命中的敏感詞", "响应中": "響應中", "响应体": "響應體", "响应头": "響應頭", "回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)": "回收已刪除資料佔用的磁碟空間(checkpoint + VACUUM,後臺執行)", "在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文": "在設定 → 通用裡開啟「除錯模式」後,新發生的請求才會儲存報文", "失败请求不记录用量": "失敗請求不記錄用量", "失败({status}):{error}": "失敗({status}):{error}", "失败:{error}": "失敗:{error}", "实际发给上游 {bytes} 字节": "實際發給上游 {bytes} 位元組", "客户端": "客戶端", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "將刪除 {count} 條日誌 · 其中 {raw} 條仍帶報文原文 · 資料庫佔用 {db}", "尝试 {n} ·": "嘗試 {n} ·", "尝试明细": "嘗試明細", "工具结果": "工具結果", "工具结果 · {id}": "工具結果 · {id}", "工具结果图片": "工具結果圖片", "已删除 {count} 条": "已刪除 {count} 條", "已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」": "已受理終止:上游連線已斷開,列表稍後重新整理為「請求已被手動終止」", "已抹掉 {count} 条报文原文": "已抹掉 {count} 條報文原文", "已用时(请求仍在转发中)": "已用時(請求仍在轉發中)", "成功": "成功", "成功({status})": "成功({status})", "执行清理": "執行清理", "报文超过单条上限,请求体 / 响应体已按上限截断": "報文超過單條上限,請求體 / 響應體已按上限截斷", "抹掉原文": "抹掉原文", "按提供商筛选": "按提供商篩選", "按提供商筛选(只列日志里出现过的)": "按提供商篩選(只列日誌裡出現過的)", "按时间倒序,全量保留": "按時間倒序,全量保留", "按模型筛选": "按模型篩選", "按模型筛选(只列日志里出现过的;精确匹配模型名)": "按模型篩選(只列日誌裡出現過的;精確匹配模型名)", "按请求结果筛选": "按請求結果篩選", "排队中": "排隊中", "提供商 / 账号": "提供商 / 帳號", "提供商: {provider}": "提供商: {provider}", "提示": "提示", "敏": "敏", "敏感词": "敏感詞", "断开上游连接,明细记为「请求已被手动终止」": "斷開上游連線,明細記為「請求已被手動終止」", "无报文原文(未开启「调试模式」,或已被清理)": "無報文原文(未開啟「除錯模式」,或已被清理)", "无结果记录": "無結果記錄", "时间": "時間", "暂无请求日志,网关还没有转发过请求": "暫無請求日誌,閘道器還沒有轉發過請求", "最终承载:{picked}": "最終承載:{picked}", "本轮尝试失败,正在退避等待或切换到下一个账号": "本輪嘗試失敗,正在退避等待或切換到下一個帳號", "来源客户端:{client}": "來源客戶端:{client}", "查看该请求的上游原始报文": "檢視該請求的上游原始報文", "模型": "模型", "模型测试": "模型測試", "模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计": "模型管理頁操作列那顆「測試」發起的請求:與真實請求同一條轉發鏈路,但不計入報表統計", "正在加载请求日志…": "正在載入請求日誌…", "正在统计…": "正在統計…", "正文超过单条上限,可能已被截断": "正文超過單條上限,可能已被截斷", "没有符合筛选条件的请求": "沒有符合篩選條件的請求", "清理": "清理", "清理中…": "清理中…", "清理失败:{message}": "清理失敗:{message}", "清理方式": "清理方式", "清理请求日志": "清理請求日誌", "生成速度:输出 token ÷ 流式段用时(总用时 − 首响);流式段不足总用时一半时按总用时计": "生成速度:輸出 token ÷ 流式段用時(總用時 − 首響);流式段不足總用時一半時按總用時計", "用时": "用時", "用量": "用量", "确定对全部请求日志执行「{label}」?将处理 {count} 条,此操作无法恢复。": "確定對全部請求日誌執行「{label}」?將處理 {count} 條,此操作無法恢復。", "确定按当前筛选执行「{label}」?将处理 {count} 条,此操作无法恢复。": "確定按當前篩選執行「{label}」?將處理 {count} 條,此操作無法恢復。", "确定终止这条正在转发的请求?
    上游连接会立即断开,明细将记为「请求已被手动终止」。": "確定終止這條正在轉發的請求?
    上游連線會立即斷開,明細將記為「請求已被手動終止」。", "等待响应": "等待響應", "策略 {strategy} · 会话粘性 {sticky}{hit}": "策略 {strategy} · 會話粘性 {sticky}{hit}", "终止": "終止", "终止失败:{error}": "終止失敗:{error}", "终止请求": "終止請求", "结果": "結果", "缓存读取: - / 命中率: -": "快取讀取: - / 命中率: -", "缓存读取: {cache} / 命中率: {rate}": "快取讀取: {cache} / 命中率: {rate}", "耗时": "耗時", "被跳过:": "被跳過:", "识别到 User-Agent,但不属于已知的客户端类型:{ua}": "識別到 User-Agent,但不屬於已知的客戶端型別:{ua}", "详情": "詳情", "详情内容": "詳情內容", "请求体": "請求體", "请求图片": "請求圖片", "请求在任何账号接手之前就失败了": "請求在任何帳號接手之前就失敗了", "请求头": "請求頭", "请求已受理,正在选路、取凭证、建立上游连接": "請求已受理,正在選路、取憑證、建立上游連線", "请求日志列表": "請求日誌列表", "请求日志时间范围": "請求日誌時間範圍", "请求日志表": "請求日誌表", "请求正在转发中,用时列显示的是已用时": "請求正在轉發中,用時列顯示的是已用時", "请求详情": "請求詳情", "读取请求日志失败,详见控制台": "讀取請求日誌失敗,詳見控制台", "路由回放": "路由回放", "转发到上游的模型:{model}": "轉發到上游的模型:{model}", "轮次": "輪次", "输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}": "輸入 {input} · 輸出 {output} · 總計 {total} · 快取讀 {cache}", "这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计": "這條明細是一次模型測試(人工發起):走的是真實轉發鏈路,但不計入報表統計", "这条明细没有记录提供商(旧数据或请求未走到转发)": "這條明細沒有記錄提供商(舊資料或請求未走到轉發)", "这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)": "這條明細沒有記錄來源客戶端(舊資料、模型測試,或請求未帶 User-Agent)", "这条记录命中了敏感词,但没有留下命中明细": "這條記錄命中了敏感詞,但沒有留下命中明細", "这条记录的尝试明细未采集(该字段上线前的旧数据,或请求在转发前就失败)": "這條記錄的嘗試明細未採集(該欄位上線前的舊資料,或請求在轉發前就失敗)", "这条请求没有保存这一段报文": "這條請求沒有儲存這一段報文", "进入「{phase}」阶段已持续 {time}": "進入「{phase}」階段已持續 {time}", "进行中": "進行中", "进行中…": "進行中…", "连接中": "連線中", "重试 {n}": "重試 {n}", "重试中": "重試中", "重试次数": "重試次數", "预览对话": "預覽對話", "预览读取失败": "預覽讀取失敗", "预览读取失败:{error}": "預覽讀取失敗:{error}", "首响 {time}": "首響 {time}", "首响:上游首帧到达的耗时": "首響:上游首幀到達的耗時", "首响:上游首帧到达的耗时(请求仍在转发中)": "首響:上游首幀到達的耗時(請求仍在轉發中)", "首帧已到,上游内容正在下发": "首幀已到,上游內容正在下發", "(请求仍在转发中)": "(請求仍在轉發中)", "(账号:{account})": "(帳號:{account})", ",{n}毫秒后重试": ",{n}毫秒後重試", ",{n}秒后重试": ",{n}秒後重試" }); +window.wbI18nDict = window.wbI18nDict || {}; Object.assign(window.wbI18nDict, { " · {n} 进行中": " · {n} 進行中", " · 会话粘住": " · 會話粘住", "
    无响应原文
    ": "
    無響應原文
    ", "
    无法解析响应内容
    ": "
    無法解析響應內容
    ", "
    无法解析请求体(不是含 messages 数组的 JSON)
    ": "
    無法解析請求體(不是含 messages 陣列的 JSON)
    ", "
    无请求原文
    ": "
    無請求原文
    ", "
    调用工具 {name}
    ": "
    呼叫工具 {name}
    ", "响应原文": "響應原文", "思考过程": "思考過程", "请求原文": "請求原文", "HTTP {status},但响应体阶段出错": "HTTP {status},但響應體階段出錯", "HTTP {status},但响应体阶段出错:见错误列": "HTTP {status},但響應體階段出錯:見錯誤列", "{duration} / 首帧 {first}": "{duration} / 首幀 {first}", "{m}分{s}秒": "{m}分{s}秒", "{n}秒": "{n}秒", "{s}秒{ms}ms": "{s}秒{ms}ms", "↻ {n} 次": "↻ {n} 次", "↻ 重试 {n} 次": "↻ 重試 {n} 次", "✅ 压缩完成": "✅ 壓縮完成", "「仅看进行中」开启时,状态固定为进行中": "「僅看進行中」開啟時,狀態固定為進行中", "上游模型繁忙,请求已排进上游队列;网关正按上游建议的时长等待后重发(不是登录态或额度问题)": "上游模型繁忙,請求已排進上游佇列;閘道器正按上游建議的時長等待後重發(不是登入態或額度問題)", "上游状态码: {status}": "上游狀態碼: {status}", "上游请求已发出,正在等第一个字节到达(模型的思考时间也在这段)": "上游請求已發出,正在等第一個位元組到達(模型的思考時間也在這段)", "上游返回错误": "上游返回錯誤", "下游请求的模型:{model}": "下游請求的模型:{model}", "仅清空报文原文": "僅清空報文原文", "仅看进行中": "僅看進行中", "令牌": "權杖", "体积": "體積", "保留统计行与报表,只抹掉请求 / 响应正文。清理后详情弹窗的「预览对话」将无原文可看,统计数字分毫不动。": "保留統計行與報表,只抹掉請求 / 響應正文。清理後詳情彈窗的「預覽對話」將無原文可看,統計數字分毫不動。", "全部删除": "全部刪除", "全部提供商": "全部提供商", "全部模型": "全部模型", "全部状态": "全部狀態", "共 {n} 次尝试": "共 {n} 次嘗試", "共 {n} 次尝试,最终由 {provider} 承载": "共 {n} 次嘗試,最終由 {provider} 承載", "其它": "其它", "内容按原样保存,请求头中的凭据字段已替换为 [redacted]": "內容按原樣儲存,請求頭中的憑據欄位已替換為 [redacted]", "内部重试": "內部重試", "切换路径:": "切換路徑:", "列表数据已刷新,请重试(关闭后重新打开这条详情)": "列表資料已重新整理,請重試(關閉後重新開啟這條詳情)", "删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。": "刪除日誌記錄本身。當前有篩選時只刪命中的條目,按天聚合報表不受影響;無篩選時清空全部並重置報表。此方式無法恢復。", "压缩中…": "壓縮中…", "压缩失败:{message}": "壓縮失敗:{message}", "压缩已在后台开始,完成后会自动提示": "壓縮已在後臺開始,完成後會自動提示", "压缩已在进行中": "壓縮已在進行中", "压缩已在进行中,接上它的进度等待完成": "壓縮已在進行中,接上它的進度等待完成", "压缩数据库": "壓縮資料庫", "压缩正在后台执行,完成后自动恢复": "壓縮正在後臺執行,完成後自動恢復", "原始报文": "原始報文", "另有 {missing} 次尝试未记录明细(只保留最早的 {kept} 条)": "另有 {missing} 次嘗試未記錄明細(只保留最早的 {kept} 條)", "另有 {n} 个词命中": "另有 {n} 個詞命中", "只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)": "只顯示正在轉發中的請求(狀態列按階段顯示:連線中 / 等待響應 / 響應中 / 重試中,用時列顯示已用時)", "只看失败": "只看失敗", "只看成功": "只看成功", "只看进行中": "只看進行中", "命中了敏感词": "命中了敏感詞", "命中的敏感词": "命中的敏感詞", "响应中": "響應中", "响应体": "響應體", "响应头": "響應頭", "回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)": "回收已刪除資料佔用的磁碟空間(checkpoint + VACUUM,後臺執行)", "在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文": "在設定 → 通用裡開啟「除錯模式」後,新發生的請求才會儲存報文", "失败请求不记录用量": "失敗請求不記錄用量", "失败({status}):{error}": "失敗({status}):{error}", "失败:{error}": "失敗:{error}", "实际下发给客户端 {bytes} 字节": "實際下發給客戶端 {bytes} 位元組", "实际发给上游 {bytes} 字节": "實際發給上游 {bytes} 位元組", "客户端": "客戶端", "将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}": "將刪除 {count} 條日誌 · 其中 {raw} 條仍帶報文原文 · 資料庫佔用 {db}", "尝试 {n} ·": "嘗試 {n} ·", "尝试明细": "嘗試明細", "工具结果": "工具結果", "工具结果 · {id}": "工具結果 · {id}", "工具结果图片": "工具結果圖片", "已下发": "已下發", "已删除 {count} 条": "已刪除 {count} 條", "已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」": "已受理終止:上游連線已斷開,列表稍後重新整理為「請求已被手動終止」", "已抹掉 {count} 条报文原文": "已抹掉 {count} 條報文原文", "已用时(请求仍在转发中)": "已用時(請求仍在轉發中)", "成功": "成功", "成功({status})": "成功({status})", "执行清理": "執行清理", "报文超过单条上限,请求体 / 响应体已按上限截断": "報文超過單條上限,請求體 / 響應體已按上限截斷", "抹掉原文": "抹掉原文", "按提供商筛选": "按提供商篩選", "按提供商筛选(只列日志里出现过的)": "按提供商篩選(只列日誌裡出現過的)", "按时间倒序,全量保留": "按時間倒序,全量保留", "按模型筛选": "按模型篩選", "按模型筛选(只列日志里出现过的;精确匹配模型名)": "按模型篩選(只列日誌裡出現過的;精確匹配模型名)", "按请求结果筛选": "按請求結果篩選", "排队中": "排隊中", "提供商 / 账号": "提供商 / 帳號", "提供商: {provider}": "提供商: {provider}", "提示": "提示", "敏": "敏", "敏感词": "敏感詞", "断开上游连接,明细记为「请求已被手动终止」": "斷開上游連線,明細記為「請求已被手動終止」", "无报文原文(未开启「调试模式」,或已被清理)": "無報文原文(未開啟「除錯模式」,或已被清理)", "无结果记录": "無結果記錄", "时间": "時間", "暂无请求日志,网关还没有转发过请求": "暫無請求日誌,閘道器還沒有轉發過請求", "最终承载:{picked}": "最終承載:{picked}", "本轮尝试失败,正在退避等待或切换到下一个账号": "本輪嘗試失敗,正在退避等待或切換到下一個帳號", "来源客户端:{client}": "來源客戶端:{client}", "查看该请求的上游原始报文": "檢視該請求的上游原始報文", "模型": "模型", "模型测试": "模型測試", "模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计": "模型管理頁操作列那顆「測試」發起的請求:與真實請求同一條轉發鏈路,但不計入報表統計", "正在加载请求日志…": "正在載入請求日誌…", "正在统计…": "正在統計…", "正文超过单条上限,可能已被截断": "正文超過單條上限,可能已被截斷", "没有符合筛选条件的请求": "沒有符合篩選條件的請求", "清理": "清理", "清理中…": "清理中…", "清理失败:{message}": "清理失敗:{message}", "清理方式": "清理方式", "清理请求日志": "清理請求日誌", "生成速度:输出 token ÷ 流式段用时(总用时 − 首响);流式段不足总用时一半时按总用时计": "生成速度:輸出 token ÷ 流式段用時(總用時 − 首響);流式段不足總用時一半時按總用時計", "用时": "用時", "用量": "用量", "确定对全部请求日志执行「{label}」?将处理 {count} 条,此操作无法恢复。": "確定對全部請求日誌執行「{label}」?將處理 {count} 條,此操作無法恢復。", "确定按当前筛选执行「{label}」?将处理 {count} 条,此操作无法恢复。": "確定按當前篩選執行「{label}」?將處理 {count} 條,此操作無法恢復。", "确定终止这条正在转发的请求?
    上游连接会立即断开,明细将记为「请求已被手动终止」。": "確定終止這條正在轉發的請求?
    上游連線會立即斷開,明細將記為「請求已被手動終止」。", "等待响应": "等待響應", "策略 {strategy} · 会话粘性 {sticky}{hit}": "策略 {strategy} · 會話粘性 {sticky}{hit}", "终止": "終止", "终止失败:{error}": "終止失敗:{error}", "终止请求": "終止請求", "结果": "結果", "缓存读取: - / 命中率: -": "快取讀取: - / 命中率: -", "缓存读取: {cache} / 命中率: {rate}": "快取讀取: {cache} / 命中率: {rate}", "耗时": "耗時", "被跳过:": "被跳過:", "识别到 User-Agent,但不属于已知的客户端类型:{ua}": "識別到 User-Agent,但不屬於已知的客戶端型別:{ua}", "详情": "詳情", "详情内容": "詳情內容", "请求体": "請求體", "请求图片": "請求圖片", "请求在任何账号接手之前就失败了": "請求在任何帳號接手之前就失敗了", "请求头": "請求頭", "请求已受理,正在选路、取凭证、建立上游连接": "請求已受理,正在選路、取憑證、建立上游連線", "请求日志列表": "請求日誌列表", "请求日志时间范围": "請求日誌時間範圍", "请求日志表": "請求日誌表", "请求正在转发中,用时列显示的是已用时": "請求正在轉發中,用時列顯示的是已用時", "请求详情": "請求詳情", "读取请求日志失败,详见控制台": "讀取請求日誌失敗,詳見控制台", "路由回放": "路由回放", "转发到上游的模型:{model}": "轉發到上游的模型:{model}", "轮次": "輪次", "输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}": "輸入 {input} · 輸出 {output} · 總計 {total} · 快取讀 {cache}", "这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计": "這條明細是一次模型測試(人工發起):走的是真實轉發鏈路,但不計入報表統計", "这条明细没有记录提供商(旧数据或请求未走到转发)": "這條明細沒有記錄提供商(舊資料或請求未走到轉發)", "这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)": "這條明細沒有記錄來源客戶端(舊資料、模型測試,或請求未帶 User-Agent)", "这条记录命中了敏感词,但没有留下命中明细": "這條記錄命中了敏感詞,但沒有留下命中明細", "这条记录的尝试明细未采集(该字段上线前的旧数据,或请求在转发前就失败)": "這條記錄的嘗試明細未採集(該欄位上線前的舊資料,或請求在轉發前就失敗)", "这条请求没有保存这一段报文": "這條請求沒有儲存這一段報文", "进入「{phase}」阶段已持续 {time}": "進入「{phase}」階段已持續 {time}", "进行中": "進行中", "进行中…": "進行中…", "连接中": "連線中", "重试 {n}": "重試 {n}", "重试中": "重試中", "重试次数": "重試次數", "预览对话": "預覽對話", "预览读取失败": "預覽讀取失敗", "预览读取失败:{error}": "預覽讀取失敗:{error}", "首响 {time}": "首響 {time}", "首响:上游首帧到达的耗时": "首響:上游首幀到達的耗時", "首响:上游首帧到达的耗时(请求仍在转发中)": "首響:上游首幀到達的耗時(請求仍在轉發中)", "首帧已到,上游内容正在下发": "首幀已到,上游內容正在下發", "(请求仍在转发中)": "(請求仍在轉發中)", "(账号:{account})": "(帳號:{account})", ",{n}毫秒后重试": ",{n}毫秒後重試", ",{n}秒后重试": ",{n}秒後重試" }); diff --git a/desktop-tauri/ui/islands/ui.js b/desktop-tauri/ui/islands/ui.js index a19f297d..9495e015 100644 --- a/desktop-tauri/ui/islands/ui.js +++ b/desktop-tauri/ui/islands/ui.js @@ -1,29 +1,29 @@ -var lre=Object.defineProperty;var ore=(Si,xl,so)=>xl in Si?lre(Si,xl,{enumerable:!0,configurable:!0,writable:!0,value:so}):Si[xl]=so;var cn=(Si,xl,so)=>ore(Si,typeof xl!="symbol"?xl+"":xl,so);(function(){"use strict";function Si(e,t){for(var a=0;ao[c]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}function xl(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var so={exports:{}},Yd={};var gC;function Z5(){if(gC)return Yd;gC=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.fragment");function a(o,c,d){var f=null;if(d!==void 0&&(f=""+d),c.key!==void 0&&(f=""+c.key),"key"in c){d={};for(var p in c)p!=="key"&&(d[p]=c[p])}else d=c;return c=d.ref,{$$typeof:e,type:o,key:f,ref:c!==void 0?c:null,props:d}}return Yd.Fragment=t,Yd.jsx=a,Yd.jsxs=a,Yd}var xC;function W5(){return xC||(xC=1,so.exports=Z5()),so.exports}var s=W5(),Cv={exports:{}},ct={};var bC;function J5(){if(bC)return ct;bC=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.portal"),a=Symbol.for("react.fragment"),o=Symbol.for("react.strict_mode"),c=Symbol.for("react.profiler"),d=Symbol.for("react.consumer"),f=Symbol.for("react.context"),p=Symbol.for("react.forward_ref"),h=Symbol.for("react.suspense"),b=Symbol.for("react.memo"),y=Symbol.for("react.lazy"),v=Symbol.for("react.activity"),j=Symbol.for("react.view_transition"),w=Symbol.iterator;function S(M){return M===null||typeof M!="object"?null:(M=w&&M[w]||M["@@iterator"],typeof M=="function"?M:null)}var N={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},k=Object.assign,A={};function P(M,U,Z){this.props=M,this.context=U,this.refs=A,this.updater=Z||N}P.prototype.isReactComponent={},P.prototype.setState=function(M,U){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,U,"setState")},P.prototype.forceUpdate=function(M){this.updater.enqueueForceUpdate(this,M,"forceUpdate")};function R(){}R.prototype=P.prototype;function C(M,U,Z){this.props=M,this.context=U,this.refs=A,this.updater=Z||N}var E=C.prototype=new R;E.constructor=C,k(E,P.prototype),E.isPureReactComponent=!0;var D=Array.isArray;function I(){}var O={H:null,A:null,T:null,S:null},_=Object.prototype.hasOwnProperty;function z(M,U,Z){var ae=Z.ref;return{$$typeof:e,type:M,key:U,ref:ae!==void 0?ae:null,props:Z}}function H(M,U){return z(M.type,U,M.props)}function B(M){return typeof M=="object"&&M!==null&&M.$$typeof===e}function L(M){var U={"=":"=0",":":"=2"};return"$"+M.replace(/[=:]/g,function(Z){return U[Z]})}var F=/\/+/g;function $(M,U){return typeof M=="object"&&M!==null&&M.key!=null?L(""+M.key):U.toString(36)}function X(M){switch(M.status){case"fulfilled":return M.value;case"rejected":throw M.reason;default:switch(typeof M.status=="string"?M.then(I,I):(M.status="pending",M.then(function(U){M.status==="pending"&&(M.status="fulfilled",M.value=U)},function(U){M.status==="pending"&&(M.status="rejected",M.reason=U)})),M.status){case"fulfilled":return M.value;case"rejected":throw M.reason}}throw M}function K(M,U,Z,ae,ce){var W=typeof M;(W==="undefined"||W==="boolean")&&(M=null);var ue=!1;if(M===null)ue=!0;else switch(W){case"bigint":case"string":case"number":ue=!0;break;case"object":switch(M.$$typeof){case e:case t:ue=!0;break;case y:return ue=M._init,K(ue(M._payload),U,Z,ae,ce)}}if(ue)return ce=ce(M),ue=ae===""?"."+$(M,0):ae,D(ce)?(Z="",ue!=null&&(Z=ue.replace(F,"$&/")+"/"),K(ce,U,Z,"",function(he){return he})):ce!=null&&(B(ce)&&(ce=H(ce,Z+(ce.key==null||M&&M.key===ce.key?"":(""+ce.key).replace(F,"$&/")+"/")+ue)),U.push(ce)),1;ue=0;var ve=ae===""?".":ae+":";if(D(M))for(var ee=0;ee"u"?!1:e instanceof ShadowRoot||e instanceof Cn(e).ShadowRoot}function Qd(e){const{overflow:t,overflowX:a,overflowY:o,display:c}=yr(e);return/auto|scroll|overlay|hidden|clip/.test(t+o+a)&&c!=="inline"&&c!=="contents"}function tz(e){return/^(table|td|th)$/.test(Fs(e))}function Hm(e){try{if(e.matches(":popover-open"))return!0}catch{}try{return e.matches(":modal")}catch{return!1}}const nz=/transform|translate|scale|rotate|perspective|filter/,sz=/paint|layout|strict|content/,Ni=e=>!!e&&e!=="none";let Ev;function Av(e){const t=Rt(e)?yr(e):e;return Ni(t.transform)||Ni(t.translate)||Ni(t.scale)||Ni(t.rotate)||Ni(t.perspective)||!Rv()&&(Ni(t.backdropFilter)||Ni(t.filter))||nz.test(t.willChange||"")||sz.test(t.contain||"")}function rz(e){let t=ao(e);for(;pn(t)&&!ro(t);){if(Av(t))return t;if(Hm(t))return null;t=ao(t)}return null}function Rv(){return Ev==null&&(Ev=typeof CSS<"u"&&CSS.supports&&CSS.supports("-webkit-backdrop-filter","none")),Ev}function ro(e){return/^(html|body|#document)$/.test(Fs(e))}function yr(e){return Cn(e).getComputedStyle(e)}function Um(e){return Rt(e)?{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}:{scrollLeft:e.scrollX,scrollTop:e.scrollY}}function ao(e){if(Fs(e)==="html")return e;const t=e.assignedSlot||e.parentNode||Gc(e)&&e.host||bl(e);return Gc(t)?t.host:t}function yC(e){const t=ao(e);return ro(t)?(e.ownerDocument||e).body:pn(t)&&Qd(t)?t:yC(t)}function Kc(e,t,a){var o;t===void 0&&(t=[]),a===void 0&&(a=!0);const c=yC(e),d=c===((o=e.ownerDocument)==null?void 0:o.body),f=Cn(c);if(d){const p=Ov(f);return t.concat(f,f.visualViewport||[],Qd(c)?c:[],p&&a?Kc(p):[])}else return t.concat(c,Kc(c,[],a))}function Ov(e){return e.parent&&Object.getPrototypeOf(e.parent)?e.frameElement:null}const Mv={...ez},jC={};function Ps(e,t){const a=x.useRef(jC);return a.current===jC&&(a.current=e(t)),a}const Dv=Mv.useInsertionEffect,az=Dv&&Dv!==Mv.useLayoutEffect?Dv:e=>e();function Ie(e){const t=Ps(lz).current;return t.next=e,az(t.effect),t.trampoline}function lz(){const e={next:void 0,callback:oz,trampoline:(...t)=>e.callback?.(...t),effect:()=>{e.callback=e.next}};return e}function oz(){}const Me=typeof document<"u"?x.useLayoutEffect:()=>{};function Iv(e,t){if(e&&!t)return e;if(!e&&t)return t;if(e||t)return{...e,...t}}const Pv={};function ta(e,t,a,o,c){if(!a&&!o&&!c&&!e)return Vm(t);let d=Vm(e);return t&&(d=Zd(d,t)),a&&(d=Zd(d,a)),o&&(d=Zd(d,o)),c&&(d=Zd(d,c)),d}function iz(e){if(e.length===0)return Pv;if(e.length===1)return Vm(e[0]);let t=Vm(e[0]);for(let a=1;a=65&&c<=90&&(typeof t=="function"||typeof t>"u")}function _v(e){return typeof e=="function"}function SC(e,t){return _v(e)?e(t):e??Pv}function dz(e,t){return t?e?(...a)=>{const o=a[0];if(CC(o)){const d=o;Fm(d);const f=t(...a);return d.baseUIHandlerPrevented||e?.(...a),f}const c=t(...a);return e?.(...a),c}:NC(t):e}function NC(e){return e&&((...t)=>{const a=t[0];return CC(a)&&Fm(a),e(...t)})}function Fm(e){return e.preventBaseUIHandler=()=>{e.baseUIHandlerPrevented=!0},e}function kC(e,t){return t?e?t+" "+e:t:e}function CC(e){return e!=null&&typeof e=="object"&&"nativeEvent"in e}function fz(e,t){return function(o,...c){const d=new URL(e);return d.searchParams.set("code",o.toString()),c.forEach(f=>d.searchParams.append("args[]",f)),`${t} error #${o}; visit ${d} for the full message.`}}const un=fz("https://base-ui.com/production-error","Base UI"),TC=x.createContext(void 0);function EC(e=!1){const t=x.useContext(TC);if(t===void 0&&!e)throw new Error(un(16));return t}function pz(e){const{focusableWhenDisabled:t,disabled:a,composite:o=!1,tabIndex:c=0,isNativeButton:d}=e,f=o&&t!==!1,p=o&&t===!1;return{props:x.useMemo(()=>{const b={onKeyDown(y){a&&t&&y.key!=="Tab"&&y.preventDefault()}};return o||(b.tabIndex=c,!d&&a&&(b.tabIndex=t?c:-1)),(d&&(t||f)||!d&&a)&&(b["aria-disabled"]=a),d&&(!t||p)&&(b.disabled=a),b},[o,a,t,f,p,d,c])}}function Gt(e){return e?.ownerDocument||document}function Yc(e,t,{detail:a=0}={}){e.dispatchEvent(new(Cn(e)).PointerEvent("click",{bubbles:!0,cancelable:!0,composed:!0,detail:a,shiftKey:t.shiftKey,ctrlKey:t.ctrlKey,altKey:t.altKey,metaKey:t.metaKey}))}function na(e={}){const{disabled:t=!1,focusableWhenDisabled:a,tabIndex:o=0,native:c=!0,composite:d}=e,f=x.useRef(null),p=EC(!0),h=d??p!==void 0,{props:b}=pz({focusableWhenDisabled:a,disabled:t,composite:h,tabIndex:o,isNativeButton:c}),y=x.useCallback(()=>{const w=f.current;Lv(w)&&h&&t&&b.disabled===void 0&&w.disabled&&(w.disabled=!1)},[t,b.disabled,h]);Me(y,[y]);const v=x.useCallback((w={})=>{const{onClick:S,onMouseDown:N,onKeyUp:k,onKeyDown:A,onPointerDown:P,...R}=w;return ta({onClick(C){if(t){C.preventDefault();return}S?.(C)},onMouseDown(C){t||N?.(C)},onKeyDown(C){if(t||(Fm(C),A?.(C),C.baseUIHandlerPrevented))return;const E=C.target===C.currentTarget,D=C.currentTarget,I=Lv(D),O=!c&&mz(D),_=E&&(c?I:!O),z=C.key==="Enter",H=C.key===" ",B=D.getAttribute("role"),L=B?.startsWith("menuitem")||B==="option"||B==="gridcell";if(E&&h&&H){if(C.defaultPrevented&&L)return;C.preventDefault(),(!c||I)&&(C.preventBaseUIHandler(),Yc(D,C));return}if(!_||c||!H&&!z){E&&O&&H&&C.preventDefault();return}C.defaultPrevented||(C.preventDefault(),z&&(C.preventBaseUIHandler(),Yc(D,C)))},onKeyUp(C){if(!t){if(Fm(C),k?.(C),C.target===C.currentTarget&&c&&h&&Lv(C.currentTarget)&&C.key===" "){C.preventDefault();return}C.baseUIHandlerPrevented||C.target===C.currentTarget&&!c&&!h&&!C.defaultPrevented&&C.key===" "&&(C.preventBaseUIHandler(),Yc(C.currentTarget,C))}},onPointerDown(C){if(t){C.preventDefault();return}P?.(C)}},c?{type:"button"}:{role:"button"},b,R)},[t,b,h,c]),j=Ie(w=>{f.current=w,y()});return{getButtonProps:v,buttonRef:j}}function Lv(e){return pn(e)&&e.tagName==="BUTTON"}function mz(e){return pn(e)&&e.tagName==="A"&&!!e.href}function sa(e,t,a,o){const c=Ps(AC).current;return gz(c,e,t,a,o)&&RC(c,[e,t,a,o]),c.callback}function hz(e){const t=Ps(AC).current;return xz(t,e)&&RC(t,e),t.callback}function AC(){return{callback:null,cleanup:null,refs:[]}}function gz(e,t,a,o,c){return e.refs[0]!==t||e.refs[1]!==a||e.refs[2]!==o||e.refs[3]!==c}function xz(e,t){return e.refs.length!==t.length||e.refs.some((a,o)=>a!==t[o])}function RC(e,t){if(e.refs=t,t.every(a=>a==null)){e.callback=null;return}e.callback=a=>{if(e.cleanup&&(e.cleanup(),e.cleanup=null),a!=null){const o=Array(t.length).fill(null);for(let c=0;c{for(let c=0;c=e}function OC(e){if(!x.isValidElement(e))return null;const t=e,a=t.props;return(zv(19)?a?.ref:t.ref)??null}function Kt(){}const Ir=Object.freeze([]),mn=Object.freeze({});function vz(e,t){const a={};for(const o in e){const c=e[o];if(t?.hasOwnProperty(o)){const d=t[o](c);d!=null&&Object.assign(a,d);continue}c===!0?a[`data-${o.toLowerCase()}`]="":c&&(a[`data-${o.toLowerCase()}`]=c.toString())}return a}function yz(e,t){return typeof e=="function"?e(t):e}function jz(e,t){return typeof e=="function"?e(t):e}function yt(e,t,a={}){let o=t.render;a.enabled!==!1&&(o=kz(o));const c=wz(t,a,o);if(a.enabled===!1)return null;const d=a.state??mn;return Cz(e,o,c,d)}function wz(e,t,a){const{className:o,style:c}=e,{state:d=mn,ref:f,props:p,stateAttributesMapping:h,enabled:b=!0}=t,y=b?yz(o,d):void 0,v=b?jz(c,d):void 0,j=b?vz(d,h):mn,w=b&&p?Sz(p):void 0,S=b?Iv(j,w)??{}:mn;return typeof document<"u"&&(b?Array.isArray(f)?S.ref=hz([S.ref,OC(a),...f]):S.ref=sa(S.ref,OC(a),f):sa(null,null)),b?(y!==void 0&&(S.className=kC(S.className,y)),v!==void 0&&(S.style=Iv(S.style,v)),S):mn}function Sz(e){return Array.isArray(e)?iz(e):ta(void 0,e)}const Nz=Symbol.for("react.lazy");function kz(e){if(e?.$$typeof!==Nz)return e;const t=x.Children.toArray(e)[0];return x.isValidElement(t)?t:e}function Cz(e,t,a,o){if(t){if(typeof t=="function")return t(a,o);const c=ta(a,t.props);return c.ref=a.ref,x.cloneElement(t,c)}if(e&&typeof e=="string")return Tz(e,a);throw new Error(un(8))}function Tz(e,t){return e==="button"?x.createElement("button",{type:"button",...t,key:t.key}):e==="img"?x.createElement("img",{alt:"",...t,key:t.key}):x.createElement(e,t)}const Ez=x.forwardRef(function(t,a){const{render:o,className:c,disabled:d=!1,focusableWhenDisabled:f=!1,nativeButton:p=!0,style:h,...b}=t,{getButtonProps:y,buttonRef:v}=na({disabled:d,focusableWhenDisabled:f,native:p});return yt("button",t,{state:{disabled:d},ref:[a,v],props:[b,y]})});function MC(e){var t,a,o="";if(typeof e=="string"||typeof e=="number")o+=e;else if(typeof e=="object")if(Array.isArray(e)){var c=e.length;for(t=0;ttypeof e=="boolean"?`${e}`:e===0?"0":e,PC=DC,Xc=(e,t)=>a=>{var o;if(t?.variants==null)return PC(e,a?.class,a?.className);const{variants:c,defaultVariants:d}=t,f=Object.keys(c).map(b=>{const y=a?.[b],v=d?.[b];if(y===null)return null;const j=IC(y)||IC(v);return c[b][j]}),p=a&&Object.entries(a).reduce((b,y)=>{let[v,j]=y;return j===void 0||(b[v]=j),b},{}),h=t==null||(o=t.compoundVariants)===null||o===void 0?void 0:o.reduce((b,y)=>{let{class:v,className:j,...w}=y;return Object.entries(w).every(S=>{let[N,k]=S;return Array.isArray(k)?k.includes({...d,...p}[N]):{...d,...p}[N]===k})?[...b,v,j]:b},[]);return PC(e,f,h,a?.class,a?.className)},Az=(e,t)=>{const a=new Array(e.length+t.length);for(let o=0;o({classGroupId:e,validator:t}),_C=(e=new Map,t=null,a)=>({nextPart:e,validators:t,classGroupId:a}),$m="-",LC=[],Oz="arbitrary..",Mz=e=>{const t=Iz(e),{conflictingClassGroups:a,conflictingClassGroupModifiers:o}=e;return{getClassGroupId:f=>{if(f.startsWith("[")&&f.endsWith("]"))return Dz(f);const p=f.split($m),h=p[0]===""&&p.length>1?1:0;return zC(p,h,t)},getConflictingClassGroupIds:(f,p)=>{if(p){const h=o[f],b=a[f];return h?b?Az(b,h):h:b||LC}return a[f]||LC}}},zC=(e,t,a)=>{if(e.length-t===0)return a.classGroupId;const c=e[t],d=a.nextPart.get(c);if(d){const b=zC(e,t+1,d);if(b)return b}const f=a.validators;if(f===null)return;const p=t===0?e.join($m):e.slice(t).join($m),h=f.length;for(let b=0;be.slice(1,-1).indexOf(":")===-1?void 0:(()=>{const t=e.slice(1,-1),a=t.indexOf(":"),o=t.slice(0,a);return o?Oz+o:void 0})(),Iz=e=>{const{theme:t,classGroups:a}=e;return Pz(a,t)},Pz=(e,t)=>{const a=_C();for(const o in e){const c=e[o];Bv(c,a,o,t)}return a},Bv=(e,t,a,o)=>{const c=e.length;for(let d=0;d{if(typeof e=="string"){Lz(e,t,a);return}if(typeof e=="function"){zz(e,t,a,o);return}Bz(e,t,a,o)},Lz=(e,t,a)=>{const o=e===""?t:BC(t,e);o.classGroupId=a},zz=(e,t,a,o)=>{if(Hz(e)){Bv(e(o),t,a,o);return}t.validators===null&&(t.validators=[]),t.validators.push(Rz(a,e))},Bz=(e,t,a,o)=>{const c=Object.entries(e),d=c.length;for(let f=0;f{let a=e;const o=t.split($m),c=o.length;for(let d=0;d"isThemeGetter"in e&&e.isThemeGetter===!0,Uz=e=>{if(e<1)return{get:()=>{},set:()=>{}};let t=0,a=Object.create(null),o=Object.create(null);const c=(d,f)=>{a[d]=f,t++,t>e&&(t=0,o=a,a=Object.create(null))};return{get(d){let f=a[d];if(f!==void 0)return f;if((f=o[d])!==void 0)return c(d,f),f},set(d,f){d in a?a[d]=f:c(d,f)}}},Hv="!",HC=":",Vz=[],UC=(e,t,a,o,c)=>({modifiers:e,hasImportantModifier:t,baseClassName:a,maybePostfixModifierPosition:o,isExternal:c}),Fz=e=>{const{prefix:t,experimentalParseClassName:a}=e;let o=c=>{const d=[];let f=0,p=0,h=0,b;const y=c.length;for(let N=0;Nh?b-h:void 0;return UC(d,w,j,S)};if(t){const c=t+HC,d=o;o=f=>f.startsWith(c)?d(f.slice(c.length)):UC(Vz,!1,f,void 0,!0)}if(a){const c=o;o=d=>a({className:d,parseClassName:c})}return o},$z=e=>{const t=new Map;return e.orderSensitiveModifiers.forEach((a,o)=>{t.set(a,1e6+o)}),a=>{const o=[];let c=[];for(let d=0;d0&&(c.sort(),o.push(...c),c=[]),o.push(f)):c.push(f)}return c.length>0&&(c.sort(),o.push(...c)),o}},qz=e=>({cache:Uz(e.cacheSize),parseClassName:Fz(e),sortModifiers:$z(e),postfixLookupClassGroupIds:Gz(e),...Mz(e)}),Gz=e=>{const t=Object.create(null),a=e.postfixLookupClassGroups;if(a)for(let o=0;o{const{parseClassName:a,getClassGroupId:o,getConflictingClassGroupIds:c,sortModifiers:d,postfixLookupClassGroupIds:f}=t,p=[],h=e.trim().split(Kz);let b="";for(let y=h.length-1;y>=0;y-=1){const v=h[y],{isExternal:j,modifiers:w,hasImportantModifier:S,baseClassName:N,maybePostfixModifierPosition:k}=a(v);if(j){b=v+(b.length>0?" "+b:b);continue}let A=!!k,P;if(A){const I=N.substring(0,k);P=o(I);const O=P&&f[P]?o(N):void 0;O&&O!==P&&(P=O,A=!1)}else P=o(N);if(!P){if(!A){b=v+(b.length>0?" "+b:b);continue}if(P=o(N),!P){b=v+(b.length>0?" "+b:b);continue}A=!1}const R=w.length===0?"":w.length===1?w[0]:d(w).join(":"),C=S?R+Hv:R,E=C+P;if(p.indexOf(E)>-1)continue;p.push(E);const D=c(P,A);for(let I=0;I0?" "+b:b)}return b},Xz=(...e)=>{let t=0,a,o,c="";for(;t{if(typeof e=="string")return e;let t,a="";for(let o=0;o{let a,o,c,d;const f=h=>{const b=t.reduce((y,v)=>v(y),e());return a=qz(b),o=a.cache.get,c=a.cache.set,d=p,p(h)},p=h=>{const b=o(h);if(b)return b;const y=Yz(h,a);return c(h,y),y};return d=f,(...h)=>d(Xz(...h))},Zz=[],ks=e=>{const t=a=>a[e]||Zz;return t.isThemeGetter=!0,t.themeKey=e,t},FC=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,$C=/^\((?:(\w[\w-]*):)?(.+)\)$/i,Wz=/^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/,Jz=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,eB=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,tB=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix|color|light-dark)\(.+\)$/,nB=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,sB=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,lo=e=>Wz.test(e),Nt=e=>!!e&&!Number.isNaN(Number(e)),qa=e=>!!e&&Number.isInteger(Number(e)),Uv=e=>e.endsWith("%")&&Nt(e.slice(0,-1)),vl=e=>Jz.test(e),qC=()=>!0,rB=e=>eB.test(e)&&!tB.test(e),Vv=()=>!1,aB=e=>nB.test(e),lB=e=>sB.test(e),oB=e=>!Ye(e)&&!Qe(e),iB=e=>e.startsWith("@container")&&(e[10]==="/"&&e[11]!==void 0||e[11]==="s"&&e[16]!==void 0&&e.startsWith("-size/",10)||e[11]==="n"&&e[18]!==void 0&&e.startsWith("-normal/",10)),cB=e=>oo(e,ZC,Vv),Ye=e=>FC.test(e),ki=e=>oo(e,WC,rB),GC=e=>oo(e,xB,Nt),uB=e=>oo(e,e1,qC),dB=e=>oo(e,JC,Vv),KC=e=>oo(e,XC,Vv),fB=e=>oo(e,QC,lB),qm=e=>oo(e,t1,aB),Qe=e=>$C.test(e),Wd=e=>Ci(e,WC),pB=e=>Ci(e,JC),YC=e=>Ci(e,XC),mB=e=>Ci(e,ZC),hB=e=>Ci(e,QC),Gm=e=>Ci(e,t1,!0),gB=e=>Ci(e,e1,!0),oo=(e,t,a)=>{const o=FC.exec(e);return o?o[1]?t(o[1]):a(o[2]):!1},Ci=(e,t,a=!1)=>{const o=$C.exec(e);return o?o[1]?t(o[1]):a:!1},XC=e=>e==="position"||e==="percentage",QC=e=>e==="image"||e==="url",ZC=e=>e==="length"||e==="size"||e==="bg-size",WC=e=>e==="length",xB=e=>e==="number",JC=e=>e==="family-name",e1=e=>e==="number"||e==="weight",t1=e=>e==="shadow",bB=Qz(()=>{const e=ks("color"),t=ks("font"),a=ks("text"),o=ks("font-weight"),c=ks("tracking"),d=ks("leading"),f=ks("breakpoint"),p=ks("container"),h=ks("spacing"),b=ks("radius"),y=ks("shadow"),v=ks("inset-shadow"),j=ks("text-shadow"),w=ks("drop-shadow"),S=ks("blur"),N=ks("perspective"),k=ks("aspect"),A=ks("ease"),P=ks("animate"),R=()=>["auto","avoid","all","avoid-page","page","left","right","column"],C=()=>["center","top","bottom","left","right","top-left","left-top","top-right","right-top","bottom-right","right-bottom","bottom-left","left-bottom"],E=()=>[...C(),Qe,Ye],D=()=>["auto","hidden","clip","visible","scroll"],I=()=>["auto","contain","none"],O=()=>[Qe,Ye,h],_=()=>[lo,"full","auto",...O()],z=()=>[qa,"none","subgrid",Qe,Ye],H=()=>["auto",{span:["full",qa,Qe,Ye]},qa,Qe,Ye],B=()=>[qa,"auto",Qe,Ye],L=()=>["auto","min","max","fr",Qe,Ye],F=()=>["start","end","center","between","around","evenly","stretch","baseline","center-safe","end-safe"],$=()=>["start","end","center","stretch","center-safe","end-safe"],X=()=>["auto",...O()],K=()=>[lo,"auto","full","dvw","dvh","lvw","lvh","svw","svh","min","max","fit",...O()],q=()=>[p,lo,"screen","full","dvw","lvw","svw","min","max","fit",...O()],Q=()=>[lo,"screen","full","lh","dvh","lvh","svh","min","max","fit",...O()],G=()=>[e,Qe,Ye],J=()=>[...C(),YC,KC,{position:[Qe,Ye]}],Y=()=>["no-repeat",{repeat:["","x","y","space","round"]}],re=()=>["auto","cover","contain",mB,cB,{size:[Qe,Ye]}],M=()=>[Uv,Wd,ki],U=()=>["","none","full",b,Qe,Ye],Z=()=>["",Nt,Wd,ki],ae=()=>["solid","dashed","dotted","double"],ce=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],W=()=>[Nt,Uv,YC,KC],ue=()=>["","none",S,Qe,Ye],ve=()=>["none",Nt,Qe,Ye],ee=()=>["none",Nt,Qe,Ye],he=()=>[Nt,Qe,Ye],te=()=>[lo,"full",...O()];return{cacheSize:500,theme:{animate:["spin","ping","pulse","bounce"],aspect:["video"],blur:[vl],breakpoint:[vl],color:[qC],container:[vl],"drop-shadow":[vl],ease:["in","out","in-out"],font:[oB],"font-weight":["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"],"inset-shadow":[vl],leading:["none","tight","snug","normal","relaxed","loose"],perspective:["dramatic","near","normal","midrange","distant","none"],radius:[vl],shadow:[vl],spacing:["px",Nt],text:[vl],"text-shadow":[vl],tracking:["tighter","tight","normal","wide","wider","widest"]},classGroups:{aspect:[{aspect:["auto","square",lo,Ye,Qe,k]}],container:["container"],"container-type":[{"@container":["","normal","size",Qe,Ye]}],"container-named":[iB],columns:[{columns:[Nt,"auto",Ye,Qe,p]}],"break-after":[{"break-after":R()}],"break-before":[{"break-before":R()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],sr:["sr-only","not-sr-only"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:E()}],overflow:[{overflow:D()}],"overflow-x":[{"overflow-x":D()}],"overflow-y":[{"overflow-y":D()}],overscroll:[{overscroll:I()}],"overscroll-x":[{"overscroll-x":I()}],"overscroll-y":[{"overscroll-y":I()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:_()}],"inset-x":[{"inset-x":_()}],"inset-y":[{"inset-y":_()}],start:[{"inset-s":_(),start:_()}],end:[{"inset-e":_(),end:_()}],"inset-bs":[{"inset-bs":_()}],"inset-be":[{"inset-be":_()}],top:[{top:_()}],right:[{right:_()}],bottom:[{bottom:_()}],left:[{left:_()}],visibility:["visible","invisible","collapse"],z:[{z:[qa,"auto",Qe,Ye]}],basis:[{basis:[lo,"full","auto",p,...O()]}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["nowrap","wrap","wrap-reverse"]}],flex:[{flex:[Nt,lo,"auto","initial","none",Ye]}],grow:[{grow:["",Nt,Qe,Ye]}],shrink:[{shrink:["",Nt,Qe,Ye]}],order:[{order:[qa,"first","last","none",Qe,Ye]}],"grid-cols":[{"grid-cols":z()}],"col-start-end":[{col:H()}],"col-start":[{"col-start":B()}],"col-end":[{"col-end":B()}],"grid-rows":[{"grid-rows":z()}],"row-start-end":[{row:H()}],"row-start":[{"row-start":B()}],"row-end":[{"row-end":B()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":L()}],"auto-rows":[{"auto-rows":L()}],gap:[{gap:O()}],"gap-x":[{"gap-x":O()}],"gap-y":[{"gap-y":O()}],"justify-content":[{justify:[...F(),"normal"]}],"justify-items":[{"justify-items":[...$(),"normal"]}],"justify-self":[{"justify-self":["auto",...$()]}],"align-content":[{content:["normal",...F()]}],"align-items":[{items:[...$(),{baseline:["","last"]}]}],"align-self":[{self:["auto",...$(),{baseline:["","last"]}]}],"place-content":[{"place-content":F()}],"place-items":[{"place-items":[...$(),"baseline"]}],"place-self":[{"place-self":["auto",...$()]}],p:[{p:O()}],px:[{px:O()}],py:[{py:O()}],ps:[{ps:O()}],pe:[{pe:O()}],pbs:[{pbs:O()}],pbe:[{pbe:O()}],pt:[{pt:O()}],pr:[{pr:O()}],pb:[{pb:O()}],pl:[{pl:O()}],m:[{m:X()}],mx:[{mx:X()}],my:[{my:X()}],ms:[{ms:X()}],me:[{me:X()}],mbs:[{mbs:X()}],mbe:[{mbe:X()}],mt:[{mt:X()}],mr:[{mr:X()}],mb:[{mb:X()}],ml:[{ml:X()}],"space-x":[{"space-x":O()}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":O()}],"space-y-reverse":["space-y-reverse"],size:[{size:K()}],"inline-size":[{inline:["auto",...q()]}],"min-inline-size":[{"min-inline":["auto",...q()]}],"max-inline-size":[{"max-inline":["none",...q()]}],"block-size":[{block:["auto",...Q()]}],"min-block-size":[{"min-block":["auto",...Q()]}],"max-block-size":[{"max-block":["none",...Q()]}],w:[{w:[p,"screen",...K()]}],"min-w":[{"min-w":[p,"screen","none",...K()]}],"max-w":[{"max-w":[p,"screen","none","prose",{screen:[f]},...K()]}],h:[{h:["screen","lh",...K()]}],"min-h":[{"min-h":["screen","lh","none",...K()]}],"max-h":[{"max-h":["screen","lh","none",...K()]}],"font-size":[{text:["base",a,Wd,ki]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:[o,gB,uB]}],"font-stretch":[{"font-stretch":["ultra-condensed","extra-condensed","condensed","semi-condensed","normal","semi-expanded","expanded","extra-expanded","ultra-expanded",Uv,Ye]}],"font-family":[{font:[pB,dB,t]}],"font-features":[{"font-features":[Ye]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:[c,Qe,Ye]}],"line-clamp":[{"line-clamp":[Nt,"none",Qe,GC]}],leading:[{leading:["none",d,...O()]}],"list-image":[{"list-image":["none",Qe,Ye]}],"list-style-position":[{list:["inside","outside"]}],"list-style-type":[{list:["disc","decimal","none",Qe,Ye]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"placeholder-color":[{placeholder:G()}],"text-color":[{text:G()}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...ae(),"wavy"]}],"text-decoration-thickness":[{decoration:[Nt,"from-font","auto",Qe,ki]}],"text-decoration-color":[{decoration:G()}],"underline-offset":[{"underline-offset":[Nt,"auto",Qe,Ye]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:O()}],"tab-size":[{tab:[qa,Qe,Ye]}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",Qe,Ye]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],wrap:[{wrap:["break-word","anywhere","normal"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",Qe,Ye]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:J()}],"bg-repeat":[{bg:Y()}],"bg-size":[{bg:re()}],"bg-image":[{bg:["none",{linear:[{to:["t","tr","r","br","b","bl","l","tl"]},qa,Qe,Ye],radial:["",Qe,Ye],conic:["",qa,Qe,Ye]},hB,fB]}],"bg-color":[{bg:G()}],"gradient-from-pos":[{from:M()}],"gradient-via-pos":[{via:M()}],"gradient-to-pos":[{to:M()}],"gradient-from":[{from:G()}],"gradient-via":[{via:G()}],"gradient-to":[{to:G()}],rounded:[{rounded:U()}],"rounded-s":[{"rounded-s":U()}],"rounded-e":[{"rounded-e":U()}],"rounded-t":[{"rounded-t":U()}],"rounded-r":[{"rounded-r":U()}],"rounded-b":[{"rounded-b":U()}],"rounded-l":[{"rounded-l":U()}],"rounded-ss":[{"rounded-ss":U()}],"rounded-se":[{"rounded-se":U()}],"rounded-ee":[{"rounded-ee":U()}],"rounded-es":[{"rounded-es":U()}],"rounded-tl":[{"rounded-tl":U()}],"rounded-tr":[{"rounded-tr":U()}],"rounded-br":[{"rounded-br":U()}],"rounded-bl":[{"rounded-bl":U()}],"border-w":[{border:Z()}],"border-w-x":[{"border-x":Z()}],"border-w-y":[{"border-y":Z()}],"border-w-s":[{"border-s":Z()}],"border-w-e":[{"border-e":Z()}],"border-w-bs":[{"border-bs":Z()}],"border-w-be":[{"border-be":Z()}],"border-w-t":[{"border-t":Z()}],"border-w-r":[{"border-r":Z()}],"border-w-b":[{"border-b":Z()}],"border-w-l":[{"border-l":Z()}],"divide-x":[{"divide-x":Z()}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":Z()}],"divide-y-reverse":["divide-y-reverse"],"border-style":[{border:[...ae(),"hidden","none"]}],"divide-style":[{divide:[...ae(),"hidden","none"]}],"border-color":[{border:G()}],"border-color-x":[{"border-x":G()}],"border-color-y":[{"border-y":G()}],"border-color-s":[{"border-s":G()}],"border-color-e":[{"border-e":G()}],"border-color-bs":[{"border-bs":G()}],"border-color-be":[{"border-be":G()}],"border-color-t":[{"border-t":G()}],"border-color-r":[{"border-r":G()}],"border-color-b":[{"border-b":G()}],"border-color-l":[{"border-l":G()}],"divide-color":[{divide:G()}],"outline-style":[{outline:[...ae(),"none","hidden"]}],"outline-offset":[{"outline-offset":[Nt,Qe,Ye]}],"outline-w":[{outline:["",Nt,Wd,ki]}],"outline-color":[{outline:G()}],shadow:[{shadow:["","inner","none",y,Gm,qm]}],"shadow-color":[{shadow:G()}],"inset-shadow":[{"inset-shadow":["none",v,Gm,qm]}],"inset-shadow-color":[{"inset-shadow":G()}],"ring-w":[{ring:Z()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:G()}],"ring-offset-w":[{"ring-offset":[Nt,ki]}],"ring-offset-color":[{"ring-offset":G()}],"inset-ring-w":[{"inset-ring":Z()}],"inset-ring-color":[{"inset-ring":G()}],"text-shadow":[{"text-shadow":["none",j,Gm,qm]}],"text-shadow-color":[{"text-shadow":G()}],opacity:[{opacity:[Nt,Qe,Ye]}],"mix-blend":[{"mix-blend":[...ce(),"plus-darker","plus-lighter"]}],"bg-blend":[{"bg-blend":ce()}],"mask-clip":[{"mask-clip":["border","padding","content","fill","stroke","view"]},"mask-no-clip"],"mask-composite":[{mask:["add","subtract","intersect","exclude"]}],"mask-image-linear-pos":[{"mask-linear":[Nt]}],"mask-image-linear-from-pos":[{"mask-linear-from":W()}],"mask-image-linear-to-pos":[{"mask-linear-to":W()}],"mask-image-linear-from-color":[{"mask-linear-from":G()}],"mask-image-linear-to-color":[{"mask-linear-to":G()}],"mask-image-t-from-pos":[{"mask-t-from":W()}],"mask-image-t-to-pos":[{"mask-t-to":W()}],"mask-image-t-from-color":[{"mask-t-from":G()}],"mask-image-t-to-color":[{"mask-t-to":G()}],"mask-image-r-from-pos":[{"mask-r-from":W()}],"mask-image-r-to-pos":[{"mask-r-to":W()}],"mask-image-r-from-color":[{"mask-r-from":G()}],"mask-image-r-to-color":[{"mask-r-to":G()}],"mask-image-b-from-pos":[{"mask-b-from":W()}],"mask-image-b-to-pos":[{"mask-b-to":W()}],"mask-image-b-from-color":[{"mask-b-from":G()}],"mask-image-b-to-color":[{"mask-b-to":G()}],"mask-image-l-from-pos":[{"mask-l-from":W()}],"mask-image-l-to-pos":[{"mask-l-to":W()}],"mask-image-l-from-color":[{"mask-l-from":G()}],"mask-image-l-to-color":[{"mask-l-to":G()}],"mask-image-x-from-pos":[{"mask-x-from":W()}],"mask-image-x-to-pos":[{"mask-x-to":W()}],"mask-image-x-from-color":[{"mask-x-from":G()}],"mask-image-x-to-color":[{"mask-x-to":G()}],"mask-image-y-from-pos":[{"mask-y-from":W()}],"mask-image-y-to-pos":[{"mask-y-to":W()}],"mask-image-y-from-color":[{"mask-y-from":G()}],"mask-image-y-to-color":[{"mask-y-to":G()}],"mask-image-radial":[{"mask-radial":[Qe,Ye]}],"mask-image-radial-from-pos":[{"mask-radial-from":W()}],"mask-image-radial-to-pos":[{"mask-radial-to":W()}],"mask-image-radial-from-color":[{"mask-radial-from":G()}],"mask-image-radial-to-color":[{"mask-radial-to":G()}],"mask-image-radial-shape":[{"mask-radial":["circle","ellipse"]}],"mask-image-radial-size":[{"mask-radial":[{closest:["side","corner"],farthest:["side","corner"]}]}],"mask-image-radial-pos":[{"mask-radial-at":C()}],"mask-image-conic-pos":[{"mask-conic":[Nt]}],"mask-image-conic-from-pos":[{"mask-conic-from":W()}],"mask-image-conic-to-pos":[{"mask-conic-to":W()}],"mask-image-conic-from-color":[{"mask-conic-from":G()}],"mask-image-conic-to-color":[{"mask-conic-to":G()}],"mask-mode":[{mask:["alpha","luminance","match"]}],"mask-origin":[{"mask-origin":["border","padding","content","fill","stroke","view"]}],"mask-position":[{mask:J()}],"mask-repeat":[{mask:Y()}],"mask-size":[{mask:re()}],"mask-type":[{"mask-type":["alpha","luminance"]}],"mask-image":[{mask:["none",Qe,Ye]}],filter:[{filter:["","none",Qe,Ye]}],blur:[{blur:ue()}],brightness:[{brightness:[Nt,Qe,Ye]}],contrast:[{contrast:[Nt,Qe,Ye]}],"drop-shadow":[{"drop-shadow":["","none",w,Gm,qm]}],"drop-shadow-color":[{"drop-shadow":G()}],grayscale:[{grayscale:["",Nt,Qe,Ye]}],"hue-rotate":[{"hue-rotate":[Nt,Qe,Ye]}],invert:[{invert:["",Nt,Qe,Ye]}],saturate:[{saturate:[Nt,Qe,Ye]}],sepia:[{sepia:["",Nt,Qe,Ye]}],"backdrop-filter":[{"backdrop-filter":["","none",Qe,Ye]}],"backdrop-blur":[{"backdrop-blur":ue()}],"backdrop-brightness":[{"backdrop-brightness":[Nt,Qe,Ye]}],"backdrop-contrast":[{"backdrop-contrast":[Nt,Qe,Ye]}],"backdrop-grayscale":[{"backdrop-grayscale":["",Nt,Qe,Ye]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[Nt,Qe,Ye]}],"backdrop-invert":[{"backdrop-invert":["",Nt,Qe,Ye]}],"backdrop-opacity":[{"backdrop-opacity":[Nt,Qe,Ye]}],"backdrop-saturate":[{"backdrop-saturate":[Nt,Qe,Ye]}],"backdrop-sepia":[{"backdrop-sepia":["",Nt,Qe,Ye]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":O()}],"border-spacing-x":[{"border-spacing-x":O()}],"border-spacing-y":[{"border-spacing-y":O()}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["","all","colors","opacity","shadow","transform","none",Qe,Ye]}],"transition-behavior":[{transition:["normal","discrete"]}],duration:[{duration:[Nt,"initial",Qe,Ye]}],ease:[{ease:["linear","initial",A,Qe,Ye]}],delay:[{delay:[Nt,Qe,Ye]}],animate:[{animate:["none",P,Qe,Ye]}],backface:[{backface:["hidden","visible"]}],perspective:[{perspective:[N,Qe,Ye]}],"perspective-origin":[{"perspective-origin":E()}],rotate:[{rotate:ve()}],"rotate-x":[{"rotate-x":ve()}],"rotate-y":[{"rotate-y":ve()}],"rotate-z":[{"rotate-z":ve()}],scale:[{scale:ee()}],"scale-x":[{"scale-x":ee()}],"scale-y":[{"scale-y":ee()}],"scale-z":[{"scale-z":ee()}],"scale-3d":["scale-3d"],skew:[{skew:he()}],"skew-x":[{"skew-x":he()}],"skew-y":[{"skew-y":he()}],transform:[{transform:[Qe,Ye,"","none","gpu","cpu"]}],"transform-origin":[{origin:E()}],"transform-style":[{transform:["3d","flat"]}],translate:[{translate:te()}],"translate-x":[{"translate-x":te()}],"translate-y":[{"translate-y":te()}],"translate-z":[{"translate-z":te()}],"translate-none":["translate-none"],zoom:[{zoom:[qa,Qe,Ye]}],accent:[{accent:G()}],appearance:[{appearance:["none","auto"]}],"caret-color":[{caret:G()}],"color-scheme":[{scheme:["normal","dark","light","light-dark","only-dark","only-light"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",Qe,Ye]}],"field-sizing":[{"field-sizing":["fixed","content"]}],"pointer-events":[{"pointer-events":["auto","none"]}],resize:[{resize:["none","","y","x"]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scrollbar-thumb-color":[{"scrollbar-thumb":G()}],"scrollbar-track-color":[{"scrollbar-track":G()}],"scrollbar-gutter":[{"scrollbar-gutter":["auto","stable","both"]}],"scrollbar-w":[{scrollbar:["auto","thin","none"]}],"scroll-m":[{"scroll-m":O()}],"scroll-mx":[{"scroll-mx":O()}],"scroll-my":[{"scroll-my":O()}],"scroll-ms":[{"scroll-ms":O()}],"scroll-me":[{"scroll-me":O()}],"scroll-mbs":[{"scroll-mbs":O()}],"scroll-mbe":[{"scroll-mbe":O()}],"scroll-mt":[{"scroll-mt":O()}],"scroll-mr":[{"scroll-mr":O()}],"scroll-mb":[{"scroll-mb":O()}],"scroll-ml":[{"scroll-ml":O()}],"scroll-p":[{"scroll-p":O()}],"scroll-px":[{"scroll-px":O()}],"scroll-py":[{"scroll-py":O()}],"scroll-ps":[{"scroll-ps":O()}],"scroll-pe":[{"scroll-pe":O()}],"scroll-pbs":[{"scroll-pbs":O()}],"scroll-pbe":[{"scroll-pbe":O()}],"scroll-pt":[{"scroll-pt":O()}],"scroll-pr":[{"scroll-pr":O()}],"scroll-pb":[{"scroll-pb":O()}],"scroll-pl":[{"scroll-pl":O()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",Qe,Ye]}],fill:[{fill:["none",...G()]}],"stroke-w":[{stroke:[Nt,Wd,ki,GC]}],stroke:[{stroke:["none",...G()]}],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{"container-named":["container-type"],overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","inset-bs","inset-be","start","end","top","right","bottom","left"],"inset-x":["start","end","right","left"],"inset-y":["inset-bs","inset-be","top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pbs","pbe","pt","pr","pb","pl"],px:["ps","pe","pr","pl"],py:["pbs","pbe","pt","pb"],m:["mx","my","ms","me","mbs","mbe","mt","mr","mb","ml"],mx:["ms","me","mr","ml"],my:["mbs","mbe","mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-x","border-w-y","border-w-s","border-w-e","border-w-bs","border-w-be","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-s","border-w-e","border-w-r","border-w-l"],"border-w-y":["border-w-bs","border-w-be","border-w-t","border-w-b"],"border-color":["border-color-x","border-color-y","border-color-s","border-color-e","border-color-bs","border-color-be","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-s","border-color-e","border-color-r","border-color-l"],"border-color-y":["border-color-bs","border-color-be","border-color-t","border-color-b"],translate:["translate-x","translate-y","translate-none"],"translate-none":["translate","translate-x","translate-y","translate-z"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mbs","scroll-mbe","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-ms","scroll-me","scroll-mr","scroll-ml"],"scroll-my":["scroll-mbs","scroll-mbe","scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pbs","scroll-pbe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-ps","scroll-pe","scroll-pr","scroll-pl"],"scroll-py":["scroll-pbs","scroll-pbe","scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]},postfixLookupClassGroups:["container-type"],orderSensitiveModifiers:["*","**","after","backdrop","before","details-content","file","first-letter","first-line","marker","placeholder","selection"]}});function Ue(...e){return bB(DC(e))}const vB=Xc(["group/button inline-flex shrink-0 items-center justify-center gap-1.5 whitespace-nowrap border","font-medium transition-[background-color,border-color,color,transform,box-shadow] duration-150 ease-out","outline-none select-none active:translate-y-[0.5px]","disabled:pointer-events-none disabled:opacity-45 disabled:shadow-none","[&_svg]:pointer-events-none [&_svg]:shrink-0"],{variants:{variant:{default:"border-primary bg-primary font-semibold text-primary-on shadow-glow hover:border-primary-hover hover:bg-primary-hover",outline:"border-control-border bg-control text-foreground shadow-1 hover:border-control-border-hover hover:bg-control-hover",secondary:"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",ghost:"border-transparent bg-transparent text-subtle shadow-none hover:bg-control-hover hover:text-foreground",destructive:"border-destructive-bd bg-destructive-soft text-destructive hover:bg-destructive-hover",dashed:"border-dashed border-border-strong bg-transparent text-muted-foreground shadow-none hover:border-primary hover:text-primary-fg",link:"border-transparent bg-transparent text-primary shadow-none underline-offset-4 hover:underline"},size:{default:"h-[30px] rounded-md px-3 text-[12.5px]","2xs":"h-4 rounded-pill px-1.5 text-[10px]",xs:"h-6 rounded-sm px-2 text-[11.5px]",sm:"h-[26px] rounded-sm px-2.5 text-[12px]",lg:"h-9 rounded-md px-4 text-[13px]",icon:"h-[30px] w-[30px] rounded-md p-0","icon-2xs":"size-4 rounded-pill p-0","icon-xs":"size-6 rounded-sm p-0","icon-sm":"h-[26px] w-[26px] rounded-sm p-0","icon-lg":"size-9 rounded-md p-0"}},defaultVariants:{variant:"default",size:"default"}});function ne({className:e,variant:t,size:a,...o}){return s.jsx(Ez,{className:Ue(vB({variant:t,size:a}),e),...o})}const yB="data-valid",jB="data-invalid",wB={badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:null,valueMissing:!1},n1={valid:null,touched:!1,dirty:!1,filled:!1,focused:!1},SB={disabled:!1,...n1},Ti={valid(e){return e===null?null:e?{[yB]:""}:{[jB]:""}}},s1={invalid:void 0,name:void 0,validityData:{state:wB,errors:[],error:"",value:"",initialValue:null},setValidityData:Kt,disabled:void 0,setTouched:Kt,setDirty:Kt,setFilled:Kt,setFocused:Kt,validationMode:"onSubmit",shouldValidateOnChange:()=>!1,state:SB,registerFieldControl:Kt,validation:{getValidationProps:(e,t=mn)=>t,inputRef:{current:null},registeredInputs:new Map,registerInput:Kt,getInputControl:()=>null,commit:async()=>{},change:Kt}},r1=x.createContext(s1);function Na(e=!0){const t=x.useContext(r1);if(t.setValidityData===Kt&&!e)throw new Error(un(28));return t}const NB=x.createContext(void 0);function kB(e=!1){const t=x.useContext(NB);if(!t&&!e)throw new Error(un(86));return t}const CB=x.createContext({elementRef:{current:null},formRef:{current:{fields:new Map}},errors:{},clearErrors:Kt,validationMode:"onSubmit",submitCountRef:{current:0}});function Qc(){return x.useContext(CB)}let a1=0;function TB(e,t="mui"){const[a,o]=x.useState(e),c=e||a;return x.useEffect(()=>{a==null&&(a1+=1,o(`${t}-${a1}`))},[a,t]),c}const l1=Mv.useId;function Jd(e,t){if(l1!==void 0){const a=l1();return e??(t?`${t}-${a}`:a)}return TB(e,t)}function ka(e){return Jd(e,"base-ui")}const EB=x.createContext({controlId:void 0,registerControlId:Kt,resetControlId:Kt,labelId:void 0,setLabelId:Kt,messageIds:[],setMessageIds:Kt,getDescriptionProps:e=>e});function yl(){return x.useContext(EB)}function Fv(e,t,a,o=!0,c){const[d,f]=x.useState(),p=ka(c?`${c}-label`:void 0),h=e??t??d;return Me(()=>{const b=e||t||!o?void 0:AB(a.current,p);d!==b&&f(b)}),h}function AB(e,t){const a=RB(e);if(a)return!a.id&&t&&(a.id=t),a.id||void 0}function RB(e){if(!e)return;const t=e.parentElement;if(t&&t.tagName==="LABEL")return t;const a=e.id;if(a){const c=e.nextElementSibling;if(c&&c.htmlFor===a)return c}const o=e.labels;return o&&o[0]}function io(e={}){const{id:t,enabled:a=!0}=e,{controlId:o,registerControlId:c,resetControlId:d}=yl(),f=ka(),p=Ps(()=>Symbol()),h=x.useRef(!1),b=x.useRef(!1),y=Ie(()=>{!h.current||c===Kt||(h.current=!1,c(p.current,void 0))});return Me(()=>{if(!a||c===Kt){y();return}let v;if(t!==void 0)b.current=!0,v=t;else if(b.current)v=f;else{d();return}if(v===void 0){y();return}h.current=!0,c(p.current,v)},[t,a,c,d,f,p,y]),Me(()=>y,[y]),(a?o:void 0)??t??f}function OB(){return typeof navigator>"u"?{userAgent:"",platform:"",maxTouchPoints:0}:{userAgent:navigator.userAgent,platform:navigator.platform??"",maxTouchPoints:navigator.maxTouchPoints??0}}const{userAgent:MB,platform:DB,maxTouchPoints:IB}=OB(),Km=MB.toLowerCase(),ef=DB.toLowerCase(),tf=/^i(os$|p)/.test(ef)||ef==="macintel"&&IB>1,o1="android",Ym=ef===o1||Km.includes(o1),i1=!tf&&ef.startsWith("mac");ef.startsWith("win");const PB=i1||tf,jl=typeof CSS<"u"&&!!CSS.supports?.("-webkit-backdrop-filter:none"),c1=!jl&&Km.includes("firefox");!jl&&Km.includes("chrom");const _B=PB,$v=/jsdom|happydom/.test(Km);function ir(e){let t=e.activeElement;for(;t?.shadowRoot?.activeElement!=null;)t=t.shadowRoot.activeElement;return t}function Ze(e,t){if(!e||!t)return!1;const a=t.getRootNode?.();if(e.contains(t))return!0;if(a&&Gc(a)){let o=t;for(;o;){if(e===o)return!0;o=o.parentNode||o.host}}return!1}function fs(e){return"composedPath"in e?e.composedPath()[0]??e.target:e.target}const qv="data-base-ui-focusable",u1="input:not([type='hidden']):not([disabled]),[contenteditable]:not([contenteditable='false']),textarea:not([disabled])",Ei="ArrowLeft",Ai="ArrowRight",Gv="ArrowUp",Xm="ArrowDown",d1="data-starting-style",LB="data-ending-style",zB={[d1]:""},BB={[LB]:""},co={transitionStatus(e){return e==="starting"?zB:e==="ending"?BB:null}},HB="data-open",UB="data-closed",VB="data-anchor-hidden",f1="data-popup-open",FB="data-pressed",$B={[f1]:""},qB={[f1]:"",[FB]:""},GB={[HB]:""},KB={[UB]:""},YB={[VB]:""},Kv={open(e){return e?$B:null}},Yv={open(e){return e?qB:null}},Zc={open(e){return e?GB:KB},anchorHidden(e){return e?YB:null}},Xv={...Zc,...co},Qv="data-trigger-disabled";function Qm(e,t){if(!Rt(e))return!1;const a=e;if(t.hasElement(a))return!a.hasAttribute(Qv);for(const[,o]of t.entries())if(Ze(o,a))return!o.hasAttribute(Qv);return!1}function Zv(e,t){if(t==null)return!1;if("composedPath"in e)return e.composedPath().includes(t);const a=e;return a.target!=null&&t.contains(a.target)}function XB(e){return e.matches("html,body")}function Zm(e){return pn(e)&&e.matches(u1)}function QB(e){return e?.closest(`button,a[href],[role="button"],select,[tabindex]:not([tabindex="-1"]),${u1}`)!=null}function Wv(e){return e?e.getAttribute("role")==="combobox"&&Zm(e):!1}function ZB(e){if(!e||$v)return!0;try{return e.matches(":focus-visible")}catch{return!0}}function Wm(e){return e?e.hasAttribute(qv)?e:e.querySelector(`[${qv}]`)||e:null}function uo(e,t,a=!0){return e.filter(c=>c.parentId===t).flatMap(c=>[...!a||c.context?.open?[c]:[],...uo(e,c.id,a)])}function p1(e,t){let a=[],o=e.find(c=>c.id===t)?.parentId;for(;o;){const c=e.find(d=>d.id===o);o=c?.parentId,c&&(a=a.concat(c))}return a}function Cs(e){e.preventDefault(),e.stopPropagation()}function WB(e){return"nativeEvent"in e}function Jm(e){return e.pointerType===""&&e.isTrusted?!0:Ym&&e.pointerType?e.type==="click"&&e.buttons===1:e.detail===0&&!e.pointerType}function Jv(e){return $v?!1:!Ym&&e.width===0&&e.height===0||Ym&&e.width===1&&e.height===1&&e.pressure===0&&e.detail===0&&e.pointerType==="mouse"||e.width<1&&e.height<1&&e.pressure===0&&e.detail===0&&e.pointerType==="touch"}function fo(e,t){const a=["mouse","pen"];return t||a.push("",void 0),a.includes(e)}function JB(e){const t=e.type;return t==="click"||t==="mousedown"||t==="keydown"||t==="keyup"}const Wc=Math.min,wl=Math.max,eh=Math.round,Ri=Math.floor,Sl=e=>({x:e,y:e}),e4={left:"right",right:"left",bottom:"top",top:"bottom"};function m1(e,t,a){return wl(e,Wc(t,a))}function po(e,t){return typeof e=="function"?e(t):e}function ra(e){return e.split("-")[0]}function Nl(e){return e.split("-")[1]}function ey(e){return e==="x"?"y":"x"}function ty(e){return e==="y"?"height":"width"}function aa(e){const t=e[0];return t==="t"||t==="b"?"y":"x"}function ny(e){return ey(aa(e))}function t4(e,t,a){a===void 0&&(a=!1);const o=Nl(e),c=ny(e),d=ty(c);let f=c==="x"?o===(a?"end":"start")?"right":"left":o==="start"?"bottom":"top";return t.reference[d]>t.floating[d]&&(f=th(f)),[f,th(f)]}function n4(e){const t=th(e);return[sy(e),t,sy(t)]}function sy(e){return e.includes("start")?e.replace("start","end"):e.replace("end","start")}const h1=["left","right"],g1=["right","left"],s4=["top","bottom"],r4=["bottom","top"];function a4(e,t,a){switch(e){case"top":case"bottom":return a?t?g1:h1:t?h1:g1;case"left":case"right":return t?s4:r4;default:return[]}}function l4(e,t,a,o){const c=Nl(e);let d=a4(ra(e),a==="start",o);return c&&(d=d.map(f=>f+"-"+c),t&&(d=d.concat(d.map(sy)))),d}function th(e){const t=ra(e);return e4[t]+e.slice(t.length)}function o4(e){var t,a,o,c;return{top:(t=e.top)!=null?t:0,right:(a=e.right)!=null?a:0,bottom:(o=e.bottom)!=null?o:0,left:(c=e.left)!=null?c:0}}function x1(e){return typeof e!="number"?o4(e):{top:e,right:e,bottom:e,left:e}}function nf(e){const{x:t,y:a,width:o,height:c}=e;return{width:o,height:c,top:a,left:t,right:t+o,bottom:a+c,x:t,y:a}}function nh(e,t,a){return Math.floor(e/t)!==a}function mo(e,t){return t<0||t>=e.length}function sh(e,t){return _s(e.current,{disabledIndices:t})}function ry(e,t){return _s(e.current,{decrement:!0,startingIndex:e.current.length,disabledIndices:t})}function _s(e,{startingIndex:t=-1,decrement:a=!1,disabledIndices:o,amount:c=1}={}){let d=t;do d+=a?-c:c;while(d>=0&&d<=e.length-1&&ho(e,d,o));return d}function i4(e,{event:t,orientation:a,loopFocus:o,onLoop:c,rtl:d,cols:f,disabledIndices:p,minIndex:h,maxIndex:b,prevIndex:y,stopEvent:v=!1}){let j=y,w;if(t.key===Gv?w="up":t.key===Xm&&(w="down"),w){const S=[],N=[];let k=!1,A=0;{let _=null,z=-1;e.forEach((H,B)=>{if(H==null)return;A+=1;const L=H.closest('[role="row"]');L&&(k=!0),(L!==_||z===-1)&&(_=L,z+=1,S[z]=[]),S[z].push(B),N[B]=z})}let P=!1,R=0;if(k)for(const _ of S){const z=_.length;z>R&&(R=z),z!==f&&(P=!0)}const C=P&&A{if(!P||y===-1)return;const z=N[y];if(z==null)return;const H=S[z].indexOf(y),B=_==="up"?-1:1;for(let L=z+B,F=0;F=S.length){if(!o||C)return;if(L=L<0?S.length-1:0,c){const X=Math.min(H,S[L].length-1),K=S[L][X]??S[L][0],q=c(t,y,K);L=N[q]??L}}const $=S[L];for(let X=Math.min(H,$.length-1);X>=0;X-=1){const K=$[X];if(!ho(e,K,p))return K}}},I=_=>{if(!C||y===-1)return;const z=y%E,H=_==="up"?-E:E,B=b-b%E,L=Ri(b/E)+1;for(let F=y-z+H,$=0;$b){if(!o)return;F=F<0?B:0}const X=Math.min(F+E-1,b);for(let K=Math.min(F+z,X);K>=F;K-=1)if(!ho(e,K,p))return K}};v&&Cs(t);const O=D(w)??I(w);if(O!==void 0)j=O;else if(y===-1)j=w==="up"?b:h;else if(j=_s(e,{startingIndex:y,amount:E,decrement:w==="up",disabledIndices:p}),o){if(w==="up"&&(y-E_?H:H-E,c&&(j=c(t,y,j))}w==="down"&&y+E>b&&(j=_s(e,{startingIndex:y%E-E,amount:E,disabledIndices:p}),c&&(j=c(t,y,j)))}mo(e,j)&&(j=y)}if(a==="both"){const S=Ri(y/f);t.key===(d?Ei:Ai)&&(v&&Cs(t),y%f!==f-1?(j=_s(e,{startingIndex:y,disabledIndices:p}),o&&nh(j,f,S)&&(j=_s(e,{startingIndex:y-y%f-1,disabledIndices:p}),c&&(j=c(t,y,j)))):o&&(j=_s(e,{startingIndex:y-y%f-1,disabledIndices:p}),c&&(j=c(t,y,j))),nh(j,f,S)&&(j=y)),t.key===(d?Ai:Ei)&&(v&&Cs(t),y%f!==0?(j=_s(e,{startingIndex:y,decrement:!0,disabledIndices:p}),o&&nh(j,f,S)&&(j=_s(e,{startingIndex:y+(f-y%f),decrement:!0,disabledIndices:p}),c&&(j=c(t,y,j)))):o&&(j=_s(e,{startingIndex:y+(f-y%f),decrement:!0,disabledIndices:p}),c&&(j=c(t,y,j))),nh(j,f,S)&&(j=y));const N=Ri(b/f)===S;mo(e,j)&&(o&&N?(j=t.key===(d?Ai:Ei)?b:_s(e,{startingIndex:y-y%f-1,disabledIndices:p}),c&&(j=c(t,y,j))):j=y)}return j}function ho(e,t,a){if(typeof a=="function"?a(t):a?.includes(t)??!1)return!0;const c=e[t];return c?!rh(c)||c.matches(":disabled")?!0:!a&&(c.hasAttribute("disabled")||c.getAttribute("aria-disabled")==="true"):!1}function c4(e){return e.visibility==="hidden"||e.visibility==="collapse"}function rh(e,t=e?yr(e):null){return!e||!e.isConnected||!t||c4(t)?!1:typeof e.checkVisibility=="function"?e.checkVisibility():t.display!=="none"&&t.display!=="contents"}const u4='a[href],button,input,select,textarea,summary,details,iframe,object,embed,[tabindex],[contenteditable]:not([contenteditable="false"]),audio[controls],video[controls]';function d4(e){const t=e.assignedSlot;if(t)return t;if(e.parentElement)return e.parentElement;const a=e.getRootNode();return Gc(a)?a.host:null}function ay(e){for(const t of Array.from(e.children))if(Fs(t)==="summary")return t;return null}function f4(e,t){const a=ay(t);return!!a&&(e===a||Ze(a,e))}function b1(e){const t=e?Fs(e):"";return e!=null&&e.matches(u4)&&(t!=="summary"||e.parentElement!=null&&Fs(e.parentElement)==="details"&&ay(e.parentElement)===e)&&(t!=="details"||ay(e)==null)&&(t!=="input"||e.type!=="hidden")}function v1(e){if(!b1(e)||!e.isConnected||e.matches(":disabled"))return!1;for(let t=e;t;t=d4(t)){const a=t!==e,o=Fs(t)==="slot";if(t.hasAttribute("inert")||a&&Fs(t)==="details"&&!t.open&&!f4(e,t)||t.hasAttribute("hidden")||!o&&!p4(t,a))return!1}return!0}function p4(e,t){const a=yr(e);return t?a.display!=="none":rh(e,a)}function y1(e){const t=e.tabIndex;if(t<0){const a=Fs(e);if(a==="details"||a==="audio"||a==="video"||pn(e)&&e.isContentEditable)return 0}return t}function ly(e){if(Fs(e)!=="input")return null;const t=e;return t.type==="radio"&&t.name!==""?t:null}function m4(e,t){const a=ly(e);if(!a)return!0;const o=t.find(c=>{const d=ly(c);return d?.name===a.name&&d.form===a.form&&d.checked});return o?o===a:t.find(c=>{const d=ly(c);return d?.name===a.name&&d.form===a.form})===a}function j1(e){if(pn(e)&&Fs(e)==="slot"){const t=e.assignedElements({flatten:!0});if(t.length>0)return t}return pn(e)&&e.shadowRoot?Array.from(e.shadowRoot.children):Array.from(e.children)}function w1(e,t){j1(e).forEach(a=>{b1(a)&&t.push(a),w1(a,t)})}function S1(e,t,a){j1(e).forEach(o=>{pn(o)&&o.matches(t)&&a.push(o),S1(o,t,a)})}function oy(e){return v1(e)&&y1(e)>=0}function N1(e){const t=[];return w1(e,t),t.filter(v1)}function sf(e){const t=N1(e);return t.filter(a=>y1(a)>=0&&m4(a,t))}function k1(e,t){const a=sf(e),o=a.length;if(o===0)return;const c=ir(Gt(e)),d=a.indexOf(c),f=d===-1?t===1?0:o-1:d+t;return a[f]}function iy(e){return k1(Gt(e).body,1)||e}function C1(e){return k1(Gt(e).body,-1)||e}function T1(e,t){if(!e)return null;const a=sf(Gt(e).body),o=a.length;if(o===0)return null;const c=a.indexOf(e);if(c===-1)return null;const d=(c+t+o)%o;return a[d]}function h4(e){return T1(e,1)}function g4(e){return T1(e,-1)}function Jc(e,t){const a=t||e.currentTarget,o=e.relatedTarget;return!o||!Ze(a,o)}function x4(e){sf(e).forEach(a=>{a.dataset.tabindex=a.getAttribute("tabindex")||"",a.setAttribute("tabindex","-1")})}function E1(e){const t=[];S1(e,"[data-tabindex]",t),t.forEach(a=>{const o=a.dataset.tabindex;delete a.dataset.tabindex,o?a.setAttribute("tabindex",o):a.removeAttribute("tabindex")})}function cy(e){x.useEffect(e,Ir)}const rf=0;class la{constructor(){cn(this,"currentId",rf);cn(this,"clear",()=>{this.currentId!==rf&&(clearTimeout(this.currentId),this.currentId=rf)});cn(this,"disposeEffect",()=>this.clear)}static create(){return new la}start(t,a){this.clear(),this.currentId=setTimeout(()=>{this.currentId=rf,a()},t)}isStarted(){return this.currentId!==rf}}function er(){const e=Ps(la.create).current;return cy(e.disposeEffect),e}function b4(e,t){return e.matches(":disabled")?!1:!t||e.form===t?!0:e.form===null&&!e.hasAttribute("form")}const v4=x.createContext({disabled:!1});function A1(){return x.useContext(v4)}var uy={exports:{}},$s={};var R1;function y4(){if(R1)return $s;R1=1;var e=Xd();function t(y){var v="https://react.dev/errors/"+y;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}return e(),uy.exports=y4(),uy.exports}var Ts=M1();const ah=null;class j4{constructor(){cn(this,"callbacks",[]);cn(this,"callbacksCount",0);cn(this,"nextId",1);cn(this,"startId",1);cn(this,"isScheduled",!1);cn(this,"tick",t=>{this.isScheduled=!1;const a=this.callbacks,o=this.callbacksCount;if(this.callbacks=[],this.callbacksCount=0,this.startId=this.nextId,o>0)for(let c=0;c=this.callbacks.length||this.callbacks[a]!==null&&(this.callbacks[a]=null,this.callbacksCount-=1)}}let lh=new j4;class Ga{constructor(){cn(this,"currentId",ah);cn(this,"cancel",()=>{this.currentId!==ah&&(lh.cancel(this.currentId),this.currentId=ah)});cn(this,"disposeEffect",()=>this.cancel)}static create(){return new Ga}static request(t){return lh.request(t)}static cancel(t){return lh.cancel(t)}request(t){this.cancel(),this.currentId=lh.request(()=>{this.currentId=ah,t()})}}function eu(){const e=Ps(Ga.create).current;return cy(e.disposeEffect),e}function kl(e){return e==null?e:"current"in e?e.current:e}let oh=null;function w4(e){if(!oh){const t=[];oh=t,queueMicrotask(()=>{oh=null,Ts.flushSync(()=>{for(const a of t)a()})})}oh.push(e)}function D1(e,t=!1,a=!1){const o=eu();return Ie((c,d=null)=>{o.cancel();const f=kl(e);if(f==null)return;const p=f,h=()=>{if(!a){Ts.flushSync(c);return}w4(()=>{d?.aborted||c()})};if(typeof p.getAnimations!="function"||globalThis.BASE_UI_ANIMATIONS_DISABLED){c();return}function b(){Promise.all(p.getAnimations().map(y=>y.finished)).then(()=>{d?.aborted||h()},()=>{if(d?.aborted)return;if(p.getAnimations().some(v=>v.pending||v.playState!=="finished")){b();return}h()})}if(t){const y=d1;if(!p.hasAttribute(y)){o.request(b);return}const v=new MutationObserver(()=>{p.hasAttribute(y)||(v.disconnect(),b())});v.observe(p,{attributes:!0,attributeFilter:[y]}),d?.addEventListener("abort",()=>v.disconnect(),{once:!0});return}o.request(b)})}function oa(e){const{enabled:t=!0,open:a,ref:o,batch:c=!1,onComplete:d}=e,f=Ie(d),p=D1(o,a,c);x.useEffect(()=>{if(!t)return;const h=new AbortController;return p(f,h.signal),()=>{h.abort()}},[t,a,f,p])}function Oi(e,t=!1,a=!1,o=!1){const[c,d]=x.useState(e&&t?"idle":void 0),[f,p]=x.useState(e&&!o);return e&&!f&&(p(!0),d("starting")),!e&&f&&c!=="ending"&&!a&&d("ending"),!e&&!f&&c==="ending"&&d(void 0),Me(()=>{if(!e&&f&&c!=="ending"&&a){const h=Ga.request(()=>{d("ending")});return()=>{Ga.cancel(h)}}},[e,f,c,a]),Me(()=>{if(!e||t)return;const h=Ga.request(()=>{d(void 0)});return()=>{Ga.cancel(h)}},[t,e]),Me(()=>{if(!e||!t)return;e&&f&&c!=="idle"&&d("starting");const h=Ga.request(()=>{d("idle")});return()=>{Ga.cancel(h)}},[t,e,f,c]),{mounted:f,setMounted:p,transitionStatus:c}}function Ka({controlled:e,default:t,name:a,state:o="value"}){const{current:c}=x.useRef(e!==void 0),[d,f]=x.useState(t),p=c&&e!==void 0?e:d,h=x.useCallback(b=>{c||f(b)},[]);return[p,h]}function tu(e,t,a,o,c=!0,d){const{registerFieldControl:f}=Na(),p=Ps(()=>Symbol());Me(()=>{const h=p.current;if(!c){f(h,void 0);return}f(h,{controlRef:e,getValue:o,id:t,name:d,value:a})},[e,c,o,t,d,f,p,a]),Me(()=>{const h=p.current;return()=>{f(h,void 0)}},[f,p])}function ia(e,t){const a=x.useRef(e),o=Ie(t);Me(()=>{a.current!==e&&o(a.current),a.current=e},[e,o])}const Qn="none",go="trigger-press",qs="trigger-hover",ih="trigger-focus",ch="outside-press",af="item-press",dy="close-press",lf="input-change",Ya="input-clear",S4="input-press",xo="focus-out",nu="escape-key",fy="list-navigation",I1="keyboard",P1="pointer",_1="cancel-open",N4="disabled",py="imperative-action",k4="window-resize";function qe(e,t,a,o){let c=!1,d=!1;const f=o??mn;return{reason:e,event:t??new Event("base-ui"),cancel(){c=!0},allowPropagation(){d=!0},get isCanceled(){return c},get isPropagationAllowed(){return d},trigger:a,...f}}function C4(e,t,a){const o=a??mn;return{reason:e,event:t??new Event("base-ui"),...o}}const T4=x.forwardRef(function(t,a){const{render:o,className:c,id:d,name:f,value:p,disabled:h=!1,onValueChange:b,defaultValue:y,autoFocus:v=!1,style:j,...w}=t,{state:S,name:N,disabled:k,setTouched:A,setDirty:P,validityData:R,setFocused:C,setFilled:E,validationMode:D,validation:I}=Na(),{clearErrors:O,elementRef:_,submitCountRef:z}=Qc(),H=k||h,B=N??f,L={...S,disabled:H},{labelId:F}=yl(),$=io({id:d}),[X]=Ka({controlled:p,default:y,name:"FieldControl",state:"value"}),K=p!==void 0,q=K?X:void 0,Q=q==null?void 0:String(q),G=Ie(()=>I.inputRef.current?.value);tu(I.inputRef,$,Q,G,!H,f),Me(()=>{const M=Q??I.inputRef.current?.value;M!==void 0&&E(M!=="")},[Q,I.inputRef,E]),ia(Q,()=>{Q!==void 0&&(O(B),P(Q!==(R.initialValue??"")),I.change(Q))});const J=x.useRef(null),Y=er();return Me(()=>{v&&J.current===ir(Gt(J.current))&&C(!0)},[v,C]),yt("input",t,{ref:[a,J],state:L,props:[{id:$,disabled:H,name:B,ref:I.inputRef,"aria-labelledby":F,autoFocus:v,...K?{value:q}:{defaultValue:y},onChange(M){const U=M.currentTarget.value,Z=qe(Qn,M.nativeEvent);b?.(U,Z),!K&&(P(U!==(R.initialValue??"")),E(U!==""),!M.nativeEvent.defaultPrevented&&!Z.isCanceled&&(O(B),I.change(U)))},onFocus(){C(!0)},onBlur(M){if(A(!0),C(!1),D==="onBlur"){const U=M.currentTarget.value;I.commit(U),K&&queueMicrotask(()=>{const Z=I.inputRef.current?.value;Z!==void 0&&Z!==U&&Z!==(R.initialValue??"")&&I.commit(Z)})}},onKeyDown(M){if(M.currentTarget.tagName==="INPUT"&&M.key==="Enter"){A(!0);const U=M.currentTarget.value,Z=M.currentTarget.form;if(Z&&Z===_.current&&!M.defaultPrevented){const ae=M.currentTarget,ce=z.current;Y.start(0,()=>{z.current===ce&&I.commit(ae.value)})}else I.commit(U)}}},w,M=>I.getValidationProps(H,M)],stateAttributesMapping:Ti})}),E4=x.forwardRef(function(t,a){return s.jsx(T4,{ref:a,...t})});function Fe({className:e,type:t,ref:a,...o}){return s.jsx(E4,{ref:a,type:t,"data-slot":"input",className:Ue("h-[30px] w-full min-w-0 rounded-md border border-control-border bg-control px-2.5","text-[12.5px] text-foreground outline-none","transition-[border-color,box-shadow] duration-150 ease-out","placeholder:text-muted-foreground","hover:border-control-border-hover","focus:border-primary focus:shadow-focus","disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-surface-3 disabled:text-muted-foreground","file:inline-flex file:border-0 file:bg-transparent file:text-foreground",e),...o})}function Mi({className:e,...t}){return s.jsx("textarea",{"data-slot":"textarea",className:Ue("min-h-26 w-full resize-y rounded-md border border-control-border bg-control px-3 py-2.5","font-mono text-[12px] leading-[1.6] text-foreground outline-none","transition-[border-color,box-shadow] duration-150 ease-out","placeholder:text-muted-foreground","hover:border-control-border-hover","focus:border-primary focus:shadow-focus","disabled:cursor-not-allowed disabled:bg-surface-3 disabled:text-muted-foreground",e),...t})}function Di({className:e,...t}){return s.jsx("div",{"data-slot":"input-group",role:"group",className:Ue("group/input-group relative flex h-[30px] w-full min-w-0 items-center rounded-md border border-control-border bg-control","transition-[border-color,box-shadow] duration-150 ease-out","outline-none","has-[[data-slot=input-group-control]:focus-visible]:border-primary","has-[[data-slot=input-group-control]:focus-visible]:shadow-focus","has-[[data-slot=input-group-control]:hover:not(:focus-visible)]:border-control-border-hover","has-[>textarea]:h-auto","has-[>[data-align=inline-start]]:[&>[data-slot=input-group-control]]:pl-1.5","has-[>[data-align=inline-end]]:[&>[data-slot=input-group-control]]:pr-1.5","has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col","has-[>[data-align=block-start]]:[&>[data-slot=input-group-control]]:pb-1.5","has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col","has-[>[data-align=block-end]]:[&>[data-slot=input-group-control]]:pt-1.5",e),...t})}const A4=Xc(["flex h-auto cursor-text items-center justify-center gap-1.5 select-none","text-[12px] font-medium text-muted-foreground","group-data-[disabled=true]/input-group:opacity-50"],{variants:{align:{"inline-start":"order-first pl-[9px] has-[>button]:-ml-1","inline-end":"order-last pr-[9px] has-[>button]:-mr-1","block-start":"order-first w-full justify-start px-2.5 pt-2 group-has-[>input]/input-group:pt-1.5","block-end":"order-last w-full justify-start px-2.5 pb-2 group-has-[>input]/input-group:pb-1.5"}},defaultVariants:{align:"inline-start"}});function Ii({className:e,align:t="inline-start",...a}){return s.jsx("div",{role:"group","data-slot":"input-group-addon","data-align":t,className:Ue(A4({align:t}),e),onClick:o=>{o.target.closest("button")||o.currentTarget.parentElement?.querySelector("input")?.focus()},...a})}const R4=Xc("flex items-center shadow-none",{variants:{size:{xs:"h-6 gap-1 rounded-xs px-1.5",sm:"h-8 gap-1.5 rounded-sm px-2.5","icon-xs":"size-6 rounded-xs p-0","icon-sm":"size-8 rounded-sm p-0"}},defaultVariants:{size:"xs"}});function O4({className:e,type:t="button",variant:a="ghost",size:o="xs",...c}){return s.jsx(ne,{type:t,"data-size":o,variant:a,className:Ue(R4({size:o}),e),...c})}function Pi({className:e,...t}){return s.jsx(Fe,{"data-slot":"input-group-control",className:Ue("flex-1 rounded-none border-0 bg-transparent shadow-none focus:border-0 focus:shadow-none",e),...t})}function M4(e){return yt(e.defaultTagName??"div",e,e)}const D4=Xc("inline-flex w-fit shrink-0 items-center justify-center gap-[5px] overflow-hidden whitespace-nowrap border font-semibold",{variants:{variant:{default:"border-transparent bg-primary text-primary-foreground",secondary:"border-transparent bg-secondary text-secondary-foreground",destructive:"border-destructive-bd bg-destructive-soft text-destructive",outline:"border-control-border bg-control text-subtle",ghost:"border-transparent bg-transparent text-muted-foreground",link:"border-transparent bg-transparent text-primary underline-offset-4 hover:underline",success:"border-success-bd bg-success-soft text-success",warning:"border-warning-bd bg-warning-soft text-warning",info:"border-info-bd bg-info-soft text-info",brand:"border-primary-bd bg-primary-soft text-primary-fg",cn:"border-cn-bd bg-cn-soft text-cn",intl:"border-intl-bd bg-intl-soft text-intl",sensitive:"border-sensitive-bd bg-sensitive-soft text-sensitive"},shape:{pill:"min-h-[21px] rounded-pill px-[9px] py-px text-[11.5px]",tag:"min-h-[19px] rounded-xs px-2 py-0 text-[11px]"}},defaultVariants:{variant:"default",shape:"pill"}});function ze({className:e,variant:t,shape:a,render:o,...c}){return M4({render:o,defaultTagName:"span",props:ta({"data-slot":"badge"},c,{className:Ue(D4({variant:t,shape:a}),e)})})}function my({className:e,...t}){return s.jsx("span",{className:Ue("size-1.5 flex-none rounded-full bg-current",e),...t})}function bo(e,t){if(typeof window>"u")return e;const a=window.wbI18n;return a?a.t(e,t)??e:e}function of({className:e,...t}){return s.jsx("svg",{"data-slot":"spinner",role:"status","aria-label":bo("加载中"),viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",className:Ue("size-4 animate-spin [animation-duration:0.7s]",e),...t,children:s.jsx("path",{d:"M21 12a9 9 0 1 1-6.219-8.56"})})}function Je({className:e,...t}){return s.jsx("label",{"data-slot":"label",className:Ue("flex items-center gap-[6px] text-[11.5px] text-muted-foreground",e),...t})}const L1={clipPath:"inset(50%)",overflow:"hidden",whiteSpace:"nowrap",border:0,padding:0,width:1,height:1,margin:-1},_i={...L1,position:"fixed",top:0,left:0},su={...L1,position:"absolute"},z1=x.createContext(void 0);function I4(){const e=x.useContext(z1);if(e===void 0)throw new Error(un(63));return e}const P4="data-checked",_4="data-unchecked",B1={...Ti,checked(e){return e?{[P4]:""}:{[_4]:""}}},L4=x.forwardRef(function(t,a){const{checked:o,className:c,defaultChecked:d,"aria-labelledby":f,form:p,id:h,inputRef:b,name:y,nativeButton:v=!1,onCheckedChange:j,readOnly:w=!1,required:S=!1,disabled:N=!1,render:k,uncheckedValue:A,value:P,style:R,...C}=t,{clearErrors:E}=Qc(),{state:D,setTouched:I,setDirty:O,validityData:_,setFilled:z,setFocused:H,validationMode:B,disabled:L,name:F,validation:$}=Na(),{labelId:X}=yl(),K=L||N,q=F??y,Q=x.useRef(null),G=sa(Q,b,$.inputRef),J=x.useRef(null),Y=ka(),re=io({id:h}),M=v?void 0:re,[U,Z]=Ka({controlled:o,default:!!d,name:"Switch",state:"checked"});tu(J,Y,U,void 0,!K,y),Me(()=>{z(U)},[U,z]),ia(U,()=>{E(q),O(U!==_.initialValue),$.change(U)});const{getButtonProps:ae,buttonRef:ce}=na({disabled:K,native:v}),W=Fv(f,X,Q,!v,M),ue={id:v?re:Y,role:"switch","aria-checked":U,"aria-readonly":w||void 0,"aria-required":S||void 0,"aria-labelledby":W,onFocus(){K||H(!0)},onBlur(){const te=Q.current;!te||K||(I(!0),H(!1),B==="onBlur"&&$.commit(te.checked))},onClick(te){if(w||K)return;te.preventDefault();const ke=Q.current;ke&&Yc(ke,te)}},ve={...$.getValidationProps(K),checked:U,disabled:K,form:p,id:M,name:q,required:S,style:q?su:_i,tabIndex:-1,type:"checkbox","aria-hidden":!0,ref:G,onChange(te){if(te.nativeEvent.defaultPrevented)return;if(w){te.preventDefault();return}const ke=te.currentTarget.checked,le=qe(Qn,te.nativeEvent);j?.(ke,le),!le.isCanceled&&Z(ke)},onClick(te){te.stopPropagation()},onFocus(){J.current?.focus()},...P!==void 0?{value:P}:mn},ee=x.useMemo(()=>({...D,checked:U,disabled:K,readOnly:w,required:S}),[D,U,K,w,S]),he=yt("span",t,{state:ee,ref:[a,J,ce],props:[ue,C,ae,te=>$.getValidationProps(K,te)],stateAttributesMapping:B1});return s.jsxs(z1.Provider,{value:ee,children:[he,!U&&q&&A!==void 0&&s.jsx("input",{type:"hidden",form:p,name:q,value:A,disabled:K}),s.jsx("input",{...ve,suppressHydrationWarning:!0})]})}),z4=x.forwardRef(function(t,a){const{render:o,className:c,style:d,...f}=t,p=I4();return yt("span",t,{state:p,ref:a,stateAttributesMapping:B1,props:f})});function ns({className:e,size:t="default",...a}){const o=t==="sm";return s.jsx(L4,{"data-slot":"switch",className:Ue("relative inline-flex flex-none cursor-pointer items-center rounded-pill border-0 bg-border-strong p-0","transition-colors duration-[180ms] ease-out","data-checked:bg-primary","focus-visible:shadow-focus focus-visible:outline-none","data-disabled:cursor-not-allowed data-disabled:opacity-50",o?"h-[14px] w-6":"h-[21px] w-9",e),...a,children:s.jsx(z4,{"data-slot":"switch-thumb",className:Ue("block rounded-full bg-primary-on shadow-1","transition-transform duration-[180ms] [transition-timing-function:cubic-bezier(.3,1.4,.6,1)]",o?"size-[10px] translate-x-0.5 data-checked:translate-x-[10px]":"size-[17px] translate-x-0.5 data-checked:translate-x-[15px]")})})}function B4(e){if(!e)return null;for(const t of e.elements){const a=t.tagName;if(a==="BUTTON"||a==="INPUT"){const o=t;if(o.type==="submit")return o}}return null}const H4="data-checked",U4="data-unchecked";function H1(e){return{checked(t){return e.indeterminate?{}:t?{[H4]:""}:{[U4]:""}},...Ti}}const V4=x.createContext(void 0);function F4(){return x.useContext(V4)}const U1=x.createContext(void 0);function $4(){const e=x.useContext(U1);if(e===void 0)throw new Error(un(14));return e}const q4="data-parent",G4=x.forwardRef(function(t,a){const{checked:o,className:c,defaultChecked:d=!1,"aria-labelledby":f,disabled:p=!1,form:h,id:b,indeterminate:y=!1,inputRef:v,name:j,onCheckedChange:w,parent:S=!1,readOnly:N=!1,render:k,required:A=!1,uncheckedValue:P,value:R,nativeButton:C=!1,style:E,...D}=t,{clearErrors:I}=Qc(),{disabled:O,name:_,setDirty:z,setFilled:H,setFocused:B,setTouched:L,state:F,validationMode:$,validityData:X,validation:K}=Na(),q=A1(),{labelId:Q,registerControlId:G,getDescriptionProps:J}=yl(),Y=F4(),re=Y?.allValues===void 0?void 0:Y.parent,M=re!==void 0,U=O||q.disabled||Y?.disabled||p,Z=_??j,ae=R??Z,ce=ka(),W=Y?.registerControlId!==G,ue=io({id:b||void 0,enabled:W}),ve=C?ue:ce;let ee={};M&&(S?ee=re.getParentProps():ae!==void 0&&(ee=re.getChildProps(ae)));const{checked:he=o,indeterminate:te=y,onCheckedChange:ke,...le}=ee,de=Y?.value,Se=x.useRef(null),{getButtonProps:Te,buttonRef:_e}=na({disabled:U,native:C}),Ae=Y?.validation??K,[oe,me]=Ka({controlled:ae!==void 0&&de!==void 0&&!S?de.includes(ae):he,default:d,name:"Checkbox",state:"checked"}),pe=M?!!he:oe,Ce=M&&te||y;tu(Se,ce,oe,void 0,!Y&&!U,j);const Ne=re?.registerChildId,be=x.useRef(null),De=Ae.registerInput,Ge=Y?ae:void 0,Ct=x.useCallback(it=>De(it,{controlRef:Se,value:Ge}),[De,Ge]),Tt=sa(v,be,S?void 0:Ct),Mt=Fv(f,Q,be,!C,ue);Me(()=>{be.current&&(be.current.indeterminate=Ce),Y||H(oe)},[oe,Ce,Y,H]),ia(oe,()=>{Y||(I(Z),z(oe!==X.initialValue),Ae.change(oe))});const ot=ta({checked:oe,disabled:U,form:h,name:S?void 0:Z,id:C?void 0:ue,required:A,ref:Tt,style:Z?su:_i,tabIndex:-1,type:"checkbox","aria-hidden":!0,onChange(it){if(it.nativeEvent.defaultPrevented)return;if(N){it.preventDefault();return}const $t=it.currentTarget.checked,Vn=qe(Qn,it.nativeEvent);if(w?.($t,Vn),!Vn.isCanceled&&(ke?.($t,Vn),!Vn.isCanceled&&(me($t),ae!==void 0&&Y!==void 0&&!S&&!M))){const rn=$t?[...Y.value,ae]:Y.value.filter(nt=>nt!==ae);Y.setValue(rn,Vn)}},onClick(it){it.stopPropagation()},onFocus(){Se.current?.focus()}},R!==void 0?{value:(Y?oe&&R:R)||""}:mn,J,it=>Ae.getValidationProps(U,it));x.useEffect(()=>{if(!re||ae===void 0)return;const it=re.disabledStatesRef.current;return it.set(ae,U),()=>{it.delete(ae)}},[re,U,ae]);const Kn=x.useMemo(()=>({...F,checked:pe,disabled:U,readOnly:N,required:A,indeterminate:Ce}),[F,pe,U,N,A,Ce]),ms=H1(Kn),Yn=yt("span",t,{state:Kn,ref:[_e,Se,a],props:[{id:ve,role:"checkbox","aria-checked":Ce?"mixed":pe,"aria-readonly":N||void 0,"aria-required":A||void 0,"aria-labelledby":Mt,[q4]:S?"":void 0,onFocus(){U||B(!0)},onBlur(){const it=be.current;it&&(L(!0),B(!1),$==="onBlur"&&Ae.commit(Y?de:it.checked))},onKeyDown(it){if(it.key!=="Enter"||(it.preventBaseUIHandler(),it.defaultPrevented))return;const $t=be.current?.form??null,Vn=it.currentTarget,rn=it.nativeEvent,nt=it.preventDefault,Dt=rn.preventDefault;let qt=!1;it.preventDefault=()=>{qt=!0,nt.call(it)},rn.preventDefault=()=>{qt=!0,Dt.call(rn)},Dt.call(rn),Cn(Vn).queueMicrotask(()=>{it.preventDefault=nt,rn.preventDefault=Dt,qt||B4($t)?.click()})},onClick(it){if(N||U)return;it.preventDefault();const $t=be.current;$t&&Yc($t,it)}},D,le,Te,J,it=>Ae.getValidationProps(U,it)],stateAttributesMapping:ms}),vs=Yn.props.id;return Me(()=>{if(!(!Ne||S||ae===void 0||vs===void 0))return Ne(ae,vs)},[Ne,S,ae,vs]),s.jsxs(U1.Provider,{value:Kn,children:[Yn,!oe&&!Y&&Z&&!S&&P!==void 0&&s.jsx("input",{type:"hidden",form:h,name:Z,value:P,disabled:U}),s.jsx("input",{...ot,suppressHydrationWarning:!0})]})}),K4=x.forwardRef(function(t,a){const{render:o,className:c,style:d,keepMounted:f=!1,...p}=t,h=$4(),b=h.checked||h.indeterminate,{mounted:y,transitionStatus:v,setMounted:j}=Oi(b),w=x.useRef(null),S={...h,transitionStatus:v};oa({batch:!0,enabled:!b,open:b,ref:w,onComplete(){b||j(!1)}});const k={...H1(h),...co},A=f||y,P=yt("span",t,{ref:[a,w],state:S,stateAttributesMapping:k,props:p});return A?P:null});function Pr({className:e,...t}){return s.jsx(G4,{"data-slot":"checkbox",className:Ue("group/checkbox relative inline-flex size-3.5 shrink-0 cursor-pointer items-center justify-center rounded-[3px] border border-control-border bg-control","outline-none transition-colors duration-150 ease-out","hover:border-control-border-hover","data-checked:border-primary data-checked:bg-primary","focus-visible:shadow-focus","data-disabled:cursor-not-allowed data-disabled:opacity-45",e),...t,children:s.jsx(K4,{"data-slot":"checkbox-indicator",className:"grid place-content-center text-primary-foreground data-unchecked:hidden",children:s.jsxs("svg",{viewBox:"0 0 12 12",className:"size-2.5","aria-hidden":"true",children:[s.jsx("path",{d:"M2 6h8",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",fill:"none",className:"hidden group-aria-[checked=indeterminate]/checkbox:block"}),s.jsx("path",{d:"M2.5 6.2 4.8 8.5 9.5 3.8",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round",fill:"none",className:"group-aria-[checked=indeterminate]/checkbox:hidden"})]})})})}function hy(e){return Array.isArray(e)?e.map(t=>hy(t)).join(","):e==null?"":String(e)}const V1=new Map;function Y4(e,t){const a=JSON.stringify({locale:hy(e),options:t}),o=V1.get(a);if(o)return o;const c=new Intl.NumberFormat(e,t);return V1.set(a,c),c}function F1(e,t,a){return e==null?"":Y4(t,a).format(e)}function Li(e,t=Number.MIN_SAFE_INTEGER,a=Number.MAX_SAFE_INTEGER){return Math.max(t,Math.min(e,a))}function X4(e,t,a){return(e-t)*100/(a-t)}const $1=x.createContext(void 0);function q1(){const e=x.useContext($1);if(e===void 0)throw new Error(un(51));return e}const Q4="data-complete",Z4="data-indeterminate",W4="data-progressing",gy={status(e){return e==="progressing"?{[W4]:""}:e==="complete"?{[Q4]:""}:e==="indeterminate"?{[Z4]:""}:null}},J4=x.forwardRef(function(t,a){const{format:o,getAriaValueText:c,locale:d,max:f=100,min:p=0,value:h,render:b,className:y,children:v,style:j,...w}=t,[S,N]=x.useState();let k="indeterminate",A=null,P=null,R="",C="indeterminate progress";if(h!=null&&Number.isFinite(h)){const _=X4(h,p,f);A=Li(Number.isNaN(_)?0:_,0,100),P=Li(h,p,f),k=P===f?"complete":"progressing",R=o?F1(P,d,o):F1(A/100,d,{style:"percent"}),C=R}const E=x.useMemo(()=>({status:k}),[k]),D={"aria-labelledby":S,"aria-valuemax":f,"aria-valuemin":p,"aria-valuenow":P??void 0,"aria-valuetext":c?c(R,h):C,role:"progressbar",children:s.jsxs(x.Fragment,{children:[v,s.jsx("span",{role:"presentation",style:_i,children:"x"})]})},I=x.useMemo(()=>({formattedValue:R,percentageValue:A,setLabelId:N,state:E,value:h}),[R,A,N,E,h]),O=yt("div",t,{state:E,ref:a,props:[D,w],stateAttributesMapping:gy});return s.jsx($1.Provider,{value:I,children:O})}),eH=x.forwardRef(function(t,a){const{render:o,className:c,style:d,...f}=t,{state:p}=q1();return yt("div",t,{state:p,ref:a,props:f,stateAttributesMapping:gy})}),tH=x.forwardRef(function(t,a){const{render:o,className:c,style:d,...f}=t,{percentageValue:p,state:h}=q1(),b=p==null?{}:{insetInlineStart:0,height:"inherit",width:`${p}%`};return yt("div",t,{state:h,ref:a,props:[{style:b},f],stateAttributesMapping:gy})});function cf({className:e,children:t,value:a,...o}){return s.jsxs(J4,{value:a,"data-slot":"progress",className:Ue("flex w-full flex-wrap items-center gap-3",e),...o,children:[t,s.jsx(nH,{children:s.jsx(sH,{})})]})}function nH({className:e,...t}){return s.jsx(eH,{"data-slot":"progress-track",className:Ue("relative flex h-1.5 w-full items-center overflow-x-hidden overflow-y-hidden rounded-pill bg-surface-3",e),...t})}function sH({className:e,...t}){return s.jsx(tH,{"data-slot":"progress-indicator",className:Ue("h-full rounded-[inherit] bg-primary transition-[width] duration-300 ease-out",e),...t})}const xy=[];let by;function rH(){return by}function aH(e){xy.push(e)}function uh(e){const t=(a,o)=>{const c=Ps(lH).current;let d;try{by=c;for(const f of xy)f.before(c);d=e(a,o);for(const f of xy)f.after(c);c.didInitialize=!0}finally{by=void 0}return d};return t.displayName=e.displayName||e.name,t}function G1(e){return x.forwardRef(uh(e))}function lH(){return{didInitialize:!1}}const K1=x.createContext(void 0);function uf(e){const t=x.useContext(K1);if(t===void 0&&!e)throw new Error(un(72));return t}function oH(e,t){return t!=null&&!fo(t)?0:typeof e=="function"?e():e}function zi(e,t,a){const o=oH(e,a);return typeof o=="number"?o:o?.[t]}function Y1(e){return typeof e=="function"?e():e}function X1(e,t){return t||e==="click"||e==="mousedown"}function iH(e){return e?.includes("mouse")&&e!=="mousedown"}const Q1=x.createContext({hasProvider:!1,timeoutMs:0,delayRef:{current:0},initialDelayRef:{current:0},timeout:new la,currentIdRef:{current:null},currentContextRef:{current:null}});function cH(e,t){e.current=t.current}function uH(e){const{children:t,delay:a,timeoutMs:o=0}=e,c=x.useRef(a),d=x.useRef(a),f=x.useRef(null),p=x.useRef(null),h=er();return Me(()=>{if(d.current=a,!f.current){c.current=a;return}c.current={open:zi(c.current,"open"),close:zi(a,"close")}},[a,f,c,d]),s.jsx(Q1.Provider,{value:x.useMemo(()=>({hasProvider:!0,delayRef:c,initialDelayRef:d,currentIdRef:f,timeoutMs:o,currentContextRef:p,timeout:h}),[o,h]),children:t})}function dH(e,t={open:!1}){const{open:a}=t,o="rootStore"in e?e.rootStore:e,c=o.useState("floatingId"),d=x.useContext(Q1),{currentIdRef:f,delayRef:p,timeoutMs:h,initialDelayRef:b,currentContextRef:y,hasProvider:v,timeout:j}=d,[w,S]=x.useState(!1),N=x.useRef(a);return Me(()=>{N.current=a},[a]),Me(()=>{function k(){y.current?.setIsInstantPhase(!1),f.current=null,y.current=null,p.current=b.current,j.clear()}if(f.current&&!a&&f.current===c){if(S(!1),h){const A=c;return j.start(h,()=>{o.select("open")||f.current&&f.current!==A||k()}),()=>{(N.current||f.current!==A)&&j.clear()}}k()}},[a,c,f,p,h,b,y,j,o]),Me(()=>{if(!a)return;const k=y.current,A=f.current;j.clear(),y.current={onOpenChange:o.setOpen,setIsInstantPhase:S},f.current=c,p.current={open:0,close:zi(b.current,"close")},A!==null&&A!==c?(S(!0),k?.setIsInstantPhase(!0),k?.onOpenChange(!1,qe(Qn))):(S(!1),k?.setIsInstantPhase(!1))},[a,c,o,f,p,b,y,j]),Me(()=>()=>{if(f.current===c){if(y.current=null,!N.current)return;f.current=null,cH(p,b),j.clear()}},[y,f,p,c,b,j]),x.useMemo(()=>({activeIdRef:f,hasProvider:v,delayRef:p,isInstantPhase:w}),[f,v,p,w])}function St(e,t,a,o){return e.addEventListener(t,a,o),()=>{e.removeEventListener(t,a,o)}}function Xa(...e){return()=>{for(let t=0;t{t.current=t.next}};return t}const ru=x.forwardRef(function(t,a){const[o,c]=x.useState();Me(()=>{_B&&jl&&c("button")},[]);const d={tabIndex:0,role:o};return s.jsx("span",{...t,ref:a,style:_i,"aria-hidden":o?void 0:!0,...d,"data-base-ui-focus-guard":""})});function df(e){return`data-base-ui-${e}`}let dh=0;function fh(e,t={}){const{preventScroll:a=!1,sync:o=!1,shouldFocus:c}=t;cancelAnimationFrame(dh);function d(){c&&!c()||e?.focus({preventScroll:a})}if(o)return d(),Kt;const f=requestAnimationFrame(d);return dh=f,()=>{dh===f&&(cancelAnimationFrame(f),dh=0)}}const vy={inert:new WeakMap,"aria-hidden":new WeakMap},Z1="data-base-ui-inert",yy={inert:new WeakSet,"aria-hidden":new WeakSet};let ff=new WeakMap,jy=0;function pH(e){return yy[e]}function W1(e){return e?Gc(e)?e.host:W1(e.parentNode):null}const J1=(e,t)=>t.map(a=>{if(e.contains(a))return a;const o=W1(a);return e.contains(o)?o:null}).filter(a=>a!=null),eT=e=>{const t=new Set;return e.forEach(a=>{let o=a;for(;o&&!t.has(o);)t.add(o),o=o.parentNode}),t},tT=(e,t,a)=>{const o=[],c=d=>{!d||a.has(d)||Array.from(d.children).forEach(f=>{Fs(f)!=="script"&&(t.has(f)?c(f):o.push(f))})};return c(e),o};function mH(e,t,a,o,{mark:c=!0}){let d=null;o?d="inert":a&&(d="aria-hidden");let f=null,p=null;const h=J1(t,e),b=c?tT(t,eT(h),new Set(h)):[],y=[],v=[];if(d){const j=vy[d],w=pH(d);p=w,f=j;const S=J1(t,Array.from(t.querySelectorAll("[aria-live]"))),N=h.concat(S);tT(t,eT(N),new Set(N)).forEach(A=>{const P=A.getAttribute(d),R=P!==null&&P!=="false",C=(j.get(A)||0)+1;j.set(A,C),y.push(A),C===1&&R&&w.add(A),R||A.setAttribute(d,d==="inert"?"":"true")})}return c&&b.forEach(j=>{const w=(ff.get(j)||0)+1;ff.set(j,w),v.push(j),w===1&&j.setAttribute(Z1,"")}),jy+=1,()=>{f&&y.forEach(j=>{const S=(f.get(j)||0)-1;f.set(j,S),S||(!p?.has(j)&&d&&j.removeAttribute(d),p?.delete(j))}),c&&v.forEach(j=>{const w=(ff.get(j)||0)-1;ff.set(j,w),w||j.removeAttribute(Z1)}),jy-=1,jy||(vy.inert=new WeakMap,vy["aria-hidden"]=new WeakMap,yy.inert=new WeakSet,yy["aria-hidden"]=new WeakSet,ff=new WeakMap)}}function nT(e,t={}){const{ariaHidden:a=!1,inert:o=!1,mark:c=!0}=t,d=Gt(e[0]).body;return mH(e,d,a,o,{mark:c})}const hH=500,gH={style:{transition:"none"}},sT="data-base-ui-click-trigger",rT={fallbackAxisSide:"none"},aT={fallbackAxisSide:"end"},xH={clipPath:"inset(50%)",position:"fixed",top:0,left:0},lT=x.createContext(null),oT=()=>x.useContext(lT),bH=df("portal");function iT(e={}){const{ref:t,container:a,componentProps:o=mn,elementProps:c}=e,d=Jd(),p=oT()?.portalNode,[h,b]=x.useState(null),[y,v]=x.useState(null),j=Ie(k=>{k!==null&&v(k)}),w=x.useRef(null);Me(()=>{if(a===null){w.current&&(w.current=null,v(null),b(null));return}const k=(a&&(Tv(a)?a:a.current))??p??document.body;if(k==null){w.current&&(w.current=null,v(null),b(null));return}w.current!==k&&(w.current=k,v(null),b(k))},[a,p]);const S=yt("div",o,{ref:[t,j],props:[{id:d,[bH]:""},c]}),N=h&&S?Ts.createPortal(S,h):null;return{node:y,nodeId:x.isValidElement(S)?S.props.id:void 0,subtree:N}}const ph=x.forwardRef(function(t,a){const{render:o,className:c,style:d,children:f,container:p,portalOwnerRole:h,...b}=t,{node:y,nodeId:v,subtree:j}=iT({container:p,ref:a,componentProps:t,elementProps:b}),w=x.useRef(null),S=x.useRef(null),N=x.useRef(null),k=x.useRef(null),[A,P]=x.useState(null),R=x.useRef(!1),C=A?.modal,E=A?.open,D=!!A&&!A.modal&&A.open&&!!y;x.useEffect(()=>{if(!y||C)return;function O(_){y&&_.relatedTarget&&Jc(_)&&(_.type==="focusin"?R.current&&(E1(y),R.current=!1):(x4(y),R.current=!0))}return Xa(St(y,"focusin",O,!0),St(y,"focusout",O,!0))},[y,C]),Me(()=>{!y||E!==!0||!R.current||(E1(y),R.current=!1)},[E,y]);const I=x.useMemo(()=>({beforeOutsideRef:w,afterOutsideRef:S,beforeInsideRef:N,afterInsideRef:k,portalNode:y,setFocusManagerState:P}),[y]);return s.jsxs(x.Fragment,{children:[j,s.jsxs(lT.Provider,{value:I,children:[D&&y&&s.jsx(ru,{"data-type":"outside",ref:w,onFocus:O=>{if(Jc(O,y))N.current?.focus();else{const _=A?A.domReference:null;C1(_)?.focus()}}}),D&&y&&s.jsx("span",{role:h,"aria-owns":v,style:xH}),y&&Ts.createPortal(f,y),D&&y&&s.jsx(ru,{"data-type":"outside",ref:S,onFocus:O=>{if(Jc(O,y))k.current?.focus();else{const _=A?A.domReference:null;iy(_)?.focus(),A?.closeOnFocusOut&&A?.onOpenChange(!1,qe(xo,O.nativeEvent))}}})]})]})});function cT(){const e=new Map;return{emit(t,a){e.get(t)?.forEach(o=>o(a))},on(t,a){e.has(t)||e.set(t,new Set),e.get(t).add(a)},off(t,a){e.get(t)?.delete(a)}}}class vH{constructor(){cn(this,"nodesRef",{current:[]});cn(this,"events",cT())}addNode(t){this.nodesRef.current.push(t)}removeNode(t){const a=this.nodesRef.current.findIndex(o=>o===t);a!==-1&&this.nodesRef.current.splice(a,1)}}const uT=x.createContext(null),dT=x.createContext(null),au=()=>x.useContext(uT)?.id||null,Bi=e=>{const t=x.useContext(dT);return e??t};function yH(e){const t=Jd(),a=Bi(e),o=au();return Me(()=>{if(!t)return;const c={id:t,parentId:o};return a?.addNode(c),()=>{a?.removeNode(c)}},[a,t,o]),t}function jH(e){const{children:t,id:a}=e,o=au();return s.jsx(uT.Provider,{value:x.useMemo(()=>({id:a,parentId:o}),[a,o]),children:t})}function wH(e){const{children:t,externalTree:a}=e,o=Ps(()=>a??new vH).current;return s.jsx(dT.Provider,{value:o,children:t})}function SH(e,t){const a=Cn(fs(e));return e instanceof a.KeyboardEvent?"keyboard":e instanceof a.FocusEvent?t||"keyboard":"pointerType"in e?e.pointerType||"keyboard":"touches"in e?"touch":e instanceof a.MouseEvent?t||(e.detail===0?"keyboard":"mouse"):""}const fT=20;let vo=[];function wy(){vo=vo.filter(e=>e.deref()?.isConnected)}function pT(e){wy(),e&&Fs(e)!=="body"&&(vo.push(new WeakRef(e)),vo.length>fT&&(vo=vo.slice(-fT)))}function mT(){return wy(),vo[vo.length-1]?.deref()}function NH(e){return e?oy(e)?e:sf(e)[0]||e:null}function hT(e){if(e.hasAttribute("tabindex")&&!e.hasAttribute("data-tabindex")||!e.getAttribute("role")?.includes("dialog"))return;const a=N1(e).filter(c=>{const d=c.getAttribute("data-tabindex")||"";return oy(c)||c.hasAttribute("data-tabindex")&&!d.startsWith("-")}),o=e.getAttribute("tabindex");a.length===0?o!=="0"&&(e.setAttribute("tabindex","0"),e.setAttribute("data-tabindex","0")):(o!=="-1"||e.hasAttribute("data-tabindex")&&e.getAttribute("data-tabindex")!=="-1")&&(e.setAttribute("tabindex","-1"),e.setAttribute("data-tabindex","-1"))}function mh(e){const{context:t,children:a,disabled:o=!1,initialFocus:c=!0,returnFocus:d=!0,restoreFocus:f=!1,modal:p=!0,closeOnFocusOut:h=!0,openInteractionType:b="",nextFocusableElement:y,previousFocusableElement:v,beforeContentFocusGuardRef:j,externalTree:w,getInsideElements:S}=e,N="rootStore"in t?t.rootStore:t,k=N.useState("open"),A=N.useState("domReferenceElement"),P=N.useState("floatingElement"),{events:R,dataRef:C}=N.context,E=Ie(()=>C.current.floatingContext?.nodeId),D=c===!1,I=Wv(A)&&D,O=Zn(c),_=Zn(d),z=Zn(b),H=Zn(k),B=Bi(w),L=oT(),F=x.useRef(!1),$=x.useRef(!1),X=x.useRef(!1),K=x.useRef(null),q=x.useRef(""),Q=x.useRef(""),G=x.useRef(null),J=x.useRef(null),Y=sa(G,j,L?.beforeInsideRef),re=sa(J,L?.afterInsideRef),M=er(),U=er(),Z=eu(),ae=L!=null,ce=Wm(P),W=Ie((ee=ce)=>ee?sf(ee):[]),ue=Ie(()=>S?.().filter(ee=>ee!=null)??[]);x.useEffect(()=>{if(o||!p)return;function ee(te){te.key==="Tab"&&Ze(ce,ir(Gt(ce)))&&W().length===0&&!I&&Cs(te)}const he=Gt(ce);return St(he,"keydown",ee)},[o,ce,p,I,W]),x.useEffect(()=>{if(o||!k)return;const ee=Gt(ce);function he(){X.current=!1}function te(le){const de=fs(le),Se=ue(),Te=Ze(P,de)||Ze(A,de)||Ze(L?.portalNode,de)||Se.some(_e=>_e===de||Ze(_e,de));X.current=!Te,Q.current=le.pointerType||"keyboard",de?.closest(`[${sT}]`)&&($.current=!0,U.start(0,()=>{$.current=!1}))}function ke(){Q.current="keyboard"}return Xa(St(ee,"pointerdown",te,!0),St(ee,"pointerup",he,!0),St(ee,"pointercancel",he,!0),St(ee,"keydown",ke,!0),he)},[o,P,A,ce,k,L,U,ue]),x.useEffect(()=>{if(o||!h)return;const ee=Gt(ce);function he(){$.current=!0,U.start(0,()=>{$.current=!1})}function te(Se){const Te=fs(Se);oy(Te)&&(K.current=Te)}function ke(Se){const Te=Se.relatedTarget,_e=Se.currentTarget,Ae=fs(Se);p&&Te==null&&Ae!=null&&Ze(P,Ae)&&pT(Ae),queueMicrotask(()=>{const oe=E(),me=N.context.triggerElements,pe=ue(),Ce=Te?.hasAttribute(df("focus-guard"))&&[G.current,J.current,L?.beforeInsideRef.current,L?.afterInsideRef.current,L?.beforeOutsideRef.current,L?.afterOutsideRef.current,kl(v),kl(y)].includes(Te),Ne=!(Ze(A,Te)||Ze(P,Te)||Ze(Te,P)||Ze(L?.portalNode,Te)||pe.some(be=>be===Te||Ze(be,Te))||me.hasMatchingElement(be=>Ze(be,Te))||Ce||B&&(uo(B.nodesRef.current,oe).find(be=>Ze(be.context?.elements.floating,Te)||Ze(be.context?.elements.domReference,Te))||p1(B.nodesRef.current,oe).find(be=>[be.context?.elements.floating,Wm(be.context?.elements.floating)].includes(Te)||be.context?.elements.domReference===Te)));if(_e===A&&ce&&hT(ce),f&&_e!==A&&!rh(Ae)&&ir(ee)===ee.body){if(pn(ce)&&(ce.focus(),f==="popup")){Z.request(()=>{ce.focus()});return}const be=W(),De=K.current,Ge=(De&&be.includes(De)?De:null)||be[be.length-1]||ce;pn(Ge)&&Ge.focus()}if(C.current.insideReactTree){C.current.insideReactTree=!1;return}(I||!p)&&Te&&Ne&&!$.current&&(I||Te!==mT())&&(F.current=!0,N.setOpen(!1,qe(xo,Se)))})}function le(){X.current||(C.current.insideReactTree=!0,M.start(0,()=>{C.current.insideReactTree=!1}))}const de=pn(A)?A:null;if(!(!P&&!de))return Xa(de&&St(de,"focusout",ke),de&&St(de,"pointerdown",he),P&&St(P,"focusin",te),P&&St(P,"focusout",ke),P&&L&&St(P,"focusout",le,!0))},[o,A,P,ce,p,B,L,N,h,f,W,I,E,C,M,U,Z,y,v,ue]),x.useEffect(()=>{if(o||!P||!k)return;const ee=Array.from(L?.portalNode?.querySelectorAll(`[${df("portal")}]`)||[]),te=(B?p1(B.nodesRef.current,E()):[]).find(_e=>Wv(_e.context?.elements.domReference||null))?.context?.elements.domReference,le=[...[P,...ee,G.current,J.current,L?.beforeOutsideRef.current,L?.afterOutsideRef.current,...ue()],te,kl(v),kl(y),I?A:null].filter(_e=>_e!=null),de=nT(le,{ariaHidden:p||I,mark:!1}),Se=[P,...ee].filter(_e=>_e!=null),Te=nT(Se);return()=>{Te(),de()}},[k,o,A,P,p,L,I,B,E,y,v,ue]),Me(()=>{if(!k||o||!pn(ce))return;q.current="",Q.current="";const ee=Gt(ce),he=ir(ee);queueMicrotask(()=>{const te=O.current,ke=typeof te=="function"?te(z.current||""):te;if(ke===void 0||ke===!1||Ze(ce,he))return;let de=null;const Se=()=>(de==null&&(de=W(ce)),de[0]||ce);let Te;ke===!0||ke===null?Te=Se():Te=kl(ke),Te=Te||Se();const _e=Ze(ce,ir(ee));fh(Te,{preventScroll:Te===ce,shouldFocus(){if(!H.current)return!1;if(_e)return!0;const Ae=ir(ee);return!(Ae!==Te&&Ze(ce,Ae))}})})},[o,k,ce,W,O,z,H]),Me(()=>{if(o||!ce)return;const ee=Gt(ce),he=ir(ee),te=z.current==null;pT(he);function ke(de){if(de.open||(q.current=SH(de.nativeEvent,Q.current)),de.reason===qs&&de.nativeEvent.type==="mouseleave"&&(F.current=!0),de.reason===ch)if(de.nested)F.current=!1;else if(Jm(de.nativeEvent)||Jv(de.nativeEvent))F.current=!1;else{let Se=!1;Gt(ce).createElement("div").focus({get preventScroll(){return Se=!0,!1}}),Se?F.current=!1:F.current=!0}}R.on("openchange",ke);function le(de){const Se=_.current;let Te=typeof Se=="function"?Se(de):Se;if(Te===void 0||Te===!1)return null;Te===null&&(Te=!0);const _e=A?.isConnected?A:null,Ae=he?.isConnected&&Fs(he)!=="body"?he:null;let oe=te?Ae||_e:_e||Ae;return oe||(oe=mT()||null),typeof Te=="boolean"?oe:kl(Te)||oe||null}return()=>{R.off("openchange",ke);const de=ir(ee),Se=ue(),Te=Ze(P,de)||Se.some(me=>me===de||Ze(me,de))||B&&uo(B.nodesRef.current,E(),!1).some(me=>Ze(me.context?.elements.floating,de)),_e=_.current,Ae=q.current,oe=le(Ae);queueMicrotask(()=>{const me=NH(oe),pe=typeof _e!="boolean";if(_e&&!F.current&&pn(me)&&(!(!pe&&me!==de&&de!==ee.body)||Te)){const Ce={preventScroll:!0};Ae==="keyboard"&&(Ce.focusVisible=!0),me.focus(Ce)}F.current=!1})}},[o,P,ce,_,z,R,B,A,E,ue]),Me(()=>{if(!jl||k||!P)return;const ee=ir(Gt(P));!pn(ee)||!Zm(ee)||Ze(P,ee)&&ee.blur()},[k,P]),Me(()=>{if(!(o||!L))return L.setFocusManagerState({modal:p,closeOnFocusOut:h,open:k,onOpenChange:N.setOpen,domReference:A}),()=>{L.setFocusManagerState(null)}},[o,L,p,k,N,h,A]),Me(()=>{if(!(o||!ce))return hT(ce),()=>{queueMicrotask(wy)}},[o,ce]);const ve=!o&&(p?!I:!0)&&(ae||p);return s.jsxs(x.Fragment,{children:[ve&&s.jsx(ru,{"data-type":"inside",ref:Y,onFocus:ee=>{if(p){const he=W();fh(he[he.length-1])}else L?.portalNode&&(F.current=!1,Jc(ee,L.portalNode)?iy(A)?.focus():kl(v??L.beforeOutsideRef)?.focus())}}),a,ve&&s.jsx(ru,{"data-type":"inside",ref:re,onFocus:ee=>{p?fh(W()[0]):L?.portalNode&&(h&&(F.current=!0),Jc(ee,L.portalNode)?C1(A)?.focus():kl(y??L.afterOutsideRef)?.focus())}})]})}function hh(e,t={}){const{enabled:a=!0,event:o="click",toggle:c=!0,ignoreMouse:d=!1,stickIfOpen:f=!0,touchOpenDelay:p=0,reason:h=go}=t,b="rootStore"in e?e.rootStore:e,y=b.context.dataRef,v=x.useRef(void 0),j=eu(),w=er(),S=x.useMemo(()=>{function N(A,P,R,C){const E=qe(h,P,R);A&&C==="touch"&&p>0?w.start(p,()=>{b.setOpen(!0,E)}):b.setOpen(A,E)}function k(A,P,R){const C=y.current.openEvent,E=b.select("domReferenceElement")!==P;return A&&E||!A||!c?!0:C&&f?!R(C.type):!1}return{onPointerDown(A){v.current=fo(A.pointerType,!0)&&Jv(A.nativeEvent)?"virtual":A.pointerType},onMouseDown(A){const P=v.current,R=A.nativeEvent,C=b.select("open");if(A.button!==0||o==="click"||fo(P,!0)&&d)return;const E=k(C,A.currentTarget,O=>O==="click"||O==="mousedown"),D=fs(R);if(Zm(D)){N(E,R,D,P);return}const I=A.currentTarget;j.request(()=>{N(E,R,I,P)})},onClick(A){if(o==="mousedown-only")return;const P=v.current;if(o==="mousedown"&&P){v.current=void 0;return}if(fo(P,!0)&&d)return;const R=b.select("open"),C=k(R,A.currentTarget,E=>E==="click"||E==="mousedown"||E==="keydown"||E==="keyup");N(C,A.nativeEvent,A.currentTarget,P)},onKeyDown(){v.current=void 0}}},[y,o,d,h,b,f,c,j,w,p]);return x.useMemo(()=>a?{reference:S}:mn,[a,S])}function kH(e,t){let a=null,o=null,c=!1;return{contextElement:e||void 0,getBoundingClientRect(){const d=e?.getBoundingClientRect()||{width:0,height:0,x:0,y:0},f=t.axis==="x"||t.axis==="both",p=t.axis==="y"||t.axis==="both",h=["mouseenter","mousemove"].includes(t.dataRef.current.openEvent?.type||"")&&t.pointerType!=="touch";let b=d.width,y=d.height,v=d.x,j=d.y;return a==null&&t.x&&f&&(a=d.x-t.x),o==null&&t.y&&p&&(o=d.y-t.y),v-=a||0,j-=o||0,b=0,y=0,!c||h?(b=t.axis==="y"?d.width:0,y=t.axis==="x"?d.height:0,v=f&&t.x!=null?t.x:v,j=p&&t.y!=null?t.y:j):c&&!h&&(y=t.axis==="x"?d.height:y,b=t.axis==="y"?d.width:b),c=!0,{width:b,height:y,x:v,y:j,top:j,right:v+b,bottom:j+y,left:v}}}}function gT(e){return e!=null&&e.clientX!=null}function CH(e,t={}){const{enabled:a=!0,axis:o="both"}=t,c="rootStore"in e?e.rootStore:e,d=c.useState("open"),f=c.useState("floatingElement"),p=c.useState("domReferenceElement"),h=c.context.dataRef,b=x.useRef(!1),y=x.useRef(null),[v,j]=x.useState(),[w,S]=x.useState([]),N=Ie(C=>{c.set("positionReference",C)}),k=Ie((C,E,D)=>{b.current||h.current.openEvent&&!gT(h.current.openEvent)||c.set("positionReference",kH(D??p,{x:C,y:E,axis:o,dataRef:h,pointerType:v}))}),A=Ie(C=>{d?y.current||(k(C.clientX,C.clientY,C.currentTarget),S([])):k(C.clientX,C.clientY,C.currentTarget)}),P=fo(v)?f:d;x.useEffect(()=>{if(!a){N(p);return}if(!P)return;function C(){y.current?.(),y.current=null}const E=Cn(f);function D(I){const O=fs(I);Ze(f,O)?C():k(I.clientX,I.clientY)}return!h.current.openEvent||gT(h.current.openEvent)?y.current=St(E,"mousemove",D):N(p),C},[P,a,f,h,p,c,k,N,w]),x.useEffect(()=>()=>{c.set("positionReference",null)},[c]),x.useEffect(()=>{a&&!f&&(b.current=!1)},[a,f]),x.useEffect(()=>{!a&&d&&(b.current=!0)},[a,d]);const R=x.useMemo(()=>{function C(E){j(E.pointerType)}return{onPointerDown:C,onPointerEnter:C,onMouseMove:A,onMouseEnter:A}},[A]);return x.useMemo(()=>a?{reference:R,trigger:R}:{},[a,R])}function TH(){return!1}function EH(e){return{escapeKey:typeof e=="boolean"?e:e?.escapeKey??!1,outsidePress:typeof e=="boolean"?e:e?.outsidePress??!0}}function pf(e,t={}){const{enabled:a=!0,escapeKey:o=!0,outsidePress:c=!0,outsidePressEvent:d="sloppy",referencePress:f=TH,bubbles:p,externalTree:h}=t,b="rootStore"in e?e.rootStore:e,y=b.useState("open"),v=b.useState("floatingElement"),{dataRef:j,events:w}=b.context,S=Bi(h),N=Ie(typeof c=="function"?c:()=>!1),k=typeof c=="function"?N:c,A=k!==!1,P=Ie(()=>d),{escapeKey:R,outsidePress:C}=EH(p),E=x.useRef(!1),D=x.useRef(!1),I=x.useRef(!1),O=x.useRef(!1),_=x.useRef(!1),z=x.useRef(""),H=x.useRef(null),B=er(),L=er(),F=Ie(()=>{L.clear(),j.current.insideReactTree=!1}),$=Ie(M=>{const U=j.current.floatingContext?.nodeId;return(S?uo(S.nodesRef.current,U):[]).some(ae=>ae.context?.open&&!ae.context.dataRef.current[M])}),X=Ie(M=>Zv(M,b.select("floatingElement"))||Zv(M,b.select("domReferenceElement"))),K=Ie(M=>{f()&&b.setOpen(!1,qe(go,M.nativeEvent))}),q=Ie(M=>{if(!y||!a||!o||M.key!=="Escape"||_.current||!R&&$("__escapeKeyBubbles"))return;const U=WB(M)?M.nativeEvent:M,Z=qe(nu,U);b.setOpen(!1,Z),Z.isCanceled||M.preventDefault(),!R&&!Z.isPropagationAllowed&&M.stopPropagation()}),Q=Ie(()=>{j.current.insideReactTree=!0,L.start(0,F)}),G=Ie(M=>{if(!y||!a||M.button!==0)return;const U=fs(M.nativeEvent);Ze(b.select("floatingElement"),U)&&(E.current||(E.current=!0,D.current=!1))}),J=Ie(M=>{!y||!a||(M.defaultPrevented||M.nativeEvent.defaultPrevented)&&E.current&&(D.current=!0)});x.useEffect(()=>{function M(U){U.open||(O.current=!1)}return w.on("openchange",M),()=>{w.off("openchange",M)}},[w]),x.useEffect(()=>{if(!y||!a)return y||(O.current=!1),F;j.current.__escapeKeyBubbles=R,j.current.__outsidePressBubbles=C;const M=new la,U=new la,Z=Gt(v);function ae(){M.clear(),_.current=!0}function ce(){M.start(jl?5:0,()=>{_.current=!1})}function W(){I.current=!0,U.start(0,()=>{I.current=!1})}function ue(){E.current=!1,D.current=!1}function ve(){const Ne=z.current,be=Ne==="pen"||!Ne?"mouse":Ne,De=P(),Ge=typeof De=="function"?De():De;return typeof Ge=="string"?Ge:Ge[be]}function ee(Ne){const be=ve();return be==="intentional"&&Ne.type!=="click"||be==="sloppy"&&Ne.type==="click"}function he(Ne){const be=j.current.floatingContext?.nodeId,De=S&&uo(S.nodesRef.current,be).some(Ge=>Zv(Ne,Ge.context?.elements.floating));return X(Ne)||De}function te(Ne){if(ee(Ne)){Ne.type!=="click"&&!X(Ne)&&(U.clear(),I.current=!1),F();return}if(j.current.insideReactTree){F();return}const be=fs(Ne),De=`[${df("inert")}]`,Ge=Rt(be)?be.getRootNode():null,Ct=Array.from((Gc(Ge)?Ge:Gt(b.select("floatingElement"))).querySelectorAll(De)),Tt=b.context.triggerElements;if(be&&(Tt.hasElement(be)||Tt.hasMatchingElement(ot=>Ze(ot,be))))return;let Mt=Rt(be)?be:null;for(;Mt&&!ro(Mt);){const ot=ao(Mt);if(ro(ot)||!Rt(ot))break;Mt=ot}if(!(Ct.length&&Rt(be)&&!XB(be)&&!Ze(be,b.select("floatingElement"))&&Ct.every(ot=>!Ze(Mt,ot)))){if(pn(be)&&!("touches"in Ne)){const ot=ro(be),Kn=yr(be),ms=/auto|scroll/,Yn=ot||ms.test(Kn.overflowX),vs=ot||ms.test(Kn.overflowY),it=Yn&&be.clientWidth>0&&be.scrollWidth>be.clientWidth,$t=vs&&be.clientHeight>0&&be.scrollHeight>be.clientHeight,Vn=Kn.direction==="rtl",rn=$t&&(Vn?Ne.offsetX<=be.offsetWidth-be.clientWidth:Ne.offsetX>be.clientWidth),nt=it&&Ne.offsetY>be.clientHeight;if(rn||nt)return}if(!he(Ne)){if(ve()==="intentional"){if(Ne.detail!==0&&!Jm(Ne)&&!O.current)return;if(I.current){U.clear(),I.current=!1;return}}typeof k=="function"&&!k(Ne)||$("__outsidePressBubbles")||(b.setOpen(!1,qe(ch,Ne)),F())}}}function ke(Ne){ve()!=="sloppy"||Ne.pointerType==="touch"||!b.select("open")||!a||X(Ne)||te(Ne)}function le(Ne){if(ve()!=="sloppy"||!b.select("open")||!a||X(Ne))return;const be=Ne.touches[0];be&&(H.current={startTime:Date.now(),startX:be.clientX,startY:be.clientY,dismissOnTouchEnd:!1,dismissOnMouseDown:!0},B.start(1e3,()=>{H.current&&(H.current.dismissOnTouchEnd=!1,H.current.dismissOnMouseDown=!1)}))}function de(Ne,be){const De=fs(Ne);if(!De)return;const Ge=St(De,Ne.type,()=>{be(Ne),Ge()})}function Se(Ne){z.current="touch",de(Ne,le)}function Te(Ne){B.clear(),Ne.type==="pointerdown"&&(Ne.button===0&&(O.current=!0),z.current=Ne.pointerType),!(Ne.type==="mousedown"&&H.current&&!H.current.dismissOnMouseDown)&&de(Ne,be=>{be.type==="pointerdown"?ke(be):te(be)})}function _e(Ne){if(Ne.type==="pointercancel"&&(O.current=!1),!E.current)return;const be=D.current;if(ue(),ve()==="intentional"){if(Ne.type==="pointercancel"){be&&W();return}if(!he(Ne)){if(be){W();return}typeof k=="function"&&!k(Ne)||(U.clear(),I.current=!0,F())}}}function Ae(Ne){if(ve()!=="sloppy"||!H.current||X(Ne))return;const be=Ne.touches[0];if(!be)return;const De=Math.abs(be.clientX-H.current.startX),Ge=Math.abs(be.clientY-H.current.startY),Ct=Math.sqrt(De*De+Ge*Ge);Ct>5&&(H.current.dismissOnTouchEnd=!0),Ct>10&&(te(Ne),B.clear(),H.current=null)}function oe(Ne){de(Ne,Ae)}function me(Ne){ve()!=="sloppy"||!H.current||X(Ne)||(H.current.dismissOnTouchEnd&&te(Ne),B.clear(),H.current=null)}function pe(Ne){de(Ne,me)}const Ce=Xa(o&&Xa(St(Z,"keydown",q),St(Z,"compositionstart",ae),St(Z,"compositionend",ce)),A&&Xa(St(Z,"click",Te,!0),St(Z,"pointerdown",Te,!0),St(Z,"pointerup",_e,!0),St(Z,"pointercancel",_e,!0),St(Z,"mousedown",Te,!0),St(Z,"mouseup",_e,!0),St(Z,"touchstart",Se,{capture:!0,passive:!0}),St(Z,"touchmove",oe,{capture:!0,passive:!0}),St(Z,"touchend",pe,{capture:!0,passive:!0})));return()=>{Ce(),M.clear(),U.clear(),ue(),I.current=!1,F()}},[j,v,o,A,k,y,a,R,C,q,F,P,$,X,S,b,B]);const Y=x.useMemo(()=>({onKeyDown:q,onPointerDown:K,onClick:K}),[q,K]),re=x.useMemo(()=>({onKeyDown:q,onPointerDown:J,onMouseDown:J,onClickCapture:Q,onMouseDownCapture(M){Q(),G(M)},onPointerDownCapture(M){Q(),G(M)},onMouseUpCapture:Q,onTouchEndCapture:Q,onTouchMoveCapture:Q}),[q,Q,G,J]);return x.useMemo(()=>a?{reference:Y,floating:re,trigger:Y}:{},[a,Y,re])}function xT(e,t,a){let{reference:o,floating:c}=e;const d=aa(t),f=ny(t),p=ty(f),h=ra(t),b=d==="y",y=o.x+o.width/2-c.width/2,v=o.y+o.height/2-c.height/2,j=o[p]/2-c[p]/2;let w;switch(h){case"top":w={x:y,y:o.y-c.height};break;case"bottom":w={x:y,y:o.y+o.height};break;case"right":w={x:o.x+o.width,y:v};break;case"left":w={x:o.x-c.width,y:v};break;default:w={x:o.x,y:o.y}}const S=Nl(t);return S&&(w[f]+=j*(S==="end"?1:-1)*(a&&b?-1:1)),w}async function AH(e,t){var a;t===void 0&&(t={});const{x:o,y:c,platform:d,rects:f,elements:p,strategy:h}=e,{boundary:b="clippingAncestors",rootBoundary:y="viewport",elementContext:v="floating",altBoundary:j=!1,padding:w=0}=po(t,e),S=x1(w),k=p[j?v==="floating"?"reference":"floating":v],A=nf(await d.getClippingRect({element:(a=await(d.isElement==null?void 0:d.isElement(k)))==null||a?k:k.contextElement||await(d.getDocumentElement==null?void 0:d.getDocumentElement(p.floating)),boundary:b,rootBoundary:y,strategy:h})),P=v==="floating"?{x:o,y:c,width:f.floating.width,height:f.floating.height}:f.reference,R=await(d.getOffsetParent==null?void 0:d.getOffsetParent(p.floating)),C=await(d.isElement==null?void 0:d.isElement(R))&&await(d.getScale==null?void 0:d.getScale(R))||{x:1,y:1},E=nf(d.convertOffsetParentRelativeRectToViewportRelativeRect?await d.convertOffsetParentRelativeRectToViewportRelativeRect({elements:p,rect:P,offsetParent:R,strategy:h}):P);return{top:(A.top-E.top+S.top)/C.y,bottom:(E.bottom-A.bottom+S.bottom)/C.y,left:(A.left-E.left+S.left)/C.x,right:(E.right-A.right+S.right)/C.x}}const RH=50,OH=async(e,t,a)=>{const{placement:o="bottom",strategy:c="absolute",middleware:d=[],platform:f}=a,p=f.detectOverflow?f:{...f,detectOverflow:AH},h=await(f.isRTL==null?void 0:f.isRTL(t));let b=await f.getElementRects({reference:e,floating:t,strategy:c}),{x:y,y:v}=xT(b,o,h),j=o,w=0;const S={};for(let N=0;NF<=0)){var H,B;const F=(((H=d.flip)==null?void 0:H.index)||0)+1,$=I[F];if($&&(!(v==="alignment"?P!==aa($):!1)||z.every(q=>aa(q.placement)===P?q.overflows[0]>0:!0)))return{data:{index:F,overflows:z},reset:{placement:$}};let X=(B=z.filter(K=>K.overflows[0]<=0).sort((K,q)=>K.overflows[1]-q.overflows[1])[0])==null?void 0:B.placement;if(!X)switch(w){case"bestFit":{var L;const K=(L=z.filter(q=>{if(D){const Q=aa(q.placement);return Q===P||Q==="y"}return!0}).map(q=>[q.placement,q.overflows.filter(Q=>Q>0).reduce((Q,G)=>Q+G,0)]).sort((q,Q)=>q[1]-Q[1])[0])==null?void 0:L[0];K&&(X=K);break}case"initialPlacement":X=p;break}if(c!==X)return{reset:{placement:X}}}return{}}}},bT=new Set(["left","top"]);async function DH(e,t){const{placement:a,platform:o,elements:c}=e,d=await(o.isRTL==null?void 0:o.isRTL(c.floating)),f=ra(a),p=Nl(a),h=aa(a)==="y",b=bT.has(f)?-1:1,y=d&&h?-1:1,v=po(t,e);let{mainAxis:j,crossAxis:w,alignmentAxis:S}=typeof v=="number"?{mainAxis:v,crossAxis:0,alignmentAxis:null}:{mainAxis:v.mainAxis||0,crossAxis:v.crossAxis||0,alignmentAxis:v.alignmentAxis};return p&&typeof S=="number"&&(w=p==="end"?S*-1:S),h?{x:w*y,y:j*b}:{x:j*b,y:w*y}}const IH=function(e){return e===void 0&&(e=0),{name:"offset",options:e,async fn(t){var a,o;const{x:c,y:d,placement:f,middlewareData:p}=t,h=await DH(t,e);return f===((a=p.offset)==null?void 0:a.placement)&&(o=p.arrow)!=null&&o.alignmentOffset?{}:{x:c+h.x,y:d+h.y,data:{...h,placement:f}}}}},PH=function(e){return e===void 0&&(e={}),{name:"shift",options:e,async fn(t){const{x:a,y:o,placement:c,platform:d}=t,{mainAxis:f=!0,crossAxis:p=!1,limiter:h={fn:P=>{let{x:R,y:C}=P;return{x:R,y:C}}},...b}=po(e,t),y={x:a,y:o},v=await d.detectOverflow(t,b),j=aa(c),w=ey(j);let S=y[w],N=y[j];const k=(P,R)=>m1(R+v[P==="y"?"top":"left"],R,R-v[P==="y"?"bottom":"right"]);f&&(S=k(w,S)),p&&(N=k(j,N));const A=h.fn({...t,[w]:S,[j]:N});return{...A,data:{x:A.x-a,y:A.y-o,enabled:{[w]:f,[j]:p}}}}}},_H=function(e){return e===void 0&&(e={}),{options:e,fn(t){var a,o;const{x:c,y:d,placement:f,rects:p,middlewareData:h}=t,{offset:b=0,mainAxis:y=!0,crossAxis:v=!0}=po(e,t),j={x:c,y:d},w=aa(f),S=ey(w);let N=j[S],k=j[w];const A=po(b,t),P=typeof A=="number"?{mainAxis:A,crossAxis:0}:{mainAxis:(a=A.mainAxis)!=null?a:0,crossAxis:(o=A.crossAxis)!=null?o:0};if(y){const E=S==="y"?"height":"width",D=p.reference[S]-p.floating[E]+P.mainAxis,I=p.reference[S]+p.reference[E]-P.mainAxis;NI&&(N=I)}if(v){var R,C;const E=S==="y"?"width":"height",D=bT.has(ra(f)),I=p.reference[w]-p.floating[E]+(D&&((R=h.offset)==null?void 0:R[w])||0)+(D?0:P.crossAxis),O=p.reference[w]+p.reference[E]+(D?0:((C=h.offset)==null?void 0:C[w])||0)-(D?P.crossAxis:0);kO&&(k=O)}return{[S]:N,[w]:k}}}},LH=function(e){return e===void 0&&(e={}),{name:"size",options:e,async fn(t){const{placement:a,rects:o,platform:c,elements:d}=t,{apply:f=()=>{},...p}=po(e,t),h=await c.detectOverflow(t,p),b=ra(a),y=Nl(a),v=aa(a)==="y",{width:j,height:w}=o.floating;let S,N;b==="top"||b==="bottom"?(S=b,N=y===(await(c.isRTL==null?void 0:c.isRTL(d.floating))?"start":"end")?"left":"right"):(N=b,S=y==="end"?"top":"bottom");const k=w-h.top-h.bottom,A=j-h.left-h.right,P=Wc(w-h[S],k),R=Wc(j-h[N],A),C=t.middlewareData.shift,E=!C;let D=P,I=R;C!=null&&C.enabled.x&&(I=A),C!=null&&C.enabled.y&&(D=k),E&&!y&&(v?I=j-2*wl(h.left,h.right):D=w-2*wl(h.top,h.bottom)),await f({...t,availableWidth:I,availableHeight:D});const O=await c.getDimensions(d.floating);return j!==O.width||w!==O.height?{reset:{rects:!0}}:{}}}};function vT(e){const t=yr(e);let a=parseFloat(t.width)||0,o=parseFloat(t.height)||0;const c=pn(e),d=c?e.offsetWidth:a,f=c?e.offsetHeight:o,p=eh(a)!==d||eh(o)!==f;return p&&(a=d,o=f),{width:a,height:o,$:p}}function Sy(e){return Rt(e)?e:e.contextElement}function lu(e){const t=Sy(e);if(!pn(t))return Sl(1);const a=t.getBoundingClientRect(),{width:o,height:c,$:d}=vT(t);let f=(d?eh(a.width):a.width)/o,p=(d?eh(a.height):a.height)/c;return(!f||!Number.isFinite(f))&&(f=1),(!p||!Number.isFinite(p))&&(p=1),{x:f,y:p}}const zH=Sl(0);function yT(e){const t=Cn(e);return!Rv()||!t.visualViewport?zH:{x:t.visualViewport.offsetLeft,y:t.visualViewport.offsetTop}}function BH(e,t,a){return t===void 0&&(t=!1),!!a&&t&&a===Cn(e)}function Hi(e,t,a,o){t===void 0&&(t=!1),a===void 0&&(a=!1);const c=e.getBoundingClientRect(),d=Sy(e);let f=Sl(1);t&&(o?Rt(o)&&(f=lu(o)):f=lu(e));const p=BH(d,a,o)?yT(d):Sl(0);let h=(c.left+p.x)/f.x,b=(c.top+p.y)/f.y,y=c.width/f.x,v=c.height/f.y;if(d&&o){const j=Cn(d),w=Rt(o)?Cn(o):o;let S=j,N=Ov(S);for(;N&&w!==S;){const k=lu(N),A=N.getBoundingClientRect(),P=yr(N),R=A.left+(N.clientLeft+parseFloat(P.paddingLeft))*k.x,C=A.top+(N.clientTop+parseFloat(P.paddingTop))*k.y;h*=k.x,b*=k.y,y*=k.x,v*=k.y,h+=R,b+=C,S=Cn(N),N=Ov(S)}}return nf({width:y,height:v,x:h,y:b})}function gh(e,t){const a=Um(e).scrollLeft;return t?t.left+a:Hi(bl(e)).left+a}function jT(e,t){const a=e.getBoundingClientRect(),o=a.left+t.scrollLeft-gh(e,a),c=a.top+t.scrollTop;return{x:o,y:c}}function HH(e){let{elements:t,rect:a,offsetParent:o,strategy:c}=e;const d=c==="fixed",f=bl(o),p=t?Hm(t.floating):!1;if(o===f||p&&d)return a;let h={scrollLeft:0,scrollTop:0},b=Sl(1);const y=Sl(0),v=pn(o);if((v||!d)&&((Fs(o)!=="body"||Qd(f))&&(h=Um(o)),v)){const w=Hi(o);b=lu(o),y.x=w.x+o.clientLeft,y.y=w.y+o.clientTop}const j=f&&!v&&!d?jT(f,h):Sl(0);return{width:a.width*b.x,height:a.height*b.y,x:a.x*b.x-h.scrollLeft*b.x+y.x+j.x,y:a.y*b.y-h.scrollTop*b.y+y.y+j.y}}function UH(e){return e.getClientRects?Array.from(e.getClientRects()):[]}function VH(e){const t=Um(e),a=e.ownerDocument.body,o=wl(e.scrollWidth,e.clientWidth,a.scrollWidth,a.clientWidth),c=wl(e.scrollHeight,e.clientHeight,a.scrollHeight,a.clientHeight);let d=-t.scrollLeft+gh(e);const f=-t.scrollTop;return yr(a).direction==="rtl"&&(d+=wl(e.clientWidth,a.clientWidth)-o),{width:o,height:c,x:d,y:f}}const FH=25;function $H(e,t,a){a===void 0&&(a="viewport");const o=a==="layoutViewport",c=Cn(e),d=bl(e),f=c.visualViewport;let p=d.clientWidth,h=d.clientHeight,b=0,y=0;if(f){const j=!Rv()||t==="fixed";o?j||(b=-f.offsetLeft,y=-f.offsetTop):(p=f.width,h=f.height,j&&(b=f.offsetLeft,y=f.offsetTop))}if(gh(d)<=0){const j=d.ownerDocument,w=j.body,S=getComputedStyle(w),N=j.compatMode==="CSS1Compat"&&parseFloat(S.marginLeft)+parseFloat(S.marginRight)||0,k=Math.abs(d.clientWidth-w.clientWidth-N),A=getComputedStyle(d).scrollbarGutter==="stable both-edges"?k/2:k;A<=FH&&(p-=A)}return{width:p,height:h,x:b,y}}function qH(e,t){const a=Hi(e,!0,t==="fixed"),o=a.top+e.clientTop,c=a.left+e.clientLeft,d=lu(e),f=e.clientWidth*d.x,p=e.clientHeight*d.y,h=c*d.x,b=o*d.y;return{width:f,height:p,x:h,y:b}}function wT(e,t,a){let o;if(t==="viewport"||t==="layoutViewport")o=$H(e,a,t);else if(t==="document")o=VH(bl(e));else if(Rt(t))o=qH(t,a);else{const c=yT(e);o={x:t.x-c.x,y:t.y-c.y,width:t.width,height:t.height}}return nf(o)}function GH(e,t){const a=t.get(e);if(a)return a;let o=Kc(e,[],!1).filter(p=>Rt(p)&&Fs(p)!=="body"),c=null;const d=yr(e).position==="fixed";let f=d?ao(e):e;for(;Rt(f)&&!ro(f);){const p=yr(f),h=Av(f),b=c?c.position:d?"fixed":"";!h&&(b==="fixed"||b==="absolute"&&p.position==="static")?o=o.filter(v=>v!==f):c=p,f=ao(f)}return t.set(e,o),o}function KH(e){let{element:t,boundary:a,rootBoundary:o,strategy:c}=e;const f=[...a==="clippingAncestors"?Hm(t)?[]:GH(t,this._c):[].concat(a),o],p=wT(t,f[0],c);let h=p.top,b=p.right,y=p.bottom,v=p.left;for(let j=1;j{p(!1,1e-7)},1e3)}I=!1}try{o=new IntersectionObserver(O,{...D,root:d.ownerDocument})}catch{o=new IntersectionObserver(O,D)}o.observe(e)}const h=Cn(e),b=()=>p(a);return h.addEventListener("resize",b),p(!0),()=>{h.removeEventListener("resize",b),f()}}function TT(e,t,a,o){o===void 0&&(o={});const{ancestorScroll:c=!0,ancestorResize:d=!0,elementResize:f=typeof ResizeObserver=="function",layoutShift:p=typeof IntersectionObserver=="function",animationFrame:h=!1}=o,b=Sy(e),y=c||d?[...b?Kc(b):[],...t?Kc(t):[]]:[];y.forEach(A=>{c&&A.addEventListener("scroll",a),d&&A.addEventListener("resize",a)});const v=b&&p?WH(b,a,d):null;let j=-1,w=null;f&&(w=new ResizeObserver(A=>{let[P]=A;P&&P.target===b&&w&&t&&(w.unobserve(t),cancelAnimationFrame(j),j=requestAnimationFrame(()=>{var R;(R=w)==null||R.observe(t)})),a()}),b&&!h&&w.observe(b),t&&w.observe(t));let S,N=h?Hi(e):null;h&&k();function k(){const A=Hi(e);N&&!CT(N,A)&&a(),N=A,S=requestAnimationFrame(k)}return a(),()=>{var A;y.forEach(P=>{c&&P.removeEventListener("scroll",a),d&&P.removeEventListener("resize",a)}),v?.(),(A=w)==null||A.disconnect(),w=null,h&&cancelAnimationFrame(S)}}const JH=IH,eU=PH,tU=MH,nU=LH,sU=_H,rU=(e,t,a)=>{const o=new Map,c=a??{},d={...kT,...c.platform,_c:o};return OH(e,t,{...c,platform:d})};var aU=typeof document<"u",lU=function(){},xh=aU?x.useLayoutEffect:lU;function bh(e,t){if(e===t)return!0;if(typeof e!=typeof t)return!1;if(typeof e=="function"&&e.toString()===t.toString())return!0;let a,o,c;if(e&&t&&typeof e=="object"){if(Array.isArray(e)){if(a=e.length,a!==t.length)return!1;for(o=a;o--!==0;)if(!bh(e[o],t[o]))return!1;return!0}if(c=Object.keys(e),a=c.length,a!==Object.keys(t).length)return!1;for(o=a;o--!==0;)if(!{}.hasOwnProperty.call(t,c[o]))return!1;for(o=a;o--!==0;){const d=c[o];if(!(d==="_owner"&&e.$$typeof)&&!bh(e[d],t[d]))return!1}return!0}return e!==e&&t!==t}function ET(e){return typeof window>"u"?1:(e.ownerDocument.defaultView||window).devicePixelRatio||1}function AT(e,t){const a=ET(e);return Math.round(t*a)/a}function ky(e){const t=x.useRef(e);return xh(()=>{t.current=e}),t}function oU(e){e===void 0&&(e={});const{placement:t="bottom",strategy:a="absolute",middleware:o=[],platform:c,elements:{reference:d,floating:f}={},transform:p=!0,whileElementsMounted:h,open:b}=e,[y,v]=x.useState({x:0,y:0,strategy:a,placement:t,middlewareData:{},isPositioned:!1}),[j,w]=x.useState(o);bh(j,o)||w(o);const[S,N]=x.useState(null),[k,A]=x.useState(null),P=x.useCallback(q=>{q!==D.current&&(D.current=q,N(q))},[]),R=x.useCallback(q=>{q!==I.current&&(I.current=q,A(q))},[]),C=d||S,E=f||k,D=x.useRef(null),I=x.useRef(null),O=x.useRef(y),_=h!=null,z=ky(h),H=ky(c),B=ky(b),L=x.useCallback(()=>{if(!D.current||!I.current)return;const q={placement:t,strategy:a,middleware:j};H.current&&(q.platform=H.current),rU(D.current,I.current,q).then(Q=>{const G={...Q,isPositioned:B.current!==!1};F.current&&!bh(O.current,G)&&(O.current=G,Ts.flushSync(()=>{v(G)}))})},[j,t,a,H,B]);xh(()=>{b===!1&&O.current.isPositioned&&(O.current.isPositioned=!1,v(q=>({...q,isPositioned:!1})))},[b]);const F=x.useRef(!1);xh(()=>(F.current=!0,()=>{F.current=!1}),[]),xh(()=>{if(C&&(D.current=C),E&&(I.current=E),C&&E){if(z.current)return z.current(C,E,L);L()}},[C,E,L,z,_]);const $=x.useMemo(()=>({reference:D,floating:I,setReference:P,setFloating:R}),[P,R]),X=x.useMemo(()=>({reference:C,floating:E}),[C,E]),K=x.useMemo(()=>{const q={position:a,left:0,top:0};if(!X.floating)return q;const Q=AT(X.floating,y.x),G=AT(X.floating,y.y);return p?{...q,transform:"translate("+Q+"px, "+G+"px)",...ET(X.floating)>=1.5&&{willChange:"transform"}}:{position:a,left:Q,top:G}},[a,p,X.floating,y.x,y.y]);return x.useMemo(()=>({...y,update:L,refs:$,elements:X,floatingStyles:K}),[y,L,$,X,K])}const iU=(e,t)=>{const a=JH(e);return{name:a.name,fn:a.fn,options:[e,t]}},cU=(e,t)=>{const a=eU(e);return{name:a.name,fn:a.fn,options:[e,t]}},uU=(e,t)=>({fn:sU(e).fn,options:[e,t]}),dU=(e,t)=>{const a=tU(e);return{name:a.name,fn:a.fn,options:[e,t]}},fU=(e,t)=>{const a=nU(e);return{name:a.name,fn:a.fn,options:[e,t]}};var Cy={exports:{}},Ty={};var RT;function pU(){if(RT)return Ty;RT=1;var e=Xd();function t(v,j){return v===j&&(v!==0||1/v===1/j)||v!==v&&j!==j}var a=typeof Object.is=="function"?Object.is:t,o=e.useState,c=e.useEffect,d=e.useLayoutEffect,f=e.useDebugValue;function p(v,j){var w=j(),S=o({inst:{value:w,getSnapshot:j}}),N=S[0].inst,k=S[1];return d(function(){N.value=w,N.getSnapshot=j,h(N)&&k({inst:N})},[v,w,j]),c(function(){return h(N)&&k({inst:N}),v(function(){h(N)&&k({inst:N})})},[v]),f(w),w}function h(v){var j=v.getSnapshot;v=v.value;try{var w=j();return!a(v,w)}catch{return!0}}function b(v,j){return j()}var y=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?b:p;return Ty.useSyncExternalStore=e.useSyncExternalStore!==void 0?e.useSyncExternalStore:y,Ty}var OT;function MT(){return OT||(OT=1,Cy.exports=pU()),Cy.exports}var Ey=MT(),Ay={exports:{}},Ry={};var DT;function mU(){if(DT)return Ry;DT=1;var e=Xd(),t=MT();function a(b,y){return b===y&&(b!==0||1/b===1/y)||b!==b&&y!==y}var o=typeof Object.is=="function"?Object.is:a,c=t.useSyncExternalStore,d=e.useRef,f=e.useEffect,p=e.useMemo,h=e.useDebugValue;return Ry.useSyncExternalStoreWithSelector=function(b,y,v,j,w){var S=d(null);if(S.current===null){var N={hasValue:!1,value:null};S.current=N}else N=S.current;S=p(function(){function A(D){if(!P){if(P=!0,R=D,D=j(D),w!==void 0&&N.hasValue){var I=N.value;if(w(I,D))return C=I}return C=D}if(I=C,o(R,D))return I;var O=j(D);return w!==void 0&&w(I,O)?(R=D,I):(R=D,C=O)}var P=!1,R,C,E=v===void 0?null:v;return[function(){return A(y())},E===null?void 0:function(){return A(E())}]},[y,v,j,w]);var k=c(b,S[0],S[1]);return f(function(){N.hasValue=!0,N.value=k},[k]),h(k),k},Ry}var IT;function hU(){return IT||(IT=1,Ay.exports=mU()),Ay.exports}var gU=hU();const xU=zv(19)?vU:yU;function PT(e,t,a,o,c){return xU(e,t,a,o,c)}function bU(e,t,a,o,c){const d=x.useCallback(()=>t(e.getSnapshot(),a,o,c),[e,t,a,o,c]);return Ey.useSyncExternalStore(e.subscribe,d,d)}aH({before(e){e.syncIndex=0,e.didInitialize||(e.syncTick=1,e.syncHooks=[],e.didChangeStore=!0,e.getSnapshot=()=>{let t=!1;for(let a=0;a0&&(e.didChangeStore&&(e.didChangeStore=!1,e.subscribe=t=>{const a=new Set;for(const c of e.syncHooks)a.add(c.store);const o=[];for(const c of a)o.push(c.subscribe(t));return()=>{for(const c of o)c()}}),Ey.useSyncExternalStore(e.subscribe,e.getSnapshot,e.getSnapshot))}});function vU(e,t,a,o,c){const d=rH();if(!d)return bU(e,t,a,o,c);const f=d.syncIndex;d.syncIndex+=1;let p;return d.didInitialize?(p=d.syncHooks[f],(p.store!==e||p.selector!==t||!Object.is(p.a1,a)||!Object.is(p.a2,o)||!Object.is(p.a3,c))&&(p.store!==e&&(d.didChangeStore=!0),p.store=e,p.selector=t,p.a1=a,p.a2=o,p.a3=c,p.value=t(e.getSnapshot(),a,o,c))):(p={store:e,selector:t,a1:a,a2:o,a3:c,value:t(e.getSnapshot(),a,o,c)},d.syncHooks.push(p)),p.value}function yU(e,t,a,o,c){return gU.useSyncExternalStoreWithSelector(e.subscribe,e.getSnapshot,e.getSnapshot,d=>t(d,a,o,c))}class jU{constructor(t){cn(this,"subscribe",t=>(this.listeners.add(t),()=>{this.listeners.delete(t)}));cn(this,"getSnapshot",()=>this.state);this.state=t,this.listeners=new Set,this.updateTick=0}static create(t){return new this(t)}setState(t){if(this.state===t)return;this.state=t,this.updateTick+=1;const a=this.updateTick;for(const o of this.listeners){if(a!==this.updateTick)return;o(t)}}update(t){for(const a in t)if(!Object.is(this.state[a],t[a])){this.setState({...this.state,...t});return}}set(t,a){Object.is(this.state[t],a)||this.setState({...this.state,[t]:a})}notifyAll(){const t={...this.state};this.setState(t)}use(t,a,o,c){return PT(this,t,a,o,c)}}class ou extends jU{constructor(t,a={},o){super(t),this.context=a,this.selectors=o}useSyncedValue(t,a){x.useDebugValue(t);const o=this;Me(()=>{o.state[t]!==a&&o.set(t,a)},[o,t,a])}useSyncedValueWithCleanup(t,a){const o=this;Me(()=>(o.state[t]!==a&&o.set(t,a),()=>{o.set(t,void 0)}),[o,t,a])}useSyncedValues(t){const a=this,o=Object.values(t);Me(()=>{a.update(t)},[a,...o])}useControlledProp(t,a){x.useDebugValue(t);const o=this,c=a!==void 0;Me(()=>{c&&!Object.is(o.state[t],a)&&o.setState({...o.state,[t]:a})},[o,t,a,c])}select(t,a,o,c){const d=this.selectors[t];return d(this.state,a,o,c)}useState(t,a,o,c){return x.useDebugValue(t),PT(this,this.selectors[t],a,o,c)}useContextCallback(t,a){x.useDebugValue(t);const o=Ie(a??Kt);this.context[t]=o}useStateSetter(t){const a=x.useRef(void 0);return a.current===void 0&&(a.current=o=>{this.set(t,o)}),a.current}observe(t,a){let o;typeof t=="function"?o=t:o=this.selectors[t];let c=o(this.state);return a(c,c,this),this.subscribe(d=>{const f=o(d);if(!Object.is(c,f)){const p=c;c=f,a(f,p,this)}})}}const wU={open:e=>e.open,transitionStatus:e=>e.transitionStatus,domReferenceElement:e=>e.domReferenceElement,referenceElement:e=>e.positionReference??e.referenceElement,floatingElement:e=>e.floatingElement,floatingId:e=>e.floatingId};class Oy extends ou{constructor(a){const{syncOnly:o,nested:c,onOpenChange:d,triggerElements:f,...p}=a;super({...p,positionReference:p.referenceElement,domReferenceElement:p.referenceElement},{onOpenChange:d,dataRef:{current:{}},events:cT(),nested:c,triggerElements:f},wU);cn(this,"syncOpenEvent",(a,o)=>{(!a||!this.state.open||o!=null&&JB(o))&&(this.context.dataRef.current.openEvent=a?o:void 0)});cn(this,"dispatchOpenChange",(a,o)=>{this.syncOpenEvent(a,o.event);const c={open:a,reason:o.reason,nativeEvent:o.event,nested:this.context.nested,triggerElement:o.trigger};this.context.events.emit("openchange",c)});cn(this,"setOpen",(a,o)=>{if(this.syncOnly){this.context.onOpenChange?.(a,o);return}this.dispatchOpenChange(a,o),this.context.onOpenChange?.(a,o)});this.syncOnly=o}}function SU(e){const{popupStore:t,treatPopupAsFloatingElement:a=!1,floatingRootContext:o,floatingId:c,nested:d,onOpenChange:f}=e,p=t.useState("open"),h=t.useState("activeTriggerElement"),b=t.useState(a?"popupElement":"positionerElement"),y=t.context.triggerElements,v=f,j=x.useRef(null);o===void 0&&j.current===null&&(j.current=new Oy({open:p,transitionStatus:void 0,referenceElement:h,floatingElement:b,triggerElements:y,onOpenChange:v,floatingId:c,syncOnly:!0,nested:d}));const w=o??j.current;return t.useSyncedValue("floatingId",c),Me(()=>{const S={open:p,floatingId:c,referenceElement:h,floatingElement:b};Rt(h)&&(S.domReferenceElement=h),w.state.positionReference===w.state.referenceElement&&(S.positionReference=h),w.update(S)},[p,c,h,b,w]),w.context.onOpenChange=v,w.context.nested=d,w}const mf={tabIndex:-1,[qv]:""};function _T(e){return t=>t==="touch"?e.current:!0}function My(e,t=!1){const a=Jd(),o=au()!=null,c=Ps(()=>e(a,o)).current;return SU({popupStore:c,treatPopupAsFloatingElement:t,floatingRootContext:c.state.floatingRootContext,floatingId:a,nested:o,onOpenChange:c.setOpen}),c}function Dy({handle:e,store:t}){return Me(()=>e.attachStore(t),[e,t]),null}function LT(e){const t=e.context.triggerElements.size;e.select("open")&&e.state.triggerCount!==t&&e.set("triggerCount",t)}function NU(e,t){const a=x.useRef(null);return Ie(o=>{const c=a.current;if(c!==null){if(c.element===o&&c.store===t&&c.id===e)return;a.current=null;const d=c.store;d.context.triggerElements.getById(c.id)===c.element&&(d.context.triggerElements.delete(c.id),LT(d))}o!==null&&e!==void 0&&(a.current={store:t,id:e,element:o},t.context.triggerElements.add(e,o),LT(t))})}function Iy(e,t,a,o=!1){let c=e.preventUnmountingOnClose;t?c=!1:o&&(c=!0);const d=a?.id??null;let f=e.activeTriggerId,p=e.activeTriggerElement;return(d||t)&&(f=d,p=a??null),{open:t,preventUnmountingOnClose:c,activeTriggerId:f,activeTriggerElement:p}}function zT(e){let t=!1;return e.preventUnmountOnClose=()=>{t=!0},()=>t}function kU(e,t,a,o={}){const c=a.reason,d=c===qs,f=t&&c===ih,p=!t&&(c===go||c===nu),h=zT(a);if(e.context.onOpenChange?.(t,a),a.isCanceled)return;o.onBeforeDispatch?.(),e.state.floatingRootContext.dispatchOpenChange(t,a);const b=()=>{const y=Iy(e.state,t,a.trigger,h()),v={...o.extraState,...y};f?v.instantType="focus":p?v.instantType="dismiss":d&&(v.instantType=void 0),e.update(v)};d?Ts.flushSync(b):b()}function BT(e,t,a,o){const c=a.useState("isMountedByTrigger",e),d=NU(e,a),f=Ie(h=>{const b=a.select("open"),y=a.select("activeTriggerId");if(y===e){const v={activeTriggerElement:h,...b?o:null};a.update(v);return}if(y==null&&b){const v={activeTriggerId:e??null,activeTriggerElement:h,...o};a.update(v)}}),p=Ie(h=>{d(h),h&&f(h)});return Me(()=>(p(t.current),()=>p(null)),[p,t,a,e]),Me(()=>{if(c){const h={activeTriggerElement:t.current,...o};a.update(h)}},[c,a,t,...Object.values(o)]),{registerTrigger:p,isMountedByThisTrigger:c}}function Py(e,t={}){const{closeOnActiveTriggerUnmount:a=!1}=t,o=x.useRef(null),c=e.useState("open"),d=e.useState("triggerCount"),f=e.useState("activeTriggerId"),p=e.useState("activeTriggerElement");Me(()=>{if(!c){o.current=null,e.state.triggerCount!==0&&e.set("triggerCount",0);return}const h=e.context.triggerElements.size,b={};e.state.triggerCount!==h&&(b.triggerCount=h);const y=e.select("activeTriggerId");let v=null;if(y){const j=e.context.triggerElements.getById(y);if(j)o.current=y,j!==e.state.activeTriggerElement&&(b.activeTriggerElement=j);else{for(const[w,S]of e.context.triggerElements.entries())if(S===e.state.activeTriggerElement){b.activeTriggerId=w,b.activeTriggerElement=S,o.current=w;break}b.activeTriggerId===void 0&&(o.current===y?v=y:o.current=null)}}else o.current=null;if(!v&&!y&&h===1){const j=e.context.triggerElements.entries().next();if(!j.done){const[w,S]=j.value;b.activeTriggerId=w,b.activeTriggerElement=S,o.current=w}}(b.triggerCount!==void 0||b.activeTriggerId!==void 0||b.activeTriggerElement!==void 0)&&e.update(b),v&&a&&queueMicrotask(()=>{if(e.select("open")&&e.select("activeTriggerId")===v&&!e.context.triggerElements.getById(v)){const j=qe(Qn);e.setOpen(!1,j),j.isCanceled||e.update({activeTriggerId:null,activeTriggerElement:null})}})},[c,e,d,f,p,a])}function _y(e,t,a,o){const{mounted:c,setMounted:d,transitionStatus:f}=Oi(e,!1,!1,o),p=t.useState("preventUnmountingOnClose"),h=e?!1:p;t.useSyncedValues({mounted:c,transitionStatus:f,preventUnmountingOnClose:h});const b=Ie(()=>{d(!1),t.update({activeTriggerId:null,activeTriggerElement:null,mounted:!1,preventUnmountingOnClose:!1}),a?.(),t.context.onOpenChangeComplete?.(!1)});return oa({enabled:c&&!e&&!h,open:e,ref:t.context.popupRef,onComplete(){e||b()}}),{forceUnmount:b,transitionStatus:f}}function Ly(e,t){e.useSyncedValues(t),Me(()=>()=>{e.update({activeTriggerProps:mn,inactiveTriggerProps:mn,popupProps:mn})},[e])}function HT(e,t){Me(()=>{!t&&e.state.openMethod!==null&&e.set("openMethod",null)},[t,e]),Me(()=>()=>{e.state.openMethod!==null&&e.set("openMethod",null)},[e])}class vh{constructor(){this.idMap=new Map}add(t,a){this.idMap.set(t,a)}delete(t){this.idMap.delete(t)}hasElement(t){for(const a of this.idMap.values())if(a===t)return!0;return!1}hasMatchingElement(t){for(const a of this.idMap.values())if(t(a))return!0;return!1}getById(t){return this.idMap.get(t)}entries(){return this.idMap.entries()}elements(){return this.idMap.values()}get size(){return this.idMap.size}}function zy(e,t,a=!1){return{open:!1,openProp:void 0,mounted:!1,transitionStatus:void 0,floatingRootContext:new Oy({open:!1,transitionStatus:void 0,floatingElement:null,referenceElement:null,triggerElements:e,floatingId:t,syncOnly:!0,nested:a,onOpenChange:void 0}),floatingId:t,triggerCount:0,preventUnmountingOnClose:!1,payload:void 0,activeTriggerId:null,activeTriggerElement:null,triggerIdProp:void 0,popupElement:null,positionerElement:null,activeTriggerProps:mn,inactiveTriggerProps:mn,popupProps:mn}}const hf=e=>e.triggerIdProp??e.activeTriggerId,By=e=>e.openProp??e.open,UT=e=>(e.popupElement?.id??e.floatingId)||void 0;function VT(e,t){return t!==void 0&&By(e)&&hf(e)===t}function CU(e,t){return VT(e,t)?!0:t!==void 0&&By(e)&&hf(e)==null&&e.triggerCount===1}const Hy={open:By,mounted:e=>e.mounted,transitionStatus:e=>e.transitionStatus,floatingRootContext:e=>e.floatingRootContext,triggerCount:e=>e.triggerCount,preventUnmountingOnClose:e=>e.preventUnmountingOnClose,payload:e=>e.payload,activeTriggerId:hf,activeTriggerElement:e=>e.mounted?e.activeTriggerElement:null,popupId:UT,isTriggerActive:(e,t)=>t!==void 0&&hf(e)===t,isOpenedByTrigger:(e,t)=>VT(e,t),isMountedByTrigger:(e,t)=>t!==void 0&&hf(e)===t&&e.mounted,triggerProps:(e,t)=>t?e.activeTriggerProps:e.inactiveTriggerProps,triggerPopupId:(e,t)=>CU(e,t)?UT(e):void 0,popupProps:e=>e.popupProps,popupElement:e=>e.popupElement,positionerElement:e=>e.positionerElement};function FT(e){const t=x.useCallback(o=>e===void 0?Kt:e.subscribeStore(o),[e]),a=x.useCallback(()=>e===void 0?void 0:e.store,[e]);return Ey.useSyncExternalStore(t,a,()=>e?.serverStore)}function $T(e){const{open:t=!1,onOpenChange:a,elements:o={}}=e,c=Jd(),d=au()!=null,f=Ps(()=>new Oy({open:t,transitionStatus:void 0,onOpenChange:a,referenceElement:o.reference??null,floatingElement:o.floating??null,triggerElements:new vh,floatingId:c,syncOnly:!1,nested:d})).current;return Me(()=>{const p={open:t,floatingId:c};o.reference!==void 0&&(p.referenceElement=o.reference,p.domReferenceElement=Rt(o.reference)?o.reference:null),o.floating!==void 0&&(p.floatingElement=o.floating),f.update(p)},[t,c,o.reference,o.floating,f]),f.context.onOpenChange=a,f.context.nested=d,f}function TU(e){return EU(e,e.rootContext)}function EU(e,t){const{nodeId:a,externalTree:o}=e,c=t.useState("referenceElement"),d=t.useState("floatingElement"),f=t.useState("domReferenceElement"),p=t.useState("open"),h=t.useState("floatingId"),[b,y]=x.useState(null),[v,j]=x.useState(void 0),[w,S]=x.useState(void 0),N=x.useRef(null),k=Bi(o),A=x.useMemo(()=>({reference:c,floating:d,domReference:f}),[c,d,f]),P=oU({...e,elements:{...A,...b&&{reference:b}}}),R=Rt(v)?v:null,C=w===void 0?t.state.floatingElement:w;t.useSyncedValue("referenceElement",v??null),t.useSyncedValue("domReferenceElement",v===void 0?f:R),t.useSyncedValue("floatingElement",C);const E=x.useCallback(H=>{const B=Rt(H)?{getBoundingClientRect:()=>H.getBoundingClientRect(),getClientRects:()=>H.getClientRects(),contextElement:H}:H;y(B),P.refs.setReference(B)},[P.refs]),D=x.useCallback(H=>{(Rt(H)||H===null)&&(N.current=H,j(H)),(Rt(P.refs.reference.current)||P.refs.reference.current===null||H!==null&&!Rt(H))&&P.refs.setReference(H)},[P.refs,j]),I=x.useCallback(H=>{S(H),P.refs.setFloating(H)},[P.refs]),O=x.useMemo(()=>({...P.refs,setReference:D,setFloating:I,setPositionReference:E,domReference:N}),[P.refs,D,I,E]),_=x.useMemo(()=>({...P.elements,domReference:f}),[P.elements,f]),z=x.useMemo(()=>({...P,dataRef:t.context.dataRef,open:p,onOpenChange:t.setOpen,events:t.context.events,floatingId:h,refs:O,elements:_,nodeId:a,rootStore:t}),[P,O,_,a,t,p,h]);return Me(()=>{f&&(N.current=f)},[f]),Me(()=>{t.context.dataRef.current.floatingContext=z;const H=k?.nodesRef.current.find(B=>B.id===a);H&&(H.context=z)}),x.useMemo(()=>({...P,context:z,refs:O,elements:_,rootStore:t}),[P,O,_,z,t])}const Uy=i1&&jl;function AU(e,t={}){const{enabled:a=!0,delay:o}=t,c="rootStore"in e?e.rootStore:e,{events:d,dataRef:f}=c.context,p=x.useRef(!1),h=x.useRef(null),b=x.useRef(!0),y=er();x.useEffect(()=>{const j=c.select("domReferenceElement");if(!a)return;const w=Cn(j);function S(){const A=c.select("domReferenceElement");!c.select("open")&&pn(A)&&A===ir(Gt(A))&&(p.current=!0,h.current=A)}function N(){b.current=!0}function k(){b.current=!1}return Xa(St(w,"blur",S),Uy&&St(w,"keydown",N,!0),Uy&&St(w,"pointerdown",k,!0))},[c,a]),x.useEffect(()=>{if(!a)return;function j(w){if(w.reason===go||w.reason===nu){const S=c.select("domReferenceElement");Rt(S)&&(h.current=S,p.current=!0)}}return d.on("openchange",j),()=>{d.off("openchange",j)}},[d,a,c]);const v=x.useMemo(()=>{function j(){p.current=!1,h.current=null}return{onMouseLeave(){j()},onFocus(w){const S=w.currentTarget;if(p.current){if(h.current===S)return;j()}const N=fs(w.nativeEvent);if(Rt(N)){if(Uy&&!w.relatedTarget){if(!b.current&&!Zm(N))return}else if(!ZB(N))return}const k=Qm(w.relatedTarget,c.context.triggerElements),{nativeEvent:A,currentTarget:P}=w,R=typeof o=="function"?o():o;if(c.select("open")&&k||R===0||R===void 0){c.setOpen(!0,qe(ih,A,P));return}y.start(R,()=>{p.current||c.setOpen(!0,qe(ih,A,P))})},onBlur(w){j();const S=w.relatedTarget,N=w.nativeEvent,k=Rt(S)&&S.hasAttribute(df("focus-guard"))&&S.getAttribute("data-type")==="outside";y.start(0,()=>{const A=c.select("domReferenceElement"),P=ir(Gt(A));!S&&P===A||Ze(f.current.floatingContext?.refs.floating.current,P)||Ze(A,P)||k||Qm(S??P,c.context.triggerElements)||c.setOpen(!1,qe(ih,N))})}}},[f,o,c,y]);return x.useMemo(()=>a?{reference:v,trigger:v}:{},[a,v])}class Vy{constructor(){cn(this,"dispose",()=>{this.openChangeTimeout.clear(),this.restTimeout.clear()});cn(this,"disposeEffect",()=>this.dispose);this.pointerType=void 0,this.interactedInside=!1,this.handler=void 0,this.blockMouseMove=!0,this.performedPointerEventsMutation=!1,this.pointerEventsScopeElement=null,this.pointerEventsReferenceElement=null,this.pointerEventsFloatingElement=null,this.restTimeoutPending=!1,this.openChangeTimeout=new la,this.restTimeout=new la,this.handleCloseOptions=void 0}static create(){return new Vy}}const yh=new WeakMap;function jh(e){if(!e.performedPointerEventsMutation)return;const t=e.pointerEventsScopeElement;t&&yh.get(t)===e&&(e.pointerEventsScopeElement?.style.removeProperty("pointer-events"),e.pointerEventsReferenceElement?.style.removeProperty("pointer-events"),e.pointerEventsFloatingElement?.style.removeProperty("pointer-events"),yh.delete(t)),e.performedPointerEventsMutation=!1,e.pointerEventsScopeElement=null,e.pointerEventsReferenceElement=null,e.pointerEventsFloatingElement=null}function qT(e,t){const{scopeElement:a,referenceElement:o,floatingElement:c}=t,d=yh.get(a);d&&d!==e&&jh(d),jh(e),e.performedPointerEventsMutation=!0,e.pointerEventsScopeElement=a,e.pointerEventsReferenceElement=o,e.pointerEventsFloatingElement=c,yh.set(a,e),a.style.pointerEvents="none",o.style.pointerEvents="auto",c.style.pointerEvents="auto"}function Fy(e){const t=e.context.dataRef.current,a=Ps(()=>t.hoverInteractionState??Vy.create()).current;return t.hoverInteractionState||(t.hoverInteractionState=a),cy(t.hoverInteractionState.disposeEffect),t.hoverInteractionState}function GT(e,t={}){const{enabled:a=!0,closeDelay:o=0,nodeId:c}=t,d="rootStore"in e?e.rootStore:e,f=d.useState("open"),p=d.useState("floatingElement"),h=d.useState("domReferenceElement"),{dataRef:b}=d.context,y=Bi(),v=au(),j=Fy(d),w=er(),S=Ie(()=>X1(b.current.openEvent?.type,j.interactedInside)),N=Ie(()=>iH(b.current.openEvent?.type)),k=Ie(()=>{jh(j)});Me(()=>{f||(j.pointerType=void 0,j.restTimeoutPending=!1,j.interactedInside=!1,k())},[f,j,k]),x.useEffect(()=>k,[k]),Me(()=>{if(a&&f&&j.handleCloseOptions?.blockPointerEvents&&N()&&Rt(h)&&p){const A=h,P=p,R=Gt(p),C=y?.nodesRef.current.find(O=>O.id===v)?.context?.elements.floating;C&&(C.style.pointerEvents="");const E=j.pointerEventsScopeElement!==P?j.pointerEventsScopeElement:null,D=C!==P?C:null,I=j.handleCloseOptions?.getScope?.()??E??D??A.closest("[data-rootownerid]")??R.body;return qT(j,{scopeElement:I,referenceElement:A,floatingElement:P}),()=>{k()}}},[a,f,h,p,j,N,y,v,k]),x.useEffect(()=>{if(!a)return;function A(){return!!(y&&v&&uo(y.nodesRef.current,v).length>0)}function P(O){const _=zi(o,"close",j.pointerType),z=()=>{d.setOpen(!1,qe(qs,O)),y?.events.emit("floating.closed",O)};_?j.openChangeTimeout.start(_,z):(j.openChangeTimeout.clear(),z())}function R(O){const _=fs(O);if(!QB(_)){j.interactedInside=!1;return}j.interactedInside=_?.closest("[aria-haspopup]")!=null}function C(){j.openChangeTimeout.clear(),w.clear(),y?.events.off("floating.closed",D),k()}function E(O){if(A()&&y){y.events.on("floating.closed",D);return}if(Qm(O.relatedTarget,d.context.triggerElements))return;const _=b.current.floatingContext?.nodeId??c,z=O.relatedTarget;if(!(y&&_&&Rt(z)&&uo(y.nodesRef.current,_,!1).some(B=>Ze(B.context?.elements.floating,z)))){if(j.handler){j.handler(O);return}k(),N()&&!S()&&P(O)}}function D(O){!y||!v||A()||w.start(0,()=>{y.events.off("floating.closed",D),d.setOpen(!1,qe(qs,O)),y.events.emit("floating.closed",O)})}const I=p;return Xa(I&&St(I,"mouseenter",C),I&&St(I,"mouseleave",E),I&&St(I,"pointerdown",R,!0),()=>{y?.events.off("floating.closed",D)})},[a,p,d,b,o,c,N,S,k,j,y,v,w])}const RU={current:null};function KT(e,t={}){const{enabled:a=!0,delay:o=0,handleClose:c=null,mouseOnly:d=!1,restMs:f=0,move:p=!0,triggerElementRef:h=RU,externalTree:b,isActiveTrigger:y=!0,getHandleCloseContext:v,isClosing:j,shouldOpen:w,guardStaleOpen:S=!1}=t,N="rootStore"in e?e.rootStore:e,{dataRef:k,events:A}=N.context,P=Bi(b),R=Fy(N),C=x.useRef(!1),E=Zn(c),D=Zn(o),I=Zn(f),O=Zn(a),_=Zn(w),z=Zn(j),H=Ie(()=>X1(k.current.openEvent?.type,R.interactedInside)),B=Ie(()=>_.current?.()!==!1),L=Ie((X,K,q)=>{const Q=N.context.triggerElements;if(Q.hasElement(K))return!X||!Ze(X,K);if(!Rt(q))return!1;const G=q;return Q.hasMatchingElement(J=>Ze(J,G))&&(!X||!Ze(X,G))}),F=Ie(()=>{if(!R.handler)return;Gt(N.select("domReferenceElement")).removeEventListener("mousemove",R.handler),R.handler=void 0}),$=Ie(()=>{jh(R)});return y&&(R.handleCloseOptions=c?.__options),x.useEffect(()=>F,[F]),x.useEffect(()=>{if(!a)return;function X(K){K.open?C.current=!1:(C.current=K.reason===qs,F(),R.openChangeTimeout.clear(),R.restTimeout.clear(),R.blockMouseMove=!0,R.restTimeoutPending=!1)}return A.on("openchange",X),()=>{A.off("openchange",X)}},[a,A,R,F]),x.useEffect(()=>{if(!a)return;function X(Y,re=!0){const M=zi(D.current,"close",R.pointerType);M?R.openChangeTimeout.start(M,()=>{N.setOpen(!1,qe(qs,Y)),P?.events.emit("floating.closed",Y)}):re&&(R.openChangeTimeout.clear(),N.setOpen(!1,qe(qs,Y)),P?.events.emit("floating.closed",Y))}const K=h.current??(y?N.select("domReferenceElement"):null);if(!Rt(K))return;function q(Y){if(R.openChangeTimeout.clear(),R.blockMouseMove=!1,d&&!fo(R.pointerType))return;const re=Y1(I.current),M=zi(D.current,"open",R.pointerType),U=fs(Y),Z=Y.currentTarget??null,ae=N.select("domReferenceElement");let ce=Z;if(Rt(U)&&!N.context.triggerElements.hasElement(U)){for(const de of N.context.triggerElements.elements())if(Ze(de,U)){ce=de;break}}Rt(Z)&&Rt(ae)&&!N.context.triggerElements.hasElement(Z)&&Ze(Z,ae)&&(ce=ae);const W=ce==null?!1:L(ae,ce,U),ue=N.select("open"),ve=z.current?.()??N.select("transitionStatus")==="ending",ee=!ue&&ve&&C.current,he=!W&&Rt(ce)&&Rt(ae)&&Ze(ae,ce)&&ee,te=re>0&&!M,ke=W&&(ue||ee)||he,le=!ue||W;if(ke){B()&&N.setOpen(!0,qe(qs,Y,ce));return}te||(M?R.openChangeTimeout.start(M,()=>{le&&B()&&N.setOpen(!0,qe(qs,Y,ce))}):le&&B()&&N.setOpen(!0,qe(qs,Y,ce)))}function Q(Y){if(H()){$();return}F();const re=N.select("domReferenceElement"),M=Gt(re);R.restTimeout.clear(),R.restTimeoutPending=!1;const U=k.current.floatingContext??v?.();if(Qm(Y.relatedTarget,N.context.triggerElements))return;if(E.current&&U){N.select("open")||R.openChangeTimeout.clear();const ae=h.current;R.handler=E.current({...U,tree:P,x:Y.clientX,y:Y.clientY,onClose(){$(),F(),O.current&&!H()&&ae===N.select("domReferenceElement")&&X(Y,!0)}}),M.addEventListener("mousemove",R.handler),R.handler(Y);return}(R.pointerType!=="touch"||!Ze(N.select("floatingElement"),Y.relatedTarget))&&X(Y)}function G(Y){Ze(K,Y.relatedTarget)||(R.openChangeTimeout.clear(),R.restTimeout.clear(),R.restTimeoutPending=!1)}const J=S?St(K,"mouseout",G):void 0;return p?Xa(St(K,"mousemove",q,{once:!0}),St(K,"mouseenter",q),St(K,"mouseleave",Q),J):Xa(St(K,"mouseenter",q),St(K,"mouseleave",Q),J)},[F,$,k,D,N,a,E,R,y,L,H,d,p,I,h,P,O,v,z,B,S]),x.useMemo(()=>{if(!a)return;function X(K){R.pointerType=K.pointerType}return{onPointerDown:X,onPointerEnter:X,onMouseMove(K){const{nativeEvent:q}=K,Q=K.currentTarget,G=N.select("domReferenceElement"),J=N.select("open"),Y=L(G,Q,K.target);if(d&&!fo(R.pointerType))return;if(J&&Y&&R.handleCloseOptions?.blockPointerEvents){const U=N.select("floatingElement");if(U){const Z=R.handleCloseOptions?.getScope?.()??Q.ownerDocument.body;qT(R,{scopeElement:Z,referenceElement:Q,floatingElement:U})}}const re=Y1(I.current);if(J&&!Y||re===0||!Y&&R.restTimeoutPending&&K.movementX**2+K.movementY**2<2)return;R.restTimeout.clear();function M(){if(R.restTimeoutPending=!1,H())return;const U=N.select("open");!R.blockMouseMove&&(!U||Y)&&B()&&N.setOpen(!0,qe(qs,q,Q))}R.pointerType==="touch"?Ts.flushSync(()=>{M()}):Y&&J?M():(R.restTimeoutPending=!0,R.restTimeout.start(re,M))}}},[a,R,H,L,d,N,I,B])}const OU="Escape";function YT(e){return jl&&e.movementX===0&&e.movementY===0}function wh(e,t,a){switch(e){case"vertical":return t;case"horizontal":return a;default:return t||a}}function Sh(e,t){return wh(t,e===Gv||e===Xm,e===Ei||e===Ai)}function $y(e,t,a){return wh(t,e===Xm,a?e===Ei:e===Ai)||e==="Enter"||e===" "||e===""}function MU(e,t,a){return wh(t,a?e===Ei:e===Ai,e===Xm)}function DU(e,t,a,o){const c=a?e===Ai:e===Ei,d=e===Gv;return t==="both"||t==="horizontal"&&o?e===OU:wh(t,c,d)}function XT(e,t){const{listRef:a,activeIndex:o,onNavigate:c=()=>{},enabled:d=!0,selectedIndex:f=null,allowEscape:p=!1,loopFocus:h=!1,nested:b=!1,rtl:y=!1,virtual:v=!1,focusItemOnOpen:j="auto",focusItemOnHover:w=!0,openOnArrowKeyDown:S=!0,disabledIndices:N=void 0,orientation:k="vertical",parentOrientation:A,id:P,resetOnPointerLeave:R=!0,externalTree:C,grid:E}=t,D=E!=null,I="rootStore"in e?e.rootStore:e,O=I.useState("open"),_=I.useState("floatingElement"),z=I.useState("domReferenceElement"),H=I.context.dataRef,B=Wm(_),L=Wv(z),F=Zn(B),$=au(),X=Bi(C),K=x.useRef(j),q=x.useRef(f??-1),Q=x.useRef(null),G=x.useRef(!0),J=Ie(pe=>{c(q.current===-1?null:q.current,pe)}),Y=x.useRef(!!_),re=x.useRef(O),M=x.useRef(!1),U=x.useRef(!1),Z=x.useRef(null),ae=Zn(N),ce=Zn(O),W=Zn(f),ue=Zn(R),ve=eu(),ee=eu(),he=Ie(()=>{function pe(De){v?X?.events.emit("virtualfocus",De):Z.current=fh(De,{sync:M.current,preventScroll:!0})}const Ce=a.current[q.current],Ne=U.current;Ce&&pe(Ce),(M.current?De=>De():De=>ve.request(De))(()=>{const De=a.current[q.current]||Ce;if(!De)return;Ce||pe(De),Te&&(Ne||!G.current)&&De.scrollIntoView?.({block:"nearest",inline:"nearest"})})});Me(()=>{H.current.orientation=k},[H,k]),Me(()=>{d&&(O&&_?(q.current=f??-1,K.current&&f!=null&&(U.current=!0,J())):Y.current&&(q.current=-1,J()))},[d,O,_,f,J]),Me(()=>{if(d){if(!O){M.current=!1;return}if(_)if(o==null){if(M.current=!1,W.current!=null)return;if(Y.current&&(q.current=-1,he()),(!re.current||!Y.current)&&K.current&&(Q.current!=null||K.current===!0&&Q.current==null)){let pe=0;const Ce=()=>{a.current[0]==null?(pe<2&&(pe?be=>ee.request(be):queueMicrotask)(Ce),pe+=1):(q.current=Q.current==null||$y(Q.current,k,y)||b?sh(a):ry(a),Q.current=null,J())};Ce()}}else mo(a.current,o)||(q.current=o,he(),U.current=!1)}},[d,O,_,o,W,b,a,k,y,J,he,ee]),Me(()=>{if(!d||_||!X||v||!Y.current)return;const pe=X.nodesRef.current,Ce=pe.find(De=>De.id===$)?.context?.elements.floating,Ne=ir(Gt(z??Ce??null)),be=pe.some(De=>De.context&&Ze(De.context.elements.floating,Ne));Ce&&!be&&G.current&&Ce.focus({preventScroll:!0})},[d,_,z,X,$,v]),Me(()=>{re.current=O,Y.current=!!_}),Me(()=>{O||(Q.current=null,K.current=j)},[O,j]);const te=o!=null,ke=Ie(pe=>{if(!ce.current)return;const Ce=a.current.indexOf(pe.currentTarget);Ce!==-1&&(q.current!==Ce||o!==Ce)&&(q.current=Ce,J(pe))}),le=Ie(()=>A??X?.nodesRef.current.find(pe=>pe.id===$)?.context?.dataRef?.current.orientation),de=Ie(()=>sh(a,ae.current)),Se=Ie(pe=>{if(G.current=!1,M.current=!0,pe.which===229||!ce.current&&pe.currentTarget===F.current)return;if(b&&DU(pe.key,k,y,D)){Sh(pe.key,le())||Cs(pe),I.setOpen(!1,qe(fy,pe.nativeEvent)),pn(z)&&(v?X?.events.emit("virtualfocus",z):z.focus());return}const Ce=q.current,Ne=sh(a,N),be=ry(a,N);if(L||(pe.key==="Home"&&(Cs(pe),q.current=Ne,J(pe)),pe.key==="End"&&(Cs(pe),q.current=be,J(pe))),E!=null){const De=E(pe,q.current,a,k,h,y,N,Ne,be);if(De!=null&&(q.current=De,J(pe)),k==="both")return}if(Sh(pe.key,k)){if(Cs(pe),O&&!v&&ir(pe.currentTarget.ownerDocument)===pe.currentTarget){q.current=$y(pe.key,k,y)?Ne:be,J(pe);return}$y(pe.key,k,y)?h?Ce>=be?p&&Ce!==a.current.length?q.current=-1:(M.current=!1,q.current=Ne):q.current=_s(a.current,{startingIndex:Ce,disabledIndices:N}):q.current=Math.min(be,_s(a.current,{startingIndex:Ce,disabledIndices:N})):h?Ce<=Ne?p&&Ce!==-1?q.current=a.current.length:(M.current=!1,q.current=be):q.current=_s(a.current,{startingIndex:Ce,decrement:!0,disabledIndices:N}):q.current=Math.max(Ne,_s(a.current,{startingIndex:Ce,decrement:!0,disabledIndices:N})),mo(a.current,q.current)&&(q.current=-1),J(pe)}}),Te=x.useMemo(()=>({onFocus(Ce){M.current=!0,ke(Ce)},onClick:({currentTarget:Ce})=>Ce.focus({preventScroll:!0}),onMouseMove(Ce){YT(Ce)||(M.current=!0,U.current=!1,w&&ke(Ce))},onPointerLeave(Ce){if(!ce.current||!G.current||Ce.pointerType==="touch")return;M.current=!0;const Ne=Ce.relatedTarget;if(!(!w||a.current.includes(Ne))&&ue.current&&(Z.current?.(),Z.current=null,q.current=-1,J(Ce),!v)){const be=F.current,De=ir(Gt(be));be&&Ze(be,De)&&be.focus({preventScroll:!0})}}}),[ke,ce,F,w,a,J,ue,v]),_e=x.useMemo(()=>v&&O&&te&&{"aria-activedescendant":`${P}-${o}`},[v,O,te,P,o]),Ae=x.useMemo(()=>({...L?{}:_e,onKeyDown(pe){if(pe.key==="Tab"&&pe.shiftKey&&O&&!v){const Ce=fs(pe.nativeEvent);if(Ce&&!Ze(F.current,Ce))return;Cs(pe),I.setOpen(!1,qe(xo,pe.nativeEvent)),pn(z)&&z.focus();return}Se(pe)},onPointerMove(pe){YT(pe)||(G.current=!0)}}),[_e,Se,F,L,I,O,v,z]),oe=x.useMemo(()=>{function pe(be){I.setOpen(!0,qe(fy,be.nativeEvent,be.currentTarget))}function Ce(be){j==="auto"&&Jm(be.nativeEvent)&&(K.current=!v)}function Ne(be){K.current=j,j==="auto"&&Jv(be.nativeEvent)&&(K.current=!0)}return{onKeyDown(be){const De=I.select("open");G.current=!1;const Ge=be.key.startsWith("Arrow"),Ct=MU(be.key,le(),y),Tt=Sh(be.key,k),Mt=(b?Ct:Tt)||be.key==="Enter"||be.key.trim()==="";if(v&&De)return Se(be);if(!(!De&&!S&&Ge)){if(Mt){const ot=Sh(be.key,le());Q.current=b&&ot?null:be.key}if(b){Ct&&(Cs(be),De?(q.current=de(),J(be)):pe(be));return}Tt&&(W.current!=null&&(q.current=W.current),Cs(be),!De&&S?pe(be):Se(be),De&&J(be))}},onFocus(be){I.select("open")&&!v&&(q.current=-1,J(be))},onPointerDown:Ne,onPointerEnter:Ne,onMouseDown:Ce,onClick:Ce}},[Se,j,de,b,J,I,S,k,le,y,W,v]),me=x.useMemo(()=>({..._e,...oe}),[_e,oe]);return x.useMemo(()=>d?{reference:me,floating:Ae,item:Te,trigger:oe}:{},[d,me,Ae,oe,Te])}function QT(e,t){const{listRef:a,elementsRef:o,activeIndex:c,onMatch:d,disabledIndices:f,onTyping:p,enabled:h=!0,resetMs:b=750,selectedIndex:y=null}=t,v="rootStore"in e?e.rootStore:e,j=v.useState("open"),w=er(),S=x.useRef(""),N=x.useRef(y??c??-1),k=x.useRef(null),A=Ie(C=>{function E(F){return o?.current[F]}function D(F){const $=E(F);return $&&!rh($)||$?.matches(":disabled")?!1:f==null||!ho(Ir,F,f)}function I(F,$,X=0){if(F.length===0)return-1;const K=(X%F.length+F.length)%F.length,q=$.toLowerCase();for(let Q=0;Q0&&C.key===" "&&(Cs(C),p?.(!0)),S.current.length>0&&S.current[0]!==" "&&I(O,S.current)===-1&&C.key!==" "&&p?.(!1),O==null||C.key.length!==1||C.ctrlKey||C.metaKey||C.altKey)return;j&&C.key!==" "&&(Cs(C),p?.(!0));const _=S.current==="";_&&(N.current=y??c??-1),O.every((F,$)=>F&&D($)?F[0]?.toLowerCase()!==F[1]?.toLowerCase():!0)&&S.current===C.key&&(S.current="",N.current=k.current),S.current+=C.key,w.start(b,()=>{S.current="",N.current=k.current,p?.(!1)});const B=((_?y??c??-1:N.current)??0)+1,L=I(O,S.current,B);L!==-1?(d?.(L),k.current=L):C.key!==" "&&(S.current="",p?.(!1))}),P=Ie(C=>{const E=C.relatedTarget,D=v.select("domReferenceElement"),I=v.select("floatingElement");Ze(D,E)||Ze(I,E)||(w.clear(),S.current="",N.current=k.current,p?.(!1))});Me(()=>{!j&&y!==null||(w.clear(),k.current=null,S.current!==""&&(S.current=""))},[j,y,w]);const R=x.useMemo(()=>({onKeyDown:A,onBlur:P}),[A,P]);return x.useMemo(()=>h?{reference:R,floating:R}:{},[h,R])}const ZT=.1,IU=ZT*ZT,yn=.5;function Nh(e,t,a,o,c,d){return o>=t!=d>=t&&e<=(c-a)*(t-o)/(d-o)+a}function kh(e,t,a,o,c,d,f,p,h,b){let y=!1;return Nh(e,t,a,o,c,d)&&(y=!y),Nh(e,t,c,d,f,p)&&(y=!y),Nh(e,t,f,p,h,b)&&(y=!y),Nh(e,t,h,b,a,o)&&(y=!y),y}function PU(e,t,a){return e>=a.x&&e<=a.x+a.width&&t>=a.y&&t<=a.y+a.height}function Ch(e,t,a,o,c,d){const f=Math.min(a,c),p=Math.max(a,c),h=Math.min(o,d),b=Math.max(o,d);return e>=f&&e<=p&&t>=h&&t<=b}function WT(e={}){const{blockPointerEvents:t=!1}=e,a=new la,o=({x:c,y:d,placement:f,elements:p,onClose:h,nodeId:b,tree:y})=>{const v=f?.split("-")[0];let j=!1,w=null,S=null,N=typeof performance<"u"?performance.now():0;function k(P,R){const C=performance.now(),E=C-N;if(w===null||S===null||E===0)return w=P,S=R,N=C,!1;const D=P-w,I=R-S,O=D*D+I*I,_=E*E*IU;return w=P,S=R,N=C,O<_}function A(){a.clear(),h()}return function(R){a.clear();const C=p.domReference,E=p.floating;if(!C||!E||v==null||c==null||d==null)return;const{clientX:D,clientY:I}=R,O=fs(R),_=R.type==="mouseleave",z=Ze(E,O),H=Ze(C,O);if(z&&(j=!0,!_))return;if(H&&(j=!1,!_)){j=!0;return}if(_&&Rt(R.relatedTarget)&&Ze(E,R.relatedTarget))return;function B(){return!!(y&&uo(y.nodesRef.current,b).length>0)}function L(){B()||A()}if(B())return;const F=C.getBoundingClientRect(),$=E.getBoundingClientRect(),X=c>$.right-$.width/2,K=d>$.bottom-$.height/2,q=$.width>F.width,Q=$.height>F.height,G=(q?F:$).left,J=(q?F:$).right,Y=(Q?F:$).top,re=(Q?F:$).bottom;if(v==="top"&&d>=F.bottom-1||v==="bottom"&&d<=F.top+1||v==="left"&&c>=F.right-1||v==="right"&&c<=F.left+1){L();return}let M=!1;switch(v){case"top":M=Ch(D,I,G,F.top+1,J,$.bottom-1);break;case"bottom":M=Ch(D,I,G,$.top+1,J,F.bottom-1);break;case"left":M=Ch(D,I,$.right-1,re,F.left+1,Y);break;case"right":M=Ch(D,I,F.right-1,re,$.left+1,Y);break}if(M)return;if(j&&!PU(D,I,F)){L();return}if(!_&&k(D,I)){L();return}let U=!1;switch(v){case"top":{const Z=q?yn/2:yn*4,ae=q||X?c+Z:c-Z,ce=q?c-Z:X?c+Z:c-Z,W=d+yn+1,ue=X||q?$.bottom-yn:$.top,ve=X?q?$.bottom-yn:$.top:$.bottom-yn;U=kh(D,I,ae,W,ce,W,$.left,ue,$.right,ve);break}case"bottom":{const Z=q?yn/2:yn*4,ae=q||X?c+Z:c-Z,ce=q?c-Z:X?c+Z:c-Z,W=d-yn,ue=X||q?$.top+yn:$.bottom,ve=X?q?$.top+yn:$.bottom:$.top+yn;U=kh(D,I,ae,W,ce,W,$.left,ue,$.right,ve);break}case"left":{const Z=Q?yn/2:yn*4,ae=Q||K?d+Z:d-Z,ce=Q?d-Z:K?d+Z:d-Z,W=c+yn+1,ue=K||Q?$.right-yn:$.left,ve=K?Q?$.right-yn:$.left:$.right-yn;U=kh(D,I,ue,$.top,ve,$.bottom,W,ae,W,ce);break}case"right":{const Z=Q?yn/2:yn*4,ae=Q||K?d+Z:d-Z,ce=Q?d-Z:K?d+Z:d-Z,W=c-yn,ue=K||Q?$.left+yn:$.right,ve=K?Q?$.left+yn:$.right:$.left+yn;U=kh(D,I,W,ae,W,ce,ue,$.top,ve,$.bottom);break}}U?j||a.start(40,L):L()}};return o.__options={...e,blockPointerEvents:t},o}const _U={...Hy,disabled:e=>e.disabled,instantType:e=>e.instantType,isInstantPhase:e=>e.isInstantPhase,trackCursorAxis:e=>e.trackCursorAxis,disableHoverablePopup:e=>e.disableHoverablePopup,lastOpenChangeReason:e=>e.openChangeReason,closeOnClick:e=>e.closeOnClick,closeDelay:e=>e.closeDelay,adaptiveOrigin:e=>e.adaptiveOrigin};class LU extends ou{constructor(a,o,c){const d=new vh;super(zU(a,d,o,c),BU(d),_U);cn(this,"setOpen",(a,o)=>{kU(this,a,o,{extraState:{openChangeReason:o.reason}})})}cancelPendingOpen(a){this.state.floatingRootContext.dispatchOpenChange(!1,qe(go,a))}}function zU(e,t,a,o=!1){return{...zy(t,a,o),disabled:!1,instantType:void 0,isInstantPhase:!1,trackCursorAxis:"none",disableHoverablePopup:!1,openChangeReason:null,closeOnClick:!0,closeDelay:0,adaptiveOrigin:void 0,...e}}function BU(e){return{popupRef:x.createRef(),onOpenChange:void 0,onOpenChangeComplete:void 0,triggerElements:e}}const HU=uh(function(t){const{disabled:a=!1,defaultOpen:o=!1,open:c,disableHoverablePopup:d=!1,trackCursorAxis:f="none",actionsRef:p,onOpenChange:h,onOpenChangeComplete:b,handle:y,triggerId:v,defaultTriggerId:j=null,children:w}=t,S=My((H,B)=>new LU({open:o,openProp:c,activeTriggerId:j,triggerIdProp:v},H,B));S.useControlledProp("openProp",c),S.useControlledProp("triggerIdProp",v),S.useContextCallback("onOpenChange",h),S.useContextCallback("onOpenChangeComplete",b);const N=S.useState("open"),k=!a&&N,A=S.useState("activeTriggerId"),P=S.useState("mounted"),R=S.useState("payload");S.useSyncedValues({trackCursorAxis:f,disableHoverablePopup:d,disabled:a}),Py(S,{closeOnActiveTriggerUnmount:!0});const{forceUnmount:C,transitionStatus:E}=_y(k,S),D=S.useState("isInstantPhase"),I=S.useState("instantType"),O=S.useState("lastOpenChangeReason"),_=x.useRef(null);Me(()=>{N&&a&&S.setOpen(!1,qe(N4))},[N,a,S]),Me(()=>{E==="ending"&&O===Qn||E!=="ending"&&D?(I!=="delay"&&(_.current=I),S.set("instantType","delay")):_.current!==null&&(S.set("instantType",_.current),_.current=null)},[E,D,O,I,S]),Me(()=>{k&&A==null&&S.set("payload",void 0)},[S,A,k]),x.useImperativeHandle(p,()=>({unmount:C,close:()=>S.setOpen(!1,qe(py))}),[C,S]);const z=k||P||!a&&f!=="none";return s.jsxs(K1.Provider,{value:S,children:[y&&s.jsx(Dy,{handle:y,store:S}),z&&s.jsx(UU,{store:S,disabled:a,trackCursorAxis:f}),typeof w=="function"?w({payload:R}):w]})});function UU({store:e,disabled:t,trackCursorAxis:a}){const o=e.useState("floatingRootContext"),c=pf(o,{enabled:!t,referencePress:()=>e.select("closeOnClick")}),d=CH(o,{enabled:!t&&a!=="none",axis:a==="none"?void 0:a}),f=x.useMemo(()=>ta(d.reference,c.reference),[d.reference,c.reference]);return Ly(e,{activeTriggerProps:f,inactiveTriggerProps:f,popupProps:c.floating??mn}),null}const JT=x.createContext(void 0);function VU(){return x.useContext(JT)}const FU=600,eE="data-base-ui-tooltip-trigger";function tE(e){if("composedPath"in e){const a=e.composedPath();for(let o=0;ok.select("transitionStatus")==="ending",shouldOpen(){return!G.current}}),ae=AU(C,{enabled:!X}).reference,ce=he=>{const te=G.current,ke=tE(he),le=U(ke),de=E.current,Se=de&&ke&&Ze(de,ke);if(le&&k.select("open")&&k.select("lastOpenChangeReason")===qs){k.setOpen(!1,qe(qs,he));return}if(te&&!le&&Se&&!K.current&&!k.select("open")&&de&&fo(Y.current)){const Te=()=>{!G.current&&!K.current&&!k.select("open")&&k.setOpen(!0,qe(qs,he,de))},_e=re();_e===0?(J.clear(),Te()):J.start(_e,Te)}},W=k.useState("triggerProps",O);return yt("button",t,{state:{open:R},ref:[a,I,E],props:[Z,ae,O||q!=="none"?W:void 0,{onMouseOver(he){ce(he.nativeEvent)},onFocus(he){M(tE(he.nativeEvent))&&he.preventBaseUIHandler()},onMouseLeave(){G.current=!1,J.clear(),Y.current=void 0},onPointerEnter(he){Y.current=he.pointerType},onPointerDown(he){Y.current=he.pointerType,k.set("closeOnClick",y),y&&!k.select("open")&&k.cancelPendingOpen(he.nativeEvent)},onClick(he){y&&!k.select("open")&&k.cancelPendingOpen(he.nativeEvent)},id:A,[Qv]:X?"":void 0,[eE]:X?void 0:""},w],stateAttributesMapping:Kv})}),nE=x.createContext(void 0);function GU(){const e=x.useContext(nE);if(e===void 0)throw new Error(un(70));return e}const KU=x.forwardRef(function(t,a){const{children:o,container:c,className:d,render:f,style:p,...h}=t,{node:b,subtree:y}=iT({container:c,ref:a,componentProps:t,elementProps:h});return!y&&!b?null:s.jsxs(x.Fragment,{children:[y,b&&Ts.createPortal(o,b)]})}),YU=x.forwardRef(function(t,a){const{keepMounted:o=!1,...c}=t;return uf().useState("mounted")||o?s.jsx(nE.Provider,{value:o,children:s.jsx(KU,{ref:a,...c})}):null}),sE=x.createContext(void 0);function rE(){const e=x.useContext(sE);if(e===void 0)throw new Error(un(71));return e}const XU=x.createContext(void 0);function gf(){return x.useContext(XU)?.direction??"ltr"}const QU=e=>({name:"arrow",options:e,async fn(t){const{x:a,y:o,placement:c,rects:d,platform:f,elements:p,middlewareData:h}=t,{element:b,padding:y=0,offsetParent:v="real"}=po(e,t)||{};if(b==null)return{};const j=x1(y),w={x:a,y:o},S=ny(c),N=ty(S),k=await f.getDimensions(b),A=S==="y",P=A?"top":"left",R=A?"bottom":"right",C=A?"clientHeight":"clientWidth",E=d.reference[N]+d.reference[S]-w[S]-d.floating[N],D=w[S]-d.reference[S],I=v==="real"?await f.getOffsetParent?.(b):p.floating;let O=p.floating[C]||d.floating[N];(!O||!await f.isElement?.(I))&&(O=p.floating[C]||d.floating[N]);const _=E/2-D/2,z=O/2-k[N]/2-1,H=Math.min(j[P],z),B=Math.min(j[R],z),L=H,F=O-k[N]-B,$=O/2-k[N]/2+_,X=m1(L,$,F),K=!h.arrow&&Nl(c)!=null&&$!==X&&d.reference[N]/2-(${const{name:a,fn:o}=QU(e);return{name:a,fn:o,options:[e,t]}},WU={name:"hide",async fn(e){const{width:t,height:a,x:o,y:c}=e.rects.reference,d=t===0&&a===0&&o===0&&c===0,f=await e.platform.detectOverflow(e,{elementContext:"reference"});return{data:{referenceHidden:f.top-a>=0||f.right-t>=0||f.bottom-a>=0||f.left-t>=0||d}}}},JU={sideX:"left",sideY:"top"},eV="--available-width",tV="--available-height",nV="--anchor-width",sV="--anchor-height",rV="--transform-origin",aE=eV,lE=tV;function oE(e,t,a){const o=e==="inline-start"||e==="inline-end";return{top:"top",right:o?a?"inline-start":"inline-end":"right",bottom:"bottom",left:o?a?"inline-end":"inline-start":"left"}[t]}function iE(e,t,a){const{rects:o,placement:c}=e;return{side:oE(t,ra(c),a),align:Nl(c)||"center",anchor:{width:o.reference.width,height:o.reference.height},positioner:{width:o.floating.width,height:o.floating.height}}}function Th(e){return aV(e,TU)}function aV(e,t){const{anchor:a,positionMethod:o="absolute",side:c="bottom",sideOffset:d=0,align:f="center",alignOffset:p=0,collisionBoundary:h,collisionPadding:b=5,sticky:y=!1,arrowPadding:v=5,disableAnchorTracking:j=!1,inline:w,keepMounted:S=!1,floatingRootContext:N,mounted:k,collisionAvoidance:A,shift:P,nodeId:R,adaptiveOrigin:C,lazyFlip:E=!1,externalTree:D}=e,[I,O]=x.useState(null);!k&&I!==null&&O(null);const _=A.side||"flip",z=A.align||"flip",H=A.fallbackAxisSide||"end",B=P?.crossAxis??!1,L=P?.rootBoundary,F=typeof a=="function"?a:void 0,$=Ie(F),X=F?$:a,K=Zn(a),q=Zn(k),G=gf()==="rtl",J=I||{top:"top",right:"right",bottom:"bottom",left:"left","inline-end":G?"left":"right","inline-start":G?"right":"left"}[c],Y=f==="center"?J:`${J}-${f}`;let re=b;typeof re=="number"?re={top:re,right:re,bottom:re,left:re}:re&&(re={top:re.top||0,right:re.right||0,bottom:re.bottom||0,left:re.left||0});const M=1,U=c==="bottom"?M:0,Z=c==="top"?M:0,ae=c==="right"?M:0,ce=c==="left"?M:0,W={boundary:h==="clipping-ancestors"?"clippingAncestors":h,padding:re},ue=x.useRef(null),ve=Zn(d),ee=Zn(p),he=typeof d!="function"?d:0,te=typeof p!="function"?p:0,ke=[];w&&ke.push(w),ke.push(iU(nt=>{const Dt=iE(nt,c,G),qt=typeof ve.current=="function"?ve.current(Dt):ve.current,es=typeof ee.current=="function"?ee.current(Dt):ee.current;return{mainAxis:qt,crossAxis:es,alignmentAxis:es}},[he,te,G,c]));const le=z==="none"&&_!=="shift",de=!le&&(y||B||_==="shift"),Se=_==="none"?null:dU({...W,padding:{top:re.top+M+U,right:re.right+M+ce,bottom:re.bottom+M+Z,left:re.left+M+ae},mainAxis:!B&&_==="flip",crossAxis:z==="flip"?"alignment":!1,fallbackAxisSideDirection:H}),Te=le?null:cU({...W,rootBoundary:L,mainAxis:z!=="none",crossAxis:de,limiter:y||B?void 0:uU(nt=>{if(!ue.current)return{};const{width:Dt,height:qt}=ue.current.getBoundingClientRect(),es=aa(ra(nt.placement)),Fn=es==="y"?Dt:qt,$n=es==="y"?re.left+re.right:re.top+re.bottom;return{offset:Fn/2+$n/2}})},[W,y,B,L,re,z]);_==="shift"||z==="shift"||f==="center"?ke.push(Te,Se):ke.push(Se,Te),ke.push(fU({...W,apply({elements:{floating:nt},availableWidth:Dt,availableHeight:qt,rects:es}){if(!q.current)return;const Fn=nt.style;Fn.setProperty(aE,`${Dt}px`),Fn.setProperty(lE,`${qt}px`);const $n=Cn(nt).devicePixelRatio||1,{x:Dn,y:en,width:cs,height:Et}=es.reference,xt=(Math.round((Dn+cs)*$n)-Math.round(Dn*$n))/$n,jt=(Math.round((en+Et)*$n)-Math.round(en*$n))/$n;Fn.setProperty(nV,`${xt}px`),Fn.setProperty(sV,`${jt}px`)}}),ZU(nt=>({element:ue.current||Gt(nt.elements.floating).createElement("div"),padding:ue.current?v:0,offsetParent:"floating"}),[v]),{name:"transformOrigin",fn(nt){const{elements:{floating:Dt},middlewareData:qt,placement:es,platform:Fn,rects:$n,y:Dn}=nt,en=ra(es),cs=Nl(es),Et=aa(en)==="y",xt=ue.current,jt=typeof d=="function"?d(iE(nt,c,G)):d;let wt;if(!xt&&cs&&Math.abs(Et?qt.shift?.x||0:qt.shift?.y||0)<=1)wt=cs==="start"===(Et&&Fn.isRTL?.(Dt)===!0)?"100%":"0%";else{const ar=Et?qt.arrow?.x||0:qt.arrow?.y||0,Fr=Et?xt?.clientWidth||0:xt?.clientHeight||0;wt=`${ar+Fr/2}px`}let Tr=en==="top"||en==="left"?`calc(100% + ${jt}px)`:`${-jt}px`;return de&&Et&&Math.abs(qt.shift?.y||0)>jt&&(Tr=`${$n.reference.y+$n.reference.height/2-Dn}px`),Dt.style.setProperty(rV,Et?`${wt} ${Tr}`:`${Tr} ${wt}`),{}}},WU,C),Me(()=>{!k&&N&&N.update({referenceElement:null,floatingElement:null,domReferenceElement:null,positionReference:null})},[k,N]);const _e=x.useMemo(()=>({ancestorScroll:!j,elementResize:!j&&typeof ResizeObserver<"u",layoutShift:!j&&typeof IntersectionObserver<"u"}),[j]),{refs:Ae,elements:oe,x:me,y:pe,middlewareData:Ce,update:Ne,placement:be,context:De,isPositioned:Ge,floatingStyles:Ct}=t({rootContext:N,open:S?k:void 0,placement:Y,middleware:ke,strategy:o,whileElementsMounted:S?void 0:(...nt)=>TT(...nt,_e),nodeId:R,externalTree:D}),{sideX:Tt,sideY:Mt}=Ce.adaptiveOrigin||JU,ot=Ge?o:"fixed",Kn=x.useMemo(()=>{let nt;return Ge?C?nt={position:ot,[Tt]:me,[Mt]:pe}:nt={...Ct,position:ot}:nt={position:ot,top:0,left:0},nt[aE]="100vw",nt[lE]="100vh",Ge||(nt.opacity=0),nt},[C,ot,Tt,me,Mt,pe,Ct,Ge]),ms=x.useRef(null);Me(()=>{if(!k)return;const nt=K.current,Dt=typeof nt=="function"?nt():nt,es=(cE(Dt)?Dt.current:Dt)||null||null;es!==ms.current&&(Ae.setPositionReference(es),ms.current=es)},[k,Ae,X,K]),x.useEffect(()=>{if(!k)return;const nt=K.current;typeof nt!="function"&&cE(nt)&&nt.current!==ms.current&&(Ae.setPositionReference(nt.current),ms.current=nt.current)},[k,Ae,X,K]),x.useEffect(()=>{if(S&&k&&oe.reference&&oe.floating)return TT(oe.reference,oe.floating,Ne,_e)},[S,k,oe,Ne,_e]);const Yn=ra(be),vs=oE(c,Yn,G),it=Nl(be)||"center",$t=!!Ce.hide?.referenceHidden;Me(()=>{E&&k&&Ge&&Yn!==J&&O(Yn)},[E,k,Ge,Yn,J]);const Vn=x.useMemo(()=>({position:"absolute",top:Ce.arrow?.y,left:Ce.arrow?.x}),[Ce.arrow]),rn=Ce.arrow?.centerOffset!==0;return x.useMemo(()=>({positionerStyles:Kn,arrowStyles:Vn,arrowRef:ue,arrowUncentered:rn,side:vs,align:it,physicalSide:Yn,anchorHidden:$t,refs:Ae,context:De,isPositioned:Ge,update:Ne}),[Kn,Vn,ue,rn,vs,it,Yn,$t,Ae,De,Ge,Ne])}function cE(e){return e!=null&&"current"in e}function xf(e){return e==="starting"?gH:mn}function Eh(e,t,{styles:a,transitionStatus:o,props:c,refs:d,hidden:f,inert:p=!1}){const h={...a};return p&&(h.pointerEvents="none"),yt("div",e,{state:t,ref:d,props:[{role:"presentation",hidden:f,style:h},xf(o),c],stateAttributesMapping:Zc})}const lV=x.forwardRef(function(t,a){const{render:o,className:c,anchor:d,positionMethod:f="absolute",side:p="top",align:h="center",sideOffset:b=0,alignOffset:y=0,collisionBoundary:v="clipping-ancestors",collisionPadding:j=5,arrowPadding:w=5,sticky:S=!1,disableAnchorTracking:N=!1,collisionAvoidance:k=aT,style:A,...P}=t,R=uf(),C=GU(),E=R.useState("open"),D=R.useState("mounted"),I=R.useState("trackCursorAxis"),O=R.useState("disableHoverablePopup"),_=R.useState("floatingRootContext"),z=R.useState("instantType"),H=R.useState("transitionStatus"),B=R.useState("adaptiveOrigin"),L=Th({anchor:d,positionMethod:f,floatingRootContext:_,mounted:D,side:p,sideOffset:b,align:h,alignOffset:y,collisionBoundary:v,collisionPadding:j,sticky:S,arrowPadding:w,disableAnchorTracking:N,keepMounted:C,collisionAvoidance:k,adaptiveOrigin:B}),F=x.useMemo(()=>({open:E,side:L.side,align:L.align,anchorHidden:L.anchorHidden,instant:I!=="none"?"tracking-cursor":z}),[E,L.side,L.align,L.anchorHidden,I,z]),$=Eh(t,F,{styles:L.positionerStyles,transitionStatus:H,props:P,refs:[a,R.useStateSetter("positionerElement")],hidden:!D,inert:!E||I==="both"||O});return s.jsx(sE.Provider,{value:L,children:$})}),oV=x.forwardRef(function(t,a){const{render:o,className:c,style:d,...f}=t,p=uf(),{side:h,align:b}=rE(),y=p.useState("open"),v=p.useState("instantType"),j=p.useState("transitionStatus"),w=p.useState("popupProps"),S=p.useState("floatingRootContext"),N=p.useState("disabled"),k=p.useState("closeDelay");oa({open:y,ref:p.context.popupRef,onComplete(){y&&p.context.onOpenChangeComplete?.(!0)}}),GT(S,{enabled:!N,closeDelay:k});const A=p.useStateSetter("popupElement");return yt("div",t,{state:{open:y,side:h,align:b,instant:v,transitionStatus:j},ref:[a,p.context.popupRef,A],props:[mf,w,xf(j),f],stateAttributesMapping:Xv})}),iV=x.forwardRef(function(t,a){const{render:o,className:c,style:d,...f}=t,p=uf(),{arrowRef:h,side:b,align:y,arrowUncentered:v,arrowStyles:j}=rE(),w=p.useState("open"),S=p.useState("instantType");return yt("div",t,{state:{open:w,side:b,align:y,uncentered:v,instant:S},ref:[a,h],props:[{style:j,"aria-hidden":!0},f],stateAttributesMapping:Zc})}),cV=function(t){const{delay:a,closeDelay:o,timeout:c=400}=t,d=x.useMemo(()=>({open:a,close:o}),[a,o]);return s.jsx(JT.Provider,{value:a,children:s.jsx(uH,{delay:d,timeoutMs:c,children:t.children})})};function Ah(e){return zv(19)?e:e?"true":void 0}function uV(e){const[t,a]=x.useState({current:e,previous:null});return Object.is(e,t.current)||a({current:e,previous:t.current}),t.previous}function dV(e){return s.jsx(cV,{"data-slot":"tooltip-provider",...e})}function Ui(e){return s.jsx(HU,{"data-slot":"tooltip",...e})}function Vi(e){return s.jsx(qU,{"data-slot":"tooltip-trigger",...e})}function Fi({className:e,sideOffset:t=8,side:a="top",children:o,...c}){return s.jsx(YU,{children:s.jsx(lV,{sideOffset:t,side:a,className:"z-[35]",children:s.jsx(oV,{"data-slot":"tooltip-content",className:Ue("max-w-[288px] rounded-md bg-primary px-2.5 py-2 text-[11.5px] leading-[1.55] text-primary-on shadow-3","break-words outline-none","transition-[opacity,transform] duration-150 ease-out","data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95","data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",e),...c,children:o})})})}function fV({className:e,...t}){return s.jsx(iV,{className:Ue("size-2 rotate-45 rounded-[1px] bg-primary","data-[side=top]:-bottom-1 data-[side=bottom]:-top-1",e),...t})}const uE=x.createContext(void 0);function iu(e){const t=x.useContext(uE);if(!e&&t===void 0)throw new Error(un(27));return t}const dE=x.forwardRef(function(t,a){const{render:o,className:c,style:d,forceRender:f=!1,...p}=t,h=iu(),b=h.useState("open"),y=h.useState("nested"),v=h.useState("mounted"),j=h.useState("transitionStatus");return yt("div",t,{state:{open:b,transitionStatus:j},ref:[h.context.backdropRef,a],stateAttributesMapping:Xv,props:[{role:"presentation",hidden:!v,style:{userSelect:"none",WebkitUserSelect:"none"}},p],enabled:f||!y})}),fE=x.forwardRef(function(t,a){const{render:o,className:c,style:d,disabled:f=!1,nativeButton:p=!0,...h}=t,b=iu(),y=b.useState("open"),{getButtonProps:v,buttonRef:j}=na({disabled:f,native:p}),w={disabled:f};function S(N){y&&b.setOpen(!1,qe(dy,N.nativeEvent))}return yt("button",t,{state:w,ref:[a,j],props:[{onClick:S},h,v]})}),pE=x.createContext(void 0);function pV(){const e=x.useContext(pE);if(e===void 0)throw new Error(un(26));return e}const qy="ArrowUp",Gy="ArrowDown",Ky="ArrowLeft",Yy="ArrowRight",Xy="Home",Qy="End",Rh=new Set([qy,Gy,Ky,Yy,Xy,Qy]),mE="Shift",mV=[mE,"Control","Alt","Meta"];function hV(e){return pn(e)&&e.tagName==="INPUT"}function hE(e){return!!(hV(e)&&e.selectionStart!=null||pn(e)&&e.tagName==="TEXTAREA")}function gE(e,t,a,o){if(!e||!t||!t.scrollTo)return;let c=e.scrollLeft,d=e.scrollTop;const f=e.clientWidthe.scrollLeft+e.clientWidth-b.scrollPaddingRight?c=h+t.offsetWidth+y.scrollMarginRight-e.clientWidth+b.scrollPaddingRight:h-y.scrollMarginLefte.scrollLeft+e.clientWidth-b.scrollPaddingRight&&(c=h+t.offsetWidth+y.scrollMarginRight-e.clientWidth+b.scrollPaddingRight))}if(p&&o!=="horizontal"){const h=xE(e,t,"top"),b=Oh(e),y=Oh(t);h-y.scrollMarginTope.scrollTop+e.clientHeight-b.scrollPaddingBottom&&(d=h+t.offsetHeight+y.scrollMarginBottom-e.clientHeight+b.scrollPaddingBottom)}e.scrollTo({left:c,top:d,behavior:"auto"})}function xE(e,t,a){const o=a==="left"?"offsetLeft":"offsetTop";let c=0;for(;t.offsetParent&&(c+=t[o],t.offsetParent!==e);)t=t.offsetParent;return c}function Oh(e){const t=getComputedStyle(e);return{scrollMarginTop:parseFloat(t.scrollMarginTop)||0,scrollMarginRight:parseFloat(t.scrollMarginRight)||0,scrollMarginBottom:parseFloat(t.scrollMarginBottom)||0,scrollMarginLeft:parseFloat(t.scrollMarginLeft)||0,scrollPaddingTop:parseFloat(t.scrollPaddingTop)||0,scrollPaddingRight:parseFloat(t.scrollPaddingRight)||0,scrollPaddingBottom:parseFloat(t.scrollPaddingBottom)||0,scrollPaddingLeft:parseFloat(t.scrollPaddingLeft)||0}}const gV="--nested-dialogs",xV="data-nested-dialog-open",bV={...Zc,...co,nestedDialogOpen(e){return e?{[xV]:""}:null}},bE=x.forwardRef(function(t,a){const{render:o,className:c,style:d,finalFocus:f,initialFocus:p,...h}=t,b=iu(),y=b.useState("descriptionElementId"),v=b.useState("disablePointerDismissal"),j=b.useState("floatingRootContext"),w=b.useState("popupProps"),S=b.useState("modal"),N=b.useState("mounted"),k=b.useState("nested"),A=b.useState("nestedOpenDialogCount"),P=b.useState("open"),R=b.useState("openMethod"),C=b.useState("titleElementId"),E=b.useState("transitionStatus"),D=b.useState("role"),I=j.useState("floatingId");pV(),oa({open:P,ref:b.context.popupRef,onComplete(){P&&b.context.onOpenChangeComplete?.(!0)}});const O=p===void 0?_T(b.context.popupRef):p,_=A>0,z=b.useStateSetter("popupElement"),B=yt("div",t,{state:{open:P,nested:k,transitionStatus:E,nestedDialogOpen:_},props:[w,{id:I,"aria-labelledby":C,"aria-describedby":y,role:D,...mf,hidden:!N,onKeyDown(L){Rh.has(L.key)&&L.stopPropagation()},style:{[gV]:A}},h],ref:[a,b.context.popupRef,z],stateAttributesMapping:bV});return s.jsx(mh,{context:j,openInteractionType:R,disabled:!N,closeOnFocusOut:!v,initialFocus:O,returnFocus:f,modal:S!==!1,restoreFocus:"popup",children:B})}),Mh=x.forwardRef(function(t,a){const{cutout:o,...c}=t;let d;if(o){const f=o.getBoundingClientRect();d=`polygon(0% 0%,100% 0%,100% 100%,0% 100%,0% 0%,${f.left}px ${f.top}px,${f.left}px ${f.bottom}px,${f.right}px ${f.bottom}px,${f.right}px ${f.top}px,${f.left}px ${f.top}px)`}return s.jsx("div",{ref:a,role:"presentation","data-base-ui-inert":"",...c,style:{position:"fixed",inset:0,userSelect:"none",WebkitUserSelect:"none",clipPath:d}})}),vE=x.forwardRef(function(t,a){const{keepMounted:o=!1,...c}=t,d=iu(),f=d.useState("mounted"),p=d.useState("modal"),h=d.useState("open");return f||o?s.jsx(pE.Provider,{value:o,children:s.jsxs(ph,{ref:a,...c,children:[f&&p===!0&&s.jsx(Mh,{ref:d.context.internalBackdropRef,inert:Ah(!h)}),t.children]})}):null});let yE={},jE={},wE="";function Dh(e,t){return Qd(e)?e:t}function SE(e,t,a){return/hidden|clip/.test(e.getComputedStyle(Dh(t,a)).overflowY)}function vV(e){if(typeof document>"u")return!1;const t=Gt(e);return Cn(t).innerWidth-t.documentElement.clientWidth>0}function yV(e){if(!(typeof CSS<"u"&&CSS.supports&&CSS.supports("scrollbar-gutter","stable"))||typeof document>"u")return!1;const a=Gt(e),o=a.documentElement,c=a.body,d=Dh(o,c),f=d.style.overflowY,p=o.style.scrollbarGutter;o.style.scrollbarGutter="stable",d.style.overflowY="scroll";const h=d.offsetWidth;d.style.overflowY="hidden";const b=d.offsetWidth;return d.style.overflowY=f,o.style.scrollbarGutter=p,h===b}function jV(e){const t=Gt(e),a=t.documentElement,o=t.body,c=Dh(a,o),d={overflowY:c.style.overflowY,overflowX:c.style.overflowX};return Object.assign(c.style,{overflowY:"hidden",overflowX:"hidden"}),()=>{Object.assign(c.style,d)}}function wV(e){const t=Gt(e),a=t.documentElement,o=t.body,c=Cn(a);let d=0,f=0,p=!1;const h=Ga.create();if(jl&&(c.visualViewport?.scale??1)!==1)return()=>{};function b(){const w=c.getComputedStyle(a),S=c.getComputedStyle(o),A=(w.scrollbarGutter||"").includes("both-edges")?"stable both-edges":"stable";d=a.scrollTop,f=a.scrollLeft,yE={scrollbarGutter:a.style.scrollbarGutter,overflowY:a.style.overflowY,overflowX:a.style.overflowX},wE=a.style.scrollBehavior,jE={position:o.style.position,height:o.style.height,width:o.style.width,boxSizing:o.style.boxSizing,overflowY:o.style.overflowY,overflowX:o.style.overflowX,scrollBehavior:o.style.scrollBehavior};const P=a.scrollHeight>a.clientHeight,R=a.scrollWidth>a.clientWidth,C=w.overflowY==="scroll"||S.overflowY==="scroll",E=w.overflowX==="scroll"||S.overflowX==="scroll",D=Math.max(0,c.innerWidth-o.clientWidth),I=Math.max(0,c.innerHeight-o.clientHeight),O=parseFloat(S.marginTop)+parseFloat(S.marginBottom),_=parseFloat(S.marginLeft)+parseFloat(S.marginRight),z=Dh(a,o);if(p=yV(e),p){a.style.scrollbarGutter=A,z.style.overflowY="hidden",z.style.overflowX="hidden";return}Object.assign(a.style,{scrollbarGutter:A,overflowY:"hidden",overflowX:"hidden"}),(P||C)&&(a.style.overflowY="scroll"),(R||E)&&(a.style.overflowX="scroll"),Object.assign(o.style,{position:"relative",height:O||I?`calc(100dvh - ${O+I}px)`:"100dvh",width:_||D?`calc(100vw - ${_+D}px)`:"100vw",boxSizing:"border-box",overflowY:"hidden",overflowX:"hidden",scrollBehavior:"unset"}),o.scrollTop=d,o.scrollLeft=f,a.setAttribute("data-base-ui-scroll-locked",""),a.style.scrollBehavior="unset"}function y(){Object.assign(a.style,yE),Object.assign(o.style,jE),p||(a.scrollTop=d,a.scrollLeft=f,a.removeAttribute("data-base-ui-scroll-locked"),a.style.scrollBehavior=wE)}function v(){y(),h.request(b)}b();const j=St(c,"resize",v);return()=>{h.cancel(),y(),typeof c.removeEventListener=="function"&&j()}}class SV{constructor(){cn(this,"lockCount",0);cn(this,"restore",null);cn(this,"timeoutLock",la.create());cn(this,"timeoutUnlock",la.create());cn(this,"release",()=>{this.lockCount-=1,this.lockCount===0&&this.restore&&this.timeoutUnlock.start(0,this.unlock)});cn(this,"unlock",()=>{this.lockCount===0&&this.restore&&(this.restore?.(),this.restore=null)})}acquire(t){return this.lockCount+=1,this.lockCount===1&&this.restore===null&&this.timeoutLock.start(0,()=>this.lock(t)),this.release}lock(t){if(this.lockCount===0||this.restore!==null)return;const a=Gt(t),o=a.documentElement,c=a.body,d=Cn(o);if(SE(d,o,c)){const p=new d.MutationObserver(()=>{SE(d,o,c)||(p.disconnect(),this.restore=null,this.lock(t))}),h={attributes:!0};p.observe(o,h),p.observe(c,h),this.restore=()=>p.disconnect();return}const f=tf||!vV(t);this.restore=f?jV(t):wV(t)}}const NV=new SV;function NE(e=!0,t=null){Me(()=>{if(e)return NV.acquire(t)},[e,t])}function kV({store:e,parentContext:t,isDrawer:a}){const o=e.useState("open"),c=e.useState("disablePointerDismissal"),d=e.useState("modal"),f=e.useState("popupElement"),p=e.useState("floatingRootContext"),[h,b]=x.useState(0),[y,v]=x.useState(0),j=h===0,w=pf(p,{outsidePressEvent(){return e.context.internalBackdropRef.current||e.context.backdropRef.current?"intentional":{mouse:d==="trap-focus"?"sloppy":"intentional",touch:"sloppy"}},outsidePress(S){if(!e.context.outsidePressEnabledRef.current||"button"in S&&S.button!==0)return!1;if("touches"in S){if(S.type==="touchend"){if(S.changedTouches.length!==1||S.touches.length!==0)return!1}else if(S.touches.length!==1)return!1}const N=fs(S);if(j&&!c){if(d){const k=e.context.internalBackdropRef.current,A=e.context.backdropRef.current;return k||A?k===N||A===N||Ze(N,f)&&!N?.hasAttribute("data-base-ui-portal"):!0}return!0}return!1},escapeKey:j});return NE(o&&d===!0,f),e.useContextCallback("onNestedDialogOpen",(S,N)=>{b(S),v(N)}),Me(()=>(t?.onNestedDialogOpen&&(o?t.onNestedDialogOpen(h+1,y+(a?1:0)):t.onNestedDialogOpen(0,0)),()=>{t?.onNestedDialogOpen&&o&&t.onNestedDialogOpen(0,0)}),[a,o,h,y,t]),Ly(e,{activeTriggerProps:w.reference,inactiveTriggerProps:w.trigger,popupProps:w.floating,nestedOpenDialogCount:h,nestedOpenDrawerCount:y}),null}const CV={...Hy,modal:e=>e.modal,nested:e=>e.nested,nestedOpenDialogCount:e=>e.nestedOpenDialogCount,nestedOpenDrawerCount:e=>e.nestedOpenDrawerCount,disablePointerDismissal:e=>e.disablePointerDismissal,openMethod:e=>e.openMethod,descriptionElementId:e=>e.descriptionElementId,titleElementId:e=>e.titleElementId,viewportElement:e=>e.viewportElement,role:e=>e.role};class TV extends ou{constructor(a,o,c){const d=new vh,f=EV(a,d,o,c);super(f,AV(d),CV);cn(this,"setOpen",(a,o)=>{o.preventUnmountOnClose=()=>{this.set("preventUnmountingOnClose",!0)},!a&&o.trigger==null&&this.state.activeTriggerId!=null&&(o.trigger=this.state.activeTriggerElement??void 0),this.context.onOpenChange?.(a,o),!o.isCanceled&&(this.state.floatingRootContext.dispatchOpenChange(a,o),this.update(Iy(this.state,a,o.trigger)))})}}function EV(e,t,a,o=!1){return{...zy(t,a,o),modal:!0,disablePointerDismissal:!1,viewportElement:null,descriptionElementId:void 0,titleElementId:void 0,openMethod:null,nested:!1,nestedOpenDialogCount:0,nestedOpenDrawerCount:0,role:"dialog",...e}}function AV(e){return{popupRef:x.createRef(),backdropRef:x.createRef(),internalBackdropRef:x.createRef(),outsidePressEnabledRef:{current:!0},triggerElements:e,onOpenChange:void 0,onOpenChangeComplete:void 0}}function kE(e,t){const{children:a,open:o,defaultOpen:c=!1,onOpenChange:d,onOpenChangeComplete:f,disablePointerDismissal:p=!1,modal:h=!0,actionsRef:b,handle:y,triggerId:v,defaultTriggerId:j=null}=t,w=e==="drawer",S=e==="alert-dialog",N=S?!0:h,k=S||p,A=S?"alertdialog":"dialog",P=iu(!0),R=P!=null,C={modal:N,disablePointerDismissal:k,nested:R,role:A},E=My((H,B)=>new TV({open:c,openProp:o,activeTriggerId:j,triggerIdProp:v,...C},H,B),!0);E.useControlledProp("openProp",o),E.useControlledProp("triggerIdProp",v),E.useSyncedValues(C),E.useContextCallback("onOpenChange",d),E.useContextCallback("onOpenChangeComplete",f);const D=E.useState("open"),I=E.useState("mounted"),O=E.useState("payload");HT(E,D),Py(E);const{forceUnmount:_}=_y(D,E);x.useImperativeHandle(b,()=>({unmount:_,close:()=>E.setOpen(!1,qe(py))}),[_,E]);const z=D||I;return s.jsxs(uE.Provider,{value:E,children:[y&&s.jsx(Dy,{handle:y,store:E}),z&&s.jsx(kV,{store:E,parentContext:P?.context,isDrawer:w}),typeof a=="function"?a({payload:O}):a]})}const RV=uh(function(t){return kE("dialog",t)}),CE=x.forwardRef(function(t,a){const{render:o,className:c,style:d,id:f,...p}=t,h=iu(),b=ka(f);return h.useSyncedValueWithCleanup("titleElementId",b),yt("h2",t,{ref:a,props:[{id:b},p]})});function OV(e){const t=x.useRef(""),a=x.useCallback(c=>{c.defaultPrevented||(t.current=c.pointerType,e(c,c.pointerType))},[e]);return{onClick:x.useCallback(c=>{if(c.detail===0){e(c,"keyboard");return}"pointerType"in c?e(c,c.pointerType):e(c,t.current),t.current=""},[e]),onPointerDown:a}}function TE(e,t){const a=Ie((d,f)=>{(typeof e=="function"?e():e)||t(f||(tf?"touch":""))}),{onClick:o,onPointerDown:c}=OV(a);return x.useMemo(()=>({onClick:o,onPointerDown:c}),[o,c])}function EE(e){const[t,a]=x.useState(null),o=TE(e,a);return ia(e,c=>{c&&!e&&a(null)}),x.useMemo(()=>({openMethod:t,triggerProps:o}),[t,o])}function Yt(e){return s.jsx(RV,{"data-slot":"dialog",...e})}function MV(e){return s.jsx(vE,{"data-slot":"dialog-portal",...e})}const Ut=e=>s.jsx(CE,{"data-slot":"dialog-title",...e});function DV({className:e,...t}){return s.jsx(dE,{"data-slot":"dialog-overlay",className:Ue("fixed inset-0 isolate z-30 bg-mask backdrop-blur-[3px]","transition-opacity duration-150","data-open:animate-in data-open:fade-in-0","data-closed:animate-out data-closed:fade-out-0 data-closed:fill-mode-forwards",e),...t})}function Xt({className:e,children:t,showCloseButton:a=!0,keepMounted:o=!1,overlayForceRender:c=!1,...d}){return s.jsxs(MV,{keepMounted:o,children:[s.jsx(DV,{forceRender:c}),s.jsxs(bE,{"data-slot":"dialog-content",className:Ue("fixed top-1/2 left-1/2 z-30 flex max-h-[calc(100vh-48px)] w-[min(620px,calc(100vw-48px))] -translate-x-1/2 -translate-y-1/2 flex-col","overflow-hidden rounded-lg border border-border bg-surface shadow-3 outline-none","transition-[opacity,transform] duration-200 [transition-timing-function:cubic-bezier(.2,.9,.3,1)]","data-open:animate-in data-open:fade-in-0 data-open:zoom-in-[.985] data-open:slide-in-from-bottom-2.5","data-closed:animate-out data-closed:fade-out-0 data-closed:fill-mode-forwards",e),...d,children:[t,a&&s.jsxs(fE,{"data-slot":"dialog-close",render:s.jsx(ne,{variant:"ghost",size:"icon-sm",className:"absolute top-3 right-3"}),children:[s.jsx("svg",{viewBox:"0 0 12 12",className:"size-3","aria-hidden":"true",children:s.jsx("path",{d:"M2.5 2.5l7 7M9.5 2.5l-7 7",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",fill:"none"})}),s.jsx("span",{className:"sr-only",children:bo("关闭")})]})]})]})}function Vt({className:e,...t}){return s.jsx("div",{"data-slot":"dialog-header",className:Ue("flex items-center gap-2.5 border-b border-hairline px-5 py-[15px] pr-10","[&_h2]:flex-1 [&_h2]:text-[14.5px] [&_h2]:font-semibold",e),...t})}function Lt({className:e,...t}){return s.jsx("div",{"data-slot":"dialog-body",className:Ue("flex flex-col gap-4 overflow-y-auto px-5 py-[18px]",e),...t})}function tn({className:e,...t}){return s.jsx("div",{"data-slot":"dialog-footer",className:Ue("flex items-center gap-2 border-t border-hairline bg-surface-2 px-5 py-[13px]",e),...t})}function dn({className:e,...t}){return s.jsx("div",{"data-slot":"dialog-section",className:Ue("flex flex-col gap-2.5 rounded-md border border-border bg-surface-inset p-3.5","[&_h3]:text-[12.5px] [&_h3]:font-semibold","[&_p]:text-xs [&_p]:leading-[1.65] [&_p]:text-subtle",e),...t})}function IV(e){return kE("alert-dialog",e)}const PV=IV,_V=vE,LV=e=>s.jsx(CE,{"data-slot":"alert-dialog-title",...e});function zV({className:e,...t}){return s.jsx(dE,{"data-slot":"alert-dialog-overlay",className:Ue("fixed inset-0 isolate z-[38] bg-mask backdrop-blur-[3px]","transition-opacity duration-150","data-open:animate-in data-open:fade-in-0","data-closed:animate-out data-closed:fade-out-0 data-closed:fill-mode-forwards",e),...t})}function BV({className:e,children:t,showCloseButton:a=!1,...o}){return s.jsxs(_V,{children:[s.jsx(zV,{}),s.jsxs(bE,{"data-slot":"alert-dialog-content",className:Ue("fixed top-1/2 left-1/2 z-[38] flex max-h-[calc(100vh-48px)] w-[min(440px,calc(100vw-48px))] -translate-x-1/2 -translate-y-1/2 flex-col","overflow-hidden rounded-lg border border-border bg-surface shadow-3 outline-none","transition-[opacity,transform] duration-200 [transition-timing-function:cubic-bezier(.2,.9,.3,1)]","data-open:animate-in data-open:fade-in-0 data-open:zoom-in-[.985] data-open:slide-in-from-bottom-2.5","data-closed:animate-out data-closed:fade-out-0 data-closed:fill-mode-forwards",e),...o,children:[t,a&&s.jsxs(fE,{"data-slot":"alert-dialog-close",render:s.jsx(ne,{variant:"ghost",size:"icon-sm",className:"absolute top-3 right-3"}),children:[s.jsx("svg",{viewBox:"0 0 12 12",className:"size-3","aria-hidden":"true",children:s.jsx("path",{d:"M2.5 2.5l7 7M9.5 2.5l-7 7",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",fill:"none"})}),s.jsx("span",{className:"sr-only",children:bo("关闭")})]})]})]})}function HV({className:e,...t}){return s.jsx("div",{"data-slot":"alert-dialog-header",className:Ue("flex items-center gap-2.5 border-b border-hairline px-5 py-[15px]","[&_h2]:flex-1 [&_h2]:text-[14.5px] [&_h2]:font-semibold",e),...t})}function UV({className:e,...t}){return s.jsx("div",{"data-slot":"alert-dialog-body",className:Ue("flex flex-col gap-4 overflow-y-auto px-5 py-[18px]",e),...t})}function VV({className:e,...t}){return s.jsx("div",{"data-slot":"alert-dialog-footer",className:Ue("flex items-center gap-2 border-t border-hairline bg-surface-2 px-5 py-[13px]",e),...t})}function FV({className:e,tone:t="danger",...a}){return s.jsx("div",{"data-slot":"alert-dialog-banner","data-tone":t,className:Ue("rounded-md border px-3.5 py-3 text-xs leading-[1.65] text-subtle","[&_strong]:font-semibold",t==="danger"?"border-destructive-bd bg-destructive-soft [&_strong]:text-destructive":"border-border bg-surface-inset [&_strong]:text-foreground",e),...a})}function Zy({className:e,containerClassName:t,...a}){return s.jsx("div",{"data-slot":"table-container",className:Ue("relative w-full overflow-auto",t),children:s.jsx("table",{"data-slot":"table",className:Ue("w-full caption-bottom border-collapse text-[12.5px]",e),...a})})}function AE({className:e,...t}){return s.jsx("thead",{"data-slot":"table-header",className:Ue(e),...t})}function Wy({className:e,...t}){return s.jsx("tbody",{"data-slot":"table-body",className:Ue(e),...t})}function Cl({className:e,...t}){return s.jsx("tr",{"data-slot":"table-row",className:Ue("transition-colors duration-100 hover:[&>td]:bg-control-hover",e),...t})}function RE({className:e,...t}){return s.jsx("th",{"data-slot":"table-head",className:Ue("sticky top-0 z-[2] whitespace-nowrap border-b border-border bg-surface-2 px-3.5 py-2","text-left text-[10.5px] font-semibold tracking-[.05em] text-muted-foreground",e),...t})}function Tn({className:e,...t}){return s.jsx("td",{"data-slot":"table-cell",className:Ue("border-b border-hairline px-3.5 py-2 align-middle",e),...t})}const $V="data-checked",qV="data-unchecked",Jy={checked(e){return e?{[$V]:""}:{[qV]:""}},...co,...Ti},ej="data-composite-item-active",OE=x.createContext({register:()=>{},unregister:()=>{},subscribeMapChange:()=>()=>{},nextIndexRef:{current:0}});function GV(){return x.useContext(OE)}function tj(e={}){const{guess:t,label:a,metadata:o,textRef:c,index:d}=e,{register:f,unregister:p,subscribeMapChange:h,nextIndexRef:b}=GV(),y=x.useRef(-1),[v,j]=x.useState(d==null&&t?()=>{if(y.current===-1){const k=b.current;b.current+=1,y.current=k}return y.current}:-1),w=d??v,S=x.useRef(null),N=x.useCallback(k=>{const A=S.current;A&&p(A),S.current=k,k&&f(k,{metadata:o??null,index:d??null,label:a,textRef:c})},[d,f,p,o,a,c]);return Me(()=>{if(d==null)return h(k=>{const A=S.current?k.get(S.current)?.index:null;A!=null&&j(A)})},[d,h]),{ref:N,index:w}}function KV(e={}){const{highlightItemOnHover:t,highlightedIndex:a,onHighlightedIndexChange:o}=EC(),{ref:c,index:d}=tj(e),f=a===d,p=x.useRef(null),h=sa(c,p);return{compositeProps:{tabIndex:f?0:-1,onFocus(){o(d)},onMouseMove(){const y=p.current;if(!t||!y)return;const v=y.hasAttribute("disabled")||y.ariaDisabled==="true";!f&&!v&&y.focus()}},compositeRef:h,index:d}}function ME(e){const{render:t,className:a,style:o,state:c=mn,props:d=Ir,refs:f=Ir,metadata:p,stateAttributesMapping:h,tag:b="div",...y}=e,{compositeProps:v,compositeRef:j}=KV({metadata:p});return yt(b,e,{state:c,ref:[j,...f],props:[v,...d,y],stateAttributesMapping:h})}const DE=x.createContext(void 0);function YV(){return x.useContext(DE)}function Ih(e){if(e==null)return"";if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}const IE=x.createContext(void 0);function XV(){const e=x.useContext(IE);if(e===void 0)throw new Error(un(52));return e}const PE=x.forwardRef(function(t,a){const{render:o,className:c,disabled:d=!1,readOnly:f=!1,required:p=!1,"aria-labelledby":h,value:b,inputRef:y,nativeButton:v=!1,id:j,style:w,...S}=t,N=YV(),{disabled:k,readOnly:A,required:P,form:R,checkedValue:C,touched:E=!1,validation:D,name:I,setCheckedValue:O=Kt,setTouched:_=Kt,registerInputRef:z=Kt}=N??{},{setTouched:H,setFilled:B,state:L,disabled:F}=Na(),$=A1(),{labelId:X,getDescriptionProps:K}=yl(),q=F||$.disabled||k||d,Q=A||f,G=P||p,J=R,Y=N?C===b:b==="",re=x.useRef(null),M=x.useRef(null),U=D?.registerInput,Z=x.useCallback(oe=>U?.(oe,{controlRef:re,value:void 0}),[U]),ae=sa(y,M,z,Z);Me(()=>{M.current?.checked&&B(!0)},[B]),Me(()=>{if(M.current){if(q&&Y){z(null);return}z(M.current)}},[Y,q,z]);const ce=ka(),W=io({id:j}),ue=v?void 0:W,ve=Fv(h,X,M,!v,ue),ee={role:"radio","aria-checked":Y,"aria-labelledby":ve,[ej]:Y?"":void 0,id:v?W:ce,onKeyDown(oe){oe.key==="Enter"&&oe.preventDefault()},onClick(oe){if(oe.defaultPrevented||q||Q)return;oe.preventDefault();const me=M.current;me&&Yc(me,oe)},onFocus(oe){oe.defaultPrevented||q||Q||!E||(M.current?.click(),_(!1))}},{getButtonProps:he,buttonRef:te}=na({disabled:q,native:v,composite:!1}),ke={type:"radio",ref:ae,form:J,id:ue,name:I,tabIndex:-1,style:I?su:_i,"aria-hidden":!0,...b!==void 0?{value:Ih(b)}:mn,disabled:q,checked:Y,required:G,readOnly:Q,onChange(oe){if(oe.nativeEvent.defaultPrevented||q||Q||b===void 0)return;const me=qe(Qn,oe.nativeEvent);O(b,me),!me.isCanceled&&H(!0)},onClick(oe){oe.stopPropagation()},onFocus(){re.current?.focus()}},le=x.useMemo(()=>({...L,required:G,disabled:q,readOnly:Q,checked:Y}),[L,q,Q,Y,G]),de=le,Se=N!==void 0,Te=[a,re,te],_e=[ee,S,he,K,D?oe=>D.getValidationProps(q,oe):mn],Ae=yt("span",t,{enabled:!Se,state:le,ref:Te,props:_e,stateAttributesMapping:Jy});return s.jsxs(IE.Provider,{value:de,children:[Se?s.jsx(ME,{tag:"span",render:o,className:c,style:w,state:le,refs:Te,props:_e,stateAttributesMapping:Jy}):Ae,s.jsx("input",{...ke,suppressHydrationWarning:!0})]})}),QV=x.forwardRef(function(t,a){const{render:o,className:c,style:d,keepMounted:f=!1,...p}=t,h=XV(),b=h.checked,{mounted:y,transitionStatus:v,setMounted:j}=Oi(b),w={...h,transitionStatus:v},S=x.useRef(null),N=f||y,k=yt("span",t,{ref:[a,S],state:w,props:p,stateAttributesMapping:Jy});return oa({batch:!0,enabled:!b,open:b,ref:S,onComplete(){b||j(!1)}}),N?k:null});function nj(e){const{children:t,elementsRef:a,labelsRef:o,onMapChange:c}=e,d=Ie(c),[,f]=x.useState(!1),p=Ps(WV).current,h=Ps(ZV).current,b=x.useRef(0),y=x.useRef(!0),v=x.useRef(null),j=x.useRef(null),w=Ie(()=>{y.current||(y.current=!0,f(E=>!E))}),S=Ie((E,D)=>{h.set(E,D),w()}),N=Ie(E=>{h.delete(E),w()}),k=Ie(E=>{const D=new Map;return a.current.length=0,o&&(o.current.length=0),E.forEach(I=>{D.set(I.element,{...I.registration.metadata??{},index:I.index}),a.current[I.index]=I.element,o&&(o.current[I.index]=I.registration.label!==void 0?I.registration.label:I.registration.textRef?.current?.textContent??I.element.textContent)}),b.current=a.current.length,D});function A(E){if(j.current?.disconnect(),j.current=null,typeof MutationObserver!="function"||E.length<2)return;const D=new MutationObserver(O=>{if(!tF(O))return;let _=null;for(const z of E)if(z.isConnected){if(_&&_E(_,z)>0){D.disconnect(),w();return}_=z}});j.current=D;const I=new Set;for(let O=1;OD.observe(O,{childList:!0}))}const P=Ie(()=>{const[E,D]=JV(h),I=k(E),O=v.current,_=!O||O.length!==E.length||E.some((z,H)=>{const B=O[H];return z.index!==B.index||z.element!==B.element||z.registration.index!==B.registration.index||z.registration.metadata!==B.registration.metadata});A(D),v.current=E,y.current=!1,_&&(p.forEach(z=>z(I)),d(I))});Me(()=>(!y.current&&v.current&&k(v.current),()=>{a.current=[],o&&(o.current=[])}),[a,o,k]),Me(()=>{y.current&&P()}),Me(()=>()=>{j.current?.disconnect(),y.current=!0},[]);const R=Ie(E=>(p.add(E),()=>{p.delete(E)})),C=x.useMemo(()=>({register:S,unregister:N,subscribeMapChange:R,nextIndexRef:b}),[S,N,R,b]);return s.jsx(OE.Provider,{value:C,children:t})}function ZV(){return new Map}function WV(){return new Set}function JV(e){const t=new Set,a=[],o=[];e.forEach((d,f)=>{if(!f.isConnected)return;const p=d.index,h={index:p??-1,element:f,registration:d};p===null?o.push(h):p>=0&&(t.add(p),a.push(h))});let c=0;return o.sort((d,f)=>_E(d.element,f.element)),o.forEach(d=>{for(;t.has(c);)c+=1;d.index=c,a.push(d),c+=1}),t.size>0&&a.sort((d,f)=>d.index-f.index),[a,o.map(d=>d.element)]}function eF(e,t){let a=e.parentElement;for(;a&&!a.contains(t);)a=a.parentElement;return a}function tF(e){for(const t of e)for(let a=0;a{if(C.current=P.current[H]??null,(p??S)(H),B){const L=P.current[H];gE(k.current,L,d,a)}}),I=Ie(H=>{if(H.size===0)return;if(R.current){const $=P.current,X=$.indexOf(C.current);if(X===-1){const K=$[E];!K||ho($,E,v)?D(sF($,v)):C.current=K}else X!==E&&D(X);return}R.current=!0;const B=Array.from(H.keys()),L=B.find($=>$?.hasAttribute(ej))??null,F=L?H.get(L)?.index??-1:-1;if(F!==-1)D(F);else if(ho(B,E,v)){const $=_s(B,{disabledIndices:v});mo(B,$)||D($)}gE(k.current,L,d,a)});Me(()=>{if(v==null||f!=null||!R.current)return;const H=P.current;if(ho(H,E,v)){const B=_s(H,{disabledIndices:v});mo(H,B)||D(B)}},[v,f,E,P,D]);const O=Ie((H,B,L)=>c?c(H,B,L,P):L),_=Ie(H=>{const B=H.key===Xy||H.key===Qy;if(!Rh.has(H.key)||!b&&B||rF(H,j)||!k.current)return;const F=d==="rtl",$=F?Ky:Yy,X=F?Yy:Ky,K=a==="vertical"?Gy:$,q=a==="vertical"?qy:X,Q=fs(H.nativeEvent);if(Q!=null&&hE(Q)&&!LE(Q)){const U=Q.selectionStart,Z=Q.selectionEnd,ae=Q.value;if(U==null||H.shiftKey||U!==Z||H.key!==q&&U0)return}let G=E;const J=sh(P,v),Y=ry(P,v);o!=null&&(G=o({disabledIndices:v,elementsRef:P,event:H,highlightedIndex:E,loopFocus:t,maxIndex:Y,minIndex:J,onLoop:O,orientation:a,rtl:F}));const re=a!=="vertical"&&H.key===$||a!=="horizontal"&&H.key===Gy,M=a!=="vertical"&&H.key===X||a!=="horizontal"&&H.key===qy;b&&(H.key===Xy?G=J:H.key===Qy&&(G=Y)),G===E&&(re||M)&&(t&&G===Y&&re?(G=J,c&&(G=c(H,E,G,P))):t&&G===J&&M?(G=Y,c&&(G=c(H,E,G,P))):G=_s(P.current,{startingIndex:G,decrement:M,disabledIndices:v})),G!==E&&!mo(P.current,G)&&(y&&H.stopPropagation(),(N||B||re||M)&&H.preventDefault(),D(G,!0),queueMicrotask(()=>{P.current[G]?.focus()}))});return{props:{ref:A,onFocus(H){const B=k.current,L=fs(H.nativeEvent);!B||L==null||!hE(L)||L.setSelectionRange(0,L.value.length)},onKeyDown:_},highlightedIndex:E,onHighlightedIndexChange:D,elementsRef:P,onMapChange:I,relayKeyboardEvent:_}}function sF(e,t){let a=-1;for(let o=0;o({highlightedIndex:_,onHighlightedIndexChange:z,highlightItemOnHover:C,relayKeyboardEvent:L}),[_,z,C,L]);return s.jsx(TC.Provider,{value:$,children:s.jsx(nj,{elementsRef:H,onMapChange:X=>{N?.(X),B(X)},children:F})})}const lF=[mE],zE=x.forwardRef(function(t,a){const{render:o,className:c,disabled:d,readOnly:f,required:p,onValueChange:h,value:b,defaultValue:y,form:v,name:j,inputRef:w,id:S,style:N,...k}=t,{setTouched:A,setFocused:P,validationMode:R,name:C,disabled:E,state:D,validation:I,setDirty:O,setFilled:_,validityData:z}=Na(),{labelId:H}=yl(),{clearErrors:B,elementRef:L}=Qc(),F=kB(!0),$=E||d,X=C??j,K=ka(S),[q,Q]=Ka({controlled:b,default:y,name:"RadioGroup",state:"value"}),[G,J]=x.useState(!1),Y=Ie((te,ke)=>{h?.(te,ke),!ke.isCanceled&&Q(te)}),re=I.getInputControl,M=x.useMemo(()=>({get current(){return re()}}),[re]),U=x.useRef(null),Z=x.useRef(null);function ae(te){let ke;return w&&(typeof w=="function"?ke=w(te):w.current=te),U.current=te,ke}const ce=Ie(te=>{if(!te||te.disabled)return;Z.current||(Z.current=te);const ke=U.current,le=te.checked||ke==null||ke.disabled?ae(te):void 0;return()=>{Z.current===te&&(Z.current=null),U.current===te?le?(le(),U.current=null):ae(null):le?.()}}),W=Ie(()=>{const te=L.current;if(!te)return q??null;for(const ke of I.registeredInputs.keys())if(ke.checked&&b4(ke,te))return q??null;return null});tu(M,K,q??null,W,!$,j),ia(q,()=>{B(X),O(q!==z.initialValue),_(q!=null),I.change(q);const te=Z.current;q==null&&te&&!te.disabled&&ae(te)});const ue=H??F?.legendId,ve={...D,disabled:$??!1,required:p??!1,readOnly:f??!1},ee=x.useMemo(()=>({checkedValue:q,disabled:$,form:v,validation:I,name:X,readOnly:f,registerInputRef:ce,required:p,setCheckedValue:Y,setTouched:J,touched:G}),[q,$,v,I,X,f,ce,p,Y,J,G]),he={id:S,role:"radiogroup","aria-required":p||void 0,"aria-disabled":$||void 0,"aria-readonly":f||void 0,"aria-labelledby":ue,onFocus(){P(!0)},onBlur(te){Ze(te.currentTarget,te.relatedTarget)||(A(!0),P(!1),R==="onBlur"&&I.commit(q))},onKeyDownCapture(te){te.key.startsWith("Arrow")&&(J(!0),P(!0))}};return s.jsx(DE.Provider,{value:ee,children:s.jsx(aF,{render:o,className:c,style:N,state:ve,props:[he,k,te=>I.getValidationProps($??!1,te)],refs:[a],stateAttributesMapping:Ti,enableHomeAndEndKeys:!1,modifierKeys:lF})})});function Ph({className:e,...t}){return s.jsx(zE,{"data-slot":"radio-group",className:Ue("flex flex-col gap-2",e),...t})}function yo({className:e,...t}){return s.jsx(PE,{"data-slot":"radio-group-item",className:Ue("relative inline-flex size-3.5 flex-none cursor-pointer items-center justify-center rounded-full border border-control-border bg-control","outline-none transition-colors duration-150 ease-out","hover:border-control-border-hover","data-checked:border-primary data-checked:bg-primary","focus-visible:shadow-focus","data-disabled:cursor-not-allowed data-disabled:opacity-45",e),...t,children:s.jsx(QV,{"data-slot":"radio-group-indicator",className:"grid place-content-center data-unchecked:hidden",children:s.jsx("span",{className:"block size-[5px] rounded-full bg-primary-foreground"})})})}function BE(e){const t=x.useRef(!0);t.current&&(t.current=!1,e())}const HE=x.createContext(void 0),UE=x.createContext(void 0),VE=x.createContext(void 0);function jo(){const e=x.useContext(HE);if(e===void 0)throw new Error(un(60));return e}function sj(){const e=x.useContext(UE);if(e===void 0)throw new Error(un(101));return e}function FE(){const e=x.useContext(VE);if(e===void 0)throw new Error(un(61));return e}function oF(e,t,a=Object.is){const{length:o}=e;if(o!==t.length)return!1;for(let c=0;cObject.is(e,t);function ca(e,t,a){return e==null||t==null?Object.is(e,t):a(e,t)}function $E(e,t,a){return Array.isArray(e)&&Array.isArray(t)?!oF(e,t,(o,c)=>ca(o,c,a)):e!==t}function Lh(e,t,a){return e?e.some(o=>o===void 0?!1:ca(t,o,a)):!1}function cu(e,t,a){return e?e.findIndex(o=>o===void 0?!1:ca(o,t,a)):-1}function iF(e,t){if(t!==_h)return o=>Lh(e,o,t);const a=new Set(e);return a.delete(void 0),o=>a.has(o)&&(o!==0||e.some(c=>Object.is(o,c)))}function bf(e,t,a,o){const c=o&&Array.isArray(t)?e.findIndex(iF(t,a)):cu(e,t,a);return c===-1?null:c}function qE(e,t,a,o,c,d){return Lh(o,t,c)?d!=null&&e>d&&Lh(o,a[d],c)?d:e:e===d?bf(a,o,c,!0):d}function GE(e,t,a){return e.filter(o=>!ca(t,o,a))}function cF(e){return typeof e=="object"&&e!=null&&Array.isArray(e.items)}function rj(e){return cF(e?.[0])}function zh(e){return rj(e)?e.flatMap(t=>t.items):e}function KE(e){if(!Array.isArray(e))return e!=null&&"null"in e;const t=e;if(rj(t)){for(const a of t)for(const o of a.items)if(o&&o.value==null&&o.label!=null)return!0;return!1}for(const a of t)if(a&&a.value==null&&a.label!=null)return!0;return!1}function wo(e,t){if(t&&e!=null)return t(e)??"";if(e&&typeof e=="object"){if("label"in e&&e.label!=null)return String(e.label);if("value"in e)return String(e.value)}return Ih(e)}function Ca(e,t){return t&&e!=null?t(e)??"":e&&typeof e=="object"&&"value"in e&&"label"in e?Ih(e.value):Ih(e)}function YE(e,t,a){function o(){return wo(e,a)}if(a&&e!=null)return a(e);if(e&&typeof e=="object"&&"label"in e&&e.label!=null)return e.label;if(t&&!Array.isArray(t))return(Object.hasOwn(t,e)?t[e]:void 0)??o();if(Array.isArray(t)){const d=zh(t);if(e==null||typeof e!="object"){const f=d.find(p=>p.value===e);return f&&f.label!=null?f.label:o()}if("value"in e){const f=d.find(p=>p&&p.value===e.value);if(f&&f.label!=null)return f.label}}return o()}function uF(e,t,a){return e.reduce((o,c,d)=>(d>0&&o.push(", "),o.push(s.jsx(x.Fragment,{children:YE(c,t,a)},d)),o),[])}const dF={id:e=>e.id,labelId:e=>e.labelId,modal:e=>e.modal,items:e=>e.items,itemToStringLabel:e=>e.itemToStringLabel,isItemEqualToValue:e=>e.isItemEqualToValue,value:e=>e.value,hasSelectedValue:e=>{const{value:t,multiple:a,itemToStringValue:o}=e;return t==null?!1:a&&Array.isArray(t)?t.length>0:Ca(t,o)!==""},hasNullItemLabel:(e,t)=>t?KE(e.items):!1,open:e=>e.open,mounted:e=>e.mounted,forceMount:e=>e.forceMount,transitionStatus:e=>e.transitionStatus,openMethod:e=>e.openMethod,activeIndex:e=>e.activeIndex,selectedIndex:e=>e.selectedIndex,isActive:(e,t)=>e.activeIndex===t,isSelected:(e,t)=>{const a=e.isItemEqualToValue,o=e.value;return e.multiple?Array.isArray(o)&&o.some(c=>ca(t,c,a)):ca(t,o,a)},isSelectedByFocus:(e,t)=>e.selectedIndex===t,popupProps:e=>e.popupProps,triggerProps:e=>e.triggerProps,triggerElement:e=>e.triggerElement,positionerElement:e=>e.positionerElement,listElement:e=>e.listElement,popupSide:e=>e.popupSide,scrollUpArrowVisible:e=>e.scrollUpArrowVisible,scrollDownArrowVisible:e=>e.scrollDownArrowVisible,hasScrollArrows:e=>e.hasScrollArrows},Tl=1;function XE(e,t){return Math.max(0,e-t)}function fF(e,t){if(t<=0)return 0;const a=Li(e,0,t),o=a,c=t-a,d=o<=Tl,f=c<=Tl;return d&&f?o<=c?0:t:d?0:f?t:a}function pF(e){const{id:t,value:a,defaultValue:o=null,onValueChange:c,open:d,defaultOpen:f=!1,onOpenChange:p,name:h,form:b,autoComplete:y,disabled:v=!1,readOnly:j=!1,required:w=!1,modal:S=!0,actionsRef:N,inputRef:k,onOpenChangeComplete:A,items:P,multiple:R=!1,itemToStringLabel:C,itemToStringValue:E,isItemEqualToValue:D=_h,highlightItemOnHover:I=!0,children:O}=e,{clearErrors:_}=Qc(),{setDirty:z,setTouched:H,setFocused:B,validityData:L,setFilled:F,name:$,disabled:X,validation:K,validationMode:q}=Na(),Q=io({id:t}),G=X||v,J=$??h,[Y,re]=Ka({controlled:a,default:R?o??Ir:o,name:"Select",state:"value"}),[M,U]=Ka({controlled:d,default:f,name:"Select",state:"open"}),Z=x.useRef([]),ae=x.useRef([]),ce=x.useRef(null),W=x.useRef(null),ue=x.useRef(0),ve=x.useRef(null),ee=x.useRef([]),he=x.useRef(!1),te=x.useRef(null),ke=x.useRef(null),le=x.useRef({allowSelectedMouseUp:!1,allowUnselectedMouseUp:!1,dragY:0}),de=x.useRef(!1),Se=x.useRef(Y),{mounted:Te,setMounted:_e,transitionStatus:Ae}=Oi(M),{openMethod:oe,triggerProps:me}=EE(M),pe=Ps(()=>new ou({id:Q,labelId:void 0,modal:S,multiple:R,itemToStringLabel:C,itemToStringValue:E,isItemEqualToValue:D,value:Y,open:M,mounted:Te,transitionStatus:Ae,items:P,forceMount:!1,openMethod:null,activeIndex:null,selectedIndex:null,popupProps:mn,triggerProps:mn,triggerElement:null,positionerElement:null,listElement:null,popupSide:null,scrollUpArrowVisible:!1,scrollDownArrowVisible:!1,hasScrollArrows:!1},{setValue:Kt,setOpen:Kt,handleScrollArrowVisibility:Kt,onOpenChangeComplete:Kt,listRef:Z,popupRef:ce,scrollHandlerRef:W,scrollArrowsMountedCountRef:ue,valueRef:ve,valuesRef:ee,labelsRef:ae,typingRef:he,selectionRef:le,firstItemTextRef:te,selectedItemTextRef:ke,alignItemWithTriggerActiveRef:de,initialValueRef:Se},dF)).current,Ce=pe.useState("activeIndex"),Ne=pe.useState("selectedIndex"),be=pe.useState("triggerElement"),De=pe.useState("positionerElement"),Ge=uV(oe),Ct=oe??Ge,Tt=x.useMemo(()=>R?"":Ca(Y,E),[R,Y,E]),Mt=x.useMemo(()=>R&&Array.isArray(Y)?Y.map(xt=>Ca(xt,E)):Ca(Y,E),[R,Y,E]),ot=Zn(be),Kn=Ie(()=>Mt);tu(ot,Q,Y,Kn,!G,h);const ms=R?Array.isArray(Y)&&Y.length>0:Y!=null&&Tt!=="";Me(()=>{F(ms)},[ms,F]),Me(function(){const jt=bf(ee.current,Y,D,R);jt===null&&(ke.current=null),!M&&pe.set("selectedIndex",jt)},[R,M,Y,D,pe]),ia(Y,()=>{_(J),z($E(Y,L.initialValue,D)),K.change(Y)});const Yn=Ie((xt,jt)=>{p?.(xt,jt),!jt.isCanceled&&(U(xt),!xt&&(jt.reason===xo||jt.reason===ch)&&(H(!0),B(!1),q==="onBlur"&&K.commit(Y)))}),vs=Ie(()=>{_e(!1),pe.update({activeIndex:null,openMethod:null,scrollUpArrowVisible:!1,scrollDownArrowVisible:!1}),A?.(!1)});oa({enabled:!N,open:M,ref:ce,onComplete(){M||vs()}}),x.useImperativeHandle(N,()=>({unmount:vs}),[vs]);const it=Ie((xt,jt)=>{c?.(xt,jt),!jt.isCanceled&&re(xt)}),$t=Ie(xt=>{const jt=XE(xt.scrollHeight,xt.clientHeight),wt=fF(xt.scrollTop,jt),Tr=wt>0,ar=wtLE(Z.current[xt]),onMatch(xt){M?pe.set("activeIndex",xt):it(ee.current[xt],qe(Qn))},onTyping(xt){he.current=xt}}),es=x.useMemo(()=>ta(qt.reference,Dt.reference,nt.reference,rn.reference,me),[rn.reference,qt.reference,Dt.reference,nt.reference,me]),Fn=x.useMemo(()=>ta(mf,qt.floating,Dt.floating,nt.floating),[qt.floating,Dt.floating,nt.floating]),$n=Dt.item??mn;pe.useContextCallback("setValue",it),pe.useContextCallback("setOpen",Yn),pe.useContextCallback("handleScrollArrowVisibility",$t),pe.useContextCallback("onOpenChangeComplete",A),BE(()=>{pe.update({popupProps:Fn,triggerProps:es})}),pe.useSyncedValues({id:Q,modal:S,multiple:R,value:Y,open:M,mounted:Te,transitionStatus:Ae,popupProps:Fn,triggerProps:es,items:P,itemToStringLabel:C,itemToStringValue:E,isItemEqualToValue:D,openMethod:Ct});const Dn=x.useMemo(()=>({disabled:G,readOnly:j,required:w,multiple:R,highlightItemOnHover:I,itemProps:$n}),[G,j,w,R,I,$n]),en=sa(k,K.inputRef),cs=R?void 0:J,Et=x.useMemo(()=>!R||!Array.isArray(Y)||!J?null:Y.map(xt=>{const jt=Ca(xt,E);return s.jsx("input",{type:"hidden",form:b,name:J,value:jt,disabled:G},jt)}),[R,Y,b,J,E,G]);return s.jsxs(HE.Provider,{value:pe,children:[s.jsx(UE.Provider,{value:Dn,children:s.jsx(VE.Provider,{value:Vn,children:O})}),s.jsx("input",{...K.getValidationProps(G,{onFocus(){pe.state.triggerElement?.focus({focusVisible:!0})},onChange(xt){if(xt.nativeEvent.defaultPrevented||G||j)return;const jt=xt.currentTarget.value,wt=qe(Qn,xt.nativeEvent);function Tr(){if(R)return;const ar=jt.toLowerCase();let Fr=ee.current.findIndex(al=>Ca(al,E).toLowerCase()===ar||wo(al,C).toLowerCase()===ar);Fr===-1&&(Fr=ee.current.findIndex((al,ln)=>{const hc=ae.current[ln];return hc!=null&&hc.toLowerCase()===ar}));const Nn=ee.current[Fr];Nn!=null&&it(Nn,wt)}pe.set("forceMount",!0),queueMicrotask(Tr)}}),id:Q&&cs==null?`${Q}-hidden-input`:void 0,form:b,name:cs,autoComplete:y,value:Tt,disabled:G,required:w&&!(R&&ms),readOnly:j,ref:en,style:J?su:_i,tabIndex:-1,"aria-hidden":!0,suppressHydrationWarning:!0}),Et]})}function QE(e,t){return e??t}const Bh=5;function ZE(e,t){const a=mF(t);return e.clientX>=a.left-Bh&&e.clientX<=a.right+Bh&&e.clientY>=a.top-Bh&&e.clientY<=a.bottom+Bh}function mF(e){const t=e.getBoundingClientRect(),a=Cn(e);if($v)return t;const o=a.getComputedStyle(e,"::before"),c=a.getComputedStyle(e,"::after");if(!(o.content!=="none"||c.content!=="none"))return t;const f=parseFloat(o.width)||0,p=parseFloat(o.height)||0,h=parseFloat(c.width)||0,b=parseFloat(c.height)||0,y=Math.max(t.width,f,h),v=Math.max(t.height,p,b),j=y-t.width,w=v-t.height;return{left:t.left-j/2,right:t.right+j/2,top:t.top-w/2,bottom:t.bottom+w/2}}const hF="data-popup-side",gF=400,xF={...Yv,...Ti,popupSide:e=>e?{[hF]:e}:null,value:()=>null},bF=x.forwardRef(function(t,a){const{render:o,className:c,id:d,disabled:f=!1,nativeButton:p=!0,style:h,...b}=t,{setTouched:y,setFocused:v,validationMode:j,validation:w,state:S,disabled:N}=Na(),{labelId:k}=yl(),A=jo(),{readOnly:P,required:R,disabled:C}=sj(),E=N||C||f,D=A.useState("open"),I=A.useState("mounted"),O=A.useState("value"),_=A.useState("triggerProps"),z=A.useState("positionerElement"),H=A.useState("listElement"),B=A.useState("popupSide"),L=A.useState("id"),F=A.useState("labelId"),$=A.useState("hasSelectedValue"),X=I&&z?B:null,K=d??L,q=QE(k,F);io({id:d});const Q=Zn(z),G=x.useRef(null),{getButtonProps:J,buttonRef:Y}=na({disabled:E,native:p}),re=A.useStateSetter("triggerElement"),M=er(),U=er(),Z=er();x.useEffect(()=>{if(D)return Z.start(gF,()=>{A.context.selectionRef.current.allowUnselectedMouseUp=!0,A.context.selectionRef.current.allowSelectedMouseUp=!0}),()=>{Z.clear()};A.context.selectionRef.current={allowSelectedMouseUp:!1,allowUnselectedMouseUp:!1,dragY:0},U.clear()},[D,A,U,Z]);const ae=ta(_,{id:K,role:"combobox","aria-expanded":D,"aria-haspopup":"listbox","aria-controls":D?H?.id??Wm(z)?.id:void 0,"aria-labelledby":q,"aria-readonly":P||void 0,"aria-required":R||void 0,tabIndex:E?-1:0,onFocus(ue){v(!0),D&&A.context.alignItemWithTriggerActiveRef.current&&A.context.setOpen(!1,qe(Qn,ue.nativeEvent)),M.start(0,()=>{A.set("forceMount",!0)})},onBlur(ue){Ze(z,ue.relatedTarget)||(y(!0),v(!1),j==="onBlur"&&w.commit(O))},onMouseDown(ue){if(D)return;const ve=Gt(ue.currentTarget);function ee(he){if(!G.current)return;const te=he.target;Ze(G.current,te)||Ze(Q.current,te)||ZE(he,G.current)||A.context.setOpen(!1,qe(_1,he))}U.start(0,()=>{ve.addEventListener("mouseup",ee,{once:!0})})}},b,J),ce=w.getValidationProps(E,ae);ce.role="combobox";const W={...S,open:D,disabled:E,value:O,readOnly:P,popupSide:X,placeholder:!$};return yt("button",t,{ref:[a,G,Y,re],state:W,stateAttributesMapping:xF,props:ce})}),vF={value:()=>null},yF=x.forwardRef(function(t,a){const{className:o,render:c,children:d,placeholder:f,style:p,...h}=t,b=jo(),y=b.useState("value"),v=b.useState("items"),j=b.useState("itemToStringLabel"),w=b.useState("hasSelectedValue"),S=!w&&f!=null&&d==null,N=b.useState("hasNullItemLabel",S),k={value:y,placeholder:!w};let A=null;return typeof d=="function"?A=d(y):d!=null?A=d:S&&!N?A=f:Array.isArray(y)?A=uF(y,v,j):A=YE(y,v,j),yt("span",t,{state:k,ref:[a,b.context.valueRef],props:[{children:A},h],stateAttributesMapping:vF})}),jF=x.forwardRef(function(t,a){const{render:o,className:c,style:d,...f}=t,b={open:jo().useState("open")};return yt("span",t,{state:b,ref:a,props:[{"aria-hidden":!0,children:"▼"},f],stateAttributesMapping:Kv})}),wF=x.forwardRef(function(t,a){const o=jo(),c=o.useState("mounted"),d=o.useState("forceMount");return c||d?s.jsx(ph,{ref:a,...t}):null}),WE=x.createContext(void 0);function SF(){const e=x.useContext(WE);if(!e)throw new Error(un(59));return e}function Hh(e,t){e&&Object.assign(e.style,t)}const NF={position:"relative",maxHeight:"100%",overflowX:"hidden",overflowY:"auto"},kF=20;function aj(e,t,a,o){const[c,d]=x.useState(!1);Me(()=>{if(!e||!t||a==null){d(!1);return}const f=Gt(a).documentElement.clientWidth,p=a.offsetWidth;d(f>0&&p>0&&p>=f-kF)},[e,t,a]),NE(e&&(!t||c),o)}const CF={position:"fixed"},TF=x.forwardRef(function(t,a){const{anchor:o,className:c,render:d,positionMethod:f,side:p,align:h,sideOffset:b,alignOffset:y,collisionBoundary:v="clipping-ancestors",collisionPadding:j,arrowPadding:w,sticky:S,disableAnchorTracking:N,alignItemWithTrigger:k=!0,collisionAvoidance:A=rT,style:P,...R}=t,C=jo(),E=FE(),D=C.useState("open"),I=C.useState("mounted"),O=C.useState("modal"),_=C.useState("value"),z=C.useState("openMethod"),H=C.useState("positionerElement"),B=C.useState("triggerElement"),L=C.useState("isItemEqualToValue"),F=C.useState("transitionStatus"),$=x.useRef(null),X=x.useRef(null),[K,q]=x.useState(k),Q=I&&K&&z!=="touch";!I&&K!==k&&q(k),x.useImperativeHandle(C.context.alignItemWithTriggerActiveRef,()=>Q),aj((Q||O)&&D,z==="touch",H,B);const G=Th({anchor:o,floatingRootContext:E,positionMethod:f,mounted:I,side:p,sideOffset:b,align:h,alignOffset:y,arrowPadding:w,collisionBoundary:v,collisionPadding:j,sticky:S,disableAnchorTracking:N??Q,collisionAvoidance:A,keepMounted:!0}),J=Q?"none":G.side,Y=Q?CF:G.positionerStyles,re={open:D,side:J,align:G.align,anchorHidden:G.anchorHidden};Me(()=>{C.set("popupSide",G.side)},[C,G.side]);const M=C.useStateSetter("positionerElement"),U=Eh(t,re,{styles:Y,transitionStatus:F,props:R,refs:[a,M],hidden:!I,inert:!D}),Z=x.useRef(0),ae=Ie(W=>{if(C.context.valuesRef.current.length===0)return;const ue=Z.current;Z.current=W.size;const ve=qe(Qn);if(ue!==0&&!C.state.multiple&&_!==null&&cu(C.context.valuesRef.current,_,L)===-1){const he=C.context.initialValueRef.current,ke=he!=null&&cu(C.context.valuesRef.current,he,L)!==-1?he:null;C.context.setValue(ke,ve),ke===null&&(C.set("selectedIndex",null),C.context.selectedItemTextRef.current=null)}if(ue!==0&&C.state.multiple&&Array.isArray(_)){const ee=_.filter(he=>cu(C.context.valuesRef.current,he,L)!==-1);ee.length!==_.length&&(C.context.setValue(ee,ve),ee.length===0&&(C.set("selectedIndex",null),C.context.selectedItemTextRef.current=null))}if(D&&Q){C.update({scrollUpArrowVisible:!1,scrollDownArrowVisible:!1});const ee={height:""};Hh(H,ee),Hh(C.context.popupRef.current,ee)}}),ce=x.useMemo(()=>({...G,side:J,alignItemWithTriggerActive:Q,setControlledAlignItemWithTrigger:q,scrollUpArrowRef:$,scrollDownArrowRef:X}),[G,J,Q,q]);return s.jsx(nj,{elementsRef:C.context.listRef,labelsRef:C.context.labelsRef,onMapChange:ae,children:s.jsxs(WE.Provider,{value:ce,children:[I&&O&&s.jsx(Mh,{inert:Ah(!D),cutout:B}),U]})})}),Uh="base-ui-disable-scrollbar",JE={className:Uh,getElement(e){return s.jsx("style",{nonce:e,href:Uh,precedence:"base-ui:low",children:`.${Uh}{scrollbar-width:none}.${Uh}::-webkit-scrollbar{display:none}`})}},EF=x.createContext(void 0);function eA(e){return x.useContext(EF)}const AF=x.createContext(void 0),RF={disableStyleElements:!1};function OF(){return x.useContext(AF)??RF}const tA="--transform-origin",MF={...Zc,...co},DF=x.forwardRef(function(t,a){const{render:o,className:c,style:d,finalFocus:f,...p}=t,h=jo(),{multiple:b,readOnly:y,highlightItemOnHover:v}=sj(),j=FE(),{side:w,align:S,alignItemWithTriggerActive:N,isPositioned:k,setControlledAlignItemWithTrigger:A}=SF(),P=eA()!=null,R=gf(),{nonce:C,disableStyleElements:E}=OF(),D=h.useState("id"),I=h.useState("open"),O=h.useState("openMethod"),_=h.useState("mounted"),z=h.useState("popupProps"),H=h.useState("transitionStatus"),B=h.useState("triggerElement"),L=h.useState("positionerElement"),F=h.useState("listElement"),$=x.useRef(!1),X=x.useRef(!1),K=x.useRef({}),q=eu(),Q=Ie(re=>{if(!L||!h.context.popupRef.current||!X.current)return;const M=L.style.top==="0px",U=L.style.bottom==="0px";if($.current||!N||!M&&!U){h.context.handleScrollArrowVisibility(re);return}const Z=sA(L),ae=vf(L.getBoundingClientRect().height,"y",Z),ce=Gt(L),W=Cn(L),ue=W.getComputedStyle(L),ve=parseFloat(ue.marginTop),ee=parseFloat(ue.marginBottom),he=nA(W.getComputedStyle(h.context.popupRef.current)),te=Math.min(ce.documentElement.clientHeight-ve-ee,he),ke=re.scrollTop,le=Vh(re);let de=null;const Se=oe=>{L.style.height=`${oe}px`},Te=M?le-ke:ke,_e=Math.min(ae+Te,te);if(Te<=Tl){const oe=Li(Te,0,te-ae);oe>0&&Se(ae+oe),re.scrollTop=M?le:0,te-(ae+oe)<=Tl&&($.current=!0),h.context.handleScrollArrowVisibility(re);return}if(te-_e>Tl)de=M?1/0:0;else if(U&&keTl&&(re.scrollTop=oe)}Ae>=te-Tl&&($.current=!0),h.context.handleScrollArrowVisibility(re)});x.useImperativeHandle(h.context.scrollHandlerRef,()=>Q,[Q]),oa({open:I,ref:h.context.popupRef,onComplete(){I&&h.context.onOpenChangeComplete(!0)}});const G={open:I,transitionStatus:H,side:w,align:S};Me(()=>{!L||!h.context.popupRef.current||Object.keys(K.current).length||(K.current={top:L.style.top||"0",left:L.style.left||"0",right:L.style.right,height:L.style.height,bottom:L.style.bottom,minHeight:L.style.minHeight,maxHeight:L.style.maxHeight,marginTop:L.style.marginTop,marginBottom:L.style.marginBottom})},[h,L]),Me(()=>{I||N||(X.current=!1,$.current=!1,Hh(L,K.current))},[I,N,L]),Me(()=>{const re=h.context.popupRef.current;if(!I||!B||!L||!re||N&&!k||h.state.transitionStatus==="ending")return;if(X.current=!0,re.style.removeProperty(tA),!N){q.request(()=>h.context.handleScrollArrowVisibility(F||re));return}const M=IF(re);try{let U=h.context.selectedItemTextRef.current;U?.isConnected||(U=!h.select("hasSelectedValue")&&h.context.firstItemTextRef.current?.isConnected?h.context.firstItemTextRef.current:null);const Z=h.context.valueRef.current,ae=Cn(L),ce=ae.getComputedStyle(L),W=ae.getComputedStyle(re),ue=Gt(B),ve=sA(B),ee=Fh(B.getBoundingClientRect(),ve),he=Fh(L.getBoundingClientRect(),ve),te=ee.height,ke=F||re,le=ke.scrollHeight,de=parseFloat(W.borderBottomWidth),Se=parseFloat(ce.marginTop)||10,Te=parseFloat(ce.marginBottom)||10,_e=parseFloat(ce.minHeight)||100,Ae=nA(W),oe=5,me=5,pe=20,Ce=ue.documentElement.clientHeight-Se-Te,Ne=ue.documentElement.clientWidth,be=Ce-ee.bottom+te;let De,Ge=R==="rtl"?ee.right-he.width:ee.left,Ct=0;if(U&&Z){const rn=Fh(Z.getBoundingClientRect(),ve);De=Fh(U.getBoundingClientRect(),ve),Ge=he.left+(R==="rtl"?rn.right-De.right:rn.left-De.left);const nt=rn.top-ee.top+rn.height/2;Ct=De.top-he.top+De.height/2-nt}const Tt=be+Ct+Te+de;let Mt=Math.min(Ce,Tt);const ot=Ce-Se-Te,Kn=Tt-Mt,ms=Ne-me;L.style.left=`${Li(Ge,oe,ms-he.width)}px`,L.style.height=`${Mt}px`,L.style.maxHeight="none",L.style.marginTop=`${Se}px`,L.style.marginBottom=`${Te}px`,re.style.height="100%";const Yn=Vh(ke),vs=Kn>=Yn-Tl;vs&&(Mt=Math.min(Ce,he.height)-(Kn-Yn));const it=ee.topCe-pe||Math.ceil(Mt)+Tl=ot?"0":`${rn}px`,L.style.height=`${Mt}px`,ke.scrollTop=Vh(ke)}else L.style.bottom="0",ke.scrollTop=Kn;if(De){const rn=he.top,nt=he.height,Dt=De.top+De.height/2,qt=Li(nt>0?(Dt-rn)/nt*100:50,0,100);re.style.setProperty(tA,`50% ${qt}%`)}(Vn===Ce||Mt>=Ae)&&($.current=!0),h.context.handleScrollArrowVisibility(ke),v&&h.state.selectedIndex===null&&h.state.activeIndex===null&&h.context.listRef.current[0]!=null&&h.set("activeIndex",0)}finally{M()}},[h,I,L,B,N,A,q,F,v,R,k]),x.useEffect(()=>{if(!N||!L||!I)return;const re=Cn(L);function M(U){h.context.setOpen(!1,qe(k4,U))}return St(re,"resize",M)},[h,N,L,I]);const J={...F?{role:"presentation"}:{role:"listbox","aria-multiselectable":b||void 0,"aria-readonly":y||void 0,id:`${D}-list`},onKeyDown(re){P&&Rh.has(re.key)&&re.stopPropagation()},onScroll(re){F||Q(re.currentTarget)},...N&&{style:F?{height:"100%"}:NF},className:!F&&N?JE.className:void 0},Y=yt("div",t,{ref:[a,h.context.popupRef],state:G,stateAttributesMapping:MF,props:[z,J,xf(H),p]});return s.jsxs(x.Fragment,{children:[!E&&JE.getElement(C),s.jsx(mh,{context:j,modal:!1,disabled:!_,openInteractionType:O,returnFocus:f,restoreFocus:!0,children:Y})]})});function nA(e){const t=e.maxHeight;return t.endsWith("px")&&parseFloat(t)||1/0}function Vh(e){return XE(e.scrollHeight,e.clientHeight)}function sA(e){return kT.getScale(e)}function vf(e,t,a){return e/a[t]}function Fh(e,t){return nf({x:vf(e.x,"x",t),y:vf(e.y,"y",t),width:vf(e.width,"x",t),height:vf(e.height,"y",t)})}const rA=[["transform","none"],["scale","1"],["translate","0 0"]];function IF(e){const{style:t}=e,a={};for(const[o,c]of rA)a[o]=t.getPropertyValue(o),t.setProperty(o,c,"important");return()=>{for(const[o]of rA){const c=a[o];c?t.setProperty(o,c):t.removeProperty(o)}}}const aA=x.createContext(void 0);function lj(){const e=x.useContext(aA);if(!e)throw new Error(un(57));return e}const PF=x.memo(x.forwardRef(function(t,a){const{render:o,className:c,style:d,value:f=null,label:p,disabled:h=!1,nativeButton:b=!1,...y}=t,v=x.useRef(null),j=tj({guess:!0,label:p,textRef:v}),w=jo(),{itemProps:S,multiple:N,disabled:k,readOnly:A}=sj(),P=k||h,R=w.useState("isActive",j.index),C=w.useState("open"),E=w.useState("isSelected",f),D=w.useState("isSelectedByFocus",j.index),I=w.useState("isItemEqualToValue"),O=j.index,_=x.useRef(null);Me(()=>{const G=w.context.valuesRef.current;return G[O]=f,()=>{delete G[O]}},[O,f,w]),Me(()=>{const G=w.state.value,J=w.state.selectedIndex;let Y=J,re;N&&Array.isArray(G)?(Y=qE(O,f,w.context.valuesRef.current,G,I,J),re=Y===O,O===J&&!re&&(w.context.selectedItemTextRef.current=null)):(re=G!==void 0&&ca(f,G,I),re&&(Y=O)),w.set("selectedIndex",Y),re&&v.current&&(w.context.selectedItemTextRef.current=v.current)},[O,N,I,w,f]);const z=x.useRef("mouse"),H=x.useRef(!1),{getButtonProps:B,buttonRef:L}=na({disabled:P,focusableWhenDisabled:!0,native:b,composite:!0}),F={disabled:P,selected:E,highlighted:R};function $(G){if(k||A)return;const J=w.state.value;if(N){const Y=Array.isArray(J)?J:[],re=E?GE(Y,f,I):[...Y,f];w.context.setValue(re,qe(af,G))}else w.context.setValue(f,qe(af,G)),w.context.setOpen(!1,qe(af,G))}function X(){w.context.selectionRef.current.dragY=0}const K={role:"option","aria-selected":E,tabIndex:C&&R?0:-1,onKeyDown(G){w.set("activeIndex",O),G.key===" "&&w.context.typingRef.current&&G.preventDefault()},onClick(G){const J=z.current!=="touch",Y=G.nativeEvent.pointerType,re=J&&Jm(G.nativeEvent)&&(Y!==void 0||R),M=J&&!re&&!H.current;H.current=!1,!(P||M)&&$(G.nativeEvent)},onPointerEnter(G){z.current=G.pointerType},onPointerMove(G){if(G.pointerType==="mouse"&&G.buttons===1){const J=w.context.selectionRef.current;J.dragY+=G.movementY,J.dragY**2>=64&&(J.allowUnselectedMouseUp=!0)}},onPointerDown(G){z.current=G.pointerType,H.current=!0,X()},onMouseUp(){if(X(),P||z.current==="touch"||H.current)return;const G=!w.context.selectionRef.current.allowSelectedMouseUp&&E,J=!w.context.selectionRef.current.allowUnselectedMouseUp&&!E;G||J||(H.current=!0,_.current?.click(),H.current=!1)}},q=yt("div",t,{ref:[L,a,j.ref,_],state:F,props:[S,K,y,B]}),Q=x.useMemo(()=>({selected:E,index:O,textRef:v,selectedByFocus:D}),[E,O,v,D]);return s.jsx(aA.Provider,{value:Q,children:q})})),_F=x.forwardRef(function(t,a){const{selected:o}=lj();return t.keepMounted||o?s.jsx(LF,{...t,ref:a}):null}),LF=x.memo(x.forwardRef((e,t)=>{const{render:a,className:o,style:c,keepMounted:d,...f}=e,{selected:p}=lj(),h=x.useRef(null),{transitionStatus:b,setMounted:y}=Oi(p),j=yt("span",e,{ref:[t,h],state:{selected:p,transitionStatus:b},props:[{"aria-hidden":!0,children:"✔️"},f],stateAttributesMapping:co});return oa({batch:!0,enabled:!p,open:p,ref:h,onComplete(){p||y(!1)}}),j})),zF=x.memo(x.forwardRef(function(t,a){const{index:o,textRef:c,selectedByFocus:d}=lj(),f=jo(),{render:p,className:h,style:b,...y}=t,v=x.useCallback(w=>{w&&(o===0&&(f.context.firstItemTextRef.current=w),d&&(f.context.selectedItemTextRef.current=w))},[f,o,d]);return yt("div",t,{ref:[v,a,c],props:y})})),ut=pF,dt=yF;function ft({className:e,children:t,...a}){return s.jsxs(bF,{"data-slot":"select-trigger",className:Ue("inline-flex h-[30px] min-w-0 cursor-pointer items-center justify-between gap-1.5 rounded-md border border-control-border bg-control px-2.5","[&>span]:min-w-0 [&>span]:truncate","text-[12.5px] font-normal text-foreground","transition-colors duration-150 ease-out","hover:border-control-border-hover hover:bg-control-hover","focus-visible:border-primary focus-visible:shadow-focus focus-visible:outline-none","data-disabled:cursor-not-allowed data-disabled:bg-control data-disabled:text-muted-foreground data-disabled:opacity-60",e),...a,children:[t,s.jsx(jF,{className:"flex flex-none text-muted-foreground transition-transform duration-[180ms] data-popup-open:rotate-180",children:s.jsx("svg",{viewBox:"0 0 10 6",className:"size-2.5","aria-hidden":"true",children:s.jsx("path",{d:"M1 1l4 4 4-4",stroke:"currentColor",strokeWidth:"1.4",fill:"none",strokeLinecap:"round",strokeLinejoin:"round"})})})]})}function pt({className:e,sideOffset:t=4,children:a,...o}){return s.jsx(wF,{children:s.jsx(TF,{sideOffset:t,alignItemWithTrigger:!1,className:"z-[35]",children:s.jsx(DF,{"data-slot":"select-content",className:Ue("max-h-[260px] min-w-[var(--anchor-width)] overflow-y-auto overscroll-contain rounded-lg border border-border-strong bg-raised p-[5px] shadow-3","outline-none","data-open:animate-in data-open:fade-in-0","data-closed:animate-out data-closed:fade-out-0",e),...o,children:a})})})}function $e({className:e,children:t,...a}){return s.jsxs(PF,{"data-slot":"select-item",className:Ue("flex min-h-7 cursor-pointer items-center gap-2 rounded-sm px-[9px] text-[12.5px] leading-[1.4] text-foreground select-none","transition-colors duration-[120ms] ease-out","data-[highlighted]:bg-nav-hover","data-[selected]:bg-primary-soft data-[selected]:font-semibold data-[selected]:text-primary-fg","data-[highlighted]:data-[selected]:bg-primary-tint","data-disabled:pointer-events-none data-disabled:font-normal data-disabled:text-muted-foreground",e),...a,children:[s.jsx(zF,{className:"min-w-0 flex-1 truncate",children:t}),s.jsx(_F,{className:"flex flex-none text-primary",children:s.jsx("svg",{viewBox:"0 0 12 12",className:"size-3","aria-hidden":"true",children:s.jsx("path",{d:"M2.5 6.2 4.8 8.5 9.5 3.8",stroke:"currentColor",strokeWidth:"1.8",fill:"none",strokeLinecap:"round",strokeLinejoin:"round"})})})]})}function En({options:e,value:t,onValueChange:a,disabled:o=!1,className:c,...d}){const f=x.useRef(null),p=x.useRef(new Map),[h,b]=x.useState({left:0,width:0,ready:!1}),[y,v]=x.useState(!1),j=x.useCallback(()=>{const w=f.current,S=p.current.get(t);if(!w||!S){b(R=>R.ready?{...R,ready:!1}:R);return}const N=w.getBoundingClientRect(),k=S.getBoundingClientRect(),A=k.left-N.left-w.clientLeft,P=k.width;b(R=>R.ready&&R.left===A&&R.width===P?R:{left:A,width:P,ready:!0})},[t]);return x.useLayoutEffect(()=>{j();const w=f.current;if(!w||typeof ResizeObserver>"u")return;const S=new ResizeObserver(()=>j());S.observe(w);for(const N of p.current.values())S.observe(N);return()=>S.disconnect()},[j,e]),x.useEffect(()=>{if(!h.ready||y)return;const w=requestAnimationFrame(()=>v(!0));return()=>cancelAnimationFrame(w)},[h.ready,y]),s.jsxs(zE,{ref:f,"data-slot":"segmented-control",className:Ue("relative inline-flex gap-0.5 rounded-md border border-control-border bg-control p-0.5",c),value:t,disabled:o,onValueChange:w=>a(w),...d,children:[s.jsx("span",{"aria-hidden":"true","data-slot":"segmented-thumb","data-ready":h.ready?"true":"false","data-animate":y?"true":"false",className:Ue("pointer-events-none absolute top-0.5 bottom-0.5 left-0 rounded-sm bg-primary-soft","data-[ready=false]:opacity-0","data-[animate=true]:transition-[transform,width] data-[animate=true]:duration-200 data-[animate=true]:ease-in-out"),style:{transform:`translateX(${h.left}px)`,width:h.width}}),e.map(w=>s.jsxs(PE,{value:w.value,"data-slot":"segmented-item",disabled:w.disabled,"data-zero":w.count===0?"true":void 0,className:Ue("group/seg relative z-[1] inline-flex h-6 items-center gap-[5px] rounded-sm border-0 bg-transparent px-2.5","text-xs font-medium whitespace-nowrap text-subtle select-none","transition-[background-color,color] duration-150 ease-out","hover:bg-nav-hover hover:text-foreground","data-checked:bg-transparent data-checked:text-primary-fg data-checked:hover:bg-transparent","data-disabled:cursor-not-allowed data-disabled:opacity-40","data-[zero=true]:data-unchecked:opacity-45"),ref:S=>{S?p.current.set(w.value,S):p.current.delete(w.value)},children:[w.label,w.count!==void 0&&s.jsx("span",{className:Ue("min-w-4 rounded-pill bg-surface-3 px-[5px] text-center text-[10.5px] font-semibold tabular-nums text-muted-foreground","group-data-checked/seg:bg-primary-tint group-data-checked/seg:text-primary-fg"),children:w.count})]},w.value))]})}const BF=x.createContext(void 0);function HF(){return x.useContext(BF)}const UF=x.forwardRef(function(t,a){const{className:o,defaultPressed:c=!1,disabled:d=!1,form:f,onPressedChange:p,pressed:h,render:b,type:y,value:v,nativeButton:j=!0,style:w,...S}=t,N=ka(v||void 0),k=HF(),A=k?.value??[],P=(d||k?.disabled)??!1,[R,C]=Ka({controlled:k?N!==void 0&&A.indexOf(N)>-1:h,default:c,name:"Toggle",state:"pressed"}),{getButtonProps:E,buttonRef:D}=na({disabled:P,native:j}),I={disabled:P,pressed:R},O=[D,a],_=[{"aria-pressed":R,onClick(B){const L=!R,F=qe(Qn,B.nativeEvent);p?.(L,F),!F.isCanceled&&(N&&k?.setGroupValue?.(N,L,F),!F.isCanceled&&C(L))}},S,E],z=yt("button",t,{enabled:!k,state:I,ref:O,props:_}),H=x.useMemo(()=>({disabled:P,focusableWhenDisabled:!1}),[P]);return k?s.jsx(ME,{tag:"button",render:b,className:o,style:w,metadata:H,state:I,refs:O,props:_}):z}),VF=Xc(["inline-flex shrink-0 cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap border","font-medium transition-[background-color,border-color,color] duration-150 ease-out","outline-none select-none disabled:pointer-events-none disabled:opacity-45","[&_svg]:pointer-events-none [&_svg]:shrink-0","data-pressed:border-primary-bd data-pressed:bg-primary-soft data-pressed:font-semibold data-pressed:text-primary-fg","data-pressed:hover:bg-primary-tint"],{variants:{variant:{default:"border-transparent bg-transparent text-subtle hover:bg-nav-hover hover:text-foreground",outline:"border-control-border bg-control text-foreground shadow-1 hover:border-control-border-hover hover:bg-control-hover"},size:{default:"h-[30px] rounded-md px-3 text-[12.5px]",xs:"h-6 rounded-sm px-2 text-[11.5px]",sm:"h-[26px] rounded-sm px-2.5 text-[12px]",lg:"h-9 rounded-md px-4 text-[13px]"}},defaultVariants:{variant:"default",size:"default"}});function FF({className:e,variant:t,size:a,...o}){return s.jsx(UF,{"data-slot":"toggle",className:Ue(VF({variant:t,size:a}),e),...o})}function $F(e,t,a,o,c,d,f,p,h,b=2){const y=i4(a.current,{event:e,orientation:o,loopFocus:c,rtl:d,cols:b,disabledIndices:f,minIndex:p,maxIndex:h,prevIndex:t>h?p:t,stopEvent:!0});return mo(a.current,y)?void 0:y}const lA=x.createContext(void 0),oA=x.createContext(void 0),iA=x.createContext(void 0),cA=x.createContext(!1),uA=x.createContext("");function Ta(){const e=x.useContext(lA);if(!e)throw new Error(un(22));return e}function $h(){const e=x.useContext(oA);if(!e)throw new Error(un(23));return e}function yf(){const e=x.useContext(iA);if(!e)throw new Error(un(24));return e}function dA(){return x.useContext(uA)}function qF(){return x.useContext(cA)}const GF={id:e=>e.id,labelId:e=>e.labelId,items:e=>e.items,selectedValue:e=>e.selectedValue,hasSelectionChips:e=>{const t=e.selectedValue;return Array.isArray(t)&&t.length>0},hasSelectedValue:e=>{const{selectedValue:t,selectionMode:a}=e;return t==null?!1:a==="multiple"&&Array.isArray(t)?t.length>0:!0},hasNullItemLabel:(e,t)=>t?KE(e.items):!1,open:e=>e.open,mounted:e=>e.mounted,forceMounted:e=>e.forceMounted,inline:e=>e.inline,activeIndex:e=>e.activeIndex,selectedIndex:e=>e.selectedIndex,isActive:(e,t)=>e.activeIndex===t,isSelected:(e,t)=>{const a=e.isItemEqualToValue,o=e.selectedValue;return Array.isArray(o)?o.some(c=>ca(t,c,a)):ca(t,o,a)},transitionStatus:e=>e.transitionStatus,popupProps:e=>e.popupProps,listProps:e=>e.listProps,inputProps:e=>e.inputProps,triggerProps:e=>e.triggerProps,itemProps:e=>e.itemProps,positionerElement:e=>e.positionerElement,listElement:e=>e.listElement,popupId:e=>e.popupId,triggerElement:e=>e.triggerElement,inputElement:e=>e.inputElement,inputGroupElement:e=>e.inputGroupElement,popupSide:e=>e.popupSide,openMethod:e=>e.openMethod,inputInsidePopup:e=>e.inputInsidePopup,inputOwnsFormValue:e=>e.inputOwnsFormValue,selectionMode:e=>e.selectionMode,name:e=>e.name,form:e=>e.form,disabled:e=>e.disabled,readOnly:e=>e.readOnly,required:e=>e.required,grid:e=>e.grid,virtualized:e=>e.virtualized,itemToStringLabel:e=>e.itemToStringLabel,isItemEqualToValue:e=>e.isItemEqualToValue,modal:e=>e.modal,autoHighlight:e=>e.autoHighlight};function fA(e){return e==null?void 0:`${e}-popup`}function KF(e,t){return(a,o)=>a==null?!1:e.contains(a,o,t)}const pA=new Map;function YF(e={}){const t={usage:"search",sensitivity:"base",ignorePunctuation:!0,...e},a=`${hy(e.locale)}|${JSON.stringify(t)}`,o=pA.get(a);if(o)return o;const c=new Intl.Collator(e.locale,t),d={contains(f,p,h){if(!p)return!0;const b=wo(f,h);for(let y=0;y<=b.length-p.length;y+=1)if(c.compare(b.slice(y,y+p.length),p)===0)return!0;return!1},startsWith(f,p,h){if(!p)return!0;const b=wo(f,h);return c.compare(b.slice(0,p.length),p)===0},endsWith(f,p,h){if(!p)return!0;const b=wo(f,h),y=p.length;return b.length>=y&&c.compare(b.slice(b.length-y),p)===0}};return pA.set(a,d),d}const XF=YF;function QF(e,t=!1){const{overflowY:a}=yr(e);return a!=="auto"&&a!=="scroll"?!1:t?e.clientHeight>0:e.scrollHeight>e.clientHeight}const mA=Symbol("none"),oj={value:mA,index:-1};function ZF(e,t,a){const o=e.get(t);if(o!==void 0||a===_h)return o;for(const[c,d]of e)if(ca(c,t,a))return d}function WF(e){const{id:t,onOpenChangeComplete:a,defaultSelectedValue:o=null,selectedValue:c,onSelectedValueChange:d,defaultInputValue:f,inputValue:p,open:h,defaultOpen:b=!1,selectionMode:y,onItemHighlighted:v,name:j,form:w,disabled:S=!1,readOnly:N=!1,required:k=!1,inputRef:A,grid:P=!1,items:R,filteredItems:C,filter:E,filterQuery:D,openOnInputClick:I=!0,autoHighlight:O=!1,keepHighlight:_=!1,highlightItemOnHover:z=!0,loopFocus:H=!0,itemToStringLabel:B,itemToStringValue:L,isItemEqualToValue:F=_h,virtualized:$=!1,inline:X=!1,fillInputOnItemPress:K=!0,modal:q=!1,limit:Q=-1,autoComplete:G="list",formAutoComplete:J,locale:Y,submitOnItemClick:re=!1}=e,{clearErrors:M}=Qc(),{setDirty:U,validityData:Z,setFilled:ae,name:ce,disabled:W,setTouched:ue,setFocused:ve,validationMode:ee,validation:he}=Na(),te=gf(),ke=io({id:t}),le=XF({locale:Y}),de=Array.isArray(R)?null:R;if(de&&typeof de.label!="function")throw new Error(un(100));const Se=de?de.data:R,Te=de?.value,_e=Te?void 0:Se,Ae=x.useMemo(()=>{if(!C||!Te)return;const Oe=zh(C),Be=Oe.map(Te);let lt;return{values:Be,findItem(Qt,In){if(!lt){lt=new Map;for(let gn=0;gnde?Oe=>de.label(Oe,F,Be=>{const lt=Ae?.findItem(Be,F);return lt!=null?de.itemLabel(lt):wo(Be,B)}):B,[de,B,Ae,F]),me=x.useMemo(()=>de?Object.assign(Oe=>de.itemLabel(Oe),{selected:Oe=>wo(Oe,oe)}):B,[de,oe,B]);function pe(Oe){return wo(Oe,oe)}const[Ce,Ne]=x.useState(!1),[be,De]=x.useState(null),Ge=x.useRef(be),Ct=x.useRef([]),Tt=x.useRef([]),Mt=x.useRef(null),ot=x.useRef(null),Kn=x.useRef(null),ms=x.useRef(null),Yn=x.useRef(null),vs=x.useRef(!0),it=x.useRef(!1),$t=x.useRef(null),Vn=x.useRef(null),rn=x.useRef(null),nt=x.useRef(oj),Dt=x.useRef(null),qt=x.useRef([]),es=x.useRef(null),Fn=W||S,$n=ce??j,Dn=y==="multiple",en=y==="single",cs=p!==void 0||f!==void 0,Et=Se!==void 0,xt=C!==void 0;let jt;O==="always"?jt="always":jt=O?"input-change":!1;const[wt,Tr]=Ka({controlled:c,default:Dn?o??Ir:o,name:"Combobox",state:"selectedValue"}),ar=x.useMemo(()=>E===null?()=>!0:E!==void 0?E:KF(le,me),[E,le,me]),Fr=Ps(()=>cs?f??"":en?pe(wt):"").current,[Nn,al]=Ka({controlled:p,default:Fr,name:"Combobox",state:"inputValue"}),[ln,hc]=Ka({controlled:h,default:b,name:"Combobox",state:"open"}),sb=rj(Se),mr=!ln&&be!==null?be:String(Nn).trim(),Pa=en?pe(wt):"",Go=en&&!Ce&&mr!==""&&Pa.length===mr.length&&le.contains(Pa,mr),Ko=Go?"":D??mr,Fp=Et&&xt&&Go&&(!de||de.hasValue(wt,F)),gc=x.useMemo(()=>Se?zh(Se):Ir,[Se]),Yo=x.useMemo(()=>{if(C&&!Fp)return C;if(!Se)return Ir;if(sb){const Be=Se,lt=[];let Qt=0;for(const In of Be){if(Q>-1&&Qt>=Q)break;const gn=Q>-1?Q-Qt:1/0,Pn=Ko===""?In.items.slice(0,gn):[];if(Ko!=="")for(const hs of In.items){if(Pn.length>=gn)break;ar(hs,Ko,me)&&Pn.push(hs)}if(Pn.length>0){const hs={...In,items:Pn};lt.push(hs),Qt+=Pn.length}}return lt}if(Ko==="")return Q>-1?gc.slice(0,Q):gc;const Oe=[];for(const Be of gc){if(Q>-1&&Oe.length>=Q)break;ar(Be,Ko,me)&&Oe.push(Be)}return Oe},[C,Fp,Se,sb,Ko,Q,ar,me,gc]),Os=x.useMemo(()=>{if(Ae&&Yo===C)return Ae.values;const Oe=zh(Yo);return Te?Oe.map(Be=>Te(Be)):Oe},[Yo,C,Ae,Te]),et=Ps(()=>{let Oe=null;return X&&ln&&Et&&y!=="none"&&(Oe=bf(Os,wt,F,Dn)),new ou({id:ke,labelId:void 0,selectedValue:wt,open:ln,items:_e,selectionMode:y,name:$n,form:w,disabled:Fn,readOnly:N,required:k,grid:P,virtualized:$,openOnInputClick:I,itemToStringLabel:oe,isItemEqualToValue:F,modal:q,autoHighlight:jt,submitOnItemClick:re,hasInputValue:cs,mounted:!1,forceMounted:!1,transitionStatus:"idle",inline:X,activeIndex:null,selectedIndex:Oe,popupProps:{},listProps:{},inputProps:{},triggerProps:{},itemProps:mn,positionerElement:null,listElement:null,popupId:void 0,triggerElement:null,inputElement:null,inputGroupElement:null,popupSide:null,openMethod:null,inputInsidePopup:!0,inputOwnsFormValue:y==="none"},{onOpenChangeComplete:Kt,setOpen:Kt,setInputValue:Kt,setSelectedValue:Kt,setIndices:Kt,handleSelection:Kt,forceMount:Kt,requestSubmit:Kt,listRef:Ct,labelsRef:Tt,popupRef:Mt,emptyRef:Yn,inputRef:ot,startDismissRef:Kn,endDismissRef:ms,keyboardActiveRef:vs,chipsContainerRef:$t,clearRef:Vn,valuesRef:qt,pointerDownItemRef:es,selectionEventRef:rn},GF)}).current,zl=y==="none"?Nn:wt,rb=x.useMemo(()=>y==="none"?zl:Array.isArray(wt)?wt.map(Oe=>Ca(Oe,L)):Ca(wt,L),[zl,L,y,wt]),_a=Ie(v),Ms=Ie(a),Ys=et.useState("activeIndex"),Xo=et.useState("selectedIndex"),xc=et.useState("positionerElement"),ab=et.useState("listElement"),bc=et.useState("triggerElement"),vc=et.useState("inputElement"),yc=et.useState("inputGroupElement"),Ds=et.useState("inline"),lr=et.useState("inputInsidePopup"),Bl=et.useState("inputOwnsFormValue"),Hl=en&&!lr&&Nn===Pa,jc=Zn(bc),{mounted:Qo,setMounted:ys,transitionStatus:$p}=Oi(ln),{openMethod:lb,triggerProps:qp}=EE(ln),Ul=Ie(()=>rb);tu(lr?jc:ot,ke,zl,Ul,!Fn,j);const Vl=Ie(()=>{Se?Tt.current=Os.map(pe):et.set("forceMounted",!0)}),Zo=Ie((Oe,Be,lt)=>{if(Be===-1){if(nt.current===oj)return;nt.current=oj}else nt.current={value:Oe,index:Be};_a(Oe,C4(lt,void 0,{index:Be}))}),$r=Ie(Oe=>{const Be={};Oe.activeIndex!==void 0&&(Be.activeIndex=Oe.activeIndex),Oe.selectedIndex!==void 0&&(Be.selectedIndex=Oe.selectedIndex),et.update(Be);const lt=Oe.activeIndex;if(lt===void 0)return;const Qt=Oe.type||Qn;lt===null?Zo(void 0,-1,Qt):Zo(qt.current[lt],lt,Qt)}),qr=Ie((Oe,Be)=>{if(e.onInputValueChange?.(Oe,Be),!Be.isCanceled){if(it.current=Be.reason===Ya,Be.reason===lf){ln&&be!==null&&De(null);const lt=Be.event,Qt=lt.inputType;if(lt.type==="compositionend"||Qt!=null&&Qt!==""&&Qt!=="insertReplacementText"){const gn=Oe.trim()!=="";gn&&Ne(!0),Dt.current={hasQuery:gn};const Pn=et.state.listElement;if(!et.state.virtualized&&Pn){const hs=Mt.current;for(const Er of Kc(Pn.firstElementChild??Pn)){if(!pn(Er)||(hs?!Ze(hs,Er):Er.getAttribute("role")==="dialog"))break;if(QF(Er)){Er.scrollTop=0;break}}}gn&&jt&&et.state.activeIndex==null&&(ln||Ds)&&et.set("activeIndex",0)}}else Be.reason===Ya&&Oe===""&&et.state.inputInsidePopup&&(Dt.current={hasQuery:!1,selection:!0});al(Oe)}}),ob=Ie(Oe=>{const Be=!Oe&&lr&&!Ds&&Nn!==""&&(String(Nn).trim()===be||Nn===Pa);!Oe&&(Be||Nn===""||Hl)&&Ne(!1),De(null),Be&&qr("",qe(Ya))}),It=Ie((Oe,Be)=>{if(ln!==Oe&&(Be.reason===nu&&Et&&Os.length===0&&!Yn.current&&Be.allowPropagation(),e.onOpenChange?.(Oe,Be),!Be.isCanceled&&(Oe&&be!==null&&ob(Be.reason===lf),!Oe&&Ce&&(en?(Ds||De(mr),mr===""&&Ne(!1)):Dn&&(Ds||De(mr),lr&&$r({activeIndex:null}),(!lr||Ds)&&qr("",qe(Ya,Be.event,void 0,{isItemPress:Be.reason===af})))),hc(Oe),!Oe&&lr&&(Be.reason===xo||Be.reason===ch)&&(ue(!0),ve(!1),ee==="onBlur")))){const lt=y==="none"?Nn:wt;he.commit(lt)}}),wc=Ie((Oe,Be)=>{if(d?.(Oe,Be),Be.isCanceled)return;Tr(Oe),(y==="none"&&Mt.current&&K||en&&!et.state.inputInsidePopup)&&qr(pe(Oe),qe(Be.reason,Be.event))}),od=Ie((Oe,Be)=>{const lt=fs(Oe),Qt=rn.current??Oe;rn.current=null;const In=qe(af,Qt),gn=lt?.closest("a")?.getAttribute("href");if(gn){gn.startsWith("#")&&It(!1,In);return}if(Dn){const Pn=Array.isArray(wt)?wt:[],hs=Lh(Pn,Be,F),Er=hs?GE(Pn,Be,F):[...Pn,Be];if(wc(Er,In),In.isCanceled||!(ot.current?ot.current.value.trim()!=="":!1))return;if(et.state.inputInsidePopup){qr("",qe(Ya,In.event,void 0,{isItemPress:!0}));const Kr=Dt.current;Kr&&!hs&&(Kr.toggledValue=Be)}else It(!1,In)}else{if(wc(Be,In),In.isCanceled)return;It(!1,In)}}),id=Ie(()=>{const Oe=he.inputRef.current?.form??et.state.inputElement?.form;Oe&&typeof Oe.requestSubmit=="function"&&Oe.requestSubmit()}),Gr=Ie(()=>{if(ys(!1),Ms?.(!1),Ne(!1),De(null),$r(y==="none"?{activeIndex:null,selectedIndex:null}:{activeIndex:null}),Dn&&ot.current&&ot.current.value!==""&&!it.current&&qr("",qe(Ya)),en)if(et.state.inputInsidePopup)ot.current&&ot.current.value!==""&&qr("",qe(Ya));else{const Oe=pe(wt);ot.current&&ot.current.value!==Oe&&qr(Oe,qe(Oe===""?Ya:Qn))}}),hr=x.useMemo(()=>Ds&&xc?{current:xc.closest('[role="dialog"]')}:Mt,[Ds,xc]);oa({enabled:!e.actionsRef,open:ln,ref:hr,onComplete(){ln||Gr()}}),x.useImperativeHandle(e.actionsRef,()=>({unmount:Gr}),[Gr]),Me(function(){const Be=Ge.current!==null&&be===null;if(Ge.current=be,ln&&(!Be||!Et)||(ln||(es.current=null),y==="none"))return;const lt=Et?Os:qt.current;$r({selectedIndex:bf(lt,wt,F,Dn)})},[ln,be,wt,y,Dn,Et,Os,F,$r]),Me(()=>{Se&&(qt.current=Os,Ct.current.length=Os.length)},[Se,Os]),Me(()=>{const Oe=Dt.current;if(Oe){const hs=ln||Ds||et.state.positionerElement?.hidden===!1;if(Oe.hasQuery)jt&&hs&&et.set("activeIndex",0),Dt.current=null;else if(String(Nn).trim()===""&&(Dt.current=null,hs)){const Er=Oe.selection;jt==="always"&&!Er&&et.state.selectionMode==="none"&&et.set("activeIndex",0),queueMicrotask(()=>{if(!et.state.open&&!et.state.inline||ot.current&&ot.current.value.trim()!=="")return;const ha=et.state.selectedValue,Kr=et.state.selectionMode==="multiple";if(Kr&&Array.isArray(ha)?ha.length>0:et.state.selectionMode!=="none"&&ha!=null){const Bs=Et||xt?Os:qt.current,za=cu(Bs,Oe.toggledValue,et.state.isItemEqualToValue);et.set("activeIndex",za!==-1?za:bf(Bs,ha,et.state.isItemEqualToValue,Kr))}else Er?et.set("activeIndex",null):jt==="always"&&et.set("activeIndex",0)})}}if(!ln&&!Ds)return;const lt=Et||xt?Os:qt.current,Qt=et.state.activeIndex;if(Qt==null){if(jt==="always"&<.length>0){et.set("activeIndex",0);return}Zo(void 0,-1,Qn);return}if(Qt>=lt.length){Zo(void 0,-1,Qn),et.set("activeIndex",null);return}const In=lt[Qt],gn=nt.current.value,Pn=gn!==mA&&ca(In,gn,et.state.isItemEqualToValue);(nt.current.index!==Qt||!Pn)&&Zo(In,Qt,Qn)},[Ys,jt,Zo,xt,Et,Os,Ds,ln,et,Nn]),Me(()=>{if(y==="none"){ae(String(Nn)!=="");return}ae(Dn?Array.isArray(wt)&&wt.length>0:wt!=null)},[ae,y,Nn,wt,Dn]),x.useEffect(()=>{Et&&jt&&Os.length===0&&$r({activeIndex:null})},[Et,jt,Os.length,$r]);function ib(){ln&&mr!==""&&mr!==String(Fr)&&!Hl&&Ne(!0)}function WS(){ln&&be!==null&&ob(!1)}let cd=!1;function Gp(){!cd&&Nn!==Pa&&(cd=!0,qr(Pa,qe(Qn)))}function JS(){y!=="none"&&(M($n),U($E(wt,Z.initialValue,F)),he.change(wt),en&&!cs&&!lr&&Gp())}function gr(){en&&!cs&&!lr&&!Ce&&Gp()}function Kp(){y==="none"&&(M($n),U(Nn!==Z.initialValue),he.change(Nn))}ia(mr,ib),ia(ln,WS),ia(wt,JS),ia(Pa,gr),ia(Se,gr),ia(Nn,Kp);const Sc=$T({open:Ds?!0:ln,onOpenChange:It,elements:{reference:lr?bc:vc,floating:xc}}),ud=P?"grid":"listbox",La=ln||Ds,Yp=La?"true":"false",Nc=x.useMemo(()=>{const Oe=vc?.tagName==="INPUT",Be=vc==null||Oe,lt=Be||La,Qt=Be?{autoComplete:"off",spellCheck:"false",autoCorrect:"off",autoCapitalize:"none"}:{};return lt&&(Qt.role="combobox",Qt["aria-expanded"]=Yp,Qt["aria-haspopup"]=ud,Qt["aria-controls"]=La?ab?.id:void 0,Qt["aria-autocomplete"]=N?"none":G),{reference:Qt,floating:{role:"presentation"}}},[vc,La,Yp,ud,ab?.id,G,N]),Fl=hh(Sc,{enabled:!Fn&&I,event:"mousedown-only",toggle:!1,touchOpenDelay:lr?0:100,reason:S4}),dd=pf(Sc,{enabled:!Fn&&!Ds,outsidePressEvent:{mouse:"sloppy",touch:"intentional"},bubbles:Ds?!0:void 0,outsidePress(Oe){const Be=fs(Oe);return!Ze(bc,Be)&&!Ze(Vn.current,Be)&&!Ze($t.current,Be)&&!Ze(yc,Be)}}),$l=XT(Sc,{enabled:!Fn,id:ke,listRef:Ct,activeIndex:Ys,selectedIndex:Xo,virtual:!0,loopFocus:H,allowEscape:H&&!jt,focusItemOnOpen:Ce||y==="none"&&!jt?!1:"auto",focusItemOnHover:z,resetOnPointerLeave:!_,orientation:P?"horizontal":void 0,rtl:te==="rtl",disabledIndices:Ir,grid:P?$F:void 0,onNavigate(Oe,Be){!Be&&!ln||$p==="ending"||$r(Be?{activeIndex:Oe,type:vs.current?I1:P1}:{activeIndex:Oe})}}),Xp=x.useMemo(()=>ta($l.reference,{onKeyDown(Oe){P&&et.state.activeIndex==null&&(Oe.key==="ArrowLeft"||Oe.key==="ArrowRight")&&Oe.preventBaseUIHandler()}},dd.reference,Fl.reference,Nc.reference),[$l.reference,dd.reference,Fl.reference,Nc.reference,P,et]),fd=x.useMemo(()=>ta(mf,dd.floating),[dd.floating]),cb=x.useMemo(()=>ta($l.floating,Nc.floating),[$l.floating,Nc.floating]),ub=x.useMemo(()=>{const Oe=$l.item;return Oe?{...Oe,onFocus:void 0}:mn},[$l.item]);et.useContextCallback("setOpen",It),et.useContextCallback("setInputValue",qr),et.useContextCallback("setSelectedValue",wc),et.useContextCallback("setIndices",$r),et.useContextCallback("handleSelection",od),et.useContextCallback("forceMount",Vl),et.useContextCallback("requestSubmit",id),et.useContextCallback("onOpenChangeComplete",a),BE(()=>{et.update({inline:X,popupProps:fd,listProps:cb,inputProps:Xp,triggerProps:qp,itemProps:ub})});const kc={id:ke,selectedValue:wt,open:ln,mounted:Qo,transitionStatus:$p,items:_e,inline:X,popupProps:fd,listProps:cb,inputProps:Xp,triggerProps:qp,itemProps:ub,openMethod:lb,selectionMode:y,name:$n,form:w,disabled:Fn,readOnly:N,required:k,grid:P,virtualized:$,openOnInputClick:I,itemToStringLabel:oe,modal:q,autoHighlight:jt,isItemEqualToValue:F,submitOnItemClick:re,hasInputValue:cs};Me(()=>{et.update({...kc,inputOwnsFormValue:y==="none"&&(X||!et.state.inputInsidePopup)})},[et,...Object.values(kc)]);const ma=sa(A,he.inputRef),Qp=x.useMemo(()=>({query:mr,hasItems:Et,filteredItems:Yo,flatFilteredValues:Os}),[mr,Et,Yo,Os]),Zp=x.useMemo(()=>Array.isArray(zl)?"":Ca(zl,L),[zl,L]),Wo=Dn&&Array.isArray(wt)&&wt.length>0,ll=Dn||y==="none"&&Bl?void 0:$n,db=x.useMemo(()=>!Dn||!Array.isArray(wt)||!$n?null:wt.map(Oe=>{const Be=Ca(Oe,L);return s.jsx("input",{type:"hidden",form:w,name:$n,value:Be,disabled:Fn},Be)}),[Dn,wt,w,$n,L,Fn]),Wp=s.jsxs(x.Fragment,{children:[e.children,s.jsx("input",{...he.getValidationProps(Fn,{onFocus(){if(lr){bc?.focus();return}(ot.current||bc)?.focus()},onChange(Oe){if(Oe.nativeEvent.defaultPrevented||Fn||N)return;const Be=Oe.currentTarget.value,lt=Be.toLowerCase(),Qt=qe(Qn,Oe.nativeEvent),In=()=>qt.current.findIndex(Pn=>Ca(Pn,L).toLowerCase()===lt||pe(Pn).toLowerCase()===lt);function gn(){if(Dn)return;if(y==="none"){qr(Be,Qt);return}let Pn=In();Pn===-1&&(Pn=qt.current.findIndex((Er,ha)=>{const Kr=Tt.current[ha];return Kr!=null&&Kr.toLowerCase()===lt}));const hs=Pn===-1?void 0:qt.current[Pn];hs!=null&&wc?.(hs,Qt)}en&&(Vl(),Se&&In()===-1&&et.set("forceMounted",!0)),queueMicrotask(gn)}}),id:ke&&ll==null?`${ke}-hidden-input`:void 0,form:w,name:ll,autoComplete:J,disabled:Fn,required:k&&!Wo,readOnly:N,value:Zp,ref:ma,style:ll?su:_i,tabIndex:-1,"aria-hidden":!0,suppressHydrationWarning:!0}),db]});return s.jsx(lA.Provider,{value:et,children:s.jsx(oA.Provider,{value:Sc,children:s.jsx(cA.Provider,{value:Et,children:s.jsx(iA.Provider,{value:Qp,children:s.jsx(uA.Provider,{value:Nn,children:Wp})})})})})}function JF(e){const{multiple:t=!1,defaultValue:a,value:o,onValueChange:c,autoComplete:d,...f}=e;return s.jsx(WF,{...f,selectionMode:t?"multiple":"single",selectedValue:o,defaultSelectedValue:a,onSelectedValueChange:c,formAutoComplete:d})}const e6="data-popup-side",t6="data-list-empty",hA={...Yv,...Ti,popupSide:e=>e?{[e6]:e}:null,listEmpty:e=>e?{[t6]:""}:null},n6=x.createContext(void 0);function s6(){return x.useContext(n6)}const gA=x.createContext(void 0);function ij(e){const t=x.useContext(gA);if(t===void 0&&!e)throw new Error(un(21));return t}const xA=x.forwardRef(function(t,a){const o=Ta(),{buttonRef:c,getButtonProps:d}=na({native:!1}),f=sa(a,c);function p(b){o.context.setOpen(!1,qe(dy,b.nativeEvent,b.currentTarget))}const h=d({onClick:p});return s.jsx("span",{ref:f,...h,"aria-label":"Dismiss",tabIndex:void 0,style:su})});function bA(e){const t=e.useState("mounted"),a=e.useState("popupSide"),o=e.useState("positionerElement");return t&&o?a:null}function qh(){return yf().filteredItems.length===0}function r6(e){return e==="rtl"?["ArrowRight","ArrowLeft"]:["ArrowLeft","ArrowRight"]}function a6(e,t){const a=e>=t-1?t-2:e;return a>=0?a:void 0}function vA(e,t,a){const o=e.context.listRef.current[t];o&&(e.context.selectionEventRef.current=a,o.click(),e.context.selectionEventRef.current=null)}const l6=x.forwardRef(function(t,a){const{render:o,className:c,disabled:d=!1,id:f,style:p,...h}=t,{state:b,disabled:y,setTouched:v,setFocused:j,validationMode:w,validation:S}=Na(),{labelId:N}=yl(),k=s6(),P=!!ij(!0),R=Ta(),C=dA(),E=gf(),D=R.useState("required"),I=R.useState("disabled"),O=R.useState("readOnly"),_=R.useState("name"),z=R.useState("form"),H=R.useState("selectionMode"),B=R.useState("autoHighlight"),L=R.useState("inputProps"),F=R.useState("triggerProps"),$=R.useState("open"),X=R.useState("mounted"),K=R.useState("selectedValue"),q=R.useState("id"),Q=R.useState("inline"),G=R.useState("modal"),J=!!B,Y=bA(R),re=y||I||d,M=qh(),U=P||Q,Z=!U||G,ae=ka(f??(U?void 0:q)),ce=P?n1:b,[W,ue]=x.useState(null),ve=x.useRef(!1),ee=x.useRef(null),he=x.useRef(!1),te=H==="none"&&!P,ke=Ie(me=>{const pe=P||R.state.inline;pe&&!R.state.hasInputValue&&R.context.setInputValue("",qe(Qn)),R.update({inputElement:me,inputInsidePopup:pe,inputOwnsFormValue:te})}),le=P?h:S.getValidationProps(re,h);function de(){R.context.setIndices({activeIndex:null,selectedIndex:null,type:R.context.keyboardActiveRef.current?I1:P1})}function Se(){R.context.keyboardActiveRef.current=!1}const Te={...ce,open:$,disabled:re,readOnly:O,popupSide:Y,listEmpty:M};function _e(me){if(!k)return;let pe;const{highlightedChipIndex:Ce}=k,Ne=k.chipsRef.current.length,[be,De]=r6(E);return Ce!==void 0?(me.key===be?(me.preventDefault(),Ce>0?pe=Ce-1:pe=void 0):me.key===De?(me.preventDefault(),Ce0&&(me.preventDefault(),pe=Ne>0?Ne-1:void 0),pe)}const Ae=yt("input",t,{state:Te,ref:[a,R.context.inputRef,ke],props:[L,F,{value:W??C,"aria-readonly":O||void 0,"aria-required":D||void 0,"aria-labelledby":N,disabled:re,readOnly:O,required:H==="none"?D:void 0,form:z,...te&&_&&{name:_},id:ae,onFocus(){if(j(!0),!Q||!he.current)return;he.current=!1;const me=ee.current;me==null||!Object.hasOwn(R.context.valuesRef.current,me)||R.context.setIndices({activeIndex:me})},onBlur(){v(!0),j(!1);const me=R.state.activeIndex;if(Q&&me!==null&&B!=="always"&&(ee.current=me,he.current=!0,R.context.setIndices({activeIndex:null})),w==="onBlur"){const pe=H==="none"?C:K;S.commit(pe)}},onCompositionStart(me){Ym||(ve.current=!0,ue(me.currentTarget.value))},onCompositionEnd(me){ve.current=!1;const pe=me.currentTarget.value;ue(null),R.context.setInputValue(pe,qe(lf,me.nativeEvent))},onChange(me){const pe=me.nativeEvent,Ce=pe.inputType,Ne=!Ce||Ce==="insertReplacementText",be=ve.current||!Ne;function De(Mt){O||re||!Mt||!be||(R.context.setOpen(!0,qe(lf,pe)),J||de())}if(ve.current){const Mt=me.currentTarget.value;ue(Mt),Mt===""&&!R.state.openOnInputClick&&!R.state.inputInsidePopup&&R.context.setOpen(!1,qe(Ya,pe));const ot=Mt.trim(),Kn=J&&ot!=="";De(ot),$&&R.state.activeIndex!==null&&!Kn&&de();return}const Ge=qe(lf,pe);if(R.context.setInputValue(me.currentTarget.value,Ge),Ge.isCanceled)return;const Ct=me.currentTarget.value==="",Tt=qe(Ya,pe);Ct&&!R.state.inputInsidePopup&&(H==="single"&&R.context.setSelectedValue(null,Tt),R.state.openOnInputClick||R.context.setOpen(!1,Tt)),De(me.currentTarget.value.trim()),$&&R.state.activeIndex!==null&&!J&&de()},onKeyDown(me){if(me.ctrlKey||me.shiftKey||me.altKey||me.metaKey)return;if(R.context.keyboardActiveRef.current=!0,re||O){O&&me.key==="Enter"&&$&&R.state.activeIndex!==null&&Cs(me);return}const pe=me.currentTarget,Ce=pe.scrollWidth-pe.clientWidth,Ne=E==="rtl";if(me.key==="Home"){Cs(me);const Ge=c1&&Ne?pe.value.length:0;pe.setSelectionRange(Ge,Ge),pe.scrollLeft=0;return}if(me.key==="End"){Cs(me);const Ge=c1&&Ne?0:pe.value.length;pe.setSelectionRange(Ge,Ge),pe.scrollLeft=Ne?-Ce:Ce;return}if(!X&&me.key==="Escape"){const Ge=H==="multiple"&&Array.isArray(K)?K.length===0:K===null,Ct=qe(nu,me.nativeEvent),Tt=H==="multiple"?[]:null;R.context.setInputValue("",Ct),R.context.setSelectedValue(Tt,Ct),!Ge&&!R.state.inline&&!Ct.isPropagationAllowed&&me.stopPropagation();return}if(k&&me.key==="Backspace"&&pe.value===""&&k.highlightedChipIndex===void 0&&Array.isArray(K)&&K.length>0){const Ge=k.chipsRef.current.length,Ct=Ge>0?Ge-1:K.length-1,Tt=K.filter((Mt,ot)=>ot!==Ct);de(),R.context.setSelectedValue(Tt,qe(Qn,me.nativeEvent));return}const be=k?.highlightedChipIndex!==void 0,De=_e(me);if(k?.setHighlightedChipIndex(De),De!==void 0?k?.chipsRef.current[De]?.focus():be&&R.context.inputRef.current?.focus(),me.which!==229&&me.key==="Enter"&&$){const Ge=R.state.activeIndex,Ct=me.nativeEvent;if(Ge===null){if(Q)return;R.context.setOpen(!1,qe(Qn,Ct));return}Cs(me),vA(R,Ge,Ct)}},onPointerMove:Se,onPointerDown:Se},le],stateAttributesMapping:hA}),oe=P?s.jsx(r1.Provider,{value:s1,children:Ae}):Ae;return s.jsxs(x.Fragment,{children:[$&&Z&&s.jsx(xA,{ref:R.context.startDismissRef}),oe]})}),o6=x.forwardRef(function(t,a){const{render:o,className:c,nativeButton:d=!0,disabled:f=!1,id:p,style:h,...b}=t,{state:y,disabled:v,setTouched:j,setFocused:w,validationMode:S,validation:N}=Na(),{labelId:k}=yl(),A=Ta(),P=A.useState("selectionMode"),R=A.useState("disabled"),C=A.useState("readOnly"),E=A.useState("required"),D=A.useState("positionerElement"),I=A.useState("listElement"),O=A.useState("popupId"),_=A.useState("triggerProps"),z=A.useState("inputInsidePopup"),H=A.useState("id"),B=A.useState("labelId"),L=A.useState("open"),F=A.useState("selectedValue"),$=A.useState("activeIndex"),X=A.useState("selectedIndex"),K=A.useState("hasSelectedValue"),q=$h(),Q=dA(),G=er(),J=v||R||f,Y=qh(),re=bA(A);io({id:z?p:void 0});const M=z?p??H:p,U=QE(k,B);let Z;L&&z?Z=O??fA(H):L&&(Z=I?.id);const ae=x.useRef("");function ce(le){ae.current=le.pointerType}const{reference:W}=QT(q,{enabled:!L&&!C&&!R&&P==="single",listRef:A.context.labelsRef,activeIndex:$,selectedIndex:X,onMatch(le){const de=A.context.valuesRef.current[le];de!==void 0&&A.context.setSelectedValue(de,qe(Qn))}}),{reference:ue}=hh(q,{enabled:!R,event:"mousedown"}),{buttonRef:ve,getButtonProps:ee}=na({native:d,disabled:J}),he={...y,readOnly:C,open:L,disabled:J,popupSide:re,listEmpty:Y,placeholder:P==="none"?!1:!K},te=Ie(le=>{A.set("triggerElement",le)});return yt("button",t,{ref:[a,ve,te],state:he,props:[_,ue,W,{id:M,tabIndex:z?0:-1,role:z?"combobox":void 0,"aria-expanded":L,"aria-haspopup":z?"dialog":"listbox","aria-controls":Z,"aria-required":z&&E||void 0,"aria-readonly":z&&C||void 0,"aria-labelledby":U,onPointerDown:ce,onPointerEnter:ce,onFocus(){w(!0),!J&&G.start(0,A.context.forceMount)},onBlur(le){if(!Ze(D,le.relatedTarget)&&(j(!0),w(!1),S==="onBlur")){const de=P==="none"?Q:F;N.commit(de)}},onMouseDown(le){if(J||(z||q.set("domReferenceElement",le.currentTarget),A.context.forceMount(),ae.current!=="touch"&&(A.context.inputRef.current?.focus(),z||le.preventDefault()),L))return;const de=Gt(le.currentTarget);function Se(Te){const _e=A.state.triggerElement;if(!_e)return;const Ae=fs(Te),oe=A.state.positionerElement,me=A.state.listElement;Ze(_e,Ae)||Ze(oe,Ae)||Ze(me,Ae)||ZE(Te,_e)||A.context.setOpen(!1,qe(_1,Te))}z&&de.addEventListener("mouseup",Se,{once:!0})},onKeyDown(le){(le.key==="ArrowDown"||le.key==="ArrowUp")&&(Cs(le),A.context.setOpen(!0,qe(fy,le.nativeEvent)),A.context.inputRef.current?.focus())}},N.getValidationProps(J,b),ee],stateAttributesMapping:hA})}),i6=x.createContext(null);function c6(){return x.useContext(i6)}function u6(e){const{children:t}=e,{filteredItems:a}=yf(),o=c6(),c=o?o.items:a;return s.jsx(x.Fragment,{children:c.map(t)})}const d6=x.forwardRef(function(t,a){var o;const{render:c,className:d,style:f,children:p,...h}=t,b=Ta(),y=$h(),v=!!ij(!0),{filteredItems:j,hasItems:w}=yf(),S=b.useState("selectionMode"),N=b.useState("grid"),k=b.useState("readOnly"),A=b.useState("listProps"),P=b.useState("virtualized"),R=b.useState("forceMounted"),C=S==="multiple",E=j.length===0,D=Ie(L=>{b.set("positionerElement",L)}),I=Ie(L=>{b.set("listElement",L)}),O=x.useMemo(()=>typeof p=="function"?o||(o=s.jsx(u6,{children:p})):p,[p]),_={empty:E},z=y.useState("floatingId"),H=yt("div",t,{state:_,ref:[a,I,v?null:D],props:[A,{children:O,tabIndex:-1,id:z,role:N?"grid":"listbox","aria-multiselectable":C?"true":void 0,"aria-readonly":!N&&k?!0:void 0,onKeyDown(L){if(!(b.state.disabled||b.state.readOnly)&&L.key==="Enter"){const F=b.state.activeIndex;if(F==null)return;Cs(L),vA(b,F,L.nativeEvent)}},onKeyDownCapture(){b.context.keyboardActiveRef.current=!0},onPointerMoveCapture(){b.context.keyboardActiveRef.current=!1}},h]});if(P)return H;const B=w&&!R?void 0:b.context.labelsRef;return s.jsx(nj,{elementsRef:b.context.listRef,labelsRef:B,children:H})}),f6="⁠",p6=200;function m6(e){const t=e.ownerDocument.createTreeWalker(e,NodeFilter.SHOW_TEXT);let a=null;for(;t.nextNode();){const o=t.currentNode;o.nodeValue!==""&&(a=o)}return a}function h6(){const e=er(),t=x.useRef(null);return x.useEffect(()=>{if(tf)return;const a=t.current;if(a==null)return;const o=m6(a);if(o==null)return;const c=o.data,d=`${c}${f6}`;return o.nodeValue=d,e.start(p6,()=>{o.nodeValue===d&&(o.nodeValue=c)}),()=>{e.clear(),o.nodeValue===d&&(o.nodeValue=c)}},[t,e]),t}const yA=x.createContext(void 0);function g6(){const e=x.useContext(yA);if(e===void 0)throw new Error(un(20));return e}const x6=x.forwardRef(function(t,a){const{keepMounted:o=!1,...c}=t,d=Ta(),f=d.useState("mounted"),p=d.useState("forceMounted");return f||o||p?s.jsx(yA.Provider,{value:o,children:s.jsx(ph,{ref:a,...c})}):null}),b6=x.forwardRef(function(t,a){const{render:o,className:c,anchor:d,positionMethod:f,side:p,align:h,sideOffset:b,alignOffset:y,collisionBoundary:v="clipping-ancestors",collisionPadding:j,arrowPadding:w,sticky:S,disableAnchorTracking:N=!1,collisionAvoidance:k=rT,style:A,...P}=t,R=Ta(),C=$h(),E=g6(),D=R.useState("modal"),I=R.useState("open"),O=R.useState("mounted"),_=R.useState("openMethod"),z=R.useState("positionerElement"),H=R.useState("triggerElement"),B=R.useState("inputElement"),L=R.useState("inputGroupElement"),F=R.useState("inputInsidePopup"),$=R.useState("transitionStatus"),X=qh(),q=Th({anchor:d??(F?H:L??B),floatingRootContext:C,positionMethod:f,mounted:O,side:p,sideOffset:b,align:h,alignOffset:y,arrowPadding:w,collisionBoundary:v,collisionPadding:j,sticky:S,disableAnchorTracking:N,keepMounted:E,collisionAvoidance:k,lazyFlip:!0});aj(I&&D,_==="touch",z,H);const Q={open:I,side:q.side,align:q.align,anchorHidden:q.anchorHidden,empty:X};Me(()=>{R.set("popupSide",q.side)},[R,q.side]);const G=Ie(Y=>{R.set("positionerElement",Y)}),J=Eh(t,Q,{styles:q.positionerStyles,transitionStatus:$,props:P,refs:[a,G],hidden:!O,inert:!I});return s.jsxs(gA.Provider,{value:q,children:[O&&D&&s.jsx(Mh,{inert:Ah(!I),cutout:L??B??H}),J]})}),v6={...Zc,...co},y6=x.forwardRef(function(t,a){const{render:o,className:c,style:d,initialFocus:f,finalFocus:p,...h}=t,b=Ta(),y=ij(),v=$h(),j=b.useState("mounted"),w=b.useState("open"),S=b.useState("openMethod"),N=b.useState("popupProps"),k=b.useState("transitionStatus"),A=b.useState("inputInsidePopup"),P=b.useState("inputElement"),R=b.useState("modal"),C=b.useState("id"),E=qh(),D=h.id??(A?fA(C):void 0);Me(()=>(b.set("popupId",b.context.popupRef.current?.id||D),()=>{b.set("popupId",void 0)}),[b,D]),oa({open:w,ref:b.context.popupRef,onComplete(){w&&b.context.onOpenChangeComplete(!0)}});const I={open:w,side:y.side,align:y.align,anchorHidden:y.anchorHidden,transitionStatus:k,empty:E},O=yt("div",t,{state:I,ref:[a,b.context.popupRef],props:[N,{id:D,role:A?"dialog":"presentation",onFocus(L){const F=fs(L.nativeEvent);S!=="touch"&&(Ze(b.state.listElement,F)||F===L.currentTarget)&&b.context.inputRef.current?.focus()}},xf(k),h],stateAttributesMapping:v6}),z=f===void 0?A?L=>L==="touch"?b.context.popupRef.current:P:!1:f;let H;p!=null?H=p:H=A?void 0:!1;const B=!A||R;return s.jsx(mh,{context:v,disabled:!j,modal:B,openInteractionType:S,initialFocus:z,returnFocus:H,getInsideElements:()=>[b.context.startDismissRef.current,b.context.endDismissRef.current],children:s.jsxs(x.Fragment,{children:[O,B&&s.jsx(xA,{ref:b.context.endDismissRef})]})})}),j6=x.forwardRef(function(t,a){const{render:o,className:c,style:d,...f}=t;return yt("span",t,{ref:a,props:[{"aria-hidden":!0,children:"▼"},f]})}),jA=x.createContext(void 0);function wA(){const e=x.useContext(jA);if(!e)throw new Error(un(19));return e}const w6=x.createContext(!1);function S6(){return x.useContext(w6)}function SA(e){const{componentProps:t,forwardedRef:a,virtualized:o,indexFromFilter:c}=e,{render:d,className:f,style:p,value:h=null,index:b,disabled:y=!1,nativeButton:v=!1,...j}=t,w=x.useRef(null),S=tj({guess:!0,index:b,textRef:w}),N=Ta(),k=S6(),A=qF(),P=N.useState("selectionMode"),R=N.useState("disabled"),C=N.useState("readOnly"),E=N.useState("isItemEqualToValue"),D=R||y,I=P!=="none",O=b??c??S.index,_=O!==-1,z=N.useState("id"),H=N.useState("isActive",O),B=N.useState("isSelected",h),L=N.useState("itemProps"),F=x.useRef(null),$=z!=null&&_?`${z}-${O}`:void 0,X=B&&I;Me(()=>{if(!(_&&(o||b!=null)))return;const U=N.context.listRef.current;return U[O]=F.current,()=>{delete U[O]}},[_,o,O,b,N]),Me(()=>{if(!_||A)return;const M=N.context.valuesRef.current;return M[O]=h,()=>{delete M[O]}},[_,A,O,h,N]),Me(()=>{if(!_||A)return;const M=N.state.selectedValue;let U=N.state.selectedIndex;N.state.selectionMode==="multiple"&&Array.isArray(M)?U=qE(O,h,N.context.valuesRef.current,M,E,U):ca(h,M,E)&&(U=O),N.set("selectedIndex",U)},[_,A,N,O,h,E]);const{getButtonProps:K,buttonRef:q}=na({disabled:D,focusableWhenDisabled:!0,native:v,composite:!0}),Q={disabled:D,selected:X,highlighted:H};function G(M){function U(){N.context.handleSelection(M,h)}N.state.submitOnItemClick?(Ts.flushSync(U),N.context.requestSubmit()):U()}const J={id:$,role:k?"gridcell":"option","aria-selected":I?X:void 0,tabIndex:void 0,onPointerDownCapture(M){M.isPrimary&&(N.context.pointerDownItemRef.current=M.currentTarget),M.preventDefault()},onMouseDown(M){M.preventDefault()},onClick(M){D||C||G(M.nativeEvent)},onMouseUp(M){const U=N.context.pointerDownItemRef.current===M.currentTarget;N.context.pointerDownItemRef.current=null,!(D||C||M.button!==0||U||!H)&&G(M.nativeEvent)}},Y=yt("div",t,{ref:[q,a,S.ref,F],state:Q,props:[L,J,j,K]}),re=x.useMemo(()=>({selected:X,textRef:w}),[X,w]);return s.jsx(jA.Provider,{value:re,children:Y})}function N6(e){const{componentProps:t,forwardedRef:a}=e,c=Ta().useState("isItemEqualToValue"),{flatFilteredValues:d}=yf(),f=t.value??null,p=cu(d,f,c);return s.jsx(SA,{componentProps:t,forwardedRef:a,virtualized:!0,indexFromFilter:p})}const k6=x.memo(x.forwardRef(function(t,a){const c=Ta().useState("virtualized");return c&&t.index==null?s.jsx(N6,{componentProps:t,forwardedRef:a}):s.jsx(SA,{componentProps:t,forwardedRef:a,virtualized:c,indexFromFilter:void 0})})),C6=x.forwardRef(function(t,a){const{selected:o}=wA();return t.keepMounted||o?s.jsx(T6,{...t,ref:a}):null}),T6=x.memo(x.forwardRef((e,t)=>{const{render:a,className:o,style:c,keepMounted:d,...f}=e,{selected:p}=wA(),h=x.useRef(null),{transitionStatus:b,setMounted:y}=Oi(p),j=yt("span",e,{ref:[t,h],state:{selected:p,transitionStatus:b},props:[{"aria-hidden":!0,children:"✔️"},f],stateAttributesMapping:co});return oa({batch:!0,enabled:!p,open:p,ref:h,onComplete(){p||y(!1)}}),j})),E6=x.forwardRef(function(t,a){const{render:o,className:c,style:d,children:f,...p}=t,{filteredItems:h}=yf(),b=Ta(),y=h6(),v=h.length===0?f:null;return yt("div",t,{ref:[a,b.context.emptyRef,y],props:[{children:v,role:"status","aria-live":"polite","aria-atomic":!0},p]})}),A6="、";function Gh({value:e,onValueChange:t,options:a,placeholder:o="",emptyHint:c=bo("没有匹配的选项"),searchPlaceholder:d=bo("搜索…"),disabled:f=!1,className:p,id:h,title:b,...y}){const v=x.useMemo(()=>a.filter(w=>e.includes(w.value)),[a,e]),j=v.length?v.map(w=>w.label).join(A6):o;return s.jsxs(JF,{items:a,multiple:!0,value:v,onValueChange:w=>t(w.map(S=>S.value)),isItemEqualToValue:(w,S)=>w?.value===S?.value,disabled:f,children:[s.jsxs(o6,{"data-slot":"multi-select-trigger",id:h,title:b,"aria-label":y["aria-label"],className:Ue("inline-flex h-[30px] min-w-0 cursor-pointer items-center justify-between gap-1.5 rounded-md border border-control-border bg-control px-2.5","text-[12.5px] font-normal text-foreground","transition-colors duration-150 ease-out","hover:border-control-border-hover hover:bg-control-hover","focus-visible:border-primary focus-visible:shadow-focus focus-visible:outline-none","data-disabled:cursor-not-allowed data-disabled:bg-control data-disabled:text-muted-foreground data-disabled:opacity-60",p),children:[s.jsx("span",{"data-slot":"multi-select-value",className:"min-w-0 flex-1 truncate text-left data-[placeholder]:text-muted-foreground","data-placeholder":v.length?void 0:"",title:j,children:j}),s.jsx(j6,{className:"flex flex-none text-muted-foreground transition-transform duration-[180ms] data-popup-open:rotate-180",children:s.jsx("svg",{viewBox:"0 0 10 6",className:"size-2.5","aria-hidden":"true",children:s.jsx("path",{d:"M1 1l4 4 4-4",stroke:"currentColor",strokeWidth:"1.4",fill:"none",strokeLinecap:"round",strokeLinejoin:"round"})})})]}),s.jsx(x6,{children:s.jsx(b6,{sideOffset:4,className:"z-[35]",children:s.jsxs(y6,{"data-slot":"multi-select-content",className:Ue("flex max-h-[280px] w-[max(var(--anchor-width),220px)] flex-col overflow-hidden","rounded-lg border border-border-strong bg-raised shadow-3 outline-none","data-open:animate-in data-open:fade-in-0","data-closed:animate-out data-closed:fade-out-0"),children:[s.jsxs("div",{className:"flex flex-none items-center gap-1.5 border-b border-hairline px-2.5 py-1.5",children:[s.jsxs("svg",{viewBox:"0 0 12 12",className:"size-3 flex-none text-muted-foreground","aria-hidden":"true",children:[s.jsx("circle",{cx:"5",cy:"5",r:"3.4",stroke:"currentColor",strokeWidth:"1.3",fill:"none"}),s.jsx("path",{d:"M7.6 7.6 10.5 10.5",stroke:"currentColor",strokeWidth:"1.3",strokeLinecap:"round"})]}),s.jsx(l6,{placeholder:d,className:"min-w-0 flex-1 border-0 bg-transparent p-0 text-[12.5px] text-foreground outline-none placeholder:text-muted-foreground"})]}),s.jsx(E6,{children:s.jsx("div",{className:"px-2.5 py-3 text-[11.5px] leading-[1.5] text-muted-foreground",children:c})}),s.jsx(d6,{className:"min-h-0 flex-1 overflow-y-auto overscroll-contain p-[5px]",children:w=>s.jsxs(k6,{value:w,className:Ue("grid cursor-pointer grid-cols-[14px_minmax(0,1fr)] items-center gap-2 rounded-sm px-[9px] py-[5px]","text-[12.5px] leading-[1.4] text-foreground select-none outline-none","transition-colors duration-[120ms] ease-out","data-[highlighted]:bg-nav-hover","data-[selected]:bg-primary-soft data-[selected]:font-semibold data-[selected]:text-primary-fg","data-[highlighted]:data-[selected]:bg-primary-tint","data-disabled:pointer-events-none data-disabled:text-muted-foreground"),children:[s.jsx(C6,{className:"col-start-1 flex text-primary",children:s.jsx("svg",{viewBox:"0 0 12 12",className:"size-3","aria-hidden":"true",children:s.jsx("path",{d:"M2.5 6.2 4.8 8.5 9.5 3.8",stroke:"currentColor",strokeWidth:"1.8",fill:"none",strokeLinecap:"round",strokeLinejoin:"round"})})}),s.jsx("span",{className:"col-start-2 min-w-0 truncate",children:w.label})]},w.value)})]})})})]})}const NA=x.createContext(void 0);function jf(e){const t=x.useContext(NA);if(t===void 0&&!e)throw new Error(un(47));return t}const R6={...Hy,disabled:e=>e.disabled,instantType:e=>e.instantType,openMethod:e=>e.openMethod,openChangeReason:e=>e.openChangeReason,modal:e=>e.modal,focusManagerModal:e=>e.focusManagerModal,stickIfOpen:e=>e.stickIfOpen,titleElementId:e=>e.titleElementId,descriptionElementId:e=>e.descriptionElementId,openOnHover:e=>e.openOnHover,closeDelay:e=>e.closeDelay,adaptiveOrigin:e=>e.adaptiveOrigin};class O6 extends ou{constructor(a,o,c){const d=new vh;super(M6(a,d,o,c),D6(d),R6);cn(this,"setOpen",(a,o)=>{const c=o.reason===qs,d=o.reason===go&&o.event.detail===0,f=!a&&(o.reason===nu||o.reason==null),p=zT(o),h=this.select("activeTriggerId");if(!a&&o.reason===dy&&o.trigger==null&&h!=null&&(o.trigger=this.context.triggerElements.getById(h)??this.select("activeTriggerElement")??void 0),this.context.onOpenChange?.(a,o),o.isCanceled)return;this.state.floatingRootContext.dispatchOpenChange(a,o);const b=()=>{const v=Iy(this.state,a,o.trigger,p());v.openChangeReason=o.reason,this.update(v)};c?(this.set("stickIfOpen",!0),this.context.stickIfOpenTimeout.start(hH,()=>{this.set("stickIfOpen",!1)}),Ts.flushSync(b)):b();let y;d?y="click":f?y="dismiss":o.reason===xo&&(y="focus"),this.set("instantType",y)})}}function M6(e,t,a,o=!1){const c={...zy(t,a,o),disabled:!1,modal:!1,focusManagerModal:!1,instantType:void 0,openMethod:null,openChangeReason:null,titleElementId:void 0,descriptionElementId:void 0,stickIfOpen:!0,openOnHover:!1,closeDelay:0,adaptiveOrigin:void 0,...e};return c.open&&e?.mounted===void 0&&(c.mounted=!0),c}function D6(e){return{popupRef:x.createRef(),onOpenChange:void 0,onOpenChangeComplete:void 0,triggerFocusTargetRef:x.createRef(),beforeContentFocusGuardRef:x.createRef(),stickIfOpenTimeout:new la,triggerElements:e}}const kA=uh(function({props:t}){const{children:a,open:o,defaultOpen:c=!1,onOpenChange:d,onOpenChangeComplete:f,modal:p=!1,handle:h,triggerId:b,defaultTriggerId:y=null}=t,v=P6(h,{modal:p,open:c,openProp:o,activeTriggerId:y,triggerIdProp:b});v.useControlledProp("openProp",o),v.useControlledProp("triggerIdProp",b);const j=v.useState("open"),w=v.useState("mounted"),S=v.useState("payload");v.useContextCallback("onOpenChange",d),v.useContextCallback("onOpenChangeComplete",f),HT(v,j),Py(v);const{forceUnmount:N}=_y(j,v,()=>{v.update({stickIfOpen:!0,openChangeReason:null})});v.useSyncedValues({modal:p}),x.useEffect(()=>{j||v.context.stickIfOpenTimeout.clear()},[v,j]),x.useImperativeHandle(t.actionsRef,()=>({unmount:N,close:()=>v.setOpen(!1,qe(py))}),[N,v]);const k=j||w;return s.jsxs(NA.Provider,{value:v,children:[h&&s.jsx(Dy,{handle:h,store:v}),k&&s.jsx(_6,{store:v,modal:p}),typeof a=="function"?a({payload:S}):a]})});function I6(e){return jf(!0)?s.jsx(kA,{props:e}):s.jsx(wH,{children:s.jsx(kA,{props:e})})}function P6(e,t){const a=My((o,c)=>new O6(t,o,c));return x.useEffect(()=>a.context.stickIfOpenTimeout.disposeEffect(),[a]),a}function _6({store:e,modal:t}){const a=e.useState("floatingRootContext"),o=pf(a,{outsidePressEvent:{mouse:t==="trap-focus"?"sloppy":"intentional",touch:"sloppy"}}),c=o.reference,d=o.floating;return Ly(e,{activeTriggerProps:c,inactiveTriggerProps:c,popupProps:d}),null}const L6=300;function z6(e,t){const a=x.useRef(null);function o(d){Ts.flushSync(()=>{e.setOpen(!1,qe(xo,d.nativeEvent,d.currentTarget))}),g4(a.current)?.focus()}function c(d){const f=e.select("positionerElement");if(f&&Jc(d,f))e.context.beforeContentFocusGuardRef.current?.focus();else{Ts.flushSync(()=>{e.setOpen(!1,qe(xo,d.nativeEvent,d.currentTarget))});let p=h4(e.context.triggerFocusTargetRef.current||t.current);for(;p!==null&&Ze(f,p);){const h=p;if(p=iy(p),p===h)break}p?.focus()}}return{preFocusGuardRef:a,handlePreFocusGuardFocus:o,handleFocusTargetFocus:c}}const B6=G1(function(t,a){const{render:o,className:c,style:d,disabled:f=!1,nativeButton:p=!0,handle:h,payload:b,openOnHover:y=!1,delay:v=L6,closeDelay:j=0,id:w,...S}=t,N=jf(!0),A=FT(h)??N;if(!A)throw new Error(un(74));const P=ka(w),R=A.useState("isTriggerActive",P),C=A.useState("floatingRootContext"),E=A.useState("isOpenedByTrigger",P),D=A.useState("triggerPopupId",P),I=x.useRef(null),{registerTrigger:O,isMountedByThisTrigger:_}=BT(P,I,A,{payload:b,disabled:f,openOnHover:y,closeDelay:j}),z=A.useState("openChangeReason"),H=A.useState("stickIfOpen"),B=A.useState("openMethod"),L=A.useState("focusManagerModal"),F=KT(C,{enabled:!f&&y&&(B!=="touch"||z!==go),mouseOnly:!0,move:!1,handleClose:WT(),restMs:v,delay:{close:j},triggerElementRef:I,isActiveTrigger:R,isClosing:()=>A.select("transitionStatus")==="ending"}),$=hh(C,{stickIfOpen:H}),X=TE(()=>A.select("open"),ae=>{A.set("openMethod",ae)}),K=A.useState("triggerProps",_),{getButtonProps:q,buttonRef:Q}=na({disabled:f,native:p}),G={open(ae){return ae&&z===go?Yv.open(ae):Kv.open(ae)}},{preFocusGuardRef:J,handlePreFocusGuardFocus:Y,handleFocusTargetFocus:re}=z6(A,I),U=yt("button",t,{state:{disabled:f,open:E},ref:[Q,a,O,I],props:[$.reference,F,K,X,{[sT]:"",id:P,"aria-haspopup":"dialog","aria-expanded":E,"aria-controls":D},S,q],stateAttributesMapping:G}),Z=s.jsx(x.Fragment,{children:U},P);return _&&!L?s.jsxs(x.Fragment,{children:[s.jsx(ru,{ref:J,onFocus:Y}),Z,s.jsx(ru,{ref:A.context.triggerFocusTargetRef,onFocus:re})]}):Z}),CA=x.createContext(void 0);function H6(){const e=x.useContext(CA);if(e===void 0)throw new Error(un(45));return e}const U6=x.forwardRef(function(t,a){const{keepMounted:o=!1,...c}=t;return jf().useState("mounted")||o?s.jsx(CA.Provider,{value:o,children:s.jsx(ph,{ref:a,...c})}):null}),TA=x.createContext(void 0);function V6(){const e=x.useContext(TA);if(!e)throw new Error(un(46));return e}const F6=x.forwardRef(function(t,a){const{render:o,className:c,style:d,anchor:f,positionMethod:p,side:h,align:b,sideOffset:y,alignOffset:v,collisionBoundary:j="clipping-ancestors",collisionPadding:w,arrowPadding:S,sticky:N,disableAnchorTracking:k=!1,collisionAvoidance:A=aT,...P}=t,R=jf(),C=H6(),E=yH(),D=R.useState("floatingRootContext"),I=R.useState("mounted"),O=R.useState("open"),_=R.useState("openChangeReason"),z=R.useState("activeTriggerElement"),H=R.useState("modal"),B=R.useState("openMethod"),L=R.useState("positionerElement"),F=R.useState("instantType"),$=R.useState("transitionStatus"),X=R.useState("adaptiveOrigin"),K=x.useRef(null),q=D1(L),Q=Th({anchor:f,floatingRootContext:D,positionMethod:p,mounted:I,side:h,sideOffset:y,align:b,alignOffset:v,arrowPadding:S,collisionBoundary:j,collisionPadding:w,sticky:N,disableAnchorTracking:k,keepMounted:C,nodeId:E,collisionAvoidance:A,adaptiveOrigin:X}),G=D.useState("domReferenceElement");Me(()=>{const U=G,Z=K.current;if(U&&(K.current=U),Z&&U&&U!==Z){R.set("instantType",void 0);const ae=new AbortController;return q(()=>{R.set("instantType","trigger-change")},ae.signal),()=>{ae.abort()}}},[G,q,R]);const J=H===!0&&_!==qs;aj(O&&J,B==="touch",L,z);const Y=R.useStateSetter("positionerElement"),re={open:O,side:Q.side,align:Q.align,anchorHidden:Q.anchorHidden,instant:F},M=Eh(t,re,{styles:Q.positionerStyles,transitionStatus:$,props:P,refs:[a,Y],hidden:!I,inert:!O});return s.jsxs(TA.Provider,{value:Q,children:[I&&J&&s.jsx(Mh,{inert:Ah(!O),cutout:z}),s.jsx(jH,{id:E,children:M})]})}),$6=x.createContext(void 0);function q6(){const[e,t]=x.useState(0),a=Ie(()=>(t(c=>c+1),()=>{t(c=>Math.max(0,c-1))}));return{context:x.useMemo(()=>({register:a}),[a]),hasClosePart:e>0}}const G6=x.forwardRef(function(t,a){const{render:o,className:c,style:d,initialFocus:f,finalFocus:p,...h}=t,b=jf(),y=V6(),v=eA()!=null,{context:j,hasClosePart:w}=q6(),S=b.useState("open"),N=b.useState("openMethod"),k=b.useState("instantType"),A=b.useState("transitionStatus"),P=b.useState("popupProps"),R=b.useState("titleElementId"),C=b.useState("descriptionElementId"),E=b.useState("modal"),D=b.useState("mounted"),I=b.useState("openChangeReason"),O=b.useState("activeTriggerElement"),_=b.useState("floatingRootContext"),z=_.useState("floatingId"),H=b.useState("disabled"),B=b.useState("openOnHover"),L=b.useState("closeDelay");oa({open:S,ref:b.context.popupRef,onComplete(){S&&b.context.onOpenChangeComplete?.(!0)}}),GT(_,{enabled:B&&!H,closeDelay:L});const F=f===void 0?_T(b.context.popupRef):f,$=E!==!1&&w;b.useSyncedValue("focusManagerModal",$);const X=b.useStateSetter("popupElement"),K={open:S,side:y.side,align:y.align,instant:k,transitionStatus:A},q=yt("div",t,{state:K,ref:[a,b.context.popupRef,X],props:[P,{id:z,role:"dialog",...mf,"aria-labelledby":R,"aria-describedby":C,onKeyDown(Q){v&&Rh.has(Q.key)&&Q.stopPropagation()}},xf(A),h],stateAttributesMapping:Xv});return s.jsx(mh,{context:_,openInteractionType:N,modal:$,disabled:!D||I===qs,initialFocus:F,returnFocus:p,restoreFocus:"popup",previousFocusableElement:pn(O)?O:void 0,nextFocusableElement:b.context.triggerFocusTargetRef,beforeContentFocusGuardRef:b.context.beforeContentFocusGuardRef,children:s.jsx($6.Provider,{value:j,children:q})})});function wf(e){return s.jsx(I6,{"data-slot":"popover",...e})}function Kh(e){return s.jsx(B6,{"data-slot":"popover-trigger",...e})}function Sf({className:e,sideOffset:t=6,align:a="start",side:o,alignOffset:c,collisionPadding:d=8,anchor:f,children:p,...h}){return s.jsx(U6,{children:s.jsx(F6,{className:"z-[35] outline-none",sideOffset:t,align:a,side:o,alignOffset:c,collisionPadding:d,anchor:f,children:s.jsx(G6,{"data-slot":"popover-content",className:Ue("flex max-h-[min(70vh,560px)] flex-col overflow-hidden rounded-lg border border-border-strong bg-raised text-foreground shadow-3","outline-none","data-open:animate-in data-open:fade-in-0 data-open:slide-in-from-top-1","data-closed:animate-out data-closed:fade-out-0",e),...h,children:p})})})}function K6({page:e,pageCount:t,onPageChange:a,disabled:o=!1,className:c,infoFirst:d=!1}){const f=Math.max(1,Number(t)||1),p=Math.min(Math.max(1,Number(e)||1),f),h=s.jsx("span",{"data-slot":"pager-info",className:"text-[11.5px] text-subtle [font-variant-numeric:tabular-nums]",children:bo("第 {current} / {total} 页",{current:p,total:f})});return s.jsxs("div",{"data-slot":"pager",className:Ue("flex items-center gap-2",c),children:[d&&h,s.jsx(ne,{type:"button",variant:"outline",size:"sm",disabled:o||p<=1,onClick:()=>a(p-1),children:bo("上一页")}),s.jsx(ne,{type:"button",variant:"outline",size:"sm",disabled:o||p>=f,onClick:()=>a(p+1),children:bo("下一页")}),!d&&h]})}const Y6=Xc(["relative flex w-full items-center gap-2.5 text-left","border border-transparent rounded-sm","shadow-none","transition-[background-color,border-color,color] duration-150 ease-out","outline-none focus-visible:shadow-focus","disabled:pointer-events-none disabled:opacity-45","[&_svg]:pointer-events-none [&_svg]:shrink-0"],{variants:{variant:{default:["cursor-pointer px-2.5 py-2","text-[12.5px] font-medium text-sidebar-fg","hover:bg-nav-hover hover:text-foreground","data-[active]:border-nav-selected-border data-[active]:bg-nav-selected-bg data-[active]:font-semibold data-[active]:text-nav-selected-fg"],add:["cursor-pointer justify-center px-2 py-1.5","border-dashed border-border-strong bg-transparent","text-[11.5px] text-muted-foreground","hover:border-primary hover:text-primary-fg"]}},defaultVariants:{variant:"default"}});function uu({className:e,variant:t,active:a=!1,count:o,icon:c,trailing:d,children:f,...p}){return s.jsxs("button",{type:"button","data-slot":"nav-item","data-active":a?"":void 0,"aria-current":a?"true":void 0,className:Ue(Y6({variant:t}),e),...p,children:[c,s.jsx("span",{"data-slot":"nav-item-label",className:"min-w-0 flex-1 truncate",children:f}),d,o!=null&&s.jsx("span",{"data-slot":"nav-item-count",className:Ue("flex-none text-[10.5px] text-sidebar-muted [font-variant-numeric:tabular-nums]",a&&"text-inherit"),children:o})]})}function r(e,t){const a=typeof window<"u"?window.wbI18n:void 0;return a&&typeof a.t=="function"?a.t(e,t):e}function Yh(e){if(e.source==="subscription"&&(e.port===null||e.port===void 0))return r("未分配端口");const t=e.resolved;return t?.protocol&&t.host&&t.port?`${String(t.protocol).toUpperCase()} ${t.host}:${t.port}`:t?.label||""}function du(e){const t=e.name||e.id,o=[e.resolveError?r("解析失败"):Yh(e),e.enabled===!1?r("已禁用"):""].filter(Boolean);return o.length?r("{name}({parts})",{name:t,parts:o.join(" · ")}):t}const Qa="pool:";function Pe(){return window}function Le(e,t){Pe().wbApp?.toast?.(e,t)}function Za(e){const t=Pe().wbApp?.esc;return t?t(e):String(e??"").replace(/[&<>"']/g,a=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[a]||a)}function Ls(e){const t=Pe().wbApp?.formatTime;if(t)return t(e);const a=Number(e);return!Number.isFinite(a)||a<=0?"":new Date(a).toLocaleString("zh-CN")}function mt(e){return e instanceof Error?e.message:String(e)}function ss(e){if(e==null||e==="")return"—";const t=Number(e);return Number.isFinite(t)?String(Number(t.toFixed(2))):String(e)}function fu(e){if(e==null||e==="")return null;const t=Number(e);return Number.isFinite(t)?t:null}function cj(e){const t=new Date(e);if(Number.isNaN(t.getTime()))return"";const a=o=>String(o).padStart(2,"0");return`${a(t.getMonth()+1)}-${a(t.getDate())} ${a(t.getHours())}:${a(t.getMinutes())}`}function X6(e){return e==="积分"?r("积分"):e==="额度"?r("额度"):e}function Q6(e){if(!e||typeof e!="object"||Array.isArray(e))return[];const t=e,a=Array.isArray(t.credits)?t.credits:[],o=Date.now();return a.map(c=>{const d=String(c?.name||"").trim()||r("积分包"),f=String(c?.unit||"").trim(),p=fu(c?.total),h=fu(c?.used),b=fu(c?.remain),y=c?.unlimited===!0,v=fu(c?.grantAt),j=fu(c?.startAt),w=fu(c?.expireAt),S=c?.daily===!0,N=String(c?.grantReason||"").trim(),k=w!==null&&w>0&&w<=o,A=p!==null&&p>0,P=y?r("不限"):b!==null?A?r("剩 {remaining} / {total}",{remaining:ss(b),total:ss(p)}):r("剩 {remaining}",{remaining:ss(b)}):h!==null?r("已用 {used}",{used:ss(h)}):"—",R=!y&&f&&P!=="—"?`${P} ${X6(f)}`:P,C=A&&b!==null?Math.max(0,Math.min(100,b/p*100)):null,E=b!==null&&b<=0||b===null&&h!==null&&A&&h>=p,D=!y&&!S&&(E||k),I=[];v!==null&&v>0?I.push(r("{time} 领取",{time:cj(v)})):j!==null&&j>0&&I.push(r("{time} 生效",{time:cj(j)})),S?I.push(r("每日重置")):w!==null&&w>0&&I.push(k?r("已过期"):r("{time} 到期",{time:cj(w)})),N&&I.push(N);const O=k||b===0?"muted":C!==null&&C<20?"warn":b===null&&h===null&&!y?"muted":"ok",_=[d,R,v!==null&&v>0?r("领取 {time}",{time:Ls(v)}):j!==null&&j>0?r("生效 {time}",{time:Ls(j)}):"",w!==null&&w>0?k?r("已过期({time})",{time:Ls(w)}):r("到期 {time}",{time:Ls(w)}):"",S?r("每日重置(额度按周期回满,不是作废)"):"",N,C===null&&b!==null?r("上游未给总量,画不出剩余比例"):"",h!==null&&p!==null?r("已用 {used} / 共 {total}",{used:ss(h),total:ss(p)}):""].filter(Boolean).join(" · ");return{name:d,view:R,tone:O,percent:C,window:I.length?I.join(" · "):null,title:_,spent:D}})}function EA({row:e}){return s.jsxs("div",{className:"usage-pop-row",title:e.title,children:[s.jsxs("div",{className:"usage-pop-line",children:[s.jsx("span",{className:"usage-pop-name",children:e.name}),s.jsx("span",{className:`usage-pop-view ${e.tone}`,children:e.view})]}),e.window?s.jsx("div",{className:"usage-pop-window",children:e.window}):null,e.percent!==null?s.jsx("span",{className:"usage-pop-track",children:s.jsx("i",{style:{width:`${e.percent}%`},"aria-label":r("剩余 {percent}%",{percent:Math.round(e.percent)})})}):null]})}function AA({rows:e,subscriptionLine:t,variant:a,kind:o,title:c,children:d}){const[f,p]=x.useState(!1),[h,b]=x.useState(!1),y=e.filter(k=>!k.spent),v=e.filter(k=>k.spent),j=a==="pool"?`usage-toggle usage-pool-line ${o}`:`usage-toggle ${o}`,w=x.useRef(null),S=x.useCallback(k=>{w.current=k,k&&(k.scrollTop=0)},[]),N=x.useCallback(()=>w.current??null,[]);return x.useLayoutEffect(()=>{if(!h)return;const k=w.current?.querySelector(".usage-pop-spent .usage-pop-row");k instanceof HTMLElement&&k.scrollIntoView({block:"nearest"})},[h]),s.jsxs(wf,{open:f,onOpenChange:p,children:[s.jsxs(Kh,{render:s.jsx("button",{type:"button",className:j,title:c,"aria-label":r("积分明细")}),children:[a==="pool"?d:s.jsx("span",{className:"val",children:d}),s.jsx("span",{className:"caret","aria-hidden":!0,children:"▾"})]}),s.jsx(Sf,{align:"end",sideOffset:6,className:"w-[320px]",initialFocus:N,children:s.jsxs("div",{className:"usage-pop-credit",ref:S,tabIndex:-1,children:[t?s.jsx("div",{className:"usage-pop-sub",children:t}):null,s.jsx("div",{className:"usage-pop-group",children:r("积分明细")}),y.map((k,A)=>s.jsx(EA,{row:k},`${k.name}-${A}`)),v.length>0?s.jsxs("div",{className:"usage-pop-spent",children:[s.jsxs("button",{type:"button",className:"usage-pop-spent-head","aria-expanded":h,title:r("展开或收起(默认收起)"),onClick:()=>b(k=>!k),children:[s.jsx("span",{className:"caret","aria-hidden":!0,children:"▾"}),s.jsx("span",{children:r("已用完 / 已过期")}),s.jsx("span",{className:"n",children:r("{count} 份",{count:v.length})})]}),h?v.map((k,A)=>s.jsx(EA,{row:k},`spent-${k.name}-${A}`)):null]}):null]})})]})}const pu="workbuddy",Z6="raccoon",W6={workbuddy:{usage:!0,creditDetail:!0,edition:!0,identifier:"uid",expiry:"expiresAt"},"workbuddy-intl":{usage:!0,creditDetail:!0,edition:!1,identifier:"uid",expiry:"expiresAt"},raccoon:{usage:!0,creditDetail:!0,edition:!1,identifier:"userId",expiry:"tokenExpiresAt"},catpaw:{usage:!0,edition:!1,identifier:"uid",expiry:"tokenExpiresAt"},autoclaw:{usage:!0,creditDetail:!0,edition:!1,identifier:"userId",expiry:"tokenExpiresAt"},"autoclaw-intl":{usage:!0,creditDetail:!0,edition:!1,identifier:"userId",expiry:"tokenExpiresAt",emailAsName:!0},qoder:{usage:!0,creditDetail:!0,edition:!0,identifier:"userId",expiry:"expiresAt",emailAsName:!0},"qoder-intl":{usage:!0,creditDetail:!0,edition:!0,identifier:"userId",expiry:"expiresAt",emailAsName:!0},"cline-free":{usage:!0,edition:!1,identifier:"account",expiry:"expiresAt"},"cline-pass":{usage:!0,edition:!1,identifier:"account",expiry:"expiresAt"},accio:{usage:!0,edition:!0,identifier:"userId",expiry:"expiresAt",emailAsName:!0},"accio-cn":{usage:!0,edition:!0,identifier:"userId",expiry:"expiresAt",emailAsName:!0},zcode:{usage:!0,creditDetail:!0,claim:!0,planChannel:!0,edition:!0,identifier:"userId",expiry:"expiresAt"},"zcode-intl":{usage:!0,creditDetail:!0,claim:!0,planChannel:!0,edition:!0,identifier:"userId",expiry:"expiresAt"},codearts:{usage:!0,usageDetail:!0,edition:!1,identifier:"userId",expiry:"expiresAt",concurrencyDefault:3},trae:{usage:!0,creditDetail:!0,edition:!1,identifier:"uid",expiry:"expiresAt"},loomy:{usage:!0,creditDetail:!0,edition:!1,identifier:"userId",expiry:"expiresAt"},kuku:{usage:!0,edition:!1,identifier:"uid",expiry:""},officeace:{usage:!0,creditDetail:!0,edition:!1,identifier:"id",expiry:"expiresAt"},antigravity:{usage:!0,edition:!1,identifier:"userId",expiry:""},chatglm:{usage:!0,creditDetail:!0,edition:!1,identifier:"uid",expiry:""}},J6={usage:!1,edition:!1,identifier:"userId",expiry:"tokenExpiresAt",emailAsName:!1};function An(e){const t=e?.provider;return typeof t=="string"&&t?t:pu}function cr(e){return e&&W6[e]||J6}const e$="opencode",t$="https://opencode.ai/zen/go/v1";function RA(e,t){if(!e||e.clientEmulation!==e$)return!1;const o=((typeof t=="string"?t.trim():"")||String(e.baseUrl||"")).trim().replace(/\/+$/,"").toLowerCase();return o?o!==t$:!1}function n$(e){const t=e?An(e):"";return t.startsWith("custom-")&&(Pe().wbProviders?.customList?.()||[]).find(o=>o?.id===t)||null}function s$(e){return e?RA(n$(e),e.baseUrl):!1}function OA(e){const t=Array.isArray(e?.providers)?e.providers:[],a=Array.isArray(e?.accounts)?e.accounts:[],o=new Map;a.forEach(d=>{const f=An(d);o.set(f,(o.get(f)||0)+1)});const c=new Map;return t.forEach(d=>{d?.id&&c.set(String(d.id),{id:String(d.id),label:String(d.label||d.id),count:Number(d.count)||0})}),o.forEach((d,f)=>{c.has(f)||c.set(f,{id:f,label:Pe().wbProviders?.labelOf?.(f)||f,count:d})}),c.size||c.set(pu,{id:pu,label:Pe().wbProviders?.labelOf?.(pu)||"WorkBuddy 国内版",count:0}),[...c.values()]}function Xh(e){const t=cr(An(e)).identifier;return String(e?.[t]||"")}function MA(e){const t=Number(e)||0;return t<=0?0:t<1e11?t*1e3:t}function r$(e){const t=cr(An(e)).expiry;return MA(e?.[t])}function Ea(e){const t=An(e);return t.startsWith("custom-")?!!e?.balanceQuery?.scheme:cr(t).usage}const uj=30,DA=86400,IA=60,PA=1800,_A=86400,LA=1,a$=95e5;function l$(e){return e==="codearts"?[{type:"token",action:"skip",threshold:a$,reset:"daily",enabled:!0}]:e==="cline-free"?[]:[{type:"balance",action:"skip",threshold:LA,enabled:!0}]}function zA(e){const t=e?.usageQuery;if(!t||typeof t!="object")return{enabled:!0,interval:IA};const a=t.enabled===void 0?!0:t.enabled===!0,o=Number(t.interval)||0,c=o>=uj&&o<=DA;return{enabled:a,interval:a&&!c?IA:o}}function o$(e){if(!e||typeof e!="object")return!1;const t=e;if(t.type!=="balance"&&t.type!=="token"||t.action!=="skip"&&t.action!=="disable")return!1;const a=Number(t.threshold);if(!Number.isFinite(a)||a<=0)return!1;if(t.type==="token"&&t.reset!=="daily"){const o=Number(t.period);if(!Number.isInteger(o)||o_A)return!1}return!0}function i$(e){const t=e?.lowBalance;if(!t||typeof t!="object"||!t.mode)return l$(An(e));const a=Number(t.threshold),o=Number.isFinite(a)&&a>0?a:LA;return t.mode!=="skip"&&t.mode!=="disable"?[]:[{type:"balance",action:t.mode,threshold:o,enabled:!0}]}function mu(e){const t=e?.limiters;return Array.isArray(t)?t.filter(o$):i$(e)}function c$(e){return mu(e).filter(t=>t.type==="token")}function u$(e,t){const a=t*1e3;return e-e%a}function BA(e){return e.reset==="daily"?0:e.period??0}function dj(e,t=Date.now()){if(e===0){const a=new Date(t);return a.setHours(0,0,0,0),a.getTime()}return u$(t,e)}function Nf(e,t,a=Date.now()){if(e.type!=="token")return null;const o=BA(e),c=(t||[]).find(d=>Number(d.kind)===o);return c&&Number(c.windowStart)===dj(o,a)?c:null}function Qh(e,t){const a=mu(e).filter(f=>f.type==="balance"&&f.action==="skip"&&f.enabled!==!1&&f.threshold>0);if(!a.length||!t||typeof t!="object")return!1;const o=t;if(o.unlimited)return!1;const c=o.totalLeft??o.available;if(c==null||c==="")return!1;const d=Number(c);return Number.isFinite(d)&&a.some(f=>dc.type==="token"&&c.action==="skip"&&c.enabled!==!1);if(!a.length||!t?.length)return!1;const o=Date.now();return a.some(c=>{const d=Nf(c,t,o);return d!==null&&Number(d.used)>=c.threshold})}function d$(e,t){return mu(e).some(a=>{if(a.type!=="token"||a.action!=="disable"||a.enabled===!1)return!1;const o=Nf(a,t);return o!==null&&Number(o.used)>=a.threshold})}function pj(e,t=Date.now()){const a=BA(e),o=dj(a,t),c=a===0?Math.max(0,dj(0,o+864e5+36e5)-t):Math.max(0,o+a*1e3-t);return{kind:a,startMs:o,remainingMs:c,daily:a===0}}function Zh(e,t){if(e<=0)return"";const a=Math.ceil(e/6e4);if(t){const o=Math.ceil(e/36e5);return o>=1?r("今日剩 {n} 小时",{n:o}):r("今日剩 {n} 分钟",{n:a})}return a>=1?r("剩 {n} 分钟",{n:a}):r("剩不到 1 分钟")}function El(e){const t=Number(e);if(!Number.isFinite(t))return"—";const a=window.wbUnits;return a?.formatTokens?a.formatTokens(t):String(Math.round(t))}function HA(e){return e>0&&e%3600===0?r("{n} 小时",{n:e/3600}):e>0&&e%60===0?r("{n} 分钟",{n:e/60}):r("{n} 秒",{n:e})}function kf(e){return e?.desktop===!0}function UA(e){return e?.chatSupported!==!1}function f$(e){return typeof e=="string"&&e.startsWith("custom-")}function p$(e){return r(e==="enterprise"?"企业":e==="ultimate"?"旗舰":"个人")}function Wh(e,t){const a=Number(e?.priority??100)-Number(t?.priority??100);return a!==0?a:(Number(e?.addedAt)||0)-(Number(t?.addedAt)||0)}function ur(e){return e?.enabled!==!1}function Cf(e,t=""){const a=e?.rateLimits||{},o=Date.now();return t?Number(a[t]?.resetAt)>o:Object.values(a).some(c=>Number(c?.resetAt)>o)}function VA(e){return e?.edition==="intl"?"intl":"cn"}function FA(e){return cr(An(e)).claim?e?.canClaim!==!1:!1}const $A="coding-plan",So="start-plan";function Jh(e){return!!cr(An(e)).planChannel}function mj(e){return Jh(e)?String(e?.zcodePlan||"").trim()===So?So:$A:""}function m$(e){return r(e===So?"套餐池(Start Plan)":"编码套餐(Coding Plan)")}function qA(e){switch(String(e??"").trim()){case"pending":return r("待生效");case"active":return r("生效中");case"expired":return r("已过期");case"unknown":return r("状态未知");default:return String(e??"")}}function eg(e){const t=e?.claimPlans;if(!t||typeof t!="object"||Array.isArray(t))return[];const a=gj();return Object.entries(t).filter(([,o])=>Number(o)>0&&gj(Number(o))===a).map(([o])=>o)}function h$(e){return eg(e).length>0}function g$(e){const t=eg(e),a=t.length?r("({plans})",{plans:t.join(r("、"))}):"";return r("今天(北京时间 {day})已领取 {n} 份{names};还有其他可领套餐时,点这里可以继续领;活动按自然日发新套餐,明天可再领",{day:gj(),n:t.length,names:a})}function x$(e){return!!cr(An(e)).usageDetail}function GA(e){return!!cr(An(e)).creditDetail}function hj(e){return/^free$/i.test(String(e??"").trim())}function KA(e){const t=String(e??"").trim();return/^trial$/i.test(t)?r("试用版"):hj(t)?r("免费版"):t}function gj(e=Date.now()){return new Date(e+8*3600*1e3).toISOString().slice(0,10)}function Tf(e,t,a){return Cf(e)||Qh(e,t)||fj(e,a)}function YA(e,t){const a=String(t.q||"").trim().toLowerCase();return a?[hu(e),e?.name,e?.nickname,e?.email,Xh(e),e?.id].some(c=>String(c||"").toLowerCase().includes(a)):!0}function XA(e,t){return t.provider==="all"||An(e)===t.provider}function xj(e,t){return t.enabled==="all"?!0:t.enabled==="enabled"?ur(e):!ur(e)}function QA(e,t,a,o){if(t.limit==="all")return!0;if(!ur(e))return!1;const c=Tf(e,a?.(e),o?.(e));return t.limit==="limited"?c:!c}function ZA(e,t,a,o){return XA(e,t)&&xj(e,t)&&QA(e,t,a,o)&&YA(e,t)}function b$(e,t,a,o){return(e||[]).filter(c=>ZA(c,t,a,o))}function WA(e,t,a,o){const c=e||[],d=h=>c.filter(b=>XA(b,t)&&(h==="enabled"||xj(b,t))&&YA(b,t)),f=d("enabled"),p=d("limit");return{enabledAll:f.length,enabled:f.filter(ur).length,disabled:f.filter(h=>!ur(h)).length,limitAll:p.length,normal:p.filter(h=>ur(h)&&!Tf(h,a?.(h),o?.(h))).length,limited:p.filter(h=>ur(h)&&Tf(h,a?.(h),o?.(h))).length}}function bj(e){const t=e?.rateLimits;if(!t||typeof t!="object")return[];const a=Date.now();return Object.entries(t).map(([o,c])=>({model:o,...c&&typeof c=="object"?c:{}})).filter(o=>Number(o.resetAt)>a).sort((o,c)=>Number(o.resetAt)-Number(c.resetAt))}function vj(e){const t=(e||[]).slice().sort(Wh),a=new Map;return t.forEach((o,c)=>a.set(o.id,{position:c+1,total:t.length})),a}function v$(e,t,a,o){const c=(e||[]).slice().sort(Wh),d=[],f=new Map;for(const p of c){const h=An(p);f.has(h)||(f.set(h,[]),d.push(h)),f.get(h).push(p)}return d.map((p,h)=>{const b=f.get(p)||[];return{provider:p,label:Pe().wbProviders?.labelOf?.(p)||p,rank:h+1,members:b,accounts:t?b.filter(t):b,enabled:b.filter(ur).length,disabled:b.filter(y=>!ur(y)).length,limited:b.filter(y=>ur(y)&&Tf(y,a?.(y),o?.(y))).length}})}function hu(e){if(!e)return"";const t=String(e.name||"").trim();if(e.nameCustom===!0&&t)return t;const a=String(e.email||"").trim();return cr(An(e)).emailAsName&&a?a:e?.nickname||t||Xh(e)||e?.id||""}const yj="已限流",JA=e=>new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime();function jj(e){const t=Number(e);if(!Number.isFinite(t)||t<=0||t<=Date.now())return yj;const a=new Date(t),o=a.toLocaleTimeString("zh-CN",{hour:"2-digit",minute:"2-digit",hour12:!1}),c=Math.round((JA(a)-JA(new Date))/864e5);return c===0?r("今天 {clock}",{clock:o}):c===1?r("明天 {clock}",{clock:o}):r("{month}月{day}日 {clock}",{month:a.getMonth()+1,day:a.getDate(),clock:o})}function y$(e){const t=An(e);if(!cr(t).edition)return"";const a=VA(e),o=e?.editionLabel,c=o||(a==="intl"?"国际版":"国内版");return(Pe().wbProviders?.labelOf?.(t)||"").includes(c)?"":o||r(a==="intl"?"国际版":"国内版")}function j$(e){return[UA(e)?null:{text:r("仅账号管理"),kind:"plain",title:r("该提供商的推理转发尚未接入,账号不参与转发")},f$(An(e))&&e.hasCredentials===!1?{text:r("未配置凭证"),kind:"bad",title:r("这条自定义账号既没有 API Key,也没有勾选「无需鉴权」:转发时会被跳过,该提供商下的模型也不会出现在模型列表里。去账号「设置」里补上 Key,或勾选「该上游无需鉴权」")}:null,e.proxy?.error?{text:r("代理异常"),kind:"bad",title:r("{error}(转发时会回退直连)",{error:e.proxy.error})}:null,mj(e)===So?{text:r("套餐池"),kind:"plain",title:r("转发走套餐池(Start Plan)通道(zcode.z.ai 的 Anthropic 端点,用账号里的体验额度 / 活动领取额度);在账号设置里可切回编码套餐")}:null].filter(t=>t!==null)}const eR="workbuddy-desktop-accounts-filters",tR="workbuddy-desktop-accounts-collapsed",nR="workbuddy-desktop-accounts-names-hidden";function w$(){const e={provider:"all",enabled:"all",limit:"all",q:""},t=Pe().wbFilterMemory?.load(eR,e)||e;return{provider:String(t.provider||"all"),enabled:String(t.enabled||"all"),limit:String(t.limit||"all"),q:""}}function S$(){try{const e=JSON.parse(localStorage.getItem(tR)||"[]");return new Set(Array.isArray(e)?e.map(t=>String(t)).filter(Boolean):[])}catch{return new Set}}function N$(){try{return localStorage.getItem(nR)==="1"}catch{return!1}}let hn={version:0,filter:w$(),collapsed:S$(),selected:new Set,panels:new Map,connections:new Map,clash:null,dialog:null,namesHidden:N$(),usageBusy:!1,usageInflight:new Set};const wj=new Set;function sR(e){return wj.add(e),()=>{wj.delete(e)}}function Gs(){return hn}function Rn(e){hn={...hn,...e,version:hn.version+1},wj.forEach(t=>t())}function jr(){Rn({})}function Sj(){return Pe().wbApp?.getState?.()?.accounts}function Al(){const e=Sj()?.accounts;return Array.isArray(e)?e:[]}function Aa(e){return Al().find(t=>t.id===e)||null}function k$(e,t){const a=v$(Al(),c=>ZA(c,hn.filter,e,t),e,t);return{groups:a.filter(c=>c.accounts.length>0),allGroups:a}}function rR(){return OA(Sj())}function C$(e,t){return WA(Al(),hn.filter,e,t)}function T$(){return vj(Al())}function tg(){Pe().wbFilterMemory?.save(eR,{provider:hn.filter.provider,enabled:hn.filter.enabled,limit:hn.filter.limit})}function E$(e){hn.filter.q!==e&&Rn({filter:{...hn.filter,q:e}})}function A$(e){const t=e||"all";hn.filter.provider!==t&&(Rn({filter:{...hn.filter,provider:t}}),tg())}function aR(e,t){const a={...hn.filter,[e]:t};a.enabled==="disabled"&&a.limit!=="all"&&(a.limit="all"),Rn({filter:a}),tg()}function ng(){return hn.filter.provider!=="all"||hn.filter.enabled!=="all"||hn.filter.limit!=="all"||hn.filter.q.trim()!==""}function lR(){Rn({filter:{provider:"all",enabled:"all",limit:"all",q:""}}),tg()}function R$(){const e=hn.filter.provider;e!=="all"&&(rR().some(t=>t.id===e)||(Rn({filter:{...hn.filter,provider:"all"}}),tg()))}function oR(e){try{localStorage.setItem(tR,JSON.stringify([...e]))}catch{}}function iR(e){const t=new Set(hn.collapsed);t.has(e)?t.delete(e):t.add(e),oR(t),Rn({collapsed:t})}function O$(e,t){const a=new Set(hn.collapsed);for(const o of e)t?a.add(o):a.delete(o);oR(a),Rn({collapsed:a})}function M$(e){return hn.selected.has(e)}function D$(e,t){const a=new Set(hn.selected);t?a.add(e):a.delete(e),Rn({selected:a})}function cR(e,t){const a=new Set(hn.selected);for(const o of e)t?a.add(o):a.delete(o);Rn({selected:a})}function I$(){Rn({selected:new Set})}function uR(e,t){return hn.panels.get(e)?.has(t)===!0}function dR(e,t,a){if(!e)return;const o=new Map(hn.panels),c=new Set(o.get(e)||[]);a?c.add(t):c.delete(t),c.size?o.set(e,c):o.delete(e),Rn({panels:o})}function P$(e,t){const a=new Map(hn.panels);for(const o of e){const c=new Set(a.get(o)||[]);c.add(t),a.set(o,c)}Rn({panels:a})}function _$(){const e=!hn.namesHidden;try{localStorage.setItem(nR,e?"1":"0")}catch{}Rn({namesHidden:e})}function L$(e){return"*".repeat(Math.min(Math.max(e.length,4),12))}const fR="usage_not_configured",No=new Map,Ef=new Map;function ua(e,t,a){No.set(e,t),t!=null&&gu(t)?Ef.set(e,a):Ef.delete(e)}function Wa(e){const t=No.get(e.id);if(t===void 0)return;if(!gu(t))return t;const a=Math.max(Number(e.addedAt)||0,Number(e.updatedAt)||0);return(Ef.get(e.id)||0)>=a?t:void 0}const Af=new Map;let pR=0;function Rl(e){return e?Af.get(e.id)||[]:[]}function z$(e){const t=Number(e?.tokenAt)||0;if(!t||t===pR)return!1;const a=e?.tokenUsage;if(!a||typeof a!="object")return!1;pR=t;const o=new Map;for(const[d,f]of Object.entries(a)){if(!Array.isArray(f))continue;const p=[];for(const h of f){if(!h||typeof h!="object")continue;const b=Number(h.kind),y=Number(h.windowStart),v=Number(h.used);!Number.isFinite(b)||b<0||!Number.isFinite(y)||y<=0||!Number.isFinite(v)||v<0||p.push({kind:b,windowStart:y,used:v})}o.set(d,p)}const c=o.size!==Af.size||[...o].some(([d,f])=>{const p=Af.get(d);return!p||p.length!==f.length||f.some((h,b)=>h.used!==p[b]?.used||h.windowStart!==p[b]?.windowStart)});Af.clear();for(const[d,f]of o)Af.set(d,f);return c}let sg=null;async function mR(e={}){const t=Gs().clash;if(!e.force&&t)return t;sg||(sg=(async()=>{try{const o=await Pe().workbuddyDesktop?.getProxies?.();if(!o||typeof o!="object"||!o.clash)throw new Error(r("代理列表响应异常({type})",{type:typeof o}));return o.clash}finally{sg=null}})());const a=await sg;return Rn({clash:a}),a}function B$(){Rn({clash:null})}let Rf=null,rg=null;function H$(){return Rf}async function hR(e={}){if(!e.force&&Rf)return Rf;rg||(rg=(async()=>{try{const a=await Pe().workbuddyDesktop?.getProxyPool?.();if(!a||typeof a!="object"||!Array.isArray(a.items))throw new Error(r("代理池响应异常({type})",{type:typeof a}));return a.items}finally{rg=null}})());const t=await rg;return Rf=t,t}let Nj=null;function U$(){return Nj}let ag=0,gR=!1;function V$(){if(!(Rf||Date.now(){ag=0,Nj=null,jr()}).catch(e=>{Nj=mt(e),jr()})}}function lg(e){if(!Aa(e)){Le(r("账号不存在,请刷新后重试"),"err");return}Rn({dialog:{kind:"settings",id:e}})}function og(){Rn({dialog:null})}function kj(e,t="enable"){const a=(Array.isArray(e)?e:[]).filter(o=>Aa(o));if(!a.length){Le(r("请先勾选要操作的账号"),"err");return}Rn({dialog:{kind:"batch",ids:a,action:t}})}function F$(e){let t=!1;for(const f of[...No.keys()])e.has(f)||(No.delete(f),t=!0);for(const f of[...Ef.keys()])e.has(f)||Ef.delete(f);const a=new Map(Gs().panels);for(const f of[...a.keys()])e.has(f)||(a.delete(f),t=!0);const o=new Map(Gs().connections);for(const f of[...o.keys()])e.has(f)||(o.delete(f),t=!0);const c=new Set(Gs().usageInflight);for(const f of[...c])e.has(f)||(c.delete(f),t=!0);const d=new Set(Gs().selected);for(const f of[...d])e.has(f)||(d.delete(f),t=!0);t&&Rn({panels:a,connections:o,usageInflight:c,selected:d})}const $$=2e3;function xR(e){return Gs().connections.get(e)||0}async function bR(){try{const e=await Pe().workbuddyDesktop?.getAccountConnections?.(),t=e?.counts&&typeof e.counts=="object"?e.counts:{},a=new Map;for(const[o,c]of Object.entries(t)){const d=Number(c)||0;d>0&&a.set(o,d)}return q$(a,Gs().connections)||Rn({connections:a}),!0}catch{return!1}}function q$(e,t){if(e.size!==t.size)return!1;for(const[a,o]of e)if(t.get(a)!==o)return!1;return!0}let vR=null;function G$(){vR||(vR=setInterval(()=>{document.hidden||Pe().wbApp?.currentPage!=="accounts"||bR()},$$))}function Cj(){return Al().filter(e=>Ea(e)&&e.available!==!1)}function gu(e){if(e==null)return null;if(typeof e=="string")return{message:e,notConfigured:!1};if(typeof e!="object")return null;const t=e.error;return t?{message:String(t),notConfigured:e.code===fR}:null}function yR(e){return e.usage?e.usage:{error:e.error?String(e.error):r("余额响应为空"),code:e.code}}function jR(e){const t=Array.isArray(e?.results)?e.results:[],a=Number(e?.at)||Date.now();let o=0;for(const c of t)c?.id&&(ua(String(c.id),yR(c),Number(c.at)||a),o++);return o&&(jr(),Pe().wbApp?.renderTopbarStatus?.()),o}let wR=!1;function K$(e){if(!GA(e))return!1;const t=Wa(e);return!t||typeof t!="object"||Array.isArray(t)||gu(t)?!1:!Array.isArray(t.credits)}async function Y$(){if(!wR){wR=!0;for(const e of Al().filter(K$)){const t=No.get(e.id);try{await Of(e.id)}catch{}const a=No.get(e.id);t!==void 0&&(!a||gu(a))&&ua(e.id,t,Date.now()),jr()}}}let SR=0;async function X$(){try{const e=await Pe().workbuddyDesktop?.getBalancesSnapshot?.(),t=z$(e);t&&(jr(),Pe().wbApp?.renderTopbarStatus?.());const a=Number(e?.at)||0;return!a||a===SR||(SR=a,!jR(e))?t:(Y$(),!0)}catch{return!1}}async function Of(e){const t=await Pe().workbuddyDesktop?.getAllBalances?.(e||void 0),a=Array.isArray(t?.results)?t.results:[],o=Date.now(),c=new Set;for(const d of a){if(!d?.id)continue;const f=String(d.id);e&&f!==e||(c.add(f),ua(f,yR(d),o))}if(e)return c.has(e)||ua(e,r("未返回余额数据"),o),jr(),t;for(const d of Cj())c.has(d.id)||ua(d.id,r("未返回余额数据"),o);return jr(),t}async function NR(){if(Gs().usageBusy)return;const e=Cj();if(!e.length){Le(r("暂无可查询余额的账号"),"err");return}Rn({usageBusy:!0}),e.forEach(t=>ua(t.id,null,Date.now())),jr();try{const t=(await Of(null))?.results||[],a=t.filter(f=>f.usage).length,o=t.filter(f=>f.code===fR).length,c=o?r("({n} 个未配置查询凭证)",{n:o}):"",d=t.length-a-o;Le(a+o===t.length?r("✅ 已更新 {n} 个账号的余额{suffix}",{n:a,suffix:c}):r("已更新 {ok}/{total} 个账号,{failed} 个失败",{ok:a,total:t.length,failed:d}),d?"err":"ok")}catch(t){const a=mt(t);e.forEach(o=>ua(o.id,r("查询失败:{message}",{message:a}),Date.now())),jr(),Le(r("余额查询失败:{message}",{message:a}),"err")}finally{Rn({usageBusy:!1})}}async function kR(e){if(Gs().usageInflight.has(e))return;const t=new Set(Gs().usageInflight);t.add(e),ua(e,null,Date.now()),Rn({usageInflight:t});try{await Of(e)}finally{const a=new Set(Gs().usageInflight);a.delete(e),Rn({usageInflight:a})}}async function Q$(e){if(!Gs().usageInflight.has(e))try{await kR(e);const t=gu(No.get(e));t?.notConfigured?Le(t.message,"ok"):t?Le(r("余额查询失败:{message}",{message:t.message}),"err"):Le(r("✅ 已更新余额"))}catch(t){ua(e,r("查询失败:{message}",{message:mt(t)}),Date.now()),jr(),Le(r("余额查询失败:{message}",{message:mt(t)}),"err")}}async function ig(e){if(e){const a=Aa(e);if(!a||!Ea(a))return;try{await kR(e)}catch(o){ua(e,r("查询失败:{message}",{message:mt(o)}),Date.now()),jr()}return}if(Gs().usageBusy)return;const t=Cj();if(t.length){Rn({usageBusy:!0}),t.forEach(a=>ua(a.id,null,Date.now())),jr();try{await Of(null)}catch(a){const o=mt(a);t.forEach(c=>ua(c.id,r("查询失败:{message}",{message:o}),Date.now())),jr()}finally{Rn({usageBusy:!1})}}}async function CR(e,t){try{await Pe().workbuddyDesktop?.updateAccount?.(e,{enabled:t}),await Pe().wbApp?.refresh?.(),Le(r(t?"✅ 已启用":"✅ 已禁用"))}catch(a){Le(r("操作失败:{message}",{message:mt(a)}),"err"),Pe().wbApp?.refresh?.()}}async function TR(e,t){if(!e.length)return!1;try{return await Pe().workbuddyDesktop?.reorderAccounts?.(e),Le(t?r("✅ 已保存:{note}",{note:t}):r("✅ 顺序已保存")),await Pe().wbApp?.refresh?.(),!0}catch(a){return Le(r("顺序未保存:{message}",{message:mt(a)}),"err"),Pe().wbApp?.refresh?.(),!1}}async function ER(e,t){if(e.length)try{await Pe().workbuddyDesktop?.batchAccounts?.({action:t?"enable":"disable",ids:e}),Le(t?r("✅ 已启用 {n} 个账号",{n:e.length}):r("✅ 已禁用 {n} 个账号",{n:e.length})),await Pe().wbApp?.refresh?.()}catch(a){Le(r("操作失败:{message}",{message:mt(a)}),"err"),Pe().wbApp?.refresh?.()}}async function Z$(e,t){try{await Pe().workbuddyDesktop?.clearRateLimits?.(e,t||null),Le(t?r("✅ 已清除 {model} 的限流标记",{model:t}):r("✅ 已清除该账号全部限流标记")),await Pe().wbApp?.refresh?.()}catch(a){Le(r("清除失败:{message}",{message:mt(a)}),"err")}}async function W$(e,t,a){if(t===AR){lg(e);return}if(t!==cg&&t!==a)try{const o=t===""?null:{source:"pool",proxyId:t.slice(Qa.length)},d=(await Pe().workbuddyDesktop?.updateAccount?.(e,{proxy:o}))?.changes,f=Array.isArray(d)&&d.length?d[0]:r("代理已更新");Le(`✅ ${f}`)}catch(o){Le(r("保存失败:{message}",{message:mt(o)}),"err")}finally{await Pe().wbApp?.refresh?.()}}const cg="__proxy_custom__",AR="__proxy_custom_edit__";function RR(e,t){const a=Aa(e);if(!a){Le(r("账号不存在,请刷新后重试"),"err");return}const o=Pe().wbZcodePlans;if(!o?.open){Le(r("套餐明细弹窗未加载,请重启应用后重试"),"err");return}o.open({id:e,name:t||a.name})}function J$(e,t,a=1){return{seat:t.get(e.id)||{position:1,total:1},rank:a,picked:M$(e.id),usageEntry:No.get(e.id),limitsOpen:uR(e.id,"limits"),connections:xR(e.id)}}const e8={DEFAULT_PROVIDER_ID:pu,RACCOON_PROVIDER_ID:Z6,providerOf:An,providerFeatures:cr,providerSummaries:OA,identifierOf:Xh,tokenExpiryOf:r$,supportsUsage:Ea,isDesktopAccount:kf,supportsChat:UA,byPriorityOrder:Wh,typeLabel:p$,isEnabled:ur,isRateLimited:Cf,isLimited:e=>Tf(e,e?Wa(e):void 0,Rl(e)),accountEdition:VA,supportsClaim:FA,matchEnabled:xj,matchLimit:QA,visibleAccounts:b$,filterCounts:WA,positionMap:vj,activeLimits:bj,editionSuffix:y$,formatResetText:jj};function t8(){window.wbAccountsView={render:()=>jr(),refreshCaches:F$,openPanels:P$,syncConnections:bR,applyBalances:jR,syncBalancesSnapshot:X$,queryUsageFor:Of,queryAllUsage:NR,refreshUsageAfterCheckin:ig,supportsUsage:Ea,isDesktopAccount:kf,isEnabled:ur,isRateLimited:Cf},window.wbAccountsModel=e8,window.wbAccountPanel={open:lg,close:og,openBatch:kj,closeBatch:og,syncAddProvider:()=>Pe().wbAccountAddForms?.syncAddProvider?.(),invalidate:B$}}function n8(e){return e.type==="balance"?r("余额 < {threshold}",{threshold:e.threshold}):e.reset==="daily"?r("每日消耗 ≥ {count} Token(每天 0 点重置)",{count:El(e.threshold)}):r("每 {period}消耗 ≥ {count} Token",{period:HA(e.period??0),count:El(e.threshold)})}function s8(e){return e.action==="disable"?r("禁用该账号(需手动重新启用)"):r("跳过该账号(自动恢复)")}function r8(e){return`${Math.round(e.threshold).toLocaleString("en-US")} Token`}function a8(e,t){if(t.enabled===!1)return{text:r("已停用"),kind:"off"};if(t.type==="balance"){const b=Wa(e),y=Qh(e,b),v=b&&typeof b=="object"?b:null,j=v?v.totalLeft??v.available:void 0,w=j==null||j===""||v?.unlimited?"":r(" · 当前 {n}",{n:ss(j)});return y?{text:r("已跳过{current}",{current:w}),kind:"warn",title:r("余额低于阈值,转发时会跳过该账号(余额回升自动恢复)")}:{text:r("未触发{current}",{current:w}),kind:"ok"}}const a=Rl(e),o=Nf(t,a),c=o?o.used:0,d=El(t.threshold),f=t.reset==="daily"?r(" · 今日 {used} / {quota}",{used:El(c),quota:d}):r(" · 本周期 {used} / {quota}",{used:El(c),quota:d}),p=o?` · ${Zh(pj(t).remainingMs,t.reset==="daily")}`:"";return o!==null&&c>=t.threshold?t.action==="disable"?e.enabled===!1?{text:r("已触发 · 账号已禁用"),kind:"danger",title:r("重置也不恢复,需手动启用")}:{text:r("已达上限{usage}",{usage:f}),kind:"warn",title:r("稍后由后台判定自动禁用")}:{text:r("已跳过{usage}{countdown}",{usage:f,countdown:p}),kind:"warn",title:r("窗口内已用 {used} / {limit} Token,转发时会跳过该账号(重置后自动恢复)",{used:Math.round(c).toLocaleString("en-US"),limit:Math.round(t.threshold).toLocaleString("en-US")})}:{text:r("未触发{usage}{countdown}",{usage:f,countdown:p}),kind:"ok",title:r8(t)}}function l8(e){return e>0&&e%3600===0?{value:String(e/3600),unit:"hours"}:e>0&&e%60===0?{value:String(e/60),unit:"minutes"}:{value:"1",unit:"hours"}}function OR(e){const t={type:"balance",action:"skip",thresholdInput:"",tokenUnit:"wan",reset:"fixed",periodInput:"1",periodUnit:"hours"};if(!e)return t;if(e.type==="balance")return{...t,type:"balance",action:e.action,thresholdInput:String(e.threshold)};if(e.reset==="daily")return{...t,type:"token",action:e.action,reset:"daily",thresholdInput:String(e.threshold%1e4===0?e.threshold/1e4:e.threshold),tokenUnit:e.threshold%1e4===0?"wan":"unit"};const a=l8(e.period??0);return{...t,type:"token",action:e.action,reset:"fixed",thresholdInput:String(e.threshold%1e4===0?e.threshold/1e4:e.threshold),tokenUnit:e.threshold%1e4===0?"wan":"unit",periodInput:a.value,periodUnit:a.unit}}function MR(e){const t=Number(e.thresholdInput);if(!Number.isFinite(t)||t<=0)return{error:r("限制阈值必须是大于 0 的数字")};if(e.type==="balance")return{rule:{type:"balance",action:e.action,threshold:t,enabled:!0}};const a=Math.round(e.tokenUnit==="wan"?t*1e4:t);if(e.reset==="daily")return{rule:{type:"token",action:e.action,threshold:a,reset:"daily",enabled:!0}};const o=Number(e.periodInput)*(e.periodUnit==="hours"?3600:60);if(!Number.isFinite(o)||o<=0)return{error:r("请填写重置周期")};const c=Math.round(o);return c_A?{error:r("重置周期必须是 30 分钟 ~ 24 小时")}:{rule:{type:"token",action:e.action,threshold:a,period:c,reset:"fixed",enabled:!0}}}function o8({account:e,target:t,onClose:a,onSave:o}){const[c,d]=x.useState(""),[f,p]=x.useState(t?.draft??null);if(x.useEffect(()=>{p(t?.draft??null),d("")},[t]),!t||!f)return null;const h=v=>{d(""),p({...f,...v})},b=()=>{const v=MR(f);if("error"in v){d(v.error);return}o(v.rule,t.index)},y=()=>{if(f.type==="balance"){const P=Wa(e);if(P&&typeof P=="object"){const R=P;if(R.unlimited)return r("当前可用 ∞(不参与余额不足判定)");const C=R.totalLeft??R.available;if(C!=null&&C!=="")return r("当前余额 {n};与余额列同一数字口径,严格小于才触发(等于阈值仍可用)",{n:ss(C)})}return r("读数来自自动余额查询;与余额列同一数字口径,严格小于才触发(等于阈值仍可用)")}const v=MR(f);if(!("rule"in v))return"";const j=v.rule,w=Nf(j,Rl(e));if(!w)return j.reset==="daily"?r("窗口 = 今天 0 点起(本地时区),0 点重置即清零恢复;按账号在 requests 表聚合"):r("窗口对齐自然时间(整点 / 整 N 分钟一轮回),重置即清零恢复;按账号在 requests 表聚合");const S=pj(j),N=El(w.used),k=El(j.threshold),A=j.reset==="daily"?r("今日已用"):r("本周期已用");return r("{scope} {used} / {quota} Token,{countdown}重置",{scope:A,used:N,quota:k,countdown:Zh(S.remainingMs,S.daily)})};return s.jsx(Yt,{open:!0,onOpenChange:v=>{v||a()},children:s.jsxs(Xt,{className:"w-[min(580px,calc(100vw-48px))]",showCloseButton:!0,children:[s.jsx(Vt,{children:s.jsx(Ut,{children:t.index>=0?r("编辑限制"):r("添加限制")})}),s.jsxs(Lt,{children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("限制类型")}),s.jsxs(ut,{value:f.type,onValueChange:v=>h({type:v,thresholdInput:""}),children:[s.jsx(ft,{className:"w-[150px]","aria-label":r("限制类型"),children:s.jsx(dt,{children:f.type==="balance"?r("余额"):r("Token 消耗")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"balance",children:r("余额")}),s.jsx($e,{value:"token",children:r("Token 消耗")})]})]}),s.jsx("span",{className:"detail",children:r("余额沿用现有口径;Token 按重置方式内的累计消耗判定(切换类型会清空阈值)")})]}),f.type==="balance"?s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("触发条件")}),s.jsx("span",{className:"detail",style:{fontSize:13},children:r("余额低于")}),s.jsx(Fe,{type:"number",min:0,step:1,className:"max-w-[120px]",placeholder:r("阈值"),value:f.thresholdInput,onChange:v=>h({thresholdInput:v.currentTarget.value})}),s.jsx("span",{className:"detail",style:{fontSize:13},children:r("时")})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("触发后")}),s.jsxs(ut,{value:f.action,onValueChange:v=>h({action:v}),children:[s.jsx(ft,{className:"min-w-[280px]","aria-label":r("触发动作"),children:s.jsx(dt,{children:f.action==="disable"?r("禁用该账号(需手动重新启用)"):r("跳过该账号(余额回升自动恢复)")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"skip",children:r("跳过该账号(余额回升自动恢复)")}),s.jsx($e,{value:"disable",children:r("禁用该账号(需手动重新启用)")})]})]})]})]}):s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("重置方式")}),s.jsxs(ut,{value:f.reset,onValueChange:v=>h({reset:v}),children:[s.jsx(ft,{className:"w-[190px]","aria-label":r("重置方式"),children:s.jsx(dt,{children:f.reset==="daily"?r("自然日(每天 0 点)"):r("固定周期")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"fixed",children:r("固定周期")}),s.jsx($e,{value:"daily",children:r("自然日(每天 0 点)")})]})]}),s.jsx("span",{className:"detail",children:r("有的账号过了 0 点额度就回来 —— 选「自然日」")})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("触发条件")}),f.reset==="daily"?s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"detail",style:{fontSize:13},children:r("每日消耗 ≥")}),s.jsx(Fe,{type:"number",min:0,step:1,className:"max-w-[110px]",placeholder:r("阈值"),value:f.thresholdInput,onChange:v=>h({thresholdInput:v.currentTarget.value})}),s.jsxs(ut,{value:f.tokenUnit,onValueChange:v=>h({tokenUnit:v}),children:[s.jsx(ft,{className:"w-[92px]","aria-label":r("Token 单位"),children:s.jsx(dt,{children:f.tokenUnit==="wan"?r("万"):r("个")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"wan",children:r("万")}),s.jsx($e,{value:"unit",children:r("个")})]})]}),s.jsx("span",{className:"detail",style:{fontSize:13},children:r("Token 时")})]}):s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"detail",style:{fontSize:13},children:r("每")}),s.jsx(Fe,{type:"number",min:1,step:1,className:"max-w-[90px]",placeholder:r("周期"),value:f.periodInput,onChange:v=>h({periodInput:v.currentTarget.value})}),s.jsxs(ut,{value:f.periodUnit,onValueChange:v=>h({periodUnit:v}),children:[s.jsx(ft,{className:"w-[92px]","aria-label":r("周期单位"),children:s.jsx(dt,{children:f.periodUnit==="hours"?r("小时"):r("分钟")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"minutes",children:r("分钟")}),s.jsx($e,{value:"hours",children:r("小时")})]})]}),s.jsx("span",{className:"detail",style:{fontSize:13},children:r("内消耗 ≥")}),s.jsx(Fe,{type:"number",min:0,step:1,className:"max-w-[110px]",placeholder:r("阈值"),value:f.thresholdInput,onChange:v=>h({thresholdInput:v.currentTarget.value})}),s.jsxs(ut,{value:f.tokenUnit,onValueChange:v=>h({tokenUnit:v}),children:[s.jsx(ft,{className:"w-[92px]","aria-label":r("Token 单位"),children:s.jsx(dt,{children:f.tokenUnit==="wan"?r("万"):r("个")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"wan",children:r("万")}),s.jsx($e,{value:"unit",children:r("个")})]})]}),s.jsx("span",{className:"detail",style:{fontSize:13},children:r("Token 时")})]})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("触发后")}),s.jsxs(ut,{value:f.action,onValueChange:v=>h({action:v}),children:[s.jsx(ft,{className:"min-w-[280px]","aria-label":r("触发动作"),children:s.jsx(dt,{children:f.action==="disable"?r("禁用该账号(需手动重新启用)"):r("跳过该账号(重置后自动恢复)")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"skip",children:r("跳过该账号(重置后自动恢复)")}),s.jsx($e,{value:"disable",children:r("禁用该账号(需手动重新启用)")})]})]})]})]}),s.jsx("p",{className:"detail limiter-hint",children:y()}),c?s.jsx("p",{className:"detail text-destructive",children:c}):null]}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"outline",onClick:a,children:r("取消")}),s.jsx(ne,{variant:"default",onClick:b,children:t.index>=0?r("保存规则"):r("添加规则")})]})]})})}function i8({account:e,draft:t,onChange:a}){const[o,c]=x.useState(null),d=()=>c({index:-1,draft:OR(null)}),f=y=>c({index:y,draft:OR(t[y])}),p=(y,v)=>{const j=[...t];v>=0?j[v]=y:j.push(y),a(j),c(null)},h=y=>{a(t.filter((v,j)=>j!==y)),o&&c(null)},b=(y,v)=>{const j=[...t];j[y]={...j[y],enabled:v},a(j)};return s.jsxs(s.Fragment,{children:[t.length?s.jsx("div",{className:"limiter-rules mt-2.5",children:t.map((y,v)=>{const j=a8(e,y);return s.jsxs("div",{className:"limiter-rule","data-off":y.enabled===!1||void 0,children:[s.jsx(ns,{checked:y.enabled!==!1,"aria-label":r("启用该规则"),onCheckedChange:w=>b(v,w===!0)}),s.jsx("div",{className:"limiter-rule-main",children:s.jsxs("span",{className:"limiter-rule-cond",children:[s.jsx(ze,{variant:"secondary",shape:"tag",children:y.type==="balance"?r("余额"):"Token"})," ",r("{cond} 时 → {action}",{cond:n8(y),action:s8(y)})]})}),s.jsx(ze,{variant:j.kind==="ok"?"success":j.kind==="warn"?"warning":j.kind==="danger"?"destructive":"secondary",shape:"tag",title:j.title,children:j.text}),s.jsxs("div",{className:"limiter-rule-ops",children:[s.jsx(ne,{variant:"ghost",onClick:()=>f(v),children:r("编辑")}),s.jsx(ne,{variant:"ghost",onClick:()=>h(v),children:r("删除")})]})]},v)})}):s.jsx("p",{className:"detail mt-2.5",children:r("还没有限制规则:账号照常参与转发,余额与 Token 消耗都不设上限。")}),s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx(ne,{variant:"outline",onClick:d,children:r("+ 添加限制")}),s.jsx("span",{className:"detail",children:r("可添加多条;同类规则也允许并存(例如一条按余额、两条不同重置方式的 Token)")})]}),s.jsx(o8,{account:e,target:o,onClose:()=>c(null),onSave:p})]})}function c8(e,t){const a=mu(e);return JSON.stringify(t)===JSON.stringify(a)?null:{patch:{limiters:t}}}function u8(e){return mu(e).map(t=>({...t}))}const d8={seconds:1,minutes:60,hours:3600};function f8(e){return e>0&&e%3600===0?{value:String(e/3600),unit:"hours"}:e>0&&e%60===0?{value:String(e/60),unit:"minutes"}:e>0?{value:String(e),unit:"seconds"}:{value:"5",unit:"minutes"}}function p8(e){const{enabled:t,interval:a}=zA(e),o=f8(t?a:0);return{enabled:t,intervalInput:o.value,unit:o.unit}}function m8(e,t){const a=Math.round(Number(t.intervalInput)*d8[t.unit]);if(t.enabled){if(!Number.isFinite(Number(t.intervalInput))||Number(t.intervalInput)<=0)return{error:r("请填写查询间隔")};if(aDA)return{error:r("查询间隔必须是 {min} 秒 ~ 24 小时",{min:uj})}}const o={},c=zA(e);return(t.enabled!==c.enabled||t.enabled&&a!==c.interval)&&(o.usageQuery={enabled:t.enabled,interval:t.enabled?a:c.interval}),o.usageQuery?{patch:o}:null}function h8({account:e,draft:t,onChange:a}){const o=c=>a({...t,...c});return s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"field-row mt-2.5",children:s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2.5 font-normal",children:[s.jsx(ns,{checked:t.enabled,onCheckedChange:c=>o({enabled:c===!0}),"aria-label":r("自动查询余额")}),s.jsx("span",{className:"text-xs text-subtle",children:r("自动查询余额(按下面的间隔自动刷新余额列的读数)")})]})}),t.enabled?s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:"account-usage-interval",children:r("查询间隔")}),s.jsx(Fe,{id:"account-usage-interval",type:"number",min:1,step:1,className:"max-w-[110px]",value:t.intervalInput,onChange:c=>o({intervalInput:c.currentTarget.value})}),s.jsxs(ut,{value:t.unit,onValueChange:c=>o({unit:c}),children:[s.jsx(ft,{className:"w-[92px]","aria-label":r("间隔单位"),children:s.jsx(dt,{children:t.unit==="hours"?r("小时"):t.unit==="minutes"?r("分钟"):r("秒")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"seconds",children:r("秒")}),s.jsx($e,{value:"minutes",children:r("分钟")}),s.jsx($e,{value:"hours",children:r("小时")})]})]}),s.jsx("span",{className:"detail",children:r("30 秒 ~ 24 小时;手动查询会顺延下一轮")})]}):null]})}const g8={sub2api:{label:"Sub2API",endpoint:"{base}/v1/usage",note:r("用这条账号的 API Key 查询,不需要额外配置。"),fields:[]},new_api:{label:"NewAPI",endpoint:"{base}/api/user/self + /api/status",note:r("用 NewAPI 站点的个人访问令牌查询账户余额(不是这条账号的上游 API Key)。令牌在站点的「个人设置」里生成,只有旧版 / 定制站点才需要额外填用户 ID。"),fields:[{key:"accessToken",label:r("NewAPI 个人访问令牌"),secret:!0,placeholder:r("在 NewAPI 站点「个人设置」里生成的访问令牌"),hint:r("必填;留空 = 不修改,清除用右边的按钮")},{key:"userId",label:r("NewAPI 用户 ID"),placeholder:r("旧版 / 定制站点才需要,新版可留空"),hint:r("可留空:新版请求头只认令牌;旧版会把用户 ID 放进 New-Api-User 头")}]},deepseek:{label:"DeepSeek",endpoint:"{base}/user/balance",note:r("用这条账号的 API Key 查询,不需要额外配置。"),fields:[]},opencode:{label:"OpenCode Go",endpoint:"{base}/v1/usage",note:r("用这条账号的 API Key 查询订阅用量,不需要额外配置;读数展开成 5 小时 / 每周 / 每月三个窗口。"),fields:[]},sensenova:{label:"商汤日日新",endpoint:"platform.sensenova.cn 控制台",note:r("用商汤日日新控制台的账号密码查询(与推理用的 API Key 不是同一套)。网关会自动换取登录票并缓存,过期后自动重登,不需要手动维护。"),fields:[{key:"username",label:r("账号"),placeholder:r("商汤日日新控制台的登录账号"),hint:r("必填")},{key:"password",label:r("密码"),secret:!0,placeholder:r("商汤日日新控制台的登录密码"),hint:r("留空 = 不修改,清除用右边的按钮")}]},zhipu:{label:"智谱",endpoint:"bigmodel.cn 控制台",note:r("用 bigmodel.cn 控制台的访问令牌查询账户余额(人民币元)。令牌是登录后浏览器 Cookie `bigmodel_token_production` 的值:开发者工具 → 应用(Application)→ Cookie 里复制。"),fields:[{key:"token",label:r("访问令牌"),secret:!0,placeholder:r("bigmodel_token_production 的值"),hint:r("必填;留空 = 不修改,清除用右边的按钮")}]},zhipu_zai:{label:"智谱(z.ai 国际)",endpoint:"api.z.ai 控制台",note:r("用这条账号的 API Key 生成签名后查询账户余额(美元),不需要额外配置。要求 API Key 是 z.ai 的「id.secret」形态(控制台生成的 Key 就是)。"),fields:[]}};function Mf(e){return e?g8[e]??null:null}function x8(e,t){return t.secret?t.key==="accessToken"?e?.hasAccessToken===!0:t.key==="password"?e?.hasPassword===!0:t.key==="token"?e?.hasToken===!0:!1:!1}function b8(e){const t=String(e?.scheme||"");return t==="new_api"?e?.hasAccessToken===!0:t==="sensenova"?String(e?.username||"").trim()!==""&&e?.hasPassword===!0:t==="zhipu"?e?.hasToken===!0:!0}function v8(e){return{userId:String(e?.userId||""),username:String(e?.username||""),accessToken:"",password:"",token:""}}function y8(e,t){const a=Mf(e);if(!a||!a.fields.length)return null;const o={};for(const c of a.fields){const d=String(t[c.key]??"").trim();if(c.secret){d&&(o[c.key]=d);continue}o[c.key]=d}return Object.keys(o).length?o:null}function j8({field:e,value:t,configured:a,onValue:o,onClear:c,clearBusy:d,idPrefix:f}){const p=`${f}-${e.key}`;return s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:p,children:e.label}),s.jsx(Fe,{id:p,className:"min-w-[220px]",type:e.secret?"password":"text",autoComplete:e.secret?"new-password":"off",placeholder:a?r("已配置,留空则不修改"):e.placeholder,title:e.hint,value:t,onChange:h=>o(h.currentTarget.value)}),a&&c?s.jsx(ne,{variant:"outline",size:"sm",disabled:d===!0,onClick:c,title:r("清除已配置的 {label}",{label:e.label}),children:r("清除")}):null]})}function w8({spec:e,values:t,configured:a,onValue:o,onClear:c,clearBusy:d,idPrefix:f}){return s.jsxs(s.Fragment,{children:[s.jsxs("p",{className:"detail mt-2.5",children:[r("余额查询方案:{label}",{label:e.label}),s.jsx("span",{className:"ml-1 text-subtle",children:e.endpoint})," — ",e.note]}),e.fields.map(p=>s.jsx(j8,{field:p,idPrefix:f,value:t[p.key]||"",configured:a[p.key]===!0,onValue:h=>o(p.key,h),onClear:c?()=>c(p.key):void 0,clearBusy:d},p.key))]})}const DR=64;function S8(e,t){const a=An(e);return t.filter(o=>o.id!==e.id&&An(o)===a)}const Ol=999,IR=8;function ug(e){return hu(e)||e?.id||""}function PR(e){const t=e?.config?.source||e?.source,a=e?.config||null,o=t==="rotate";return{mode:t==="pool"?"pool":o?"rotate":t==="clash"?"clash":t==="custom"?"custom":"none",proxyId:t==="pool"?String(a?.proxyId||""):"",rotateAll:o?a?.all!==!1:!0,rotateIds:o&&Array.isArray(a?.proxyIds)?a.proxyIds.map(String):[],listenerUid:a?.source==="clash"||t==="clash"?String(a?.listenerUid||""):"",protocol:a?.protocol==="socks5"?"socks5":"http",host:a?.host||"",port:a?.port===void 0||a?.port===null?"":String(a.port),username:a?.username||"",password:a?.password||""}}function Tj(e){if(e.mode==="none")return null;if(e.mode==="pool"){if(!e.proxyId)throw new Error(r("请先选择已保存的代理"));return{source:"pool",proxyId:e.proxyId}}if(e.mode==="rotate"){if(e.rotateAll)return{source:"rotate",all:!0};if(!e.rotateIds.length)throw new Error(r("轮询至少要选择一条代理(或改选「全部」)"));return{source:"rotate",all:!1,proxyIds:[...e.rotateIds]}}if(e.mode==="clash"){if(!e.listenerUid)throw new Error(r("请先选择 Clash Verge 出口"));return{source:"clash",listenerUid:e.listenerUid}}const t=e.host.trim(),a=Number(e.port);if(!t)throw new Error(r("请填写代理主机地址"));if(!Number.isInteger(a)||a<1||a>65535)throw new Error(r("代理端口必须是 1-65535 的整数"));return{source:"custom",protocol:e.protocol==="socks5"?"socks5":"http",host:t,port:a,username:e.username.trim(),password:e.password}}function _R(e){const t=e.profileActive===!1?r("(其他订阅,可能未生效)"):"",a=e.enabled===!1?r("(已在 Clash 中禁用)"):"";return`${e.name} :${e.port}${t}${a}`}function LR({draft:e,onChange:t,idPrefix:a,allowRotate:o=!1}){const d=Gs().clash,f=Array.isArray(d?.options)?d.options:[],p=e.mode==="clash",[h,b]=x.useState(!1),[y,v]=x.useState(null),[j,w]=x.useState(null),[S,N]=x.useState(""),k=C=>t({...e,...C});x.useEffect(()=>{p&&mR().catch(()=>{})},[p]);const A=x.useCallback((C=!1)=>{N(""),hR({force:C}).then(E=>w(E)).catch(E=>{w([]),N(mt(E))})},[]);x.useEffect(()=>{(e.mode==="pool"||e.mode==="rotate")&&j===null&&A()},[e.mode,j,A]);function P(){return e.rotateAll?(j??[]).filter(C=>C.enabled!==!1).map(C=>C.id):[...e.rotateIds]}async function R(){if(h)return;if(e.mode==="rotate"){const E=P();if(!E.length){Le(r("轮询至少要选择一条代理(或改选「全部」)"),"err");return}const D=E.slice(0,IR);b(!0),v(r("正在连接上游…"));let I=0;const O=[];for(const H of D){const B=j?.find(L=>L.id===H)?.name||H;try{const L=await Pe().workbuddyDesktop?.testProxyPoolItem?.(H);L?.success?I+=1:O.push(`${B}(${L?.error||r("连接失败")})`)}catch(L){O.push(`${B}(${mt(L)})`)}}const _=E.length-D.length,z=r("✅ {ok}/{total} 个出口可用",{ok:I,total:D.length})+(_>0?r("(只测了前 {limit} 个,还有 {rest} 个未测)",{limit:IR,rest:_}):"");v(s.jsxs("span",{className:O.length?"text-destructive":"text-success",children:[z,O.length?` ${Za(O.join(r(";")))}`:""]})),b(!1);return}if(e.mode==="pool"){if(!e.proxyId){Le(r("请先选择已保存的代理"),"err");return}b(!0),v(r("正在连接上游…"));try{const E=await Pe().workbuddyDesktop?.testProxyPoolItem?.(e.proxyId);v(E?.success?s.jsxs("span",{className:"text-success",children:[r("✅ 出口可用"),E.ip?r(" 出口 IP {ip}",{ip:Za(E.ip)}):"",E.durationMs!==void 0&&E.durationMs!==""?` ${Za(String(E.durationMs))}ms`:""]}):s.jsxs("span",{className:"text-destructive",children:["❌ ",Za(E?.error||r("连接失败"))]}))}catch(E){v(s.jsxs("span",{className:"text-destructive",children:["❌ ",Za(mt(E))]}))}finally{b(!1)}return}let C;try{C=Tj(e)}catch(E){Le(mt(E),"err");return}b(!0),v(r("正在连接上游…"));try{const E=await Pe().workbuddyDesktop?.testProxy?.({proxy:C});E?.success?v(s.jsxs("span",{className:"text-success",children:[r("✅ 出口可用"),E.status!==void 0&&E.status!==""?` HTTP ${Za(String(E.status))}`:"",E.ip?r(" 出口 IP {ip}",{ip:Za(E.ip)}):"",E.durationMs!==void 0&&E.durationMs!==""?` ${Za(String(E.durationMs))}ms`:""]})):v(s.jsxs("span",{className:"text-destructive",children:["❌ ",Za(E?.error||r("连接失败"))]}))}catch(E){v(s.jsxs("span",{className:"text-destructive",children:["❌ ",Za(mt(E))]}))}finally{b(!1)}}return s.jsxs("div",{className:"flex flex-col",children:[s.jsxs(Ph,{value:e.mode,onValueChange:C=>k({mode:C}),className:"flex-row flex-wrap items-center gap-5","aria-label":r("代理方式"),children:[s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2 font-normal",children:[s.jsx(yo,{value:"none"}),r("无代理(直连)")]}),s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2 font-normal",children:[s.jsx(yo,{value:"pool"}),r("已保存的代理")]}),o?s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2 font-normal",title:r("逐请求轮换出口(换 IP):OpenCode Zen 免费档按 IP 限流,单个 IP 的额度用完会对它一律 429"),children:[s.jsx(yo,{value:"rotate"}),r("轮询(换 IP)")]}):null,p?s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2 font-normal",children:[s.jsx(yo,{value:"clash"}),"Clash Verge"]}):null,s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2 font-normal",children:[s.jsx(yo,{value:"custom"}),r("自定义")]})]}),e.mode==="pool"?s.jsxs("div",{className:"mt-3",children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:`${a}-pool-exit`,children:r("代理")}),s.jsxs(ut,{value:e.proxyId,disabled:!j?.length,onValueChange:C=>k({proxyId:String(C)}),children:[s.jsx(ft,{id:`${a}-pool-exit`,className:"min-w-[220px]",children:s.jsx(dt,{children:j?.find(C=>C.id===e.proxyId)?du(j.find(C=>C.id===e.proxyId)):j===null?r("正在读取…"):j.length?r("请选择代理"):r("还没有保存的代理")})}),s.jsx(pt,{children:(j??[]).map(C=>s.jsx($e,{value:C.id,children:du(C)},C.id))})]}),s.jsx(ne,{variant:"outline",size:"sm",onClick:()=>A(!0),children:r("重新读取")})]}),s.jsx("div",{className:"detail mt-1.5",children:S?r("读取失败:{error}",{error:S}):j===null?r("正在读取代理列表…"):j.length?r("地址与端口由「网络代理」页管理:那边改一次、测一次,所有引用它的账号一起生效"):r("「网络代理」页还没有出口 —— 去那里新增,或在「订阅管理」里点「同步 Clash Verge」把 Clash 的出口导进来")})]}):null,e.mode==="rotate"?s.jsxs("div",{className:"mt-3",children:[s.jsxs("div",{className:"flex flex-wrap items-center gap-x-2.5 gap-y-1",children:[s.jsx(Pr,{id:`${a}-rotate-all`,checked:e.rotateAll,"aria-label":r("轮询全部代理节点"),onCheckedChange:C=>k({rotateAll:C===!0})}),s.jsx("label",{htmlFor:`${a}-rotate-all`,className:"cursor-pointer text-xs text-subtle",children:r("全部代理节点(推荐)")}),s.jsx("span",{className:"detail",children:r("「网络代理」页里每个启用的出口轮流使用;之后新加的出口自动加入")})]}),e.rotateAll?null:s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:`${a}-rotate-exits`,children:r("轮询的代理")}),s.jsx(Gh,{id:`${a}-rotate-exits`,value:e.rotateIds,onValueChange:C=>k({rotateIds:C.map(String)}),options:(j??[]).map(C=>({value:C.id,label:du(C)})),placeholder:r("选择要轮询的代理(可多选)"),"aria-label":r("轮询的代理")}),s.jsx(ne,{variant:"outline",size:"sm",onClick:()=>A(!0),children:r("重新读取")})]}),s.jsx("div",{className:"detail mt-1.5",children:S?r("读取失败:{error}",{error:S}):j===null?r("正在读取代理列表…"):j.length?r("每条请求换一个出口(按顺序轮流),上游看到的 IP 因此是变化的;出口的地址与端口由「网络代理」页管理"):r("「网络代理」页还没有出口 —— 去那里新增,或在「订阅管理」里点「同步 Clash Verge」把 Clash 的出口导进来")})]}):null,e.mode==="clash"?s.jsxs("div",{className:"mt-3",children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:`${a}-clash-exit`,children:r("出口")}),s.jsxs(ut,{value:e.listenerUid,disabled:!f.length,onValueChange:C=>k({listenerUid:String(C)}),children:[s.jsx(ft,{id:`${a}-clash-exit`,className:"min-w-[220px]",children:s.jsx(dt,{children:f.find(C=>String(C.uid)===e.listenerUid)?_R(f.find(C=>String(C.uid)===e.listenerUid)):d?.available===!1?r("未检测到 Clash Verge"):f.length?r("请选择出口"):r("没有可用出口")})}),s.jsx(pt,{children:f.map(C=>s.jsx($e,{value:String(C.uid),children:_R(C)},String(C.uid)))})]}),s.jsx(ne,{variant:"outline",size:"sm",onClick:()=>{mR({force:!0}).then(()=>Le(r("✅ 已重新读取 Clash Verge 配置"))).catch(C=>Le(r("读取失败:{error}",{error:mt(C)}),"err"))},children:r("重新读取")})]}),s.jsx("div",{className:"detail mt-1.5",children:d===null?r("正在读取 Clash Verge 配置…"):d.available===!1?d.error?r("不可用:{error}",{error:d.error}):r("未检测到 Clash Verge 配置"):f.length?r("读取自 {dir};端口由 Clash Verge 管理,这里实时同步",{dir:d.dir||"Clash Verge"}):r("Clash Verge 里还没有配置混合监听器或节点端口")}),s.jsxs("p",{className:"detail mt-1.5",children:[r("这是既有配置(直接引用 Clash 出口,不经过代理池)。推荐改用上面的"),s.jsx("b",{children:r("「已保存的代理」")}),r(":到「网络代理」页的「订阅管理」里点「同步 Clash Verge」把出口导进池后即可选 —— 那样能集中测试、也能被多个账号共用。")]})]}):null,e.mode==="custom"?s.jsxs("div",{className:"mt-3",children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:`${a}-protocol`,children:r("协议")}),s.jsxs(ut,{value:e.protocol,onValueChange:C=>k({protocol:C}),children:[s.jsx(ft,{id:`${a}-protocol`,className:"min-w-[110px]",children:s.jsx(dt,{children:e.protocol==="socks5"?"SOCKS5":"HTTP"})}),s.jsxs(pt,{children:[s.jsx($e,{value:"http",children:"HTTP"}),s.jsx($e,{value:"socks5",children:"SOCKS5"})]})]}),s.jsx("label",{htmlFor:`${a}-host`,className:"ml-2",children:r("主机")}),s.jsx(Fe,{id:`${a}-host`,className:"min-w-[140px]",placeholder:"127.0.0.1",value:e.host,onChange:C=>k({host:C.currentTarget.value})}),s.jsx("label",{htmlFor:`${a}-port`,children:r("端口")}),s.jsx(Fe,{id:`${a}-port`,type:"number",min:1,max:65535,className:"max-w-[110px]",placeholder:"7890",value:e.port,onChange:C=>k({port:C.currentTarget.value})})]}),s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:`${a}-user`,children:r("用户名")}),s.jsx(Fe,{id:`${a}-user`,placeholder:r("可选"),autoComplete:"off",value:e.username,onChange:C=>k({username:C.currentTarget.value})}),s.jsx("label",{htmlFor:`${a}-pass`,children:r("密码")}),s.jsx(Fe,{id:`${a}-pass`,type:"password",placeholder:r("可选"),autoComplete:"new-password",value:e.password,onChange:C=>k({password:C.currentTarget.value})})]})]}):null,s.jsxs("div",{className:"field-row mt-3",children:[s.jsx(ne,{variant:"outline",size:"sm",disabled:h,onClick:()=>{R()},children:r(h?"测试中…":"测试出口")}),s.jsx("span",{className:"detail",children:y})]})]})}function N8({plan:e,hasJwt:t,onChange:a}){const o=m$(e),[c,d]=x.useState(null);return x.useEffect(()=>{if(e!==So)return()=>{};let f=!0;const p=()=>{Promise.resolve(Pe().workbuddyDesktop?.zcodeCaptchaStats?.()).then(b=>{f&&b&&d({ready:Number(b.ready)||0,target:Number(b.target)||0})}).catch(()=>{})};p();const h=window.setInterval(p,5e3);return()=>{f=!1,window.clearInterval(h)}},[e]),s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:"account-plan-channel",children:r("使用套餐")}),s.jsxs(ut,{value:e,onValueChange:f=>a(String(f)),children:[s.jsx(ft,{id:"account-plan-channel",className:"min-w-[220px]",children:s.jsx(dt,{children:o})}),s.jsxs(pt,{children:[s.jsx($e,{value:$A,children:r("编码套餐(Coding Plan)— 走开放平台端点,消耗开放平台的额度(订阅套餐 / 资源包 / 充值余额)")}),s.jsx($e,{value:So,children:r("套餐池(Start Plan)— 走 zcode.z.ai 端点,消耗 ZCode 套餐池的额度(新用户体验额度 / 活动领取)")})]})]}),e===So&&!t?s.jsx("span",{className:"detail text-destructive",children:r("该账号没有套餐登录态(jwt),套餐池通道会失败:请重新登录该账号")}):null,e===So?s.jsxs("span",{className:"detail",title:r("套餐池的推理端点要求每条请求带一个阿里云验证码令牌;令牌由本应用在后台静默铸造,界面关闭时无法铸造"),children:[r("验证码令牌:{counts}",{counts:c?`${c.ready} / ${c.target||3}`:r("读取中…")}),c&&c.ready===0?r("(库存为空,正在补;补不上时转发会失败)"):""]}):null]})}function k8({configured:e,value:t,onChange:a,onClear:o,clearBusy:c}){return s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:"account-balance-token-input",children:r("余额查询凭证")}),s.jsx(Fe,{id:"account-balance-token-input",className:"min-w-[220px]",placeholder:r(e?"已配置,留空则不修改":"一般不用填"),title:r("积分查询已复用转发用的登录凭证,这里通常留空即可。只有旧版本填过、或从旧代理导入过凭证时才有值"),value:t,onChange:d=>a(d.currentTarget.value)}),e?s.jsx(ne,{variant:"outline",size:"sm",disabled:c,onClick:o,title:r("清除已配置的余额查询凭证"),children:r("清除")}):null]})}function C8({hasApiKey:e,noAuth:t,value:a,onValue:o,onToggleNoAuth:c,onClear:d,clearBusy:f}){return s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:"account-api-key-input",children:r("API Key")}),s.jsx(Fe,{id:"account-api-key-input",type:"password",autoComplete:"new-password",className:"min-w-[220px]",disabled:t,placeholder:e?r("已配置,留空则不修改"):"sk-…",title:r("上游的 API Key。留空 = 不修改(改凭证不会换账号 id);要清掉已配的凭证用右边的「清除」"),value:a,onChange:p=>o(p.currentTarget.value)}),e&&!t?s.jsx(ne,{variant:"outline",size:"sm",disabled:f,onClick:d,title:r("清除已配置的 API Key(转发时会因缺少凭证被跳过)"),children:r("清除")}):null]}),s.jsx("div",{className:"field-row mt-2.5",children:s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2.5 font-normal",children:[s.jsx(Pr,{checked:t,"aria-label":r("该上游无需鉴权"),onCheckedChange:p=>c(p===!0)}),s.jsx("span",{className:"text-xs text-subtle",children:r("该上游无需鉴权(转发与拉取模型都不发送鉴权头)")})]})}),s.jsxs("p",{className:"detail",children:[r("上游本来不要凭证时(本地 Ollama、OpenCode Zen 的免费档)勾上这一项:账号会被正常选路;只把 Key 留空"),s.jsx("strong",{children:r("又不勾")}),r("会被当成「未配置凭证」而跳过。")]})]})}function T8({provider:e,name:t,protocol:a,baseUrl:o,count:c,emulation:d,onName:f,onProtocol:p,onBaseUrl:h,onEmulation:b,onRemove:y}){const v=Pe().wbProviders?.PROTOCOL_OPTIONS||[{value:"openai",label:r("OpenAI 兼容")},{value:"anthropic",label:"Anthropic"}],j=v.find(w=>w.value===a)?.label||a;return s.jsxs(dn,{children:[s.jsx("h3",{children:r("提供商")}),s.jsxs("p",{children:[r("这一栏改的是"),s.jsx("strong",{children:r("「{name}」本身",{name:e.name||e.id})}),r("(名下 {count} 个账号共用)",{count:c}),r("改协议 / Base URL 会改变它们的转发方式,正在进行的请求可能失败;模型清单与映射在「模型管理」页。")]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:"account-provider-name",children:r("名称")}),s.jsx(Fe,{id:"account-provider-name",maxLength:DR,className:"min-w-[220px]",placeholder:r("提供商显示名,1~{max} 个字符",{max:DR}),value:t,onChange:w=>f(w.currentTarget.value)})]}),s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:"account-provider-protocol",children:r("协议")}),s.jsxs(ut,{value:a,onValueChange:w=>p(String(w)),children:[s.jsx(ft,{id:"account-provider-protocol",className:"min-w-[180px]",children:s.jsx(dt,{children:j})}),s.jsx(pt,{children:v.map(w=>s.jsx($e,{value:w.value,children:w.label},w.value))})]})]}),s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:"account-provider-baseurl",children:r("Base URL")}),s.jsx(Fe,{id:"account-provider-baseurl",className:"min-w-[260px]",placeholder:r("OpenAI 兼容填到 /v1;Anthropic 填根地址"),value:o,onChange:w=>h(w.currentTarget.value)})]}),s.jsx("div",{className:"field-row mt-2.5",children:s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2.5 font-normal",children:[s.jsx(Pr,{checked:d==="opencode","aria-label":r("伪装 OpenCode 官方客户端"),onCheckedChange:w=>b(w===!0?"opencode":"")}),s.jsx("span",{className:"text-xs text-subtle",children:r("伪装 OpenCode 官方客户端(OpenCode Zen 免费档要它才放行)")})]})}),s.jsxs("p",{className:"detail",children:[r("开启后,网关按 OpenCode 官方 CLI 的形状发请求:没填 Key 的账号用匿名凭证"),s.jsx("code",{children:"public"}),r("、补官方会话头、请求体补两个占位工具(上游免费档的三道校验)。"),s.jsx("strong",{children:r("它会改写请求体")}),r(",只对 OpenCode Zen 这类上游有意义,别的家请关掉。")]}),s.jsxs("div",{className:"field-row mt-3",children:[s.jsx(ne,{variant:"destructive",onClick:y,children:r("删除提供商")}),s.jsx("span",{className:"detail",children:r("级联删除名下全部账号,不可恢复")})]})]})}function E8({id:e,onClose:t}){const a=Aa(e),[o,c]=x.useState(a?.enabled!==!1),[d,f]=x.useState(a?.name||""),[p,h]=x.useState(()=>PR(a?.proxy)),[b,y]=x.useState(""),[v,j]=x.useState(()=>mj(a)),[w,S]=x.useState(!1),[N,k]=x.useState(""),[A,P]=x.useState(null),[R,C]=x.useState(""),[E,D]=x.useState("chat_completions"),[I,O]=x.useState(""),[_,z]=x.useState(""),[H,B]=x.useState(""),[L,F]=x.useState(a?.noAuth===!0),[$,X]=x.useState(()=>p8(a)),[K,q]=x.useState(()=>v8(a?.balanceQuery)),[Q,G]=x.useState(()=>u8(a)),[J,Y]=x.useState("basic"),[re,M]=x.useState(()=>String(Number(a?.maxConcurrent)||0));if(x.useEffect(()=>{let Ae=!0;P(null);const oe=Aa(e);return oe?(Promise.resolve(Pe().wbCustomProvidersUi?.find?.(An(oe))).then(me=>{!Ae||!me||(P(me),C(me.name||""),D(me.protocol||"chat_completions"),O(me.baseUrl||""),z(typeof me.clientEmulation=="string"?me.clientEmulation:""))}).catch(()=>{}),()=>{Ae=!1}):()=>{Ae=!1}},[e]),!a)return s.jsx(Yt,{open:!0,onOpenChange:Ae=>{Ae||t()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("账号设置")})}),s.jsx(Lt,{children:s.jsx("p",{className:"detail",children:r("账号不存在,请刷新后重试。")})}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:t,children:r("关闭")})]})]})});const U=a,Z=S8(U,Al()),ae=An(U)==="catpaw",ce=An(U).startsWith("custom-"),W=RA(A,U.baseUrl)||p.mode==="rotate",ue=Mf(U.balanceQuery?.scheme),ve={};for(const Ae of ue?.fields??[])ve[Ae.key]=x8(U.balanceQuery,Ae);const ee=!!(ue&&ue.fields.length)&&!b8(U.balanceQuery);async function he(){if(!w){S(!0);try{await Pe().workbuddyDesktop?.updateAccount?.(e,{balanceToken:null}),y(""),Le(r("✅ 已清除余额查询凭证")),await Pe().wbApp?.refresh?.()}catch(Ae){Le(r("清除失败:{error}",{error:mt(Ae)}),"err")}finally{S(!1)}}}async function te(){if(!w){S(!0);try{await Pe().workbuddyDesktop?.updateAccount?.(e,{apiKey:null}),B(""),Le(r("✅ 已清除 API Key(该账号现在没有凭证,转发会被跳过)")),await Pe().wbApp?.refresh?.()}catch(Ae){Le(r("清除失败:{error}",{error:mt(Ae)}),"err")}finally{S(!1)}}}async function ke(Ae){if(!w){S(!0);try{await Pe().workbuddyDesktop?.updateAccount?.(e,{balanceQuery:{[Ae]:null}}),q(oe=>({...oe,[Ae]:""})),Le(r("✅ 已清除查询凭证")),await Pe().wbApp?.refresh?.()}catch(oe){Le(r("清除失败:{error}",{error:mt(oe)}),"err")}finally{S(!1)}}}function le(){if(!ce)return null;const Ae=H.trim(),oe={};return Ae?oe.apiKey=Ae:L!==(U.noAuth===!0)&&(oe.noAuth=L),Object.keys(oe).length?oe:null}function de(){if(!A)return null;const Ae=R.trim(),oe=I.trim();if(!Ae)return{error:r("请填写提供商名称")};if(!oe)return{error:r("请填写提供商的 Base URL")};const me=typeof A.clientEmulation=="string"?A.clientEmulation:"";return A.name===Ae&&A.protocol===E&&A.baseUrl===oe&&me===_?null:{id:A.id,name:Ae,protocol:E,baseUrl:oe,clientEmulation:_}}async function Se(){if(w)return;let Ae;try{Ae=Tj(p)}catch(Tt){Le(mt(Tt),"err");return}const oe=de();if(oe&&"error"in oe){k(s.jsx("span",{className:"text-destructive",children:oe.error}));return}const me=Ea(U)?m8(U,$):null;if(me&&"error"in me){k(s.jsx("span",{className:"text-destructive",children:me.error}));return}const pe=c8(U,Q),Ce=y8(U.balanceQuery?.scheme,K),Ne=le()||{},be=Number(re);if(!Number.isFinite(be)||be<0){Le(r("并发上限必须是 0~{max} 的整数",{max:Ol}),"err");return}const De=Math.min(Ol,Math.round(be)),Ge=Number(U.maxConcurrent)||0,Ct=De!==Ge?{maxConcurrent:De}:{};S(!0);try{const Tt=ae&&b.trim()?{balanceToken:b.trim()}:{},Mt=Jh(U)&&v!==mj(U)?{zcodePlan:v}:{};if(await Pe().workbuddyDesktop?.updateAccount?.(e,{name:d.trim()||U.name,enabled:o,proxy:Ae,...Tt,...Mt,...Ne,...Ct,...Ce?{balanceQuery:Ce}:{},...me&&"patch"in me?me.patch:{},...pe&&"patch"in pe?pe.patch:{}}),oe)try{await Pe().wbCustomProvidersUi?.update?.(oe)}catch(ot){k(s.jsx("span",{className:"text-destructive",children:r("账号已保存,但提供商未更新:{error}",{error:mt(ot)})})),await Pe().wbApp?.refresh?.(),S(!1);return}t(),Le(r("✅ 账号设置已保存"));try{await Pe().wbApp?.refresh?.()}catch{}}catch(Tt){k(s.jsx("span",{className:"text-destructive",children:mt(Tt)})),Le(r("保存失败:{error}",{error:mt(Tt)}),"err")}finally{S(!1)}}const Te=Number(cr(An(U)).concurrencyDefault)||0,_e=ae||ce||Jh(U);return s.jsx(Yt,{open:!0,onOpenChange:Ae=>{Ae||w||t()},children:s.jsxs(Xt,{className:"w-[min(900px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("账号设置 · {name}",{name:ug(a)})})}),s.jsx(Lt,{className:"p-0",children:s.jsxs("div",{className:"acct-dlg",children:[s.jsx("aside",{className:"prov-rail acct-dlg-rail","aria-label":r("账号设置分组"),children:s.jsxs("div",{className:"rail-scroll",children:[s.jsx("div",{className:"rail-label",children:r("账号设置")}),[["basic",r("基本")],..._e?[["credential",r("凭证与套餐")]]:[],...Ea(U)?[["query",r("查询设置")],["limiter",r("限制器")]]:[],["network",r("网络")]].map(([Ae,oe])=>s.jsx(uu,{active:J===Ae,title:oe,className:"shadow-none",onClick:()=>Y(Ae),children:oe},Ae))]})}),s.jsxs("div",{className:"acct-dlg-main",children:[J==="basic"?s.jsxs("div",{children:[s.jsxs("div",{className:"acct-dlg-pane-head",children:[s.jsx("h3",{children:r("基本")}),s.jsx("p",{children:r("这条账号是谁、排第几、能不能接请求。")})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("启用")}),s.jsx(ns,{checked:o,onCheckedChange:c,"aria-label":r("启用该账号")}),s.jsx("span",{className:"detail",children:r("关闭则不参与转发(余额查询照常,见「查询设置」)")})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:"account-name-input",children:r("备注名")}),s.jsx(Fe,{id:"account-name-input",maxLength:100,placeholder:r("账号显示名称"),className:"max-w-[280px]",value:d,onChange:Ae=>f(Ae.currentTarget.value)})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("转发顺序")}),s.jsx("span",{className:"detail",children:r("在列表里拖:拖分组带调整整组顺位,拖行首把手调整组内先后(顺序由拖拽表达,不再填数字)")})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:"account-conc-input",children:r("并发上限")}),s.jsx(Fe,{id:"account-conc-input",type:"number",min:0,max:Ol,step:1,className:"max-w-[110px]",value:re,onChange:Ae=>M(Ae.currentTarget.value)}),s.jsx("span",{className:"detail",children:Te>0?r("0 = 按本家默认 {default}",{default:Te}):r("0 = 不限")})]}),s.jsx("p",{className:"detail acct-dlg-hint",children:Te>0?r("并发上限 受上游硬顶约束,达到上限的账号跳过、请求转给其他账号(原列表 ⋯ 菜单的「并发上限」入口已并入这里)"):r("并发上限 0~{max};达到上限的账号跳过、请求转给其他账号(原列表 ⋯ 菜单的「并发上限」入口已并入这里)",{max:Ol})})]}):null,J==="credential"&&_e?s.jsxs("div",{children:[s.jsxs("div",{className:"acct-dlg-pane-head",children:[s.jsx("h3",{children:r("凭证与套餐")}),s.jsx("p",{children:r("这一段按提供商出现:有什么配什么。")})]}),ae?s.jsx(k8,{configured:a.hasBalanceToken===!0,value:b,onChange:y,clearBusy:w,onClear:()=>{he()}}):null,ce?s.jsx(C8,{hasApiKey:U.hasApiKey===!0,noAuth:L,value:H,onValue:B,onToggleNoAuth:F,onClear:()=>{te()},clearBusy:w}):null,Jh(U)?s.jsx(N8,{plan:v,hasJwt:a.canClaim===!0,onChange:j}):null,A?s.jsx(T8,{provider:A,count:Z.length+1,name:R,protocol:E,baseUrl:I,emulation:_,onName:C,onProtocol:D,onBaseUrl:O,onEmulation:z,onRemove:()=>{w||Promise.resolve(Pe().wbCustomProvidersUi?.remove?.(A.id)).then(Ae=>{Ae&&t()})}}):null]}):null,J==="query"&&Ea(U)?s.jsxs("div",{children:[s.jsxs("div",{className:"acct-dlg-pane-head",children:[s.jsx("h3",{children:r("查询设置")}),s.jsx("p",{children:r("余额列读数的自动刷新;也是「限制器」余额规则的数据来源。")})]}),ue?s.jsx(w8,{spec:ue,values:K,configured:ve,onValue:(Ae,oe)=>q(me=>({...me,[Ae]:oe})),onClear:Ae=>{ke(Ae)},clearBusy:w,idPrefix:"account-balance-query"}):null,ee?s.jsx("p",{className:"detail mt-2.5 text-subtle",children:r("查询凭证还没配齐:填好后余额列才有读数,在此之前会显示「未配置」。")}):null,s.jsx(h8,{account:U,draft:$,onChange:X})]}):null,J==="limiter"&&Ea(U)?s.jsxs("div",{children:[s.jsxs("div",{className:"acct-dlg-pane-head",children:[s.jsx("h3",{children:r("限制器")}),s.jsx("p",{children:r("按规则限制该账号参与转发:没有规则(或全部停用)= 不限制;跳过档自动恢复,禁用档需手动启用。")})]}),s.jsx(i8,{account:U,draft:Q,onChange:G})]}):null,J==="network"?s.jsxs("div",{children:[s.jsxs("div",{className:"acct-dlg-pane-head",children:[s.jsx("h3",{children:r("网络")}),s.jsx("p",{children:r("这条账号访问上游走哪条线路。")})]}),s.jsx(LR,{draft:p,onChange:h,idPrefix:"account-proxy",allowRotate:W}),a.proxy?.error?s.jsx("p",{className:"detail text-destructive",children:r("当前代理不可用:{error}(转发时会回退直连)",{error:a.proxy.error})}):null]}):null]})]})}),s.jsxs(tn,{children:[s.jsx("div",{className:"detail mr-auto",style:{minHeight:18},children:N}),s.jsx(ne,{variant:"outline",disabled:w,onClick:t,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:w,onClick:()=>{Se()},children:r(w?"保存中…":"保存")})]})]})})}const A8=[{value:"enable",label:r("启用")},{value:"disable",label:r("禁用")},{value:"proxy",label:r("修改代理")},{value:"concurrency",label:r("设置并发上限")},{value:"remove",label:r("删除")}];function R8({ids:e,action:t,onClose:a}){const[o,c]=x.useState(t),[d,f]=x.useState(()=>PR(null)),[p,h]=x.useState(""),[b,y]=x.useState(!1),[v,j]=x.useState(""),w=e.map(R=>Aa(R)).filter(R=>R!==null),S=w.reduce((R,C)=>Math.max(R,Number(cr(An(C)).concurrencyDefault)||0),0),N=w.length>0&&w.every(R=>s$(R));function k(R){return Promise.resolve(Pe().wbConfirm?.ask?.({title:r("删除选中的账号"),html:r("确定删除选中的 {count} 个账号?此操作不可恢复,账号的登录态会一并移除。",{count:R}),okText:r("删除"),okClass:"danger"})??!1)}async function A(){if(b)return;if(!e.length){Le(r("没有选中的账号"),"err");return}let R;if(o==="proxy")try{R=Tj(d)}catch(E){Le(mt(E),"err");return}let C;if(o==="concurrency"){const E=Number(p.trim());if(!p.trim()||!Number.isFinite(E)||E<0){Le(r("并发上限必须是 0~{max} 的整数",{max:Ol}),"err");return}C=Math.min(Ol,Math.round(E))}if(!(o==="remove"&&!await k(e.length))){if(o==="disable"||o==="remove"){const E=new Set(e);if(!Al().filter(I=>I.enabled!==!1&&!E.has(I.id)).length){const I=r(o==="remove"?"删除":"禁用");if(!await Promise.resolve(Pe().wbConfirm?.ask?.({title:r("全部启用中的账号将被{what}",{what:I}),html:r("这会{what}所有启用中的账号,转发将不可用。确定继续?",{what:I}),okText:I,okClass:o==="remove"?"danger":"primary"})??!1))return}}y(!0),j("");try{const E={action:o,ids:e};o==="proxy"&&(E.proxy=R),o==="concurrency"&&(E.maxConcurrent=C);const D=await Pe().workbuddyDesktop?.batchAccounts?.(E),I=(D?.ok||[]).filter(B=>B.changes?.length).length,O=(D?.removed||[]).length,_=D?.failed||[],z=o==="remove"?O:I,H={enable:r("启用"),disable:r("禁用"),proxy:r("修改代理"),concurrency:r("设置并发上限"),remove:r("删除")}[o]||o;if(_.length){const B=_.slice(0,3).map(L=>ug(Aa(String(L.id)))||L.id).join(r("、"));Le(r("{verb}完成:成功 {ok} 个,失败 {fail} 个({detail}{more})",{verb:H,ok:z,fail:_.length,detail:B,more:_.length>3?r(" 等"):""}),"err"),j(s.jsxs("span",{className:"text-destructive",children:[r("失败 {count} 个:",{count:_.length}),_.map(L=>`${ug(Aa(String(L.id)))||L.id}(${L.error})`).join(r(";"))]}))}else Le(r("✅ {verb}完成:共 {count} 个账号",{verb:H,count:z})),a();await Pe().wbApp?.refresh?.()}catch(E){j(s.jsx("span",{className:"text-destructive",children:mt(E)})),Le(r("批量操作失败:{error}",{error:mt(E)}),"err")}finally{y(!1)}}}const P={enable:r("启用"),disable:r("禁用"),proxy:r("修改代理"),concurrency:r("设置并发上限"),remove:r("删除")}[o]||o;return s.jsx(Yt,{open:!0,onOpenChange:R=>{R||b||a()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("批量操作 · 已选 {count} 个账号",{count:e.length})})}),s.jsxs(Lt,{children:[s.jsxs(dn,{children:[s.jsx("h3",{children:r("将作用于以下账号")}),s.jsx("p",{style:{maxHeight:84,overflowY:"auto"},children:w.map(R=>ug(R)).join(r("、"))})]}),s.jsxs(dn,{children:[s.jsx("h3",{children:r("操作")}),s.jsx(Ph,{value:o,onValueChange:c,className:"flex-row flex-wrap items-center gap-5","aria-label":r("批量动作"),children:A8.map(R=>s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2 font-normal",children:[s.jsx(yo,{value:R.value}),R.label]},R.value))}),o==="proxy"?s.jsx("div",{className:"mt-2.5",children:s.jsx(LR,{draft:d,onChange:f,idPrefix:"batch-proxy",allowRotate:N})}):null,o==="concurrency"?s.jsxs("div",{className:"mt-2.5",children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:"batch-conc-input",children:r("并发上限")}),s.jsx(Fe,{id:"batch-conc-input",type:"number",min:0,max:Ol,step:1,className:"max-w-[110px]",placeholder:r("不限"),value:p,onChange:R=>h(R.currentTarget.value)}),s.jsx("span",{className:"detail",children:r("0 = 不限")})]}),s.jsx("p",{className:"detail acct-dlg-hint",children:S>0?r("并发上限 0~{max};有上游硬顶的家(如 CodeArts)以它的硬顶为准;达到上限的账号会被跳过、请求转给其他账号",{max:Ol}):r("并发上限 0~{max};达到上限的账号会被跳过、请求转给其他账号",{max:Ol})})]}):null]}),s.jsx("div",{className:"detail",style:{minHeight:18},children:v})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",disabled:b,onClick:a,children:r("取消")}),s.jsx(ne,{variant:o==="remove"?"destructive":"default",disabled:b,onClick:()=>{A()},children:b?r("执行中…"):r("执行({action})",{action:P})})]})]})})}function O8(){const e=Gs();return e.dialog?e.dialog.kind==="settings"?s.jsx(E8,{id:e.dialog.id,onClose:og},e.dialog.id):s.jsx(R8,{ids:e.dialog.ids,action:e.dialog.action,onClose:og},e.dialog.ids.join(",")):null}var Ej={exports:{}},Df={},Aj={exports:{}},Rj={};var zR;function M8(){return zR||(zR=1,(function(e){function t(X,K){var q=X.length;X.push(K);e:for(;0>>1,G=X[Q];if(0>>1;Qc(re,q))Mc(U,re)?(X[Q]=U,X[M]=q,Q=M):(X[Q]=re,X[Y]=q,Q=Y);else if(Mc(U,q))X[Q]=U,X[M]=q,Q=M;else break e}}return K}function c(X,K){var q=X.sortIndex-K.sortIndex;return q!==0?q:X.id-K.id}if(e.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var d=performance;e.unstable_now=function(){return d.now()}}else{var f=Date,p=f.now();e.unstable_now=function(){return f.now()-p}}var h=[],b=[],y=1,v=null,j=3,w=!1,S=!1,N=!1,k=!1,A=typeof setTimeout=="function"?setTimeout:null,P=typeof clearTimeout=="function"?clearTimeout:null,R=typeof setImmediate<"u"?setImmediate:null;function C(X){for(var K=a(b);K!==null;){if(K.callback===null)o(b);else if(K.startTime<=X)o(b),K.sortIndex=K.expirationTime,t(h,K);else break;K=a(b)}}function E(X){if(N=!1,C(X),!S)if(a(h)!==null)S=!0,D||(D=!0,B());else{var K=a(b);K!==null&&$(E,K.startTime-X)}}var D=!1,I=-1,O=5,_=-1;function z(){return k?!0:!(e.unstable_now()-_X&&z());){var Q=v.callback;if(typeof Q=="function"){v.callback=null,j=v.priorityLevel;var G=Q(v.expirationTime<=X);if(X=e.unstable_now(),typeof G=="function"){v.callback=G,C(X),K=!0;break t}v===a(h)&&o(h),C(X)}else o(h);v=a(h)}if(v!==null)K=!0;else{var J=a(b);J!==null&&$(E,J.startTime-X),K=!1}}break e}finally{v=null,j=q,w=!1}K=void 0}}finally{K?B():D=!1}}}var B;if(typeof R=="function")B=function(){R(H)};else if(typeof MessageChannel<"u"){var L=new MessageChannel,F=L.port2;L.port1.onmessage=H,B=function(){F.postMessage(null)}}else B=function(){A(H,0)};function $(X,K){I=A(function(){X(e.unstable_now())},K)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(X){X.callback=null},e.unstable_forceFrameRate=function(X){0>X||125Q?(X.sortIndex=q,t(b,X),a(h)===null&&X===a(b)&&(N?(P(I),I=-1):N=!0,$(E,q-Q))):(X.sortIndex=G,t(h,X),S||w||(S=!0,D||(D=!0,B()))),X},e.unstable_shouldYield=z,e.unstable_wrapCallback=function(X){var K=j;return function(){var q=j;j=K;try{return X.apply(this,arguments)}finally{j=q}}}})(Rj)),Rj}var BR;function D8(){return BR||(BR=1,Aj.exports=M8()),Aj.exports}var HR;function I8(){if(HR)return Df;HR=1;var e=D8(),t=Xd(),a=M1();function o(n){var l="https://react.dev/errors/"+n;if(1ke||(n.current=te[ke],te[ke]=null,ke--)}function Se(n,l){ke++,te[ke]=n.current,n.current=l}var Te=le(null),_e=le(null),Ae=le(null),oe=le(null);function me(n,l){switch(Se(Ae,l),Se(_e,n),Se(Te,null),l.nodeType){case 9:case 11:n=(n=l.documentElement)&&(n=n.namespaceURI)?l5(n):0;break;default:if(n=l.tagName,l=l.namespaceURI)l=l5(l),n=o5(l,n);else switch(n){case"svg":n=1;break;case"math":n=2;break;default:n=0}}de(Te),Se(Te,n)}function pe(){de(Te),de(_e),de(Ae)}function Ce(n){var l=n.memoizedState;l!==null&&(qd._currentValue=l.memoizedState,Se(oe,n)),l=Te.current;var i=o5(l,n.type);l!==i&&(Se(_e,n),Se(Te,i))}function Ne(n){_e.current===n&&(de(Te),de(_e)),oe.current===n&&(de(oe),qd._currentValue=he)}var be,De;function Ge(n){if(be===void 0)try{throw Error()}catch(i){var l=i.stack.trim().match(/\n( *(at )?)/);be=l&&l[1]||"",De=-1xl in Si?cre(Si,xl,{enumerable:!0,configurable:!0,writable:!0,value:ro}):Si[xl]=ro;var cn=(Si,xl,ro)=>ure(Si,typeof xl!="symbol"?xl+"":xl,ro);(function(){"use strict";function Si(e,t){for(var a=0;ao[c]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}function xl(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var ro={exports:{}},Yd={};var gC;function W5(){if(gC)return Yd;gC=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.fragment");function a(o,c,d){var f=null;if(d!==void 0&&(f=""+d),c.key!==void 0&&(f=""+c.key),"key"in c){d={};for(var p in c)p!=="key"&&(d[p]=c[p])}else d=c;return c=d.ref,{$$typeof:e,type:o,key:f,ref:c!==void 0?c:null,props:d}}return Yd.Fragment=t,Yd.jsx=a,Yd.jsxs=a,Yd}var xC;function J5(){return xC||(xC=1,ro.exports=W5()),ro.exports}var s=J5(),Cv={exports:{}},ct={};var bC;function ez(){if(bC)return ct;bC=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.portal"),a=Symbol.for("react.fragment"),o=Symbol.for("react.strict_mode"),c=Symbol.for("react.profiler"),d=Symbol.for("react.consumer"),f=Symbol.for("react.context"),p=Symbol.for("react.forward_ref"),h=Symbol.for("react.suspense"),b=Symbol.for("react.memo"),y=Symbol.for("react.lazy"),v=Symbol.for("react.activity"),j=Symbol.for("react.view_transition"),w=Symbol.iterator;function S(M){return M===null||typeof M!="object"?null:(M=w&&M[w]||M["@@iterator"],typeof M=="function"?M:null)}var N={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},k=Object.assign,A={};function P(M,U,Z){this.props=M,this.context=U,this.refs=A,this.updater=Z||N}P.prototype.isReactComponent={},P.prototype.setState=function(M,U){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,U,"setState")},P.prototype.forceUpdate=function(M){this.updater.enqueueForceUpdate(this,M,"forceUpdate")};function R(){}R.prototype=P.prototype;function C(M,U,Z){this.props=M,this.context=U,this.refs=A,this.updater=Z||N}var E=C.prototype=new R;E.constructor=C,k(E,P.prototype),E.isPureReactComponent=!0;var D=Array.isArray;function I(){}var O={H:null,A:null,T:null,S:null},_=Object.prototype.hasOwnProperty;function z(M,U,Z){var ae=Z.ref;return{$$typeof:e,type:M,key:U,ref:ae!==void 0?ae:null,props:Z}}function H(M,U){return z(M.type,U,M.props)}function B(M){return typeof M=="object"&&M!==null&&M.$$typeof===e}function L(M){var U={"=":"=0",":":"=2"};return"$"+M.replace(/[=:]/g,function(Z){return U[Z]})}var F=/\/+/g;function $(M,U){return typeof M=="object"&&M!==null&&M.key!=null?L(""+M.key):U.toString(36)}function X(M){switch(M.status){case"fulfilled":return M.value;case"rejected":throw M.reason;default:switch(typeof M.status=="string"?M.then(I,I):(M.status="pending",M.then(function(U){M.status==="pending"&&(M.status="fulfilled",M.value=U)},function(U){M.status==="pending"&&(M.status="rejected",M.reason=U)})),M.status){case"fulfilled":return M.value;case"rejected":throw M.reason}}throw M}function K(M,U,Z,ae,ce){var W=typeof M;(W==="undefined"||W==="boolean")&&(M=null);var ue=!1;if(M===null)ue=!0;else switch(W){case"bigint":case"string":case"number":ue=!0;break;case"object":switch(M.$$typeof){case e:case t:ue=!0;break;case y:return ue=M._init,K(ue(M._payload),U,Z,ae,ce)}}if(ue)return ce=ce(M),ue=ae===""?"."+$(M,0):ae,D(ce)?(Z="",ue!=null&&(Z=ue.replace(F,"$&/")+"/"),K(ce,U,Z,"",function(he){return he})):ce!=null&&(B(ce)&&(ce=H(ce,Z+(ce.key==null||M&&M.key===ce.key?"":(""+ce.key).replace(F,"$&/")+"/")+ue)),U.push(ce)),1;ue=0;var ve=ae===""?".":ae+":";if(D(M))for(var ee=0;ee"u"?!1:e instanceof ShadowRoot||e instanceof Tn(e).ShadowRoot}function Qd(e){const{overflow:t,overflowX:a,overflowY:o,display:c}=yr(e);return/auto|scroll|overlay|hidden|clip/.test(t+o+a)&&c!=="inline"&&c!=="contents"}function nz(e){return/^(table|td|th)$/.test(Fs(e))}function Hm(e){try{if(e.matches(":popover-open"))return!0}catch{}try{return e.matches(":modal")}catch{return!1}}const sz=/transform|translate|scale|rotate|perspective|filter/,rz=/paint|layout|strict|content/,Ni=e=>!!e&&e!=="none";let Ev;function Av(e){const t=Rt(e)?yr(e):e;return Ni(t.transform)||Ni(t.translate)||Ni(t.scale)||Ni(t.rotate)||Ni(t.perspective)||!Rv()&&(Ni(t.backdropFilter)||Ni(t.filter))||sz.test(t.willChange||"")||rz.test(t.contain||"")}function az(e){let t=lo(e);for(;pn(t)&&!ao(t);){if(Av(t))return t;if(Hm(t))return null;t=lo(t)}return null}function Rv(){return Ev==null&&(Ev=typeof CSS<"u"&&CSS.supports&&CSS.supports("-webkit-backdrop-filter","none")),Ev}function ao(e){return/^(html|body|#document)$/.test(Fs(e))}function yr(e){return Tn(e).getComputedStyle(e)}function Um(e){return Rt(e)?{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}:{scrollLeft:e.scrollX,scrollTop:e.scrollY}}function lo(e){if(Fs(e)==="html")return e;const t=e.assignedSlot||e.parentNode||Gc(e)&&e.host||bl(e);return Gc(t)?t.host:t}function yC(e){const t=lo(e);return ao(t)?(e.ownerDocument||e).body:pn(t)&&Qd(t)?t:yC(t)}function Kc(e,t,a){var o;t===void 0&&(t=[]),a===void 0&&(a=!0);const c=yC(e),d=c===((o=e.ownerDocument)==null?void 0:o.body),f=Tn(c);if(d){const p=Ov(f);return t.concat(f,f.visualViewport||[],Qd(c)?c:[],p&&a?Kc(p):[])}else return t.concat(c,Kc(c,[],a))}function Ov(e){return e.parent&&Object.getPrototypeOf(e.parent)?e.frameElement:null}const Mv={...tz},jC={};function Ps(e,t){const a=x.useRef(jC);return a.current===jC&&(a.current=e(t)),a}const Dv=Mv.useInsertionEffect,lz=Dv&&Dv!==Mv.useLayoutEffect?Dv:e=>e();function Ie(e){const t=Ps(oz).current;return t.next=e,lz(t.effect),t.trampoline}function oz(){const e={next:void 0,callback:iz,trampoline:(...t)=>e.callback?.(...t),effect:()=>{e.callback=e.next}};return e}function iz(){}const Me=typeof document<"u"?x.useLayoutEffect:()=>{};function Iv(e,t){if(e&&!t)return e;if(!e&&t)return t;if(e||t)return{...e,...t}}const Pv={};function ta(e,t,a,o,c){if(!a&&!o&&!c&&!e)return Vm(t);let d=Vm(e);return t&&(d=Zd(d,t)),a&&(d=Zd(d,a)),o&&(d=Zd(d,o)),c&&(d=Zd(d,c)),d}function cz(e){if(e.length===0)return Pv;if(e.length===1)return Vm(e[0]);let t=Vm(e[0]);for(let a=1;a=65&&c<=90&&(typeof t=="function"||typeof t>"u")}function _v(e){return typeof e=="function"}function SC(e,t){return _v(e)?e(t):e??Pv}function fz(e,t){return t?e?(...a)=>{const o=a[0];if(CC(o)){const d=o;Fm(d);const f=t(...a);return d.baseUIHandlerPrevented||e?.(...a),f}const c=t(...a);return e?.(...a),c}:NC(t):e}function NC(e){return e&&((...t)=>{const a=t[0];return CC(a)&&Fm(a),e(...t)})}function Fm(e){return e.preventBaseUIHandler=()=>{e.baseUIHandlerPrevented=!0},e}function kC(e,t){return t?e?t+" "+e:t:e}function CC(e){return e!=null&&typeof e=="object"&&"nativeEvent"in e}function pz(e,t){return function(o,...c){const d=new URL(e);return d.searchParams.set("code",o.toString()),c.forEach(f=>d.searchParams.append("args[]",f)),`${t} error #${o}; visit ${d} for the full message.`}}const un=pz("https://base-ui.com/production-error","Base UI"),TC=x.createContext(void 0);function EC(e=!1){const t=x.useContext(TC);if(t===void 0&&!e)throw new Error(un(16));return t}function mz(e){const{focusableWhenDisabled:t,disabled:a,composite:o=!1,tabIndex:c=0,isNativeButton:d}=e,f=o&&t!==!1,p=o&&t===!1;return{props:x.useMemo(()=>{const b={onKeyDown(y){a&&t&&y.key!=="Tab"&&y.preventDefault()}};return o||(b.tabIndex=c,!d&&a&&(b.tabIndex=t?c:-1)),(d&&(t||f)||!d&&a)&&(b["aria-disabled"]=a),d&&(!t||p)&&(b.disabled=a),b},[o,a,t,f,p,d,c])}}function Gt(e){return e?.ownerDocument||document}function Yc(e,t,{detail:a=0}={}){e.dispatchEvent(new(Tn(e)).PointerEvent("click",{bubbles:!0,cancelable:!0,composed:!0,detail:a,shiftKey:t.shiftKey,ctrlKey:t.ctrlKey,altKey:t.altKey,metaKey:t.metaKey}))}function na(e={}){const{disabled:t=!1,focusableWhenDisabled:a,tabIndex:o=0,native:c=!0,composite:d}=e,f=x.useRef(null),p=EC(!0),h=d??p!==void 0,{props:b}=mz({focusableWhenDisabled:a,disabled:t,composite:h,tabIndex:o,isNativeButton:c}),y=x.useCallback(()=>{const w=f.current;Lv(w)&&h&&t&&b.disabled===void 0&&w.disabled&&(w.disabled=!1)},[t,b.disabled,h]);Me(y,[y]);const v=x.useCallback((w={})=>{const{onClick:S,onMouseDown:N,onKeyUp:k,onKeyDown:A,onPointerDown:P,...R}=w;return ta({onClick(C){if(t){C.preventDefault();return}S?.(C)},onMouseDown(C){t||N?.(C)},onKeyDown(C){if(t||(Fm(C),A?.(C),C.baseUIHandlerPrevented))return;const E=C.target===C.currentTarget,D=C.currentTarget,I=Lv(D),O=!c&&hz(D),_=E&&(c?I:!O),z=C.key==="Enter",H=C.key===" ",B=D.getAttribute("role"),L=B?.startsWith("menuitem")||B==="option"||B==="gridcell";if(E&&h&&H){if(C.defaultPrevented&&L)return;C.preventDefault(),(!c||I)&&(C.preventBaseUIHandler(),Yc(D,C));return}if(!_||c||!H&&!z){E&&O&&H&&C.preventDefault();return}C.defaultPrevented||(C.preventDefault(),z&&(C.preventBaseUIHandler(),Yc(D,C)))},onKeyUp(C){if(!t){if(Fm(C),k?.(C),C.target===C.currentTarget&&c&&h&&Lv(C.currentTarget)&&C.key===" "){C.preventDefault();return}C.baseUIHandlerPrevented||C.target===C.currentTarget&&!c&&!h&&!C.defaultPrevented&&C.key===" "&&(C.preventBaseUIHandler(),Yc(C.currentTarget,C))}},onPointerDown(C){if(t){C.preventDefault();return}P?.(C)}},c?{type:"button"}:{role:"button"},b,R)},[t,b,h,c]),j=Ie(w=>{f.current=w,y()});return{getButtonProps:v,buttonRef:j}}function Lv(e){return pn(e)&&e.tagName==="BUTTON"}function hz(e){return pn(e)&&e.tagName==="A"&&!!e.href}function sa(e,t,a,o){const c=Ps(AC).current;return xz(c,e,t,a,o)&&RC(c,[e,t,a,o]),c.callback}function gz(e){const t=Ps(AC).current;return bz(t,e)&&RC(t,e),t.callback}function AC(){return{callback:null,cleanup:null,refs:[]}}function xz(e,t,a,o,c){return e.refs[0]!==t||e.refs[1]!==a||e.refs[2]!==o||e.refs[3]!==c}function bz(e,t){return e.refs.length!==t.length||e.refs.some((a,o)=>a!==t[o])}function RC(e,t){if(e.refs=t,t.every(a=>a==null)){e.callback=null;return}e.callback=a=>{if(e.cleanup&&(e.cleanup(),e.cleanup=null),a!=null){const o=Array(t.length).fill(null);for(let c=0;c{for(let c=0;c=e}function OC(e){if(!x.isValidElement(e))return null;const t=e,a=t.props;return(zv(19)?a?.ref:t.ref)??null}function Kt(){}const Ir=Object.freeze([]),mn=Object.freeze({});function yz(e,t){const a={};for(const o in e){const c=e[o];if(t?.hasOwnProperty(o)){const d=t[o](c);d!=null&&Object.assign(a,d);continue}c===!0?a[`data-${o.toLowerCase()}`]="":c&&(a[`data-${o.toLowerCase()}`]=c.toString())}return a}function jz(e,t){return typeof e=="function"?e(t):e}function wz(e,t){return typeof e=="function"?e(t):e}function yt(e,t,a={}){let o=t.render;a.enabled!==!1&&(o=Cz(o));const c=Sz(t,a,o);if(a.enabled===!1)return null;const d=a.state??mn;return Tz(e,o,c,d)}function Sz(e,t,a){const{className:o,style:c}=e,{state:d=mn,ref:f,props:p,stateAttributesMapping:h,enabled:b=!0}=t,y=b?jz(o,d):void 0,v=b?wz(c,d):void 0,j=b?yz(d,h):mn,w=b&&p?Nz(p):void 0,S=b?Iv(j,w)??{}:mn;return typeof document<"u"&&(b?Array.isArray(f)?S.ref=gz([S.ref,OC(a),...f]):S.ref=sa(S.ref,OC(a),f):sa(null,null)),b?(y!==void 0&&(S.className=kC(S.className,y)),v!==void 0&&(S.style=Iv(S.style,v)),S):mn}function Nz(e){return Array.isArray(e)?cz(e):ta(void 0,e)}const kz=Symbol.for("react.lazy");function Cz(e){if(e?.$$typeof!==kz)return e;const t=x.Children.toArray(e)[0];return x.isValidElement(t)?t:e}function Tz(e,t,a,o){if(t){if(typeof t=="function")return t(a,o);const c=ta(a,t.props);return c.ref=a.ref,x.cloneElement(t,c)}if(e&&typeof e=="string")return Ez(e,a);throw new Error(un(8))}function Ez(e,t){return e==="button"?x.createElement("button",{type:"button",...t,key:t.key}):e==="img"?x.createElement("img",{alt:"",...t,key:t.key}):x.createElement(e,t)}const Az=x.forwardRef(function(t,a){const{render:o,className:c,disabled:d=!1,focusableWhenDisabled:f=!1,nativeButton:p=!0,style:h,...b}=t,{getButtonProps:y,buttonRef:v}=na({disabled:d,focusableWhenDisabled:f,native:p});return yt("button",t,{state:{disabled:d},ref:[a,v],props:[b,y]})});function MC(e){var t,a,o="";if(typeof e=="string"||typeof e=="number")o+=e;else if(typeof e=="object")if(Array.isArray(e)){var c=e.length;for(t=0;ttypeof e=="boolean"?`${e}`:e===0?"0":e,PC=DC,Xc=(e,t)=>a=>{var o;if(t?.variants==null)return PC(e,a?.class,a?.className);const{variants:c,defaultVariants:d}=t,f=Object.keys(c).map(b=>{const y=a?.[b],v=d?.[b];if(y===null)return null;const j=IC(y)||IC(v);return c[b][j]}),p=a&&Object.entries(a).reduce((b,y)=>{let[v,j]=y;return j===void 0||(b[v]=j),b},{}),h=t==null||(o=t.compoundVariants)===null||o===void 0?void 0:o.reduce((b,y)=>{let{class:v,className:j,...w}=y;return Object.entries(w).every(S=>{let[N,k]=S;return Array.isArray(k)?k.includes({...d,...p}[N]):{...d,...p}[N]===k})?[...b,v,j]:b},[]);return PC(e,f,h,a?.class,a?.className)},Rz=(e,t)=>{const a=new Array(e.length+t.length);for(let o=0;o({classGroupId:e,validator:t}),_C=(e=new Map,t=null,a)=>({nextPart:e,validators:t,classGroupId:a}),$m="-",LC=[],Mz="arbitrary..",Dz=e=>{const t=Pz(e),{conflictingClassGroups:a,conflictingClassGroupModifiers:o}=e;return{getClassGroupId:f=>{if(f.startsWith("[")&&f.endsWith("]"))return Iz(f);const p=f.split($m),h=p[0]===""&&p.length>1?1:0;return zC(p,h,t)},getConflictingClassGroupIds:(f,p)=>{if(p){const h=o[f],b=a[f];return h?b?Rz(b,h):h:b||LC}return a[f]||LC}}},zC=(e,t,a)=>{if(e.length-t===0)return a.classGroupId;const c=e[t],d=a.nextPart.get(c);if(d){const b=zC(e,t+1,d);if(b)return b}const f=a.validators;if(f===null)return;const p=t===0?e.join($m):e.slice(t).join($m),h=f.length;for(let b=0;be.slice(1,-1).indexOf(":")===-1?void 0:(()=>{const t=e.slice(1,-1),a=t.indexOf(":"),o=t.slice(0,a);return o?Mz+o:void 0})(),Pz=e=>{const{theme:t,classGroups:a}=e;return _z(a,t)},_z=(e,t)=>{const a=_C();for(const o in e){const c=e[o];Bv(c,a,o,t)}return a},Bv=(e,t,a,o)=>{const c=e.length;for(let d=0;d{if(typeof e=="string"){zz(e,t,a);return}if(typeof e=="function"){Bz(e,t,a,o);return}Hz(e,t,a,o)},zz=(e,t,a)=>{const o=e===""?t:BC(t,e);o.classGroupId=a},Bz=(e,t,a,o)=>{if(Uz(e)){Bv(e(o),t,a,o);return}t.validators===null&&(t.validators=[]),t.validators.push(Oz(a,e))},Hz=(e,t,a,o)=>{const c=Object.entries(e),d=c.length;for(let f=0;f{let a=e;const o=t.split($m),c=o.length;for(let d=0;d"isThemeGetter"in e&&e.isThemeGetter===!0,Vz=e=>{if(e<1)return{get:()=>{},set:()=>{}};let t=0,a=Object.create(null),o=Object.create(null);const c=(d,f)=>{a[d]=f,t++,t>e&&(t=0,o=a,a=Object.create(null))};return{get(d){let f=a[d];if(f!==void 0)return f;if((f=o[d])!==void 0)return c(d,f),f},set(d,f){d in a?a[d]=f:c(d,f)}}},Hv="!",HC=":",Fz=[],UC=(e,t,a,o,c)=>({modifiers:e,hasImportantModifier:t,baseClassName:a,maybePostfixModifierPosition:o,isExternal:c}),$z=e=>{const{prefix:t,experimentalParseClassName:a}=e;let o=c=>{const d=[];let f=0,p=0,h=0,b;const y=c.length;for(let N=0;Nh?b-h:void 0;return UC(d,w,j,S)};if(t){const c=t+HC,d=o;o=f=>f.startsWith(c)?d(f.slice(c.length)):UC(Fz,!1,f,void 0,!0)}if(a){const c=o;o=d=>a({className:d,parseClassName:c})}return o},qz=e=>{const t=new Map;return e.orderSensitiveModifiers.forEach((a,o)=>{t.set(a,1e6+o)}),a=>{const o=[];let c=[];for(let d=0;d0&&(c.sort(),o.push(...c),c=[]),o.push(f)):c.push(f)}return c.length>0&&(c.sort(),o.push(...c)),o}},Gz=e=>({cache:Vz(e.cacheSize),parseClassName:$z(e),sortModifiers:qz(e),postfixLookupClassGroupIds:Kz(e),...Dz(e)}),Kz=e=>{const t=Object.create(null),a=e.postfixLookupClassGroups;if(a)for(let o=0;o{const{parseClassName:a,getClassGroupId:o,getConflictingClassGroupIds:c,sortModifiers:d,postfixLookupClassGroupIds:f}=t,p=[],h=e.trim().split(Yz);let b="";for(let y=h.length-1;y>=0;y-=1){const v=h[y],{isExternal:j,modifiers:w,hasImportantModifier:S,baseClassName:N,maybePostfixModifierPosition:k}=a(v);if(j){b=v+(b.length>0?" "+b:b);continue}let A=!!k,P;if(A){const I=N.substring(0,k);P=o(I);const O=P&&f[P]?o(N):void 0;O&&O!==P&&(P=O,A=!1)}else P=o(N);if(!P){if(!A){b=v+(b.length>0?" "+b:b);continue}if(P=o(N),!P){b=v+(b.length>0?" "+b:b);continue}A=!1}const R=w.length===0?"":w.length===1?w[0]:d(w).join(":"),C=S?R+Hv:R,E=C+P;if(p.indexOf(E)>-1)continue;p.push(E);const D=c(P,A);for(let I=0;I0?" "+b:b)}return b},Qz=(...e)=>{let t=0,a,o,c="";for(;t{if(typeof e=="string")return e;let t,a="";for(let o=0;o{let a,o,c,d;const f=h=>{const b=t.reduce((y,v)=>v(y),e());return a=Gz(b),o=a.cache.get,c=a.cache.set,d=p,p(h)},p=h=>{const b=o(h);if(b)return b;const y=Xz(h,a);return c(h,y),y};return d=f,(...h)=>d(Qz(...h))},Wz=[],ks=e=>{const t=a=>a[e]||Wz;return t.isThemeGetter=!0,t.themeKey=e,t},FC=/^\[(?:(\w[\w-]*):)?(.+)\]$/i,$C=/^\((?:(\w[\w-]*):)?(.+)\)$/i,Jz=/^\d+(?:\.\d+)?\/\d+(?:\.\d+)?$/,eB=/^(\d+(\.\d+)?)?(xs|sm|md|lg|xl)$/,tB=/\d+(%|px|r?em|[sdl]?v([hwib]|min|max)|pt|pc|in|cm|mm|cap|ch|ex|r?lh|cq(w|h|i|b|min|max))|\b(calc|min|max|clamp)\(.+\)|^0$/,nB=/^(rgba?|hsla?|hwb|(ok)?(lab|lch)|color-mix|color|light-dark)\(.+\)$/,sB=/^(inset_)?-?((\d+)?\.?(\d+)[a-z]+|0)_-?((\d+)?\.?(\d+)[a-z]+|0)/,rB=/^(url|image|image-set|cross-fade|element|(repeating-)?(linear|radial|conic)-gradient)\(.+\)$/,oo=e=>Jz.test(e),Nt=e=>!!e&&!Number.isNaN(Number(e)),qa=e=>!!e&&Number.isInteger(Number(e)),Uv=e=>e.endsWith("%")&&Nt(e.slice(0,-1)),vl=e=>eB.test(e),qC=()=>!0,aB=e=>tB.test(e)&&!nB.test(e),Vv=()=>!1,lB=e=>sB.test(e),oB=e=>rB.test(e),iB=e=>!Ye(e)&&!Qe(e),cB=e=>e.startsWith("@container")&&(e[10]==="/"&&e[11]!==void 0||e[11]==="s"&&e[16]!==void 0&&e.startsWith("-size/",10)||e[11]==="n"&&e[18]!==void 0&&e.startsWith("-normal/",10)),uB=e=>io(e,ZC,Vv),Ye=e=>FC.test(e),ki=e=>io(e,WC,aB),GC=e=>io(e,bB,Nt),dB=e=>io(e,e1,qC),fB=e=>io(e,JC,Vv),KC=e=>io(e,XC,Vv),pB=e=>io(e,QC,oB),qm=e=>io(e,t1,lB),Qe=e=>$C.test(e),Wd=e=>Ci(e,WC),mB=e=>Ci(e,JC),YC=e=>Ci(e,XC),hB=e=>Ci(e,ZC),gB=e=>Ci(e,QC),Gm=e=>Ci(e,t1,!0),xB=e=>Ci(e,e1,!0),io=(e,t,a)=>{const o=FC.exec(e);return o?o[1]?t(o[1]):a(o[2]):!1},Ci=(e,t,a=!1)=>{const o=$C.exec(e);return o?o[1]?t(o[1]):a:!1},XC=e=>e==="position"||e==="percentage",QC=e=>e==="image"||e==="url",ZC=e=>e==="length"||e==="size"||e==="bg-size",WC=e=>e==="length",bB=e=>e==="number",JC=e=>e==="family-name",e1=e=>e==="number"||e==="weight",t1=e=>e==="shadow",vB=Zz(()=>{const e=ks("color"),t=ks("font"),a=ks("text"),o=ks("font-weight"),c=ks("tracking"),d=ks("leading"),f=ks("breakpoint"),p=ks("container"),h=ks("spacing"),b=ks("radius"),y=ks("shadow"),v=ks("inset-shadow"),j=ks("text-shadow"),w=ks("drop-shadow"),S=ks("blur"),N=ks("perspective"),k=ks("aspect"),A=ks("ease"),P=ks("animate"),R=()=>["auto","avoid","all","avoid-page","page","left","right","column"],C=()=>["center","top","bottom","left","right","top-left","left-top","top-right","right-top","bottom-right","right-bottom","bottom-left","left-bottom"],E=()=>[...C(),Qe,Ye],D=()=>["auto","hidden","clip","visible","scroll"],I=()=>["auto","contain","none"],O=()=>[Qe,Ye,h],_=()=>[oo,"full","auto",...O()],z=()=>[qa,"none","subgrid",Qe,Ye],H=()=>["auto",{span:["full",qa,Qe,Ye]},qa,Qe,Ye],B=()=>[qa,"auto",Qe,Ye],L=()=>["auto","min","max","fr",Qe,Ye],F=()=>["start","end","center","between","around","evenly","stretch","baseline","center-safe","end-safe"],$=()=>["start","end","center","stretch","center-safe","end-safe"],X=()=>["auto",...O()],K=()=>[oo,"auto","full","dvw","dvh","lvw","lvh","svw","svh","min","max","fit",...O()],q=()=>[p,oo,"screen","full","dvw","lvw","svw","min","max","fit",...O()],Q=()=>[oo,"screen","full","lh","dvh","lvh","svh","min","max","fit",...O()],G=()=>[e,Qe,Ye],J=()=>[...C(),YC,KC,{position:[Qe,Ye]}],Y=()=>["no-repeat",{repeat:["","x","y","space","round"]}],re=()=>["auto","cover","contain",hB,uB,{size:[Qe,Ye]}],M=()=>[Uv,Wd,ki],U=()=>["","none","full",b,Qe,Ye],Z=()=>["",Nt,Wd,ki],ae=()=>["solid","dashed","dotted","double"],ce=()=>["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"],W=()=>[Nt,Uv,YC,KC],ue=()=>["","none",S,Qe,Ye],ve=()=>["none",Nt,Qe,Ye],ee=()=>["none",Nt,Qe,Ye],he=()=>[Nt,Qe,Ye],te=()=>[oo,"full",...O()];return{cacheSize:500,theme:{animate:["spin","ping","pulse","bounce"],aspect:["video"],blur:[vl],breakpoint:[vl],color:[qC],container:[vl],"drop-shadow":[vl],ease:["in","out","in-out"],font:[iB],"font-weight":["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"],"inset-shadow":[vl],leading:["none","tight","snug","normal","relaxed","loose"],perspective:["dramatic","near","normal","midrange","distant","none"],radius:[vl],shadow:[vl],spacing:["px",Nt],text:[vl],"text-shadow":[vl],tracking:["tighter","tight","normal","wide","wider","widest"]},classGroups:{aspect:[{aspect:["auto","square",oo,Ye,Qe,k]}],container:["container"],"container-type":[{"@container":["","normal","size",Qe,Ye]}],"container-named":[cB],columns:[{columns:[Nt,"auto",Ye,Qe,p]}],"break-after":[{"break-after":R()}],"break-before":[{"break-before":R()}],"break-inside":[{"break-inside":["auto","avoid","avoid-page","avoid-column"]}],"box-decoration":[{"box-decoration":["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],sr:["sr-only","not-sr-only"],float:[{float:["right","left","none","start","end"]}],clear:[{clear:["left","right","both","none","start","end"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:E()}],overflow:[{overflow:D()}],"overflow-x":[{"overflow-x":D()}],"overflow-y":[{"overflow-y":D()}],overscroll:[{overscroll:I()}],"overscroll-x":[{"overscroll-x":I()}],"overscroll-y":[{"overscroll-y":I()}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:_()}],"inset-x":[{"inset-x":_()}],"inset-y":[{"inset-y":_()}],start:[{"inset-s":_(),start:_()}],end:[{"inset-e":_(),end:_()}],"inset-bs":[{"inset-bs":_()}],"inset-be":[{"inset-be":_()}],top:[{top:_()}],right:[{right:_()}],bottom:[{bottom:_()}],left:[{left:_()}],visibility:["visible","invisible","collapse"],z:[{z:[qa,"auto",Qe,Ye]}],basis:[{basis:[oo,"full","auto",p,...O()]}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["nowrap","wrap","wrap-reverse"]}],flex:[{flex:[Nt,oo,"auto","initial","none",Ye]}],grow:[{grow:["",Nt,Qe,Ye]}],shrink:[{shrink:["",Nt,Qe,Ye]}],order:[{order:[qa,"first","last","none",Qe,Ye]}],"grid-cols":[{"grid-cols":z()}],"col-start-end":[{col:H()}],"col-start":[{"col-start":B()}],"col-end":[{"col-end":B()}],"grid-rows":[{"grid-rows":z()}],"row-start-end":[{row:H()}],"row-start":[{"row-start":B()}],"row-end":[{"row-end":B()}],"grid-flow":[{"grid-flow":["row","col","dense","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":L()}],"auto-rows":[{"auto-rows":L()}],gap:[{gap:O()}],"gap-x":[{"gap-x":O()}],"gap-y":[{"gap-y":O()}],"justify-content":[{justify:[...F(),"normal"]}],"justify-items":[{"justify-items":[...$(),"normal"]}],"justify-self":[{"justify-self":["auto",...$()]}],"align-content":[{content:["normal",...F()]}],"align-items":[{items:[...$(),{baseline:["","last"]}]}],"align-self":[{self:["auto",...$(),{baseline:["","last"]}]}],"place-content":[{"place-content":F()}],"place-items":[{"place-items":[...$(),"baseline"]}],"place-self":[{"place-self":["auto",...$()]}],p:[{p:O()}],px:[{px:O()}],py:[{py:O()}],ps:[{ps:O()}],pe:[{pe:O()}],pbs:[{pbs:O()}],pbe:[{pbe:O()}],pt:[{pt:O()}],pr:[{pr:O()}],pb:[{pb:O()}],pl:[{pl:O()}],m:[{m:X()}],mx:[{mx:X()}],my:[{my:X()}],ms:[{ms:X()}],me:[{me:X()}],mbs:[{mbs:X()}],mbe:[{mbe:X()}],mt:[{mt:X()}],mr:[{mr:X()}],mb:[{mb:X()}],ml:[{ml:X()}],"space-x":[{"space-x":O()}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":O()}],"space-y-reverse":["space-y-reverse"],size:[{size:K()}],"inline-size":[{inline:["auto",...q()]}],"min-inline-size":[{"min-inline":["auto",...q()]}],"max-inline-size":[{"max-inline":["none",...q()]}],"block-size":[{block:["auto",...Q()]}],"min-block-size":[{"min-block":["auto",...Q()]}],"max-block-size":[{"max-block":["none",...Q()]}],w:[{w:[p,"screen",...K()]}],"min-w":[{"min-w":[p,"screen","none",...K()]}],"max-w":[{"max-w":[p,"screen","none","prose",{screen:[f]},...K()]}],h:[{h:["screen","lh",...K()]}],"min-h":[{"min-h":["screen","lh","none",...K()]}],"max-h":[{"max-h":["screen","lh","none",...K()]}],"font-size":[{text:["base",a,Wd,ki]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:[o,xB,dB]}],"font-stretch":[{"font-stretch":["ultra-condensed","extra-condensed","condensed","semi-condensed","normal","semi-expanded","expanded","extra-expanded","ultra-expanded",Uv,Ye]}],"font-family":[{font:[mB,fB,t]}],"font-features":[{"font-features":[Ye]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractions"],tracking:[{tracking:[c,Qe,Ye]}],"line-clamp":[{"line-clamp":[Nt,"none",Qe,GC]}],leading:[{leading:["none",d,...O()]}],"list-image":[{"list-image":["none",Qe,Ye]}],"list-style-position":[{list:["inside","outside"]}],"list-style-type":[{list:["disc","decimal","none",Qe,Ye]}],"text-alignment":[{text:["left","center","right","justify","start","end"]}],"placeholder-color":[{placeholder:G()}],"text-color":[{text:G()}],"text-decoration":["underline","overline","line-through","no-underline"],"text-decoration-style":[{decoration:[...ae(),"wavy"]}],"text-decoration-thickness":[{decoration:[Nt,"from-font","auto",Qe,ki]}],"text-decoration-color":[{decoration:G()}],"underline-offset":[{"underline-offset":[Nt,"auto",Qe,Ye]}],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","text-ellipsis","text-clip"],"text-wrap":[{text:["wrap","nowrap","balance","pretty"]}],indent:[{indent:O()}],"tab-size":[{tab:[qa,Qe,Ye]}],"vertical-align":[{align:["baseline","top","middle","bottom","text-top","text-bottom","sub","super",Qe,Ye]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap","break-spaces"]}],break:[{break:["normal","words","all","keep"]}],wrap:[{wrap:["break-word","anywhere","normal"]}],hyphens:[{hyphens:["none","manual","auto"]}],content:[{content:["none",Qe,Ye]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:J()}],"bg-repeat":[{bg:Y()}],"bg-size":[{bg:re()}],"bg-image":[{bg:["none",{linear:[{to:["t","tr","r","br","b","bl","l","tl"]},qa,Qe,Ye],radial:["",Qe,Ye],conic:["",qa,Qe,Ye]},gB,pB]}],"bg-color":[{bg:G()}],"gradient-from-pos":[{from:M()}],"gradient-via-pos":[{via:M()}],"gradient-to-pos":[{to:M()}],"gradient-from":[{from:G()}],"gradient-via":[{via:G()}],"gradient-to":[{to:G()}],rounded:[{rounded:U()}],"rounded-s":[{"rounded-s":U()}],"rounded-e":[{"rounded-e":U()}],"rounded-t":[{"rounded-t":U()}],"rounded-r":[{"rounded-r":U()}],"rounded-b":[{"rounded-b":U()}],"rounded-l":[{"rounded-l":U()}],"rounded-ss":[{"rounded-ss":U()}],"rounded-se":[{"rounded-se":U()}],"rounded-ee":[{"rounded-ee":U()}],"rounded-es":[{"rounded-es":U()}],"rounded-tl":[{"rounded-tl":U()}],"rounded-tr":[{"rounded-tr":U()}],"rounded-br":[{"rounded-br":U()}],"rounded-bl":[{"rounded-bl":U()}],"border-w":[{border:Z()}],"border-w-x":[{"border-x":Z()}],"border-w-y":[{"border-y":Z()}],"border-w-s":[{"border-s":Z()}],"border-w-e":[{"border-e":Z()}],"border-w-bs":[{"border-bs":Z()}],"border-w-be":[{"border-be":Z()}],"border-w-t":[{"border-t":Z()}],"border-w-r":[{"border-r":Z()}],"border-w-b":[{"border-b":Z()}],"border-w-l":[{"border-l":Z()}],"divide-x":[{"divide-x":Z()}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":Z()}],"divide-y-reverse":["divide-y-reverse"],"border-style":[{border:[...ae(),"hidden","none"]}],"divide-style":[{divide:[...ae(),"hidden","none"]}],"border-color":[{border:G()}],"border-color-x":[{"border-x":G()}],"border-color-y":[{"border-y":G()}],"border-color-s":[{"border-s":G()}],"border-color-e":[{"border-e":G()}],"border-color-bs":[{"border-bs":G()}],"border-color-be":[{"border-be":G()}],"border-color-t":[{"border-t":G()}],"border-color-r":[{"border-r":G()}],"border-color-b":[{"border-b":G()}],"border-color-l":[{"border-l":G()}],"divide-color":[{divide:G()}],"outline-style":[{outline:[...ae(),"none","hidden"]}],"outline-offset":[{"outline-offset":[Nt,Qe,Ye]}],"outline-w":[{outline:["",Nt,Wd,ki]}],"outline-color":[{outline:G()}],shadow:[{shadow:["","inner","none",y,Gm,qm]}],"shadow-color":[{shadow:G()}],"inset-shadow":[{"inset-shadow":["none",v,Gm,qm]}],"inset-shadow-color":[{"inset-shadow":G()}],"ring-w":[{ring:Z()}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:G()}],"ring-offset-w":[{"ring-offset":[Nt,ki]}],"ring-offset-color":[{"ring-offset":G()}],"inset-ring-w":[{"inset-ring":Z()}],"inset-ring-color":[{"inset-ring":G()}],"text-shadow":[{"text-shadow":["none",j,Gm,qm]}],"text-shadow-color":[{"text-shadow":G()}],opacity:[{opacity:[Nt,Qe,Ye]}],"mix-blend":[{"mix-blend":[...ce(),"plus-darker","plus-lighter"]}],"bg-blend":[{"bg-blend":ce()}],"mask-clip":[{"mask-clip":["border","padding","content","fill","stroke","view"]},"mask-no-clip"],"mask-composite":[{mask:["add","subtract","intersect","exclude"]}],"mask-image-linear-pos":[{"mask-linear":[Nt]}],"mask-image-linear-from-pos":[{"mask-linear-from":W()}],"mask-image-linear-to-pos":[{"mask-linear-to":W()}],"mask-image-linear-from-color":[{"mask-linear-from":G()}],"mask-image-linear-to-color":[{"mask-linear-to":G()}],"mask-image-t-from-pos":[{"mask-t-from":W()}],"mask-image-t-to-pos":[{"mask-t-to":W()}],"mask-image-t-from-color":[{"mask-t-from":G()}],"mask-image-t-to-color":[{"mask-t-to":G()}],"mask-image-r-from-pos":[{"mask-r-from":W()}],"mask-image-r-to-pos":[{"mask-r-to":W()}],"mask-image-r-from-color":[{"mask-r-from":G()}],"mask-image-r-to-color":[{"mask-r-to":G()}],"mask-image-b-from-pos":[{"mask-b-from":W()}],"mask-image-b-to-pos":[{"mask-b-to":W()}],"mask-image-b-from-color":[{"mask-b-from":G()}],"mask-image-b-to-color":[{"mask-b-to":G()}],"mask-image-l-from-pos":[{"mask-l-from":W()}],"mask-image-l-to-pos":[{"mask-l-to":W()}],"mask-image-l-from-color":[{"mask-l-from":G()}],"mask-image-l-to-color":[{"mask-l-to":G()}],"mask-image-x-from-pos":[{"mask-x-from":W()}],"mask-image-x-to-pos":[{"mask-x-to":W()}],"mask-image-x-from-color":[{"mask-x-from":G()}],"mask-image-x-to-color":[{"mask-x-to":G()}],"mask-image-y-from-pos":[{"mask-y-from":W()}],"mask-image-y-to-pos":[{"mask-y-to":W()}],"mask-image-y-from-color":[{"mask-y-from":G()}],"mask-image-y-to-color":[{"mask-y-to":G()}],"mask-image-radial":[{"mask-radial":[Qe,Ye]}],"mask-image-radial-from-pos":[{"mask-radial-from":W()}],"mask-image-radial-to-pos":[{"mask-radial-to":W()}],"mask-image-radial-from-color":[{"mask-radial-from":G()}],"mask-image-radial-to-color":[{"mask-radial-to":G()}],"mask-image-radial-shape":[{"mask-radial":["circle","ellipse"]}],"mask-image-radial-size":[{"mask-radial":[{closest:["side","corner"],farthest:["side","corner"]}]}],"mask-image-radial-pos":[{"mask-radial-at":C()}],"mask-image-conic-pos":[{"mask-conic":[Nt]}],"mask-image-conic-from-pos":[{"mask-conic-from":W()}],"mask-image-conic-to-pos":[{"mask-conic-to":W()}],"mask-image-conic-from-color":[{"mask-conic-from":G()}],"mask-image-conic-to-color":[{"mask-conic-to":G()}],"mask-mode":[{mask:["alpha","luminance","match"]}],"mask-origin":[{"mask-origin":["border","padding","content","fill","stroke","view"]}],"mask-position":[{mask:J()}],"mask-repeat":[{mask:Y()}],"mask-size":[{mask:re()}],"mask-type":[{"mask-type":["alpha","luminance"]}],"mask-image":[{mask:["none",Qe,Ye]}],filter:[{filter:["","none",Qe,Ye]}],blur:[{blur:ue()}],brightness:[{brightness:[Nt,Qe,Ye]}],contrast:[{contrast:[Nt,Qe,Ye]}],"drop-shadow":[{"drop-shadow":["","none",w,Gm,qm]}],"drop-shadow-color":[{"drop-shadow":G()}],grayscale:[{grayscale:["",Nt,Qe,Ye]}],"hue-rotate":[{"hue-rotate":[Nt,Qe,Ye]}],invert:[{invert:["",Nt,Qe,Ye]}],saturate:[{saturate:[Nt,Qe,Ye]}],sepia:[{sepia:["",Nt,Qe,Ye]}],"backdrop-filter":[{"backdrop-filter":["","none",Qe,Ye]}],"backdrop-blur":[{"backdrop-blur":ue()}],"backdrop-brightness":[{"backdrop-brightness":[Nt,Qe,Ye]}],"backdrop-contrast":[{"backdrop-contrast":[Nt,Qe,Ye]}],"backdrop-grayscale":[{"backdrop-grayscale":["",Nt,Qe,Ye]}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":[Nt,Qe,Ye]}],"backdrop-invert":[{"backdrop-invert":["",Nt,Qe,Ye]}],"backdrop-opacity":[{"backdrop-opacity":[Nt,Qe,Ye]}],"backdrop-saturate":[{"backdrop-saturate":[Nt,Qe,Ye]}],"backdrop-sepia":[{"backdrop-sepia":["",Nt,Qe,Ye]}],"border-collapse":[{border:["collapse","separate"]}],"border-spacing":[{"border-spacing":O()}],"border-spacing-x":[{"border-spacing-x":O()}],"border-spacing-y":[{"border-spacing-y":O()}],"table-layout":[{table:["auto","fixed"]}],caption:[{caption:["top","bottom"]}],transition:[{transition:["","all","colors","opacity","shadow","transform","none",Qe,Ye]}],"transition-behavior":[{transition:["normal","discrete"]}],duration:[{duration:[Nt,"initial",Qe,Ye]}],ease:[{ease:["linear","initial",A,Qe,Ye]}],delay:[{delay:[Nt,Qe,Ye]}],animate:[{animate:["none",P,Qe,Ye]}],backface:[{backface:["hidden","visible"]}],perspective:[{perspective:[N,Qe,Ye]}],"perspective-origin":[{"perspective-origin":E()}],rotate:[{rotate:ve()}],"rotate-x":[{"rotate-x":ve()}],"rotate-y":[{"rotate-y":ve()}],"rotate-z":[{"rotate-z":ve()}],scale:[{scale:ee()}],"scale-x":[{"scale-x":ee()}],"scale-y":[{"scale-y":ee()}],"scale-z":[{"scale-z":ee()}],"scale-3d":["scale-3d"],skew:[{skew:he()}],"skew-x":[{"skew-x":he()}],"skew-y":[{"skew-y":he()}],transform:[{transform:[Qe,Ye,"","none","gpu","cpu"]}],"transform-origin":[{origin:E()}],"transform-style":[{transform:["3d","flat"]}],translate:[{translate:te()}],"translate-x":[{"translate-x":te()}],"translate-y":[{"translate-y":te()}],"translate-z":[{"translate-z":te()}],"translate-none":["translate-none"],zoom:[{zoom:[qa,Qe,Ye]}],accent:[{accent:G()}],appearance:[{appearance:["none","auto"]}],"caret-color":[{caret:G()}],"color-scheme":[{scheme:["normal","dark","light","light-dark","only-dark","only-light"]}],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed","none","context-menu","progress","cell","crosshair","vertical-text","alias","copy","no-drop","grab","grabbing","all-scroll","col-resize","row-resize","n-resize","e-resize","s-resize","w-resize","ne-resize","nw-resize","se-resize","sw-resize","ew-resize","ns-resize","nesw-resize","nwse-resize","zoom-in","zoom-out",Qe,Ye]}],"field-sizing":[{"field-sizing":["fixed","content"]}],"pointer-events":[{"pointer-events":["auto","none"]}],resize:[{resize:["none","","y","x"]}],"scroll-behavior":[{scroll:["auto","smooth"]}],"scrollbar-thumb-color":[{"scrollbar-thumb":G()}],"scrollbar-track-color":[{"scrollbar-track":G()}],"scrollbar-gutter":[{"scrollbar-gutter":["auto","stable","both"]}],"scrollbar-w":[{scrollbar:["auto","thin","none"]}],"scroll-m":[{"scroll-m":O()}],"scroll-mx":[{"scroll-mx":O()}],"scroll-my":[{"scroll-my":O()}],"scroll-ms":[{"scroll-ms":O()}],"scroll-me":[{"scroll-me":O()}],"scroll-mbs":[{"scroll-mbs":O()}],"scroll-mbe":[{"scroll-mbe":O()}],"scroll-mt":[{"scroll-mt":O()}],"scroll-mr":[{"scroll-mr":O()}],"scroll-mb":[{"scroll-mb":O()}],"scroll-ml":[{"scroll-ml":O()}],"scroll-p":[{"scroll-p":O()}],"scroll-px":[{"scroll-px":O()}],"scroll-py":[{"scroll-py":O()}],"scroll-ps":[{"scroll-ps":O()}],"scroll-pe":[{"scroll-pe":O()}],"scroll-pbs":[{"scroll-pbs":O()}],"scroll-pbe":[{"scroll-pbe":O()}],"scroll-pt":[{"scroll-pt":O()}],"scroll-pr":[{"scroll-pr":O()}],"scroll-pb":[{"scroll-pb":O()}],"scroll-pl":[{"scroll-pl":O()}],"snap-align":[{snap:["start","end","center","align-none"]}],"snap-stop":[{snap:["normal","always"]}],"snap-type":[{snap:["none","x","y","both"]}],"snap-strictness":[{snap:["mandatory","proximity"]}],touch:[{touch:["auto","none","manipulation"]}],"touch-x":[{"touch-pan":["x","left","right"]}],"touch-y":[{"touch-pan":["y","up","down"]}],"touch-pz":["touch-pinch-zoom"],select:[{select:["none","text","all","auto"]}],"will-change":[{"will-change":["auto","scroll","contents","transform",Qe,Ye]}],fill:[{fill:["none",...G()]}],"stroke-w":[{stroke:[Nt,Wd,ki,GC]}],stroke:[{stroke:["none",...G()]}],"forced-color-adjust":[{"forced-color-adjust":["auto","none"]}]},conflictingClassGroups:{"container-named":["container-type"],overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","inset-bs","inset-be","start","end","top","right","bottom","left"],"inset-x":["start","end","right","left"],"inset-y":["inset-bs","inset-be","top","bottom"],flex:["basis","grow","shrink"],gap:["gap-x","gap-y"],p:["px","py","ps","pe","pbs","pbe","pt","pr","pb","pl"],px:["ps","pe","pr","pl"],py:["pbs","pbe","pt","pb"],m:["mx","my","ms","me","mbs","mbe","mt","mr","mb","ml"],mx:["ms","me","mr","ml"],my:["mbs","mbe","mt","mb"],size:["w","h"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],"line-clamp":["display","overflow"],rounded:["rounded-s","rounded-e","rounded-t","rounded-r","rounded-b","rounded-l","rounded-ss","rounded-se","rounded-ee","rounded-es","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-s":["rounded-ss","rounded-es"],"rounded-e":["rounded-se","rounded-ee"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-spacing":["border-spacing-x","border-spacing-y"],"border-w":["border-w-x","border-w-y","border-w-s","border-w-e","border-w-bs","border-w-be","border-w-t","border-w-r","border-w-b","border-w-l"],"border-w-x":["border-w-s","border-w-e","border-w-r","border-w-l"],"border-w-y":["border-w-bs","border-w-be","border-w-t","border-w-b"],"border-color":["border-color-x","border-color-y","border-color-s","border-color-e","border-color-bs","border-color-be","border-color-t","border-color-r","border-color-b","border-color-l"],"border-color-x":["border-color-s","border-color-e","border-color-r","border-color-l"],"border-color-y":["border-color-bs","border-color-be","border-color-t","border-color-b"],translate:["translate-x","translate-y","translate-none"],"translate-none":["translate","translate-x","translate-y","translate-z"],"scroll-m":["scroll-mx","scroll-my","scroll-ms","scroll-me","scroll-mbs","scroll-mbe","scroll-mt","scroll-mr","scroll-mb","scroll-ml"],"scroll-mx":["scroll-ms","scroll-me","scroll-mr","scroll-ml"],"scroll-my":["scroll-mbs","scroll-mbe","scroll-mt","scroll-mb"],"scroll-p":["scroll-px","scroll-py","scroll-ps","scroll-pe","scroll-pbs","scroll-pbe","scroll-pt","scroll-pr","scroll-pb","scroll-pl"],"scroll-px":["scroll-ps","scroll-pe","scroll-pr","scroll-pl"],"scroll-py":["scroll-pbs","scroll-pbe","scroll-pt","scroll-pb"],touch:["touch-x","touch-y","touch-pz"],"touch-x":["touch"],"touch-y":["touch"],"touch-pz":["touch"]},conflictingClassGroupModifiers:{"font-size":["leading"]},postfixLookupClassGroups:["container-type"],orderSensitiveModifiers:["*","**","after","backdrop","before","details-content","file","first-letter","first-line","marker","placeholder","selection"]}});function Ue(...e){return vB(DC(e))}const yB=Xc(["group/button inline-flex shrink-0 items-center justify-center gap-1.5 whitespace-nowrap border","font-medium transition-[background-color,border-color,color,transform,box-shadow] duration-150 ease-out","outline-none select-none active:translate-y-[0.5px]","disabled:pointer-events-none disabled:opacity-45 disabled:shadow-none","[&_svg]:pointer-events-none [&_svg]:shrink-0"],{variants:{variant:{default:"border-primary bg-primary font-semibold text-primary-on shadow-glow hover:border-primary-hover hover:bg-primary-hover",outline:"border-control-border bg-control text-foreground shadow-1 hover:border-control-border-hover hover:bg-control-hover",secondary:"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",ghost:"border-transparent bg-transparent text-subtle shadow-none hover:bg-control-hover hover:text-foreground",destructive:"border-destructive-bd bg-destructive-soft text-destructive hover:bg-destructive-hover",dashed:"border-dashed border-border-strong bg-transparent text-muted-foreground shadow-none hover:border-primary hover:text-primary-fg",link:"border-transparent bg-transparent text-primary shadow-none underline-offset-4 hover:underline"},size:{default:"h-[30px] rounded-md px-3 text-[12.5px]","2xs":"h-4 rounded-pill px-1.5 text-[10px]",xs:"h-6 rounded-sm px-2 text-[11.5px]",sm:"h-[26px] rounded-sm px-2.5 text-[12px]",lg:"h-9 rounded-md px-4 text-[13px]",icon:"h-[30px] w-[30px] rounded-md p-0","icon-2xs":"size-4 rounded-pill p-0","icon-xs":"size-6 rounded-sm p-0","icon-sm":"h-[26px] w-[26px] rounded-sm p-0","icon-lg":"size-9 rounded-md p-0"}},defaultVariants:{variant:"default",size:"default"}});function ne({className:e,variant:t,size:a,...o}){return s.jsx(Az,{className:Ue(yB({variant:t,size:a}),e),...o})}const jB="data-valid",wB="data-invalid",SB={badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:null,valueMissing:!1},n1={valid:null,touched:!1,dirty:!1,filled:!1,focused:!1},NB={disabled:!1,...n1},Ti={valid(e){return e===null?null:e?{[jB]:""}:{[wB]:""}}},s1={invalid:void 0,name:void 0,validityData:{state:SB,errors:[],error:"",value:"",initialValue:null},setValidityData:Kt,disabled:void 0,setTouched:Kt,setDirty:Kt,setFilled:Kt,setFocused:Kt,validationMode:"onSubmit",shouldValidateOnChange:()=>!1,state:NB,registerFieldControl:Kt,validation:{getValidationProps:(e,t=mn)=>t,inputRef:{current:null},registeredInputs:new Map,registerInput:Kt,getInputControl:()=>null,commit:async()=>{},change:Kt}},r1=x.createContext(s1);function Na(e=!0){const t=x.useContext(r1);if(t.setValidityData===Kt&&!e)throw new Error(un(28));return t}const kB=x.createContext(void 0);function CB(e=!1){const t=x.useContext(kB);if(!t&&!e)throw new Error(un(86));return t}const TB=x.createContext({elementRef:{current:null},formRef:{current:{fields:new Map}},errors:{},clearErrors:Kt,validationMode:"onSubmit",submitCountRef:{current:0}});function Qc(){return x.useContext(TB)}let a1=0;function EB(e,t="mui"){const[a,o]=x.useState(e),c=e||a;return x.useEffect(()=>{a==null&&(a1+=1,o(`${t}-${a1}`))},[a,t]),c}const l1=Mv.useId;function Jd(e,t){if(l1!==void 0){const a=l1();return e??(t?`${t}-${a}`:a)}return EB(e,t)}function ka(e){return Jd(e,"base-ui")}const AB=x.createContext({controlId:void 0,registerControlId:Kt,resetControlId:Kt,labelId:void 0,setLabelId:Kt,messageIds:[],setMessageIds:Kt,getDescriptionProps:e=>e});function yl(){return x.useContext(AB)}function Fv(e,t,a,o=!0,c){const[d,f]=x.useState(),p=ka(c?`${c}-label`:void 0),h=e??t??d;return Me(()=>{const b=e||t||!o?void 0:RB(a.current,p);d!==b&&f(b)}),h}function RB(e,t){const a=OB(e);if(a)return!a.id&&t&&(a.id=t),a.id||void 0}function OB(e){if(!e)return;const t=e.parentElement;if(t&&t.tagName==="LABEL")return t;const a=e.id;if(a){const c=e.nextElementSibling;if(c&&c.htmlFor===a)return c}const o=e.labels;return o&&o[0]}function co(e={}){const{id:t,enabled:a=!0}=e,{controlId:o,registerControlId:c,resetControlId:d}=yl(),f=ka(),p=Ps(()=>Symbol()),h=x.useRef(!1),b=x.useRef(!1),y=Ie(()=>{!h.current||c===Kt||(h.current=!1,c(p.current,void 0))});return Me(()=>{if(!a||c===Kt){y();return}let v;if(t!==void 0)b.current=!0,v=t;else if(b.current)v=f;else{d();return}if(v===void 0){y();return}h.current=!0,c(p.current,v)},[t,a,c,d,f,p,y]),Me(()=>y,[y]),(a?o:void 0)??t??f}function MB(){return typeof navigator>"u"?{userAgent:"",platform:"",maxTouchPoints:0}:{userAgent:navigator.userAgent,platform:navigator.platform??"",maxTouchPoints:navigator.maxTouchPoints??0}}const{userAgent:DB,platform:IB,maxTouchPoints:PB}=MB(),Km=DB.toLowerCase(),ef=IB.toLowerCase(),tf=/^i(os$|p)/.test(ef)||ef==="macintel"&&PB>1,o1="android",Ym=ef===o1||Km.includes(o1),i1=!tf&&ef.startsWith("mac");ef.startsWith("win");const _B=i1||tf,jl=typeof CSS<"u"&&!!CSS.supports?.("-webkit-backdrop-filter:none"),c1=!jl&&Km.includes("firefox");!jl&&Km.includes("chrom");const LB=_B,$v=/jsdom|happydom/.test(Km);function ir(e){let t=e.activeElement;for(;t?.shadowRoot?.activeElement!=null;)t=t.shadowRoot.activeElement;return t}function Ze(e,t){if(!e||!t)return!1;const a=t.getRootNode?.();if(e.contains(t))return!0;if(a&&Gc(a)){let o=t;for(;o;){if(e===o)return!0;o=o.parentNode||o.host}}return!1}function fs(e){return"composedPath"in e?e.composedPath()[0]??e.target:e.target}const qv="data-base-ui-focusable",u1="input:not([type='hidden']):not([disabled]),[contenteditable]:not([contenteditable='false']),textarea:not([disabled])",Ei="ArrowLeft",Ai="ArrowRight",Gv="ArrowUp",Xm="ArrowDown",d1="data-starting-style",zB="data-ending-style",BB={[d1]:""},HB={[zB]:""},uo={transitionStatus(e){return e==="starting"?BB:e==="ending"?HB:null}},UB="data-open",VB="data-closed",FB="data-anchor-hidden",f1="data-popup-open",$B="data-pressed",qB={[f1]:""},GB={[f1]:"",[$B]:""},KB={[UB]:""},YB={[VB]:""},XB={[FB]:""},Kv={open(e){return e?qB:null}},Yv={open(e){return e?GB:null}},Zc={open(e){return e?KB:YB},anchorHidden(e){return e?XB:null}},Xv={...Zc,...uo},Qv="data-trigger-disabled";function Qm(e,t){if(!Rt(e))return!1;const a=e;if(t.hasElement(a))return!a.hasAttribute(Qv);for(const[,o]of t.entries())if(Ze(o,a))return!o.hasAttribute(Qv);return!1}function Zv(e,t){if(t==null)return!1;if("composedPath"in e)return e.composedPath().includes(t);const a=e;return a.target!=null&&t.contains(a.target)}function QB(e){return e.matches("html,body")}function Zm(e){return pn(e)&&e.matches(u1)}function ZB(e){return e?.closest(`button,a[href],[role="button"],select,[tabindex]:not([tabindex="-1"]),${u1}`)!=null}function Wv(e){return e?e.getAttribute("role")==="combobox"&&Zm(e):!1}function WB(e){if(!e||$v)return!0;try{return e.matches(":focus-visible")}catch{return!0}}function Wm(e){return e?e.hasAttribute(qv)?e:e.querySelector(`[${qv}]`)||e:null}function fo(e,t,a=!0){return e.filter(c=>c.parentId===t).flatMap(c=>[...!a||c.context?.open?[c]:[],...fo(e,c.id,a)])}function p1(e,t){let a=[],o=e.find(c=>c.id===t)?.parentId;for(;o;){const c=e.find(d=>d.id===o);o=c?.parentId,c&&(a=a.concat(c))}return a}function Cs(e){e.preventDefault(),e.stopPropagation()}function JB(e){return"nativeEvent"in e}function Jm(e){return e.pointerType===""&&e.isTrusted?!0:Ym&&e.pointerType?e.type==="click"&&e.buttons===1:e.detail===0&&!e.pointerType}function Jv(e){return $v?!1:!Ym&&e.width===0&&e.height===0||Ym&&e.width===1&&e.height===1&&e.pressure===0&&e.detail===0&&e.pointerType==="mouse"||e.width<1&&e.height<1&&e.pressure===0&&e.detail===0&&e.pointerType==="touch"}function po(e,t){const a=["mouse","pen"];return t||a.push("",void 0),a.includes(e)}function e4(e){const t=e.type;return t==="click"||t==="mousedown"||t==="keydown"||t==="keyup"}const Wc=Math.min,wl=Math.max,eh=Math.round,Ri=Math.floor,Sl=e=>({x:e,y:e}),t4={left:"right",right:"left",bottom:"top",top:"bottom"};function m1(e,t,a){return wl(e,Wc(t,a))}function mo(e,t){return typeof e=="function"?e(t):e}function ra(e){return e.split("-")[0]}function Nl(e){return e.split("-")[1]}function ey(e){return e==="x"?"y":"x"}function ty(e){return e==="y"?"height":"width"}function aa(e){const t=e[0];return t==="t"||t==="b"?"y":"x"}function ny(e){return ey(aa(e))}function n4(e,t,a){a===void 0&&(a=!1);const o=Nl(e),c=ny(e),d=ty(c);let f=c==="x"?o===(a?"end":"start")?"right":"left":o==="start"?"bottom":"top";return t.reference[d]>t.floating[d]&&(f=th(f)),[f,th(f)]}function s4(e){const t=th(e);return[sy(e),t,sy(t)]}function sy(e){return e.includes("start")?e.replace("start","end"):e.replace("end","start")}const h1=["left","right"],g1=["right","left"],r4=["top","bottom"],a4=["bottom","top"];function l4(e,t,a){switch(e){case"top":case"bottom":return a?t?g1:h1:t?h1:g1;case"left":case"right":return t?r4:a4;default:return[]}}function o4(e,t,a,o){const c=Nl(e);let d=l4(ra(e),a==="start",o);return c&&(d=d.map(f=>f+"-"+c),t&&(d=d.concat(d.map(sy)))),d}function th(e){const t=ra(e);return t4[t]+e.slice(t.length)}function i4(e){var t,a,o,c;return{top:(t=e.top)!=null?t:0,right:(a=e.right)!=null?a:0,bottom:(o=e.bottom)!=null?o:0,left:(c=e.left)!=null?c:0}}function x1(e){return typeof e!="number"?i4(e):{top:e,right:e,bottom:e,left:e}}function nf(e){const{x:t,y:a,width:o,height:c}=e;return{width:o,height:c,top:a,left:t,right:t+o,bottom:a+c,x:t,y:a}}function nh(e,t,a){return Math.floor(e/t)!==a}function ho(e,t){return t<0||t>=e.length}function sh(e,t){return _s(e.current,{disabledIndices:t})}function ry(e,t){return _s(e.current,{decrement:!0,startingIndex:e.current.length,disabledIndices:t})}function _s(e,{startingIndex:t=-1,decrement:a=!1,disabledIndices:o,amount:c=1}={}){let d=t;do d+=a?-c:c;while(d>=0&&d<=e.length-1&&go(e,d,o));return d}function c4(e,{event:t,orientation:a,loopFocus:o,onLoop:c,rtl:d,cols:f,disabledIndices:p,minIndex:h,maxIndex:b,prevIndex:y,stopEvent:v=!1}){let j=y,w;if(t.key===Gv?w="up":t.key===Xm&&(w="down"),w){const S=[],N=[];let k=!1,A=0;{let _=null,z=-1;e.forEach((H,B)=>{if(H==null)return;A+=1;const L=H.closest('[role="row"]');L&&(k=!0),(L!==_||z===-1)&&(_=L,z+=1,S[z]=[]),S[z].push(B),N[B]=z})}let P=!1,R=0;if(k)for(const _ of S){const z=_.length;z>R&&(R=z),z!==f&&(P=!0)}const C=P&&A{if(!P||y===-1)return;const z=N[y];if(z==null)return;const H=S[z].indexOf(y),B=_==="up"?-1:1;for(let L=z+B,F=0;F=S.length){if(!o||C)return;if(L=L<0?S.length-1:0,c){const X=Math.min(H,S[L].length-1),K=S[L][X]??S[L][0],q=c(t,y,K);L=N[q]??L}}const $=S[L];for(let X=Math.min(H,$.length-1);X>=0;X-=1){const K=$[X];if(!go(e,K,p))return K}}},I=_=>{if(!C||y===-1)return;const z=y%E,H=_==="up"?-E:E,B=b-b%E,L=Ri(b/E)+1;for(let F=y-z+H,$=0;$b){if(!o)return;F=F<0?B:0}const X=Math.min(F+E-1,b);for(let K=Math.min(F+z,X);K>=F;K-=1)if(!go(e,K,p))return K}};v&&Cs(t);const O=D(w)??I(w);if(O!==void 0)j=O;else if(y===-1)j=w==="up"?b:h;else if(j=_s(e,{startingIndex:y,amount:E,decrement:w==="up",disabledIndices:p}),o){if(w==="up"&&(y-E_?H:H-E,c&&(j=c(t,y,j))}w==="down"&&y+E>b&&(j=_s(e,{startingIndex:y%E-E,amount:E,disabledIndices:p}),c&&(j=c(t,y,j)))}ho(e,j)&&(j=y)}if(a==="both"){const S=Ri(y/f);t.key===(d?Ei:Ai)&&(v&&Cs(t),y%f!==f-1?(j=_s(e,{startingIndex:y,disabledIndices:p}),o&&nh(j,f,S)&&(j=_s(e,{startingIndex:y-y%f-1,disabledIndices:p}),c&&(j=c(t,y,j)))):o&&(j=_s(e,{startingIndex:y-y%f-1,disabledIndices:p}),c&&(j=c(t,y,j))),nh(j,f,S)&&(j=y)),t.key===(d?Ai:Ei)&&(v&&Cs(t),y%f!==0?(j=_s(e,{startingIndex:y,decrement:!0,disabledIndices:p}),o&&nh(j,f,S)&&(j=_s(e,{startingIndex:y+(f-y%f),decrement:!0,disabledIndices:p}),c&&(j=c(t,y,j)))):o&&(j=_s(e,{startingIndex:y+(f-y%f),decrement:!0,disabledIndices:p}),c&&(j=c(t,y,j))),nh(j,f,S)&&(j=y));const N=Ri(b/f)===S;ho(e,j)&&(o&&N?(j=t.key===(d?Ai:Ei)?b:_s(e,{startingIndex:y-y%f-1,disabledIndices:p}),c&&(j=c(t,y,j))):j=y)}return j}function go(e,t,a){if(typeof a=="function"?a(t):a?.includes(t)??!1)return!0;const c=e[t];return c?!rh(c)||c.matches(":disabled")?!0:!a&&(c.hasAttribute("disabled")||c.getAttribute("aria-disabled")==="true"):!1}function u4(e){return e.visibility==="hidden"||e.visibility==="collapse"}function rh(e,t=e?yr(e):null){return!e||!e.isConnected||!t||u4(t)?!1:typeof e.checkVisibility=="function"?e.checkVisibility():t.display!=="none"&&t.display!=="contents"}const d4='a[href],button,input,select,textarea,summary,details,iframe,object,embed,[tabindex],[contenteditable]:not([contenteditable="false"]),audio[controls],video[controls]';function f4(e){const t=e.assignedSlot;if(t)return t;if(e.parentElement)return e.parentElement;const a=e.getRootNode();return Gc(a)?a.host:null}function ay(e){for(const t of Array.from(e.children))if(Fs(t)==="summary")return t;return null}function p4(e,t){const a=ay(t);return!!a&&(e===a||Ze(a,e))}function b1(e){const t=e?Fs(e):"";return e!=null&&e.matches(d4)&&(t!=="summary"||e.parentElement!=null&&Fs(e.parentElement)==="details"&&ay(e.parentElement)===e)&&(t!=="details"||ay(e)==null)&&(t!=="input"||e.type!=="hidden")}function v1(e){if(!b1(e)||!e.isConnected||e.matches(":disabled"))return!1;for(let t=e;t;t=f4(t)){const a=t!==e,o=Fs(t)==="slot";if(t.hasAttribute("inert")||a&&Fs(t)==="details"&&!t.open&&!p4(e,t)||t.hasAttribute("hidden")||!o&&!m4(t,a))return!1}return!0}function m4(e,t){const a=yr(e);return t?a.display!=="none":rh(e,a)}function y1(e){const t=e.tabIndex;if(t<0){const a=Fs(e);if(a==="details"||a==="audio"||a==="video"||pn(e)&&e.isContentEditable)return 0}return t}function ly(e){if(Fs(e)!=="input")return null;const t=e;return t.type==="radio"&&t.name!==""?t:null}function h4(e,t){const a=ly(e);if(!a)return!0;const o=t.find(c=>{const d=ly(c);return d?.name===a.name&&d.form===a.form&&d.checked});return o?o===a:t.find(c=>{const d=ly(c);return d?.name===a.name&&d.form===a.form})===a}function j1(e){if(pn(e)&&Fs(e)==="slot"){const t=e.assignedElements({flatten:!0});if(t.length>0)return t}return pn(e)&&e.shadowRoot?Array.from(e.shadowRoot.children):Array.from(e.children)}function w1(e,t){j1(e).forEach(a=>{b1(a)&&t.push(a),w1(a,t)})}function S1(e,t,a){j1(e).forEach(o=>{pn(o)&&o.matches(t)&&a.push(o),S1(o,t,a)})}function oy(e){return v1(e)&&y1(e)>=0}function N1(e){const t=[];return w1(e,t),t.filter(v1)}function sf(e){const t=N1(e);return t.filter(a=>y1(a)>=0&&h4(a,t))}function k1(e,t){const a=sf(e),o=a.length;if(o===0)return;const c=ir(Gt(e)),d=a.indexOf(c),f=d===-1?t===1?0:o-1:d+t;return a[f]}function iy(e){return k1(Gt(e).body,1)||e}function C1(e){return k1(Gt(e).body,-1)||e}function T1(e,t){if(!e)return null;const a=sf(Gt(e).body),o=a.length;if(o===0)return null;const c=a.indexOf(e);if(c===-1)return null;const d=(c+t+o)%o;return a[d]}function g4(e){return T1(e,1)}function x4(e){return T1(e,-1)}function Jc(e,t){const a=t||e.currentTarget,o=e.relatedTarget;return!o||!Ze(a,o)}function b4(e){sf(e).forEach(a=>{a.dataset.tabindex=a.getAttribute("tabindex")||"",a.setAttribute("tabindex","-1")})}function E1(e){const t=[];S1(e,"[data-tabindex]",t),t.forEach(a=>{const o=a.dataset.tabindex;delete a.dataset.tabindex,o?a.setAttribute("tabindex",o):a.removeAttribute("tabindex")})}function cy(e){x.useEffect(e,Ir)}const rf=0;class la{constructor(){cn(this,"currentId",rf);cn(this,"clear",()=>{this.currentId!==rf&&(clearTimeout(this.currentId),this.currentId=rf)});cn(this,"disposeEffect",()=>this.clear)}static create(){return new la}start(t,a){this.clear(),this.currentId=setTimeout(()=>{this.currentId=rf,a()},t)}isStarted(){return this.currentId!==rf}}function er(){const e=Ps(la.create).current;return cy(e.disposeEffect),e}function v4(e,t){return e.matches(":disabled")?!1:!t||e.form===t?!0:e.form===null&&!e.hasAttribute("form")}const y4=x.createContext({disabled:!1});function A1(){return x.useContext(y4)}var uy={exports:{}},$s={};var R1;function j4(){if(R1)return $s;R1=1;var e=Xd();function t(y){var v="https://react.dev/errors/"+y;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}return e(),uy.exports=j4(),uy.exports}var Ts=M1();const ah=null;class w4{constructor(){cn(this,"callbacks",[]);cn(this,"callbacksCount",0);cn(this,"nextId",1);cn(this,"startId",1);cn(this,"isScheduled",!1);cn(this,"tick",t=>{this.isScheduled=!1;const a=this.callbacks,o=this.callbacksCount;if(this.callbacks=[],this.callbacksCount=0,this.startId=this.nextId,o>0)for(let c=0;c=this.callbacks.length||this.callbacks[a]!==null&&(this.callbacks[a]=null,this.callbacksCount-=1)}}let lh=new w4;class Ga{constructor(){cn(this,"currentId",ah);cn(this,"cancel",()=>{this.currentId!==ah&&(lh.cancel(this.currentId),this.currentId=ah)});cn(this,"disposeEffect",()=>this.cancel)}static create(){return new Ga}static request(t){return lh.request(t)}static cancel(t){return lh.cancel(t)}request(t){this.cancel(),this.currentId=lh.request(()=>{this.currentId=ah,t()})}}function eu(){const e=Ps(Ga.create).current;return cy(e.disposeEffect),e}function kl(e){return e==null?e:"current"in e?e.current:e}let oh=null;function S4(e){if(!oh){const t=[];oh=t,queueMicrotask(()=>{oh=null,Ts.flushSync(()=>{for(const a of t)a()})})}oh.push(e)}function D1(e,t=!1,a=!1){const o=eu();return Ie((c,d=null)=>{o.cancel();const f=kl(e);if(f==null)return;const p=f,h=()=>{if(!a){Ts.flushSync(c);return}S4(()=>{d?.aborted||c()})};if(typeof p.getAnimations!="function"||globalThis.BASE_UI_ANIMATIONS_DISABLED){c();return}function b(){Promise.all(p.getAnimations().map(y=>y.finished)).then(()=>{d?.aborted||h()},()=>{if(d?.aborted)return;if(p.getAnimations().some(v=>v.pending||v.playState!=="finished")){b();return}h()})}if(t){const y=d1;if(!p.hasAttribute(y)){o.request(b);return}const v=new MutationObserver(()=>{p.hasAttribute(y)||(v.disconnect(),b())});v.observe(p,{attributes:!0,attributeFilter:[y]}),d?.addEventListener("abort",()=>v.disconnect(),{once:!0});return}o.request(b)})}function oa(e){const{enabled:t=!0,open:a,ref:o,batch:c=!1,onComplete:d}=e,f=Ie(d),p=D1(o,a,c);x.useEffect(()=>{if(!t)return;const h=new AbortController;return p(f,h.signal),()=>{h.abort()}},[t,a,f,p])}function Oi(e,t=!1,a=!1,o=!1){const[c,d]=x.useState(e&&t?"idle":void 0),[f,p]=x.useState(e&&!o);return e&&!f&&(p(!0),d("starting")),!e&&f&&c!=="ending"&&!a&&d("ending"),!e&&!f&&c==="ending"&&d(void 0),Me(()=>{if(!e&&f&&c!=="ending"&&a){const h=Ga.request(()=>{d("ending")});return()=>{Ga.cancel(h)}}},[e,f,c,a]),Me(()=>{if(!e||t)return;const h=Ga.request(()=>{d(void 0)});return()=>{Ga.cancel(h)}},[t,e]),Me(()=>{if(!e||!t)return;e&&f&&c!=="idle"&&d("starting");const h=Ga.request(()=>{d("idle")});return()=>{Ga.cancel(h)}},[t,e,f,c]),{mounted:f,setMounted:p,transitionStatus:c}}function Ka({controlled:e,default:t,name:a,state:o="value"}){const{current:c}=x.useRef(e!==void 0),[d,f]=x.useState(t),p=c&&e!==void 0?e:d,h=x.useCallback(b=>{c||f(b)},[]);return[p,h]}function tu(e,t,a,o,c=!0,d){const{registerFieldControl:f}=Na(),p=Ps(()=>Symbol());Me(()=>{const h=p.current;if(!c){f(h,void 0);return}f(h,{controlRef:e,getValue:o,id:t,name:d,value:a})},[e,c,o,t,d,f,p,a]),Me(()=>{const h=p.current;return()=>{f(h,void 0)}},[f,p])}function ia(e,t){const a=x.useRef(e),o=Ie(t);Me(()=>{a.current!==e&&o(a.current),a.current=e},[e,o])}const Qn="none",xo="trigger-press",qs="trigger-hover",ih="trigger-focus",ch="outside-press",af="item-press",dy="close-press",lf="input-change",Ya="input-clear",N4="input-press",bo="focus-out",nu="escape-key",fy="list-navigation",I1="keyboard",P1="pointer",_1="cancel-open",k4="disabled",py="imperative-action",C4="window-resize";function qe(e,t,a,o){let c=!1,d=!1;const f=o??mn;return{reason:e,event:t??new Event("base-ui"),cancel(){c=!0},allowPropagation(){d=!0},get isCanceled(){return c},get isPropagationAllowed(){return d},trigger:a,...f}}function T4(e,t,a){const o=a??mn;return{reason:e,event:t??new Event("base-ui"),...o}}const E4=x.forwardRef(function(t,a){const{render:o,className:c,id:d,name:f,value:p,disabled:h=!1,onValueChange:b,defaultValue:y,autoFocus:v=!1,style:j,...w}=t,{state:S,name:N,disabled:k,setTouched:A,setDirty:P,validityData:R,setFocused:C,setFilled:E,validationMode:D,validation:I}=Na(),{clearErrors:O,elementRef:_,submitCountRef:z}=Qc(),H=k||h,B=N??f,L={...S,disabled:H},{labelId:F}=yl(),$=co({id:d}),[X]=Ka({controlled:p,default:y,name:"FieldControl",state:"value"}),K=p!==void 0,q=K?X:void 0,Q=q==null?void 0:String(q),G=Ie(()=>I.inputRef.current?.value);tu(I.inputRef,$,Q,G,!H,f),Me(()=>{const M=Q??I.inputRef.current?.value;M!==void 0&&E(M!=="")},[Q,I.inputRef,E]),ia(Q,()=>{Q!==void 0&&(O(B),P(Q!==(R.initialValue??"")),I.change(Q))});const J=x.useRef(null),Y=er();return Me(()=>{v&&J.current===ir(Gt(J.current))&&C(!0)},[v,C]),yt("input",t,{ref:[a,J],state:L,props:[{id:$,disabled:H,name:B,ref:I.inputRef,"aria-labelledby":F,autoFocus:v,...K?{value:q}:{defaultValue:y},onChange(M){const U=M.currentTarget.value,Z=qe(Qn,M.nativeEvent);b?.(U,Z),!K&&(P(U!==(R.initialValue??"")),E(U!==""),!M.nativeEvent.defaultPrevented&&!Z.isCanceled&&(O(B),I.change(U)))},onFocus(){C(!0)},onBlur(M){if(A(!0),C(!1),D==="onBlur"){const U=M.currentTarget.value;I.commit(U),K&&queueMicrotask(()=>{const Z=I.inputRef.current?.value;Z!==void 0&&Z!==U&&Z!==(R.initialValue??"")&&I.commit(Z)})}},onKeyDown(M){if(M.currentTarget.tagName==="INPUT"&&M.key==="Enter"){A(!0);const U=M.currentTarget.value,Z=M.currentTarget.form;if(Z&&Z===_.current&&!M.defaultPrevented){const ae=M.currentTarget,ce=z.current;Y.start(0,()=>{z.current===ce&&I.commit(ae.value)})}else I.commit(U)}}},w,M=>I.getValidationProps(H,M)],stateAttributesMapping:Ti})}),A4=x.forwardRef(function(t,a){return s.jsx(E4,{ref:a,...t})});function Fe({className:e,type:t,ref:a,...o}){return s.jsx(A4,{ref:a,type:t,"data-slot":"input",className:Ue("h-[30px] w-full min-w-0 rounded-md border border-control-border bg-control px-2.5","text-[12.5px] text-foreground outline-none","transition-[border-color,box-shadow] duration-150 ease-out","placeholder:text-muted-foreground","hover:border-control-border-hover","focus:border-primary focus:shadow-focus","disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-surface-3 disabled:text-muted-foreground","file:inline-flex file:border-0 file:bg-transparent file:text-foreground",e),...o})}function Mi({className:e,...t}){return s.jsx("textarea",{"data-slot":"textarea",className:Ue("min-h-26 w-full resize-y rounded-md border border-control-border bg-control px-3 py-2.5","font-mono text-[12px] leading-[1.6] text-foreground outline-none","transition-[border-color,box-shadow] duration-150 ease-out","placeholder:text-muted-foreground","hover:border-control-border-hover","focus:border-primary focus:shadow-focus","disabled:cursor-not-allowed disabled:bg-surface-3 disabled:text-muted-foreground",e),...t})}function Di({className:e,...t}){return s.jsx("div",{"data-slot":"input-group",role:"group",className:Ue("group/input-group relative flex h-[30px] w-full min-w-0 items-center rounded-md border border-control-border bg-control","transition-[border-color,box-shadow] duration-150 ease-out","outline-none","has-[[data-slot=input-group-control]:focus-visible]:border-primary","has-[[data-slot=input-group-control]:focus-visible]:shadow-focus","has-[[data-slot=input-group-control]:hover:not(:focus-visible)]:border-control-border-hover","has-[>textarea]:h-auto","has-[>[data-align=inline-start]]:[&>[data-slot=input-group-control]]:pl-1.5","has-[>[data-align=inline-end]]:[&>[data-slot=input-group-control]]:pr-1.5","has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col","has-[>[data-align=block-start]]:[&>[data-slot=input-group-control]]:pb-1.5","has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col","has-[>[data-align=block-end]]:[&>[data-slot=input-group-control]]:pt-1.5",e),...t})}const R4=Xc(["flex h-auto cursor-text items-center justify-center gap-1.5 select-none","text-[12px] font-medium text-muted-foreground","group-data-[disabled=true]/input-group:opacity-50"],{variants:{align:{"inline-start":"order-first pl-[9px] has-[>button]:-ml-1","inline-end":"order-last pr-[9px] has-[>button]:-mr-1","block-start":"order-first w-full justify-start px-2.5 pt-2 group-has-[>input]/input-group:pt-1.5","block-end":"order-last w-full justify-start px-2.5 pb-2 group-has-[>input]/input-group:pb-1.5"}},defaultVariants:{align:"inline-start"}});function Ii({className:e,align:t="inline-start",...a}){return s.jsx("div",{role:"group","data-slot":"input-group-addon","data-align":t,className:Ue(R4({align:t}),e),onClick:o=>{o.target.closest("button")||o.currentTarget.parentElement?.querySelector("input")?.focus()},...a})}const O4=Xc("flex items-center shadow-none",{variants:{size:{xs:"h-6 gap-1 rounded-xs px-1.5",sm:"h-8 gap-1.5 rounded-sm px-2.5","icon-xs":"size-6 rounded-xs p-0","icon-sm":"size-8 rounded-sm p-0"}},defaultVariants:{size:"xs"}});function M4({className:e,type:t="button",variant:a="ghost",size:o="xs",...c}){return s.jsx(ne,{type:t,"data-size":o,variant:a,className:Ue(O4({size:o}),e),...c})}function Pi({className:e,...t}){return s.jsx(Fe,{"data-slot":"input-group-control",className:Ue("flex-1 rounded-none border-0 bg-transparent shadow-none focus:border-0 focus:shadow-none",e),...t})}function D4(e){return yt(e.defaultTagName??"div",e,e)}const I4=Xc("inline-flex w-fit shrink-0 items-center justify-center gap-[5px] overflow-hidden whitespace-nowrap border font-semibold",{variants:{variant:{default:"border-transparent bg-primary text-primary-foreground",secondary:"border-transparent bg-secondary text-secondary-foreground",destructive:"border-destructive-bd bg-destructive-soft text-destructive",outline:"border-control-border bg-control text-subtle",ghost:"border-transparent bg-transparent text-muted-foreground",link:"border-transparent bg-transparent text-primary underline-offset-4 hover:underline",success:"border-success-bd bg-success-soft text-success",warning:"border-warning-bd bg-warning-soft text-warning",info:"border-info-bd bg-info-soft text-info",brand:"border-primary-bd bg-primary-soft text-primary-fg",cn:"border-cn-bd bg-cn-soft text-cn",intl:"border-intl-bd bg-intl-soft text-intl",sensitive:"border-sensitive-bd bg-sensitive-soft text-sensitive"},shape:{pill:"min-h-[21px] rounded-pill px-[9px] py-px text-[11.5px]",tag:"min-h-[19px] rounded-xs px-2 py-0 text-[11px]"}},defaultVariants:{variant:"default",shape:"pill"}});function ze({className:e,variant:t,shape:a,render:o,...c}){return D4({render:o,defaultTagName:"span",props:ta({"data-slot":"badge"},c,{className:Ue(I4({variant:t,shape:a}),e)})})}function my({className:e,...t}){return s.jsx("span",{className:Ue("size-1.5 flex-none rounded-full bg-current",e),...t})}function vo(e,t){if(typeof window>"u")return e;const a=window.wbI18n;return a?a.t(e,t)??e:e}function of({className:e,...t}){return s.jsx("svg",{"data-slot":"spinner",role:"status","aria-label":vo("加载中"),viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",className:Ue("size-4 animate-spin [animation-duration:0.7s]",e),...t,children:s.jsx("path",{d:"M21 12a9 9 0 1 1-6.219-8.56"})})}function Je({className:e,...t}){return s.jsx("label",{"data-slot":"label",className:Ue("flex items-center gap-[6px] text-[11.5px] text-muted-foreground",e),...t})}const L1={clipPath:"inset(50%)",overflow:"hidden",whiteSpace:"nowrap",border:0,padding:0,width:1,height:1,margin:-1},_i={...L1,position:"fixed",top:0,left:0},su={...L1,position:"absolute"},z1=x.createContext(void 0);function P4(){const e=x.useContext(z1);if(e===void 0)throw new Error(un(63));return e}const _4="data-checked",L4="data-unchecked",B1={...Ti,checked(e){return e?{[_4]:""}:{[L4]:""}}},z4=x.forwardRef(function(t,a){const{checked:o,className:c,defaultChecked:d,"aria-labelledby":f,form:p,id:h,inputRef:b,name:y,nativeButton:v=!1,onCheckedChange:j,readOnly:w=!1,required:S=!1,disabled:N=!1,render:k,uncheckedValue:A,value:P,style:R,...C}=t,{clearErrors:E}=Qc(),{state:D,setTouched:I,setDirty:O,validityData:_,setFilled:z,setFocused:H,validationMode:B,disabled:L,name:F,validation:$}=Na(),{labelId:X}=yl(),K=L||N,q=F??y,Q=x.useRef(null),G=sa(Q,b,$.inputRef),J=x.useRef(null),Y=ka(),re=co({id:h}),M=v?void 0:re,[U,Z]=Ka({controlled:o,default:!!d,name:"Switch",state:"checked"});tu(J,Y,U,void 0,!K,y),Me(()=>{z(U)},[U,z]),ia(U,()=>{E(q),O(U!==_.initialValue),$.change(U)});const{getButtonProps:ae,buttonRef:ce}=na({disabled:K,native:v}),W=Fv(f,X,Q,!v,M),ue={id:v?re:Y,role:"switch","aria-checked":U,"aria-readonly":w||void 0,"aria-required":S||void 0,"aria-labelledby":W,onFocus(){K||H(!0)},onBlur(){const te=Q.current;!te||K||(I(!0),H(!1),B==="onBlur"&&$.commit(te.checked))},onClick(te){if(w||K)return;te.preventDefault();const ke=Q.current;ke&&Yc(ke,te)}},ve={...$.getValidationProps(K),checked:U,disabled:K,form:p,id:M,name:q,required:S,style:q?su:_i,tabIndex:-1,type:"checkbox","aria-hidden":!0,ref:G,onChange(te){if(te.nativeEvent.defaultPrevented)return;if(w){te.preventDefault();return}const ke=te.currentTarget.checked,le=qe(Qn,te.nativeEvent);j?.(ke,le),!le.isCanceled&&Z(ke)},onClick(te){te.stopPropagation()},onFocus(){J.current?.focus()},...P!==void 0?{value:P}:mn},ee=x.useMemo(()=>({...D,checked:U,disabled:K,readOnly:w,required:S}),[D,U,K,w,S]),he=yt("span",t,{state:ee,ref:[a,J,ce],props:[ue,C,ae,te=>$.getValidationProps(K,te)],stateAttributesMapping:B1});return s.jsxs(z1.Provider,{value:ee,children:[he,!U&&q&&A!==void 0&&s.jsx("input",{type:"hidden",form:p,name:q,value:A,disabled:K}),s.jsx("input",{...ve,suppressHydrationWarning:!0})]})}),B4=x.forwardRef(function(t,a){const{render:o,className:c,style:d,...f}=t,p=P4();return yt("span",t,{state:p,ref:a,stateAttributesMapping:B1,props:f})});function ns({className:e,size:t="default",...a}){const o=t==="sm";return s.jsx(z4,{"data-slot":"switch",className:Ue("relative inline-flex flex-none cursor-pointer items-center rounded-pill border-0 bg-border-strong p-0","transition-colors duration-[180ms] ease-out","data-checked:bg-primary","focus-visible:shadow-focus focus-visible:outline-none","data-disabled:cursor-not-allowed data-disabled:opacity-50",o?"h-[14px] w-6":"h-[21px] w-9",e),...a,children:s.jsx(B4,{"data-slot":"switch-thumb",className:Ue("block rounded-full bg-primary-on shadow-1","transition-transform duration-[180ms] [transition-timing-function:cubic-bezier(.3,1.4,.6,1)]",o?"size-[10px] translate-x-0.5 data-checked:translate-x-[10px]":"size-[17px] translate-x-0.5 data-checked:translate-x-[15px]")})})}function H4(e){if(!e)return null;for(const t of e.elements){const a=t.tagName;if(a==="BUTTON"||a==="INPUT"){const o=t;if(o.type==="submit")return o}}return null}const U4="data-checked",V4="data-unchecked";function H1(e){return{checked(t){return e.indeterminate?{}:t?{[U4]:""}:{[V4]:""}},...Ti}}const F4=x.createContext(void 0);function $4(){return x.useContext(F4)}const U1=x.createContext(void 0);function q4(){const e=x.useContext(U1);if(e===void 0)throw new Error(un(14));return e}const G4="data-parent",K4=x.forwardRef(function(t,a){const{checked:o,className:c,defaultChecked:d=!1,"aria-labelledby":f,disabled:p=!1,form:h,id:b,indeterminate:y=!1,inputRef:v,name:j,onCheckedChange:w,parent:S=!1,readOnly:N=!1,render:k,required:A=!1,uncheckedValue:P,value:R,nativeButton:C=!1,style:E,...D}=t,{clearErrors:I}=Qc(),{disabled:O,name:_,setDirty:z,setFilled:H,setFocused:B,setTouched:L,state:F,validationMode:$,validityData:X,validation:K}=Na(),q=A1(),{labelId:Q,registerControlId:G,getDescriptionProps:J}=yl(),Y=$4(),re=Y?.allValues===void 0?void 0:Y.parent,M=re!==void 0,U=O||q.disabled||Y?.disabled||p,Z=_??j,ae=R??Z,ce=ka(),W=Y?.registerControlId!==G,ue=co({id:b||void 0,enabled:W}),ve=C?ue:ce;let ee={};M&&(S?ee=re.getParentProps():ae!==void 0&&(ee=re.getChildProps(ae)));const{checked:he=o,indeterminate:te=y,onCheckedChange:ke,...le}=ee,de=Y?.value,Se=x.useRef(null),{getButtonProps:Te,buttonRef:_e}=na({disabled:U,native:C}),Ae=Y?.validation??K,[oe,me]=Ka({controlled:ae!==void 0&&de!==void 0&&!S?de.includes(ae):he,default:d,name:"Checkbox",state:"checked"}),pe=M?!!he:oe,Ce=M&&te||y;tu(Se,ce,oe,void 0,!Y&&!U,j);const Ne=re?.registerChildId,be=x.useRef(null),De=Ae.registerInput,Ge=Y?ae:void 0,Ct=x.useCallback(it=>De(it,{controlRef:Se,value:Ge}),[De,Ge]),Tt=sa(v,be,S?void 0:Ct),Mt=Fv(f,Q,be,!C,ue);Me(()=>{be.current&&(be.current.indeterminate=Ce),Y||H(oe)},[oe,Ce,Y,H]),ia(oe,()=>{Y||(I(Z),z(oe!==X.initialValue),Ae.change(oe))});const ot=ta({checked:oe,disabled:U,form:h,name:S?void 0:Z,id:C?void 0:ue,required:A,ref:Tt,style:Z?su:_i,tabIndex:-1,type:"checkbox","aria-hidden":!0,onChange(it){if(it.nativeEvent.defaultPrevented)return;if(N){it.preventDefault();return}const $t=it.currentTarget.checked,Vn=qe(Qn,it.nativeEvent);if(w?.($t,Vn),!Vn.isCanceled&&(ke?.($t,Vn),!Vn.isCanceled&&(me($t),ae!==void 0&&Y!==void 0&&!S&&!M))){const rn=$t?[...Y.value,ae]:Y.value.filter(nt=>nt!==ae);Y.setValue(rn,Vn)}},onClick(it){it.stopPropagation()},onFocus(){Se.current?.focus()}},R!==void 0?{value:(Y?oe&&R:R)||""}:mn,J,it=>Ae.getValidationProps(U,it));x.useEffect(()=>{if(!re||ae===void 0)return;const it=re.disabledStatesRef.current;return it.set(ae,U),()=>{it.delete(ae)}},[re,U,ae]);const Kn=x.useMemo(()=>({...F,checked:pe,disabled:U,readOnly:N,required:A,indeterminate:Ce}),[F,pe,U,N,A,Ce]),ms=H1(Kn),Yn=yt("span",t,{state:Kn,ref:[_e,Se,a],props:[{id:ve,role:"checkbox","aria-checked":Ce?"mixed":pe,"aria-readonly":N||void 0,"aria-required":A||void 0,"aria-labelledby":Mt,[G4]:S?"":void 0,onFocus(){U||B(!0)},onBlur(){const it=be.current;it&&(L(!0),B(!1),$==="onBlur"&&Ae.commit(Y?de:it.checked))},onKeyDown(it){if(it.key!=="Enter"||(it.preventBaseUIHandler(),it.defaultPrevented))return;const $t=be.current?.form??null,Vn=it.currentTarget,rn=it.nativeEvent,nt=it.preventDefault,Dt=rn.preventDefault;let qt=!1;it.preventDefault=()=>{qt=!0,nt.call(it)},rn.preventDefault=()=>{qt=!0,Dt.call(rn)},Dt.call(rn),Tn(Vn).queueMicrotask(()=>{it.preventDefault=nt,rn.preventDefault=Dt,qt||H4($t)?.click()})},onClick(it){if(N||U)return;it.preventDefault();const $t=be.current;$t&&Yc($t,it)}},D,le,Te,J,it=>Ae.getValidationProps(U,it)],stateAttributesMapping:ms}),vs=Yn.props.id;return Me(()=>{if(!(!Ne||S||ae===void 0||vs===void 0))return Ne(ae,vs)},[Ne,S,ae,vs]),s.jsxs(U1.Provider,{value:Kn,children:[Yn,!oe&&!Y&&Z&&!S&&P!==void 0&&s.jsx("input",{type:"hidden",form:h,name:Z,value:P,disabled:U}),s.jsx("input",{...ot,suppressHydrationWarning:!0})]})}),Y4=x.forwardRef(function(t,a){const{render:o,className:c,style:d,keepMounted:f=!1,...p}=t,h=q4(),b=h.checked||h.indeterminate,{mounted:y,transitionStatus:v,setMounted:j}=Oi(b),w=x.useRef(null),S={...h,transitionStatus:v};oa({batch:!0,enabled:!b,open:b,ref:w,onComplete(){b||j(!1)}});const k={...H1(h),...uo},A=f||y,P=yt("span",t,{ref:[a,w],state:S,stateAttributesMapping:k,props:p});return A?P:null});function Pr({className:e,...t}){return s.jsx(K4,{"data-slot":"checkbox",className:Ue("group/checkbox relative inline-flex size-3.5 shrink-0 cursor-pointer items-center justify-center rounded-[3px] border border-control-border bg-control","outline-none transition-colors duration-150 ease-out","hover:border-control-border-hover","data-checked:border-primary data-checked:bg-primary","focus-visible:shadow-focus","data-disabled:cursor-not-allowed data-disabled:opacity-45",e),...t,children:s.jsx(Y4,{"data-slot":"checkbox-indicator",className:"grid place-content-center text-primary-foreground data-unchecked:hidden",children:s.jsxs("svg",{viewBox:"0 0 12 12",className:"size-2.5","aria-hidden":"true",children:[s.jsx("path",{d:"M2 6h8",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",fill:"none",className:"hidden group-aria-[checked=indeterminate]/checkbox:block"}),s.jsx("path",{d:"M2.5 6.2 4.8 8.5 9.5 3.8",stroke:"currentColor",strokeWidth:"1.8",strokeLinecap:"round",strokeLinejoin:"round",fill:"none",className:"group-aria-[checked=indeterminate]/checkbox:hidden"})]})})})}function hy(e){return Array.isArray(e)?e.map(t=>hy(t)).join(","):e==null?"":String(e)}const V1=new Map;function X4(e,t){const a=JSON.stringify({locale:hy(e),options:t}),o=V1.get(a);if(o)return o;const c=new Intl.NumberFormat(e,t);return V1.set(a,c),c}function F1(e,t,a){return e==null?"":X4(t,a).format(e)}function Li(e,t=Number.MIN_SAFE_INTEGER,a=Number.MAX_SAFE_INTEGER){return Math.max(t,Math.min(e,a))}function Q4(e,t,a){return(e-t)*100/(a-t)}const $1=x.createContext(void 0);function q1(){const e=x.useContext($1);if(e===void 0)throw new Error(un(51));return e}const Z4="data-complete",W4="data-indeterminate",J4="data-progressing",gy={status(e){return e==="progressing"?{[J4]:""}:e==="complete"?{[Z4]:""}:e==="indeterminate"?{[W4]:""}:null}},eH=x.forwardRef(function(t,a){const{format:o,getAriaValueText:c,locale:d,max:f=100,min:p=0,value:h,render:b,className:y,children:v,style:j,...w}=t,[S,N]=x.useState();let k="indeterminate",A=null,P=null,R="",C="indeterminate progress";if(h!=null&&Number.isFinite(h)){const _=Q4(h,p,f);A=Li(Number.isNaN(_)?0:_,0,100),P=Li(h,p,f),k=P===f?"complete":"progressing",R=o?F1(P,d,o):F1(A/100,d,{style:"percent"}),C=R}const E=x.useMemo(()=>({status:k}),[k]),D={"aria-labelledby":S,"aria-valuemax":f,"aria-valuemin":p,"aria-valuenow":P??void 0,"aria-valuetext":c?c(R,h):C,role:"progressbar",children:s.jsxs(x.Fragment,{children:[v,s.jsx("span",{role:"presentation",style:_i,children:"x"})]})},I=x.useMemo(()=>({formattedValue:R,percentageValue:A,setLabelId:N,state:E,value:h}),[R,A,N,E,h]),O=yt("div",t,{state:E,ref:a,props:[D,w],stateAttributesMapping:gy});return s.jsx($1.Provider,{value:I,children:O})}),tH=x.forwardRef(function(t,a){const{render:o,className:c,style:d,...f}=t,{state:p}=q1();return yt("div",t,{state:p,ref:a,props:f,stateAttributesMapping:gy})}),nH=x.forwardRef(function(t,a){const{render:o,className:c,style:d,...f}=t,{percentageValue:p,state:h}=q1(),b=p==null?{}:{insetInlineStart:0,height:"inherit",width:`${p}%`};return yt("div",t,{state:h,ref:a,props:[{style:b},f],stateAttributesMapping:gy})});function cf({className:e,children:t,value:a,...o}){return s.jsxs(eH,{value:a,"data-slot":"progress",className:Ue("flex w-full flex-wrap items-center gap-3",e),...o,children:[t,s.jsx(sH,{children:s.jsx(rH,{})})]})}function sH({className:e,...t}){return s.jsx(tH,{"data-slot":"progress-track",className:Ue("relative flex h-1.5 w-full items-center overflow-x-hidden overflow-y-hidden rounded-pill bg-surface-3",e),...t})}function rH({className:e,...t}){return s.jsx(nH,{"data-slot":"progress-indicator",className:Ue("h-full rounded-[inherit] bg-primary transition-[width] duration-300 ease-out",e),...t})}const xy=[];let by;function aH(){return by}function lH(e){xy.push(e)}function uh(e){const t=(a,o)=>{const c=Ps(oH).current;let d;try{by=c;for(const f of xy)f.before(c);d=e(a,o);for(const f of xy)f.after(c);c.didInitialize=!0}finally{by=void 0}return d};return t.displayName=e.displayName||e.name,t}function G1(e){return x.forwardRef(uh(e))}function oH(){return{didInitialize:!1}}const K1=x.createContext(void 0);function uf(e){const t=x.useContext(K1);if(t===void 0&&!e)throw new Error(un(72));return t}function iH(e,t){return t!=null&&!po(t)?0:typeof e=="function"?e():e}function zi(e,t,a){const o=iH(e,a);return typeof o=="number"?o:o?.[t]}function Y1(e){return typeof e=="function"?e():e}function X1(e,t){return t||e==="click"||e==="mousedown"}function cH(e){return e?.includes("mouse")&&e!=="mousedown"}const Q1=x.createContext({hasProvider:!1,timeoutMs:0,delayRef:{current:0},initialDelayRef:{current:0},timeout:new la,currentIdRef:{current:null},currentContextRef:{current:null}});function uH(e,t){e.current=t.current}function dH(e){const{children:t,delay:a,timeoutMs:o=0}=e,c=x.useRef(a),d=x.useRef(a),f=x.useRef(null),p=x.useRef(null),h=er();return Me(()=>{if(d.current=a,!f.current){c.current=a;return}c.current={open:zi(c.current,"open"),close:zi(a,"close")}},[a,f,c,d]),s.jsx(Q1.Provider,{value:x.useMemo(()=>({hasProvider:!0,delayRef:c,initialDelayRef:d,currentIdRef:f,timeoutMs:o,currentContextRef:p,timeout:h}),[o,h]),children:t})}function fH(e,t={open:!1}){const{open:a}=t,o="rootStore"in e?e.rootStore:e,c=o.useState("floatingId"),d=x.useContext(Q1),{currentIdRef:f,delayRef:p,timeoutMs:h,initialDelayRef:b,currentContextRef:y,hasProvider:v,timeout:j}=d,[w,S]=x.useState(!1),N=x.useRef(a);return Me(()=>{N.current=a},[a]),Me(()=>{function k(){y.current?.setIsInstantPhase(!1),f.current=null,y.current=null,p.current=b.current,j.clear()}if(f.current&&!a&&f.current===c){if(S(!1),h){const A=c;return j.start(h,()=>{o.select("open")||f.current&&f.current!==A||k()}),()=>{(N.current||f.current!==A)&&j.clear()}}k()}},[a,c,f,p,h,b,y,j,o]),Me(()=>{if(!a)return;const k=y.current,A=f.current;j.clear(),y.current={onOpenChange:o.setOpen,setIsInstantPhase:S},f.current=c,p.current={open:0,close:zi(b.current,"close")},A!==null&&A!==c?(S(!0),k?.setIsInstantPhase(!0),k?.onOpenChange(!1,qe(Qn))):(S(!1),k?.setIsInstantPhase(!1))},[a,c,o,f,p,b,y,j]),Me(()=>()=>{if(f.current===c){if(y.current=null,!N.current)return;f.current=null,uH(p,b),j.clear()}},[y,f,p,c,b,j]),x.useMemo(()=>({activeIdRef:f,hasProvider:v,delayRef:p,isInstantPhase:w}),[f,v,p,w])}function St(e,t,a,o){return e.addEventListener(t,a,o),()=>{e.removeEventListener(t,a,o)}}function Xa(...e){return()=>{for(let t=0;t{t.current=t.next}};return t}const ru=x.forwardRef(function(t,a){const[o,c]=x.useState();Me(()=>{LB&&jl&&c("button")},[]);const d={tabIndex:0,role:o};return s.jsx("span",{...t,ref:a,style:_i,"aria-hidden":o?void 0:!0,...d,"data-base-ui-focus-guard":""})});function df(e){return`data-base-ui-${e}`}let dh=0;function fh(e,t={}){const{preventScroll:a=!1,sync:o=!1,shouldFocus:c}=t;cancelAnimationFrame(dh);function d(){c&&!c()||e?.focus({preventScroll:a})}if(o)return d(),Kt;const f=requestAnimationFrame(d);return dh=f,()=>{dh===f&&(cancelAnimationFrame(f),dh=0)}}const vy={inert:new WeakMap,"aria-hidden":new WeakMap},Z1="data-base-ui-inert",yy={inert:new WeakSet,"aria-hidden":new WeakSet};let ff=new WeakMap,jy=0;function mH(e){return yy[e]}function W1(e){return e?Gc(e)?e.host:W1(e.parentNode):null}const J1=(e,t)=>t.map(a=>{if(e.contains(a))return a;const o=W1(a);return e.contains(o)?o:null}).filter(a=>a!=null),eT=e=>{const t=new Set;return e.forEach(a=>{let o=a;for(;o&&!t.has(o);)t.add(o),o=o.parentNode}),t},tT=(e,t,a)=>{const o=[],c=d=>{!d||a.has(d)||Array.from(d.children).forEach(f=>{Fs(f)!=="script"&&(t.has(f)?c(f):o.push(f))})};return c(e),o};function hH(e,t,a,o,{mark:c=!0}){let d=null;o?d="inert":a&&(d="aria-hidden");let f=null,p=null;const h=J1(t,e),b=c?tT(t,eT(h),new Set(h)):[],y=[],v=[];if(d){const j=vy[d],w=mH(d);p=w,f=j;const S=J1(t,Array.from(t.querySelectorAll("[aria-live]"))),N=h.concat(S);tT(t,eT(N),new Set(N)).forEach(A=>{const P=A.getAttribute(d),R=P!==null&&P!=="false",C=(j.get(A)||0)+1;j.set(A,C),y.push(A),C===1&&R&&w.add(A),R||A.setAttribute(d,d==="inert"?"":"true")})}return c&&b.forEach(j=>{const w=(ff.get(j)||0)+1;ff.set(j,w),v.push(j),w===1&&j.setAttribute(Z1,"")}),jy+=1,()=>{f&&y.forEach(j=>{const S=(f.get(j)||0)-1;f.set(j,S),S||(!p?.has(j)&&d&&j.removeAttribute(d),p?.delete(j))}),c&&v.forEach(j=>{const w=(ff.get(j)||0)-1;ff.set(j,w),w||j.removeAttribute(Z1)}),jy-=1,jy||(vy.inert=new WeakMap,vy["aria-hidden"]=new WeakMap,yy.inert=new WeakSet,yy["aria-hidden"]=new WeakSet,ff=new WeakMap)}}function nT(e,t={}){const{ariaHidden:a=!1,inert:o=!1,mark:c=!0}=t,d=Gt(e[0]).body;return hH(e,d,a,o,{mark:c})}const gH=500,xH={style:{transition:"none"}},sT="data-base-ui-click-trigger",rT={fallbackAxisSide:"none"},aT={fallbackAxisSide:"end"},bH={clipPath:"inset(50%)",position:"fixed",top:0,left:0},lT=x.createContext(null),oT=()=>x.useContext(lT),vH=df("portal");function iT(e={}){const{ref:t,container:a,componentProps:o=mn,elementProps:c}=e,d=Jd(),p=oT()?.portalNode,[h,b]=x.useState(null),[y,v]=x.useState(null),j=Ie(k=>{k!==null&&v(k)}),w=x.useRef(null);Me(()=>{if(a===null){w.current&&(w.current=null,v(null),b(null));return}const k=(a&&(Tv(a)?a:a.current))??p??document.body;if(k==null){w.current&&(w.current=null,v(null),b(null));return}w.current!==k&&(w.current=k,v(null),b(k))},[a,p]);const S=yt("div",o,{ref:[t,j],props:[{id:d,[vH]:""},c]}),N=h&&S?Ts.createPortal(S,h):null;return{node:y,nodeId:x.isValidElement(S)?S.props.id:void 0,subtree:N}}const ph=x.forwardRef(function(t,a){const{render:o,className:c,style:d,children:f,container:p,portalOwnerRole:h,...b}=t,{node:y,nodeId:v,subtree:j}=iT({container:p,ref:a,componentProps:t,elementProps:b}),w=x.useRef(null),S=x.useRef(null),N=x.useRef(null),k=x.useRef(null),[A,P]=x.useState(null),R=x.useRef(!1),C=A?.modal,E=A?.open,D=!!A&&!A.modal&&A.open&&!!y;x.useEffect(()=>{if(!y||C)return;function O(_){y&&_.relatedTarget&&Jc(_)&&(_.type==="focusin"?R.current&&(E1(y),R.current=!1):(b4(y),R.current=!0))}return Xa(St(y,"focusin",O,!0),St(y,"focusout",O,!0))},[y,C]),Me(()=>{!y||E!==!0||!R.current||(E1(y),R.current=!1)},[E,y]);const I=x.useMemo(()=>({beforeOutsideRef:w,afterOutsideRef:S,beforeInsideRef:N,afterInsideRef:k,portalNode:y,setFocusManagerState:P}),[y]);return s.jsxs(x.Fragment,{children:[j,s.jsxs(lT.Provider,{value:I,children:[D&&y&&s.jsx(ru,{"data-type":"outside",ref:w,onFocus:O=>{if(Jc(O,y))N.current?.focus();else{const _=A?A.domReference:null;C1(_)?.focus()}}}),D&&y&&s.jsx("span",{role:h,"aria-owns":v,style:bH}),y&&Ts.createPortal(f,y),D&&y&&s.jsx(ru,{"data-type":"outside",ref:S,onFocus:O=>{if(Jc(O,y))k.current?.focus();else{const _=A?A.domReference:null;iy(_)?.focus(),A?.closeOnFocusOut&&A?.onOpenChange(!1,qe(bo,O.nativeEvent))}}})]})]})});function cT(){const e=new Map;return{emit(t,a){e.get(t)?.forEach(o=>o(a))},on(t,a){e.has(t)||e.set(t,new Set),e.get(t).add(a)},off(t,a){e.get(t)?.delete(a)}}}class yH{constructor(){cn(this,"nodesRef",{current:[]});cn(this,"events",cT())}addNode(t){this.nodesRef.current.push(t)}removeNode(t){const a=this.nodesRef.current.findIndex(o=>o===t);a!==-1&&this.nodesRef.current.splice(a,1)}}const uT=x.createContext(null),dT=x.createContext(null),au=()=>x.useContext(uT)?.id||null,Bi=e=>{const t=x.useContext(dT);return e??t};function jH(e){const t=Jd(),a=Bi(e),o=au();return Me(()=>{if(!t)return;const c={id:t,parentId:o};return a?.addNode(c),()=>{a?.removeNode(c)}},[a,t,o]),t}function wH(e){const{children:t,id:a}=e,o=au();return s.jsx(uT.Provider,{value:x.useMemo(()=>({id:a,parentId:o}),[a,o]),children:t})}function SH(e){const{children:t,externalTree:a}=e,o=Ps(()=>a??new yH).current;return s.jsx(dT.Provider,{value:o,children:t})}function NH(e,t){const a=Tn(fs(e));return e instanceof a.KeyboardEvent?"keyboard":e instanceof a.FocusEvent?t||"keyboard":"pointerType"in e?e.pointerType||"keyboard":"touches"in e?"touch":e instanceof a.MouseEvent?t||(e.detail===0?"keyboard":"mouse"):""}const fT=20;let yo=[];function wy(){yo=yo.filter(e=>e.deref()?.isConnected)}function pT(e){wy(),e&&Fs(e)!=="body"&&(yo.push(new WeakRef(e)),yo.length>fT&&(yo=yo.slice(-fT)))}function mT(){return wy(),yo[yo.length-1]?.deref()}function kH(e){return e?oy(e)?e:sf(e)[0]||e:null}function hT(e){if(e.hasAttribute("tabindex")&&!e.hasAttribute("data-tabindex")||!e.getAttribute("role")?.includes("dialog"))return;const a=N1(e).filter(c=>{const d=c.getAttribute("data-tabindex")||"";return oy(c)||c.hasAttribute("data-tabindex")&&!d.startsWith("-")}),o=e.getAttribute("tabindex");a.length===0?o!=="0"&&(e.setAttribute("tabindex","0"),e.setAttribute("data-tabindex","0")):(o!=="-1"||e.hasAttribute("data-tabindex")&&e.getAttribute("data-tabindex")!=="-1")&&(e.setAttribute("tabindex","-1"),e.setAttribute("data-tabindex","-1"))}function mh(e){const{context:t,children:a,disabled:o=!1,initialFocus:c=!0,returnFocus:d=!0,restoreFocus:f=!1,modal:p=!0,closeOnFocusOut:h=!0,openInteractionType:b="",nextFocusableElement:y,previousFocusableElement:v,beforeContentFocusGuardRef:j,externalTree:w,getInsideElements:S}=e,N="rootStore"in t?t.rootStore:t,k=N.useState("open"),A=N.useState("domReferenceElement"),P=N.useState("floatingElement"),{events:R,dataRef:C}=N.context,E=Ie(()=>C.current.floatingContext?.nodeId),D=c===!1,I=Wv(A)&&D,O=Zn(c),_=Zn(d),z=Zn(b),H=Zn(k),B=Bi(w),L=oT(),F=x.useRef(!1),$=x.useRef(!1),X=x.useRef(!1),K=x.useRef(null),q=x.useRef(""),Q=x.useRef(""),G=x.useRef(null),J=x.useRef(null),Y=sa(G,j,L?.beforeInsideRef),re=sa(J,L?.afterInsideRef),M=er(),U=er(),Z=eu(),ae=L!=null,ce=Wm(P),W=Ie((ee=ce)=>ee?sf(ee):[]),ue=Ie(()=>S?.().filter(ee=>ee!=null)??[]);x.useEffect(()=>{if(o||!p)return;function ee(te){te.key==="Tab"&&Ze(ce,ir(Gt(ce)))&&W().length===0&&!I&&Cs(te)}const he=Gt(ce);return St(he,"keydown",ee)},[o,ce,p,I,W]),x.useEffect(()=>{if(o||!k)return;const ee=Gt(ce);function he(){X.current=!1}function te(le){const de=fs(le),Se=ue(),Te=Ze(P,de)||Ze(A,de)||Ze(L?.portalNode,de)||Se.some(_e=>_e===de||Ze(_e,de));X.current=!Te,Q.current=le.pointerType||"keyboard",de?.closest(`[${sT}]`)&&($.current=!0,U.start(0,()=>{$.current=!1}))}function ke(){Q.current="keyboard"}return Xa(St(ee,"pointerdown",te,!0),St(ee,"pointerup",he,!0),St(ee,"pointercancel",he,!0),St(ee,"keydown",ke,!0),he)},[o,P,A,ce,k,L,U,ue]),x.useEffect(()=>{if(o||!h)return;const ee=Gt(ce);function he(){$.current=!0,U.start(0,()=>{$.current=!1})}function te(Se){const Te=fs(Se);oy(Te)&&(K.current=Te)}function ke(Se){const Te=Se.relatedTarget,_e=Se.currentTarget,Ae=fs(Se);p&&Te==null&&Ae!=null&&Ze(P,Ae)&&pT(Ae),queueMicrotask(()=>{const oe=E(),me=N.context.triggerElements,pe=ue(),Ce=Te?.hasAttribute(df("focus-guard"))&&[G.current,J.current,L?.beforeInsideRef.current,L?.afterInsideRef.current,L?.beforeOutsideRef.current,L?.afterOutsideRef.current,kl(v),kl(y)].includes(Te),Ne=!(Ze(A,Te)||Ze(P,Te)||Ze(Te,P)||Ze(L?.portalNode,Te)||pe.some(be=>be===Te||Ze(be,Te))||me.hasMatchingElement(be=>Ze(be,Te))||Ce||B&&(fo(B.nodesRef.current,oe).find(be=>Ze(be.context?.elements.floating,Te)||Ze(be.context?.elements.domReference,Te))||p1(B.nodesRef.current,oe).find(be=>[be.context?.elements.floating,Wm(be.context?.elements.floating)].includes(Te)||be.context?.elements.domReference===Te)));if(_e===A&&ce&&hT(ce),f&&_e!==A&&!rh(Ae)&&ir(ee)===ee.body){if(pn(ce)&&(ce.focus(),f==="popup")){Z.request(()=>{ce.focus()});return}const be=W(),De=K.current,Ge=(De&&be.includes(De)?De:null)||be[be.length-1]||ce;pn(Ge)&&Ge.focus()}if(C.current.insideReactTree){C.current.insideReactTree=!1;return}(I||!p)&&Te&&Ne&&!$.current&&(I||Te!==mT())&&(F.current=!0,N.setOpen(!1,qe(bo,Se)))})}function le(){X.current||(C.current.insideReactTree=!0,M.start(0,()=>{C.current.insideReactTree=!1}))}const de=pn(A)?A:null;if(!(!P&&!de))return Xa(de&&St(de,"focusout",ke),de&&St(de,"pointerdown",he),P&&St(P,"focusin",te),P&&St(P,"focusout",ke),P&&L&&St(P,"focusout",le,!0))},[o,A,P,ce,p,B,L,N,h,f,W,I,E,C,M,U,Z,y,v,ue]),x.useEffect(()=>{if(o||!P||!k)return;const ee=Array.from(L?.portalNode?.querySelectorAll(`[${df("portal")}]`)||[]),te=(B?p1(B.nodesRef.current,E()):[]).find(_e=>Wv(_e.context?.elements.domReference||null))?.context?.elements.domReference,le=[...[P,...ee,G.current,J.current,L?.beforeOutsideRef.current,L?.afterOutsideRef.current,...ue()],te,kl(v),kl(y),I?A:null].filter(_e=>_e!=null),de=nT(le,{ariaHidden:p||I,mark:!1}),Se=[P,...ee].filter(_e=>_e!=null),Te=nT(Se);return()=>{Te(),de()}},[k,o,A,P,p,L,I,B,E,y,v,ue]),Me(()=>{if(!k||o||!pn(ce))return;q.current="",Q.current="";const ee=Gt(ce),he=ir(ee);queueMicrotask(()=>{const te=O.current,ke=typeof te=="function"?te(z.current||""):te;if(ke===void 0||ke===!1||Ze(ce,he))return;let de=null;const Se=()=>(de==null&&(de=W(ce)),de[0]||ce);let Te;ke===!0||ke===null?Te=Se():Te=kl(ke),Te=Te||Se();const _e=Ze(ce,ir(ee));fh(Te,{preventScroll:Te===ce,shouldFocus(){if(!H.current)return!1;if(_e)return!0;const Ae=ir(ee);return!(Ae!==Te&&Ze(ce,Ae))}})})},[o,k,ce,W,O,z,H]),Me(()=>{if(o||!ce)return;const ee=Gt(ce),he=ir(ee),te=z.current==null;pT(he);function ke(de){if(de.open||(q.current=NH(de.nativeEvent,Q.current)),de.reason===qs&&de.nativeEvent.type==="mouseleave"&&(F.current=!0),de.reason===ch)if(de.nested)F.current=!1;else if(Jm(de.nativeEvent)||Jv(de.nativeEvent))F.current=!1;else{let Se=!1;Gt(ce).createElement("div").focus({get preventScroll(){return Se=!0,!1}}),Se?F.current=!1:F.current=!0}}R.on("openchange",ke);function le(de){const Se=_.current;let Te=typeof Se=="function"?Se(de):Se;if(Te===void 0||Te===!1)return null;Te===null&&(Te=!0);const _e=A?.isConnected?A:null,Ae=he?.isConnected&&Fs(he)!=="body"?he:null;let oe=te?Ae||_e:_e||Ae;return oe||(oe=mT()||null),typeof Te=="boolean"?oe:kl(Te)||oe||null}return()=>{R.off("openchange",ke);const de=ir(ee),Se=ue(),Te=Ze(P,de)||Se.some(me=>me===de||Ze(me,de))||B&&fo(B.nodesRef.current,E(),!1).some(me=>Ze(me.context?.elements.floating,de)),_e=_.current,Ae=q.current,oe=le(Ae);queueMicrotask(()=>{const me=kH(oe),pe=typeof _e!="boolean";if(_e&&!F.current&&pn(me)&&(!(!pe&&me!==de&&de!==ee.body)||Te)){const Ce={preventScroll:!0};Ae==="keyboard"&&(Ce.focusVisible=!0),me.focus(Ce)}F.current=!1})}},[o,P,ce,_,z,R,B,A,E,ue]),Me(()=>{if(!jl||k||!P)return;const ee=ir(Gt(P));!pn(ee)||!Zm(ee)||Ze(P,ee)&&ee.blur()},[k,P]),Me(()=>{if(!(o||!L))return L.setFocusManagerState({modal:p,closeOnFocusOut:h,open:k,onOpenChange:N.setOpen,domReference:A}),()=>{L.setFocusManagerState(null)}},[o,L,p,k,N,h,A]),Me(()=>{if(!(o||!ce))return hT(ce),()=>{queueMicrotask(wy)}},[o,ce]);const ve=!o&&(p?!I:!0)&&(ae||p);return s.jsxs(x.Fragment,{children:[ve&&s.jsx(ru,{"data-type":"inside",ref:Y,onFocus:ee=>{if(p){const he=W();fh(he[he.length-1])}else L?.portalNode&&(F.current=!1,Jc(ee,L.portalNode)?iy(A)?.focus():kl(v??L.beforeOutsideRef)?.focus())}}),a,ve&&s.jsx(ru,{"data-type":"inside",ref:re,onFocus:ee=>{p?fh(W()[0]):L?.portalNode&&(h&&(F.current=!0),Jc(ee,L.portalNode)?C1(A)?.focus():kl(y??L.afterOutsideRef)?.focus())}})]})}function hh(e,t={}){const{enabled:a=!0,event:o="click",toggle:c=!0,ignoreMouse:d=!1,stickIfOpen:f=!0,touchOpenDelay:p=0,reason:h=xo}=t,b="rootStore"in e?e.rootStore:e,y=b.context.dataRef,v=x.useRef(void 0),j=eu(),w=er(),S=x.useMemo(()=>{function N(A,P,R,C){const E=qe(h,P,R);A&&C==="touch"&&p>0?w.start(p,()=>{b.setOpen(!0,E)}):b.setOpen(A,E)}function k(A,P,R){const C=y.current.openEvent,E=b.select("domReferenceElement")!==P;return A&&E||!A||!c?!0:C&&f?!R(C.type):!1}return{onPointerDown(A){v.current=po(A.pointerType,!0)&&Jv(A.nativeEvent)?"virtual":A.pointerType},onMouseDown(A){const P=v.current,R=A.nativeEvent,C=b.select("open");if(A.button!==0||o==="click"||po(P,!0)&&d)return;const E=k(C,A.currentTarget,O=>O==="click"||O==="mousedown"),D=fs(R);if(Zm(D)){N(E,R,D,P);return}const I=A.currentTarget;j.request(()=>{N(E,R,I,P)})},onClick(A){if(o==="mousedown-only")return;const P=v.current;if(o==="mousedown"&&P){v.current=void 0;return}if(po(P,!0)&&d)return;const R=b.select("open"),C=k(R,A.currentTarget,E=>E==="click"||E==="mousedown"||E==="keydown"||E==="keyup");N(C,A.nativeEvent,A.currentTarget,P)},onKeyDown(){v.current=void 0}}},[y,o,d,h,b,f,c,j,w,p]);return x.useMemo(()=>a?{reference:S}:mn,[a,S])}function CH(e,t){let a=null,o=null,c=!1;return{contextElement:e||void 0,getBoundingClientRect(){const d=e?.getBoundingClientRect()||{width:0,height:0,x:0,y:0},f=t.axis==="x"||t.axis==="both",p=t.axis==="y"||t.axis==="both",h=["mouseenter","mousemove"].includes(t.dataRef.current.openEvent?.type||"")&&t.pointerType!=="touch";let b=d.width,y=d.height,v=d.x,j=d.y;return a==null&&t.x&&f&&(a=d.x-t.x),o==null&&t.y&&p&&(o=d.y-t.y),v-=a||0,j-=o||0,b=0,y=0,!c||h?(b=t.axis==="y"?d.width:0,y=t.axis==="x"?d.height:0,v=f&&t.x!=null?t.x:v,j=p&&t.y!=null?t.y:j):c&&!h&&(y=t.axis==="x"?d.height:y,b=t.axis==="y"?d.width:b),c=!0,{width:b,height:y,x:v,y:j,top:j,right:v+b,bottom:j+y,left:v}}}}function gT(e){return e!=null&&e.clientX!=null}function TH(e,t={}){const{enabled:a=!0,axis:o="both"}=t,c="rootStore"in e?e.rootStore:e,d=c.useState("open"),f=c.useState("floatingElement"),p=c.useState("domReferenceElement"),h=c.context.dataRef,b=x.useRef(!1),y=x.useRef(null),[v,j]=x.useState(),[w,S]=x.useState([]),N=Ie(C=>{c.set("positionReference",C)}),k=Ie((C,E,D)=>{b.current||h.current.openEvent&&!gT(h.current.openEvent)||c.set("positionReference",CH(D??p,{x:C,y:E,axis:o,dataRef:h,pointerType:v}))}),A=Ie(C=>{d?y.current||(k(C.clientX,C.clientY,C.currentTarget),S([])):k(C.clientX,C.clientY,C.currentTarget)}),P=po(v)?f:d;x.useEffect(()=>{if(!a){N(p);return}if(!P)return;function C(){y.current?.(),y.current=null}const E=Tn(f);function D(I){const O=fs(I);Ze(f,O)?C():k(I.clientX,I.clientY)}return!h.current.openEvent||gT(h.current.openEvent)?y.current=St(E,"mousemove",D):N(p),C},[P,a,f,h,p,c,k,N,w]),x.useEffect(()=>()=>{c.set("positionReference",null)},[c]),x.useEffect(()=>{a&&!f&&(b.current=!1)},[a,f]),x.useEffect(()=>{!a&&d&&(b.current=!0)},[a,d]);const R=x.useMemo(()=>{function C(E){j(E.pointerType)}return{onPointerDown:C,onPointerEnter:C,onMouseMove:A,onMouseEnter:A}},[A]);return x.useMemo(()=>a?{reference:R,trigger:R}:{},[a,R])}function EH(){return!1}function AH(e){return{escapeKey:typeof e=="boolean"?e:e?.escapeKey??!1,outsidePress:typeof e=="boolean"?e:e?.outsidePress??!0}}function pf(e,t={}){const{enabled:a=!0,escapeKey:o=!0,outsidePress:c=!0,outsidePressEvent:d="sloppy",referencePress:f=EH,bubbles:p,externalTree:h}=t,b="rootStore"in e?e.rootStore:e,y=b.useState("open"),v=b.useState("floatingElement"),{dataRef:j,events:w}=b.context,S=Bi(h),N=Ie(typeof c=="function"?c:()=>!1),k=typeof c=="function"?N:c,A=k!==!1,P=Ie(()=>d),{escapeKey:R,outsidePress:C}=AH(p),E=x.useRef(!1),D=x.useRef(!1),I=x.useRef(!1),O=x.useRef(!1),_=x.useRef(!1),z=x.useRef(""),H=x.useRef(null),B=er(),L=er(),F=Ie(()=>{L.clear(),j.current.insideReactTree=!1}),$=Ie(M=>{const U=j.current.floatingContext?.nodeId;return(S?fo(S.nodesRef.current,U):[]).some(ae=>ae.context?.open&&!ae.context.dataRef.current[M])}),X=Ie(M=>Zv(M,b.select("floatingElement"))||Zv(M,b.select("domReferenceElement"))),K=Ie(M=>{f()&&b.setOpen(!1,qe(xo,M.nativeEvent))}),q=Ie(M=>{if(!y||!a||!o||M.key!=="Escape"||_.current||!R&&$("__escapeKeyBubbles"))return;const U=JB(M)?M.nativeEvent:M,Z=qe(nu,U);b.setOpen(!1,Z),Z.isCanceled||M.preventDefault(),!R&&!Z.isPropagationAllowed&&M.stopPropagation()}),Q=Ie(()=>{j.current.insideReactTree=!0,L.start(0,F)}),G=Ie(M=>{if(!y||!a||M.button!==0)return;const U=fs(M.nativeEvent);Ze(b.select("floatingElement"),U)&&(E.current||(E.current=!0,D.current=!1))}),J=Ie(M=>{!y||!a||(M.defaultPrevented||M.nativeEvent.defaultPrevented)&&E.current&&(D.current=!0)});x.useEffect(()=>{function M(U){U.open||(O.current=!1)}return w.on("openchange",M),()=>{w.off("openchange",M)}},[w]),x.useEffect(()=>{if(!y||!a)return y||(O.current=!1),F;j.current.__escapeKeyBubbles=R,j.current.__outsidePressBubbles=C;const M=new la,U=new la,Z=Gt(v);function ae(){M.clear(),_.current=!0}function ce(){M.start(jl?5:0,()=>{_.current=!1})}function W(){I.current=!0,U.start(0,()=>{I.current=!1})}function ue(){E.current=!1,D.current=!1}function ve(){const Ne=z.current,be=Ne==="pen"||!Ne?"mouse":Ne,De=P(),Ge=typeof De=="function"?De():De;return typeof Ge=="string"?Ge:Ge[be]}function ee(Ne){const be=ve();return be==="intentional"&&Ne.type!=="click"||be==="sloppy"&&Ne.type==="click"}function he(Ne){const be=j.current.floatingContext?.nodeId,De=S&&fo(S.nodesRef.current,be).some(Ge=>Zv(Ne,Ge.context?.elements.floating));return X(Ne)||De}function te(Ne){if(ee(Ne)){Ne.type!=="click"&&!X(Ne)&&(U.clear(),I.current=!1),F();return}if(j.current.insideReactTree){F();return}const be=fs(Ne),De=`[${df("inert")}]`,Ge=Rt(be)?be.getRootNode():null,Ct=Array.from((Gc(Ge)?Ge:Gt(b.select("floatingElement"))).querySelectorAll(De)),Tt=b.context.triggerElements;if(be&&(Tt.hasElement(be)||Tt.hasMatchingElement(ot=>Ze(ot,be))))return;let Mt=Rt(be)?be:null;for(;Mt&&!ao(Mt);){const ot=lo(Mt);if(ao(ot)||!Rt(ot))break;Mt=ot}if(!(Ct.length&&Rt(be)&&!QB(be)&&!Ze(be,b.select("floatingElement"))&&Ct.every(ot=>!Ze(Mt,ot)))){if(pn(be)&&!("touches"in Ne)){const ot=ao(be),Kn=yr(be),ms=/auto|scroll/,Yn=ot||ms.test(Kn.overflowX),vs=ot||ms.test(Kn.overflowY),it=Yn&&be.clientWidth>0&&be.scrollWidth>be.clientWidth,$t=vs&&be.clientHeight>0&&be.scrollHeight>be.clientHeight,Vn=Kn.direction==="rtl",rn=$t&&(Vn?Ne.offsetX<=be.offsetWidth-be.clientWidth:Ne.offsetX>be.clientWidth),nt=it&&Ne.offsetY>be.clientHeight;if(rn||nt)return}if(!he(Ne)){if(ve()==="intentional"){if(Ne.detail!==0&&!Jm(Ne)&&!O.current)return;if(I.current){U.clear(),I.current=!1;return}}typeof k=="function"&&!k(Ne)||$("__outsidePressBubbles")||(b.setOpen(!1,qe(ch,Ne)),F())}}}function ke(Ne){ve()!=="sloppy"||Ne.pointerType==="touch"||!b.select("open")||!a||X(Ne)||te(Ne)}function le(Ne){if(ve()!=="sloppy"||!b.select("open")||!a||X(Ne))return;const be=Ne.touches[0];be&&(H.current={startTime:Date.now(),startX:be.clientX,startY:be.clientY,dismissOnTouchEnd:!1,dismissOnMouseDown:!0},B.start(1e3,()=>{H.current&&(H.current.dismissOnTouchEnd=!1,H.current.dismissOnMouseDown=!1)}))}function de(Ne,be){const De=fs(Ne);if(!De)return;const Ge=St(De,Ne.type,()=>{be(Ne),Ge()})}function Se(Ne){z.current="touch",de(Ne,le)}function Te(Ne){B.clear(),Ne.type==="pointerdown"&&(Ne.button===0&&(O.current=!0),z.current=Ne.pointerType),!(Ne.type==="mousedown"&&H.current&&!H.current.dismissOnMouseDown)&&de(Ne,be=>{be.type==="pointerdown"?ke(be):te(be)})}function _e(Ne){if(Ne.type==="pointercancel"&&(O.current=!1),!E.current)return;const be=D.current;if(ue(),ve()==="intentional"){if(Ne.type==="pointercancel"){be&&W();return}if(!he(Ne)){if(be){W();return}typeof k=="function"&&!k(Ne)||(U.clear(),I.current=!0,F())}}}function Ae(Ne){if(ve()!=="sloppy"||!H.current||X(Ne))return;const be=Ne.touches[0];if(!be)return;const De=Math.abs(be.clientX-H.current.startX),Ge=Math.abs(be.clientY-H.current.startY),Ct=Math.sqrt(De*De+Ge*Ge);Ct>5&&(H.current.dismissOnTouchEnd=!0),Ct>10&&(te(Ne),B.clear(),H.current=null)}function oe(Ne){de(Ne,Ae)}function me(Ne){ve()!=="sloppy"||!H.current||X(Ne)||(H.current.dismissOnTouchEnd&&te(Ne),B.clear(),H.current=null)}function pe(Ne){de(Ne,me)}const Ce=Xa(o&&Xa(St(Z,"keydown",q),St(Z,"compositionstart",ae),St(Z,"compositionend",ce)),A&&Xa(St(Z,"click",Te,!0),St(Z,"pointerdown",Te,!0),St(Z,"pointerup",_e,!0),St(Z,"pointercancel",_e,!0),St(Z,"mousedown",Te,!0),St(Z,"mouseup",_e,!0),St(Z,"touchstart",Se,{capture:!0,passive:!0}),St(Z,"touchmove",oe,{capture:!0,passive:!0}),St(Z,"touchend",pe,{capture:!0,passive:!0})));return()=>{Ce(),M.clear(),U.clear(),ue(),I.current=!1,F()}},[j,v,o,A,k,y,a,R,C,q,F,P,$,X,S,b,B]);const Y=x.useMemo(()=>({onKeyDown:q,onPointerDown:K,onClick:K}),[q,K]),re=x.useMemo(()=>({onKeyDown:q,onPointerDown:J,onMouseDown:J,onClickCapture:Q,onMouseDownCapture(M){Q(),G(M)},onPointerDownCapture(M){Q(),G(M)},onMouseUpCapture:Q,onTouchEndCapture:Q,onTouchMoveCapture:Q}),[q,Q,G,J]);return x.useMemo(()=>a?{reference:Y,floating:re,trigger:Y}:{},[a,Y,re])}function xT(e,t,a){let{reference:o,floating:c}=e;const d=aa(t),f=ny(t),p=ty(f),h=ra(t),b=d==="y",y=o.x+o.width/2-c.width/2,v=o.y+o.height/2-c.height/2,j=o[p]/2-c[p]/2;let w;switch(h){case"top":w={x:y,y:o.y-c.height};break;case"bottom":w={x:y,y:o.y+o.height};break;case"right":w={x:o.x+o.width,y:v};break;case"left":w={x:o.x-c.width,y:v};break;default:w={x:o.x,y:o.y}}const S=Nl(t);return S&&(w[f]+=j*(S==="end"?1:-1)*(a&&b?-1:1)),w}async function RH(e,t){var a;t===void 0&&(t={});const{x:o,y:c,platform:d,rects:f,elements:p,strategy:h}=e,{boundary:b="clippingAncestors",rootBoundary:y="viewport",elementContext:v="floating",altBoundary:j=!1,padding:w=0}=mo(t,e),S=x1(w),k=p[j?v==="floating"?"reference":"floating":v],A=nf(await d.getClippingRect({element:(a=await(d.isElement==null?void 0:d.isElement(k)))==null||a?k:k.contextElement||await(d.getDocumentElement==null?void 0:d.getDocumentElement(p.floating)),boundary:b,rootBoundary:y,strategy:h})),P=v==="floating"?{x:o,y:c,width:f.floating.width,height:f.floating.height}:f.reference,R=await(d.getOffsetParent==null?void 0:d.getOffsetParent(p.floating)),C=await(d.isElement==null?void 0:d.isElement(R))&&await(d.getScale==null?void 0:d.getScale(R))||{x:1,y:1},E=nf(d.convertOffsetParentRelativeRectToViewportRelativeRect?await d.convertOffsetParentRelativeRectToViewportRelativeRect({elements:p,rect:P,offsetParent:R,strategy:h}):P);return{top:(A.top-E.top+S.top)/C.y,bottom:(E.bottom-A.bottom+S.bottom)/C.y,left:(A.left-E.left+S.left)/C.x,right:(E.right-A.right+S.right)/C.x}}const OH=50,MH=async(e,t,a)=>{const{placement:o="bottom",strategy:c="absolute",middleware:d=[],platform:f}=a,p=f.detectOverflow?f:{...f,detectOverflow:RH},h=await(f.isRTL==null?void 0:f.isRTL(t));let b=await f.getElementRects({reference:e,floating:t,strategy:c}),{x:y,y:v}=xT(b,o,h),j=o,w=0;const S={};for(let N=0;NF<=0)){var H,B;const F=(((H=d.flip)==null?void 0:H.index)||0)+1,$=I[F];if($&&(!(v==="alignment"?P!==aa($):!1)||z.every(q=>aa(q.placement)===P?q.overflows[0]>0:!0)))return{data:{index:F,overflows:z},reset:{placement:$}};let X=(B=z.filter(K=>K.overflows[0]<=0).sort((K,q)=>K.overflows[1]-q.overflows[1])[0])==null?void 0:B.placement;if(!X)switch(w){case"bestFit":{var L;const K=(L=z.filter(q=>{if(D){const Q=aa(q.placement);return Q===P||Q==="y"}return!0}).map(q=>[q.placement,q.overflows.filter(Q=>Q>0).reduce((Q,G)=>Q+G,0)]).sort((q,Q)=>q[1]-Q[1])[0])==null?void 0:L[0];K&&(X=K);break}case"initialPlacement":X=p;break}if(c!==X)return{reset:{placement:X}}}return{}}}},bT=new Set(["left","top"]);async function IH(e,t){const{placement:a,platform:o,elements:c}=e,d=await(o.isRTL==null?void 0:o.isRTL(c.floating)),f=ra(a),p=Nl(a),h=aa(a)==="y",b=bT.has(f)?-1:1,y=d&&h?-1:1,v=mo(t,e);let{mainAxis:j,crossAxis:w,alignmentAxis:S}=typeof v=="number"?{mainAxis:v,crossAxis:0,alignmentAxis:null}:{mainAxis:v.mainAxis||0,crossAxis:v.crossAxis||0,alignmentAxis:v.alignmentAxis};return p&&typeof S=="number"&&(w=p==="end"?S*-1:S),h?{x:w*y,y:j*b}:{x:j*b,y:w*y}}const PH=function(e){return e===void 0&&(e=0),{name:"offset",options:e,async fn(t){var a,o;const{x:c,y:d,placement:f,middlewareData:p}=t,h=await IH(t,e);return f===((a=p.offset)==null?void 0:a.placement)&&(o=p.arrow)!=null&&o.alignmentOffset?{}:{x:c+h.x,y:d+h.y,data:{...h,placement:f}}}}},_H=function(e){return e===void 0&&(e={}),{name:"shift",options:e,async fn(t){const{x:a,y:o,placement:c,platform:d}=t,{mainAxis:f=!0,crossAxis:p=!1,limiter:h={fn:P=>{let{x:R,y:C}=P;return{x:R,y:C}}},...b}=mo(e,t),y={x:a,y:o},v=await d.detectOverflow(t,b),j=aa(c),w=ey(j);let S=y[w],N=y[j];const k=(P,R)=>m1(R+v[P==="y"?"top":"left"],R,R-v[P==="y"?"bottom":"right"]);f&&(S=k(w,S)),p&&(N=k(j,N));const A=h.fn({...t,[w]:S,[j]:N});return{...A,data:{x:A.x-a,y:A.y-o,enabled:{[w]:f,[j]:p}}}}}},LH=function(e){return e===void 0&&(e={}),{options:e,fn(t){var a,o;const{x:c,y:d,placement:f,rects:p,middlewareData:h}=t,{offset:b=0,mainAxis:y=!0,crossAxis:v=!0}=mo(e,t),j={x:c,y:d},w=aa(f),S=ey(w);let N=j[S],k=j[w];const A=mo(b,t),P=typeof A=="number"?{mainAxis:A,crossAxis:0}:{mainAxis:(a=A.mainAxis)!=null?a:0,crossAxis:(o=A.crossAxis)!=null?o:0};if(y){const E=S==="y"?"height":"width",D=p.reference[S]-p.floating[E]+P.mainAxis,I=p.reference[S]+p.reference[E]-P.mainAxis;NI&&(N=I)}if(v){var R,C;const E=S==="y"?"width":"height",D=bT.has(ra(f)),I=p.reference[w]-p.floating[E]+(D&&((R=h.offset)==null?void 0:R[w])||0)+(D?0:P.crossAxis),O=p.reference[w]+p.reference[E]+(D?0:((C=h.offset)==null?void 0:C[w])||0)-(D?P.crossAxis:0);kO&&(k=O)}return{[S]:N,[w]:k}}}},zH=function(e){return e===void 0&&(e={}),{name:"size",options:e,async fn(t){const{placement:a,rects:o,platform:c,elements:d}=t,{apply:f=()=>{},...p}=mo(e,t),h=await c.detectOverflow(t,p),b=ra(a),y=Nl(a),v=aa(a)==="y",{width:j,height:w}=o.floating;let S,N;b==="top"||b==="bottom"?(S=b,N=y===(await(c.isRTL==null?void 0:c.isRTL(d.floating))?"start":"end")?"left":"right"):(N=b,S=y==="end"?"top":"bottom");const k=w-h.top-h.bottom,A=j-h.left-h.right,P=Wc(w-h[S],k),R=Wc(j-h[N],A),C=t.middlewareData.shift,E=!C;let D=P,I=R;C!=null&&C.enabled.x&&(I=A),C!=null&&C.enabled.y&&(D=k),E&&!y&&(v?I=j-2*wl(h.left,h.right):D=w-2*wl(h.top,h.bottom)),await f({...t,availableWidth:I,availableHeight:D});const O=await c.getDimensions(d.floating);return j!==O.width||w!==O.height?{reset:{rects:!0}}:{}}}};function vT(e){const t=yr(e);let a=parseFloat(t.width)||0,o=parseFloat(t.height)||0;const c=pn(e),d=c?e.offsetWidth:a,f=c?e.offsetHeight:o,p=eh(a)!==d||eh(o)!==f;return p&&(a=d,o=f),{width:a,height:o,$:p}}function Sy(e){return Rt(e)?e:e.contextElement}function lu(e){const t=Sy(e);if(!pn(t))return Sl(1);const a=t.getBoundingClientRect(),{width:o,height:c,$:d}=vT(t);let f=(d?eh(a.width):a.width)/o,p=(d?eh(a.height):a.height)/c;return(!f||!Number.isFinite(f))&&(f=1),(!p||!Number.isFinite(p))&&(p=1),{x:f,y:p}}const BH=Sl(0);function yT(e){const t=Tn(e);return!Rv()||!t.visualViewport?BH:{x:t.visualViewport.offsetLeft,y:t.visualViewport.offsetTop}}function HH(e,t,a){return t===void 0&&(t=!1),!!a&&t&&a===Tn(e)}function Hi(e,t,a,o){t===void 0&&(t=!1),a===void 0&&(a=!1);const c=e.getBoundingClientRect(),d=Sy(e);let f=Sl(1);t&&(o?Rt(o)&&(f=lu(o)):f=lu(e));const p=HH(d,a,o)?yT(d):Sl(0);let h=(c.left+p.x)/f.x,b=(c.top+p.y)/f.y,y=c.width/f.x,v=c.height/f.y;if(d&&o){const j=Tn(d),w=Rt(o)?Tn(o):o;let S=j,N=Ov(S);for(;N&&w!==S;){const k=lu(N),A=N.getBoundingClientRect(),P=yr(N),R=A.left+(N.clientLeft+parseFloat(P.paddingLeft))*k.x,C=A.top+(N.clientTop+parseFloat(P.paddingTop))*k.y;h*=k.x,b*=k.y,y*=k.x,v*=k.y,h+=R,b+=C,S=Tn(N),N=Ov(S)}}return nf({width:y,height:v,x:h,y:b})}function gh(e,t){const a=Um(e).scrollLeft;return t?t.left+a:Hi(bl(e)).left+a}function jT(e,t){const a=e.getBoundingClientRect(),o=a.left+t.scrollLeft-gh(e,a),c=a.top+t.scrollTop;return{x:o,y:c}}function UH(e){let{elements:t,rect:a,offsetParent:o,strategy:c}=e;const d=c==="fixed",f=bl(o),p=t?Hm(t.floating):!1;if(o===f||p&&d)return a;let h={scrollLeft:0,scrollTop:0},b=Sl(1);const y=Sl(0),v=pn(o);if((v||!d)&&((Fs(o)!=="body"||Qd(f))&&(h=Um(o)),v)){const w=Hi(o);b=lu(o),y.x=w.x+o.clientLeft,y.y=w.y+o.clientTop}const j=f&&!v&&!d?jT(f,h):Sl(0);return{width:a.width*b.x,height:a.height*b.y,x:a.x*b.x-h.scrollLeft*b.x+y.x+j.x,y:a.y*b.y-h.scrollTop*b.y+y.y+j.y}}function VH(e){return e.getClientRects?Array.from(e.getClientRects()):[]}function FH(e){const t=Um(e),a=e.ownerDocument.body,o=wl(e.scrollWidth,e.clientWidth,a.scrollWidth,a.clientWidth),c=wl(e.scrollHeight,e.clientHeight,a.scrollHeight,a.clientHeight);let d=-t.scrollLeft+gh(e);const f=-t.scrollTop;return yr(a).direction==="rtl"&&(d+=wl(e.clientWidth,a.clientWidth)-o),{width:o,height:c,x:d,y:f}}const $H=25;function qH(e,t,a){a===void 0&&(a="viewport");const o=a==="layoutViewport",c=Tn(e),d=bl(e),f=c.visualViewport;let p=d.clientWidth,h=d.clientHeight,b=0,y=0;if(f){const j=!Rv()||t==="fixed";o?j||(b=-f.offsetLeft,y=-f.offsetTop):(p=f.width,h=f.height,j&&(b=f.offsetLeft,y=f.offsetTop))}if(gh(d)<=0){const j=d.ownerDocument,w=j.body,S=getComputedStyle(w),N=j.compatMode==="CSS1Compat"&&parseFloat(S.marginLeft)+parseFloat(S.marginRight)||0,k=Math.abs(d.clientWidth-w.clientWidth-N),A=getComputedStyle(d).scrollbarGutter==="stable both-edges"?k/2:k;A<=$H&&(p-=A)}return{width:p,height:h,x:b,y}}function GH(e,t){const a=Hi(e,!0,t==="fixed"),o=a.top+e.clientTop,c=a.left+e.clientLeft,d=lu(e),f=e.clientWidth*d.x,p=e.clientHeight*d.y,h=c*d.x,b=o*d.y;return{width:f,height:p,x:h,y:b}}function wT(e,t,a){let o;if(t==="viewport"||t==="layoutViewport")o=qH(e,a,t);else if(t==="document")o=FH(bl(e));else if(Rt(t))o=GH(t,a);else{const c=yT(e);o={x:t.x-c.x,y:t.y-c.y,width:t.width,height:t.height}}return nf(o)}function KH(e,t){const a=t.get(e);if(a)return a;let o=Kc(e,[],!1).filter(p=>Rt(p)&&Fs(p)!=="body"),c=null;const d=yr(e).position==="fixed";let f=d?lo(e):e;for(;Rt(f)&&!ao(f);){const p=yr(f),h=Av(f),b=c?c.position:d?"fixed":"";!h&&(b==="fixed"||b==="absolute"&&p.position==="static")?o=o.filter(v=>v!==f):c=p,f=lo(f)}return t.set(e,o),o}function YH(e){let{element:t,boundary:a,rootBoundary:o,strategy:c}=e;const f=[...a==="clippingAncestors"?Hm(t)?[]:KH(t,this._c):[].concat(a),o],p=wT(t,f[0],c);let h=p.top,b=p.right,y=p.bottom,v=p.left;for(let j=1;j{p(!1,1e-7)},1e3)}I=!1}try{o=new IntersectionObserver(O,{...D,root:d.ownerDocument})}catch{o=new IntersectionObserver(O,D)}o.observe(e)}const h=Tn(e),b=()=>p(a);return h.addEventListener("resize",b),p(!0),()=>{h.removeEventListener("resize",b),f()}}function TT(e,t,a,o){o===void 0&&(o={});const{ancestorScroll:c=!0,ancestorResize:d=!0,elementResize:f=typeof ResizeObserver=="function",layoutShift:p=typeof IntersectionObserver=="function",animationFrame:h=!1}=o,b=Sy(e),y=c||d?[...b?Kc(b):[],...t?Kc(t):[]]:[];y.forEach(A=>{c&&A.addEventListener("scroll",a),d&&A.addEventListener("resize",a)});const v=b&&p?JH(b,a,d):null;let j=-1,w=null;f&&(w=new ResizeObserver(A=>{let[P]=A;P&&P.target===b&&w&&t&&(w.unobserve(t),cancelAnimationFrame(j),j=requestAnimationFrame(()=>{var R;(R=w)==null||R.observe(t)})),a()}),b&&!h&&w.observe(b),t&&w.observe(t));let S,N=h?Hi(e):null;h&&k();function k(){const A=Hi(e);N&&!CT(N,A)&&a(),N=A,S=requestAnimationFrame(k)}return a(),()=>{var A;y.forEach(P=>{c&&P.removeEventListener("scroll",a),d&&P.removeEventListener("resize",a)}),v?.(),(A=w)==null||A.disconnect(),w=null,h&&cancelAnimationFrame(S)}}const eU=PH,tU=_H,nU=DH,sU=zH,rU=LH,aU=(e,t,a)=>{const o=new Map,c=a??{},d={...kT,...c.platform,_c:o};return MH(e,t,{...c,platform:d})};var lU=typeof document<"u",oU=function(){},xh=lU?x.useLayoutEffect:oU;function bh(e,t){if(e===t)return!0;if(typeof e!=typeof t)return!1;if(typeof e=="function"&&e.toString()===t.toString())return!0;let a,o,c;if(e&&t&&typeof e=="object"){if(Array.isArray(e)){if(a=e.length,a!==t.length)return!1;for(o=a;o--!==0;)if(!bh(e[o],t[o]))return!1;return!0}if(c=Object.keys(e),a=c.length,a!==Object.keys(t).length)return!1;for(o=a;o--!==0;)if(!{}.hasOwnProperty.call(t,c[o]))return!1;for(o=a;o--!==0;){const d=c[o];if(!(d==="_owner"&&e.$$typeof)&&!bh(e[d],t[d]))return!1}return!0}return e!==e&&t!==t}function ET(e){return typeof window>"u"?1:(e.ownerDocument.defaultView||window).devicePixelRatio||1}function AT(e,t){const a=ET(e);return Math.round(t*a)/a}function ky(e){const t=x.useRef(e);return xh(()=>{t.current=e}),t}function iU(e){e===void 0&&(e={});const{placement:t="bottom",strategy:a="absolute",middleware:o=[],platform:c,elements:{reference:d,floating:f}={},transform:p=!0,whileElementsMounted:h,open:b}=e,[y,v]=x.useState({x:0,y:0,strategy:a,placement:t,middlewareData:{},isPositioned:!1}),[j,w]=x.useState(o);bh(j,o)||w(o);const[S,N]=x.useState(null),[k,A]=x.useState(null),P=x.useCallback(q=>{q!==D.current&&(D.current=q,N(q))},[]),R=x.useCallback(q=>{q!==I.current&&(I.current=q,A(q))},[]),C=d||S,E=f||k,D=x.useRef(null),I=x.useRef(null),O=x.useRef(y),_=h!=null,z=ky(h),H=ky(c),B=ky(b),L=x.useCallback(()=>{if(!D.current||!I.current)return;const q={placement:t,strategy:a,middleware:j};H.current&&(q.platform=H.current),aU(D.current,I.current,q).then(Q=>{const G={...Q,isPositioned:B.current!==!1};F.current&&!bh(O.current,G)&&(O.current=G,Ts.flushSync(()=>{v(G)}))})},[j,t,a,H,B]);xh(()=>{b===!1&&O.current.isPositioned&&(O.current.isPositioned=!1,v(q=>({...q,isPositioned:!1})))},[b]);const F=x.useRef(!1);xh(()=>(F.current=!0,()=>{F.current=!1}),[]),xh(()=>{if(C&&(D.current=C),E&&(I.current=E),C&&E){if(z.current)return z.current(C,E,L);L()}},[C,E,L,z,_]);const $=x.useMemo(()=>({reference:D,floating:I,setReference:P,setFloating:R}),[P,R]),X=x.useMemo(()=>({reference:C,floating:E}),[C,E]),K=x.useMemo(()=>{const q={position:a,left:0,top:0};if(!X.floating)return q;const Q=AT(X.floating,y.x),G=AT(X.floating,y.y);return p?{...q,transform:"translate("+Q+"px, "+G+"px)",...ET(X.floating)>=1.5&&{willChange:"transform"}}:{position:a,left:Q,top:G}},[a,p,X.floating,y.x,y.y]);return x.useMemo(()=>({...y,update:L,refs:$,elements:X,floatingStyles:K}),[y,L,$,X,K])}const cU=(e,t)=>{const a=eU(e);return{name:a.name,fn:a.fn,options:[e,t]}},uU=(e,t)=>{const a=tU(e);return{name:a.name,fn:a.fn,options:[e,t]}},dU=(e,t)=>({fn:rU(e).fn,options:[e,t]}),fU=(e,t)=>{const a=nU(e);return{name:a.name,fn:a.fn,options:[e,t]}},pU=(e,t)=>{const a=sU(e);return{name:a.name,fn:a.fn,options:[e,t]}};var Cy={exports:{}},Ty={};var RT;function mU(){if(RT)return Ty;RT=1;var e=Xd();function t(v,j){return v===j&&(v!==0||1/v===1/j)||v!==v&&j!==j}var a=typeof Object.is=="function"?Object.is:t,o=e.useState,c=e.useEffect,d=e.useLayoutEffect,f=e.useDebugValue;function p(v,j){var w=j(),S=o({inst:{value:w,getSnapshot:j}}),N=S[0].inst,k=S[1];return d(function(){N.value=w,N.getSnapshot=j,h(N)&&k({inst:N})},[v,w,j]),c(function(){return h(N)&&k({inst:N}),v(function(){h(N)&&k({inst:N})})},[v]),f(w),w}function h(v){var j=v.getSnapshot;v=v.value;try{var w=j();return!a(v,w)}catch{return!0}}function b(v,j){return j()}var y=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?b:p;return Ty.useSyncExternalStore=e.useSyncExternalStore!==void 0?e.useSyncExternalStore:y,Ty}var OT;function MT(){return OT||(OT=1,Cy.exports=mU()),Cy.exports}var Ey=MT(),Ay={exports:{}},Ry={};var DT;function hU(){if(DT)return Ry;DT=1;var e=Xd(),t=MT();function a(b,y){return b===y&&(b!==0||1/b===1/y)||b!==b&&y!==y}var o=typeof Object.is=="function"?Object.is:a,c=t.useSyncExternalStore,d=e.useRef,f=e.useEffect,p=e.useMemo,h=e.useDebugValue;return Ry.useSyncExternalStoreWithSelector=function(b,y,v,j,w){var S=d(null);if(S.current===null){var N={hasValue:!1,value:null};S.current=N}else N=S.current;S=p(function(){function A(D){if(!P){if(P=!0,R=D,D=j(D),w!==void 0&&N.hasValue){var I=N.value;if(w(I,D))return C=I}return C=D}if(I=C,o(R,D))return I;var O=j(D);return w!==void 0&&w(I,O)?(R=D,I):(R=D,C=O)}var P=!1,R,C,E=v===void 0?null:v;return[function(){return A(y())},E===null?void 0:function(){return A(E())}]},[y,v,j,w]);var k=c(b,S[0],S[1]);return f(function(){N.hasValue=!0,N.value=k},[k]),h(k),k},Ry}var IT;function gU(){return IT||(IT=1,Ay.exports=hU()),Ay.exports}var xU=gU();const bU=zv(19)?yU:jU;function PT(e,t,a,o,c){return bU(e,t,a,o,c)}function vU(e,t,a,o,c){const d=x.useCallback(()=>t(e.getSnapshot(),a,o,c),[e,t,a,o,c]);return Ey.useSyncExternalStore(e.subscribe,d,d)}lH({before(e){e.syncIndex=0,e.didInitialize||(e.syncTick=1,e.syncHooks=[],e.didChangeStore=!0,e.getSnapshot=()=>{let t=!1;for(let a=0;a0&&(e.didChangeStore&&(e.didChangeStore=!1,e.subscribe=t=>{const a=new Set;for(const c of e.syncHooks)a.add(c.store);const o=[];for(const c of a)o.push(c.subscribe(t));return()=>{for(const c of o)c()}}),Ey.useSyncExternalStore(e.subscribe,e.getSnapshot,e.getSnapshot))}});function yU(e,t,a,o,c){const d=aH();if(!d)return vU(e,t,a,o,c);const f=d.syncIndex;d.syncIndex+=1;let p;return d.didInitialize?(p=d.syncHooks[f],(p.store!==e||p.selector!==t||!Object.is(p.a1,a)||!Object.is(p.a2,o)||!Object.is(p.a3,c))&&(p.store!==e&&(d.didChangeStore=!0),p.store=e,p.selector=t,p.a1=a,p.a2=o,p.a3=c,p.value=t(e.getSnapshot(),a,o,c))):(p={store:e,selector:t,a1:a,a2:o,a3:c,value:t(e.getSnapshot(),a,o,c)},d.syncHooks.push(p)),p.value}function jU(e,t,a,o,c){return xU.useSyncExternalStoreWithSelector(e.subscribe,e.getSnapshot,e.getSnapshot,d=>t(d,a,o,c))}class wU{constructor(t){cn(this,"subscribe",t=>(this.listeners.add(t),()=>{this.listeners.delete(t)}));cn(this,"getSnapshot",()=>this.state);this.state=t,this.listeners=new Set,this.updateTick=0}static create(t){return new this(t)}setState(t){if(this.state===t)return;this.state=t,this.updateTick+=1;const a=this.updateTick;for(const o of this.listeners){if(a!==this.updateTick)return;o(t)}}update(t){for(const a in t)if(!Object.is(this.state[a],t[a])){this.setState({...this.state,...t});return}}set(t,a){Object.is(this.state[t],a)||this.setState({...this.state,[t]:a})}notifyAll(){const t={...this.state};this.setState(t)}use(t,a,o,c){return PT(this,t,a,o,c)}}class ou extends wU{constructor(t,a={},o){super(t),this.context=a,this.selectors=o}useSyncedValue(t,a){x.useDebugValue(t);const o=this;Me(()=>{o.state[t]!==a&&o.set(t,a)},[o,t,a])}useSyncedValueWithCleanup(t,a){const o=this;Me(()=>(o.state[t]!==a&&o.set(t,a),()=>{o.set(t,void 0)}),[o,t,a])}useSyncedValues(t){const a=this,o=Object.values(t);Me(()=>{a.update(t)},[a,...o])}useControlledProp(t,a){x.useDebugValue(t);const o=this,c=a!==void 0;Me(()=>{c&&!Object.is(o.state[t],a)&&o.setState({...o.state,[t]:a})},[o,t,a,c])}select(t,a,o,c){const d=this.selectors[t];return d(this.state,a,o,c)}useState(t,a,o,c){return x.useDebugValue(t),PT(this,this.selectors[t],a,o,c)}useContextCallback(t,a){x.useDebugValue(t);const o=Ie(a??Kt);this.context[t]=o}useStateSetter(t){const a=x.useRef(void 0);return a.current===void 0&&(a.current=o=>{this.set(t,o)}),a.current}observe(t,a){let o;typeof t=="function"?o=t:o=this.selectors[t];let c=o(this.state);return a(c,c,this),this.subscribe(d=>{const f=o(d);if(!Object.is(c,f)){const p=c;c=f,a(f,p,this)}})}}const SU={open:e=>e.open,transitionStatus:e=>e.transitionStatus,domReferenceElement:e=>e.domReferenceElement,referenceElement:e=>e.positionReference??e.referenceElement,floatingElement:e=>e.floatingElement,floatingId:e=>e.floatingId};class Oy extends ou{constructor(a){const{syncOnly:o,nested:c,onOpenChange:d,triggerElements:f,...p}=a;super({...p,positionReference:p.referenceElement,domReferenceElement:p.referenceElement},{onOpenChange:d,dataRef:{current:{}},events:cT(),nested:c,triggerElements:f},SU);cn(this,"syncOpenEvent",(a,o)=>{(!a||!this.state.open||o!=null&&e4(o))&&(this.context.dataRef.current.openEvent=a?o:void 0)});cn(this,"dispatchOpenChange",(a,o)=>{this.syncOpenEvent(a,o.event);const c={open:a,reason:o.reason,nativeEvent:o.event,nested:this.context.nested,triggerElement:o.trigger};this.context.events.emit("openchange",c)});cn(this,"setOpen",(a,o)=>{if(this.syncOnly){this.context.onOpenChange?.(a,o);return}this.dispatchOpenChange(a,o),this.context.onOpenChange?.(a,o)});this.syncOnly=o}}function NU(e){const{popupStore:t,treatPopupAsFloatingElement:a=!1,floatingRootContext:o,floatingId:c,nested:d,onOpenChange:f}=e,p=t.useState("open"),h=t.useState("activeTriggerElement"),b=t.useState(a?"popupElement":"positionerElement"),y=t.context.triggerElements,v=f,j=x.useRef(null);o===void 0&&j.current===null&&(j.current=new Oy({open:p,transitionStatus:void 0,referenceElement:h,floatingElement:b,triggerElements:y,onOpenChange:v,floatingId:c,syncOnly:!0,nested:d}));const w=o??j.current;return t.useSyncedValue("floatingId",c),Me(()=>{const S={open:p,floatingId:c,referenceElement:h,floatingElement:b};Rt(h)&&(S.domReferenceElement=h),w.state.positionReference===w.state.referenceElement&&(S.positionReference=h),w.update(S)},[p,c,h,b,w]),w.context.onOpenChange=v,w.context.nested=d,w}const mf={tabIndex:-1,[qv]:""};function _T(e){return t=>t==="touch"?e.current:!0}function My(e,t=!1){const a=Jd(),o=au()!=null,c=Ps(()=>e(a,o)).current;return NU({popupStore:c,treatPopupAsFloatingElement:t,floatingRootContext:c.state.floatingRootContext,floatingId:a,nested:o,onOpenChange:c.setOpen}),c}function Dy({handle:e,store:t}){return Me(()=>e.attachStore(t),[e,t]),null}function LT(e){const t=e.context.triggerElements.size;e.select("open")&&e.state.triggerCount!==t&&e.set("triggerCount",t)}function kU(e,t){const a=x.useRef(null);return Ie(o=>{const c=a.current;if(c!==null){if(c.element===o&&c.store===t&&c.id===e)return;a.current=null;const d=c.store;d.context.triggerElements.getById(c.id)===c.element&&(d.context.triggerElements.delete(c.id),LT(d))}o!==null&&e!==void 0&&(a.current={store:t,id:e,element:o},t.context.triggerElements.add(e,o),LT(t))})}function Iy(e,t,a,o=!1){let c=e.preventUnmountingOnClose;t?c=!1:o&&(c=!0);const d=a?.id??null;let f=e.activeTriggerId,p=e.activeTriggerElement;return(d||t)&&(f=d,p=a??null),{open:t,preventUnmountingOnClose:c,activeTriggerId:f,activeTriggerElement:p}}function zT(e){let t=!1;return e.preventUnmountOnClose=()=>{t=!0},()=>t}function CU(e,t,a,o={}){const c=a.reason,d=c===qs,f=t&&c===ih,p=!t&&(c===xo||c===nu),h=zT(a);if(e.context.onOpenChange?.(t,a),a.isCanceled)return;o.onBeforeDispatch?.(),e.state.floatingRootContext.dispatchOpenChange(t,a);const b=()=>{const y=Iy(e.state,t,a.trigger,h()),v={...o.extraState,...y};f?v.instantType="focus":p?v.instantType="dismiss":d&&(v.instantType=void 0),e.update(v)};d?Ts.flushSync(b):b()}function BT(e,t,a,o){const c=a.useState("isMountedByTrigger",e),d=kU(e,a),f=Ie(h=>{const b=a.select("open"),y=a.select("activeTriggerId");if(y===e){const v={activeTriggerElement:h,...b?o:null};a.update(v);return}if(y==null&&b){const v={activeTriggerId:e??null,activeTriggerElement:h,...o};a.update(v)}}),p=Ie(h=>{d(h),h&&f(h)});return Me(()=>(p(t.current),()=>p(null)),[p,t,a,e]),Me(()=>{if(c){const h={activeTriggerElement:t.current,...o};a.update(h)}},[c,a,t,...Object.values(o)]),{registerTrigger:p,isMountedByThisTrigger:c}}function Py(e,t={}){const{closeOnActiveTriggerUnmount:a=!1}=t,o=x.useRef(null),c=e.useState("open"),d=e.useState("triggerCount"),f=e.useState("activeTriggerId"),p=e.useState("activeTriggerElement");Me(()=>{if(!c){o.current=null,e.state.triggerCount!==0&&e.set("triggerCount",0);return}const h=e.context.triggerElements.size,b={};e.state.triggerCount!==h&&(b.triggerCount=h);const y=e.select("activeTriggerId");let v=null;if(y){const j=e.context.triggerElements.getById(y);if(j)o.current=y,j!==e.state.activeTriggerElement&&(b.activeTriggerElement=j);else{for(const[w,S]of e.context.triggerElements.entries())if(S===e.state.activeTriggerElement){b.activeTriggerId=w,b.activeTriggerElement=S,o.current=w;break}b.activeTriggerId===void 0&&(o.current===y?v=y:o.current=null)}}else o.current=null;if(!v&&!y&&h===1){const j=e.context.triggerElements.entries().next();if(!j.done){const[w,S]=j.value;b.activeTriggerId=w,b.activeTriggerElement=S,o.current=w}}(b.triggerCount!==void 0||b.activeTriggerId!==void 0||b.activeTriggerElement!==void 0)&&e.update(b),v&&a&&queueMicrotask(()=>{if(e.select("open")&&e.select("activeTriggerId")===v&&!e.context.triggerElements.getById(v)){const j=qe(Qn);e.setOpen(!1,j),j.isCanceled||e.update({activeTriggerId:null,activeTriggerElement:null})}})},[c,e,d,f,p,a])}function _y(e,t,a,o){const{mounted:c,setMounted:d,transitionStatus:f}=Oi(e,!1,!1,o),p=t.useState("preventUnmountingOnClose"),h=e?!1:p;t.useSyncedValues({mounted:c,transitionStatus:f,preventUnmountingOnClose:h});const b=Ie(()=>{d(!1),t.update({activeTriggerId:null,activeTriggerElement:null,mounted:!1,preventUnmountingOnClose:!1}),a?.(),t.context.onOpenChangeComplete?.(!1)});return oa({enabled:c&&!e&&!h,open:e,ref:t.context.popupRef,onComplete(){e||b()}}),{forceUnmount:b,transitionStatus:f}}function Ly(e,t){e.useSyncedValues(t),Me(()=>()=>{e.update({activeTriggerProps:mn,inactiveTriggerProps:mn,popupProps:mn})},[e])}function HT(e,t){Me(()=>{!t&&e.state.openMethod!==null&&e.set("openMethod",null)},[t,e]),Me(()=>()=>{e.state.openMethod!==null&&e.set("openMethod",null)},[e])}class vh{constructor(){this.idMap=new Map}add(t,a){this.idMap.set(t,a)}delete(t){this.idMap.delete(t)}hasElement(t){for(const a of this.idMap.values())if(a===t)return!0;return!1}hasMatchingElement(t){for(const a of this.idMap.values())if(t(a))return!0;return!1}getById(t){return this.idMap.get(t)}entries(){return this.idMap.entries()}elements(){return this.idMap.values()}get size(){return this.idMap.size}}function zy(e,t,a=!1){return{open:!1,openProp:void 0,mounted:!1,transitionStatus:void 0,floatingRootContext:new Oy({open:!1,transitionStatus:void 0,floatingElement:null,referenceElement:null,triggerElements:e,floatingId:t,syncOnly:!0,nested:a,onOpenChange:void 0}),floatingId:t,triggerCount:0,preventUnmountingOnClose:!1,payload:void 0,activeTriggerId:null,activeTriggerElement:null,triggerIdProp:void 0,popupElement:null,positionerElement:null,activeTriggerProps:mn,inactiveTriggerProps:mn,popupProps:mn}}const hf=e=>e.triggerIdProp??e.activeTriggerId,By=e=>e.openProp??e.open,UT=e=>(e.popupElement?.id??e.floatingId)||void 0;function VT(e,t){return t!==void 0&&By(e)&&hf(e)===t}function TU(e,t){return VT(e,t)?!0:t!==void 0&&By(e)&&hf(e)==null&&e.triggerCount===1}const Hy={open:By,mounted:e=>e.mounted,transitionStatus:e=>e.transitionStatus,floatingRootContext:e=>e.floatingRootContext,triggerCount:e=>e.triggerCount,preventUnmountingOnClose:e=>e.preventUnmountingOnClose,payload:e=>e.payload,activeTriggerId:hf,activeTriggerElement:e=>e.mounted?e.activeTriggerElement:null,popupId:UT,isTriggerActive:(e,t)=>t!==void 0&&hf(e)===t,isOpenedByTrigger:(e,t)=>VT(e,t),isMountedByTrigger:(e,t)=>t!==void 0&&hf(e)===t&&e.mounted,triggerProps:(e,t)=>t?e.activeTriggerProps:e.inactiveTriggerProps,triggerPopupId:(e,t)=>TU(e,t)?UT(e):void 0,popupProps:e=>e.popupProps,popupElement:e=>e.popupElement,positionerElement:e=>e.positionerElement};function FT(e){const t=x.useCallback(o=>e===void 0?Kt:e.subscribeStore(o),[e]),a=x.useCallback(()=>e===void 0?void 0:e.store,[e]);return Ey.useSyncExternalStore(t,a,()=>e?.serverStore)}function $T(e){const{open:t=!1,onOpenChange:a,elements:o={}}=e,c=Jd(),d=au()!=null,f=Ps(()=>new Oy({open:t,transitionStatus:void 0,onOpenChange:a,referenceElement:o.reference??null,floatingElement:o.floating??null,triggerElements:new vh,floatingId:c,syncOnly:!1,nested:d})).current;return Me(()=>{const p={open:t,floatingId:c};o.reference!==void 0&&(p.referenceElement=o.reference,p.domReferenceElement=Rt(o.reference)?o.reference:null),o.floating!==void 0&&(p.floatingElement=o.floating),f.update(p)},[t,c,o.reference,o.floating,f]),f.context.onOpenChange=a,f.context.nested=d,f}function EU(e){return AU(e,e.rootContext)}function AU(e,t){const{nodeId:a,externalTree:o}=e,c=t.useState("referenceElement"),d=t.useState("floatingElement"),f=t.useState("domReferenceElement"),p=t.useState("open"),h=t.useState("floatingId"),[b,y]=x.useState(null),[v,j]=x.useState(void 0),[w,S]=x.useState(void 0),N=x.useRef(null),k=Bi(o),A=x.useMemo(()=>({reference:c,floating:d,domReference:f}),[c,d,f]),P=iU({...e,elements:{...A,...b&&{reference:b}}}),R=Rt(v)?v:null,C=w===void 0?t.state.floatingElement:w;t.useSyncedValue("referenceElement",v??null),t.useSyncedValue("domReferenceElement",v===void 0?f:R),t.useSyncedValue("floatingElement",C);const E=x.useCallback(H=>{const B=Rt(H)?{getBoundingClientRect:()=>H.getBoundingClientRect(),getClientRects:()=>H.getClientRects(),contextElement:H}:H;y(B),P.refs.setReference(B)},[P.refs]),D=x.useCallback(H=>{(Rt(H)||H===null)&&(N.current=H,j(H)),(Rt(P.refs.reference.current)||P.refs.reference.current===null||H!==null&&!Rt(H))&&P.refs.setReference(H)},[P.refs,j]),I=x.useCallback(H=>{S(H),P.refs.setFloating(H)},[P.refs]),O=x.useMemo(()=>({...P.refs,setReference:D,setFloating:I,setPositionReference:E,domReference:N}),[P.refs,D,I,E]),_=x.useMemo(()=>({...P.elements,domReference:f}),[P.elements,f]),z=x.useMemo(()=>({...P,dataRef:t.context.dataRef,open:p,onOpenChange:t.setOpen,events:t.context.events,floatingId:h,refs:O,elements:_,nodeId:a,rootStore:t}),[P,O,_,a,t,p,h]);return Me(()=>{f&&(N.current=f)},[f]),Me(()=>{t.context.dataRef.current.floatingContext=z;const H=k?.nodesRef.current.find(B=>B.id===a);H&&(H.context=z)}),x.useMemo(()=>({...P,context:z,refs:O,elements:_,rootStore:t}),[P,O,_,z,t])}const Uy=i1&&jl;function RU(e,t={}){const{enabled:a=!0,delay:o}=t,c="rootStore"in e?e.rootStore:e,{events:d,dataRef:f}=c.context,p=x.useRef(!1),h=x.useRef(null),b=x.useRef(!0),y=er();x.useEffect(()=>{const j=c.select("domReferenceElement");if(!a)return;const w=Tn(j);function S(){const A=c.select("domReferenceElement");!c.select("open")&&pn(A)&&A===ir(Gt(A))&&(p.current=!0,h.current=A)}function N(){b.current=!0}function k(){b.current=!1}return Xa(St(w,"blur",S),Uy&&St(w,"keydown",N,!0),Uy&&St(w,"pointerdown",k,!0))},[c,a]),x.useEffect(()=>{if(!a)return;function j(w){if(w.reason===xo||w.reason===nu){const S=c.select("domReferenceElement");Rt(S)&&(h.current=S,p.current=!0)}}return d.on("openchange",j),()=>{d.off("openchange",j)}},[d,a,c]);const v=x.useMemo(()=>{function j(){p.current=!1,h.current=null}return{onMouseLeave(){j()},onFocus(w){const S=w.currentTarget;if(p.current){if(h.current===S)return;j()}const N=fs(w.nativeEvent);if(Rt(N)){if(Uy&&!w.relatedTarget){if(!b.current&&!Zm(N))return}else if(!WB(N))return}const k=Qm(w.relatedTarget,c.context.triggerElements),{nativeEvent:A,currentTarget:P}=w,R=typeof o=="function"?o():o;if(c.select("open")&&k||R===0||R===void 0){c.setOpen(!0,qe(ih,A,P));return}y.start(R,()=>{p.current||c.setOpen(!0,qe(ih,A,P))})},onBlur(w){j();const S=w.relatedTarget,N=w.nativeEvent,k=Rt(S)&&S.hasAttribute(df("focus-guard"))&&S.getAttribute("data-type")==="outside";y.start(0,()=>{const A=c.select("domReferenceElement"),P=ir(Gt(A));!S&&P===A||Ze(f.current.floatingContext?.refs.floating.current,P)||Ze(A,P)||k||Qm(S??P,c.context.triggerElements)||c.setOpen(!1,qe(ih,N))})}}},[f,o,c,y]);return x.useMemo(()=>a?{reference:v,trigger:v}:{},[a,v])}class Vy{constructor(){cn(this,"dispose",()=>{this.openChangeTimeout.clear(),this.restTimeout.clear()});cn(this,"disposeEffect",()=>this.dispose);this.pointerType=void 0,this.interactedInside=!1,this.handler=void 0,this.blockMouseMove=!0,this.performedPointerEventsMutation=!1,this.pointerEventsScopeElement=null,this.pointerEventsReferenceElement=null,this.pointerEventsFloatingElement=null,this.restTimeoutPending=!1,this.openChangeTimeout=new la,this.restTimeout=new la,this.handleCloseOptions=void 0}static create(){return new Vy}}const yh=new WeakMap;function jh(e){if(!e.performedPointerEventsMutation)return;const t=e.pointerEventsScopeElement;t&&yh.get(t)===e&&(e.pointerEventsScopeElement?.style.removeProperty("pointer-events"),e.pointerEventsReferenceElement?.style.removeProperty("pointer-events"),e.pointerEventsFloatingElement?.style.removeProperty("pointer-events"),yh.delete(t)),e.performedPointerEventsMutation=!1,e.pointerEventsScopeElement=null,e.pointerEventsReferenceElement=null,e.pointerEventsFloatingElement=null}function qT(e,t){const{scopeElement:a,referenceElement:o,floatingElement:c}=t,d=yh.get(a);d&&d!==e&&jh(d),jh(e),e.performedPointerEventsMutation=!0,e.pointerEventsScopeElement=a,e.pointerEventsReferenceElement=o,e.pointerEventsFloatingElement=c,yh.set(a,e),a.style.pointerEvents="none",o.style.pointerEvents="auto",c.style.pointerEvents="auto"}function Fy(e){const t=e.context.dataRef.current,a=Ps(()=>t.hoverInteractionState??Vy.create()).current;return t.hoverInteractionState||(t.hoverInteractionState=a),cy(t.hoverInteractionState.disposeEffect),t.hoverInteractionState}function GT(e,t={}){const{enabled:a=!0,closeDelay:o=0,nodeId:c}=t,d="rootStore"in e?e.rootStore:e,f=d.useState("open"),p=d.useState("floatingElement"),h=d.useState("domReferenceElement"),{dataRef:b}=d.context,y=Bi(),v=au(),j=Fy(d),w=er(),S=Ie(()=>X1(b.current.openEvent?.type,j.interactedInside)),N=Ie(()=>cH(b.current.openEvent?.type)),k=Ie(()=>{jh(j)});Me(()=>{f||(j.pointerType=void 0,j.restTimeoutPending=!1,j.interactedInside=!1,k())},[f,j,k]),x.useEffect(()=>k,[k]),Me(()=>{if(a&&f&&j.handleCloseOptions?.blockPointerEvents&&N()&&Rt(h)&&p){const A=h,P=p,R=Gt(p),C=y?.nodesRef.current.find(O=>O.id===v)?.context?.elements.floating;C&&(C.style.pointerEvents="");const E=j.pointerEventsScopeElement!==P?j.pointerEventsScopeElement:null,D=C!==P?C:null,I=j.handleCloseOptions?.getScope?.()??E??D??A.closest("[data-rootownerid]")??R.body;return qT(j,{scopeElement:I,referenceElement:A,floatingElement:P}),()=>{k()}}},[a,f,h,p,j,N,y,v,k]),x.useEffect(()=>{if(!a)return;function A(){return!!(y&&v&&fo(y.nodesRef.current,v).length>0)}function P(O){const _=zi(o,"close",j.pointerType),z=()=>{d.setOpen(!1,qe(qs,O)),y?.events.emit("floating.closed",O)};_?j.openChangeTimeout.start(_,z):(j.openChangeTimeout.clear(),z())}function R(O){const _=fs(O);if(!ZB(_)){j.interactedInside=!1;return}j.interactedInside=_?.closest("[aria-haspopup]")!=null}function C(){j.openChangeTimeout.clear(),w.clear(),y?.events.off("floating.closed",D),k()}function E(O){if(A()&&y){y.events.on("floating.closed",D);return}if(Qm(O.relatedTarget,d.context.triggerElements))return;const _=b.current.floatingContext?.nodeId??c,z=O.relatedTarget;if(!(y&&_&&Rt(z)&&fo(y.nodesRef.current,_,!1).some(B=>Ze(B.context?.elements.floating,z)))){if(j.handler){j.handler(O);return}k(),N()&&!S()&&P(O)}}function D(O){!y||!v||A()||w.start(0,()=>{y.events.off("floating.closed",D),d.setOpen(!1,qe(qs,O)),y.events.emit("floating.closed",O)})}const I=p;return Xa(I&&St(I,"mouseenter",C),I&&St(I,"mouseleave",E),I&&St(I,"pointerdown",R,!0),()=>{y?.events.off("floating.closed",D)})},[a,p,d,b,o,c,N,S,k,j,y,v,w])}const OU={current:null};function KT(e,t={}){const{enabled:a=!0,delay:o=0,handleClose:c=null,mouseOnly:d=!1,restMs:f=0,move:p=!0,triggerElementRef:h=OU,externalTree:b,isActiveTrigger:y=!0,getHandleCloseContext:v,isClosing:j,shouldOpen:w,guardStaleOpen:S=!1}=t,N="rootStore"in e?e.rootStore:e,{dataRef:k,events:A}=N.context,P=Bi(b),R=Fy(N),C=x.useRef(!1),E=Zn(c),D=Zn(o),I=Zn(f),O=Zn(a),_=Zn(w),z=Zn(j),H=Ie(()=>X1(k.current.openEvent?.type,R.interactedInside)),B=Ie(()=>_.current?.()!==!1),L=Ie((X,K,q)=>{const Q=N.context.triggerElements;if(Q.hasElement(K))return!X||!Ze(X,K);if(!Rt(q))return!1;const G=q;return Q.hasMatchingElement(J=>Ze(J,G))&&(!X||!Ze(X,G))}),F=Ie(()=>{if(!R.handler)return;Gt(N.select("domReferenceElement")).removeEventListener("mousemove",R.handler),R.handler=void 0}),$=Ie(()=>{jh(R)});return y&&(R.handleCloseOptions=c?.__options),x.useEffect(()=>F,[F]),x.useEffect(()=>{if(!a)return;function X(K){K.open?C.current=!1:(C.current=K.reason===qs,F(),R.openChangeTimeout.clear(),R.restTimeout.clear(),R.blockMouseMove=!0,R.restTimeoutPending=!1)}return A.on("openchange",X),()=>{A.off("openchange",X)}},[a,A,R,F]),x.useEffect(()=>{if(!a)return;function X(Y,re=!0){const M=zi(D.current,"close",R.pointerType);M?R.openChangeTimeout.start(M,()=>{N.setOpen(!1,qe(qs,Y)),P?.events.emit("floating.closed",Y)}):re&&(R.openChangeTimeout.clear(),N.setOpen(!1,qe(qs,Y)),P?.events.emit("floating.closed",Y))}const K=h.current??(y?N.select("domReferenceElement"):null);if(!Rt(K))return;function q(Y){if(R.openChangeTimeout.clear(),R.blockMouseMove=!1,d&&!po(R.pointerType))return;const re=Y1(I.current),M=zi(D.current,"open",R.pointerType),U=fs(Y),Z=Y.currentTarget??null,ae=N.select("domReferenceElement");let ce=Z;if(Rt(U)&&!N.context.triggerElements.hasElement(U)){for(const de of N.context.triggerElements.elements())if(Ze(de,U)){ce=de;break}}Rt(Z)&&Rt(ae)&&!N.context.triggerElements.hasElement(Z)&&Ze(Z,ae)&&(ce=ae);const W=ce==null?!1:L(ae,ce,U),ue=N.select("open"),ve=z.current?.()??N.select("transitionStatus")==="ending",ee=!ue&&ve&&C.current,he=!W&&Rt(ce)&&Rt(ae)&&Ze(ae,ce)&&ee,te=re>0&&!M,ke=W&&(ue||ee)||he,le=!ue||W;if(ke){B()&&N.setOpen(!0,qe(qs,Y,ce));return}te||(M?R.openChangeTimeout.start(M,()=>{le&&B()&&N.setOpen(!0,qe(qs,Y,ce))}):le&&B()&&N.setOpen(!0,qe(qs,Y,ce)))}function Q(Y){if(H()){$();return}F();const re=N.select("domReferenceElement"),M=Gt(re);R.restTimeout.clear(),R.restTimeoutPending=!1;const U=k.current.floatingContext??v?.();if(Qm(Y.relatedTarget,N.context.triggerElements))return;if(E.current&&U){N.select("open")||R.openChangeTimeout.clear();const ae=h.current;R.handler=E.current({...U,tree:P,x:Y.clientX,y:Y.clientY,onClose(){$(),F(),O.current&&!H()&&ae===N.select("domReferenceElement")&&X(Y,!0)}}),M.addEventListener("mousemove",R.handler),R.handler(Y);return}(R.pointerType!=="touch"||!Ze(N.select("floatingElement"),Y.relatedTarget))&&X(Y)}function G(Y){Ze(K,Y.relatedTarget)||(R.openChangeTimeout.clear(),R.restTimeout.clear(),R.restTimeoutPending=!1)}const J=S?St(K,"mouseout",G):void 0;return p?Xa(St(K,"mousemove",q,{once:!0}),St(K,"mouseenter",q),St(K,"mouseleave",Q),J):Xa(St(K,"mouseenter",q),St(K,"mouseleave",Q),J)},[F,$,k,D,N,a,E,R,y,L,H,d,p,I,h,P,O,v,z,B,S]),x.useMemo(()=>{if(!a)return;function X(K){R.pointerType=K.pointerType}return{onPointerDown:X,onPointerEnter:X,onMouseMove(K){const{nativeEvent:q}=K,Q=K.currentTarget,G=N.select("domReferenceElement"),J=N.select("open"),Y=L(G,Q,K.target);if(d&&!po(R.pointerType))return;if(J&&Y&&R.handleCloseOptions?.blockPointerEvents){const U=N.select("floatingElement");if(U){const Z=R.handleCloseOptions?.getScope?.()??Q.ownerDocument.body;qT(R,{scopeElement:Z,referenceElement:Q,floatingElement:U})}}const re=Y1(I.current);if(J&&!Y||re===0||!Y&&R.restTimeoutPending&&K.movementX**2+K.movementY**2<2)return;R.restTimeout.clear();function M(){if(R.restTimeoutPending=!1,H())return;const U=N.select("open");!R.blockMouseMove&&(!U||Y)&&B()&&N.setOpen(!0,qe(qs,q,Q))}R.pointerType==="touch"?Ts.flushSync(()=>{M()}):Y&&J?M():(R.restTimeoutPending=!0,R.restTimeout.start(re,M))}}},[a,R,H,L,d,N,I,B])}const MU="Escape";function YT(e){return jl&&e.movementX===0&&e.movementY===0}function wh(e,t,a){switch(e){case"vertical":return t;case"horizontal":return a;default:return t||a}}function Sh(e,t){return wh(t,e===Gv||e===Xm,e===Ei||e===Ai)}function $y(e,t,a){return wh(t,e===Xm,a?e===Ei:e===Ai)||e==="Enter"||e===" "||e===""}function DU(e,t,a){return wh(t,a?e===Ei:e===Ai,e===Xm)}function IU(e,t,a,o){const c=a?e===Ai:e===Ei,d=e===Gv;return t==="both"||t==="horizontal"&&o?e===MU:wh(t,c,d)}function XT(e,t){const{listRef:a,activeIndex:o,onNavigate:c=()=>{},enabled:d=!0,selectedIndex:f=null,allowEscape:p=!1,loopFocus:h=!1,nested:b=!1,rtl:y=!1,virtual:v=!1,focusItemOnOpen:j="auto",focusItemOnHover:w=!0,openOnArrowKeyDown:S=!0,disabledIndices:N=void 0,orientation:k="vertical",parentOrientation:A,id:P,resetOnPointerLeave:R=!0,externalTree:C,grid:E}=t,D=E!=null,I="rootStore"in e?e.rootStore:e,O=I.useState("open"),_=I.useState("floatingElement"),z=I.useState("domReferenceElement"),H=I.context.dataRef,B=Wm(_),L=Wv(z),F=Zn(B),$=au(),X=Bi(C),K=x.useRef(j),q=x.useRef(f??-1),Q=x.useRef(null),G=x.useRef(!0),J=Ie(pe=>{c(q.current===-1?null:q.current,pe)}),Y=x.useRef(!!_),re=x.useRef(O),M=x.useRef(!1),U=x.useRef(!1),Z=x.useRef(null),ae=Zn(N),ce=Zn(O),W=Zn(f),ue=Zn(R),ve=eu(),ee=eu(),he=Ie(()=>{function pe(De){v?X?.events.emit("virtualfocus",De):Z.current=fh(De,{sync:M.current,preventScroll:!0})}const Ce=a.current[q.current],Ne=U.current;Ce&&pe(Ce),(M.current?De=>De():De=>ve.request(De))(()=>{const De=a.current[q.current]||Ce;if(!De)return;Ce||pe(De),Te&&(Ne||!G.current)&&De.scrollIntoView?.({block:"nearest",inline:"nearest"})})});Me(()=>{H.current.orientation=k},[H,k]),Me(()=>{d&&(O&&_?(q.current=f??-1,K.current&&f!=null&&(U.current=!0,J())):Y.current&&(q.current=-1,J()))},[d,O,_,f,J]),Me(()=>{if(d){if(!O){M.current=!1;return}if(_)if(o==null){if(M.current=!1,W.current!=null)return;if(Y.current&&(q.current=-1,he()),(!re.current||!Y.current)&&K.current&&(Q.current!=null||K.current===!0&&Q.current==null)){let pe=0;const Ce=()=>{a.current[0]==null?(pe<2&&(pe?be=>ee.request(be):queueMicrotask)(Ce),pe+=1):(q.current=Q.current==null||$y(Q.current,k,y)||b?sh(a):ry(a),Q.current=null,J())};Ce()}}else ho(a.current,o)||(q.current=o,he(),U.current=!1)}},[d,O,_,o,W,b,a,k,y,J,he,ee]),Me(()=>{if(!d||_||!X||v||!Y.current)return;const pe=X.nodesRef.current,Ce=pe.find(De=>De.id===$)?.context?.elements.floating,Ne=ir(Gt(z??Ce??null)),be=pe.some(De=>De.context&&Ze(De.context.elements.floating,Ne));Ce&&!be&&G.current&&Ce.focus({preventScroll:!0})},[d,_,z,X,$,v]),Me(()=>{re.current=O,Y.current=!!_}),Me(()=>{O||(Q.current=null,K.current=j)},[O,j]);const te=o!=null,ke=Ie(pe=>{if(!ce.current)return;const Ce=a.current.indexOf(pe.currentTarget);Ce!==-1&&(q.current!==Ce||o!==Ce)&&(q.current=Ce,J(pe))}),le=Ie(()=>A??X?.nodesRef.current.find(pe=>pe.id===$)?.context?.dataRef?.current.orientation),de=Ie(()=>sh(a,ae.current)),Se=Ie(pe=>{if(G.current=!1,M.current=!0,pe.which===229||!ce.current&&pe.currentTarget===F.current)return;if(b&&IU(pe.key,k,y,D)){Sh(pe.key,le())||Cs(pe),I.setOpen(!1,qe(fy,pe.nativeEvent)),pn(z)&&(v?X?.events.emit("virtualfocus",z):z.focus());return}const Ce=q.current,Ne=sh(a,N),be=ry(a,N);if(L||(pe.key==="Home"&&(Cs(pe),q.current=Ne,J(pe)),pe.key==="End"&&(Cs(pe),q.current=be,J(pe))),E!=null){const De=E(pe,q.current,a,k,h,y,N,Ne,be);if(De!=null&&(q.current=De,J(pe)),k==="both")return}if(Sh(pe.key,k)){if(Cs(pe),O&&!v&&ir(pe.currentTarget.ownerDocument)===pe.currentTarget){q.current=$y(pe.key,k,y)?Ne:be,J(pe);return}$y(pe.key,k,y)?h?Ce>=be?p&&Ce!==a.current.length?q.current=-1:(M.current=!1,q.current=Ne):q.current=_s(a.current,{startingIndex:Ce,disabledIndices:N}):q.current=Math.min(be,_s(a.current,{startingIndex:Ce,disabledIndices:N})):h?Ce<=Ne?p&&Ce!==-1?q.current=a.current.length:(M.current=!1,q.current=be):q.current=_s(a.current,{startingIndex:Ce,decrement:!0,disabledIndices:N}):q.current=Math.max(Ne,_s(a.current,{startingIndex:Ce,decrement:!0,disabledIndices:N})),ho(a.current,q.current)&&(q.current=-1),J(pe)}}),Te=x.useMemo(()=>({onFocus(Ce){M.current=!0,ke(Ce)},onClick:({currentTarget:Ce})=>Ce.focus({preventScroll:!0}),onMouseMove(Ce){YT(Ce)||(M.current=!0,U.current=!1,w&&ke(Ce))},onPointerLeave(Ce){if(!ce.current||!G.current||Ce.pointerType==="touch")return;M.current=!0;const Ne=Ce.relatedTarget;if(!(!w||a.current.includes(Ne))&&ue.current&&(Z.current?.(),Z.current=null,q.current=-1,J(Ce),!v)){const be=F.current,De=ir(Gt(be));be&&Ze(be,De)&&be.focus({preventScroll:!0})}}}),[ke,ce,F,w,a,J,ue,v]),_e=x.useMemo(()=>v&&O&&te&&{"aria-activedescendant":`${P}-${o}`},[v,O,te,P,o]),Ae=x.useMemo(()=>({...L?{}:_e,onKeyDown(pe){if(pe.key==="Tab"&&pe.shiftKey&&O&&!v){const Ce=fs(pe.nativeEvent);if(Ce&&!Ze(F.current,Ce))return;Cs(pe),I.setOpen(!1,qe(bo,pe.nativeEvent)),pn(z)&&z.focus();return}Se(pe)},onPointerMove(pe){YT(pe)||(G.current=!0)}}),[_e,Se,F,L,I,O,v,z]),oe=x.useMemo(()=>{function pe(be){I.setOpen(!0,qe(fy,be.nativeEvent,be.currentTarget))}function Ce(be){j==="auto"&&Jm(be.nativeEvent)&&(K.current=!v)}function Ne(be){K.current=j,j==="auto"&&Jv(be.nativeEvent)&&(K.current=!0)}return{onKeyDown(be){const De=I.select("open");G.current=!1;const Ge=be.key.startsWith("Arrow"),Ct=DU(be.key,le(),y),Tt=Sh(be.key,k),Mt=(b?Ct:Tt)||be.key==="Enter"||be.key.trim()==="";if(v&&De)return Se(be);if(!(!De&&!S&&Ge)){if(Mt){const ot=Sh(be.key,le());Q.current=b&&ot?null:be.key}if(b){Ct&&(Cs(be),De?(q.current=de(),J(be)):pe(be));return}Tt&&(W.current!=null&&(q.current=W.current),Cs(be),!De&&S?pe(be):Se(be),De&&J(be))}},onFocus(be){I.select("open")&&!v&&(q.current=-1,J(be))},onPointerDown:Ne,onPointerEnter:Ne,onMouseDown:Ce,onClick:Ce}},[Se,j,de,b,J,I,S,k,le,y,W,v]),me=x.useMemo(()=>({..._e,...oe}),[_e,oe]);return x.useMemo(()=>d?{reference:me,floating:Ae,item:Te,trigger:oe}:{},[d,me,Ae,oe,Te])}function QT(e,t){const{listRef:a,elementsRef:o,activeIndex:c,onMatch:d,disabledIndices:f,onTyping:p,enabled:h=!0,resetMs:b=750,selectedIndex:y=null}=t,v="rootStore"in e?e.rootStore:e,j=v.useState("open"),w=er(),S=x.useRef(""),N=x.useRef(y??c??-1),k=x.useRef(null),A=Ie(C=>{function E(F){return o?.current[F]}function D(F){const $=E(F);return $&&!rh($)||$?.matches(":disabled")?!1:f==null||!go(Ir,F,f)}function I(F,$,X=0){if(F.length===0)return-1;const K=(X%F.length+F.length)%F.length,q=$.toLowerCase();for(let Q=0;Q0&&C.key===" "&&(Cs(C),p?.(!0)),S.current.length>0&&S.current[0]!==" "&&I(O,S.current)===-1&&C.key!==" "&&p?.(!1),O==null||C.key.length!==1||C.ctrlKey||C.metaKey||C.altKey)return;j&&C.key!==" "&&(Cs(C),p?.(!0));const _=S.current==="";_&&(N.current=y??c??-1),O.every((F,$)=>F&&D($)?F[0]?.toLowerCase()!==F[1]?.toLowerCase():!0)&&S.current===C.key&&(S.current="",N.current=k.current),S.current+=C.key,w.start(b,()=>{S.current="",N.current=k.current,p?.(!1)});const B=((_?y??c??-1:N.current)??0)+1,L=I(O,S.current,B);L!==-1?(d?.(L),k.current=L):C.key!==" "&&(S.current="",p?.(!1))}),P=Ie(C=>{const E=C.relatedTarget,D=v.select("domReferenceElement"),I=v.select("floatingElement");Ze(D,E)||Ze(I,E)||(w.clear(),S.current="",N.current=k.current,p?.(!1))});Me(()=>{!j&&y!==null||(w.clear(),k.current=null,S.current!==""&&(S.current=""))},[j,y,w]);const R=x.useMemo(()=>({onKeyDown:A,onBlur:P}),[A,P]);return x.useMemo(()=>h?{reference:R,floating:R}:{},[h,R])}const ZT=.1,PU=ZT*ZT,yn=.5;function Nh(e,t,a,o,c,d){return o>=t!=d>=t&&e<=(c-a)*(t-o)/(d-o)+a}function kh(e,t,a,o,c,d,f,p,h,b){let y=!1;return Nh(e,t,a,o,c,d)&&(y=!y),Nh(e,t,c,d,f,p)&&(y=!y),Nh(e,t,f,p,h,b)&&(y=!y),Nh(e,t,h,b,a,o)&&(y=!y),y}function _U(e,t,a){return e>=a.x&&e<=a.x+a.width&&t>=a.y&&t<=a.y+a.height}function Ch(e,t,a,o,c,d){const f=Math.min(a,c),p=Math.max(a,c),h=Math.min(o,d),b=Math.max(o,d);return e>=f&&e<=p&&t>=h&&t<=b}function WT(e={}){const{blockPointerEvents:t=!1}=e,a=new la,o=({x:c,y:d,placement:f,elements:p,onClose:h,nodeId:b,tree:y})=>{const v=f?.split("-")[0];let j=!1,w=null,S=null,N=typeof performance<"u"?performance.now():0;function k(P,R){const C=performance.now(),E=C-N;if(w===null||S===null||E===0)return w=P,S=R,N=C,!1;const D=P-w,I=R-S,O=D*D+I*I,_=E*E*PU;return w=P,S=R,N=C,O<_}function A(){a.clear(),h()}return function(R){a.clear();const C=p.domReference,E=p.floating;if(!C||!E||v==null||c==null||d==null)return;const{clientX:D,clientY:I}=R,O=fs(R),_=R.type==="mouseleave",z=Ze(E,O),H=Ze(C,O);if(z&&(j=!0,!_))return;if(H&&(j=!1,!_)){j=!0;return}if(_&&Rt(R.relatedTarget)&&Ze(E,R.relatedTarget))return;function B(){return!!(y&&fo(y.nodesRef.current,b).length>0)}function L(){B()||A()}if(B())return;const F=C.getBoundingClientRect(),$=E.getBoundingClientRect(),X=c>$.right-$.width/2,K=d>$.bottom-$.height/2,q=$.width>F.width,Q=$.height>F.height,G=(q?F:$).left,J=(q?F:$).right,Y=(Q?F:$).top,re=(Q?F:$).bottom;if(v==="top"&&d>=F.bottom-1||v==="bottom"&&d<=F.top+1||v==="left"&&c>=F.right-1||v==="right"&&c<=F.left+1){L();return}let M=!1;switch(v){case"top":M=Ch(D,I,G,F.top+1,J,$.bottom-1);break;case"bottom":M=Ch(D,I,G,$.top+1,J,F.bottom-1);break;case"left":M=Ch(D,I,$.right-1,re,F.left+1,Y);break;case"right":M=Ch(D,I,F.right-1,re,$.left+1,Y);break}if(M)return;if(j&&!_U(D,I,F)){L();return}if(!_&&k(D,I)){L();return}let U=!1;switch(v){case"top":{const Z=q?yn/2:yn*4,ae=q||X?c+Z:c-Z,ce=q?c-Z:X?c+Z:c-Z,W=d+yn+1,ue=X||q?$.bottom-yn:$.top,ve=X?q?$.bottom-yn:$.top:$.bottom-yn;U=kh(D,I,ae,W,ce,W,$.left,ue,$.right,ve);break}case"bottom":{const Z=q?yn/2:yn*4,ae=q||X?c+Z:c-Z,ce=q?c-Z:X?c+Z:c-Z,W=d-yn,ue=X||q?$.top+yn:$.bottom,ve=X?q?$.top+yn:$.bottom:$.top+yn;U=kh(D,I,ae,W,ce,W,$.left,ue,$.right,ve);break}case"left":{const Z=Q?yn/2:yn*4,ae=Q||K?d+Z:d-Z,ce=Q?d-Z:K?d+Z:d-Z,W=c+yn+1,ue=K||Q?$.right-yn:$.left,ve=K?Q?$.right-yn:$.left:$.right-yn;U=kh(D,I,ue,$.top,ve,$.bottom,W,ae,W,ce);break}case"right":{const Z=Q?yn/2:yn*4,ae=Q||K?d+Z:d-Z,ce=Q?d-Z:K?d+Z:d-Z,W=c-yn,ue=K||Q?$.left+yn:$.right,ve=K?Q?$.left+yn:$.right:$.left+yn;U=kh(D,I,W,ae,W,ce,ue,$.top,ve,$.bottom);break}}U?j||a.start(40,L):L()}};return o.__options={...e,blockPointerEvents:t},o}const LU={...Hy,disabled:e=>e.disabled,instantType:e=>e.instantType,isInstantPhase:e=>e.isInstantPhase,trackCursorAxis:e=>e.trackCursorAxis,disableHoverablePopup:e=>e.disableHoverablePopup,lastOpenChangeReason:e=>e.openChangeReason,closeOnClick:e=>e.closeOnClick,closeDelay:e=>e.closeDelay,adaptiveOrigin:e=>e.adaptiveOrigin};class zU extends ou{constructor(a,o,c){const d=new vh;super(BU(a,d,o,c),HU(d),LU);cn(this,"setOpen",(a,o)=>{CU(this,a,o,{extraState:{openChangeReason:o.reason}})})}cancelPendingOpen(a){this.state.floatingRootContext.dispatchOpenChange(!1,qe(xo,a))}}function BU(e,t,a,o=!1){return{...zy(t,a,o),disabled:!1,instantType:void 0,isInstantPhase:!1,trackCursorAxis:"none",disableHoverablePopup:!1,openChangeReason:null,closeOnClick:!0,closeDelay:0,adaptiveOrigin:void 0,...e}}function HU(e){return{popupRef:x.createRef(),onOpenChange:void 0,onOpenChangeComplete:void 0,triggerElements:e}}const UU=uh(function(t){const{disabled:a=!1,defaultOpen:o=!1,open:c,disableHoverablePopup:d=!1,trackCursorAxis:f="none",actionsRef:p,onOpenChange:h,onOpenChangeComplete:b,handle:y,triggerId:v,defaultTriggerId:j=null,children:w}=t,S=My((H,B)=>new zU({open:o,openProp:c,activeTriggerId:j,triggerIdProp:v},H,B));S.useControlledProp("openProp",c),S.useControlledProp("triggerIdProp",v),S.useContextCallback("onOpenChange",h),S.useContextCallback("onOpenChangeComplete",b);const N=S.useState("open"),k=!a&&N,A=S.useState("activeTriggerId"),P=S.useState("mounted"),R=S.useState("payload");S.useSyncedValues({trackCursorAxis:f,disableHoverablePopup:d,disabled:a}),Py(S,{closeOnActiveTriggerUnmount:!0});const{forceUnmount:C,transitionStatus:E}=_y(k,S),D=S.useState("isInstantPhase"),I=S.useState("instantType"),O=S.useState("lastOpenChangeReason"),_=x.useRef(null);Me(()=>{N&&a&&S.setOpen(!1,qe(k4))},[N,a,S]),Me(()=>{E==="ending"&&O===Qn||E!=="ending"&&D?(I!=="delay"&&(_.current=I),S.set("instantType","delay")):_.current!==null&&(S.set("instantType",_.current),_.current=null)},[E,D,O,I,S]),Me(()=>{k&&A==null&&S.set("payload",void 0)},[S,A,k]),x.useImperativeHandle(p,()=>({unmount:C,close:()=>S.setOpen(!1,qe(py))}),[C,S]);const z=k||P||!a&&f!=="none";return s.jsxs(K1.Provider,{value:S,children:[y&&s.jsx(Dy,{handle:y,store:S}),z&&s.jsx(VU,{store:S,disabled:a,trackCursorAxis:f}),typeof w=="function"?w({payload:R}):w]})});function VU({store:e,disabled:t,trackCursorAxis:a}){const o=e.useState("floatingRootContext"),c=pf(o,{enabled:!t,referencePress:()=>e.select("closeOnClick")}),d=TH(o,{enabled:!t&&a!=="none",axis:a==="none"?void 0:a}),f=x.useMemo(()=>ta(d.reference,c.reference),[d.reference,c.reference]);return Ly(e,{activeTriggerProps:f,inactiveTriggerProps:f,popupProps:c.floating??mn}),null}const JT=x.createContext(void 0);function FU(){return x.useContext(JT)}const $U=600,eE="data-base-ui-tooltip-trigger";function tE(e){if("composedPath"in e){const a=e.composedPath();for(let o=0;ok.select("transitionStatus")==="ending",shouldOpen(){return!G.current}}),ae=RU(C,{enabled:!X}).reference,ce=he=>{const te=G.current,ke=tE(he),le=U(ke),de=E.current,Se=de&&ke&&Ze(de,ke);if(le&&k.select("open")&&k.select("lastOpenChangeReason")===qs){k.setOpen(!1,qe(qs,he));return}if(te&&!le&&Se&&!K.current&&!k.select("open")&&de&&po(Y.current)){const Te=()=>{!G.current&&!K.current&&!k.select("open")&&k.setOpen(!0,qe(qs,he,de))},_e=re();_e===0?(J.clear(),Te()):J.start(_e,Te)}},W=k.useState("triggerProps",O);return yt("button",t,{state:{open:R},ref:[a,I,E],props:[Z,ae,O||q!=="none"?W:void 0,{onMouseOver(he){ce(he.nativeEvent)},onFocus(he){M(tE(he.nativeEvent))&&he.preventBaseUIHandler()},onMouseLeave(){G.current=!1,J.clear(),Y.current=void 0},onPointerEnter(he){Y.current=he.pointerType},onPointerDown(he){Y.current=he.pointerType,k.set("closeOnClick",y),y&&!k.select("open")&&k.cancelPendingOpen(he.nativeEvent)},onClick(he){y&&!k.select("open")&&k.cancelPendingOpen(he.nativeEvent)},id:A,[Qv]:X?"":void 0,[eE]:X?void 0:""},w],stateAttributesMapping:Kv})}),nE=x.createContext(void 0);function KU(){const e=x.useContext(nE);if(e===void 0)throw new Error(un(70));return e}const YU=x.forwardRef(function(t,a){const{children:o,container:c,className:d,render:f,style:p,...h}=t,{node:b,subtree:y}=iT({container:c,ref:a,componentProps:t,elementProps:h});return!y&&!b?null:s.jsxs(x.Fragment,{children:[y,b&&Ts.createPortal(o,b)]})}),XU=x.forwardRef(function(t,a){const{keepMounted:o=!1,...c}=t;return uf().useState("mounted")||o?s.jsx(nE.Provider,{value:o,children:s.jsx(YU,{ref:a,...c})}):null}),sE=x.createContext(void 0);function rE(){const e=x.useContext(sE);if(e===void 0)throw new Error(un(71));return e}const QU=x.createContext(void 0);function gf(){return x.useContext(QU)?.direction??"ltr"}const ZU=e=>({name:"arrow",options:e,async fn(t){const{x:a,y:o,placement:c,rects:d,platform:f,elements:p,middlewareData:h}=t,{element:b,padding:y=0,offsetParent:v="real"}=mo(e,t)||{};if(b==null)return{};const j=x1(y),w={x:a,y:o},S=ny(c),N=ty(S),k=await f.getDimensions(b),A=S==="y",P=A?"top":"left",R=A?"bottom":"right",C=A?"clientHeight":"clientWidth",E=d.reference[N]+d.reference[S]-w[S]-d.floating[N],D=w[S]-d.reference[S],I=v==="real"?await f.getOffsetParent?.(b):p.floating;let O=p.floating[C]||d.floating[N];(!O||!await f.isElement?.(I))&&(O=p.floating[C]||d.floating[N]);const _=E/2-D/2,z=O/2-k[N]/2-1,H=Math.min(j[P],z),B=Math.min(j[R],z),L=H,F=O-k[N]-B,$=O/2-k[N]/2+_,X=m1(L,$,F),K=!h.arrow&&Nl(c)!=null&&$!==X&&d.reference[N]/2-(${const{name:a,fn:o}=ZU(e);return{name:a,fn:o,options:[e,t]}},JU={name:"hide",async fn(e){const{width:t,height:a,x:o,y:c}=e.rects.reference,d=t===0&&a===0&&o===0&&c===0,f=await e.platform.detectOverflow(e,{elementContext:"reference"});return{data:{referenceHidden:f.top-a>=0||f.right-t>=0||f.bottom-a>=0||f.left-t>=0||d}}}},eV={sideX:"left",sideY:"top"},tV="--available-width",nV="--available-height",sV="--anchor-width",rV="--anchor-height",aV="--transform-origin",aE=tV,lE=nV;function oE(e,t,a){const o=e==="inline-start"||e==="inline-end";return{top:"top",right:o?a?"inline-start":"inline-end":"right",bottom:"bottom",left:o?a?"inline-end":"inline-start":"left"}[t]}function iE(e,t,a){const{rects:o,placement:c}=e;return{side:oE(t,ra(c),a),align:Nl(c)||"center",anchor:{width:o.reference.width,height:o.reference.height},positioner:{width:o.floating.width,height:o.floating.height}}}function Th(e){return lV(e,EU)}function lV(e,t){const{anchor:a,positionMethod:o="absolute",side:c="bottom",sideOffset:d=0,align:f="center",alignOffset:p=0,collisionBoundary:h,collisionPadding:b=5,sticky:y=!1,arrowPadding:v=5,disableAnchorTracking:j=!1,inline:w,keepMounted:S=!1,floatingRootContext:N,mounted:k,collisionAvoidance:A,shift:P,nodeId:R,adaptiveOrigin:C,lazyFlip:E=!1,externalTree:D}=e,[I,O]=x.useState(null);!k&&I!==null&&O(null);const _=A.side||"flip",z=A.align||"flip",H=A.fallbackAxisSide||"end",B=P?.crossAxis??!1,L=P?.rootBoundary,F=typeof a=="function"?a:void 0,$=Ie(F),X=F?$:a,K=Zn(a),q=Zn(k),G=gf()==="rtl",J=I||{top:"top",right:"right",bottom:"bottom",left:"left","inline-end":G?"left":"right","inline-start":G?"right":"left"}[c],Y=f==="center"?J:`${J}-${f}`;let re=b;typeof re=="number"?re={top:re,right:re,bottom:re,left:re}:re&&(re={top:re.top||0,right:re.right||0,bottom:re.bottom||0,left:re.left||0});const M=1,U=c==="bottom"?M:0,Z=c==="top"?M:0,ae=c==="right"?M:0,ce=c==="left"?M:0,W={boundary:h==="clipping-ancestors"?"clippingAncestors":h,padding:re},ue=x.useRef(null),ve=Zn(d),ee=Zn(p),he=typeof d!="function"?d:0,te=typeof p!="function"?p:0,ke=[];w&&ke.push(w),ke.push(cU(nt=>{const Dt=iE(nt,c,G),qt=typeof ve.current=="function"?ve.current(Dt):ve.current,es=typeof ee.current=="function"?ee.current(Dt):ee.current;return{mainAxis:qt,crossAxis:es,alignmentAxis:es}},[he,te,G,c]));const le=z==="none"&&_!=="shift",de=!le&&(y||B||_==="shift"),Se=_==="none"?null:fU({...W,padding:{top:re.top+M+U,right:re.right+M+ce,bottom:re.bottom+M+Z,left:re.left+M+ae},mainAxis:!B&&_==="flip",crossAxis:z==="flip"?"alignment":!1,fallbackAxisSideDirection:H}),Te=le?null:uU({...W,rootBoundary:L,mainAxis:z!=="none",crossAxis:de,limiter:y||B?void 0:dU(nt=>{if(!ue.current)return{};const{width:Dt,height:qt}=ue.current.getBoundingClientRect(),es=aa(ra(nt.placement)),Fn=es==="y"?Dt:qt,$n=es==="y"?re.left+re.right:re.top+re.bottom;return{offset:Fn/2+$n/2}})},[W,y,B,L,re,z]);_==="shift"||z==="shift"||f==="center"?ke.push(Te,Se):ke.push(Se,Te),ke.push(pU({...W,apply({elements:{floating:nt},availableWidth:Dt,availableHeight:qt,rects:es}){if(!q.current)return;const Fn=nt.style;Fn.setProperty(aE,`${Dt}px`),Fn.setProperty(lE,`${qt}px`);const $n=Tn(nt).devicePixelRatio||1,{x:Dn,y:en,width:cs,height:Et}=es.reference,xt=(Math.round((Dn+cs)*$n)-Math.round(Dn*$n))/$n,jt=(Math.round((en+Et)*$n)-Math.round(en*$n))/$n;Fn.setProperty(sV,`${xt}px`),Fn.setProperty(rV,`${jt}px`)}}),WU(nt=>({element:ue.current||Gt(nt.elements.floating).createElement("div"),padding:ue.current?v:0,offsetParent:"floating"}),[v]),{name:"transformOrigin",fn(nt){const{elements:{floating:Dt},middlewareData:qt,placement:es,platform:Fn,rects:$n,y:Dn}=nt,en=ra(es),cs=Nl(es),Et=aa(en)==="y",xt=ue.current,jt=typeof d=="function"?d(iE(nt,c,G)):d;let wt;if(!xt&&cs&&Math.abs(Et?qt.shift?.x||0:qt.shift?.y||0)<=1)wt=cs==="start"===(Et&&Fn.isRTL?.(Dt)===!0)?"100%":"0%";else{const ar=Et?qt.arrow?.x||0:qt.arrow?.y||0,Fr=Et?xt?.clientWidth||0:xt?.clientHeight||0;wt=`${ar+Fr/2}px`}let Tr=en==="top"||en==="left"?`calc(100% + ${jt}px)`:`${-jt}px`;return de&&Et&&Math.abs(qt.shift?.y||0)>jt&&(Tr=`${$n.reference.y+$n.reference.height/2-Dn}px`),Dt.style.setProperty(aV,Et?`${wt} ${Tr}`:`${Tr} ${wt}`),{}}},JU,C),Me(()=>{!k&&N&&N.update({referenceElement:null,floatingElement:null,domReferenceElement:null,positionReference:null})},[k,N]);const _e=x.useMemo(()=>({ancestorScroll:!j,elementResize:!j&&typeof ResizeObserver<"u",layoutShift:!j&&typeof IntersectionObserver<"u"}),[j]),{refs:Ae,elements:oe,x:me,y:pe,middlewareData:Ce,update:Ne,placement:be,context:De,isPositioned:Ge,floatingStyles:Ct}=t({rootContext:N,open:S?k:void 0,placement:Y,middleware:ke,strategy:o,whileElementsMounted:S?void 0:(...nt)=>TT(...nt,_e),nodeId:R,externalTree:D}),{sideX:Tt,sideY:Mt}=Ce.adaptiveOrigin||eV,ot=Ge?o:"fixed",Kn=x.useMemo(()=>{let nt;return Ge?C?nt={position:ot,[Tt]:me,[Mt]:pe}:nt={...Ct,position:ot}:nt={position:ot,top:0,left:0},nt[aE]="100vw",nt[lE]="100vh",Ge||(nt.opacity=0),nt},[C,ot,Tt,me,Mt,pe,Ct,Ge]),ms=x.useRef(null);Me(()=>{if(!k)return;const nt=K.current,Dt=typeof nt=="function"?nt():nt,es=(cE(Dt)?Dt.current:Dt)||null||null;es!==ms.current&&(Ae.setPositionReference(es),ms.current=es)},[k,Ae,X,K]),x.useEffect(()=>{if(!k)return;const nt=K.current;typeof nt!="function"&&cE(nt)&&nt.current!==ms.current&&(Ae.setPositionReference(nt.current),ms.current=nt.current)},[k,Ae,X,K]),x.useEffect(()=>{if(S&&k&&oe.reference&&oe.floating)return TT(oe.reference,oe.floating,Ne,_e)},[S,k,oe,Ne,_e]);const Yn=ra(be),vs=oE(c,Yn,G),it=Nl(be)||"center",$t=!!Ce.hide?.referenceHidden;Me(()=>{E&&k&&Ge&&Yn!==J&&O(Yn)},[E,k,Ge,Yn,J]);const Vn=x.useMemo(()=>({position:"absolute",top:Ce.arrow?.y,left:Ce.arrow?.x}),[Ce.arrow]),rn=Ce.arrow?.centerOffset!==0;return x.useMemo(()=>({positionerStyles:Kn,arrowStyles:Vn,arrowRef:ue,arrowUncentered:rn,side:vs,align:it,physicalSide:Yn,anchorHidden:$t,refs:Ae,context:De,isPositioned:Ge,update:Ne}),[Kn,Vn,ue,rn,vs,it,Yn,$t,Ae,De,Ge,Ne])}function cE(e){return e!=null&&"current"in e}function xf(e){return e==="starting"?xH:mn}function Eh(e,t,{styles:a,transitionStatus:o,props:c,refs:d,hidden:f,inert:p=!1}){const h={...a};return p&&(h.pointerEvents="none"),yt("div",e,{state:t,ref:d,props:[{role:"presentation",hidden:f,style:h},xf(o),c],stateAttributesMapping:Zc})}const oV=x.forwardRef(function(t,a){const{render:o,className:c,anchor:d,positionMethod:f="absolute",side:p="top",align:h="center",sideOffset:b=0,alignOffset:y=0,collisionBoundary:v="clipping-ancestors",collisionPadding:j=5,arrowPadding:w=5,sticky:S=!1,disableAnchorTracking:N=!1,collisionAvoidance:k=aT,style:A,...P}=t,R=uf(),C=KU(),E=R.useState("open"),D=R.useState("mounted"),I=R.useState("trackCursorAxis"),O=R.useState("disableHoverablePopup"),_=R.useState("floatingRootContext"),z=R.useState("instantType"),H=R.useState("transitionStatus"),B=R.useState("adaptiveOrigin"),L=Th({anchor:d,positionMethod:f,floatingRootContext:_,mounted:D,side:p,sideOffset:b,align:h,alignOffset:y,collisionBoundary:v,collisionPadding:j,sticky:S,arrowPadding:w,disableAnchorTracking:N,keepMounted:C,collisionAvoidance:k,adaptiveOrigin:B}),F=x.useMemo(()=>({open:E,side:L.side,align:L.align,anchorHidden:L.anchorHidden,instant:I!=="none"?"tracking-cursor":z}),[E,L.side,L.align,L.anchorHidden,I,z]),$=Eh(t,F,{styles:L.positionerStyles,transitionStatus:H,props:P,refs:[a,R.useStateSetter("positionerElement")],hidden:!D,inert:!E||I==="both"||O});return s.jsx(sE.Provider,{value:L,children:$})}),iV=x.forwardRef(function(t,a){const{render:o,className:c,style:d,...f}=t,p=uf(),{side:h,align:b}=rE(),y=p.useState("open"),v=p.useState("instantType"),j=p.useState("transitionStatus"),w=p.useState("popupProps"),S=p.useState("floatingRootContext"),N=p.useState("disabled"),k=p.useState("closeDelay");oa({open:y,ref:p.context.popupRef,onComplete(){y&&p.context.onOpenChangeComplete?.(!0)}}),GT(S,{enabled:!N,closeDelay:k});const A=p.useStateSetter("popupElement");return yt("div",t,{state:{open:y,side:h,align:b,instant:v,transitionStatus:j},ref:[a,p.context.popupRef,A],props:[mf,w,xf(j),f],stateAttributesMapping:Xv})}),cV=x.forwardRef(function(t,a){const{render:o,className:c,style:d,...f}=t,p=uf(),{arrowRef:h,side:b,align:y,arrowUncentered:v,arrowStyles:j}=rE(),w=p.useState("open"),S=p.useState("instantType");return yt("div",t,{state:{open:w,side:b,align:y,uncentered:v,instant:S},ref:[a,h],props:[{style:j,"aria-hidden":!0},f],stateAttributesMapping:Zc})}),uV=function(t){const{delay:a,closeDelay:o,timeout:c=400}=t,d=x.useMemo(()=>({open:a,close:o}),[a,o]);return s.jsx(JT.Provider,{value:a,children:s.jsx(dH,{delay:d,timeoutMs:c,children:t.children})})};function Ah(e){return zv(19)?e:e?"true":void 0}function dV(e){const[t,a]=x.useState({current:e,previous:null});return Object.is(e,t.current)||a({current:e,previous:t.current}),t.previous}function fV(e){return s.jsx(uV,{"data-slot":"tooltip-provider",...e})}function Ui(e){return s.jsx(UU,{"data-slot":"tooltip",...e})}function Vi(e){return s.jsx(GU,{"data-slot":"tooltip-trigger",...e})}function Fi({className:e,sideOffset:t=8,side:a="top",children:o,...c}){return s.jsx(XU,{children:s.jsx(oV,{sideOffset:t,side:a,className:"z-[35]",children:s.jsx(iV,{"data-slot":"tooltip-content",className:Ue("max-w-[288px] rounded-md bg-primary px-2.5 py-2 text-[11.5px] leading-[1.55] text-primary-on shadow-3","break-words outline-none","transition-[opacity,transform] duration-150 ease-out","data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95","data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",e),...c,children:o})})})}function pV({className:e,...t}){return s.jsx(cV,{className:Ue("size-2 rotate-45 rounded-[1px] bg-primary","data-[side=top]:-bottom-1 data-[side=bottom]:-top-1",e),...t})}const uE=x.createContext(void 0);function iu(e){const t=x.useContext(uE);if(!e&&t===void 0)throw new Error(un(27));return t}const dE=x.forwardRef(function(t,a){const{render:o,className:c,style:d,forceRender:f=!1,...p}=t,h=iu(),b=h.useState("open"),y=h.useState("nested"),v=h.useState("mounted"),j=h.useState("transitionStatus");return yt("div",t,{state:{open:b,transitionStatus:j},ref:[h.context.backdropRef,a],stateAttributesMapping:Xv,props:[{role:"presentation",hidden:!v,style:{userSelect:"none",WebkitUserSelect:"none"}},p],enabled:f||!y})}),fE=x.forwardRef(function(t,a){const{render:o,className:c,style:d,disabled:f=!1,nativeButton:p=!0,...h}=t,b=iu(),y=b.useState("open"),{getButtonProps:v,buttonRef:j}=na({disabled:f,native:p}),w={disabled:f};function S(N){y&&b.setOpen(!1,qe(dy,N.nativeEvent))}return yt("button",t,{state:w,ref:[a,j],props:[{onClick:S},h,v]})}),pE=x.createContext(void 0);function mV(){const e=x.useContext(pE);if(e===void 0)throw new Error(un(26));return e}const qy="ArrowUp",Gy="ArrowDown",Ky="ArrowLeft",Yy="ArrowRight",Xy="Home",Qy="End",Rh=new Set([qy,Gy,Ky,Yy,Xy,Qy]),mE="Shift",hV=[mE,"Control","Alt","Meta"];function gV(e){return pn(e)&&e.tagName==="INPUT"}function hE(e){return!!(gV(e)&&e.selectionStart!=null||pn(e)&&e.tagName==="TEXTAREA")}function gE(e,t,a,o){if(!e||!t||!t.scrollTo)return;let c=e.scrollLeft,d=e.scrollTop;const f=e.clientWidthe.scrollLeft+e.clientWidth-b.scrollPaddingRight?c=h+t.offsetWidth+y.scrollMarginRight-e.clientWidth+b.scrollPaddingRight:h-y.scrollMarginLefte.scrollLeft+e.clientWidth-b.scrollPaddingRight&&(c=h+t.offsetWidth+y.scrollMarginRight-e.clientWidth+b.scrollPaddingRight))}if(p&&o!=="horizontal"){const h=xE(e,t,"top"),b=Oh(e),y=Oh(t);h-y.scrollMarginTope.scrollTop+e.clientHeight-b.scrollPaddingBottom&&(d=h+t.offsetHeight+y.scrollMarginBottom-e.clientHeight+b.scrollPaddingBottom)}e.scrollTo({left:c,top:d,behavior:"auto"})}function xE(e,t,a){const o=a==="left"?"offsetLeft":"offsetTop";let c=0;for(;t.offsetParent&&(c+=t[o],t.offsetParent!==e);)t=t.offsetParent;return c}function Oh(e){const t=getComputedStyle(e);return{scrollMarginTop:parseFloat(t.scrollMarginTop)||0,scrollMarginRight:parseFloat(t.scrollMarginRight)||0,scrollMarginBottom:parseFloat(t.scrollMarginBottom)||0,scrollMarginLeft:parseFloat(t.scrollMarginLeft)||0,scrollPaddingTop:parseFloat(t.scrollPaddingTop)||0,scrollPaddingRight:parseFloat(t.scrollPaddingRight)||0,scrollPaddingBottom:parseFloat(t.scrollPaddingBottom)||0,scrollPaddingLeft:parseFloat(t.scrollPaddingLeft)||0}}const xV="--nested-dialogs",bV="data-nested-dialog-open",vV={...Zc,...uo,nestedDialogOpen(e){return e?{[bV]:""}:null}},bE=x.forwardRef(function(t,a){const{render:o,className:c,style:d,finalFocus:f,initialFocus:p,...h}=t,b=iu(),y=b.useState("descriptionElementId"),v=b.useState("disablePointerDismissal"),j=b.useState("floatingRootContext"),w=b.useState("popupProps"),S=b.useState("modal"),N=b.useState("mounted"),k=b.useState("nested"),A=b.useState("nestedOpenDialogCount"),P=b.useState("open"),R=b.useState("openMethod"),C=b.useState("titleElementId"),E=b.useState("transitionStatus"),D=b.useState("role"),I=j.useState("floatingId");mV(),oa({open:P,ref:b.context.popupRef,onComplete(){P&&b.context.onOpenChangeComplete?.(!0)}});const O=p===void 0?_T(b.context.popupRef):p,_=A>0,z=b.useStateSetter("popupElement"),B=yt("div",t,{state:{open:P,nested:k,transitionStatus:E,nestedDialogOpen:_},props:[w,{id:I,"aria-labelledby":C,"aria-describedby":y,role:D,...mf,hidden:!N,onKeyDown(L){Rh.has(L.key)&&L.stopPropagation()},style:{[xV]:A}},h],ref:[a,b.context.popupRef,z],stateAttributesMapping:vV});return s.jsx(mh,{context:j,openInteractionType:R,disabled:!N,closeOnFocusOut:!v,initialFocus:O,returnFocus:f,modal:S!==!1,restoreFocus:"popup",children:B})}),Mh=x.forwardRef(function(t,a){const{cutout:o,...c}=t;let d;if(o){const f=o.getBoundingClientRect();d=`polygon(0% 0%,100% 0%,100% 100%,0% 100%,0% 0%,${f.left}px ${f.top}px,${f.left}px ${f.bottom}px,${f.right}px ${f.bottom}px,${f.right}px ${f.top}px,${f.left}px ${f.top}px)`}return s.jsx("div",{ref:a,role:"presentation","data-base-ui-inert":"",...c,style:{position:"fixed",inset:0,userSelect:"none",WebkitUserSelect:"none",clipPath:d}})}),vE=x.forwardRef(function(t,a){const{keepMounted:o=!1,...c}=t,d=iu(),f=d.useState("mounted"),p=d.useState("modal"),h=d.useState("open");return f||o?s.jsx(pE.Provider,{value:o,children:s.jsxs(ph,{ref:a,...c,children:[f&&p===!0&&s.jsx(Mh,{ref:d.context.internalBackdropRef,inert:Ah(!h)}),t.children]})}):null});let yE={},jE={},wE="";function Dh(e,t){return Qd(e)?e:t}function SE(e,t,a){return/hidden|clip/.test(e.getComputedStyle(Dh(t,a)).overflowY)}function yV(e){if(typeof document>"u")return!1;const t=Gt(e);return Tn(t).innerWidth-t.documentElement.clientWidth>0}function jV(e){if(!(typeof CSS<"u"&&CSS.supports&&CSS.supports("scrollbar-gutter","stable"))||typeof document>"u")return!1;const a=Gt(e),o=a.documentElement,c=a.body,d=Dh(o,c),f=d.style.overflowY,p=o.style.scrollbarGutter;o.style.scrollbarGutter="stable",d.style.overflowY="scroll";const h=d.offsetWidth;d.style.overflowY="hidden";const b=d.offsetWidth;return d.style.overflowY=f,o.style.scrollbarGutter=p,h===b}function wV(e){const t=Gt(e),a=t.documentElement,o=t.body,c=Dh(a,o),d={overflowY:c.style.overflowY,overflowX:c.style.overflowX};return Object.assign(c.style,{overflowY:"hidden",overflowX:"hidden"}),()=>{Object.assign(c.style,d)}}function SV(e){const t=Gt(e),a=t.documentElement,o=t.body,c=Tn(a);let d=0,f=0,p=!1;const h=Ga.create();if(jl&&(c.visualViewport?.scale??1)!==1)return()=>{};function b(){const w=c.getComputedStyle(a),S=c.getComputedStyle(o),A=(w.scrollbarGutter||"").includes("both-edges")?"stable both-edges":"stable";d=a.scrollTop,f=a.scrollLeft,yE={scrollbarGutter:a.style.scrollbarGutter,overflowY:a.style.overflowY,overflowX:a.style.overflowX},wE=a.style.scrollBehavior,jE={position:o.style.position,height:o.style.height,width:o.style.width,boxSizing:o.style.boxSizing,overflowY:o.style.overflowY,overflowX:o.style.overflowX,scrollBehavior:o.style.scrollBehavior};const P=a.scrollHeight>a.clientHeight,R=a.scrollWidth>a.clientWidth,C=w.overflowY==="scroll"||S.overflowY==="scroll",E=w.overflowX==="scroll"||S.overflowX==="scroll",D=Math.max(0,c.innerWidth-o.clientWidth),I=Math.max(0,c.innerHeight-o.clientHeight),O=parseFloat(S.marginTop)+parseFloat(S.marginBottom),_=parseFloat(S.marginLeft)+parseFloat(S.marginRight),z=Dh(a,o);if(p=jV(e),p){a.style.scrollbarGutter=A,z.style.overflowY="hidden",z.style.overflowX="hidden";return}Object.assign(a.style,{scrollbarGutter:A,overflowY:"hidden",overflowX:"hidden"}),(P||C)&&(a.style.overflowY="scroll"),(R||E)&&(a.style.overflowX="scroll"),Object.assign(o.style,{position:"relative",height:O||I?`calc(100dvh - ${O+I}px)`:"100dvh",width:_||D?`calc(100vw - ${_+D}px)`:"100vw",boxSizing:"border-box",overflowY:"hidden",overflowX:"hidden",scrollBehavior:"unset"}),o.scrollTop=d,o.scrollLeft=f,a.setAttribute("data-base-ui-scroll-locked",""),a.style.scrollBehavior="unset"}function y(){Object.assign(a.style,yE),Object.assign(o.style,jE),p||(a.scrollTop=d,a.scrollLeft=f,a.removeAttribute("data-base-ui-scroll-locked"),a.style.scrollBehavior=wE)}function v(){y(),h.request(b)}b();const j=St(c,"resize",v);return()=>{h.cancel(),y(),typeof c.removeEventListener=="function"&&j()}}class NV{constructor(){cn(this,"lockCount",0);cn(this,"restore",null);cn(this,"timeoutLock",la.create());cn(this,"timeoutUnlock",la.create());cn(this,"release",()=>{this.lockCount-=1,this.lockCount===0&&this.restore&&this.timeoutUnlock.start(0,this.unlock)});cn(this,"unlock",()=>{this.lockCount===0&&this.restore&&(this.restore?.(),this.restore=null)})}acquire(t){return this.lockCount+=1,this.lockCount===1&&this.restore===null&&this.timeoutLock.start(0,()=>this.lock(t)),this.release}lock(t){if(this.lockCount===0||this.restore!==null)return;const a=Gt(t),o=a.documentElement,c=a.body,d=Tn(o);if(SE(d,o,c)){const p=new d.MutationObserver(()=>{SE(d,o,c)||(p.disconnect(),this.restore=null,this.lock(t))}),h={attributes:!0};p.observe(o,h),p.observe(c,h),this.restore=()=>p.disconnect();return}const f=tf||!yV(t);this.restore=f?wV(t):SV(t)}}const kV=new NV;function NE(e=!0,t=null){Me(()=>{if(e)return kV.acquire(t)},[e,t])}function CV({store:e,parentContext:t,isDrawer:a}){const o=e.useState("open"),c=e.useState("disablePointerDismissal"),d=e.useState("modal"),f=e.useState("popupElement"),p=e.useState("floatingRootContext"),[h,b]=x.useState(0),[y,v]=x.useState(0),j=h===0,w=pf(p,{outsidePressEvent(){return e.context.internalBackdropRef.current||e.context.backdropRef.current?"intentional":{mouse:d==="trap-focus"?"sloppy":"intentional",touch:"sloppy"}},outsidePress(S){if(!e.context.outsidePressEnabledRef.current||"button"in S&&S.button!==0)return!1;if("touches"in S){if(S.type==="touchend"){if(S.changedTouches.length!==1||S.touches.length!==0)return!1}else if(S.touches.length!==1)return!1}const N=fs(S);if(j&&!c){if(d){const k=e.context.internalBackdropRef.current,A=e.context.backdropRef.current;return k||A?k===N||A===N||Ze(N,f)&&!N?.hasAttribute("data-base-ui-portal"):!0}return!0}return!1},escapeKey:j});return NE(o&&d===!0,f),e.useContextCallback("onNestedDialogOpen",(S,N)=>{b(S),v(N)}),Me(()=>(t?.onNestedDialogOpen&&(o?t.onNestedDialogOpen(h+1,y+(a?1:0)):t.onNestedDialogOpen(0,0)),()=>{t?.onNestedDialogOpen&&o&&t.onNestedDialogOpen(0,0)}),[a,o,h,y,t]),Ly(e,{activeTriggerProps:w.reference,inactiveTriggerProps:w.trigger,popupProps:w.floating,nestedOpenDialogCount:h,nestedOpenDrawerCount:y}),null}const TV={...Hy,modal:e=>e.modal,nested:e=>e.nested,nestedOpenDialogCount:e=>e.nestedOpenDialogCount,nestedOpenDrawerCount:e=>e.nestedOpenDrawerCount,disablePointerDismissal:e=>e.disablePointerDismissal,openMethod:e=>e.openMethod,descriptionElementId:e=>e.descriptionElementId,titleElementId:e=>e.titleElementId,viewportElement:e=>e.viewportElement,role:e=>e.role};class EV extends ou{constructor(a,o,c){const d=new vh,f=AV(a,d,o,c);super(f,RV(d),TV);cn(this,"setOpen",(a,o)=>{o.preventUnmountOnClose=()=>{this.set("preventUnmountingOnClose",!0)},!a&&o.trigger==null&&this.state.activeTriggerId!=null&&(o.trigger=this.state.activeTriggerElement??void 0),this.context.onOpenChange?.(a,o),!o.isCanceled&&(this.state.floatingRootContext.dispatchOpenChange(a,o),this.update(Iy(this.state,a,o.trigger)))})}}function AV(e,t,a,o=!1){return{...zy(t,a,o),modal:!0,disablePointerDismissal:!1,viewportElement:null,descriptionElementId:void 0,titleElementId:void 0,openMethod:null,nested:!1,nestedOpenDialogCount:0,nestedOpenDrawerCount:0,role:"dialog",...e}}function RV(e){return{popupRef:x.createRef(),backdropRef:x.createRef(),internalBackdropRef:x.createRef(),outsidePressEnabledRef:{current:!0},triggerElements:e,onOpenChange:void 0,onOpenChangeComplete:void 0}}function kE(e,t){const{children:a,open:o,defaultOpen:c=!1,onOpenChange:d,onOpenChangeComplete:f,disablePointerDismissal:p=!1,modal:h=!0,actionsRef:b,handle:y,triggerId:v,defaultTriggerId:j=null}=t,w=e==="drawer",S=e==="alert-dialog",N=S?!0:h,k=S||p,A=S?"alertdialog":"dialog",P=iu(!0),R=P!=null,C={modal:N,disablePointerDismissal:k,nested:R,role:A},E=My((H,B)=>new EV({open:c,openProp:o,activeTriggerId:j,triggerIdProp:v,...C},H,B),!0);E.useControlledProp("openProp",o),E.useControlledProp("triggerIdProp",v),E.useSyncedValues(C),E.useContextCallback("onOpenChange",d),E.useContextCallback("onOpenChangeComplete",f);const D=E.useState("open"),I=E.useState("mounted"),O=E.useState("payload");HT(E,D),Py(E);const{forceUnmount:_}=_y(D,E);x.useImperativeHandle(b,()=>({unmount:_,close:()=>E.setOpen(!1,qe(py))}),[_,E]);const z=D||I;return s.jsxs(uE.Provider,{value:E,children:[y&&s.jsx(Dy,{handle:y,store:E}),z&&s.jsx(CV,{store:E,parentContext:P?.context,isDrawer:w}),typeof a=="function"?a({payload:O}):a]})}const OV=uh(function(t){return kE("dialog",t)}),CE=x.forwardRef(function(t,a){const{render:o,className:c,style:d,id:f,...p}=t,h=iu(),b=ka(f);return h.useSyncedValueWithCleanup("titleElementId",b),yt("h2",t,{ref:a,props:[{id:b},p]})});function MV(e){const t=x.useRef(""),a=x.useCallback(c=>{c.defaultPrevented||(t.current=c.pointerType,e(c,c.pointerType))},[e]);return{onClick:x.useCallback(c=>{if(c.detail===0){e(c,"keyboard");return}"pointerType"in c?e(c,c.pointerType):e(c,t.current),t.current=""},[e]),onPointerDown:a}}function TE(e,t){const a=Ie((d,f)=>{(typeof e=="function"?e():e)||t(f||(tf?"touch":""))}),{onClick:o,onPointerDown:c}=MV(a);return x.useMemo(()=>({onClick:o,onPointerDown:c}),[o,c])}function EE(e){const[t,a]=x.useState(null),o=TE(e,a);return ia(e,c=>{c&&!e&&a(null)}),x.useMemo(()=>({openMethod:t,triggerProps:o}),[t,o])}function Yt(e){return s.jsx(OV,{"data-slot":"dialog",...e})}function DV(e){return s.jsx(vE,{"data-slot":"dialog-portal",...e})}const Ut=e=>s.jsx(CE,{"data-slot":"dialog-title",...e});function IV({className:e,...t}){return s.jsx(dE,{"data-slot":"dialog-overlay",className:Ue("fixed inset-0 isolate z-30 bg-mask backdrop-blur-[3px]","transition-opacity duration-150","data-open:animate-in data-open:fade-in-0","data-closed:animate-out data-closed:fade-out-0 data-closed:fill-mode-forwards",e),...t})}function Xt({className:e,children:t,showCloseButton:a=!0,keepMounted:o=!1,overlayForceRender:c=!1,...d}){return s.jsxs(DV,{keepMounted:o,children:[s.jsx(IV,{forceRender:c}),s.jsxs(bE,{"data-slot":"dialog-content",className:Ue("fixed top-1/2 left-1/2 z-30 flex max-h-[calc(100vh-48px)] w-[min(620px,calc(100vw-48px))] -translate-x-1/2 -translate-y-1/2 flex-col","overflow-hidden rounded-lg border border-border bg-surface shadow-3 outline-none","transition-[opacity,transform] duration-200 [transition-timing-function:cubic-bezier(.2,.9,.3,1)]","data-open:animate-in data-open:fade-in-0 data-open:zoom-in-[.985] data-open:slide-in-from-bottom-2.5","data-closed:animate-out data-closed:fade-out-0 data-closed:fill-mode-forwards",e),...d,children:[t,a&&s.jsxs(fE,{"data-slot":"dialog-close",render:s.jsx(ne,{variant:"ghost",size:"icon-sm",className:"absolute top-3 right-3"}),children:[s.jsx("svg",{viewBox:"0 0 12 12",className:"size-3","aria-hidden":"true",children:s.jsx("path",{d:"M2.5 2.5l7 7M9.5 2.5l-7 7",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",fill:"none"})}),s.jsx("span",{className:"sr-only",children:vo("关闭")})]})]})]})}function Vt({className:e,...t}){return s.jsx("div",{"data-slot":"dialog-header",className:Ue("flex items-center gap-2.5 border-b border-hairline px-5 py-[15px] pr-10","[&_h2]:flex-1 [&_h2]:text-[14.5px] [&_h2]:font-semibold",e),...t})}function Lt({className:e,...t}){return s.jsx("div",{"data-slot":"dialog-body",className:Ue("flex flex-col gap-4 overflow-y-auto px-5 py-[18px]",e),...t})}function tn({className:e,...t}){return s.jsx("div",{"data-slot":"dialog-footer",className:Ue("flex items-center gap-2 border-t border-hairline bg-surface-2 px-5 py-[13px]",e),...t})}function dn({className:e,...t}){return s.jsx("div",{"data-slot":"dialog-section",className:Ue("flex flex-col gap-2.5 rounded-md border border-border bg-surface-inset p-3.5","[&_h3]:text-[12.5px] [&_h3]:font-semibold","[&_p]:text-xs [&_p]:leading-[1.65] [&_p]:text-subtle",e),...t})}function PV(e){return kE("alert-dialog",e)}const _V=PV,LV=vE,zV=e=>s.jsx(CE,{"data-slot":"alert-dialog-title",...e});function BV({className:e,...t}){return s.jsx(dE,{"data-slot":"alert-dialog-overlay",className:Ue("fixed inset-0 isolate z-[38] bg-mask backdrop-blur-[3px]","transition-opacity duration-150","data-open:animate-in data-open:fade-in-0","data-closed:animate-out data-closed:fade-out-0 data-closed:fill-mode-forwards",e),...t})}function HV({className:e,children:t,showCloseButton:a=!1,...o}){return s.jsxs(LV,{children:[s.jsx(BV,{}),s.jsxs(bE,{"data-slot":"alert-dialog-content",className:Ue("fixed top-1/2 left-1/2 z-[38] flex max-h-[calc(100vh-48px)] w-[min(440px,calc(100vw-48px))] -translate-x-1/2 -translate-y-1/2 flex-col","overflow-hidden rounded-lg border border-border bg-surface shadow-3 outline-none","transition-[opacity,transform] duration-200 [transition-timing-function:cubic-bezier(.2,.9,.3,1)]","data-open:animate-in data-open:fade-in-0 data-open:zoom-in-[.985] data-open:slide-in-from-bottom-2.5","data-closed:animate-out data-closed:fade-out-0 data-closed:fill-mode-forwards",e),...o,children:[t,a&&s.jsxs(fE,{"data-slot":"alert-dialog-close",render:s.jsx(ne,{variant:"ghost",size:"icon-sm",className:"absolute top-3 right-3"}),children:[s.jsx("svg",{viewBox:"0 0 12 12",className:"size-3","aria-hidden":"true",children:s.jsx("path",{d:"M2.5 2.5l7 7M9.5 2.5l-7 7",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",fill:"none"})}),s.jsx("span",{className:"sr-only",children:vo("关闭")})]})]})]})}function UV({className:e,...t}){return s.jsx("div",{"data-slot":"alert-dialog-header",className:Ue("flex items-center gap-2.5 border-b border-hairline px-5 py-[15px]","[&_h2]:flex-1 [&_h2]:text-[14.5px] [&_h2]:font-semibold",e),...t})}function VV({className:e,...t}){return s.jsx("div",{"data-slot":"alert-dialog-body",className:Ue("flex flex-col gap-4 overflow-y-auto px-5 py-[18px]",e),...t})}function FV({className:e,...t}){return s.jsx("div",{"data-slot":"alert-dialog-footer",className:Ue("flex items-center gap-2 border-t border-hairline bg-surface-2 px-5 py-[13px]",e),...t})}function $V({className:e,tone:t="danger",...a}){return s.jsx("div",{"data-slot":"alert-dialog-banner","data-tone":t,className:Ue("rounded-md border px-3.5 py-3 text-xs leading-[1.65] text-subtle","[&_strong]:font-semibold",t==="danger"?"border-destructive-bd bg-destructive-soft [&_strong]:text-destructive":"border-border bg-surface-inset [&_strong]:text-foreground",e),...a})}function Zy({className:e,containerClassName:t,...a}){return s.jsx("div",{"data-slot":"table-container",className:Ue("relative w-full overflow-auto",t),children:s.jsx("table",{"data-slot":"table",className:Ue("w-full caption-bottom border-collapse text-[12.5px]",e),...a})})}function AE({className:e,...t}){return s.jsx("thead",{"data-slot":"table-header",className:Ue(e),...t})}function Wy({className:e,...t}){return s.jsx("tbody",{"data-slot":"table-body",className:Ue(e),...t})}function Cl({className:e,...t}){return s.jsx("tr",{"data-slot":"table-row",className:Ue("transition-colors duration-100 hover:[&>td]:bg-control-hover",e),...t})}function RE({className:e,...t}){return s.jsx("th",{"data-slot":"table-head",className:Ue("sticky top-0 z-[2] whitespace-nowrap border-b border-border bg-surface-2 px-3.5 py-2","text-left text-[10.5px] font-semibold tracking-[.05em] text-muted-foreground",e),...t})}function jn({className:e,...t}){return s.jsx("td",{"data-slot":"table-cell",className:Ue("border-b border-hairline px-3.5 py-2 align-middle",e),...t})}const qV="data-checked",GV="data-unchecked",Jy={checked(e){return e?{[qV]:""}:{[GV]:""}},...uo,...Ti},ej="data-composite-item-active",OE=x.createContext({register:()=>{},unregister:()=>{},subscribeMapChange:()=>()=>{},nextIndexRef:{current:0}});function KV(){return x.useContext(OE)}function tj(e={}){const{guess:t,label:a,metadata:o,textRef:c,index:d}=e,{register:f,unregister:p,subscribeMapChange:h,nextIndexRef:b}=KV(),y=x.useRef(-1),[v,j]=x.useState(d==null&&t?()=>{if(y.current===-1){const k=b.current;b.current+=1,y.current=k}return y.current}:-1),w=d??v,S=x.useRef(null),N=x.useCallback(k=>{const A=S.current;A&&p(A),S.current=k,k&&f(k,{metadata:o??null,index:d??null,label:a,textRef:c})},[d,f,p,o,a,c]);return Me(()=>{if(d==null)return h(k=>{const A=S.current?k.get(S.current)?.index:null;A!=null&&j(A)})},[d,h]),{ref:N,index:w}}function YV(e={}){const{highlightItemOnHover:t,highlightedIndex:a,onHighlightedIndexChange:o}=EC(),{ref:c,index:d}=tj(e),f=a===d,p=x.useRef(null),h=sa(c,p);return{compositeProps:{tabIndex:f?0:-1,onFocus(){o(d)},onMouseMove(){const y=p.current;if(!t||!y)return;const v=y.hasAttribute("disabled")||y.ariaDisabled==="true";!f&&!v&&y.focus()}},compositeRef:h,index:d}}function ME(e){const{render:t,className:a,style:o,state:c=mn,props:d=Ir,refs:f=Ir,metadata:p,stateAttributesMapping:h,tag:b="div",...y}=e,{compositeProps:v,compositeRef:j}=YV({metadata:p});return yt(b,e,{state:c,ref:[j,...f],props:[v,...d,y],stateAttributesMapping:h})}const DE=x.createContext(void 0);function XV(){return x.useContext(DE)}function Ih(e){if(e==null)return"";if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}const IE=x.createContext(void 0);function QV(){const e=x.useContext(IE);if(e===void 0)throw new Error(un(52));return e}const PE=x.forwardRef(function(t,a){const{render:o,className:c,disabled:d=!1,readOnly:f=!1,required:p=!1,"aria-labelledby":h,value:b,inputRef:y,nativeButton:v=!1,id:j,style:w,...S}=t,N=XV(),{disabled:k,readOnly:A,required:P,form:R,checkedValue:C,touched:E=!1,validation:D,name:I,setCheckedValue:O=Kt,setTouched:_=Kt,registerInputRef:z=Kt}=N??{},{setTouched:H,setFilled:B,state:L,disabled:F}=Na(),$=A1(),{labelId:X,getDescriptionProps:K}=yl(),q=F||$.disabled||k||d,Q=A||f,G=P||p,J=R,Y=N?C===b:b==="",re=x.useRef(null),M=x.useRef(null),U=D?.registerInput,Z=x.useCallback(oe=>U?.(oe,{controlRef:re,value:void 0}),[U]),ae=sa(y,M,z,Z);Me(()=>{M.current?.checked&&B(!0)},[B]),Me(()=>{if(M.current){if(q&&Y){z(null);return}z(M.current)}},[Y,q,z]);const ce=ka(),W=co({id:j}),ue=v?void 0:W,ve=Fv(h,X,M,!v,ue),ee={role:"radio","aria-checked":Y,"aria-labelledby":ve,[ej]:Y?"":void 0,id:v?W:ce,onKeyDown(oe){oe.key==="Enter"&&oe.preventDefault()},onClick(oe){if(oe.defaultPrevented||q||Q)return;oe.preventDefault();const me=M.current;me&&Yc(me,oe)},onFocus(oe){oe.defaultPrevented||q||Q||!E||(M.current?.click(),_(!1))}},{getButtonProps:he,buttonRef:te}=na({disabled:q,native:v,composite:!1}),ke={type:"radio",ref:ae,form:J,id:ue,name:I,tabIndex:-1,style:I?su:_i,"aria-hidden":!0,...b!==void 0?{value:Ih(b)}:mn,disabled:q,checked:Y,required:G,readOnly:Q,onChange(oe){if(oe.nativeEvent.defaultPrevented||q||Q||b===void 0)return;const me=qe(Qn,oe.nativeEvent);O(b,me),!me.isCanceled&&H(!0)},onClick(oe){oe.stopPropagation()},onFocus(){re.current?.focus()}},le=x.useMemo(()=>({...L,required:G,disabled:q,readOnly:Q,checked:Y}),[L,q,Q,Y,G]),de=le,Se=N!==void 0,Te=[a,re,te],_e=[ee,S,he,K,D?oe=>D.getValidationProps(q,oe):mn],Ae=yt("span",t,{enabled:!Se,state:le,ref:Te,props:_e,stateAttributesMapping:Jy});return s.jsxs(IE.Provider,{value:de,children:[Se?s.jsx(ME,{tag:"span",render:o,className:c,style:w,state:le,refs:Te,props:_e,stateAttributesMapping:Jy}):Ae,s.jsx("input",{...ke,suppressHydrationWarning:!0})]})}),ZV=x.forwardRef(function(t,a){const{render:o,className:c,style:d,keepMounted:f=!1,...p}=t,h=QV(),b=h.checked,{mounted:y,transitionStatus:v,setMounted:j}=Oi(b),w={...h,transitionStatus:v},S=x.useRef(null),N=f||y,k=yt("span",t,{ref:[a,S],state:w,props:p,stateAttributesMapping:Jy});return oa({batch:!0,enabled:!b,open:b,ref:S,onComplete(){b||j(!1)}}),N?k:null});function nj(e){const{children:t,elementsRef:a,labelsRef:o,onMapChange:c}=e,d=Ie(c),[,f]=x.useState(!1),p=Ps(JV).current,h=Ps(WV).current,b=x.useRef(0),y=x.useRef(!0),v=x.useRef(null),j=x.useRef(null),w=Ie(()=>{y.current||(y.current=!0,f(E=>!E))}),S=Ie((E,D)=>{h.set(E,D),w()}),N=Ie(E=>{h.delete(E),w()}),k=Ie(E=>{const D=new Map;return a.current.length=0,o&&(o.current.length=0),E.forEach(I=>{D.set(I.element,{...I.registration.metadata??{},index:I.index}),a.current[I.index]=I.element,o&&(o.current[I.index]=I.registration.label!==void 0?I.registration.label:I.registration.textRef?.current?.textContent??I.element.textContent)}),b.current=a.current.length,D});function A(E){if(j.current?.disconnect(),j.current=null,typeof MutationObserver!="function"||E.length<2)return;const D=new MutationObserver(O=>{if(!nF(O))return;let _=null;for(const z of E)if(z.isConnected){if(_&&_E(_,z)>0){D.disconnect(),w();return}_=z}});j.current=D;const I=new Set;for(let O=1;OD.observe(O,{childList:!0}))}const P=Ie(()=>{const[E,D]=eF(h),I=k(E),O=v.current,_=!O||O.length!==E.length||E.some((z,H)=>{const B=O[H];return z.index!==B.index||z.element!==B.element||z.registration.index!==B.registration.index||z.registration.metadata!==B.registration.metadata});A(D),v.current=E,y.current=!1,_&&(p.forEach(z=>z(I)),d(I))});Me(()=>(!y.current&&v.current&&k(v.current),()=>{a.current=[],o&&(o.current=[])}),[a,o,k]),Me(()=>{y.current&&P()}),Me(()=>()=>{j.current?.disconnect(),y.current=!0},[]);const R=Ie(E=>(p.add(E),()=>{p.delete(E)})),C=x.useMemo(()=>({register:S,unregister:N,subscribeMapChange:R,nextIndexRef:b}),[S,N,R,b]);return s.jsx(OE.Provider,{value:C,children:t})}function WV(){return new Map}function JV(){return new Set}function eF(e){const t=new Set,a=[],o=[];e.forEach((d,f)=>{if(!f.isConnected)return;const p=d.index,h={index:p??-1,element:f,registration:d};p===null?o.push(h):p>=0&&(t.add(p),a.push(h))});let c=0;return o.sort((d,f)=>_E(d.element,f.element)),o.forEach(d=>{for(;t.has(c);)c+=1;d.index=c,a.push(d),c+=1}),t.size>0&&a.sort((d,f)=>d.index-f.index),[a,o.map(d=>d.element)]}function tF(e,t){let a=e.parentElement;for(;a&&!a.contains(t);)a=a.parentElement;return a}function nF(e){for(const t of e)for(let a=0;a{if(C.current=P.current[H]??null,(p??S)(H),B){const L=P.current[H];gE(k.current,L,d,a)}}),I=Ie(H=>{if(H.size===0)return;if(R.current){const $=P.current,X=$.indexOf(C.current);if(X===-1){const K=$[E];!K||go($,E,v)?D(rF($,v)):C.current=K}else X!==E&&D(X);return}R.current=!0;const B=Array.from(H.keys()),L=B.find($=>$?.hasAttribute(ej))??null,F=L?H.get(L)?.index??-1:-1;if(F!==-1)D(F);else if(go(B,E,v)){const $=_s(B,{disabledIndices:v});ho(B,$)||D($)}gE(k.current,L,d,a)});Me(()=>{if(v==null||f!=null||!R.current)return;const H=P.current;if(go(H,E,v)){const B=_s(H,{disabledIndices:v});ho(H,B)||D(B)}},[v,f,E,P,D]);const O=Ie((H,B,L)=>c?c(H,B,L,P):L),_=Ie(H=>{const B=H.key===Xy||H.key===Qy;if(!Rh.has(H.key)||!b&&B||aF(H,j)||!k.current)return;const F=d==="rtl",$=F?Ky:Yy,X=F?Yy:Ky,K=a==="vertical"?Gy:$,q=a==="vertical"?qy:X,Q=fs(H.nativeEvent);if(Q!=null&&hE(Q)&&!LE(Q)){const U=Q.selectionStart,Z=Q.selectionEnd,ae=Q.value;if(U==null||H.shiftKey||U!==Z||H.key!==q&&U0)return}let G=E;const J=sh(P,v),Y=ry(P,v);o!=null&&(G=o({disabledIndices:v,elementsRef:P,event:H,highlightedIndex:E,loopFocus:t,maxIndex:Y,minIndex:J,onLoop:O,orientation:a,rtl:F}));const re=a!=="vertical"&&H.key===$||a!=="horizontal"&&H.key===Gy,M=a!=="vertical"&&H.key===X||a!=="horizontal"&&H.key===qy;b&&(H.key===Xy?G=J:H.key===Qy&&(G=Y)),G===E&&(re||M)&&(t&&G===Y&&re?(G=J,c&&(G=c(H,E,G,P))):t&&G===J&&M?(G=Y,c&&(G=c(H,E,G,P))):G=_s(P.current,{startingIndex:G,decrement:M,disabledIndices:v})),G!==E&&!ho(P.current,G)&&(y&&H.stopPropagation(),(N||B||re||M)&&H.preventDefault(),D(G,!0),queueMicrotask(()=>{P.current[G]?.focus()}))});return{props:{ref:A,onFocus(H){const B=k.current,L=fs(H.nativeEvent);!B||L==null||!hE(L)||L.setSelectionRange(0,L.value.length)},onKeyDown:_},highlightedIndex:E,onHighlightedIndexChange:D,elementsRef:P,onMapChange:I,relayKeyboardEvent:_}}function rF(e,t){let a=-1;for(let o=0;o({highlightedIndex:_,onHighlightedIndexChange:z,highlightItemOnHover:C,relayKeyboardEvent:L}),[_,z,C,L]);return s.jsx(TC.Provider,{value:$,children:s.jsx(nj,{elementsRef:H,onMapChange:X=>{N?.(X),B(X)},children:F})})}const oF=[mE],zE=x.forwardRef(function(t,a){const{render:o,className:c,disabled:d,readOnly:f,required:p,onValueChange:h,value:b,defaultValue:y,form:v,name:j,inputRef:w,id:S,style:N,...k}=t,{setTouched:A,setFocused:P,validationMode:R,name:C,disabled:E,state:D,validation:I,setDirty:O,setFilled:_,validityData:z}=Na(),{labelId:H}=yl(),{clearErrors:B,elementRef:L}=Qc(),F=CB(!0),$=E||d,X=C??j,K=ka(S),[q,Q]=Ka({controlled:b,default:y,name:"RadioGroup",state:"value"}),[G,J]=x.useState(!1),Y=Ie((te,ke)=>{h?.(te,ke),!ke.isCanceled&&Q(te)}),re=I.getInputControl,M=x.useMemo(()=>({get current(){return re()}}),[re]),U=x.useRef(null),Z=x.useRef(null);function ae(te){let ke;return w&&(typeof w=="function"?ke=w(te):w.current=te),U.current=te,ke}const ce=Ie(te=>{if(!te||te.disabled)return;Z.current||(Z.current=te);const ke=U.current,le=te.checked||ke==null||ke.disabled?ae(te):void 0;return()=>{Z.current===te&&(Z.current=null),U.current===te?le?(le(),U.current=null):ae(null):le?.()}}),W=Ie(()=>{const te=L.current;if(!te)return q??null;for(const ke of I.registeredInputs.keys())if(ke.checked&&v4(ke,te))return q??null;return null});tu(M,K,q??null,W,!$,j),ia(q,()=>{B(X),O(q!==z.initialValue),_(q!=null),I.change(q);const te=Z.current;q==null&&te&&!te.disabled&&ae(te)});const ue=H??F?.legendId,ve={...D,disabled:$??!1,required:p??!1,readOnly:f??!1},ee=x.useMemo(()=>({checkedValue:q,disabled:$,form:v,validation:I,name:X,readOnly:f,registerInputRef:ce,required:p,setCheckedValue:Y,setTouched:J,touched:G}),[q,$,v,I,X,f,ce,p,Y,J,G]),he={id:S,role:"radiogroup","aria-required":p||void 0,"aria-disabled":$||void 0,"aria-readonly":f||void 0,"aria-labelledby":ue,onFocus(){P(!0)},onBlur(te){Ze(te.currentTarget,te.relatedTarget)||(A(!0),P(!1),R==="onBlur"&&I.commit(q))},onKeyDownCapture(te){te.key.startsWith("Arrow")&&(J(!0),P(!0))}};return s.jsx(DE.Provider,{value:ee,children:s.jsx(lF,{render:o,className:c,style:N,state:ve,props:[he,k,te=>I.getValidationProps($??!1,te)],refs:[a],stateAttributesMapping:Ti,enableHomeAndEndKeys:!1,modifierKeys:oF})})});function Ph({className:e,...t}){return s.jsx(zE,{"data-slot":"radio-group",className:Ue("flex flex-col gap-2",e),...t})}function jo({className:e,...t}){return s.jsx(PE,{"data-slot":"radio-group-item",className:Ue("relative inline-flex size-3.5 flex-none cursor-pointer items-center justify-center rounded-full border border-control-border bg-control","outline-none transition-colors duration-150 ease-out","hover:border-control-border-hover","data-checked:border-primary data-checked:bg-primary","focus-visible:shadow-focus","data-disabled:cursor-not-allowed data-disabled:opacity-45",e),...t,children:s.jsx(ZV,{"data-slot":"radio-group-indicator",className:"grid place-content-center data-unchecked:hidden",children:s.jsx("span",{className:"block size-[5px] rounded-full bg-primary-foreground"})})})}function BE(e){const t=x.useRef(!0);t.current&&(t.current=!1,e())}const HE=x.createContext(void 0),UE=x.createContext(void 0),VE=x.createContext(void 0);function wo(){const e=x.useContext(HE);if(e===void 0)throw new Error(un(60));return e}function sj(){const e=x.useContext(UE);if(e===void 0)throw new Error(un(101));return e}function FE(){const e=x.useContext(VE);if(e===void 0)throw new Error(un(61));return e}function iF(e,t,a=Object.is){const{length:o}=e;if(o!==t.length)return!1;for(let c=0;cObject.is(e,t);function ca(e,t,a){return e==null||t==null?Object.is(e,t):a(e,t)}function $E(e,t,a){return Array.isArray(e)&&Array.isArray(t)?!iF(e,t,(o,c)=>ca(o,c,a)):e!==t}function Lh(e,t,a){return e?e.some(o=>o===void 0?!1:ca(t,o,a)):!1}function cu(e,t,a){return e?e.findIndex(o=>o===void 0?!1:ca(o,t,a)):-1}function cF(e,t){if(t!==_h)return o=>Lh(e,o,t);const a=new Set(e);return a.delete(void 0),o=>a.has(o)&&(o!==0||e.some(c=>Object.is(o,c)))}function bf(e,t,a,o){const c=o&&Array.isArray(t)?e.findIndex(cF(t,a)):cu(e,t,a);return c===-1?null:c}function qE(e,t,a,o,c,d){return Lh(o,t,c)?d!=null&&e>d&&Lh(o,a[d],c)?d:e:e===d?bf(a,o,c,!0):d}function GE(e,t,a){return e.filter(o=>!ca(t,o,a))}function uF(e){return typeof e=="object"&&e!=null&&Array.isArray(e.items)}function rj(e){return uF(e?.[0])}function zh(e){return rj(e)?e.flatMap(t=>t.items):e}function KE(e){if(!Array.isArray(e))return e!=null&&"null"in e;const t=e;if(rj(t)){for(const a of t)for(const o of a.items)if(o&&o.value==null&&o.label!=null)return!0;return!1}for(const a of t)if(a&&a.value==null&&a.label!=null)return!0;return!1}function So(e,t){if(t&&e!=null)return t(e)??"";if(e&&typeof e=="object"){if("label"in e&&e.label!=null)return String(e.label);if("value"in e)return String(e.value)}return Ih(e)}function Ca(e,t){return t&&e!=null?t(e)??"":e&&typeof e=="object"&&"value"in e&&"label"in e?Ih(e.value):Ih(e)}function YE(e,t,a){function o(){return So(e,a)}if(a&&e!=null)return a(e);if(e&&typeof e=="object"&&"label"in e&&e.label!=null)return e.label;if(t&&!Array.isArray(t))return(Object.hasOwn(t,e)?t[e]:void 0)??o();if(Array.isArray(t)){const d=zh(t);if(e==null||typeof e!="object"){const f=d.find(p=>p.value===e);return f&&f.label!=null?f.label:o()}if("value"in e){const f=d.find(p=>p&&p.value===e.value);if(f&&f.label!=null)return f.label}}return o()}function dF(e,t,a){return e.reduce((o,c,d)=>(d>0&&o.push(", "),o.push(s.jsx(x.Fragment,{children:YE(c,t,a)},d)),o),[])}const fF={id:e=>e.id,labelId:e=>e.labelId,modal:e=>e.modal,items:e=>e.items,itemToStringLabel:e=>e.itemToStringLabel,isItemEqualToValue:e=>e.isItemEqualToValue,value:e=>e.value,hasSelectedValue:e=>{const{value:t,multiple:a,itemToStringValue:o}=e;return t==null?!1:a&&Array.isArray(t)?t.length>0:Ca(t,o)!==""},hasNullItemLabel:(e,t)=>t?KE(e.items):!1,open:e=>e.open,mounted:e=>e.mounted,forceMount:e=>e.forceMount,transitionStatus:e=>e.transitionStatus,openMethod:e=>e.openMethod,activeIndex:e=>e.activeIndex,selectedIndex:e=>e.selectedIndex,isActive:(e,t)=>e.activeIndex===t,isSelected:(e,t)=>{const a=e.isItemEqualToValue,o=e.value;return e.multiple?Array.isArray(o)&&o.some(c=>ca(t,c,a)):ca(t,o,a)},isSelectedByFocus:(e,t)=>e.selectedIndex===t,popupProps:e=>e.popupProps,triggerProps:e=>e.triggerProps,triggerElement:e=>e.triggerElement,positionerElement:e=>e.positionerElement,listElement:e=>e.listElement,popupSide:e=>e.popupSide,scrollUpArrowVisible:e=>e.scrollUpArrowVisible,scrollDownArrowVisible:e=>e.scrollDownArrowVisible,hasScrollArrows:e=>e.hasScrollArrows},Tl=1;function XE(e,t){return Math.max(0,e-t)}function pF(e,t){if(t<=0)return 0;const a=Li(e,0,t),o=a,c=t-a,d=o<=Tl,f=c<=Tl;return d&&f?o<=c?0:t:d?0:f?t:a}function mF(e){const{id:t,value:a,defaultValue:o=null,onValueChange:c,open:d,defaultOpen:f=!1,onOpenChange:p,name:h,form:b,autoComplete:y,disabled:v=!1,readOnly:j=!1,required:w=!1,modal:S=!0,actionsRef:N,inputRef:k,onOpenChangeComplete:A,items:P,multiple:R=!1,itemToStringLabel:C,itemToStringValue:E,isItemEqualToValue:D=_h,highlightItemOnHover:I=!0,children:O}=e,{clearErrors:_}=Qc(),{setDirty:z,setTouched:H,setFocused:B,validityData:L,setFilled:F,name:$,disabled:X,validation:K,validationMode:q}=Na(),Q=co({id:t}),G=X||v,J=$??h,[Y,re]=Ka({controlled:a,default:R?o??Ir:o,name:"Select",state:"value"}),[M,U]=Ka({controlled:d,default:f,name:"Select",state:"open"}),Z=x.useRef([]),ae=x.useRef([]),ce=x.useRef(null),W=x.useRef(null),ue=x.useRef(0),ve=x.useRef(null),ee=x.useRef([]),he=x.useRef(!1),te=x.useRef(null),ke=x.useRef(null),le=x.useRef({allowSelectedMouseUp:!1,allowUnselectedMouseUp:!1,dragY:0}),de=x.useRef(!1),Se=x.useRef(Y),{mounted:Te,setMounted:_e,transitionStatus:Ae}=Oi(M),{openMethod:oe,triggerProps:me}=EE(M),pe=Ps(()=>new ou({id:Q,labelId:void 0,modal:S,multiple:R,itemToStringLabel:C,itemToStringValue:E,isItemEqualToValue:D,value:Y,open:M,mounted:Te,transitionStatus:Ae,items:P,forceMount:!1,openMethod:null,activeIndex:null,selectedIndex:null,popupProps:mn,triggerProps:mn,triggerElement:null,positionerElement:null,listElement:null,popupSide:null,scrollUpArrowVisible:!1,scrollDownArrowVisible:!1,hasScrollArrows:!1},{setValue:Kt,setOpen:Kt,handleScrollArrowVisibility:Kt,onOpenChangeComplete:Kt,listRef:Z,popupRef:ce,scrollHandlerRef:W,scrollArrowsMountedCountRef:ue,valueRef:ve,valuesRef:ee,labelsRef:ae,typingRef:he,selectionRef:le,firstItemTextRef:te,selectedItemTextRef:ke,alignItemWithTriggerActiveRef:de,initialValueRef:Se},fF)).current,Ce=pe.useState("activeIndex"),Ne=pe.useState("selectedIndex"),be=pe.useState("triggerElement"),De=pe.useState("positionerElement"),Ge=dV(oe),Ct=oe??Ge,Tt=x.useMemo(()=>R?"":Ca(Y,E),[R,Y,E]),Mt=x.useMemo(()=>R&&Array.isArray(Y)?Y.map(xt=>Ca(xt,E)):Ca(Y,E),[R,Y,E]),ot=Zn(be),Kn=Ie(()=>Mt);tu(ot,Q,Y,Kn,!G,h);const ms=R?Array.isArray(Y)&&Y.length>0:Y!=null&&Tt!=="";Me(()=>{F(ms)},[ms,F]),Me(function(){const jt=bf(ee.current,Y,D,R);jt===null&&(ke.current=null),!M&&pe.set("selectedIndex",jt)},[R,M,Y,D,pe]),ia(Y,()=>{_(J),z($E(Y,L.initialValue,D)),K.change(Y)});const Yn=Ie((xt,jt)=>{p?.(xt,jt),!jt.isCanceled&&(U(xt),!xt&&(jt.reason===bo||jt.reason===ch)&&(H(!0),B(!1),q==="onBlur"&&K.commit(Y)))}),vs=Ie(()=>{_e(!1),pe.update({activeIndex:null,openMethod:null,scrollUpArrowVisible:!1,scrollDownArrowVisible:!1}),A?.(!1)});oa({enabled:!N,open:M,ref:ce,onComplete(){M||vs()}}),x.useImperativeHandle(N,()=>({unmount:vs}),[vs]);const it=Ie((xt,jt)=>{c?.(xt,jt),!jt.isCanceled&&re(xt)}),$t=Ie(xt=>{const jt=XE(xt.scrollHeight,xt.clientHeight),wt=pF(xt.scrollTop,jt),Tr=wt>0,ar=wtLE(Z.current[xt]),onMatch(xt){M?pe.set("activeIndex",xt):it(ee.current[xt],qe(Qn))},onTyping(xt){he.current=xt}}),es=x.useMemo(()=>ta(qt.reference,Dt.reference,nt.reference,rn.reference,me),[rn.reference,qt.reference,Dt.reference,nt.reference,me]),Fn=x.useMemo(()=>ta(mf,qt.floating,Dt.floating,nt.floating),[qt.floating,Dt.floating,nt.floating]),$n=Dt.item??mn;pe.useContextCallback("setValue",it),pe.useContextCallback("setOpen",Yn),pe.useContextCallback("handleScrollArrowVisibility",$t),pe.useContextCallback("onOpenChangeComplete",A),BE(()=>{pe.update({popupProps:Fn,triggerProps:es})}),pe.useSyncedValues({id:Q,modal:S,multiple:R,value:Y,open:M,mounted:Te,transitionStatus:Ae,popupProps:Fn,triggerProps:es,items:P,itemToStringLabel:C,itemToStringValue:E,isItemEqualToValue:D,openMethod:Ct});const Dn=x.useMemo(()=>({disabled:G,readOnly:j,required:w,multiple:R,highlightItemOnHover:I,itemProps:$n}),[G,j,w,R,I,$n]),en=sa(k,K.inputRef),cs=R?void 0:J,Et=x.useMemo(()=>!R||!Array.isArray(Y)||!J?null:Y.map(xt=>{const jt=Ca(xt,E);return s.jsx("input",{type:"hidden",form:b,name:J,value:jt,disabled:G},jt)}),[R,Y,b,J,E,G]);return s.jsxs(HE.Provider,{value:pe,children:[s.jsx(UE.Provider,{value:Dn,children:s.jsx(VE.Provider,{value:Vn,children:O})}),s.jsx("input",{...K.getValidationProps(G,{onFocus(){pe.state.triggerElement?.focus({focusVisible:!0})},onChange(xt){if(xt.nativeEvent.defaultPrevented||G||j)return;const jt=xt.currentTarget.value,wt=qe(Qn,xt.nativeEvent);function Tr(){if(R)return;const ar=jt.toLowerCase();let Fr=ee.current.findIndex(al=>Ca(al,E).toLowerCase()===ar||So(al,C).toLowerCase()===ar);Fr===-1&&(Fr=ee.current.findIndex((al,ln)=>{const hc=ae.current[ln];return hc!=null&&hc.toLowerCase()===ar}));const kn=ee.current[Fr];kn!=null&&it(kn,wt)}pe.set("forceMount",!0),queueMicrotask(Tr)}}),id:Q&&cs==null?`${Q}-hidden-input`:void 0,form:b,name:cs,autoComplete:y,value:Tt,disabled:G,required:w&&!(R&&ms),readOnly:j,ref:en,style:J?su:_i,tabIndex:-1,"aria-hidden":!0,suppressHydrationWarning:!0}),Et]})}function QE(e,t){return e??t}const Bh=5;function ZE(e,t){const a=hF(t);return e.clientX>=a.left-Bh&&e.clientX<=a.right+Bh&&e.clientY>=a.top-Bh&&e.clientY<=a.bottom+Bh}function hF(e){const t=e.getBoundingClientRect(),a=Tn(e);if($v)return t;const o=a.getComputedStyle(e,"::before"),c=a.getComputedStyle(e,"::after");if(!(o.content!=="none"||c.content!=="none"))return t;const f=parseFloat(o.width)||0,p=parseFloat(o.height)||0,h=parseFloat(c.width)||0,b=parseFloat(c.height)||0,y=Math.max(t.width,f,h),v=Math.max(t.height,p,b),j=y-t.width,w=v-t.height;return{left:t.left-j/2,right:t.right+j/2,top:t.top-w/2,bottom:t.bottom+w/2}}const gF="data-popup-side",xF=400,bF={...Yv,...Ti,popupSide:e=>e?{[gF]:e}:null,value:()=>null},vF=x.forwardRef(function(t,a){const{render:o,className:c,id:d,disabled:f=!1,nativeButton:p=!0,style:h,...b}=t,{setTouched:y,setFocused:v,validationMode:j,validation:w,state:S,disabled:N}=Na(),{labelId:k}=yl(),A=wo(),{readOnly:P,required:R,disabled:C}=sj(),E=N||C||f,D=A.useState("open"),I=A.useState("mounted"),O=A.useState("value"),_=A.useState("triggerProps"),z=A.useState("positionerElement"),H=A.useState("listElement"),B=A.useState("popupSide"),L=A.useState("id"),F=A.useState("labelId"),$=A.useState("hasSelectedValue"),X=I&&z?B:null,K=d??L,q=QE(k,F);co({id:d});const Q=Zn(z),G=x.useRef(null),{getButtonProps:J,buttonRef:Y}=na({disabled:E,native:p}),re=A.useStateSetter("triggerElement"),M=er(),U=er(),Z=er();x.useEffect(()=>{if(D)return Z.start(xF,()=>{A.context.selectionRef.current.allowUnselectedMouseUp=!0,A.context.selectionRef.current.allowSelectedMouseUp=!0}),()=>{Z.clear()};A.context.selectionRef.current={allowSelectedMouseUp:!1,allowUnselectedMouseUp:!1,dragY:0},U.clear()},[D,A,U,Z]);const ae=ta(_,{id:K,role:"combobox","aria-expanded":D,"aria-haspopup":"listbox","aria-controls":D?H?.id??Wm(z)?.id:void 0,"aria-labelledby":q,"aria-readonly":P||void 0,"aria-required":R||void 0,tabIndex:E?-1:0,onFocus(ue){v(!0),D&&A.context.alignItemWithTriggerActiveRef.current&&A.context.setOpen(!1,qe(Qn,ue.nativeEvent)),M.start(0,()=>{A.set("forceMount",!0)})},onBlur(ue){Ze(z,ue.relatedTarget)||(y(!0),v(!1),j==="onBlur"&&w.commit(O))},onMouseDown(ue){if(D)return;const ve=Gt(ue.currentTarget);function ee(he){if(!G.current)return;const te=he.target;Ze(G.current,te)||Ze(Q.current,te)||ZE(he,G.current)||A.context.setOpen(!1,qe(_1,he))}U.start(0,()=>{ve.addEventListener("mouseup",ee,{once:!0})})}},b,J),ce=w.getValidationProps(E,ae);ce.role="combobox";const W={...S,open:D,disabled:E,value:O,readOnly:P,popupSide:X,placeholder:!$};return yt("button",t,{ref:[a,G,Y,re],state:W,stateAttributesMapping:bF,props:ce})}),yF={value:()=>null},jF=x.forwardRef(function(t,a){const{className:o,render:c,children:d,placeholder:f,style:p,...h}=t,b=wo(),y=b.useState("value"),v=b.useState("items"),j=b.useState("itemToStringLabel"),w=b.useState("hasSelectedValue"),S=!w&&f!=null&&d==null,N=b.useState("hasNullItemLabel",S),k={value:y,placeholder:!w};let A=null;return typeof d=="function"?A=d(y):d!=null?A=d:S&&!N?A=f:Array.isArray(y)?A=dF(y,v,j):A=YE(y,v,j),yt("span",t,{state:k,ref:[a,b.context.valueRef],props:[{children:A},h],stateAttributesMapping:yF})}),wF=x.forwardRef(function(t,a){const{render:o,className:c,style:d,...f}=t,b={open:wo().useState("open")};return yt("span",t,{state:b,ref:a,props:[{"aria-hidden":!0,children:"▼"},f],stateAttributesMapping:Kv})}),SF=x.forwardRef(function(t,a){const o=wo(),c=o.useState("mounted"),d=o.useState("forceMount");return c||d?s.jsx(ph,{ref:a,...t}):null}),WE=x.createContext(void 0);function NF(){const e=x.useContext(WE);if(!e)throw new Error(un(59));return e}function Hh(e,t){e&&Object.assign(e.style,t)}const kF={position:"relative",maxHeight:"100%",overflowX:"hidden",overflowY:"auto"},CF=20;function aj(e,t,a,o){const[c,d]=x.useState(!1);Me(()=>{if(!e||!t||a==null){d(!1);return}const f=Gt(a).documentElement.clientWidth,p=a.offsetWidth;d(f>0&&p>0&&p>=f-CF)},[e,t,a]),NE(e&&(!t||c),o)}const TF={position:"fixed"},EF=x.forwardRef(function(t,a){const{anchor:o,className:c,render:d,positionMethod:f,side:p,align:h,sideOffset:b,alignOffset:y,collisionBoundary:v="clipping-ancestors",collisionPadding:j,arrowPadding:w,sticky:S,disableAnchorTracking:N,alignItemWithTrigger:k=!0,collisionAvoidance:A=rT,style:P,...R}=t,C=wo(),E=FE(),D=C.useState("open"),I=C.useState("mounted"),O=C.useState("modal"),_=C.useState("value"),z=C.useState("openMethod"),H=C.useState("positionerElement"),B=C.useState("triggerElement"),L=C.useState("isItemEqualToValue"),F=C.useState("transitionStatus"),$=x.useRef(null),X=x.useRef(null),[K,q]=x.useState(k),Q=I&&K&&z!=="touch";!I&&K!==k&&q(k),x.useImperativeHandle(C.context.alignItemWithTriggerActiveRef,()=>Q),aj((Q||O)&&D,z==="touch",H,B);const G=Th({anchor:o,floatingRootContext:E,positionMethod:f,mounted:I,side:p,sideOffset:b,align:h,alignOffset:y,arrowPadding:w,collisionBoundary:v,collisionPadding:j,sticky:S,disableAnchorTracking:N??Q,collisionAvoidance:A,keepMounted:!0}),J=Q?"none":G.side,Y=Q?TF:G.positionerStyles,re={open:D,side:J,align:G.align,anchorHidden:G.anchorHidden};Me(()=>{C.set("popupSide",G.side)},[C,G.side]);const M=C.useStateSetter("positionerElement"),U=Eh(t,re,{styles:Y,transitionStatus:F,props:R,refs:[a,M],hidden:!I,inert:!D}),Z=x.useRef(0),ae=Ie(W=>{if(C.context.valuesRef.current.length===0)return;const ue=Z.current;Z.current=W.size;const ve=qe(Qn);if(ue!==0&&!C.state.multiple&&_!==null&&cu(C.context.valuesRef.current,_,L)===-1){const he=C.context.initialValueRef.current,ke=he!=null&&cu(C.context.valuesRef.current,he,L)!==-1?he:null;C.context.setValue(ke,ve),ke===null&&(C.set("selectedIndex",null),C.context.selectedItemTextRef.current=null)}if(ue!==0&&C.state.multiple&&Array.isArray(_)){const ee=_.filter(he=>cu(C.context.valuesRef.current,he,L)!==-1);ee.length!==_.length&&(C.context.setValue(ee,ve),ee.length===0&&(C.set("selectedIndex",null),C.context.selectedItemTextRef.current=null))}if(D&&Q){C.update({scrollUpArrowVisible:!1,scrollDownArrowVisible:!1});const ee={height:""};Hh(H,ee),Hh(C.context.popupRef.current,ee)}}),ce=x.useMemo(()=>({...G,side:J,alignItemWithTriggerActive:Q,setControlledAlignItemWithTrigger:q,scrollUpArrowRef:$,scrollDownArrowRef:X}),[G,J,Q,q]);return s.jsx(nj,{elementsRef:C.context.listRef,labelsRef:C.context.labelsRef,onMapChange:ae,children:s.jsxs(WE.Provider,{value:ce,children:[I&&O&&s.jsx(Mh,{inert:Ah(!D),cutout:B}),U]})})}),Uh="base-ui-disable-scrollbar",JE={className:Uh,getElement(e){return s.jsx("style",{nonce:e,href:Uh,precedence:"base-ui:low",children:`.${Uh}{scrollbar-width:none}.${Uh}::-webkit-scrollbar{display:none}`})}},AF=x.createContext(void 0);function eA(e){return x.useContext(AF)}const RF=x.createContext(void 0),OF={disableStyleElements:!1};function MF(){return x.useContext(RF)??OF}const tA="--transform-origin",DF={...Zc,...uo},IF=x.forwardRef(function(t,a){const{render:o,className:c,style:d,finalFocus:f,...p}=t,h=wo(),{multiple:b,readOnly:y,highlightItemOnHover:v}=sj(),j=FE(),{side:w,align:S,alignItemWithTriggerActive:N,isPositioned:k,setControlledAlignItemWithTrigger:A}=NF(),P=eA()!=null,R=gf(),{nonce:C,disableStyleElements:E}=MF(),D=h.useState("id"),I=h.useState("open"),O=h.useState("openMethod"),_=h.useState("mounted"),z=h.useState("popupProps"),H=h.useState("transitionStatus"),B=h.useState("triggerElement"),L=h.useState("positionerElement"),F=h.useState("listElement"),$=x.useRef(!1),X=x.useRef(!1),K=x.useRef({}),q=eu(),Q=Ie(re=>{if(!L||!h.context.popupRef.current||!X.current)return;const M=L.style.top==="0px",U=L.style.bottom==="0px";if($.current||!N||!M&&!U){h.context.handleScrollArrowVisibility(re);return}const Z=sA(L),ae=vf(L.getBoundingClientRect().height,"y",Z),ce=Gt(L),W=Tn(L),ue=W.getComputedStyle(L),ve=parseFloat(ue.marginTop),ee=parseFloat(ue.marginBottom),he=nA(W.getComputedStyle(h.context.popupRef.current)),te=Math.min(ce.documentElement.clientHeight-ve-ee,he),ke=re.scrollTop,le=Vh(re);let de=null;const Se=oe=>{L.style.height=`${oe}px`},Te=M?le-ke:ke,_e=Math.min(ae+Te,te);if(Te<=Tl){const oe=Li(Te,0,te-ae);oe>0&&Se(ae+oe),re.scrollTop=M?le:0,te-(ae+oe)<=Tl&&($.current=!0),h.context.handleScrollArrowVisibility(re);return}if(te-_e>Tl)de=M?1/0:0;else if(U&&keTl&&(re.scrollTop=oe)}Ae>=te-Tl&&($.current=!0),h.context.handleScrollArrowVisibility(re)});x.useImperativeHandle(h.context.scrollHandlerRef,()=>Q,[Q]),oa({open:I,ref:h.context.popupRef,onComplete(){I&&h.context.onOpenChangeComplete(!0)}});const G={open:I,transitionStatus:H,side:w,align:S};Me(()=>{!L||!h.context.popupRef.current||Object.keys(K.current).length||(K.current={top:L.style.top||"0",left:L.style.left||"0",right:L.style.right,height:L.style.height,bottom:L.style.bottom,minHeight:L.style.minHeight,maxHeight:L.style.maxHeight,marginTop:L.style.marginTop,marginBottom:L.style.marginBottom})},[h,L]),Me(()=>{I||N||(X.current=!1,$.current=!1,Hh(L,K.current))},[I,N,L]),Me(()=>{const re=h.context.popupRef.current;if(!I||!B||!L||!re||N&&!k||h.state.transitionStatus==="ending")return;if(X.current=!0,re.style.removeProperty(tA),!N){q.request(()=>h.context.handleScrollArrowVisibility(F||re));return}const M=PF(re);try{let U=h.context.selectedItemTextRef.current;U?.isConnected||(U=!h.select("hasSelectedValue")&&h.context.firstItemTextRef.current?.isConnected?h.context.firstItemTextRef.current:null);const Z=h.context.valueRef.current,ae=Tn(L),ce=ae.getComputedStyle(L),W=ae.getComputedStyle(re),ue=Gt(B),ve=sA(B),ee=Fh(B.getBoundingClientRect(),ve),he=Fh(L.getBoundingClientRect(),ve),te=ee.height,ke=F||re,le=ke.scrollHeight,de=parseFloat(W.borderBottomWidth),Se=parseFloat(ce.marginTop)||10,Te=parseFloat(ce.marginBottom)||10,_e=parseFloat(ce.minHeight)||100,Ae=nA(W),oe=5,me=5,pe=20,Ce=ue.documentElement.clientHeight-Se-Te,Ne=ue.documentElement.clientWidth,be=Ce-ee.bottom+te;let De,Ge=R==="rtl"?ee.right-he.width:ee.left,Ct=0;if(U&&Z){const rn=Fh(Z.getBoundingClientRect(),ve);De=Fh(U.getBoundingClientRect(),ve),Ge=he.left+(R==="rtl"?rn.right-De.right:rn.left-De.left);const nt=rn.top-ee.top+rn.height/2;Ct=De.top-he.top+De.height/2-nt}const Tt=be+Ct+Te+de;let Mt=Math.min(Ce,Tt);const ot=Ce-Se-Te,Kn=Tt-Mt,ms=Ne-me;L.style.left=`${Li(Ge,oe,ms-he.width)}px`,L.style.height=`${Mt}px`,L.style.maxHeight="none",L.style.marginTop=`${Se}px`,L.style.marginBottom=`${Te}px`,re.style.height="100%";const Yn=Vh(ke),vs=Kn>=Yn-Tl;vs&&(Mt=Math.min(Ce,he.height)-(Kn-Yn));const it=ee.topCe-pe||Math.ceil(Mt)+Tl=ot?"0":`${rn}px`,L.style.height=`${Mt}px`,ke.scrollTop=Vh(ke)}else L.style.bottom="0",ke.scrollTop=Kn;if(De){const rn=he.top,nt=he.height,Dt=De.top+De.height/2,qt=Li(nt>0?(Dt-rn)/nt*100:50,0,100);re.style.setProperty(tA,`50% ${qt}%`)}(Vn===Ce||Mt>=Ae)&&($.current=!0),h.context.handleScrollArrowVisibility(ke),v&&h.state.selectedIndex===null&&h.state.activeIndex===null&&h.context.listRef.current[0]!=null&&h.set("activeIndex",0)}finally{M()}},[h,I,L,B,N,A,q,F,v,R,k]),x.useEffect(()=>{if(!N||!L||!I)return;const re=Tn(L);function M(U){h.context.setOpen(!1,qe(C4,U))}return St(re,"resize",M)},[h,N,L,I]);const J={...F?{role:"presentation"}:{role:"listbox","aria-multiselectable":b||void 0,"aria-readonly":y||void 0,id:`${D}-list`},onKeyDown(re){P&&Rh.has(re.key)&&re.stopPropagation()},onScroll(re){F||Q(re.currentTarget)},...N&&{style:F?{height:"100%"}:kF},className:!F&&N?JE.className:void 0},Y=yt("div",t,{ref:[a,h.context.popupRef],state:G,stateAttributesMapping:DF,props:[z,J,xf(H),p]});return s.jsxs(x.Fragment,{children:[!E&&JE.getElement(C),s.jsx(mh,{context:j,modal:!1,disabled:!_,openInteractionType:O,returnFocus:f,restoreFocus:!0,children:Y})]})});function nA(e){const t=e.maxHeight;return t.endsWith("px")&&parseFloat(t)||1/0}function Vh(e){return XE(e.scrollHeight,e.clientHeight)}function sA(e){return kT.getScale(e)}function vf(e,t,a){return e/a[t]}function Fh(e,t){return nf({x:vf(e.x,"x",t),y:vf(e.y,"y",t),width:vf(e.width,"x",t),height:vf(e.height,"y",t)})}const rA=[["transform","none"],["scale","1"],["translate","0 0"]];function PF(e){const{style:t}=e,a={};for(const[o,c]of rA)a[o]=t.getPropertyValue(o),t.setProperty(o,c,"important");return()=>{for(const[o]of rA){const c=a[o];c?t.setProperty(o,c):t.removeProperty(o)}}}const aA=x.createContext(void 0);function lj(){const e=x.useContext(aA);if(!e)throw new Error(un(57));return e}const _F=x.memo(x.forwardRef(function(t,a){const{render:o,className:c,style:d,value:f=null,label:p,disabled:h=!1,nativeButton:b=!1,...y}=t,v=x.useRef(null),j=tj({guess:!0,label:p,textRef:v}),w=wo(),{itemProps:S,multiple:N,disabled:k,readOnly:A}=sj(),P=k||h,R=w.useState("isActive",j.index),C=w.useState("open"),E=w.useState("isSelected",f),D=w.useState("isSelectedByFocus",j.index),I=w.useState("isItemEqualToValue"),O=j.index,_=x.useRef(null);Me(()=>{const G=w.context.valuesRef.current;return G[O]=f,()=>{delete G[O]}},[O,f,w]),Me(()=>{const G=w.state.value,J=w.state.selectedIndex;let Y=J,re;N&&Array.isArray(G)?(Y=qE(O,f,w.context.valuesRef.current,G,I,J),re=Y===O,O===J&&!re&&(w.context.selectedItemTextRef.current=null)):(re=G!==void 0&&ca(f,G,I),re&&(Y=O)),w.set("selectedIndex",Y),re&&v.current&&(w.context.selectedItemTextRef.current=v.current)},[O,N,I,w,f]);const z=x.useRef("mouse"),H=x.useRef(!1),{getButtonProps:B,buttonRef:L}=na({disabled:P,focusableWhenDisabled:!0,native:b,composite:!0}),F={disabled:P,selected:E,highlighted:R};function $(G){if(k||A)return;const J=w.state.value;if(N){const Y=Array.isArray(J)?J:[],re=E?GE(Y,f,I):[...Y,f];w.context.setValue(re,qe(af,G))}else w.context.setValue(f,qe(af,G)),w.context.setOpen(!1,qe(af,G))}function X(){w.context.selectionRef.current.dragY=0}const K={role:"option","aria-selected":E,tabIndex:C&&R?0:-1,onKeyDown(G){w.set("activeIndex",O),G.key===" "&&w.context.typingRef.current&&G.preventDefault()},onClick(G){const J=z.current!=="touch",Y=G.nativeEvent.pointerType,re=J&&Jm(G.nativeEvent)&&(Y!==void 0||R),M=J&&!re&&!H.current;H.current=!1,!(P||M)&&$(G.nativeEvent)},onPointerEnter(G){z.current=G.pointerType},onPointerMove(G){if(G.pointerType==="mouse"&&G.buttons===1){const J=w.context.selectionRef.current;J.dragY+=G.movementY,J.dragY**2>=64&&(J.allowUnselectedMouseUp=!0)}},onPointerDown(G){z.current=G.pointerType,H.current=!0,X()},onMouseUp(){if(X(),P||z.current==="touch"||H.current)return;const G=!w.context.selectionRef.current.allowSelectedMouseUp&&E,J=!w.context.selectionRef.current.allowUnselectedMouseUp&&!E;G||J||(H.current=!0,_.current?.click(),H.current=!1)}},q=yt("div",t,{ref:[L,a,j.ref,_],state:F,props:[S,K,y,B]}),Q=x.useMemo(()=>({selected:E,index:O,textRef:v,selectedByFocus:D}),[E,O,v,D]);return s.jsx(aA.Provider,{value:Q,children:q})})),LF=x.forwardRef(function(t,a){const{selected:o}=lj();return t.keepMounted||o?s.jsx(zF,{...t,ref:a}):null}),zF=x.memo(x.forwardRef((e,t)=>{const{render:a,className:o,style:c,keepMounted:d,...f}=e,{selected:p}=lj(),h=x.useRef(null),{transitionStatus:b,setMounted:y}=Oi(p),j=yt("span",e,{ref:[t,h],state:{selected:p,transitionStatus:b},props:[{"aria-hidden":!0,children:"✔️"},f],stateAttributesMapping:uo});return oa({batch:!0,enabled:!p,open:p,ref:h,onComplete(){p||y(!1)}}),j})),BF=x.memo(x.forwardRef(function(t,a){const{index:o,textRef:c,selectedByFocus:d}=lj(),f=wo(),{render:p,className:h,style:b,...y}=t,v=x.useCallback(w=>{w&&(o===0&&(f.context.firstItemTextRef.current=w),d&&(f.context.selectedItemTextRef.current=w))},[f,o,d]);return yt("div",t,{ref:[v,a,c],props:y})})),ut=mF,dt=jF;function ft({className:e,children:t,...a}){return s.jsxs(vF,{"data-slot":"select-trigger",className:Ue("inline-flex h-[30px] min-w-0 cursor-pointer items-center justify-between gap-1.5 rounded-md border border-control-border bg-control px-2.5","[&>span]:min-w-0 [&>span]:truncate","text-[12.5px] font-normal text-foreground","transition-colors duration-150 ease-out","hover:border-control-border-hover hover:bg-control-hover","focus-visible:border-primary focus-visible:shadow-focus focus-visible:outline-none","data-disabled:cursor-not-allowed data-disabled:bg-control data-disabled:text-muted-foreground data-disabled:opacity-60",e),...a,children:[t,s.jsx(wF,{className:"flex flex-none text-muted-foreground transition-transform duration-[180ms] data-popup-open:rotate-180",children:s.jsx("svg",{viewBox:"0 0 10 6",className:"size-2.5","aria-hidden":"true",children:s.jsx("path",{d:"M1 1l4 4 4-4",stroke:"currentColor",strokeWidth:"1.4",fill:"none",strokeLinecap:"round",strokeLinejoin:"round"})})})]})}function pt({className:e,sideOffset:t=4,children:a,...o}){return s.jsx(SF,{children:s.jsx(EF,{sideOffset:t,alignItemWithTrigger:!1,className:"z-[35]",children:s.jsx(IF,{"data-slot":"select-content",className:Ue("max-h-[260px] min-w-[var(--anchor-width)] overflow-y-auto overscroll-contain rounded-lg border border-border-strong bg-raised p-[5px] shadow-3","outline-none","data-open:animate-in data-open:fade-in-0","data-closed:animate-out data-closed:fade-out-0",e),...o,children:a})})})}function $e({className:e,children:t,...a}){return s.jsxs(_F,{"data-slot":"select-item",className:Ue("flex min-h-7 cursor-pointer items-center gap-2 rounded-sm px-[9px] text-[12.5px] leading-[1.4] text-foreground select-none","transition-colors duration-[120ms] ease-out","data-[highlighted]:bg-nav-hover","data-[selected]:bg-primary-soft data-[selected]:font-semibold data-[selected]:text-primary-fg","data-[highlighted]:data-[selected]:bg-primary-tint","data-disabled:pointer-events-none data-disabled:font-normal data-disabled:text-muted-foreground",e),...a,children:[s.jsx(BF,{className:"min-w-0 flex-1 truncate",children:t}),s.jsx(LF,{className:"flex flex-none text-primary",children:s.jsx("svg",{viewBox:"0 0 12 12",className:"size-3","aria-hidden":"true",children:s.jsx("path",{d:"M2.5 6.2 4.8 8.5 9.5 3.8",stroke:"currentColor",strokeWidth:"1.8",fill:"none",strokeLinecap:"round",strokeLinejoin:"round"})})})]})}function En({options:e,value:t,onValueChange:a,disabled:o=!1,className:c,...d}){const f=x.useRef(null),p=x.useRef(new Map),[h,b]=x.useState({left:0,width:0,ready:!1}),[y,v]=x.useState(!1),j=x.useCallback(()=>{const w=f.current,S=p.current.get(t);if(!w||!S){b(R=>R.ready?{...R,ready:!1}:R);return}const N=w.getBoundingClientRect(),k=S.getBoundingClientRect(),A=k.left-N.left-w.clientLeft,P=k.width;b(R=>R.ready&&R.left===A&&R.width===P?R:{left:A,width:P,ready:!0})},[t]);return x.useLayoutEffect(()=>{j();const w=f.current;if(!w||typeof ResizeObserver>"u")return;const S=new ResizeObserver(()=>j());S.observe(w);for(const N of p.current.values())S.observe(N);return()=>S.disconnect()},[j,e]),x.useEffect(()=>{if(!h.ready||y)return;const w=requestAnimationFrame(()=>v(!0));return()=>cancelAnimationFrame(w)},[h.ready,y]),s.jsxs(zE,{ref:f,"data-slot":"segmented-control",className:Ue("relative inline-flex gap-0.5 rounded-md border border-control-border bg-control p-0.5",c),value:t,disabled:o,onValueChange:w=>a(w),...d,children:[s.jsx("span",{"aria-hidden":"true","data-slot":"segmented-thumb","data-ready":h.ready?"true":"false","data-animate":y?"true":"false",className:Ue("pointer-events-none absolute top-0.5 bottom-0.5 left-0 rounded-sm bg-primary-soft","data-[ready=false]:opacity-0","data-[animate=true]:transition-[transform,width] data-[animate=true]:duration-200 data-[animate=true]:ease-in-out"),style:{transform:`translateX(${h.left}px)`,width:h.width}}),e.map(w=>s.jsxs(PE,{value:w.value,"data-slot":"segmented-item",disabled:w.disabled,"data-zero":w.count===0?"true":void 0,className:Ue("group/seg relative z-[1] inline-flex h-6 items-center gap-[5px] rounded-sm border-0 bg-transparent px-2.5","text-xs font-medium whitespace-nowrap text-subtle select-none","transition-[background-color,color] duration-150 ease-out","hover:bg-nav-hover hover:text-foreground","data-checked:bg-transparent data-checked:text-primary-fg data-checked:hover:bg-transparent","data-disabled:cursor-not-allowed data-disabled:opacity-40","data-[zero=true]:data-unchecked:opacity-45"),ref:S=>{S?p.current.set(w.value,S):p.current.delete(w.value)},children:[w.label,w.count!==void 0&&s.jsx("span",{className:Ue("min-w-4 rounded-pill bg-surface-3 px-[5px] text-center text-[10.5px] font-semibold tabular-nums text-muted-foreground","group-data-checked/seg:bg-primary-tint group-data-checked/seg:text-primary-fg"),children:w.count})]},w.value))]})}const HF=x.createContext(void 0);function UF(){return x.useContext(HF)}const VF=x.forwardRef(function(t,a){const{className:o,defaultPressed:c=!1,disabled:d=!1,form:f,onPressedChange:p,pressed:h,render:b,type:y,value:v,nativeButton:j=!0,style:w,...S}=t,N=ka(v||void 0),k=UF(),A=k?.value??[],P=(d||k?.disabled)??!1,[R,C]=Ka({controlled:k?N!==void 0&&A.indexOf(N)>-1:h,default:c,name:"Toggle",state:"pressed"}),{getButtonProps:E,buttonRef:D}=na({disabled:P,native:j}),I={disabled:P,pressed:R},O=[D,a],_=[{"aria-pressed":R,onClick(B){const L=!R,F=qe(Qn,B.nativeEvent);p?.(L,F),!F.isCanceled&&(N&&k?.setGroupValue?.(N,L,F),!F.isCanceled&&C(L))}},S,E],z=yt("button",t,{enabled:!k,state:I,ref:O,props:_}),H=x.useMemo(()=>({disabled:P,focusableWhenDisabled:!1}),[P]);return k?s.jsx(ME,{tag:"button",render:b,className:o,style:w,metadata:H,state:I,refs:O,props:_}):z}),FF=Xc(["inline-flex shrink-0 cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap border","font-medium transition-[background-color,border-color,color] duration-150 ease-out","outline-none select-none disabled:pointer-events-none disabled:opacity-45","[&_svg]:pointer-events-none [&_svg]:shrink-0","data-pressed:border-primary-bd data-pressed:bg-primary-soft data-pressed:font-semibold data-pressed:text-primary-fg","data-pressed:hover:bg-primary-tint"],{variants:{variant:{default:"border-transparent bg-transparent text-subtle hover:bg-nav-hover hover:text-foreground",outline:"border-control-border bg-control text-foreground shadow-1 hover:border-control-border-hover hover:bg-control-hover"},size:{default:"h-[30px] rounded-md px-3 text-[12.5px]",xs:"h-6 rounded-sm px-2 text-[11.5px]",sm:"h-[26px] rounded-sm px-2.5 text-[12px]",lg:"h-9 rounded-md px-4 text-[13px]"}},defaultVariants:{variant:"default",size:"default"}});function $F({className:e,variant:t,size:a,...o}){return s.jsx(VF,{"data-slot":"toggle",className:Ue(FF({variant:t,size:a}),e),...o})}function qF(e,t,a,o,c,d,f,p,h,b=2){const y=c4(a.current,{event:e,orientation:o,loopFocus:c,rtl:d,cols:b,disabledIndices:f,minIndex:p,maxIndex:h,prevIndex:t>h?p:t,stopEvent:!0});return ho(a.current,y)?void 0:y}const lA=x.createContext(void 0),oA=x.createContext(void 0),iA=x.createContext(void 0),cA=x.createContext(!1),uA=x.createContext("");function Ta(){const e=x.useContext(lA);if(!e)throw new Error(un(22));return e}function $h(){const e=x.useContext(oA);if(!e)throw new Error(un(23));return e}function yf(){const e=x.useContext(iA);if(!e)throw new Error(un(24));return e}function dA(){return x.useContext(uA)}function GF(){return x.useContext(cA)}const KF={id:e=>e.id,labelId:e=>e.labelId,items:e=>e.items,selectedValue:e=>e.selectedValue,hasSelectionChips:e=>{const t=e.selectedValue;return Array.isArray(t)&&t.length>0},hasSelectedValue:e=>{const{selectedValue:t,selectionMode:a}=e;return t==null?!1:a==="multiple"&&Array.isArray(t)?t.length>0:!0},hasNullItemLabel:(e,t)=>t?KE(e.items):!1,open:e=>e.open,mounted:e=>e.mounted,forceMounted:e=>e.forceMounted,inline:e=>e.inline,activeIndex:e=>e.activeIndex,selectedIndex:e=>e.selectedIndex,isActive:(e,t)=>e.activeIndex===t,isSelected:(e,t)=>{const a=e.isItemEqualToValue,o=e.selectedValue;return Array.isArray(o)?o.some(c=>ca(t,c,a)):ca(t,o,a)},transitionStatus:e=>e.transitionStatus,popupProps:e=>e.popupProps,listProps:e=>e.listProps,inputProps:e=>e.inputProps,triggerProps:e=>e.triggerProps,itemProps:e=>e.itemProps,positionerElement:e=>e.positionerElement,listElement:e=>e.listElement,popupId:e=>e.popupId,triggerElement:e=>e.triggerElement,inputElement:e=>e.inputElement,inputGroupElement:e=>e.inputGroupElement,popupSide:e=>e.popupSide,openMethod:e=>e.openMethod,inputInsidePopup:e=>e.inputInsidePopup,inputOwnsFormValue:e=>e.inputOwnsFormValue,selectionMode:e=>e.selectionMode,name:e=>e.name,form:e=>e.form,disabled:e=>e.disabled,readOnly:e=>e.readOnly,required:e=>e.required,grid:e=>e.grid,virtualized:e=>e.virtualized,itemToStringLabel:e=>e.itemToStringLabel,isItemEqualToValue:e=>e.isItemEqualToValue,modal:e=>e.modal,autoHighlight:e=>e.autoHighlight};function fA(e){return e==null?void 0:`${e}-popup`}function YF(e,t){return(a,o)=>a==null?!1:e.contains(a,o,t)}const pA=new Map;function XF(e={}){const t={usage:"search",sensitivity:"base",ignorePunctuation:!0,...e},a=`${hy(e.locale)}|${JSON.stringify(t)}`,o=pA.get(a);if(o)return o;const c=new Intl.Collator(e.locale,t),d={contains(f,p,h){if(!p)return!0;const b=So(f,h);for(let y=0;y<=b.length-p.length;y+=1)if(c.compare(b.slice(y,y+p.length),p)===0)return!0;return!1},startsWith(f,p,h){if(!p)return!0;const b=So(f,h);return c.compare(b.slice(0,p.length),p)===0},endsWith(f,p,h){if(!p)return!0;const b=So(f,h),y=p.length;return b.length>=y&&c.compare(b.slice(b.length-y),p)===0}};return pA.set(a,d),d}const QF=XF;function ZF(e,t=!1){const{overflowY:a}=yr(e);return a!=="auto"&&a!=="scroll"?!1:t?e.clientHeight>0:e.scrollHeight>e.clientHeight}const mA=Symbol("none"),oj={value:mA,index:-1};function WF(e,t,a){const o=e.get(t);if(o!==void 0||a===_h)return o;for(const[c,d]of e)if(ca(c,t,a))return d}function JF(e){const{id:t,onOpenChangeComplete:a,defaultSelectedValue:o=null,selectedValue:c,onSelectedValueChange:d,defaultInputValue:f,inputValue:p,open:h,defaultOpen:b=!1,selectionMode:y,onItemHighlighted:v,name:j,form:w,disabled:S=!1,readOnly:N=!1,required:k=!1,inputRef:A,grid:P=!1,items:R,filteredItems:C,filter:E,filterQuery:D,openOnInputClick:I=!0,autoHighlight:O=!1,keepHighlight:_=!1,highlightItemOnHover:z=!0,loopFocus:H=!0,itemToStringLabel:B,itemToStringValue:L,isItemEqualToValue:F=_h,virtualized:$=!1,inline:X=!1,fillInputOnItemPress:K=!0,modal:q=!1,limit:Q=-1,autoComplete:G="list",formAutoComplete:J,locale:Y,submitOnItemClick:re=!1}=e,{clearErrors:M}=Qc(),{setDirty:U,validityData:Z,setFilled:ae,name:ce,disabled:W,setTouched:ue,setFocused:ve,validationMode:ee,validation:he}=Na(),te=gf(),ke=co({id:t}),le=QF({locale:Y}),de=Array.isArray(R)?null:R;if(de&&typeof de.label!="function")throw new Error(un(100));const Se=de?de.data:R,Te=de?.value,_e=Te?void 0:Se,Ae=x.useMemo(()=>{if(!C||!Te)return;const Oe=zh(C),Be=Oe.map(Te);let lt;return{values:Be,findItem(Qt,In){if(!lt){lt=new Map;for(let gn=0;gnde?Oe=>de.label(Oe,F,Be=>{const lt=Ae?.findItem(Be,F);return lt!=null?de.itemLabel(lt):So(Be,B)}):B,[de,B,Ae,F]),me=x.useMemo(()=>de?Object.assign(Oe=>de.itemLabel(Oe),{selected:Oe=>So(Oe,oe)}):B,[de,oe,B]);function pe(Oe){return So(Oe,oe)}const[Ce,Ne]=x.useState(!1),[be,De]=x.useState(null),Ge=x.useRef(be),Ct=x.useRef([]),Tt=x.useRef([]),Mt=x.useRef(null),ot=x.useRef(null),Kn=x.useRef(null),ms=x.useRef(null),Yn=x.useRef(null),vs=x.useRef(!0),it=x.useRef(!1),$t=x.useRef(null),Vn=x.useRef(null),rn=x.useRef(null),nt=x.useRef(oj),Dt=x.useRef(null),qt=x.useRef([]),es=x.useRef(null),Fn=W||S,$n=ce??j,Dn=y==="multiple",en=y==="single",cs=p!==void 0||f!==void 0,Et=Se!==void 0,xt=C!==void 0;let jt;O==="always"?jt="always":jt=O?"input-change":!1;const[wt,Tr]=Ka({controlled:c,default:Dn?o??Ir:o,name:"Combobox",state:"selectedValue"}),ar=x.useMemo(()=>E===null?()=>!0:E!==void 0?E:YF(le,me),[E,le,me]),Fr=Ps(()=>cs?f??"":en?pe(wt):"").current,[kn,al]=Ka({controlled:p,default:Fr,name:"Combobox",state:"inputValue"}),[ln,hc]=Ka({controlled:h,default:b,name:"Combobox",state:"open"}),sb=rj(Se),mr=!ln&&be!==null?be:String(kn).trim(),Pa=en?pe(wt):"",Go=en&&!Ce&&mr!==""&&Pa.length===mr.length&&le.contains(Pa,mr),Ko=Go?"":D??mr,Fp=Et&&xt&&Go&&(!de||de.hasValue(wt,F)),gc=x.useMemo(()=>Se?zh(Se):Ir,[Se]),Yo=x.useMemo(()=>{if(C&&!Fp)return C;if(!Se)return Ir;if(sb){const Be=Se,lt=[];let Qt=0;for(const In of Be){if(Q>-1&&Qt>=Q)break;const gn=Q>-1?Q-Qt:1/0,Pn=Ko===""?In.items.slice(0,gn):[];if(Ko!=="")for(const hs of In.items){if(Pn.length>=gn)break;ar(hs,Ko,me)&&Pn.push(hs)}if(Pn.length>0){const hs={...In,items:Pn};lt.push(hs),Qt+=Pn.length}}return lt}if(Ko==="")return Q>-1?gc.slice(0,Q):gc;const Oe=[];for(const Be of gc){if(Q>-1&&Oe.length>=Q)break;ar(Be,Ko,me)&&Oe.push(Be)}return Oe},[C,Fp,Se,sb,Ko,Q,ar,me,gc]),Os=x.useMemo(()=>{if(Ae&&Yo===C)return Ae.values;const Oe=zh(Yo);return Te?Oe.map(Be=>Te(Be)):Oe},[Yo,C,Ae,Te]),et=Ps(()=>{let Oe=null;return X&&ln&&Et&&y!=="none"&&(Oe=bf(Os,wt,F,Dn)),new ou({id:ke,labelId:void 0,selectedValue:wt,open:ln,items:_e,selectionMode:y,name:$n,form:w,disabled:Fn,readOnly:N,required:k,grid:P,virtualized:$,openOnInputClick:I,itemToStringLabel:oe,isItemEqualToValue:F,modal:q,autoHighlight:jt,submitOnItemClick:re,hasInputValue:cs,mounted:!1,forceMounted:!1,transitionStatus:"idle",inline:X,activeIndex:null,selectedIndex:Oe,popupProps:{},listProps:{},inputProps:{},triggerProps:{},itemProps:mn,positionerElement:null,listElement:null,popupId:void 0,triggerElement:null,inputElement:null,inputGroupElement:null,popupSide:null,openMethod:null,inputInsidePopup:!0,inputOwnsFormValue:y==="none"},{onOpenChangeComplete:Kt,setOpen:Kt,setInputValue:Kt,setSelectedValue:Kt,setIndices:Kt,handleSelection:Kt,forceMount:Kt,requestSubmit:Kt,listRef:Ct,labelsRef:Tt,popupRef:Mt,emptyRef:Yn,inputRef:ot,startDismissRef:Kn,endDismissRef:ms,keyboardActiveRef:vs,chipsContainerRef:$t,clearRef:Vn,valuesRef:qt,pointerDownItemRef:es,selectionEventRef:rn},KF)}).current,Bl=y==="none"?kn:wt,rb=x.useMemo(()=>y==="none"?Bl:Array.isArray(wt)?wt.map(Oe=>Ca(Oe,L)):Ca(wt,L),[Bl,L,y,wt]),_a=Ie(v),Ms=Ie(a),Ys=et.useState("activeIndex"),Xo=et.useState("selectedIndex"),xc=et.useState("positionerElement"),ab=et.useState("listElement"),bc=et.useState("triggerElement"),vc=et.useState("inputElement"),yc=et.useState("inputGroupElement"),Ds=et.useState("inline"),lr=et.useState("inputInsidePopup"),Hl=et.useState("inputOwnsFormValue"),Ul=en&&!lr&&kn===Pa,jc=Zn(bc),{mounted:Qo,setMounted:ys,transitionStatus:$p}=Oi(ln),{openMethod:lb,triggerProps:qp}=EE(ln),Vl=Ie(()=>rb);tu(lr?jc:ot,ke,Bl,Vl,!Fn,j);const Fl=Ie(()=>{Se?Tt.current=Os.map(pe):et.set("forceMounted",!0)}),Zo=Ie((Oe,Be,lt)=>{if(Be===-1){if(nt.current===oj)return;nt.current=oj}else nt.current={value:Oe,index:Be};_a(Oe,T4(lt,void 0,{index:Be}))}),$r=Ie(Oe=>{const Be={};Oe.activeIndex!==void 0&&(Be.activeIndex=Oe.activeIndex),Oe.selectedIndex!==void 0&&(Be.selectedIndex=Oe.selectedIndex),et.update(Be);const lt=Oe.activeIndex;if(lt===void 0)return;const Qt=Oe.type||Qn;lt===null?Zo(void 0,-1,Qt):Zo(qt.current[lt],lt,Qt)}),qr=Ie((Oe,Be)=>{if(e.onInputValueChange?.(Oe,Be),!Be.isCanceled){if(it.current=Be.reason===Ya,Be.reason===lf){ln&&be!==null&&De(null);const lt=Be.event,Qt=lt.inputType;if(lt.type==="compositionend"||Qt!=null&&Qt!==""&&Qt!=="insertReplacementText"){const gn=Oe.trim()!=="";gn&&Ne(!0),Dt.current={hasQuery:gn};const Pn=et.state.listElement;if(!et.state.virtualized&&Pn){const hs=Mt.current;for(const Er of Kc(Pn.firstElementChild??Pn)){if(!pn(Er)||(hs?!Ze(hs,Er):Er.getAttribute("role")==="dialog"))break;if(ZF(Er)){Er.scrollTop=0;break}}}gn&&jt&&et.state.activeIndex==null&&(ln||Ds)&&et.set("activeIndex",0)}}else Be.reason===Ya&&Oe===""&&et.state.inputInsidePopup&&(Dt.current={hasQuery:!1,selection:!0});al(Oe)}}),ob=Ie(Oe=>{const Be=!Oe&&lr&&!Ds&&kn!==""&&(String(kn).trim()===be||kn===Pa);!Oe&&(Be||kn===""||Ul)&&Ne(!1),De(null),Be&&qr("",qe(Ya))}),It=Ie((Oe,Be)=>{if(ln!==Oe&&(Be.reason===nu&&Et&&Os.length===0&&!Yn.current&&Be.allowPropagation(),e.onOpenChange?.(Oe,Be),!Be.isCanceled&&(Oe&&be!==null&&ob(Be.reason===lf),!Oe&&Ce&&(en?(Ds||De(mr),mr===""&&Ne(!1)):Dn&&(Ds||De(mr),lr&&$r({activeIndex:null}),(!lr||Ds)&&qr("",qe(Ya,Be.event,void 0,{isItemPress:Be.reason===af})))),hc(Oe),!Oe&&lr&&(Be.reason===bo||Be.reason===ch)&&(ue(!0),ve(!1),ee==="onBlur")))){const lt=y==="none"?kn:wt;he.commit(lt)}}),wc=Ie((Oe,Be)=>{if(d?.(Oe,Be),Be.isCanceled)return;Tr(Oe),(y==="none"&&Mt.current&&K||en&&!et.state.inputInsidePopup)&&qr(pe(Oe),qe(Be.reason,Be.event))}),od=Ie((Oe,Be)=>{const lt=fs(Oe),Qt=rn.current??Oe;rn.current=null;const In=qe(af,Qt),gn=lt?.closest("a")?.getAttribute("href");if(gn){gn.startsWith("#")&&It(!1,In);return}if(Dn){const Pn=Array.isArray(wt)?wt:[],hs=Lh(Pn,Be,F),Er=hs?GE(Pn,Be,F):[...Pn,Be];if(wc(Er,In),In.isCanceled||!(ot.current?ot.current.value.trim()!=="":!1))return;if(et.state.inputInsidePopup){qr("",qe(Ya,In.event,void 0,{isItemPress:!0}));const Kr=Dt.current;Kr&&!hs&&(Kr.toggledValue=Be)}else It(!1,In)}else{if(wc(Be,In),In.isCanceled)return;It(!1,In)}}),id=Ie(()=>{const Oe=he.inputRef.current?.form??et.state.inputElement?.form;Oe&&typeof Oe.requestSubmit=="function"&&Oe.requestSubmit()}),Gr=Ie(()=>{if(ys(!1),Ms?.(!1),Ne(!1),De(null),$r(y==="none"?{activeIndex:null,selectedIndex:null}:{activeIndex:null}),Dn&&ot.current&&ot.current.value!==""&&!it.current&&qr("",qe(Ya)),en)if(et.state.inputInsidePopup)ot.current&&ot.current.value!==""&&qr("",qe(Ya));else{const Oe=pe(wt);ot.current&&ot.current.value!==Oe&&qr(Oe,qe(Oe===""?Ya:Qn))}}),hr=x.useMemo(()=>Ds&&xc?{current:xc.closest('[role="dialog"]')}:Mt,[Ds,xc]);oa({enabled:!e.actionsRef,open:ln,ref:hr,onComplete(){ln||Gr()}}),x.useImperativeHandle(e.actionsRef,()=>({unmount:Gr}),[Gr]),Me(function(){const Be=Ge.current!==null&&be===null;if(Ge.current=be,ln&&(!Be||!Et)||(ln||(es.current=null),y==="none"))return;const lt=Et?Os:qt.current;$r({selectedIndex:bf(lt,wt,F,Dn)})},[ln,be,wt,y,Dn,Et,Os,F,$r]),Me(()=>{Se&&(qt.current=Os,Ct.current.length=Os.length)},[Se,Os]),Me(()=>{const Oe=Dt.current;if(Oe){const hs=ln||Ds||et.state.positionerElement?.hidden===!1;if(Oe.hasQuery)jt&&hs&&et.set("activeIndex",0),Dt.current=null;else if(String(kn).trim()===""&&(Dt.current=null,hs)){const Er=Oe.selection;jt==="always"&&!Er&&et.state.selectionMode==="none"&&et.set("activeIndex",0),queueMicrotask(()=>{if(!et.state.open&&!et.state.inline||ot.current&&ot.current.value.trim()!=="")return;const ha=et.state.selectedValue,Kr=et.state.selectionMode==="multiple";if(Kr&&Array.isArray(ha)?ha.length>0:et.state.selectionMode!=="none"&&ha!=null){const Bs=Et||xt?Os:qt.current,za=cu(Bs,Oe.toggledValue,et.state.isItemEqualToValue);et.set("activeIndex",za!==-1?za:bf(Bs,ha,et.state.isItemEqualToValue,Kr))}else Er?et.set("activeIndex",null):jt==="always"&&et.set("activeIndex",0)})}}if(!ln&&!Ds)return;const lt=Et||xt?Os:qt.current,Qt=et.state.activeIndex;if(Qt==null){if(jt==="always"&<.length>0){et.set("activeIndex",0);return}Zo(void 0,-1,Qn);return}if(Qt>=lt.length){Zo(void 0,-1,Qn),et.set("activeIndex",null);return}const In=lt[Qt],gn=nt.current.value,Pn=gn!==mA&&ca(In,gn,et.state.isItemEqualToValue);(nt.current.index!==Qt||!Pn)&&Zo(In,Qt,Qn)},[Ys,jt,Zo,xt,Et,Os,Ds,ln,et,kn]),Me(()=>{if(y==="none"){ae(String(kn)!=="");return}ae(Dn?Array.isArray(wt)&&wt.length>0:wt!=null)},[ae,y,kn,wt,Dn]),x.useEffect(()=>{Et&&jt&&Os.length===0&&$r({activeIndex:null})},[Et,jt,Os.length,$r]);function ib(){ln&&mr!==""&&mr!==String(Fr)&&!Ul&&Ne(!0)}function WS(){ln&&be!==null&&ob(!1)}let cd=!1;function Gp(){!cd&&kn!==Pa&&(cd=!0,qr(Pa,qe(Qn)))}function JS(){y!=="none"&&(M($n),U($E(wt,Z.initialValue,F)),he.change(wt),en&&!cs&&!lr&&Gp())}function gr(){en&&!cs&&!lr&&!Ce&&Gp()}function Kp(){y==="none"&&(M($n),U(kn!==Z.initialValue),he.change(kn))}ia(mr,ib),ia(ln,WS),ia(wt,JS),ia(Pa,gr),ia(Se,gr),ia(kn,Kp);const Sc=$T({open:Ds?!0:ln,onOpenChange:It,elements:{reference:lr?bc:vc,floating:xc}}),ud=P?"grid":"listbox",La=ln||Ds,Yp=La?"true":"false",Nc=x.useMemo(()=>{const Oe=vc?.tagName==="INPUT",Be=vc==null||Oe,lt=Be||La,Qt=Be?{autoComplete:"off",spellCheck:"false",autoCorrect:"off",autoCapitalize:"none"}:{};return lt&&(Qt.role="combobox",Qt["aria-expanded"]=Yp,Qt["aria-haspopup"]=ud,Qt["aria-controls"]=La?ab?.id:void 0,Qt["aria-autocomplete"]=N?"none":G),{reference:Qt,floating:{role:"presentation"}}},[vc,La,Yp,ud,ab?.id,G,N]),$l=hh(Sc,{enabled:!Fn&&I,event:"mousedown-only",toggle:!1,touchOpenDelay:lr?0:100,reason:N4}),dd=pf(Sc,{enabled:!Fn&&!Ds,outsidePressEvent:{mouse:"sloppy",touch:"intentional"},bubbles:Ds?!0:void 0,outsidePress(Oe){const Be=fs(Oe);return!Ze(bc,Be)&&!Ze(Vn.current,Be)&&!Ze($t.current,Be)&&!Ze(yc,Be)}}),ql=XT(Sc,{enabled:!Fn,id:ke,listRef:Ct,activeIndex:Ys,selectedIndex:Xo,virtual:!0,loopFocus:H,allowEscape:H&&!jt,focusItemOnOpen:Ce||y==="none"&&!jt?!1:"auto",focusItemOnHover:z,resetOnPointerLeave:!_,orientation:P?"horizontal":void 0,rtl:te==="rtl",disabledIndices:Ir,grid:P?qF:void 0,onNavigate(Oe,Be){!Be&&!ln||$p==="ending"||$r(Be?{activeIndex:Oe,type:vs.current?I1:P1}:{activeIndex:Oe})}}),Xp=x.useMemo(()=>ta(ql.reference,{onKeyDown(Oe){P&&et.state.activeIndex==null&&(Oe.key==="ArrowLeft"||Oe.key==="ArrowRight")&&Oe.preventBaseUIHandler()}},dd.reference,$l.reference,Nc.reference),[ql.reference,dd.reference,$l.reference,Nc.reference,P,et]),fd=x.useMemo(()=>ta(mf,dd.floating),[dd.floating]),cb=x.useMemo(()=>ta(ql.floating,Nc.floating),[ql.floating,Nc.floating]),ub=x.useMemo(()=>{const Oe=ql.item;return Oe?{...Oe,onFocus:void 0}:mn},[ql.item]);et.useContextCallback("setOpen",It),et.useContextCallback("setInputValue",qr),et.useContextCallback("setSelectedValue",wc),et.useContextCallback("setIndices",$r),et.useContextCallback("handleSelection",od),et.useContextCallback("forceMount",Fl),et.useContextCallback("requestSubmit",id),et.useContextCallback("onOpenChangeComplete",a),BE(()=>{et.update({inline:X,popupProps:fd,listProps:cb,inputProps:Xp,triggerProps:qp,itemProps:ub})});const kc={id:ke,selectedValue:wt,open:ln,mounted:Qo,transitionStatus:$p,items:_e,inline:X,popupProps:fd,listProps:cb,inputProps:Xp,triggerProps:qp,itemProps:ub,openMethod:lb,selectionMode:y,name:$n,form:w,disabled:Fn,readOnly:N,required:k,grid:P,virtualized:$,openOnInputClick:I,itemToStringLabel:oe,modal:q,autoHighlight:jt,isItemEqualToValue:F,submitOnItemClick:re,hasInputValue:cs};Me(()=>{et.update({...kc,inputOwnsFormValue:y==="none"&&(X||!et.state.inputInsidePopup)})},[et,...Object.values(kc)]);const ma=sa(A,he.inputRef),Qp=x.useMemo(()=>({query:mr,hasItems:Et,filteredItems:Yo,flatFilteredValues:Os}),[mr,Et,Yo,Os]),Zp=x.useMemo(()=>Array.isArray(Bl)?"":Ca(Bl,L),[Bl,L]),Wo=Dn&&Array.isArray(wt)&&wt.length>0,ll=Dn||y==="none"&&Hl?void 0:$n,db=x.useMemo(()=>!Dn||!Array.isArray(wt)||!$n?null:wt.map(Oe=>{const Be=Ca(Oe,L);return s.jsx("input",{type:"hidden",form:w,name:$n,value:Be,disabled:Fn},Be)}),[Dn,wt,w,$n,L,Fn]),Wp=s.jsxs(x.Fragment,{children:[e.children,s.jsx("input",{...he.getValidationProps(Fn,{onFocus(){if(lr){bc?.focus();return}(ot.current||bc)?.focus()},onChange(Oe){if(Oe.nativeEvent.defaultPrevented||Fn||N)return;const Be=Oe.currentTarget.value,lt=Be.toLowerCase(),Qt=qe(Qn,Oe.nativeEvent),In=()=>qt.current.findIndex(Pn=>Ca(Pn,L).toLowerCase()===lt||pe(Pn).toLowerCase()===lt);function gn(){if(Dn)return;if(y==="none"){qr(Be,Qt);return}let Pn=In();Pn===-1&&(Pn=qt.current.findIndex((Er,ha)=>{const Kr=Tt.current[ha];return Kr!=null&&Kr.toLowerCase()===lt}));const hs=Pn===-1?void 0:qt.current[Pn];hs!=null&&wc?.(hs,Qt)}en&&(Fl(),Se&&In()===-1&&et.set("forceMounted",!0)),queueMicrotask(gn)}}),id:ke&&ll==null?`${ke}-hidden-input`:void 0,form:w,name:ll,autoComplete:J,disabled:Fn,required:k&&!Wo,readOnly:N,value:Zp,ref:ma,style:ll?su:_i,tabIndex:-1,"aria-hidden":!0,suppressHydrationWarning:!0}),db]});return s.jsx(lA.Provider,{value:et,children:s.jsx(oA.Provider,{value:Sc,children:s.jsx(cA.Provider,{value:Et,children:s.jsx(iA.Provider,{value:Qp,children:s.jsx(uA.Provider,{value:kn,children:Wp})})})})})}function e6(e){const{multiple:t=!1,defaultValue:a,value:o,onValueChange:c,autoComplete:d,...f}=e;return s.jsx(JF,{...f,selectionMode:t?"multiple":"single",selectedValue:o,defaultSelectedValue:a,onSelectedValueChange:c,formAutoComplete:d})}const t6="data-popup-side",n6="data-list-empty",hA={...Yv,...Ti,popupSide:e=>e?{[t6]:e}:null,listEmpty:e=>e?{[n6]:""}:null},s6=x.createContext(void 0);function r6(){return x.useContext(s6)}const gA=x.createContext(void 0);function ij(e){const t=x.useContext(gA);if(t===void 0&&!e)throw new Error(un(21));return t}const xA=x.forwardRef(function(t,a){const o=Ta(),{buttonRef:c,getButtonProps:d}=na({native:!1}),f=sa(a,c);function p(b){o.context.setOpen(!1,qe(dy,b.nativeEvent,b.currentTarget))}const h=d({onClick:p});return s.jsx("span",{ref:f,...h,"aria-label":"Dismiss",tabIndex:void 0,style:su})});function bA(e){const t=e.useState("mounted"),a=e.useState("popupSide"),o=e.useState("positionerElement");return t&&o?a:null}function qh(){return yf().filteredItems.length===0}function a6(e){return e==="rtl"?["ArrowRight","ArrowLeft"]:["ArrowLeft","ArrowRight"]}function l6(e,t){const a=e>=t-1?t-2:e;return a>=0?a:void 0}function vA(e,t,a){const o=e.context.listRef.current[t];o&&(e.context.selectionEventRef.current=a,o.click(),e.context.selectionEventRef.current=null)}const o6=x.forwardRef(function(t,a){const{render:o,className:c,disabled:d=!1,id:f,style:p,...h}=t,{state:b,disabled:y,setTouched:v,setFocused:j,validationMode:w,validation:S}=Na(),{labelId:N}=yl(),k=r6(),P=!!ij(!0),R=Ta(),C=dA(),E=gf(),D=R.useState("required"),I=R.useState("disabled"),O=R.useState("readOnly"),_=R.useState("name"),z=R.useState("form"),H=R.useState("selectionMode"),B=R.useState("autoHighlight"),L=R.useState("inputProps"),F=R.useState("triggerProps"),$=R.useState("open"),X=R.useState("mounted"),K=R.useState("selectedValue"),q=R.useState("id"),Q=R.useState("inline"),G=R.useState("modal"),J=!!B,Y=bA(R),re=y||I||d,M=qh(),U=P||Q,Z=!U||G,ae=ka(f??(U?void 0:q)),ce=P?n1:b,[W,ue]=x.useState(null),ve=x.useRef(!1),ee=x.useRef(null),he=x.useRef(!1),te=H==="none"&&!P,ke=Ie(me=>{const pe=P||R.state.inline;pe&&!R.state.hasInputValue&&R.context.setInputValue("",qe(Qn)),R.update({inputElement:me,inputInsidePopup:pe,inputOwnsFormValue:te})}),le=P?h:S.getValidationProps(re,h);function de(){R.context.setIndices({activeIndex:null,selectedIndex:null,type:R.context.keyboardActiveRef.current?I1:P1})}function Se(){R.context.keyboardActiveRef.current=!1}const Te={...ce,open:$,disabled:re,readOnly:O,popupSide:Y,listEmpty:M};function _e(me){if(!k)return;let pe;const{highlightedChipIndex:Ce}=k,Ne=k.chipsRef.current.length,[be,De]=a6(E);return Ce!==void 0?(me.key===be?(me.preventDefault(),Ce>0?pe=Ce-1:pe=void 0):me.key===De?(me.preventDefault(),Ce0&&(me.preventDefault(),pe=Ne>0?Ne-1:void 0),pe)}const Ae=yt("input",t,{state:Te,ref:[a,R.context.inputRef,ke],props:[L,F,{value:W??C,"aria-readonly":O||void 0,"aria-required":D||void 0,"aria-labelledby":N,disabled:re,readOnly:O,required:H==="none"?D:void 0,form:z,...te&&_&&{name:_},id:ae,onFocus(){if(j(!0),!Q||!he.current)return;he.current=!1;const me=ee.current;me==null||!Object.hasOwn(R.context.valuesRef.current,me)||R.context.setIndices({activeIndex:me})},onBlur(){v(!0),j(!1);const me=R.state.activeIndex;if(Q&&me!==null&&B!=="always"&&(ee.current=me,he.current=!0,R.context.setIndices({activeIndex:null})),w==="onBlur"){const pe=H==="none"?C:K;S.commit(pe)}},onCompositionStart(me){Ym||(ve.current=!0,ue(me.currentTarget.value))},onCompositionEnd(me){ve.current=!1;const pe=me.currentTarget.value;ue(null),R.context.setInputValue(pe,qe(lf,me.nativeEvent))},onChange(me){const pe=me.nativeEvent,Ce=pe.inputType,Ne=!Ce||Ce==="insertReplacementText",be=ve.current||!Ne;function De(Mt){O||re||!Mt||!be||(R.context.setOpen(!0,qe(lf,pe)),J||de())}if(ve.current){const Mt=me.currentTarget.value;ue(Mt),Mt===""&&!R.state.openOnInputClick&&!R.state.inputInsidePopup&&R.context.setOpen(!1,qe(Ya,pe));const ot=Mt.trim(),Kn=J&&ot!=="";De(ot),$&&R.state.activeIndex!==null&&!Kn&&de();return}const Ge=qe(lf,pe);if(R.context.setInputValue(me.currentTarget.value,Ge),Ge.isCanceled)return;const Ct=me.currentTarget.value==="",Tt=qe(Ya,pe);Ct&&!R.state.inputInsidePopup&&(H==="single"&&R.context.setSelectedValue(null,Tt),R.state.openOnInputClick||R.context.setOpen(!1,Tt)),De(me.currentTarget.value.trim()),$&&R.state.activeIndex!==null&&!J&&de()},onKeyDown(me){if(me.ctrlKey||me.shiftKey||me.altKey||me.metaKey)return;if(R.context.keyboardActiveRef.current=!0,re||O){O&&me.key==="Enter"&&$&&R.state.activeIndex!==null&&Cs(me);return}const pe=me.currentTarget,Ce=pe.scrollWidth-pe.clientWidth,Ne=E==="rtl";if(me.key==="Home"){Cs(me);const Ge=c1&&Ne?pe.value.length:0;pe.setSelectionRange(Ge,Ge),pe.scrollLeft=0;return}if(me.key==="End"){Cs(me);const Ge=c1&&Ne?0:pe.value.length;pe.setSelectionRange(Ge,Ge),pe.scrollLeft=Ne?-Ce:Ce;return}if(!X&&me.key==="Escape"){const Ge=H==="multiple"&&Array.isArray(K)?K.length===0:K===null,Ct=qe(nu,me.nativeEvent),Tt=H==="multiple"?[]:null;R.context.setInputValue("",Ct),R.context.setSelectedValue(Tt,Ct),!Ge&&!R.state.inline&&!Ct.isPropagationAllowed&&me.stopPropagation();return}if(k&&me.key==="Backspace"&&pe.value===""&&k.highlightedChipIndex===void 0&&Array.isArray(K)&&K.length>0){const Ge=k.chipsRef.current.length,Ct=Ge>0?Ge-1:K.length-1,Tt=K.filter((Mt,ot)=>ot!==Ct);de(),R.context.setSelectedValue(Tt,qe(Qn,me.nativeEvent));return}const be=k?.highlightedChipIndex!==void 0,De=_e(me);if(k?.setHighlightedChipIndex(De),De!==void 0?k?.chipsRef.current[De]?.focus():be&&R.context.inputRef.current?.focus(),me.which!==229&&me.key==="Enter"&&$){const Ge=R.state.activeIndex,Ct=me.nativeEvent;if(Ge===null){if(Q)return;R.context.setOpen(!1,qe(Qn,Ct));return}Cs(me),vA(R,Ge,Ct)}},onPointerMove:Se,onPointerDown:Se},le],stateAttributesMapping:hA}),oe=P?s.jsx(r1.Provider,{value:s1,children:Ae}):Ae;return s.jsxs(x.Fragment,{children:[$&&Z&&s.jsx(xA,{ref:R.context.startDismissRef}),oe]})}),i6=x.forwardRef(function(t,a){const{render:o,className:c,nativeButton:d=!0,disabled:f=!1,id:p,style:h,...b}=t,{state:y,disabled:v,setTouched:j,setFocused:w,validationMode:S,validation:N}=Na(),{labelId:k}=yl(),A=Ta(),P=A.useState("selectionMode"),R=A.useState("disabled"),C=A.useState("readOnly"),E=A.useState("required"),D=A.useState("positionerElement"),I=A.useState("listElement"),O=A.useState("popupId"),_=A.useState("triggerProps"),z=A.useState("inputInsidePopup"),H=A.useState("id"),B=A.useState("labelId"),L=A.useState("open"),F=A.useState("selectedValue"),$=A.useState("activeIndex"),X=A.useState("selectedIndex"),K=A.useState("hasSelectedValue"),q=$h(),Q=dA(),G=er(),J=v||R||f,Y=qh(),re=bA(A);co({id:z?p:void 0});const M=z?p??H:p,U=QE(k,B);let Z;L&&z?Z=O??fA(H):L&&(Z=I?.id);const ae=x.useRef("");function ce(le){ae.current=le.pointerType}const{reference:W}=QT(q,{enabled:!L&&!C&&!R&&P==="single",listRef:A.context.labelsRef,activeIndex:$,selectedIndex:X,onMatch(le){const de=A.context.valuesRef.current[le];de!==void 0&&A.context.setSelectedValue(de,qe(Qn))}}),{reference:ue}=hh(q,{enabled:!R,event:"mousedown"}),{buttonRef:ve,getButtonProps:ee}=na({native:d,disabled:J}),he={...y,readOnly:C,open:L,disabled:J,popupSide:re,listEmpty:Y,placeholder:P==="none"?!1:!K},te=Ie(le=>{A.set("triggerElement",le)});return yt("button",t,{ref:[a,ve,te],state:he,props:[_,ue,W,{id:M,tabIndex:z?0:-1,role:z?"combobox":void 0,"aria-expanded":L,"aria-haspopup":z?"dialog":"listbox","aria-controls":Z,"aria-required":z&&E||void 0,"aria-readonly":z&&C||void 0,"aria-labelledby":U,onPointerDown:ce,onPointerEnter:ce,onFocus(){w(!0),!J&&G.start(0,A.context.forceMount)},onBlur(le){if(!Ze(D,le.relatedTarget)&&(j(!0),w(!1),S==="onBlur")){const de=P==="none"?Q:F;N.commit(de)}},onMouseDown(le){if(J||(z||q.set("domReferenceElement",le.currentTarget),A.context.forceMount(),ae.current!=="touch"&&(A.context.inputRef.current?.focus(),z||le.preventDefault()),L))return;const de=Gt(le.currentTarget);function Se(Te){const _e=A.state.triggerElement;if(!_e)return;const Ae=fs(Te),oe=A.state.positionerElement,me=A.state.listElement;Ze(_e,Ae)||Ze(oe,Ae)||Ze(me,Ae)||ZE(Te,_e)||A.context.setOpen(!1,qe(_1,Te))}z&&de.addEventListener("mouseup",Se,{once:!0})},onKeyDown(le){(le.key==="ArrowDown"||le.key==="ArrowUp")&&(Cs(le),A.context.setOpen(!0,qe(fy,le.nativeEvent)),A.context.inputRef.current?.focus())}},N.getValidationProps(J,b),ee],stateAttributesMapping:hA})}),c6=x.createContext(null);function u6(){return x.useContext(c6)}function d6(e){const{children:t}=e,{filteredItems:a}=yf(),o=u6(),c=o?o.items:a;return s.jsx(x.Fragment,{children:c.map(t)})}const f6=x.forwardRef(function(t,a){var o;const{render:c,className:d,style:f,children:p,...h}=t,b=Ta(),y=$h(),v=!!ij(!0),{filteredItems:j,hasItems:w}=yf(),S=b.useState("selectionMode"),N=b.useState("grid"),k=b.useState("readOnly"),A=b.useState("listProps"),P=b.useState("virtualized"),R=b.useState("forceMounted"),C=S==="multiple",E=j.length===0,D=Ie(L=>{b.set("positionerElement",L)}),I=Ie(L=>{b.set("listElement",L)}),O=x.useMemo(()=>typeof p=="function"?o||(o=s.jsx(d6,{children:p})):p,[p]),_={empty:E},z=y.useState("floatingId"),H=yt("div",t,{state:_,ref:[a,I,v?null:D],props:[A,{children:O,tabIndex:-1,id:z,role:N?"grid":"listbox","aria-multiselectable":C?"true":void 0,"aria-readonly":!N&&k?!0:void 0,onKeyDown(L){if(!(b.state.disabled||b.state.readOnly)&&L.key==="Enter"){const F=b.state.activeIndex;if(F==null)return;Cs(L),vA(b,F,L.nativeEvent)}},onKeyDownCapture(){b.context.keyboardActiveRef.current=!0},onPointerMoveCapture(){b.context.keyboardActiveRef.current=!1}},h]});if(P)return H;const B=w&&!R?void 0:b.context.labelsRef;return s.jsx(nj,{elementsRef:b.context.listRef,labelsRef:B,children:H})}),p6="⁠",m6=200;function h6(e){const t=e.ownerDocument.createTreeWalker(e,NodeFilter.SHOW_TEXT);let a=null;for(;t.nextNode();){const o=t.currentNode;o.nodeValue!==""&&(a=o)}return a}function g6(){const e=er(),t=x.useRef(null);return x.useEffect(()=>{if(tf)return;const a=t.current;if(a==null)return;const o=h6(a);if(o==null)return;const c=o.data,d=`${c}${p6}`;return o.nodeValue=d,e.start(m6,()=>{o.nodeValue===d&&(o.nodeValue=c)}),()=>{e.clear(),o.nodeValue===d&&(o.nodeValue=c)}},[t,e]),t}const yA=x.createContext(void 0);function x6(){const e=x.useContext(yA);if(e===void 0)throw new Error(un(20));return e}const b6=x.forwardRef(function(t,a){const{keepMounted:o=!1,...c}=t,d=Ta(),f=d.useState("mounted"),p=d.useState("forceMounted");return f||o||p?s.jsx(yA.Provider,{value:o,children:s.jsx(ph,{ref:a,...c})}):null}),v6=x.forwardRef(function(t,a){const{render:o,className:c,anchor:d,positionMethod:f,side:p,align:h,sideOffset:b,alignOffset:y,collisionBoundary:v="clipping-ancestors",collisionPadding:j,arrowPadding:w,sticky:S,disableAnchorTracking:N=!1,collisionAvoidance:k=rT,style:A,...P}=t,R=Ta(),C=$h(),E=x6(),D=R.useState("modal"),I=R.useState("open"),O=R.useState("mounted"),_=R.useState("openMethod"),z=R.useState("positionerElement"),H=R.useState("triggerElement"),B=R.useState("inputElement"),L=R.useState("inputGroupElement"),F=R.useState("inputInsidePopup"),$=R.useState("transitionStatus"),X=qh(),q=Th({anchor:d??(F?H:L??B),floatingRootContext:C,positionMethod:f,mounted:O,side:p,sideOffset:b,align:h,alignOffset:y,arrowPadding:w,collisionBoundary:v,collisionPadding:j,sticky:S,disableAnchorTracking:N,keepMounted:E,collisionAvoidance:k,lazyFlip:!0});aj(I&&D,_==="touch",z,H);const Q={open:I,side:q.side,align:q.align,anchorHidden:q.anchorHidden,empty:X};Me(()=>{R.set("popupSide",q.side)},[R,q.side]);const G=Ie(Y=>{R.set("positionerElement",Y)}),J=Eh(t,Q,{styles:q.positionerStyles,transitionStatus:$,props:P,refs:[a,G],hidden:!O,inert:!I});return s.jsxs(gA.Provider,{value:q,children:[O&&D&&s.jsx(Mh,{inert:Ah(!I),cutout:L??B??H}),J]})}),y6={...Zc,...uo},j6=x.forwardRef(function(t,a){const{render:o,className:c,style:d,initialFocus:f,finalFocus:p,...h}=t,b=Ta(),y=ij(),v=$h(),j=b.useState("mounted"),w=b.useState("open"),S=b.useState("openMethod"),N=b.useState("popupProps"),k=b.useState("transitionStatus"),A=b.useState("inputInsidePopup"),P=b.useState("inputElement"),R=b.useState("modal"),C=b.useState("id"),E=qh(),D=h.id??(A?fA(C):void 0);Me(()=>(b.set("popupId",b.context.popupRef.current?.id||D),()=>{b.set("popupId",void 0)}),[b,D]),oa({open:w,ref:b.context.popupRef,onComplete(){w&&b.context.onOpenChangeComplete(!0)}});const I={open:w,side:y.side,align:y.align,anchorHidden:y.anchorHidden,transitionStatus:k,empty:E},O=yt("div",t,{state:I,ref:[a,b.context.popupRef],props:[N,{id:D,role:A?"dialog":"presentation",onFocus(L){const F=fs(L.nativeEvent);S!=="touch"&&(Ze(b.state.listElement,F)||F===L.currentTarget)&&b.context.inputRef.current?.focus()}},xf(k),h],stateAttributesMapping:y6}),z=f===void 0?A?L=>L==="touch"?b.context.popupRef.current:P:!1:f;let H;p!=null?H=p:H=A?void 0:!1;const B=!A||R;return s.jsx(mh,{context:v,disabled:!j,modal:B,openInteractionType:S,initialFocus:z,returnFocus:H,getInsideElements:()=>[b.context.startDismissRef.current,b.context.endDismissRef.current],children:s.jsxs(x.Fragment,{children:[O,B&&s.jsx(xA,{ref:b.context.endDismissRef})]})})}),w6=x.forwardRef(function(t,a){const{render:o,className:c,style:d,...f}=t;return yt("span",t,{ref:a,props:[{"aria-hidden":!0,children:"▼"},f]})}),jA=x.createContext(void 0);function wA(){const e=x.useContext(jA);if(!e)throw new Error(un(19));return e}const S6=x.createContext(!1);function N6(){return x.useContext(S6)}function SA(e){const{componentProps:t,forwardedRef:a,virtualized:o,indexFromFilter:c}=e,{render:d,className:f,style:p,value:h=null,index:b,disabled:y=!1,nativeButton:v=!1,...j}=t,w=x.useRef(null),S=tj({guess:!0,index:b,textRef:w}),N=Ta(),k=N6(),A=GF(),P=N.useState("selectionMode"),R=N.useState("disabled"),C=N.useState("readOnly"),E=N.useState("isItemEqualToValue"),D=R||y,I=P!=="none",O=b??c??S.index,_=O!==-1,z=N.useState("id"),H=N.useState("isActive",O),B=N.useState("isSelected",h),L=N.useState("itemProps"),F=x.useRef(null),$=z!=null&&_?`${z}-${O}`:void 0,X=B&&I;Me(()=>{if(!(_&&(o||b!=null)))return;const U=N.context.listRef.current;return U[O]=F.current,()=>{delete U[O]}},[_,o,O,b,N]),Me(()=>{if(!_||A)return;const M=N.context.valuesRef.current;return M[O]=h,()=>{delete M[O]}},[_,A,O,h,N]),Me(()=>{if(!_||A)return;const M=N.state.selectedValue;let U=N.state.selectedIndex;N.state.selectionMode==="multiple"&&Array.isArray(M)?U=qE(O,h,N.context.valuesRef.current,M,E,U):ca(h,M,E)&&(U=O),N.set("selectedIndex",U)},[_,A,N,O,h,E]);const{getButtonProps:K,buttonRef:q}=na({disabled:D,focusableWhenDisabled:!0,native:v,composite:!0}),Q={disabled:D,selected:X,highlighted:H};function G(M){function U(){N.context.handleSelection(M,h)}N.state.submitOnItemClick?(Ts.flushSync(U),N.context.requestSubmit()):U()}const J={id:$,role:k?"gridcell":"option","aria-selected":I?X:void 0,tabIndex:void 0,onPointerDownCapture(M){M.isPrimary&&(N.context.pointerDownItemRef.current=M.currentTarget),M.preventDefault()},onMouseDown(M){M.preventDefault()},onClick(M){D||C||G(M.nativeEvent)},onMouseUp(M){const U=N.context.pointerDownItemRef.current===M.currentTarget;N.context.pointerDownItemRef.current=null,!(D||C||M.button!==0||U||!H)&&G(M.nativeEvent)}},Y=yt("div",t,{ref:[q,a,S.ref,F],state:Q,props:[L,J,j,K]}),re=x.useMemo(()=>({selected:X,textRef:w}),[X,w]);return s.jsx(jA.Provider,{value:re,children:Y})}function k6(e){const{componentProps:t,forwardedRef:a}=e,c=Ta().useState("isItemEqualToValue"),{flatFilteredValues:d}=yf(),f=t.value??null,p=cu(d,f,c);return s.jsx(SA,{componentProps:t,forwardedRef:a,virtualized:!0,indexFromFilter:p})}const C6=x.memo(x.forwardRef(function(t,a){const c=Ta().useState("virtualized");return c&&t.index==null?s.jsx(k6,{componentProps:t,forwardedRef:a}):s.jsx(SA,{componentProps:t,forwardedRef:a,virtualized:c,indexFromFilter:void 0})})),T6=x.forwardRef(function(t,a){const{selected:o}=wA();return t.keepMounted||o?s.jsx(E6,{...t,ref:a}):null}),E6=x.memo(x.forwardRef((e,t)=>{const{render:a,className:o,style:c,keepMounted:d,...f}=e,{selected:p}=wA(),h=x.useRef(null),{transitionStatus:b,setMounted:y}=Oi(p),j=yt("span",e,{ref:[t,h],state:{selected:p,transitionStatus:b},props:[{"aria-hidden":!0,children:"✔️"},f],stateAttributesMapping:uo});return oa({batch:!0,enabled:!p,open:p,ref:h,onComplete(){p||y(!1)}}),j})),A6=x.forwardRef(function(t,a){const{render:o,className:c,style:d,children:f,...p}=t,{filteredItems:h}=yf(),b=Ta(),y=g6(),v=h.length===0?f:null;return yt("div",t,{ref:[a,b.context.emptyRef,y],props:[{children:v,role:"status","aria-live":"polite","aria-atomic":!0},p]})}),R6="、";function Gh({value:e,onValueChange:t,options:a,placeholder:o="",emptyHint:c=vo("没有匹配的选项"),searchPlaceholder:d=vo("搜索…"),disabled:f=!1,className:p,id:h,title:b,...y}){const v=x.useMemo(()=>a.filter(w=>e.includes(w.value)),[a,e]),j=v.length?v.map(w=>w.label).join(R6):o;return s.jsxs(e6,{items:a,multiple:!0,value:v,onValueChange:w=>t(w.map(S=>S.value)),isItemEqualToValue:(w,S)=>w?.value===S?.value,disabled:f,children:[s.jsxs(i6,{"data-slot":"multi-select-trigger",id:h,title:b,"aria-label":y["aria-label"],className:Ue("inline-flex h-[30px] min-w-0 cursor-pointer items-center justify-between gap-1.5 rounded-md border border-control-border bg-control px-2.5","text-[12.5px] font-normal text-foreground","transition-colors duration-150 ease-out","hover:border-control-border-hover hover:bg-control-hover","focus-visible:border-primary focus-visible:shadow-focus focus-visible:outline-none","data-disabled:cursor-not-allowed data-disabled:bg-control data-disabled:text-muted-foreground data-disabled:opacity-60",p),children:[s.jsx("span",{"data-slot":"multi-select-value",className:"min-w-0 flex-1 truncate text-left data-[placeholder]:text-muted-foreground","data-placeholder":v.length?void 0:"",title:j,children:j}),s.jsx(w6,{className:"flex flex-none text-muted-foreground transition-transform duration-[180ms] data-popup-open:rotate-180",children:s.jsx("svg",{viewBox:"0 0 10 6",className:"size-2.5","aria-hidden":"true",children:s.jsx("path",{d:"M1 1l4 4 4-4",stroke:"currentColor",strokeWidth:"1.4",fill:"none",strokeLinecap:"round",strokeLinejoin:"round"})})})]}),s.jsx(b6,{children:s.jsx(v6,{sideOffset:4,className:"z-[35]",children:s.jsxs(j6,{"data-slot":"multi-select-content",className:Ue("flex max-h-[280px] w-[max(var(--anchor-width),220px)] flex-col overflow-hidden","rounded-lg border border-border-strong bg-raised shadow-3 outline-none","data-open:animate-in data-open:fade-in-0","data-closed:animate-out data-closed:fade-out-0"),children:[s.jsxs("div",{className:"flex flex-none items-center gap-1.5 border-b border-hairline px-2.5 py-1.5",children:[s.jsxs("svg",{viewBox:"0 0 12 12",className:"size-3 flex-none text-muted-foreground","aria-hidden":"true",children:[s.jsx("circle",{cx:"5",cy:"5",r:"3.4",stroke:"currentColor",strokeWidth:"1.3",fill:"none"}),s.jsx("path",{d:"M7.6 7.6 10.5 10.5",stroke:"currentColor",strokeWidth:"1.3",strokeLinecap:"round"})]}),s.jsx(o6,{placeholder:d,className:"min-w-0 flex-1 border-0 bg-transparent p-0 text-[12.5px] text-foreground outline-none placeholder:text-muted-foreground"})]}),s.jsx(A6,{children:s.jsx("div",{className:"px-2.5 py-3 text-[11.5px] leading-[1.5] text-muted-foreground",children:c})}),s.jsx(f6,{className:"min-h-0 flex-1 overflow-y-auto overscroll-contain p-[5px]",children:w=>s.jsxs(C6,{value:w,className:Ue("grid cursor-pointer grid-cols-[14px_minmax(0,1fr)] items-center gap-2 rounded-sm px-[9px] py-[5px]","text-[12.5px] leading-[1.4] text-foreground select-none outline-none","transition-colors duration-[120ms] ease-out","data-[highlighted]:bg-nav-hover","data-[selected]:bg-primary-soft data-[selected]:font-semibold data-[selected]:text-primary-fg","data-[highlighted]:data-[selected]:bg-primary-tint","data-disabled:pointer-events-none data-disabled:text-muted-foreground"),children:[s.jsx(T6,{className:"col-start-1 flex text-primary",children:s.jsx("svg",{viewBox:"0 0 12 12",className:"size-3","aria-hidden":"true",children:s.jsx("path",{d:"M2.5 6.2 4.8 8.5 9.5 3.8",stroke:"currentColor",strokeWidth:"1.8",fill:"none",strokeLinecap:"round",strokeLinejoin:"round"})})}),s.jsx("span",{className:"col-start-2 min-w-0 truncate",children:w.label})]},w.value)})]})})})]})}const NA=x.createContext(void 0);function jf(e){const t=x.useContext(NA);if(t===void 0&&!e)throw new Error(un(47));return t}const O6={...Hy,disabled:e=>e.disabled,instantType:e=>e.instantType,openMethod:e=>e.openMethod,openChangeReason:e=>e.openChangeReason,modal:e=>e.modal,focusManagerModal:e=>e.focusManagerModal,stickIfOpen:e=>e.stickIfOpen,titleElementId:e=>e.titleElementId,descriptionElementId:e=>e.descriptionElementId,openOnHover:e=>e.openOnHover,closeDelay:e=>e.closeDelay,adaptiveOrigin:e=>e.adaptiveOrigin};class M6 extends ou{constructor(a,o,c){const d=new vh;super(D6(a,d,o,c),I6(d),O6);cn(this,"setOpen",(a,o)=>{const c=o.reason===qs,d=o.reason===xo&&o.event.detail===0,f=!a&&(o.reason===nu||o.reason==null),p=zT(o),h=this.select("activeTriggerId");if(!a&&o.reason===dy&&o.trigger==null&&h!=null&&(o.trigger=this.context.triggerElements.getById(h)??this.select("activeTriggerElement")??void 0),this.context.onOpenChange?.(a,o),o.isCanceled)return;this.state.floatingRootContext.dispatchOpenChange(a,o);const b=()=>{const v=Iy(this.state,a,o.trigger,p());v.openChangeReason=o.reason,this.update(v)};c?(this.set("stickIfOpen",!0),this.context.stickIfOpenTimeout.start(gH,()=>{this.set("stickIfOpen",!1)}),Ts.flushSync(b)):b();let y;d?y="click":f?y="dismiss":o.reason===bo&&(y="focus"),this.set("instantType",y)})}}function D6(e,t,a,o=!1){const c={...zy(t,a,o),disabled:!1,modal:!1,focusManagerModal:!1,instantType:void 0,openMethod:null,openChangeReason:null,titleElementId:void 0,descriptionElementId:void 0,stickIfOpen:!0,openOnHover:!1,closeDelay:0,adaptiveOrigin:void 0,...e};return c.open&&e?.mounted===void 0&&(c.mounted=!0),c}function I6(e){return{popupRef:x.createRef(),onOpenChange:void 0,onOpenChangeComplete:void 0,triggerFocusTargetRef:x.createRef(),beforeContentFocusGuardRef:x.createRef(),stickIfOpenTimeout:new la,triggerElements:e}}const kA=uh(function({props:t}){const{children:a,open:o,defaultOpen:c=!1,onOpenChange:d,onOpenChangeComplete:f,modal:p=!1,handle:h,triggerId:b,defaultTriggerId:y=null}=t,v=_6(h,{modal:p,open:c,openProp:o,activeTriggerId:y,triggerIdProp:b});v.useControlledProp("openProp",o),v.useControlledProp("triggerIdProp",b);const j=v.useState("open"),w=v.useState("mounted"),S=v.useState("payload");v.useContextCallback("onOpenChange",d),v.useContextCallback("onOpenChangeComplete",f),HT(v,j),Py(v);const{forceUnmount:N}=_y(j,v,()=>{v.update({stickIfOpen:!0,openChangeReason:null})});v.useSyncedValues({modal:p}),x.useEffect(()=>{j||v.context.stickIfOpenTimeout.clear()},[v,j]),x.useImperativeHandle(t.actionsRef,()=>({unmount:N,close:()=>v.setOpen(!1,qe(py))}),[N,v]);const k=j||w;return s.jsxs(NA.Provider,{value:v,children:[h&&s.jsx(Dy,{handle:h,store:v}),k&&s.jsx(L6,{store:v,modal:p}),typeof a=="function"?a({payload:S}):a]})});function P6(e){return jf(!0)?s.jsx(kA,{props:e}):s.jsx(SH,{children:s.jsx(kA,{props:e})})}function _6(e,t){const a=My((o,c)=>new M6(t,o,c));return x.useEffect(()=>a.context.stickIfOpenTimeout.disposeEffect(),[a]),a}function L6({store:e,modal:t}){const a=e.useState("floatingRootContext"),o=pf(a,{outsidePressEvent:{mouse:t==="trap-focus"?"sloppy":"intentional",touch:"sloppy"}}),c=o.reference,d=o.floating;return Ly(e,{activeTriggerProps:c,inactiveTriggerProps:c,popupProps:d}),null}const z6=300;function B6(e,t){const a=x.useRef(null);function o(d){Ts.flushSync(()=>{e.setOpen(!1,qe(bo,d.nativeEvent,d.currentTarget))}),x4(a.current)?.focus()}function c(d){const f=e.select("positionerElement");if(f&&Jc(d,f))e.context.beforeContentFocusGuardRef.current?.focus();else{Ts.flushSync(()=>{e.setOpen(!1,qe(bo,d.nativeEvent,d.currentTarget))});let p=g4(e.context.triggerFocusTargetRef.current||t.current);for(;p!==null&&Ze(f,p);){const h=p;if(p=iy(p),p===h)break}p?.focus()}}return{preFocusGuardRef:a,handlePreFocusGuardFocus:o,handleFocusTargetFocus:c}}const H6=G1(function(t,a){const{render:o,className:c,style:d,disabled:f=!1,nativeButton:p=!0,handle:h,payload:b,openOnHover:y=!1,delay:v=z6,closeDelay:j=0,id:w,...S}=t,N=jf(!0),A=FT(h)??N;if(!A)throw new Error(un(74));const P=ka(w),R=A.useState("isTriggerActive",P),C=A.useState("floatingRootContext"),E=A.useState("isOpenedByTrigger",P),D=A.useState("triggerPopupId",P),I=x.useRef(null),{registerTrigger:O,isMountedByThisTrigger:_}=BT(P,I,A,{payload:b,disabled:f,openOnHover:y,closeDelay:j}),z=A.useState("openChangeReason"),H=A.useState("stickIfOpen"),B=A.useState("openMethod"),L=A.useState("focusManagerModal"),F=KT(C,{enabled:!f&&y&&(B!=="touch"||z!==xo),mouseOnly:!0,move:!1,handleClose:WT(),restMs:v,delay:{close:j},triggerElementRef:I,isActiveTrigger:R,isClosing:()=>A.select("transitionStatus")==="ending"}),$=hh(C,{stickIfOpen:H}),X=TE(()=>A.select("open"),ae=>{A.set("openMethod",ae)}),K=A.useState("triggerProps",_),{getButtonProps:q,buttonRef:Q}=na({disabled:f,native:p}),G={open(ae){return ae&&z===xo?Yv.open(ae):Kv.open(ae)}},{preFocusGuardRef:J,handlePreFocusGuardFocus:Y,handleFocusTargetFocus:re}=B6(A,I),U=yt("button",t,{state:{disabled:f,open:E},ref:[Q,a,O,I],props:[$.reference,F,K,X,{[sT]:"",id:P,"aria-haspopup":"dialog","aria-expanded":E,"aria-controls":D},S,q],stateAttributesMapping:G}),Z=s.jsx(x.Fragment,{children:U},P);return _&&!L?s.jsxs(x.Fragment,{children:[s.jsx(ru,{ref:J,onFocus:Y}),Z,s.jsx(ru,{ref:A.context.triggerFocusTargetRef,onFocus:re})]}):Z}),CA=x.createContext(void 0);function U6(){const e=x.useContext(CA);if(e===void 0)throw new Error(un(45));return e}const V6=x.forwardRef(function(t,a){const{keepMounted:o=!1,...c}=t;return jf().useState("mounted")||o?s.jsx(CA.Provider,{value:o,children:s.jsx(ph,{ref:a,...c})}):null}),TA=x.createContext(void 0);function F6(){const e=x.useContext(TA);if(!e)throw new Error(un(46));return e}const $6=x.forwardRef(function(t,a){const{render:o,className:c,style:d,anchor:f,positionMethod:p,side:h,align:b,sideOffset:y,alignOffset:v,collisionBoundary:j="clipping-ancestors",collisionPadding:w,arrowPadding:S,sticky:N,disableAnchorTracking:k=!1,collisionAvoidance:A=aT,...P}=t,R=jf(),C=U6(),E=jH(),D=R.useState("floatingRootContext"),I=R.useState("mounted"),O=R.useState("open"),_=R.useState("openChangeReason"),z=R.useState("activeTriggerElement"),H=R.useState("modal"),B=R.useState("openMethod"),L=R.useState("positionerElement"),F=R.useState("instantType"),$=R.useState("transitionStatus"),X=R.useState("adaptiveOrigin"),K=x.useRef(null),q=D1(L),Q=Th({anchor:f,floatingRootContext:D,positionMethod:p,mounted:I,side:h,sideOffset:y,align:b,alignOffset:v,arrowPadding:S,collisionBoundary:j,collisionPadding:w,sticky:N,disableAnchorTracking:k,keepMounted:C,nodeId:E,collisionAvoidance:A,adaptiveOrigin:X}),G=D.useState("domReferenceElement");Me(()=>{const U=G,Z=K.current;if(U&&(K.current=U),Z&&U&&U!==Z){R.set("instantType",void 0);const ae=new AbortController;return q(()=>{R.set("instantType","trigger-change")},ae.signal),()=>{ae.abort()}}},[G,q,R]);const J=H===!0&&_!==qs;aj(O&&J,B==="touch",L,z);const Y=R.useStateSetter("positionerElement"),re={open:O,side:Q.side,align:Q.align,anchorHidden:Q.anchorHidden,instant:F},M=Eh(t,re,{styles:Q.positionerStyles,transitionStatus:$,props:P,refs:[a,Y],hidden:!I,inert:!O});return s.jsxs(TA.Provider,{value:Q,children:[I&&J&&s.jsx(Mh,{inert:Ah(!O),cutout:z}),s.jsx(wH,{id:E,children:M})]})}),q6=x.createContext(void 0);function G6(){const[e,t]=x.useState(0),a=Ie(()=>(t(c=>c+1),()=>{t(c=>Math.max(0,c-1))}));return{context:x.useMemo(()=>({register:a}),[a]),hasClosePart:e>0}}const K6=x.forwardRef(function(t,a){const{render:o,className:c,style:d,initialFocus:f,finalFocus:p,...h}=t,b=jf(),y=F6(),v=eA()!=null,{context:j,hasClosePart:w}=G6(),S=b.useState("open"),N=b.useState("openMethod"),k=b.useState("instantType"),A=b.useState("transitionStatus"),P=b.useState("popupProps"),R=b.useState("titleElementId"),C=b.useState("descriptionElementId"),E=b.useState("modal"),D=b.useState("mounted"),I=b.useState("openChangeReason"),O=b.useState("activeTriggerElement"),_=b.useState("floatingRootContext"),z=_.useState("floatingId"),H=b.useState("disabled"),B=b.useState("openOnHover"),L=b.useState("closeDelay");oa({open:S,ref:b.context.popupRef,onComplete(){S&&b.context.onOpenChangeComplete?.(!0)}}),GT(_,{enabled:B&&!H,closeDelay:L});const F=f===void 0?_T(b.context.popupRef):f,$=E!==!1&&w;b.useSyncedValue("focusManagerModal",$);const X=b.useStateSetter("popupElement"),K={open:S,side:y.side,align:y.align,instant:k,transitionStatus:A},q=yt("div",t,{state:K,ref:[a,b.context.popupRef,X],props:[P,{id:z,role:"dialog",...mf,"aria-labelledby":R,"aria-describedby":C,onKeyDown(Q){v&&Rh.has(Q.key)&&Q.stopPropagation()}},xf(A),h],stateAttributesMapping:Xv});return s.jsx(mh,{context:_,openInteractionType:N,modal:$,disabled:!D||I===qs,initialFocus:F,returnFocus:p,restoreFocus:"popup",previousFocusableElement:pn(O)?O:void 0,nextFocusableElement:b.context.triggerFocusTargetRef,beforeContentFocusGuardRef:b.context.beforeContentFocusGuardRef,children:s.jsx(q6.Provider,{value:j,children:q})})});function wf(e){return s.jsx(P6,{"data-slot":"popover",...e})}function Kh(e){return s.jsx(H6,{"data-slot":"popover-trigger",...e})}function Sf({className:e,sideOffset:t=6,align:a="start",side:o,alignOffset:c,collisionPadding:d=8,anchor:f,children:p,...h}){return s.jsx(V6,{children:s.jsx($6,{className:"z-[35] outline-none",sideOffset:t,align:a,side:o,alignOffset:c,collisionPadding:d,anchor:f,children:s.jsx(K6,{"data-slot":"popover-content",className:Ue("flex max-h-[min(70vh,560px)] flex-col overflow-hidden rounded-lg border border-border-strong bg-raised text-foreground shadow-3","outline-none","data-open:animate-in data-open:fade-in-0 data-open:slide-in-from-top-1","data-closed:animate-out data-closed:fade-out-0",e),...h,children:p})})})}function Y6({page:e,pageCount:t,onPageChange:a,disabled:o=!1,className:c,infoFirst:d=!1}){const f=Math.max(1,Number(t)||1),p=Math.min(Math.max(1,Number(e)||1),f),h=s.jsx("span",{"data-slot":"pager-info",className:"text-[11.5px] text-subtle [font-variant-numeric:tabular-nums]",children:vo("第 {current} / {total} 页",{current:p,total:f})});return s.jsxs("div",{"data-slot":"pager",className:Ue("flex items-center gap-2",c),children:[d&&h,s.jsx(ne,{type:"button",variant:"outline",size:"sm",disabled:o||p<=1,onClick:()=>a(p-1),children:vo("上一页")}),s.jsx(ne,{type:"button",variant:"outline",size:"sm",disabled:o||p>=f,onClick:()=>a(p+1),children:vo("下一页")}),!d&&h]})}const X6=Xc(["relative flex w-full items-center gap-2.5 text-left","border border-transparent rounded-sm","shadow-none","transition-[background-color,border-color,color] duration-150 ease-out","outline-none focus-visible:shadow-focus","disabled:pointer-events-none disabled:opacity-45","[&_svg]:pointer-events-none [&_svg]:shrink-0"],{variants:{variant:{default:["cursor-pointer px-2.5 py-2","text-[12.5px] font-medium text-sidebar-fg","hover:bg-nav-hover hover:text-foreground","data-[active]:border-nav-selected-border data-[active]:bg-nav-selected-bg data-[active]:font-semibold data-[active]:text-nav-selected-fg"],add:["cursor-pointer justify-center px-2 py-1.5","border-dashed border-border-strong bg-transparent","text-[11.5px] text-muted-foreground","hover:border-primary hover:text-primary-fg"]}},defaultVariants:{variant:"default"}});function uu({className:e,variant:t,active:a=!1,count:o,icon:c,trailing:d,children:f,...p}){return s.jsxs("button",{type:"button","data-slot":"nav-item","data-active":a?"":void 0,"aria-current":a?"true":void 0,className:Ue(X6({variant:t}),e),...p,children:[c,s.jsx("span",{"data-slot":"nav-item-label",className:"min-w-0 flex-1 truncate",children:f}),d,o!=null&&s.jsx("span",{"data-slot":"nav-item-count",className:Ue("flex-none text-[10.5px] text-sidebar-muted [font-variant-numeric:tabular-nums]",a&&"text-inherit"),children:o})]})}function r(e,t){const a=typeof window<"u"?window.wbI18n:void 0;return a&&typeof a.t=="function"?a.t(e,t):e}function Yh(e){if(e.source==="subscription"&&(e.port===null||e.port===void 0))return r("未分配端口");const t=e.resolved;return t?.protocol&&t.host&&t.port?`${String(t.protocol).toUpperCase()} ${t.host}:${t.port}`:t?.label||""}function du(e){const t=e.name||e.id,o=[e.resolveError?r("解析失败"):Yh(e),e.enabled===!1?r("已禁用"):""].filter(Boolean);return o.length?r("{name}({parts})",{name:t,parts:o.join(" · ")}):t}const Qa="pool:";function Pe(){return window}function Le(e,t){Pe().wbApp?.toast?.(e,t)}function Za(e){const t=Pe().wbApp?.esc;return t?t(e):String(e??"").replace(/[&<>"']/g,a=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[a]||a)}function Ls(e){const t=Pe().wbApp?.formatTime;if(t)return t(e);const a=Number(e);return!Number.isFinite(a)||a<=0?"":new Date(a).toLocaleString("zh-CN")}function mt(e){return e instanceof Error?e.message:String(e)}function ss(e){if(e==null||e==="")return"—";const t=Number(e);return Number.isFinite(t)?String(Number(t.toFixed(2))):String(e)}function fu(e){if(e==null||e==="")return null;const t=Number(e);return Number.isFinite(t)?t:null}function cj(e){const t=new Date(e);if(Number.isNaN(t.getTime()))return"";const a=o=>String(o).padStart(2,"0");return`${a(t.getMonth()+1)}-${a(t.getDate())} ${a(t.getHours())}:${a(t.getMinutes())}`}function Q6(e){return e==="积分"?r("积分"):e==="额度"?r("额度"):e}function Z6(e){if(!e||typeof e!="object"||Array.isArray(e))return[];const t=e,a=Array.isArray(t.credits)?t.credits:[],o=Date.now();return a.map(c=>{const d=String(c?.name||"").trim()||r("积分包"),f=String(c?.unit||"").trim(),p=fu(c?.total),h=fu(c?.used),b=fu(c?.remain),y=c?.unlimited===!0,v=fu(c?.grantAt),j=fu(c?.startAt),w=fu(c?.expireAt),S=c?.daily===!0,N=String(c?.grantReason||"").trim(),k=w!==null&&w>0&&w<=o,A=p!==null&&p>0,P=y?r("不限"):b!==null?A?r("剩 {remaining} / {total}",{remaining:ss(b),total:ss(p)}):r("剩 {remaining}",{remaining:ss(b)}):h!==null?r("已用 {used}",{used:ss(h)}):"—",R=!y&&f&&P!=="—"?`${P} ${Q6(f)}`:P,C=A&&b!==null?Math.max(0,Math.min(100,b/p*100)):null,E=b!==null&&b<=0||b===null&&h!==null&&A&&h>=p,D=!y&&!S&&(E||k),I=[];v!==null&&v>0?I.push(r("{time} 领取",{time:cj(v)})):j!==null&&j>0&&I.push(r("{time} 生效",{time:cj(j)})),S?I.push(r("每日重置")):w!==null&&w>0&&I.push(k?r("已过期"):r("{time} 到期",{time:cj(w)})),N&&I.push(N);const O=k||b===0?"muted":C!==null&&C<20?"warn":b===null&&h===null&&!y?"muted":"ok",_=[d,R,v!==null&&v>0?r("领取 {time}",{time:Ls(v)}):j!==null&&j>0?r("生效 {time}",{time:Ls(j)}):"",w!==null&&w>0?k?r("已过期({time})",{time:Ls(w)}):r("到期 {time}",{time:Ls(w)}):"",S?r("每日重置(额度按周期回满,不是作废)"):"",N,C===null&&b!==null?r("上游未给总量,画不出剩余比例"):"",h!==null&&p!==null?r("已用 {used} / 共 {total}",{used:ss(h),total:ss(p)}):""].filter(Boolean).join(" · ");return{name:d,view:R,tone:O,percent:C,window:I.length?I.join(" · "):null,title:_,spent:D}})}function EA({row:e}){return s.jsxs("div",{className:"usage-pop-row",title:e.title,children:[s.jsxs("div",{className:"usage-pop-line",children:[s.jsx("span",{className:"usage-pop-name",children:e.name}),s.jsx("span",{className:`usage-pop-view ${e.tone}`,children:e.view})]}),e.window?s.jsx("div",{className:"usage-pop-window",children:e.window}):null,e.percent!==null?s.jsx("span",{className:"usage-pop-track",children:s.jsx("i",{style:{width:`${e.percent}%`},"aria-label":r("剩余 {percent}%",{percent:Math.round(e.percent)})})}):null]})}function AA({rows:e,subscriptionLine:t,variant:a,kind:o,title:c,children:d}){const[f,p]=x.useState(!1),[h,b]=x.useState(!1),y=e.filter(k=>!k.spent),v=e.filter(k=>k.spent),j=a==="pool"?`usage-toggle usage-pool-line ${o}`:`usage-toggle ${o}`,w=x.useRef(null),S=x.useCallback(k=>{w.current=k,k&&(k.scrollTop=0)},[]),N=x.useCallback(()=>w.current??null,[]);return x.useLayoutEffect(()=>{if(!h)return;const k=w.current?.querySelector(".usage-pop-spent .usage-pop-row");k instanceof HTMLElement&&k.scrollIntoView({block:"nearest"})},[h]),s.jsxs(wf,{open:f,onOpenChange:p,children:[s.jsxs(Kh,{render:s.jsx("button",{type:"button",className:j,title:c,"aria-label":r("积分明细")}),children:[a==="pool"?d:s.jsx("span",{className:"val",children:d}),s.jsx("span",{className:"caret","aria-hidden":!0,children:"▾"})]}),s.jsx(Sf,{align:"end",sideOffset:6,className:"w-[320px]",initialFocus:N,children:s.jsxs("div",{className:"usage-pop-credit",ref:S,tabIndex:-1,children:[t?s.jsx("div",{className:"usage-pop-sub",children:t}):null,s.jsx("div",{className:"usage-pop-group",children:r("积分明细")}),y.map((k,A)=>s.jsx(EA,{row:k},`${k.name}-${A}`)),v.length>0?s.jsxs("div",{className:"usage-pop-spent",children:[s.jsxs("button",{type:"button",className:"usage-pop-spent-head","aria-expanded":h,title:r("展开或收起(默认收起)"),onClick:()=>b(k=>!k),children:[s.jsx("span",{className:"caret","aria-hidden":!0,children:"▾"}),s.jsx("span",{children:r("已用完 / 已过期")}),s.jsx("span",{className:"n",children:r("{count} 份",{count:v.length})})]}),h?v.map((k,A)=>s.jsx(EA,{row:k},`spent-${k.name}-${A}`)):null]}):null]})})]})}const pu="workbuddy",W6="raccoon",J6={workbuddy:{usage:!0,creditDetail:!0,edition:!0,identifier:"uid",expiry:"expiresAt"},"workbuddy-intl":{usage:!0,creditDetail:!0,edition:!1,identifier:"uid",expiry:"expiresAt"},raccoon:{usage:!0,creditDetail:!0,edition:!1,identifier:"userId",expiry:"tokenExpiresAt"},catpaw:{usage:!0,edition:!1,identifier:"uid",expiry:"tokenExpiresAt"},autoclaw:{usage:!0,creditDetail:!0,edition:!1,identifier:"userId",expiry:"tokenExpiresAt"},"autoclaw-intl":{usage:!0,creditDetail:!0,edition:!1,identifier:"userId",expiry:"tokenExpiresAt",emailAsName:!0},qoder:{usage:!0,creditDetail:!0,edition:!0,identifier:"userId",expiry:"expiresAt",emailAsName:!0},"qoder-intl":{usage:!0,creditDetail:!0,edition:!0,identifier:"userId",expiry:"expiresAt",emailAsName:!0},"cline-free":{usage:!0,edition:!1,identifier:"account",expiry:"expiresAt"},"cline-pass":{usage:!0,edition:!1,identifier:"account",expiry:"expiresAt"},accio:{usage:!0,edition:!0,identifier:"userId",expiry:"expiresAt",emailAsName:!0},"accio-cn":{usage:!0,edition:!0,identifier:"userId",expiry:"expiresAt",emailAsName:!0},zcode:{usage:!0,creditDetail:!0,claim:!0,planChannel:!0,edition:!0,identifier:"userId",expiry:"expiresAt"},"zcode-intl":{usage:!0,creditDetail:!0,claim:!0,planChannel:!0,edition:!0,identifier:"userId",expiry:"expiresAt"},codearts:{usage:!0,usageDetail:!0,edition:!1,identifier:"userId",expiry:"expiresAt",concurrencyDefault:3},trae:{usage:!0,creditDetail:!0,edition:!1,identifier:"uid",expiry:"expiresAt"},loomy:{usage:!0,creditDetail:!0,edition:!1,identifier:"userId",expiry:"expiresAt"},kuku:{usage:!0,edition:!1,identifier:"uid",expiry:""},officeace:{usage:!0,creditDetail:!0,edition:!1,identifier:"id",expiry:"expiresAt"},antigravity:{usage:!0,edition:!1,identifier:"userId",expiry:""},chatglm:{usage:!0,creditDetail:!0,edition:!1,identifier:"uid",expiry:""},atomcode:{usage:!0,edition:!1,identifier:"userId",expiry:"expiresAt"},lobsterai:{usage:!0,creditDetail:!0,edition:!1,identifier:"userId",expiry:"expiresAt"}},e$={usage:!1,edition:!1,identifier:"userId",expiry:"tokenExpiresAt",emailAsName:!1};function An(e){const t=e?.provider;return typeof t=="string"&&t?t:pu}function cr(e){return e&&J6[e]||e$}const t$="opencode",n$="https://opencode.ai/zen/go/v1";function RA(e,t){if(!e||e.clientEmulation!==t$)return!1;const o=((typeof t=="string"?t.trim():"")||String(e.baseUrl||"")).trim().replace(/\/+$/,"").toLowerCase();return o?o!==n$:!1}function s$(e){const t=e?An(e):"";return t.startsWith("custom-")&&(Pe().wbProviders?.customList?.()||[]).find(o=>o?.id===t)||null}function r$(e){return e?RA(s$(e),e.baseUrl):!1}function OA(e){const t=Array.isArray(e?.providers)?e.providers:[],a=Array.isArray(e?.accounts)?e.accounts:[],o=new Map;a.forEach(d=>{const f=An(d);o.set(f,(o.get(f)||0)+1)});const c=new Map;return t.forEach(d=>{d?.id&&c.set(String(d.id),{id:String(d.id),label:String(d.label||d.id),count:Number(d.count)||0})}),o.forEach((d,f)=>{c.has(f)||c.set(f,{id:f,label:Pe().wbProviders?.labelOf?.(f)||f,count:d})}),c.size||c.set(pu,{id:pu,label:Pe().wbProviders?.labelOf?.(pu)||"WorkBuddy 国内版",count:0}),[...c.values()]}function Xh(e){const t=cr(An(e)).identifier;return String(e?.[t]||"")}function MA(e){const t=Number(e)||0;return t<=0?0:t<1e11?t*1e3:t}function a$(e){const t=cr(An(e)).expiry;return MA(e?.[t])}function Ea(e){const t=An(e);return t.startsWith("custom-")?!!e?.balanceQuery?.scheme:cr(t).usage}const uj=30,DA=86400,IA=60,PA=1800,_A=86400,LA=1,l$=95e5;function o$(e){return e==="codearts"?[{type:"token",action:"skip",threshold:l$,reset:"daily",enabled:!0}]:e==="cline-free"?[]:[{type:"balance",action:"skip",threshold:LA,enabled:!0}]}function zA(e){const t=e?.usageQuery;if(!t||typeof t!="object")return{enabled:!0,interval:IA};const a=t.enabled===void 0?!0:t.enabled===!0,o=Number(t.interval)||0,c=o>=uj&&o<=DA;return{enabled:a,interval:a&&!c?IA:o}}function i$(e){if(!e||typeof e!="object")return!1;const t=e;if(t.type!=="balance"&&t.type!=="token"||t.action!=="skip"&&t.action!=="disable")return!1;const a=Number(t.threshold);if(!Number.isFinite(a)||a<=0)return!1;if(t.type==="token"&&t.reset!=="daily"){const o=Number(t.period);if(!Number.isInteger(o)||o_A)return!1}return!0}function c$(e){const t=e?.lowBalance;if(!t||typeof t!="object"||!t.mode)return o$(An(e));const a=Number(t.threshold),o=Number.isFinite(a)&&a>0?a:LA;return t.mode!=="skip"&&t.mode!=="disable"?[]:[{type:"balance",action:t.mode,threshold:o,enabled:!0}]}function mu(e){const t=e?.limiters;return Array.isArray(t)?t.filter(i$):c$(e)}function u$(e){return mu(e).filter(t=>t.type==="token")}function d$(e,t){const a=t*1e3;return e-e%a}function BA(e){return e.reset==="daily"?0:e.period??0}function dj(e,t=Date.now()){if(e===0){const a=new Date(t);return a.setHours(0,0,0,0),a.getTime()}return d$(t,e)}function Nf(e,t,a=Date.now()){if(e.type!=="token")return null;const o=BA(e),c=(t||[]).find(d=>Number(d.kind)===o);return c&&Number(c.windowStart)===dj(o,a)?c:null}function Qh(e,t){const a=mu(e).filter(f=>f.type==="balance"&&f.action==="skip"&&f.enabled!==!1&&f.threshold>0);if(!a.length||!t||typeof t!="object")return!1;const o=t;if(o.unlimited)return!1;const c=o.totalLeft??o.available;if(c==null||c==="")return!1;const d=Number(c);return Number.isFinite(d)&&a.some(f=>dc.type==="token"&&c.action==="skip"&&c.enabled!==!1);if(!a.length||!t?.length)return!1;const o=Date.now();return a.some(c=>{const d=Nf(c,t,o);return d!==null&&Number(d.used)>=c.threshold})}function f$(e,t){return mu(e).some(a=>{if(a.type!=="token"||a.action!=="disable"||a.enabled===!1)return!1;const o=Nf(a,t);return o!==null&&Number(o.used)>=a.threshold})}function pj(e,t=Date.now()){const a=BA(e),o=dj(a,t),c=a===0?Math.max(0,dj(0,o+864e5+36e5)-t):Math.max(0,o+a*1e3-t);return{kind:a,startMs:o,remainingMs:c,daily:a===0}}function Zh(e,t){if(e<=0)return"";const a=Math.ceil(e/6e4);if(t){const o=Math.ceil(e/36e5);return o>=1?r("今日剩 {n} 小时",{n:o}):r("今日剩 {n} 分钟",{n:a})}return a>=1?r("剩 {n} 分钟",{n:a}):r("剩不到 1 分钟")}function El(e){const t=Number(e);if(!Number.isFinite(t))return"—";const a=window.wbUnits;return a?.formatTokens?a.formatTokens(t):String(Math.round(t))}function HA(e){return e>0&&e%3600===0?r("{n} 小时",{n:e/3600}):e>0&&e%60===0?r("{n} 分钟",{n:e/60}):r("{n} 秒",{n:e})}function kf(e){return e?.desktop===!0}function UA(e){return e?.chatSupported!==!1}function p$(e){return typeof e=="string"&&e.startsWith("custom-")}function m$(e){return r(e==="enterprise"?"企业":e==="ultimate"?"旗舰":"个人")}function Wh(e,t){const a=Number(e?.priority??100)-Number(t?.priority??100);return a!==0?a:(Number(e?.addedAt)||0)-(Number(t?.addedAt)||0)}function ur(e){return e?.enabled!==!1}function Cf(e,t=""){const a=e?.rateLimits||{},o=Date.now();return t?Number(a[t]?.resetAt)>o:Object.values(a).some(c=>Number(c?.resetAt)>o)}function VA(e){return e?.edition==="intl"?"intl":"cn"}function FA(e){return cr(An(e)).claim?e?.canClaim!==!1:!1}const $A="coding-plan",No="start-plan";function Jh(e){return!!cr(An(e)).planChannel}function mj(e){return Jh(e)?String(e?.zcodePlan||"").trim()===No?No:$A:""}function h$(e){return r(e===No?"套餐池(Start Plan)":"编码套餐(Coding Plan)")}function qA(e){switch(String(e??"").trim()){case"pending":return r("待生效");case"active":return r("生效中");case"expired":return r("已过期");case"unknown":return r("状态未知");default:return String(e??"")}}function eg(e){const t=e?.claimPlans;if(!t||typeof t!="object"||Array.isArray(t))return[];const a=gj();return Object.entries(t).filter(([,o])=>Number(o)>0&&gj(Number(o))===a).map(([o])=>o)}function g$(e){return eg(e).length>0}function x$(e){const t=eg(e),a=t.length?r("({plans})",{plans:t.join(r("、"))}):"";return r("今天(北京时间 {day})已领取 {n} 份{names};还有其他可领套餐时,点这里可以继续领;活动按自然日发新套餐,明天可再领",{day:gj(),n:t.length,names:a})}function b$(e){return!!cr(An(e)).usageDetail}function GA(e){return!!cr(An(e)).creditDetail}function hj(e){return/^free$/i.test(String(e??"").trim())}function KA(e){const t=String(e??"").trim();return/^trial$/i.test(t)?r("试用版"):hj(t)?r("免费版"):t}function gj(e=Date.now()){return new Date(e+8*3600*1e3).toISOString().slice(0,10)}function Tf(e,t,a){return Cf(e)||Qh(e,t)||fj(e,a)}function YA(e,t){const a=String(t.q||"").trim().toLowerCase();return a?[hu(e),e?.name,e?.nickname,e?.email,Xh(e),e?.id].some(c=>String(c||"").toLowerCase().includes(a)):!0}function XA(e,t){return t.provider==="all"||An(e)===t.provider}function xj(e,t){return t.enabled==="all"?!0:t.enabled==="enabled"?ur(e):!ur(e)}function QA(e,t,a,o){if(t.limit==="all")return!0;if(!ur(e))return!1;const c=Tf(e,a?.(e),o?.(e));return t.limit==="limited"?c:!c}function ZA(e,t,a,o){return XA(e,t)&&xj(e,t)&&QA(e,t,a,o)&&YA(e,t)}function v$(e,t,a,o){return(e||[]).filter(c=>ZA(c,t,a,o))}function WA(e,t,a,o){const c=e||[],d=h=>c.filter(b=>XA(b,t)&&(h==="enabled"||xj(b,t))&&YA(b,t)),f=d("enabled"),p=d("limit");return{enabledAll:f.length,enabled:f.filter(ur).length,disabled:f.filter(h=>!ur(h)).length,limitAll:p.length,normal:p.filter(h=>ur(h)&&!Tf(h,a?.(h),o?.(h))).length,limited:p.filter(h=>ur(h)&&Tf(h,a?.(h),o?.(h))).length}}function bj(e){const t=e?.rateLimits;if(!t||typeof t!="object")return[];const a=Date.now();return Object.entries(t).map(([o,c])=>({model:o,...c&&typeof c=="object"?c:{}})).filter(o=>Number(o.resetAt)>a).sort((o,c)=>Number(o.resetAt)-Number(c.resetAt))}function vj(e){const t=(e||[]).slice().sort(Wh),a=new Map;return t.forEach((o,c)=>a.set(o.id,{position:c+1,total:t.length})),a}function y$(e,t,a,o){const c=(e||[]).slice().sort(Wh),d=[],f=new Map;for(const p of c){const h=An(p);f.has(h)||(f.set(h,[]),d.push(h)),f.get(h).push(p)}return d.map((p,h)=>{const b=f.get(p)||[];return{provider:p,label:Pe().wbProviders?.labelOf?.(p)||p,rank:h+1,members:b,accounts:t?b.filter(t):b,enabled:b.filter(ur).length,disabled:b.filter(y=>!ur(y)).length,limited:b.filter(y=>ur(y)&&Tf(y,a?.(y),o?.(y))).length}})}function hu(e){if(!e)return"";const t=String(e.name||"").trim();if(e.nameCustom===!0&&t)return t;const a=String(e.email||"").trim();return cr(An(e)).emailAsName&&a?a:e?.nickname||t||Xh(e)||e?.id||""}const yj="已限流",JA=e=>new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime();function jj(e){const t=Number(e);if(!Number.isFinite(t)||t<=0||t<=Date.now())return yj;const a=new Date(t),o=a.toLocaleTimeString("zh-CN",{hour:"2-digit",minute:"2-digit",hour12:!1}),c=Math.round((JA(a)-JA(new Date))/864e5);return c===0?r("今天 {clock}",{clock:o}):c===1?r("明天 {clock}",{clock:o}):r("{month}月{day}日 {clock}",{month:a.getMonth()+1,day:a.getDate(),clock:o})}function j$(e){const t=An(e);if(!cr(t).edition)return"";const a=VA(e),o=e?.editionLabel,c=o||(a==="intl"?"国际版":"国内版");return(Pe().wbProviders?.labelOf?.(t)||"").includes(c)?"":o||r(a==="intl"?"国际版":"国内版")}function w$(e){return[UA(e)?null:{text:r("仅账号管理"),kind:"plain",title:r("该提供商的推理转发尚未接入,账号不参与转发")},p$(An(e))&&e.hasCredentials===!1?{text:r("未配置凭证"),kind:"bad",title:r("这条自定义账号既没有 API Key,也没有勾选「无需鉴权」:转发时会被跳过,该提供商下的模型也不会出现在模型列表里。去账号「设置」里补上 Key,或勾选「该上游无需鉴权」")}:null,e.proxy?.error?{text:r("代理异常"),kind:"bad",title:r("{error}(转发时会回退直连)",{error:e.proxy.error})}:null,mj(e)===No?{text:r("套餐池"),kind:"plain",title:r("转发走套餐池(Start Plan)通道(zcode.z.ai 的 Anthropic 端点,用账号里的体验额度 / 活动领取额度);在账号设置里可切回编码套餐")}:null].filter(t=>t!==null)}const eR="workbuddy-desktop-accounts-filters",tR="workbuddy-desktop-accounts-collapsed",nR="workbuddy-desktop-accounts-names-hidden";function S$(){const e={provider:"all",enabled:"all",limit:"all",q:""},t=Pe().wbFilterMemory?.load(eR,e)||e;return{provider:String(t.provider||"all"),enabled:String(t.enabled||"all"),limit:String(t.limit||"all"),q:""}}function N$(){try{const e=JSON.parse(localStorage.getItem(tR)||"[]");return new Set(Array.isArray(e)?e.map(t=>String(t)).filter(Boolean):[])}catch{return new Set}}function k$(){try{return localStorage.getItem(nR)==="1"}catch{return!1}}let hn={version:0,filter:S$(),collapsed:N$(),selected:new Set,panels:new Map,connections:new Map,clash:null,dialog:null,namesHidden:k$(),usageBusy:!1,usageInflight:new Set};const wj=new Set;function sR(e){return wj.add(e),()=>{wj.delete(e)}}function Gs(){return hn}function Rn(e){hn={...hn,...e,version:hn.version+1},wj.forEach(t=>t())}function jr(){Rn({})}function Sj(){return Pe().wbApp?.getState?.()?.accounts}function Al(){const e=Sj()?.accounts;return Array.isArray(e)?e:[]}function Aa(e){return Al().find(t=>t.id===e)||null}function C$(e,t){const a=y$(Al(),c=>ZA(c,hn.filter,e,t),e,t);return{groups:a.filter(c=>c.accounts.length>0),allGroups:a}}function rR(){return OA(Sj())}function T$(e,t){return WA(Al(),hn.filter,e,t)}function E$(){return vj(Al())}function tg(){Pe().wbFilterMemory?.save(eR,{provider:hn.filter.provider,enabled:hn.filter.enabled,limit:hn.filter.limit})}function A$(e){hn.filter.q!==e&&Rn({filter:{...hn.filter,q:e}})}function R$(e){const t=e||"all";hn.filter.provider!==t&&(Rn({filter:{...hn.filter,provider:t}}),tg())}function aR(e,t){const a={...hn.filter,[e]:t};a.enabled==="disabled"&&a.limit!=="all"&&(a.limit="all"),Rn({filter:a}),tg()}function ng(){return hn.filter.provider!=="all"||hn.filter.enabled!=="all"||hn.filter.limit!=="all"||hn.filter.q.trim()!==""}function lR(){Rn({filter:{provider:"all",enabled:"all",limit:"all",q:""}}),tg()}function O$(){const e=hn.filter.provider;e!=="all"&&(rR().some(t=>t.id===e)||(Rn({filter:{...hn.filter,provider:"all"}}),tg()))}function oR(e){try{localStorage.setItem(tR,JSON.stringify([...e]))}catch{}}function iR(e){const t=new Set(hn.collapsed);t.has(e)?t.delete(e):t.add(e),oR(t),Rn({collapsed:t})}function M$(e,t){const a=new Set(hn.collapsed);for(const o of e)t?a.add(o):a.delete(o);oR(a),Rn({collapsed:a})}function D$(e){return hn.selected.has(e)}function I$(e,t){const a=new Set(hn.selected);t?a.add(e):a.delete(e),Rn({selected:a})}function cR(e,t){const a=new Set(hn.selected);for(const o of e)t?a.add(o):a.delete(o);Rn({selected:a})}function P$(){Rn({selected:new Set})}function uR(e,t){return hn.panels.get(e)?.has(t)===!0}function dR(e,t,a){if(!e)return;const o=new Map(hn.panels),c=new Set(o.get(e)||[]);a?c.add(t):c.delete(t),c.size?o.set(e,c):o.delete(e),Rn({panels:o})}function _$(e,t){const a=new Map(hn.panels);for(const o of e){const c=new Set(a.get(o)||[]);c.add(t),a.set(o,c)}Rn({panels:a})}function L$(){const e=!hn.namesHidden;try{localStorage.setItem(nR,e?"1":"0")}catch{}Rn({namesHidden:e})}function z$(e){return"*".repeat(Math.min(Math.max(e.length,4),12))}const fR="usage_not_configured",ko=new Map,Ef=new Map;function ua(e,t,a){ko.set(e,t),t!=null&&gu(t)?Ef.set(e,a):Ef.delete(e)}function Wa(e){const t=ko.get(e.id);if(t===void 0)return;if(!gu(t))return t;const a=Math.max(Number(e.addedAt)||0,Number(e.updatedAt)||0);return(Ef.get(e.id)||0)>=a?t:void 0}const Af=new Map;let pR=0;function Rl(e){return e?Af.get(e.id)||[]:[]}function B$(e){const t=Number(e?.tokenAt)||0;if(!t||t===pR)return!1;const a=e?.tokenUsage;if(!a||typeof a!="object")return!1;pR=t;const o=new Map;for(const[d,f]of Object.entries(a)){if(!Array.isArray(f))continue;const p=[];for(const h of f){if(!h||typeof h!="object")continue;const b=Number(h.kind),y=Number(h.windowStart),v=Number(h.used);!Number.isFinite(b)||b<0||!Number.isFinite(y)||y<=0||!Number.isFinite(v)||v<0||p.push({kind:b,windowStart:y,used:v})}o.set(d,p)}const c=o.size!==Af.size||[...o].some(([d,f])=>{const p=Af.get(d);return!p||p.length!==f.length||f.some((h,b)=>h.used!==p[b]?.used||h.windowStart!==p[b]?.windowStart)});Af.clear();for(const[d,f]of o)Af.set(d,f);return c}let sg=null;async function mR(e={}){const t=Gs().clash;if(!e.force&&t)return t;sg||(sg=(async()=>{try{const o=await Pe().workbuddyDesktop?.getProxies?.();if(!o||typeof o!="object"||!o.clash)throw new Error(r("代理列表响应异常({type})",{type:typeof o}));return o.clash}finally{sg=null}})());const a=await sg;return Rn({clash:a}),a}function H$(){Rn({clash:null})}let Rf=null,rg=null;function U$(){return Rf}async function hR(e={}){if(!e.force&&Rf)return Rf;rg||(rg=(async()=>{try{const a=await Pe().workbuddyDesktop?.getProxyPool?.();if(!a||typeof a!="object"||!Array.isArray(a.items))throw new Error(r("代理池响应异常({type})",{type:typeof a}));return a.items}finally{rg=null}})());const t=await rg;return Rf=t,t}let Nj=null;function V$(){return Nj}let ag=0,gR=!1;function F$(){if(!(Rf||Date.now(){ag=0,Nj=null,jr()}).catch(e=>{Nj=mt(e),jr()})}}function lg(e){if(!Aa(e)){Le(r("账号不存在,请刷新后重试"),"err");return}Rn({dialog:{kind:"settings",id:e}})}function og(){Rn({dialog:null})}function kj(e,t="enable"){const a=(Array.isArray(e)?e:[]).filter(o=>Aa(o));if(!a.length){Le(r("请先勾选要操作的账号"),"err");return}Rn({dialog:{kind:"batch",ids:a,action:t}})}function $$(e){let t=!1;for(const f of[...ko.keys()])e.has(f)||(ko.delete(f),t=!0);for(const f of[...Ef.keys()])e.has(f)||Ef.delete(f);const a=new Map(Gs().panels);for(const f of[...a.keys()])e.has(f)||(a.delete(f),t=!0);const o=new Map(Gs().connections);for(const f of[...o.keys()])e.has(f)||(o.delete(f),t=!0);const c=new Set(Gs().usageInflight);for(const f of[...c])e.has(f)||(c.delete(f),t=!0);const d=new Set(Gs().selected);for(const f of[...d])e.has(f)||(d.delete(f),t=!0);t&&Rn({panels:a,connections:o,usageInflight:c,selected:d})}const q$=2e3;function xR(e){return Gs().connections.get(e)||0}async function bR(){try{const e=await Pe().workbuddyDesktop?.getAccountConnections?.(),t=e?.counts&&typeof e.counts=="object"?e.counts:{},a=new Map;for(const[o,c]of Object.entries(t)){const d=Number(c)||0;d>0&&a.set(o,d)}return G$(a,Gs().connections)||Rn({connections:a}),!0}catch{return!1}}function G$(e,t){if(e.size!==t.size)return!1;for(const[a,o]of e)if(t.get(a)!==o)return!1;return!0}let vR=null;function K$(){vR||(vR=setInterval(()=>{document.hidden||Pe().wbApp?.currentPage!=="accounts"||bR()},q$))}function Cj(){return Al().filter(e=>Ea(e)&&e.available!==!1)}function gu(e){if(e==null)return null;if(typeof e=="string")return{message:e,notConfigured:!1};if(typeof e!="object")return null;const t=e.error;return t?{message:String(t),notConfigured:e.code===fR}:null}function yR(e){return e.usage?e.usage:{error:e.error?String(e.error):r("余额响应为空"),code:e.code}}function jR(e){const t=Array.isArray(e?.results)?e.results:[],a=Number(e?.at)||Date.now();let o=0;for(const c of t)c?.id&&(ua(String(c.id),yR(c),Number(c.at)||a),o++);return o&&(jr(),Pe().wbApp?.renderTopbarStatus?.()),o}let wR=!1;function Y$(e){if(!GA(e))return!1;const t=Wa(e);return!t||typeof t!="object"||Array.isArray(t)||gu(t)?!1:!Array.isArray(t.credits)}async function X$(){if(!wR){wR=!0;for(const e of Al().filter(Y$)){const t=ko.get(e.id);try{await Of(e.id)}catch{}const a=ko.get(e.id);t!==void 0&&(!a||gu(a))&&ua(e.id,t,Date.now()),jr()}}}let SR=0;async function Q$(){try{const e=await Pe().workbuddyDesktop?.getBalancesSnapshot?.(),t=B$(e);t&&(jr(),Pe().wbApp?.renderTopbarStatus?.());const a=Number(e?.at)||0;return!a||a===SR||(SR=a,!jR(e))?t:(X$(),!0)}catch{return!1}}async function Of(e){const t=await Pe().workbuddyDesktop?.getAllBalances?.(e||void 0),a=Array.isArray(t?.results)?t.results:[],o=Date.now(),c=new Set;for(const d of a){if(!d?.id)continue;const f=String(d.id);e&&f!==e||(c.add(f),ua(f,yR(d),o))}if(e)return c.has(e)||ua(e,r("未返回余额数据"),o),jr(),t;for(const d of Cj())c.has(d.id)||ua(d.id,r("未返回余额数据"),o);return jr(),t}async function NR(){if(Gs().usageBusy)return;const e=Cj();if(!e.length){Le(r("暂无可查询余额的账号"),"err");return}Rn({usageBusy:!0}),e.forEach(t=>ua(t.id,null,Date.now())),jr();try{const t=(await Of(null))?.results||[],a=t.filter(f=>f.usage).length,o=t.filter(f=>f.code===fR).length,c=o?r("({n} 个未配置查询凭证)",{n:o}):"",d=t.length-a-o;Le(a+o===t.length?r("✅ 已更新 {n} 个账号的余额{suffix}",{n:a,suffix:c}):r("已更新 {ok}/{total} 个账号,{failed} 个失败",{ok:a,total:t.length,failed:d}),d?"err":"ok")}catch(t){const a=mt(t);e.forEach(o=>ua(o.id,r("查询失败:{message}",{message:a}),Date.now())),jr(),Le(r("余额查询失败:{message}",{message:a}),"err")}finally{Rn({usageBusy:!1})}}async function kR(e){if(Gs().usageInflight.has(e))return;const t=new Set(Gs().usageInflight);t.add(e),ua(e,null,Date.now()),Rn({usageInflight:t});try{await Of(e)}finally{const a=new Set(Gs().usageInflight);a.delete(e),Rn({usageInflight:a})}}async function Z$(e){if(!Gs().usageInflight.has(e))try{await kR(e);const t=gu(ko.get(e));t?.notConfigured?Le(t.message,"ok"):t?Le(r("余额查询失败:{message}",{message:t.message}),"err"):Le(r("✅ 已更新余额"))}catch(t){ua(e,r("查询失败:{message}",{message:mt(t)}),Date.now()),jr(),Le(r("余额查询失败:{message}",{message:mt(t)}),"err")}}async function ig(e){if(e){const a=Aa(e);if(!a||!Ea(a))return;try{await kR(e)}catch(o){ua(e,r("查询失败:{message}",{message:mt(o)}),Date.now()),jr()}return}if(Gs().usageBusy)return;const t=Cj();if(t.length){Rn({usageBusy:!0}),t.forEach(a=>ua(a.id,null,Date.now())),jr();try{await Of(null)}catch(a){const o=mt(a);t.forEach(c=>ua(c.id,r("查询失败:{message}",{message:o}),Date.now())),jr()}finally{Rn({usageBusy:!1})}}}async function CR(e,t){try{await Pe().workbuddyDesktop?.updateAccount?.(e,{enabled:t}),await Pe().wbApp?.refresh?.(),Le(r(t?"✅ 已启用":"✅ 已禁用"))}catch(a){Le(r("操作失败:{message}",{message:mt(a)}),"err"),Pe().wbApp?.refresh?.()}}async function TR(e,t){if(!e.length)return!1;try{return await Pe().workbuddyDesktop?.reorderAccounts?.(e),Le(t?r("✅ 已保存:{note}",{note:t}):r("✅ 顺序已保存")),await Pe().wbApp?.refresh?.(),!0}catch(a){return Le(r("顺序未保存:{message}",{message:mt(a)}),"err"),Pe().wbApp?.refresh?.(),!1}}async function ER(e,t){if(e.length)try{await Pe().workbuddyDesktop?.batchAccounts?.({action:t?"enable":"disable",ids:e}),Le(t?r("✅ 已启用 {n} 个账号",{n:e.length}):r("✅ 已禁用 {n} 个账号",{n:e.length})),await Pe().wbApp?.refresh?.()}catch(a){Le(r("操作失败:{message}",{message:mt(a)}),"err"),Pe().wbApp?.refresh?.()}}async function W$(e,t){try{await Pe().workbuddyDesktop?.clearRateLimits?.(e,t||null),Le(t?r("✅ 已清除 {model} 的限流标记",{model:t}):r("✅ 已清除该账号全部限流标记")),await Pe().wbApp?.refresh?.()}catch(a){Le(r("清除失败:{message}",{message:mt(a)}),"err")}}async function J$(e,t,a){if(t===AR){lg(e);return}if(t!==cg&&t!==a)try{const o=t===""?null:{source:"pool",proxyId:t.slice(Qa.length)},d=(await Pe().workbuddyDesktop?.updateAccount?.(e,{proxy:o}))?.changes,f=Array.isArray(d)&&d.length?d[0]:r("代理已更新");Le(`✅ ${f}`)}catch(o){Le(r("保存失败:{message}",{message:mt(o)}),"err")}finally{await Pe().wbApp?.refresh?.()}}const cg="__proxy_custom__",AR="__proxy_custom_edit__";function RR(e,t){const a=Aa(e);if(!a){Le(r("账号不存在,请刷新后重试"),"err");return}const o=Pe().wbZcodePlans;if(!o?.open){Le(r("套餐明细弹窗未加载,请重启应用后重试"),"err");return}o.open({id:e,name:t||a.name})}function e8(e,t,a=1){return{seat:t.get(e.id)||{position:1,total:1},rank:a,picked:D$(e.id),usageEntry:ko.get(e.id),limitsOpen:uR(e.id,"limits"),connections:xR(e.id)}}const t8={DEFAULT_PROVIDER_ID:pu,RACCOON_PROVIDER_ID:W6,providerOf:An,providerFeatures:cr,providerSummaries:OA,identifierOf:Xh,tokenExpiryOf:a$,supportsUsage:Ea,isDesktopAccount:kf,supportsChat:UA,byPriorityOrder:Wh,typeLabel:m$,isEnabled:ur,isRateLimited:Cf,isLimited:e=>Tf(e,e?Wa(e):void 0,Rl(e)),accountEdition:VA,supportsClaim:FA,matchEnabled:xj,matchLimit:QA,visibleAccounts:v$,filterCounts:WA,positionMap:vj,activeLimits:bj,editionSuffix:j$,formatResetText:jj};function n8(){window.wbAccountsView={render:()=>jr(),refreshCaches:$$,openPanels:_$,syncConnections:bR,applyBalances:jR,syncBalancesSnapshot:Q$,queryUsageFor:Of,queryAllUsage:NR,refreshUsageAfterCheckin:ig,supportsUsage:Ea,isDesktopAccount:kf,isEnabled:ur,isRateLimited:Cf},window.wbAccountsModel=t8,window.wbAccountPanel={open:lg,close:og,openBatch:kj,closeBatch:og,syncAddProvider:()=>Pe().wbAccountAddForms?.syncAddProvider?.(),invalidate:H$}}function s8(e){return e.type==="balance"?r("余额 < {threshold}",{threshold:e.threshold}):e.reset==="daily"?r("每日消耗 ≥ {count} Token(每天 0 点重置)",{count:El(e.threshold)}):r("每 {period}消耗 ≥ {count} Token",{period:HA(e.period??0),count:El(e.threshold)})}function r8(e){return e.action==="disable"?r("禁用该账号(需手动重新启用)"):r("跳过该账号(自动恢复)")}function a8(e){return`${Math.round(e.threshold).toLocaleString("en-US")} Token`}function l8(e,t){if(t.enabled===!1)return{text:r("已停用"),kind:"off"};if(t.type==="balance"){const b=Wa(e),y=Qh(e,b),v=b&&typeof b=="object"?b:null,j=v?v.totalLeft??v.available:void 0,w=j==null||j===""||v?.unlimited?"":r(" · 当前 {n}",{n:ss(j)});return y?{text:r("已跳过{current}",{current:w}),kind:"warn",title:r("余额低于阈值,转发时会跳过该账号(余额回升自动恢复)")}:{text:r("未触发{current}",{current:w}),kind:"ok"}}const a=Rl(e),o=Nf(t,a),c=o?o.used:0,d=El(t.threshold),f=t.reset==="daily"?r(" · 今日 {used} / {quota}",{used:El(c),quota:d}):r(" · 本周期 {used} / {quota}",{used:El(c),quota:d}),p=o?` · ${Zh(pj(t).remainingMs,t.reset==="daily")}`:"";return o!==null&&c>=t.threshold?t.action==="disable"?e.enabled===!1?{text:r("已触发 · 账号已禁用"),kind:"danger",title:r("重置也不恢复,需手动启用")}:{text:r("已达上限{usage}",{usage:f}),kind:"warn",title:r("稍后由后台判定自动禁用")}:{text:r("已跳过{usage}{countdown}",{usage:f,countdown:p}),kind:"warn",title:r("窗口内已用 {used} / {limit} Token,转发时会跳过该账号(重置后自动恢复)",{used:Math.round(c).toLocaleString("en-US"),limit:Math.round(t.threshold).toLocaleString("en-US")})}:{text:r("未触发{usage}{countdown}",{usage:f,countdown:p}),kind:"ok",title:a8(t)}}function o8(e){return e>0&&e%3600===0?{value:String(e/3600),unit:"hours"}:e>0&&e%60===0?{value:String(e/60),unit:"minutes"}:{value:"1",unit:"hours"}}function OR(e){const t={type:"balance",action:"skip",thresholdInput:"",tokenUnit:"wan",reset:"fixed",periodInput:"1",periodUnit:"hours"};if(!e)return t;if(e.type==="balance")return{...t,type:"balance",action:e.action,thresholdInput:String(e.threshold)};if(e.reset==="daily")return{...t,type:"token",action:e.action,reset:"daily",thresholdInput:String(e.threshold%1e4===0?e.threshold/1e4:e.threshold),tokenUnit:e.threshold%1e4===0?"wan":"unit"};const a=o8(e.period??0);return{...t,type:"token",action:e.action,reset:"fixed",thresholdInput:String(e.threshold%1e4===0?e.threshold/1e4:e.threshold),tokenUnit:e.threshold%1e4===0?"wan":"unit",periodInput:a.value,periodUnit:a.unit}}function MR(e){const t=Number(e.thresholdInput);if(!Number.isFinite(t)||t<=0)return{error:r("限制阈值必须是大于 0 的数字")};if(e.type==="balance")return{rule:{type:"balance",action:e.action,threshold:t,enabled:!0}};const a=Math.round(e.tokenUnit==="wan"?t*1e4:t);if(e.reset==="daily")return{rule:{type:"token",action:e.action,threshold:a,reset:"daily",enabled:!0}};const o=Number(e.periodInput)*(e.periodUnit==="hours"?3600:60);if(!Number.isFinite(o)||o<=0)return{error:r("请填写重置周期")};const c=Math.round(o);return c_A?{error:r("重置周期必须是 30 分钟 ~ 24 小时")}:{rule:{type:"token",action:e.action,threshold:a,period:c,reset:"fixed",enabled:!0}}}function i8({account:e,target:t,onClose:a,onSave:o}){const[c,d]=x.useState(""),[f,p]=x.useState(t?.draft??null);if(x.useEffect(()=>{p(t?.draft??null),d("")},[t]),!t||!f)return null;const h=v=>{d(""),p({...f,...v})},b=()=>{const v=MR(f);if("error"in v){d(v.error);return}o(v.rule,t.index)},y=()=>{if(f.type==="balance"){const P=Wa(e);if(P&&typeof P=="object"){const R=P;if(R.unlimited)return r("当前可用 ∞(不参与余额不足判定)");const C=R.totalLeft??R.available;if(C!=null&&C!=="")return r("当前余额 {n};与余额列同一数字口径,严格小于才触发(等于阈值仍可用)",{n:ss(C)})}return r("读数来自自动余额查询;与余额列同一数字口径,严格小于才触发(等于阈值仍可用)")}const v=MR(f);if(!("rule"in v))return"";const j=v.rule,w=Nf(j,Rl(e));if(!w)return j.reset==="daily"?r("窗口 = 今天 0 点起(本地时区),0 点重置即清零恢复;按账号在 requests 表聚合"):r("窗口对齐自然时间(整点 / 整 N 分钟一轮回),重置即清零恢复;按账号在 requests 表聚合");const S=pj(j),N=El(w.used),k=El(j.threshold),A=j.reset==="daily"?r("今日已用"):r("本周期已用");return r("{scope} {used} / {quota} Token,{countdown}重置",{scope:A,used:N,quota:k,countdown:Zh(S.remainingMs,S.daily)})};return s.jsx(Yt,{open:!0,onOpenChange:v=>{v||a()},children:s.jsxs(Xt,{className:"w-[min(580px,calc(100vw-48px))]",showCloseButton:!0,children:[s.jsx(Vt,{children:s.jsx(Ut,{children:t.index>=0?r("编辑限制"):r("添加限制")})}),s.jsxs(Lt,{children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("限制类型")}),s.jsxs(ut,{value:f.type,onValueChange:v=>h({type:v,thresholdInput:""}),children:[s.jsx(ft,{className:"w-[150px]","aria-label":r("限制类型"),children:s.jsx(dt,{children:f.type==="balance"?r("余额"):r("Token 消耗")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"balance",children:r("余额")}),s.jsx($e,{value:"token",children:r("Token 消耗")})]})]}),s.jsx("span",{className:"detail",children:r("余额沿用现有口径;Token 按重置方式内的累计消耗判定(切换类型会清空阈值)")})]}),f.type==="balance"?s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("触发条件")}),s.jsx("span",{className:"detail",style:{fontSize:13},children:r("余额低于")}),s.jsx(Fe,{type:"number",min:0,step:1,className:"max-w-[120px]",placeholder:r("阈值"),value:f.thresholdInput,onChange:v=>h({thresholdInput:v.currentTarget.value})}),s.jsx("span",{className:"detail",style:{fontSize:13},children:r("时")})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("触发后")}),s.jsxs(ut,{value:f.action,onValueChange:v=>h({action:v}),children:[s.jsx(ft,{className:"min-w-[280px]","aria-label":r("触发动作"),children:s.jsx(dt,{children:f.action==="disable"?r("禁用该账号(需手动重新启用)"):r("跳过该账号(余额回升自动恢复)")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"skip",children:r("跳过该账号(余额回升自动恢复)")}),s.jsx($e,{value:"disable",children:r("禁用该账号(需手动重新启用)")})]})]})]})]}):s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("重置方式")}),s.jsxs(ut,{value:f.reset,onValueChange:v=>h({reset:v}),children:[s.jsx(ft,{className:"w-[190px]","aria-label":r("重置方式"),children:s.jsx(dt,{children:f.reset==="daily"?r("自然日(每天 0 点)"):r("固定周期")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"fixed",children:r("固定周期")}),s.jsx($e,{value:"daily",children:r("自然日(每天 0 点)")})]})]}),s.jsx("span",{className:"detail",children:r("有的账号过了 0 点额度就回来 —— 选「自然日」")})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("触发条件")}),f.reset==="daily"?s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"detail",style:{fontSize:13},children:r("每日消耗 ≥")}),s.jsx(Fe,{type:"number",min:0,step:1,className:"max-w-[110px]",placeholder:r("阈值"),value:f.thresholdInput,onChange:v=>h({thresholdInput:v.currentTarget.value})}),s.jsxs(ut,{value:f.tokenUnit,onValueChange:v=>h({tokenUnit:v}),children:[s.jsx(ft,{className:"w-[92px]","aria-label":r("Token 单位"),children:s.jsx(dt,{children:f.tokenUnit==="wan"?r("万"):r("个")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"wan",children:r("万")}),s.jsx($e,{value:"unit",children:r("个")})]})]}),s.jsx("span",{className:"detail",style:{fontSize:13},children:r("Token 时")})]}):s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"detail",style:{fontSize:13},children:r("每")}),s.jsx(Fe,{type:"number",min:1,step:1,className:"max-w-[90px]",placeholder:r("周期"),value:f.periodInput,onChange:v=>h({periodInput:v.currentTarget.value})}),s.jsxs(ut,{value:f.periodUnit,onValueChange:v=>h({periodUnit:v}),children:[s.jsx(ft,{className:"w-[92px]","aria-label":r("周期单位"),children:s.jsx(dt,{children:f.periodUnit==="hours"?r("小时"):r("分钟")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"minutes",children:r("分钟")}),s.jsx($e,{value:"hours",children:r("小时")})]})]}),s.jsx("span",{className:"detail",style:{fontSize:13},children:r("内消耗 ≥")}),s.jsx(Fe,{type:"number",min:0,step:1,className:"max-w-[110px]",placeholder:r("阈值"),value:f.thresholdInput,onChange:v=>h({thresholdInput:v.currentTarget.value})}),s.jsxs(ut,{value:f.tokenUnit,onValueChange:v=>h({tokenUnit:v}),children:[s.jsx(ft,{className:"w-[92px]","aria-label":r("Token 单位"),children:s.jsx(dt,{children:f.tokenUnit==="wan"?r("万"):r("个")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"wan",children:r("万")}),s.jsx($e,{value:"unit",children:r("个")})]})]}),s.jsx("span",{className:"detail",style:{fontSize:13},children:r("Token 时")})]})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("触发后")}),s.jsxs(ut,{value:f.action,onValueChange:v=>h({action:v}),children:[s.jsx(ft,{className:"min-w-[280px]","aria-label":r("触发动作"),children:s.jsx(dt,{children:f.action==="disable"?r("禁用该账号(需手动重新启用)"):r("跳过该账号(重置后自动恢复)")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"skip",children:r("跳过该账号(重置后自动恢复)")}),s.jsx($e,{value:"disable",children:r("禁用该账号(需手动重新启用)")})]})]})]})]}),s.jsx("p",{className:"detail limiter-hint",children:y()}),c?s.jsx("p",{className:"detail text-destructive",children:c}):null]}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"outline",onClick:a,children:r("取消")}),s.jsx(ne,{variant:"default",onClick:b,children:t.index>=0?r("保存规则"):r("添加规则")})]})]})})}function c8({account:e,draft:t,onChange:a}){const[o,c]=x.useState(null),d=()=>c({index:-1,draft:OR(null)}),f=y=>c({index:y,draft:OR(t[y])}),p=(y,v)=>{const j=[...t];v>=0?j[v]=y:j.push(y),a(j),c(null)},h=y=>{a(t.filter((v,j)=>j!==y)),o&&c(null)},b=(y,v)=>{const j=[...t];j[y]={...j[y],enabled:v},a(j)};return s.jsxs(s.Fragment,{children:[t.length?s.jsx("div",{className:"limiter-rules mt-2.5",children:t.map((y,v)=>{const j=l8(e,y);return s.jsxs("div",{className:"limiter-rule","data-off":y.enabled===!1||void 0,children:[s.jsx(ns,{checked:y.enabled!==!1,"aria-label":r("启用该规则"),onCheckedChange:w=>b(v,w===!0)}),s.jsx("div",{className:"limiter-rule-main",children:s.jsxs("span",{className:"limiter-rule-cond",children:[s.jsx(ze,{variant:"secondary",shape:"tag",children:y.type==="balance"?r("余额"):"Token"})," ",r("{cond} 时 → {action}",{cond:s8(y),action:r8(y)})]})}),s.jsx(ze,{variant:j.kind==="ok"?"success":j.kind==="warn"?"warning":j.kind==="danger"?"destructive":"secondary",shape:"tag",title:j.title,children:j.text}),s.jsxs("div",{className:"limiter-rule-ops",children:[s.jsx(ne,{variant:"ghost",onClick:()=>f(v),children:r("编辑")}),s.jsx(ne,{variant:"ghost",onClick:()=>h(v),children:r("删除")})]})]},v)})}):s.jsx("p",{className:"detail mt-2.5",children:r("还没有限制规则:账号照常参与转发,余额与 Token 消耗都不设上限。")}),s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx(ne,{variant:"outline",onClick:d,children:r("+ 添加限制")}),s.jsx("span",{className:"detail",children:r("可添加多条;同类规则也允许并存(例如一条按余额、两条不同重置方式的 Token)")})]}),s.jsx(i8,{account:e,target:o,onClose:()=>c(null),onSave:p})]})}function u8(e,t){const a=mu(e);return JSON.stringify(t)===JSON.stringify(a)?null:{patch:{limiters:t}}}function d8(e){return mu(e).map(t=>({...t}))}const f8={seconds:1,minutes:60,hours:3600};function p8(e){return e>0&&e%3600===0?{value:String(e/3600),unit:"hours"}:e>0&&e%60===0?{value:String(e/60),unit:"minutes"}:e>0?{value:String(e),unit:"seconds"}:{value:"5",unit:"minutes"}}function m8(e){const{enabled:t,interval:a}=zA(e),o=p8(t?a:0);return{enabled:t,intervalInput:o.value,unit:o.unit}}function h8(e,t){const a=Math.round(Number(t.intervalInput)*f8[t.unit]);if(t.enabled){if(!Number.isFinite(Number(t.intervalInput))||Number(t.intervalInput)<=0)return{error:r("请填写查询间隔")};if(aDA)return{error:r("查询间隔必须是 {min} 秒 ~ 24 小时",{min:uj})}}const o={},c=zA(e);return(t.enabled!==c.enabled||t.enabled&&a!==c.interval)&&(o.usageQuery={enabled:t.enabled,interval:t.enabled?a:c.interval}),o.usageQuery?{patch:o}:null}function g8({account:e,draft:t,onChange:a}){const o=c=>a({...t,...c});return s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"field-row mt-2.5",children:s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2.5 font-normal",children:[s.jsx(ns,{checked:t.enabled,onCheckedChange:c=>o({enabled:c===!0}),"aria-label":r("自动查询余额")}),s.jsx("span",{className:"text-xs text-subtle",children:r("自动查询余额(按下面的间隔自动刷新余额列的读数)")})]})}),t.enabled?s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:"account-usage-interval",children:r("查询间隔")}),s.jsx(Fe,{id:"account-usage-interval",type:"number",min:1,step:1,className:"max-w-[110px]",value:t.intervalInput,onChange:c=>o({intervalInput:c.currentTarget.value})}),s.jsxs(ut,{value:t.unit,onValueChange:c=>o({unit:c}),children:[s.jsx(ft,{className:"w-[92px]","aria-label":r("间隔单位"),children:s.jsx(dt,{children:t.unit==="hours"?r("小时"):t.unit==="minutes"?r("分钟"):r("秒")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"seconds",children:r("秒")}),s.jsx($e,{value:"minutes",children:r("分钟")}),s.jsx($e,{value:"hours",children:r("小时")})]})]}),s.jsx("span",{className:"detail",children:r("30 秒 ~ 24 小时;手动查询会顺延下一轮")})]}):null]})}const x8={sub2api:{label:"Sub2API",endpoint:"{base}/v1/usage",note:r("用这条账号的 API Key 查询,不需要额外配置。"),fields:[]},new_api:{label:"NewAPI",endpoint:"{base}/api/user/self + /api/status",note:r("用 NewAPI 站点的个人访问令牌查询账户余额(不是这条账号的上游 API Key)。令牌在站点的「个人设置」里生成,只有旧版 / 定制站点才需要额外填用户 ID。"),fields:[{key:"accessToken",label:r("NewAPI 个人访问令牌"),secret:!0,placeholder:r("在 NewAPI 站点「个人设置」里生成的访问令牌"),hint:r("必填;留空 = 不修改,清除用右边的按钮")},{key:"userId",label:r("NewAPI 用户 ID"),placeholder:r("旧版 / 定制站点才需要,新版可留空"),hint:r("可留空:新版请求头只认令牌;旧版会把用户 ID 放进 New-Api-User 头")}]},deepseek:{label:"DeepSeek",endpoint:"{base}/user/balance",note:r("用这条账号的 API Key 查询,不需要额外配置。"),fields:[]},opencode:{label:"OpenCode Go",endpoint:"{base}/v1/usage",note:r("用这条账号的 API Key 查询订阅用量,不需要额外配置;读数展开成 5 小时 / 每周 / 每月三个窗口。"),fields:[]},sensenova:{label:"商汤日日新",endpoint:"platform.sensenova.cn 控制台",note:r("用商汤日日新控制台的账号密码查询(与推理用的 API Key 不是同一套)。网关会自动换取登录票并缓存,过期后自动重登,不需要手动维护。"),fields:[{key:"username",label:r("账号"),placeholder:r("商汤日日新控制台的登录账号"),hint:r("必填")},{key:"password",label:r("密码"),secret:!0,placeholder:r("商汤日日新控制台的登录密码"),hint:r("留空 = 不修改,清除用右边的按钮")}]},zhipu:{label:"智谱",endpoint:"bigmodel.cn 控制台",note:r("用 bigmodel.cn 控制台的访问令牌查询账户余额(人民币元)。令牌是登录后浏览器 Cookie `bigmodel_token_production` 的值:开发者工具 → 应用(Application)→ Cookie 里复制。"),fields:[{key:"token",label:r("访问令牌"),secret:!0,placeholder:r("bigmodel_token_production 的值"),hint:r("必填;留空 = 不修改,清除用右边的按钮")}]},zhipu_zai:{label:"智谱(z.ai 国际)",endpoint:"api.z.ai 控制台",note:r("用这条账号的 API Key 生成签名后查询账户余额(美元),不需要额外配置。要求 API Key 是 z.ai 的「id.secret」形态(控制台生成的 Key 就是)。"),fields:[]}};function Mf(e){return e?x8[e]??null:null}function b8(e,t){return t.secret?t.key==="accessToken"?e?.hasAccessToken===!0:t.key==="password"?e?.hasPassword===!0:t.key==="token"?e?.hasToken===!0:!1:!1}function v8(e){const t=String(e?.scheme||"");return t==="new_api"?e?.hasAccessToken===!0:t==="sensenova"?String(e?.username||"").trim()!==""&&e?.hasPassword===!0:t==="zhipu"?e?.hasToken===!0:!0}function y8(e){return{userId:String(e?.userId||""),username:String(e?.username||""),accessToken:"",password:"",token:""}}function j8(e,t){const a=Mf(e);if(!a||!a.fields.length)return null;const o={};for(const c of a.fields){const d=String(t[c.key]??"").trim();if(c.secret){d&&(o[c.key]=d);continue}o[c.key]=d}return Object.keys(o).length?o:null}function w8({field:e,value:t,configured:a,onValue:o,onClear:c,clearBusy:d,idPrefix:f}){const p=`${f}-${e.key}`;return s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:p,children:e.label}),s.jsx(Fe,{id:p,className:"min-w-[220px]",type:e.secret?"password":"text",autoComplete:e.secret?"new-password":"off",placeholder:a?r("已配置,留空则不修改"):e.placeholder,title:e.hint,value:t,onChange:h=>o(h.currentTarget.value)}),a&&c?s.jsx(ne,{variant:"outline",size:"sm",disabled:d===!0,onClick:c,title:r("清除已配置的 {label}",{label:e.label}),children:r("清除")}):null]})}function S8({spec:e,values:t,configured:a,onValue:o,onClear:c,clearBusy:d,idPrefix:f}){return s.jsxs(s.Fragment,{children:[s.jsxs("p",{className:"detail mt-2.5",children:[r("余额查询方案:{label}",{label:e.label}),s.jsx("span",{className:"ml-1 text-subtle",children:e.endpoint})," — ",e.note]}),e.fields.map(p=>s.jsx(w8,{field:p,idPrefix:f,value:t[p.key]||"",configured:a[p.key]===!0,onValue:h=>o(p.key,h),onClear:c?()=>c(p.key):void 0,clearBusy:d},p.key))]})}const DR=64;function N8(e,t){const a=An(e);return t.filter(o=>o.id!==e.id&&An(o)===a)}const Ol=999,IR=8;function ug(e){return hu(e)||e?.id||""}function PR(e){const t=e?.config?.source||e?.source,a=e?.config||null,o=t==="rotate";return{mode:t==="pool"?"pool":o?"rotate":t==="clash"?"clash":t==="custom"?"custom":"none",proxyId:t==="pool"?String(a?.proxyId||""):"",rotateAll:o?a?.all!==!1:!0,rotateIds:o&&Array.isArray(a?.proxyIds)?a.proxyIds.map(String):[],listenerUid:a?.source==="clash"||t==="clash"?String(a?.listenerUid||""):"",protocol:a?.protocol==="socks5"?"socks5":"http",host:a?.host||"",port:a?.port===void 0||a?.port===null?"":String(a.port),username:a?.username||"",password:a?.password||""}}function Tj(e){if(e.mode==="none")return null;if(e.mode==="pool"){if(!e.proxyId)throw new Error(r("请先选择已保存的代理"));return{source:"pool",proxyId:e.proxyId}}if(e.mode==="rotate"){if(e.rotateAll)return{source:"rotate",all:!0};if(!e.rotateIds.length)throw new Error(r("轮询至少要选择一条代理(或改选「全部」)"));return{source:"rotate",all:!1,proxyIds:[...e.rotateIds]}}if(e.mode==="clash"){if(!e.listenerUid)throw new Error(r("请先选择 Clash Verge 出口"));return{source:"clash",listenerUid:e.listenerUid}}const t=e.host.trim(),a=Number(e.port);if(!t)throw new Error(r("请填写代理主机地址"));if(!Number.isInteger(a)||a<1||a>65535)throw new Error(r("代理端口必须是 1-65535 的整数"));return{source:"custom",protocol:e.protocol==="socks5"?"socks5":"http",host:t,port:a,username:e.username.trim(),password:e.password}}function _R(e){const t=e.profileActive===!1?r("(其他订阅,可能未生效)"):"",a=e.enabled===!1?r("(已在 Clash 中禁用)"):"";return`${e.name} :${e.port}${t}${a}`}function LR({draft:e,onChange:t,idPrefix:a,allowRotate:o=!1}){const d=Gs().clash,f=Array.isArray(d?.options)?d.options:[],p=e.mode==="clash",[h,b]=x.useState(!1),[y,v]=x.useState(null),[j,w]=x.useState(null),[S,N]=x.useState(""),k=C=>t({...e,...C});x.useEffect(()=>{p&&mR().catch(()=>{})},[p]);const A=x.useCallback((C=!1)=>{N(""),hR({force:C}).then(E=>w(E)).catch(E=>{w([]),N(mt(E))})},[]);x.useEffect(()=>{(e.mode==="pool"||e.mode==="rotate")&&j===null&&A()},[e.mode,j,A]);function P(){return e.rotateAll?(j??[]).filter(C=>C.enabled!==!1).map(C=>C.id):[...e.rotateIds]}async function R(){if(h)return;if(e.mode==="rotate"){const E=P();if(!E.length){Le(r("轮询至少要选择一条代理(或改选「全部」)"),"err");return}const D=E.slice(0,IR);b(!0),v(r("正在连接上游…"));let I=0;const O=[];for(const H of D){const B=j?.find(L=>L.id===H)?.name||H;try{const L=await Pe().workbuddyDesktop?.testProxyPoolItem?.(H);L?.success?I+=1:O.push(`${B}(${L?.error||r("连接失败")})`)}catch(L){O.push(`${B}(${mt(L)})`)}}const _=E.length-D.length,z=r("✅ {ok}/{total} 个出口可用",{ok:I,total:D.length})+(_>0?r("(只测了前 {limit} 个,还有 {rest} 个未测)",{limit:IR,rest:_}):"");v(s.jsxs("span",{className:O.length?"text-destructive":"text-success",children:[z,O.length?` ${Za(O.join(r(";")))}`:""]})),b(!1);return}if(e.mode==="pool"){if(!e.proxyId){Le(r("请先选择已保存的代理"),"err");return}b(!0),v(r("正在连接上游…"));try{const E=await Pe().workbuddyDesktop?.testProxyPoolItem?.(e.proxyId);v(E?.success?s.jsxs("span",{className:"text-success",children:[r("✅ 出口可用"),E.ip?r(" 出口 IP {ip}",{ip:Za(E.ip)}):"",E.durationMs!==void 0&&E.durationMs!==""?` ${Za(String(E.durationMs))}ms`:""]}):s.jsxs("span",{className:"text-destructive",children:["❌ ",Za(E?.error||r("连接失败"))]}))}catch(E){v(s.jsxs("span",{className:"text-destructive",children:["❌ ",Za(mt(E))]}))}finally{b(!1)}return}let C;try{C=Tj(e)}catch(E){Le(mt(E),"err");return}b(!0),v(r("正在连接上游…"));try{const E=await Pe().workbuddyDesktop?.testProxy?.({proxy:C});E?.success?v(s.jsxs("span",{className:"text-success",children:[r("✅ 出口可用"),E.status!==void 0&&E.status!==""?` HTTP ${Za(String(E.status))}`:"",E.ip?r(" 出口 IP {ip}",{ip:Za(E.ip)}):"",E.durationMs!==void 0&&E.durationMs!==""?` ${Za(String(E.durationMs))}ms`:""]})):v(s.jsxs("span",{className:"text-destructive",children:["❌ ",Za(E?.error||r("连接失败"))]}))}catch(E){v(s.jsxs("span",{className:"text-destructive",children:["❌ ",Za(mt(E))]}))}finally{b(!1)}}return s.jsxs("div",{className:"flex flex-col",children:[s.jsxs(Ph,{value:e.mode,onValueChange:C=>k({mode:C}),className:"flex-row flex-wrap items-center gap-5","aria-label":r("代理方式"),children:[s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2 font-normal",children:[s.jsx(jo,{value:"none"}),r("无代理(直连)")]}),s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2 font-normal",children:[s.jsx(jo,{value:"pool"}),r("已保存的代理")]}),o?s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2 font-normal",title:r("逐请求轮换出口(换 IP):OpenCode Zen 免费档按 IP 限流,单个 IP 的额度用完会对它一律 429"),children:[s.jsx(jo,{value:"rotate"}),r("轮询(换 IP)")]}):null,p?s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2 font-normal",children:[s.jsx(jo,{value:"clash"}),"Clash Verge"]}):null,s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2 font-normal",children:[s.jsx(jo,{value:"custom"}),r("自定义")]})]}),e.mode==="pool"?s.jsxs("div",{className:"mt-3",children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:`${a}-pool-exit`,children:r("代理")}),s.jsxs(ut,{value:e.proxyId,disabled:!j?.length,onValueChange:C=>k({proxyId:String(C)}),children:[s.jsx(ft,{id:`${a}-pool-exit`,className:"min-w-[220px]",children:s.jsx(dt,{children:j?.find(C=>C.id===e.proxyId)?du(j.find(C=>C.id===e.proxyId)):j===null?r("正在读取…"):j.length?r("请选择代理"):r("还没有保存的代理")})}),s.jsx(pt,{children:(j??[]).map(C=>s.jsx($e,{value:C.id,children:du(C)},C.id))})]}),s.jsx(ne,{variant:"outline",size:"sm",onClick:()=>A(!0),children:r("重新读取")})]}),s.jsx("div",{className:"detail mt-1.5",children:S?r("读取失败:{error}",{error:S}):j===null?r("正在读取代理列表…"):j.length?r("地址与端口由「网络代理」页管理:那边改一次、测一次,所有引用它的账号一起生效"):r("「网络代理」页还没有出口 —— 去那里新增,或在「订阅管理」里点「同步 Clash Verge」把 Clash 的出口导进来")})]}):null,e.mode==="rotate"?s.jsxs("div",{className:"mt-3",children:[s.jsxs("div",{className:"flex flex-wrap items-center gap-x-2.5 gap-y-1",children:[s.jsx(Pr,{id:`${a}-rotate-all`,checked:e.rotateAll,"aria-label":r("轮询全部代理节点"),onCheckedChange:C=>k({rotateAll:C===!0})}),s.jsx("label",{htmlFor:`${a}-rotate-all`,className:"cursor-pointer text-xs text-subtle",children:r("全部代理节点(推荐)")}),s.jsx("span",{className:"detail",children:r("「网络代理」页里每个启用的出口轮流使用;之后新加的出口自动加入")})]}),e.rotateAll?null:s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:`${a}-rotate-exits`,children:r("轮询的代理")}),s.jsx(Gh,{id:`${a}-rotate-exits`,value:e.rotateIds,onValueChange:C=>k({rotateIds:C.map(String)}),options:(j??[]).map(C=>({value:C.id,label:du(C)})),placeholder:r("选择要轮询的代理(可多选)"),"aria-label":r("轮询的代理")}),s.jsx(ne,{variant:"outline",size:"sm",onClick:()=>A(!0),children:r("重新读取")})]}),s.jsx("div",{className:"detail mt-1.5",children:S?r("读取失败:{error}",{error:S}):j===null?r("正在读取代理列表…"):j.length?r("每条请求换一个出口(按顺序轮流),上游看到的 IP 因此是变化的;出口的地址与端口由「网络代理」页管理"):r("「网络代理」页还没有出口 —— 去那里新增,或在「订阅管理」里点「同步 Clash Verge」把 Clash 的出口导进来")})]}):null,e.mode==="clash"?s.jsxs("div",{className:"mt-3",children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:`${a}-clash-exit`,children:r("出口")}),s.jsxs(ut,{value:e.listenerUid,disabled:!f.length,onValueChange:C=>k({listenerUid:String(C)}),children:[s.jsx(ft,{id:`${a}-clash-exit`,className:"min-w-[220px]",children:s.jsx(dt,{children:f.find(C=>String(C.uid)===e.listenerUid)?_R(f.find(C=>String(C.uid)===e.listenerUid)):d?.available===!1?r("未检测到 Clash Verge"):f.length?r("请选择出口"):r("没有可用出口")})}),s.jsx(pt,{children:f.map(C=>s.jsx($e,{value:String(C.uid),children:_R(C)},String(C.uid)))})]}),s.jsx(ne,{variant:"outline",size:"sm",onClick:()=>{mR({force:!0}).then(()=>Le(r("✅ 已重新读取 Clash Verge 配置"))).catch(C=>Le(r("读取失败:{error}",{error:mt(C)}),"err"))},children:r("重新读取")})]}),s.jsx("div",{className:"detail mt-1.5",children:d===null?r("正在读取 Clash Verge 配置…"):d.available===!1?d.error?r("不可用:{error}",{error:d.error}):r("未检测到 Clash Verge 配置"):f.length?r("读取自 {dir};端口由 Clash Verge 管理,这里实时同步",{dir:d.dir||"Clash Verge"}):r("Clash Verge 里还没有配置混合监听器或节点端口")}),s.jsxs("p",{className:"detail mt-1.5",children:[r("这是既有配置(直接引用 Clash 出口,不经过代理池)。推荐改用上面的"),s.jsx("b",{children:r("「已保存的代理」")}),r(":到「网络代理」页的「订阅管理」里点「同步 Clash Verge」把出口导进池后即可选 —— 那样能集中测试、也能被多个账号共用。")]})]}):null,e.mode==="custom"?s.jsxs("div",{className:"mt-3",children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:`${a}-protocol`,children:r("协议")}),s.jsxs(ut,{value:e.protocol,onValueChange:C=>k({protocol:C}),children:[s.jsx(ft,{id:`${a}-protocol`,className:"min-w-[110px]",children:s.jsx(dt,{children:e.protocol==="socks5"?"SOCKS5":"HTTP"})}),s.jsxs(pt,{children:[s.jsx($e,{value:"http",children:"HTTP"}),s.jsx($e,{value:"socks5",children:"SOCKS5"})]})]}),s.jsx("label",{htmlFor:`${a}-host`,className:"ml-2",children:r("主机")}),s.jsx(Fe,{id:`${a}-host`,className:"min-w-[140px]",placeholder:"127.0.0.1",value:e.host,onChange:C=>k({host:C.currentTarget.value})}),s.jsx("label",{htmlFor:`${a}-port`,children:r("端口")}),s.jsx(Fe,{id:`${a}-port`,type:"number",min:1,max:65535,className:"max-w-[110px]",placeholder:"7890",value:e.port,onChange:C=>k({port:C.currentTarget.value})})]}),s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:`${a}-user`,children:r("用户名")}),s.jsx(Fe,{id:`${a}-user`,placeholder:r("可选"),autoComplete:"off",value:e.username,onChange:C=>k({username:C.currentTarget.value})}),s.jsx("label",{htmlFor:`${a}-pass`,children:r("密码")}),s.jsx(Fe,{id:`${a}-pass`,type:"password",placeholder:r("可选"),autoComplete:"new-password",value:e.password,onChange:C=>k({password:C.currentTarget.value})})]})]}):null,s.jsxs("div",{className:"field-row mt-3",children:[s.jsx(ne,{variant:"outline",size:"sm",disabled:h,onClick:()=>{R()},children:r(h?"测试中…":"测试出口")}),s.jsx("span",{className:"detail",children:y})]})]})}function k8({plan:e,hasJwt:t,onChange:a}){const o=h$(e),[c,d]=x.useState(null);return x.useEffect(()=>{if(e!==No)return()=>{};let f=!0;const p=()=>{Promise.resolve(Pe().workbuddyDesktop?.zcodeCaptchaStats?.()).then(b=>{f&&b&&d({ready:Number(b.ready)||0,target:Number(b.target)||0})}).catch(()=>{})};p();const h=window.setInterval(p,5e3);return()=>{f=!1,window.clearInterval(h)}},[e]),s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:"account-plan-channel",children:r("使用套餐")}),s.jsxs(ut,{value:e,onValueChange:f=>a(String(f)),children:[s.jsx(ft,{id:"account-plan-channel",className:"min-w-[220px]",children:s.jsx(dt,{children:o})}),s.jsxs(pt,{children:[s.jsx($e,{value:$A,children:r("编码套餐(Coding Plan)— 走开放平台端点,消耗开放平台的额度(订阅套餐 / 资源包 / 充值余额)")}),s.jsx($e,{value:No,children:r("套餐池(Start Plan)— 走 zcode.z.ai 端点,消耗 ZCode 套餐池的额度(新用户体验额度 / 活动领取)")})]})]}),e===No&&!t?s.jsx("span",{className:"detail text-destructive",children:r("该账号没有套餐登录态(jwt),套餐池通道会失败:请重新登录该账号")}):null,e===No?s.jsxs("span",{className:"detail",title:r("套餐池的推理端点要求每条请求带一个阿里云验证码令牌;令牌由本应用在后台静默铸造,界面关闭时无法铸造"),children:[r("验证码令牌:{counts}",{counts:c?`${c.ready} / ${c.target||3}`:r("读取中…")}),c&&c.ready===0?r("(库存为空,正在补;补不上时转发会失败)"):""]}):null]})}function C8({configured:e,value:t,onChange:a,onClear:o,clearBusy:c}){return s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:"account-balance-token-input",children:r("余额查询凭证")}),s.jsx(Fe,{id:"account-balance-token-input",className:"min-w-[220px]",placeholder:r(e?"已配置,留空则不修改":"一般不用填"),title:r("积分查询已复用转发用的登录凭证,这里通常留空即可。只有旧版本填过、或从旧代理导入过凭证时才有值"),value:t,onChange:d=>a(d.currentTarget.value)}),e?s.jsx(ne,{variant:"outline",size:"sm",disabled:c,onClick:o,title:r("清除已配置的余额查询凭证"),children:r("清除")}):null]})}function T8({hasApiKey:e,noAuth:t,value:a,onValue:o,onToggleNoAuth:c,onClear:d,clearBusy:f}){return s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:"account-api-key-input",children:r("API Key")}),s.jsx(Fe,{id:"account-api-key-input",type:"password",autoComplete:"new-password",className:"min-w-[220px]",disabled:t,placeholder:e?r("已配置,留空则不修改"):"sk-…",title:r("上游的 API Key。留空 = 不修改(改凭证不会换账号 id);要清掉已配的凭证用右边的「清除」"),value:a,onChange:p=>o(p.currentTarget.value)}),e&&!t?s.jsx(ne,{variant:"outline",size:"sm",disabled:f,onClick:d,title:r("清除已配置的 API Key(转发时会因缺少凭证被跳过)"),children:r("清除")}):null]}),s.jsx("div",{className:"field-row mt-2.5",children:s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2.5 font-normal",children:[s.jsx(Pr,{checked:t,"aria-label":r("该上游无需鉴权"),onCheckedChange:p=>c(p===!0)}),s.jsx("span",{className:"text-xs text-subtle",children:r("该上游无需鉴权(转发与拉取模型都不发送鉴权头)")})]})}),s.jsxs("p",{className:"detail",children:[r("上游本来不要凭证时(本地 Ollama、OpenCode Zen 的免费档)勾上这一项:账号会被正常选路;只把 Key 留空"),s.jsx("strong",{children:r("又不勾")}),r("会被当成「未配置凭证」而跳过。")]})]})}function E8({provider:e,name:t,protocol:a,baseUrl:o,count:c,emulation:d,onName:f,onProtocol:p,onBaseUrl:h,onEmulation:b,onRemove:y}){const v=Pe().wbProviders?.PROTOCOL_OPTIONS||[{value:"openai",label:r("OpenAI 兼容")},{value:"anthropic",label:"Anthropic"}],j=v.find(w=>w.value===a)?.label||a;return s.jsxs(dn,{children:[s.jsx("h3",{children:r("提供商")}),s.jsxs("p",{children:[r("这一栏改的是"),s.jsx("strong",{children:r("「{name}」本身",{name:e.name||e.id})}),r("(名下 {count} 个账号共用)",{count:c}),r("改协议 / Base URL 会改变它们的转发方式,正在进行的请求可能失败;模型清单与映射在「模型管理」页。")]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:"account-provider-name",children:r("名称")}),s.jsx(Fe,{id:"account-provider-name",maxLength:DR,className:"min-w-[220px]",placeholder:r("提供商显示名,1~{max} 个字符",{max:DR}),value:t,onChange:w=>f(w.currentTarget.value)})]}),s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:"account-provider-protocol",children:r("协议")}),s.jsxs(ut,{value:a,onValueChange:w=>p(String(w)),children:[s.jsx(ft,{id:"account-provider-protocol",className:"min-w-[180px]",children:s.jsx(dt,{children:j})}),s.jsx(pt,{children:v.map(w=>s.jsx($e,{value:w.value,children:w.label},w.value))})]})]}),s.jsxs("div",{className:"field-row mt-2.5",children:[s.jsx("label",{htmlFor:"account-provider-baseurl",children:r("Base URL")}),s.jsx(Fe,{id:"account-provider-baseurl",className:"min-w-[260px]",placeholder:r("OpenAI 兼容填到 /v1;Anthropic 填根地址"),value:o,onChange:w=>h(w.currentTarget.value)})]}),s.jsx("div",{className:"field-row mt-2.5",children:s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2.5 font-normal",children:[s.jsx(Pr,{checked:d==="opencode","aria-label":r("伪装 OpenCode 官方客户端"),onCheckedChange:w=>b(w===!0?"opencode":"")}),s.jsx("span",{className:"text-xs text-subtle",children:r("伪装 OpenCode 官方客户端(OpenCode Zen 免费档要它才放行)")})]})}),s.jsxs("p",{className:"detail",children:[r("开启后,网关按 OpenCode 官方 CLI 的形状发请求:没填 Key 的账号用匿名凭证"),s.jsx("code",{children:"public"}),r("、补官方会话头、请求体补两个占位工具(上游免费档的三道校验)。"),s.jsx("strong",{children:r("它会改写请求体")}),r(",只对 OpenCode Zen 这类上游有意义,别的家请关掉。")]}),s.jsxs("div",{className:"field-row mt-3",children:[s.jsx(ne,{variant:"destructive",onClick:y,children:r("删除提供商")}),s.jsx("span",{className:"detail",children:r("级联删除名下全部账号,不可恢复")})]})]})}function A8({id:e,onClose:t}){const a=Aa(e),[o,c]=x.useState(a?.enabled!==!1),[d,f]=x.useState(a?.name||""),[p,h]=x.useState(()=>PR(a?.proxy)),[b,y]=x.useState(""),[v,j]=x.useState(()=>mj(a)),[w,S]=x.useState(!1),[N,k]=x.useState(""),[A,P]=x.useState(null),[R,C]=x.useState(""),[E,D]=x.useState("chat_completions"),[I,O]=x.useState(""),[_,z]=x.useState(""),[H,B]=x.useState(""),[L,F]=x.useState(a?.noAuth===!0),[$,X]=x.useState(()=>m8(a)),[K,q]=x.useState(()=>y8(a?.balanceQuery)),[Q,G]=x.useState(()=>d8(a)),[J,Y]=x.useState("basic"),[re,M]=x.useState(()=>String(Number(a?.maxConcurrent)||0));if(x.useEffect(()=>{let Ae=!0;P(null);const oe=Aa(e);return oe?(Promise.resolve(Pe().wbCustomProvidersUi?.find?.(An(oe))).then(me=>{!Ae||!me||(P(me),C(me.name||""),D(me.protocol||"chat_completions"),O(me.baseUrl||""),z(typeof me.clientEmulation=="string"?me.clientEmulation:""))}).catch(()=>{}),()=>{Ae=!1}):()=>{Ae=!1}},[e]),!a)return s.jsx(Yt,{open:!0,onOpenChange:Ae=>{Ae||t()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("账号设置")})}),s.jsx(Lt,{children:s.jsx("p",{className:"detail",children:r("账号不存在,请刷新后重试。")})}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:t,children:r("关闭")})]})]})});const U=a,Z=N8(U,Al()),ae=An(U)==="catpaw",ce=An(U).startsWith("custom-"),W=RA(A,U.baseUrl)||p.mode==="rotate",ue=Mf(U.balanceQuery?.scheme),ve={};for(const Ae of ue?.fields??[])ve[Ae.key]=b8(U.balanceQuery,Ae);const ee=!!(ue&&ue.fields.length)&&!v8(U.balanceQuery);async function he(){if(!w){S(!0);try{await Pe().workbuddyDesktop?.updateAccount?.(e,{balanceToken:null}),y(""),Le(r("✅ 已清除余额查询凭证")),await Pe().wbApp?.refresh?.()}catch(Ae){Le(r("清除失败:{error}",{error:mt(Ae)}),"err")}finally{S(!1)}}}async function te(){if(!w){S(!0);try{await Pe().workbuddyDesktop?.updateAccount?.(e,{apiKey:null}),B(""),Le(r("✅ 已清除 API Key(该账号现在没有凭证,转发会被跳过)")),await Pe().wbApp?.refresh?.()}catch(Ae){Le(r("清除失败:{error}",{error:mt(Ae)}),"err")}finally{S(!1)}}}async function ke(Ae){if(!w){S(!0);try{await Pe().workbuddyDesktop?.updateAccount?.(e,{balanceQuery:{[Ae]:null}}),q(oe=>({...oe,[Ae]:""})),Le(r("✅ 已清除查询凭证")),await Pe().wbApp?.refresh?.()}catch(oe){Le(r("清除失败:{error}",{error:mt(oe)}),"err")}finally{S(!1)}}}function le(){if(!ce)return null;const Ae=H.trim(),oe={};return Ae?oe.apiKey=Ae:L!==(U.noAuth===!0)&&(oe.noAuth=L),Object.keys(oe).length?oe:null}function de(){if(!A)return null;const Ae=R.trim(),oe=I.trim();if(!Ae)return{error:r("请填写提供商名称")};if(!oe)return{error:r("请填写提供商的 Base URL")};const me=typeof A.clientEmulation=="string"?A.clientEmulation:"";return A.name===Ae&&A.protocol===E&&A.baseUrl===oe&&me===_?null:{id:A.id,name:Ae,protocol:E,baseUrl:oe,clientEmulation:_}}async function Se(){if(w)return;let Ae;try{Ae=Tj(p)}catch(Tt){Le(mt(Tt),"err");return}const oe=de();if(oe&&"error"in oe){k(s.jsx("span",{className:"text-destructive",children:oe.error}));return}const me=Ea(U)?h8(U,$):null;if(me&&"error"in me){k(s.jsx("span",{className:"text-destructive",children:me.error}));return}const pe=u8(U,Q),Ce=j8(U.balanceQuery?.scheme,K),Ne=le()||{},be=Number(re);if(!Number.isFinite(be)||be<0){Le(r("并发上限必须是 0~{max} 的整数",{max:Ol}),"err");return}const De=Math.min(Ol,Math.round(be)),Ge=Number(U.maxConcurrent)||0,Ct=De!==Ge?{maxConcurrent:De}:{};S(!0);try{const Tt=ae&&b.trim()?{balanceToken:b.trim()}:{},Mt=Jh(U)&&v!==mj(U)?{zcodePlan:v}:{};if(await Pe().workbuddyDesktop?.updateAccount?.(e,{name:d.trim()||U.name,enabled:o,proxy:Ae,...Tt,...Mt,...Ne,...Ct,...Ce?{balanceQuery:Ce}:{},...me&&"patch"in me?me.patch:{},...pe&&"patch"in pe?pe.patch:{}}),oe)try{await Pe().wbCustomProvidersUi?.update?.(oe)}catch(ot){k(s.jsx("span",{className:"text-destructive",children:r("账号已保存,但提供商未更新:{error}",{error:mt(ot)})})),await Pe().wbApp?.refresh?.(),S(!1);return}t(),Le(r("✅ 账号设置已保存"));try{await Pe().wbApp?.refresh?.()}catch{}}catch(Tt){k(s.jsx("span",{className:"text-destructive",children:mt(Tt)})),Le(r("保存失败:{error}",{error:mt(Tt)}),"err")}finally{S(!1)}}const Te=Number(cr(An(U)).concurrencyDefault)||0,_e=ae||ce||Jh(U);return s.jsx(Yt,{open:!0,onOpenChange:Ae=>{Ae||w||t()},children:s.jsxs(Xt,{className:"w-[min(900px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("账号设置 · {name}",{name:ug(a)})})}),s.jsx(Lt,{className:"p-0",children:s.jsxs("div",{className:"acct-dlg",children:[s.jsx("aside",{className:"prov-rail acct-dlg-rail","aria-label":r("账号设置分组"),children:s.jsxs("div",{className:"rail-scroll",children:[s.jsx("div",{className:"rail-label",children:r("账号设置")}),[["basic",r("基本")],..._e?[["credential",r("凭证与套餐")]]:[],...Ea(U)?[["query",r("查询设置")],["limiter",r("限制器")]]:[],["network",r("网络")]].map(([Ae,oe])=>s.jsx(uu,{active:J===Ae,title:oe,className:"shadow-none",onClick:()=>Y(Ae),children:oe},Ae))]})}),s.jsxs("div",{className:"acct-dlg-main",children:[J==="basic"?s.jsxs("div",{children:[s.jsxs("div",{className:"acct-dlg-pane-head",children:[s.jsx("h3",{children:r("基本")}),s.jsx("p",{children:r("这条账号是谁、排第几、能不能接请求。")})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("启用")}),s.jsx(ns,{checked:o,onCheckedChange:c,"aria-label":r("启用该账号")}),s.jsx("span",{className:"detail",children:r("关闭则不参与转发(余额查询照常,见「查询设置」)")})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:"account-name-input",children:r("备注名")}),s.jsx(Fe,{id:"account-name-input",maxLength:100,placeholder:r("账号显示名称"),className:"max-w-[280px]",value:d,onChange:Ae=>f(Ae.currentTarget.value)})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{children:r("转发顺序")}),s.jsx("span",{className:"detail",children:r("在列表里拖:拖分组带调整整组顺位,拖行首把手调整组内先后(顺序由拖拽表达,不再填数字)")})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:"account-conc-input",children:r("并发上限")}),s.jsx(Fe,{id:"account-conc-input",type:"number",min:0,max:Ol,step:1,className:"max-w-[110px]",value:re,onChange:Ae=>M(Ae.currentTarget.value)}),s.jsx("span",{className:"detail",children:Te>0?r("0 = 按本家默认 {default}",{default:Te}):r("0 = 不限")})]}),s.jsx("p",{className:"detail acct-dlg-hint",children:Te>0?r("并发上限 受上游硬顶约束,达到上限的账号跳过、请求转给其他账号(原列表 ⋯ 菜单的「并发上限」入口已并入这里)"):r("并发上限 0~{max};达到上限的账号跳过、请求转给其他账号(原列表 ⋯ 菜单的「并发上限」入口已并入这里)",{max:Ol})})]}):null,J==="credential"&&_e?s.jsxs("div",{children:[s.jsxs("div",{className:"acct-dlg-pane-head",children:[s.jsx("h3",{children:r("凭证与套餐")}),s.jsx("p",{children:r("这一段按提供商出现:有什么配什么。")})]}),ae?s.jsx(C8,{configured:a.hasBalanceToken===!0,value:b,onChange:y,clearBusy:w,onClear:()=>{he()}}):null,ce?s.jsx(T8,{hasApiKey:U.hasApiKey===!0,noAuth:L,value:H,onValue:B,onToggleNoAuth:F,onClear:()=>{te()},clearBusy:w}):null,Jh(U)?s.jsx(k8,{plan:v,hasJwt:a.canClaim===!0,onChange:j}):null,A?s.jsx(E8,{provider:A,count:Z.length+1,name:R,protocol:E,baseUrl:I,emulation:_,onName:C,onProtocol:D,onBaseUrl:O,onEmulation:z,onRemove:()=>{w||Promise.resolve(Pe().wbCustomProvidersUi?.remove?.(A.id)).then(Ae=>{Ae&&t()})}}):null]}):null,J==="query"&&Ea(U)?s.jsxs("div",{children:[s.jsxs("div",{className:"acct-dlg-pane-head",children:[s.jsx("h3",{children:r("查询设置")}),s.jsx("p",{children:r("余额列读数的自动刷新;也是「限制器」余额规则的数据来源。")})]}),ue?s.jsx(S8,{spec:ue,values:K,configured:ve,onValue:(Ae,oe)=>q(me=>({...me,[Ae]:oe})),onClear:Ae=>{ke(Ae)},clearBusy:w,idPrefix:"account-balance-query"}):null,ee?s.jsx("p",{className:"detail mt-2.5 text-subtle",children:r("查询凭证还没配齐:填好后余额列才有读数,在此之前会显示「未配置」。")}):null,s.jsx(g8,{account:U,draft:$,onChange:X})]}):null,J==="limiter"&&Ea(U)?s.jsxs("div",{children:[s.jsxs("div",{className:"acct-dlg-pane-head",children:[s.jsx("h3",{children:r("限制器")}),s.jsx("p",{children:r("按规则限制该账号参与转发:没有规则(或全部停用)= 不限制;跳过档自动恢复,禁用档需手动启用。")})]}),s.jsx(c8,{account:U,draft:Q,onChange:G})]}):null,J==="network"?s.jsxs("div",{children:[s.jsxs("div",{className:"acct-dlg-pane-head",children:[s.jsx("h3",{children:r("网络")}),s.jsx("p",{children:r("这条账号访问上游走哪条线路。")})]}),s.jsx(LR,{draft:p,onChange:h,idPrefix:"account-proxy",allowRotate:W}),a.proxy?.error?s.jsx("p",{className:"detail text-destructive",children:r("当前代理不可用:{error}(转发时会回退直连)",{error:a.proxy.error})}):null]}):null]})]})}),s.jsxs(tn,{children:[s.jsx("div",{className:"detail mr-auto",style:{minHeight:18},children:N}),s.jsx(ne,{variant:"outline",disabled:w,onClick:t,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:w,onClick:()=>{Se()},children:r(w?"保存中…":"保存")})]})]})})}const R8=[{value:"enable",label:r("启用")},{value:"disable",label:r("禁用")},{value:"proxy",label:r("修改代理")},{value:"concurrency",label:r("设置并发上限")},{value:"remove",label:r("删除")}];function O8({ids:e,action:t,onClose:a}){const[o,c]=x.useState(t),[d,f]=x.useState(()=>PR(null)),[p,h]=x.useState(""),[b,y]=x.useState(!1),[v,j]=x.useState(""),w=e.map(R=>Aa(R)).filter(R=>R!==null),S=w.reduce((R,C)=>Math.max(R,Number(cr(An(C)).concurrencyDefault)||0),0),N=w.length>0&&w.every(R=>r$(R));function k(R){return Promise.resolve(Pe().wbConfirm?.ask?.({title:r("删除选中的账号"),html:r("确定删除选中的 {count} 个账号?此操作不可恢复,账号的登录态会一并移除。",{count:R}),okText:r("删除"),okClass:"danger"})??!1)}async function A(){if(b)return;if(!e.length){Le(r("没有选中的账号"),"err");return}let R;if(o==="proxy")try{R=Tj(d)}catch(E){Le(mt(E),"err");return}let C;if(o==="concurrency"){const E=Number(p.trim());if(!p.trim()||!Number.isFinite(E)||E<0){Le(r("并发上限必须是 0~{max} 的整数",{max:Ol}),"err");return}C=Math.min(Ol,Math.round(E))}if(!(o==="remove"&&!await k(e.length))){if(o==="disable"||o==="remove"){const E=new Set(e);if(!Al().filter(I=>I.enabled!==!1&&!E.has(I.id)).length){const I=r(o==="remove"?"删除":"禁用");if(!await Promise.resolve(Pe().wbConfirm?.ask?.({title:r("全部启用中的账号将被{what}",{what:I}),html:r("这会{what}所有启用中的账号,转发将不可用。确定继续?",{what:I}),okText:I,okClass:o==="remove"?"danger":"primary"})??!1))return}}y(!0),j("");try{const E={action:o,ids:e};o==="proxy"&&(E.proxy=R),o==="concurrency"&&(E.maxConcurrent=C);const D=await Pe().workbuddyDesktop?.batchAccounts?.(E),I=(D?.ok||[]).filter(B=>B.changes?.length).length,O=(D?.removed||[]).length,_=D?.failed||[],z=o==="remove"?O:I,H={enable:r("启用"),disable:r("禁用"),proxy:r("修改代理"),concurrency:r("设置并发上限"),remove:r("删除")}[o]||o;if(_.length){const B=_.slice(0,3).map(L=>ug(Aa(String(L.id)))||L.id).join(r("、"));Le(r("{verb}完成:成功 {ok} 个,失败 {fail} 个({detail}{more})",{verb:H,ok:z,fail:_.length,detail:B,more:_.length>3?r(" 等"):""}),"err"),j(s.jsxs("span",{className:"text-destructive",children:[r("失败 {count} 个:",{count:_.length}),_.map(L=>`${ug(Aa(String(L.id)))||L.id}(${L.error})`).join(r(";"))]}))}else Le(r("✅ {verb}完成:共 {count} 个账号",{verb:H,count:z})),a();await Pe().wbApp?.refresh?.()}catch(E){j(s.jsx("span",{className:"text-destructive",children:mt(E)})),Le(r("批量操作失败:{error}",{error:mt(E)}),"err")}finally{y(!1)}}}const P={enable:r("启用"),disable:r("禁用"),proxy:r("修改代理"),concurrency:r("设置并发上限"),remove:r("删除")}[o]||o;return s.jsx(Yt,{open:!0,onOpenChange:R=>{R||b||a()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("批量操作 · 已选 {count} 个账号",{count:e.length})})}),s.jsxs(Lt,{children:[s.jsxs(dn,{children:[s.jsx("h3",{children:r("将作用于以下账号")}),s.jsx("p",{style:{maxHeight:84,overflowY:"auto"},children:w.map(R=>ug(R)).join(r("、"))})]}),s.jsxs(dn,{children:[s.jsx("h3",{children:r("操作")}),s.jsx(Ph,{value:o,onValueChange:c,className:"flex-row flex-wrap items-center gap-5","aria-label":r("批量动作"),children:R8.map(R=>s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2 font-normal",children:[s.jsx(jo,{value:R.value}),R.label]},R.value))}),o==="proxy"?s.jsx("div",{className:"mt-2.5",children:s.jsx(LR,{draft:d,onChange:f,idPrefix:"batch-proxy",allowRotate:N})}):null,o==="concurrency"?s.jsxs("div",{className:"mt-2.5",children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:"batch-conc-input",children:r("并发上限")}),s.jsx(Fe,{id:"batch-conc-input",type:"number",min:0,max:Ol,step:1,className:"max-w-[110px]",placeholder:r("不限"),value:p,onChange:R=>h(R.currentTarget.value)}),s.jsx("span",{className:"detail",children:r("0 = 不限")})]}),s.jsx("p",{className:"detail acct-dlg-hint",children:S>0?r("并发上限 0~{max};有上游硬顶的家(如 CodeArts)以它的硬顶为准;达到上限的账号会被跳过、请求转给其他账号",{max:Ol}):r("并发上限 0~{max};达到上限的账号会被跳过、请求转给其他账号",{max:Ol})})]}):null]}),s.jsx("div",{className:"detail",style:{minHeight:18},children:v})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",disabled:b,onClick:a,children:r("取消")}),s.jsx(ne,{variant:o==="remove"?"destructive":"default",disabled:b,onClick:()=>{A()},children:b?r("执行中…"):r("执行({action})",{action:P})})]})]})})}function M8(){const e=Gs();return e.dialog?e.dialog.kind==="settings"?s.jsx(A8,{id:e.dialog.id,onClose:og},e.dialog.id):s.jsx(O8,{ids:e.dialog.ids,action:e.dialog.action,onClose:og},e.dialog.ids.join(",")):null}var Ej={exports:{}},Df={},Aj={exports:{}},Rj={};var zR;function D8(){return zR||(zR=1,(function(e){function t(X,K){var q=X.length;X.push(K);e:for(;0>>1,G=X[Q];if(0>>1;Qc(re,q))Mc(U,re)?(X[Q]=U,X[M]=q,Q=M):(X[Q]=re,X[Y]=q,Q=Y);else if(Mc(U,q))X[Q]=U,X[M]=q,Q=M;else break e}}return K}function c(X,K){var q=X.sortIndex-K.sortIndex;return q!==0?q:X.id-K.id}if(e.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var d=performance;e.unstable_now=function(){return d.now()}}else{var f=Date,p=f.now();e.unstable_now=function(){return f.now()-p}}var h=[],b=[],y=1,v=null,j=3,w=!1,S=!1,N=!1,k=!1,A=typeof setTimeout=="function"?setTimeout:null,P=typeof clearTimeout=="function"?clearTimeout:null,R=typeof setImmediate<"u"?setImmediate:null;function C(X){for(var K=a(b);K!==null;){if(K.callback===null)o(b);else if(K.startTime<=X)o(b),K.sortIndex=K.expirationTime,t(h,K);else break;K=a(b)}}function E(X){if(N=!1,C(X),!S)if(a(h)!==null)S=!0,D||(D=!0,B());else{var K=a(b);K!==null&&$(E,K.startTime-X)}}var D=!1,I=-1,O=5,_=-1;function z(){return k?!0:!(e.unstable_now()-_X&&z());){var Q=v.callback;if(typeof Q=="function"){v.callback=null,j=v.priorityLevel;var G=Q(v.expirationTime<=X);if(X=e.unstable_now(),typeof G=="function"){v.callback=G,C(X),K=!0;break t}v===a(h)&&o(h),C(X)}else o(h);v=a(h)}if(v!==null)K=!0;else{var J=a(b);J!==null&&$(E,J.startTime-X),K=!1}}break e}finally{v=null,j=q,w=!1}K=void 0}}finally{K?B():D=!1}}}var B;if(typeof R=="function")B=function(){R(H)};else if(typeof MessageChannel<"u"){var L=new MessageChannel,F=L.port2;L.port1.onmessage=H,B=function(){F.postMessage(null)}}else B=function(){A(H,0)};function $(X,K){I=A(function(){X(e.unstable_now())},K)}e.unstable_IdlePriority=5,e.unstable_ImmediatePriority=1,e.unstable_LowPriority=4,e.unstable_NormalPriority=3,e.unstable_Profiling=null,e.unstable_UserBlockingPriority=2,e.unstable_cancelCallback=function(X){X.callback=null},e.unstable_forceFrameRate=function(X){0>X||125Q?(X.sortIndex=q,t(b,X),a(h)===null&&X===a(b)&&(N?(P(I),I=-1):N=!0,$(E,q-Q))):(X.sortIndex=G,t(h,X),S||w||(S=!0,D||(D=!0,B()))),X},e.unstable_shouldYield=z,e.unstable_wrapCallback=function(X){var K=j;return function(){var q=j;j=K;try{return X.apply(this,arguments)}finally{j=q}}}})(Rj)),Rj}var BR;function I8(){return BR||(BR=1,Aj.exports=D8()),Aj.exports}var HR;function P8(){if(HR)return Df;HR=1;var e=I8(),t=Xd(),a=M1();function o(n){var l="https://react.dev/errors/"+n;if(1ke||(n.current=te[ke],te[ke]=null,ke--)}function Se(n,l){ke++,te[ke]=n.current,n.current=l}var Te=le(null),_e=le(null),Ae=le(null),oe=le(null);function me(n,l){switch(Se(Ae,l),Se(_e,n),Se(Te,null),l.nodeType){case 9:case 11:n=(n=l.documentElement)&&(n=n.namespaceURI)?o5(n):0;break;default:if(n=l.tagName,l=l.namespaceURI)l=o5(l),n=i5(l,n);else switch(n){case"svg":n=1;break;case"math":n=2;break;default:n=0}}de(Te),Se(Te,n)}function pe(){de(Te),de(_e),de(Ae)}function Ce(n){var l=n.memoizedState;l!==null&&(qd._currentValue=l.memoizedState,Se(oe,n)),l=Te.current;var i=i5(l,n.type);l!==i&&(Se(_e,n),Se(Te,i))}function Ne(n){_e.current===n&&(de(Te),de(_e)),oe.current===n&&(de(oe),qd._currentValue=he)}var be,De;function Ge(n){if(be===void 0)try{throw Error()}catch(i){var l=i.stack.trim().match(/\n( *(at )?)/);be=l&&l[1]||"",De=-1)":-1m||se[u]!==xe[m]){var we=` `+se[u].replace(" at new "," at ");return n.displayName&&we.includes("")&&(we=we.replace("",n.displayName)),we}while(1<=u&&0<=m);break}}}finally{Ct=!1,Error.prepareStackTrace=i}return(i=n?n.displayName||n.name:"")?Ge(i):""}function Mt(n,l){switch(n.tag){case 26:case 27:case 5:return Ge(n.type);case 16:return Ge("Lazy");case 13:return n.child!==l&&l!==null?Ge("Suspense Fallback"):Ge("Suspense");case 19:return Ge("SuspenseList");case 0:case 15:return Tt(n.type,!1);case 11:return Tt(n.type.render,!1);case 1:return Tt(n.type,!0);case 31:return Ge("Activity");case 30:return Ge("ViewTransition");default:return""}}function ot(n){try{var l="",i=null;do l+=Mt(n,i),i=n,n=n.return;while(n);return l}catch(u){return` Error generating stack: `+u.message+` -`+u.stack}}var Kn=Object.prototype.hasOwnProperty,ms=e.unstable_scheduleCallback,Yn=e.unstable_cancelCallback,vs=e.unstable_shouldYield,it=e.unstable_requestPaint,$t=e.unstable_now,Vn=e.unstable_getCurrentPriorityLevel,rn=e.unstable_ImmediatePriority,nt=e.unstable_UserBlockingPriority,Dt=e.unstable_NormalPriority,qt=e.unstable_LowPriority,es=e.unstable_IdlePriority,Fn=e.log,$n=e.unstable_setDisableYieldValue,Dn=null,en=null;function cs(n){if(typeof Fn=="function"&&$n(n),en&&typeof en.setStrictMode=="function")try{en.setStrictMode(Dn,n)}catch{}}var Et=Math.clz32?Math.clz32:wt,xt=Math.log,jt=Math.LN2;function wt(n){return n>>>=0,n===0?32:31-(xt(n)/jt|0)|0}var Tr=256,ar=262144,Fr=4194304;function Nn(n){var l=n&42;if(l!==0)return l;switch(n&-n){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 n&-n;case 262144:case 524288:case 1048576:case 2097152:return n&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return n&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return n}}function al(n,l,i){var u=n.pendingLanes;if(u===0)return 0;var m=0,g=n.suspendedLanes,T=n.pingedLanes;n=n.warmLanes;var V=u&134217727;return V!==0?(u=V&~g,u!==0?m=Nn(u):(T&=V,T!==0?m=Nn(T):i||(i=V&~n,i!==0&&(m=Nn(i))))):(V=u&~g,V!==0?m=Nn(V):T!==0?m=Nn(T):i||(i=u&~n,i!==0&&(m=Nn(i)))),m===0?0:l!==0&&l!==m&&(l&g)===0&&(g=m&-m,i=l&-l,g>=i||g===32&&(i&4194048)!==0)?l:m}function ln(n,l){return(n.pendingLanes&~(n.suspendedLanes&~n.pingedLanes)&l)===0}function hc(n,l){(l&8)!==0&&(l|=l&32);var i=n.entangledLanes;if(i!==0)for(n=n.entanglements,i&=l;0i;i++)l.push(n);return l}function Go(n,l){n.pendingLanes|=l,l!==268435456&&(n.suspendedLanes=0,n.pingedLanes=0,n.warmLanes=0)}function Ko(n,l,i,u,m,g){var T=n.pendingLanes;n.pendingLanes=i,n.suspendedLanes=0,n.pingedLanes=0,n.warmLanes=0,n.expiredLanes&=i,n.entangledLanes&=i,n.errorRecoveryDisabledLanes&=i,n.shellSuspendCounter=0;var V=n.entanglements,se=n.expirationTimes,xe=n.hiddenUpdates;for(i=T&~i;0"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Qt=!1;if(lt)try{var In={};Object.defineProperty(In,"passive",{get:function(){Qt=!0}}),window.addEventListener("test",In,In),window.removeEventListener("test",In,In)}catch{Qt=!1}var gn=null,Pn=null,hs=null;function Er(){if(hs)return hs;var n,l=Pn,i=l.length,u,m="value"in gn?gn.value:gn.textContent,g=m.length;for(n=0;n=tm),i_=" ",c_=!1;function u_(n,l){switch(n){case"keyup":return sne.indexOf(l.keyCode)!==-1;case"keydown":return l.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function d_(n){return n=n.detail,typeof n=="object"&&"data"in n?n.data:null}var pd=!1;function ane(n,l){switch(n){case"compositionend":return d_(l);case"keypress":return l.which!==32?null:(c_=!0,i_);case"textInput":return n=l.data,n===i_&&c_?null:n;default:return null}}function lne(n,l){if(pd)return n==="compositionend"||!aN&&u_(n,l)?(n=Er(),hs=Pn=gn=null,pd=!1,n):null;switch(n){case"paste":return null;case"keypress":if(!(l.ctrlKey||l.altKey||l.metaKey)||l.ctrlKey&&l.altKey){if(l.char&&1"u")return null;try{return n.activeElement||n.body}catch{return n.body}}function v_(n){for(;n&&n.firstChild;)n=n.firstChild;return n}function y_(n,l){var i=v_(n);n=0;for(var u;i;){if(i.nodeType===3){if(u=n+i.textContent.length,n<=l&&u>=l)return{node:i,offset:l-n};n=u}e:{for(;i;){if(i.nextSibling){i=i.nextSibling;break e}i=i.parentNode}i=void 0}i=v_(i)}}function j_(n,l){return n&&l?n===l?!0:n&&n.nodeType===3?!1:l&&l.nodeType===3?j_(n,l.parentNode):"contains"in n?n.contains(l):n.compareDocumentPosition?!!(n.compareDocumentPosition(l)&16):!1:!1}function w_(n){n=n!=null&&n.ownerDocument!=null&&n.ownerDocument.defaultView!=null?n.ownerDocument.defaultView:window;for(var l=iN(n.document);l instanceof n.HTMLIFrameElement;){try{var i=typeof l.contentWindow.location.href=="string"}catch{i=!1}if(i)n=l.contentWindow;else break;l=iN(n.document)}return l}function cN(n){var l=n&&n.nodeName&&n.nodeName.toLowerCase();return l&&(l==="input"&&(n.type==="text"||n.type==="search"||n.type==="tel"||n.type==="url"||n.type==="password")||l==="textarea"||n.contentEditable==="true")}var mne=lt&&"documentMode"in document&&11>=document.documentMode,md=null,uN=null,am=null,dN=!1;function S_(n,l,i){var u=i.window===i?i.document:i.nodeType===9?i:i.ownerDocument;dN||md==null||md!==iN(u)||(u=md,"selectionStart"in u&&cN(u)?u={start:u.selectionStart,end:u.selectionEnd}:(u=(u.ownerDocument&&u.ownerDocument.defaultView||window).getSelection(),u={anchorNode:u.anchorNode,anchorOffset:u.anchorOffset,focusNode:u.focusNode,focusOffset:u.focusOffset}),am&&rm(am,u)||(am=u,u=mv(uN,"onSelect"),0>=T,m-=T,ol=1<<32-Et(l)+m|i<rt?(Ns=st,st=null):Ns=st.sibling;var Zt=fe(ge,st,ye[rt],Ee);if(Zt===null){st===null&&(st=Ns);break}n&&st&&Zt.alternate===null&&l(ge,st),ie=g(Zt,ie,rt),Ht===null?Ke=Zt:Ht.sibling=Zt,Ht=Zt,st=Ns}if(rt===ye.length)return i(ge,st),At&&Yl(ge,rt),Ke;if(st===null){for(;rtrt?(Ns=st,st=null):Ns=st.sibling;var wi=fe(ge,st,Zt.value,Ee);if(wi===null){st===null&&(st=Ns);break}n&&st&&wi.alternate===null&&l(ge,st),ie=g(wi,ie,rt),Ht===null?Ke=wi:Ht.sibling=wi,Ht=wi,st=Ns}if(Zt.done)return i(ge,st),At&&Yl(ge,rt),Ke;if(st===null){for(;!Zt.done;rt++,Zt=ye.next())Zt=Re(ge,Zt.value,Ee),Zt!==null&&(ie=g(Zt,ie,rt),Ht===null?Ke=Zt:Ht.sibling=Zt,Ht=Zt);return At&&Yl(ge,rt),Ke}for(st=u(st);!Zt.done;rt++,Zt=ye.next())Zt=je(st,ge,rt,Zt.value,Ee),Zt!==null&&(n&&(Ns=Zt.alternate,Ns!==null&&st.delete(Ns.key===null?rt:Ns.key)),ie=g(Zt,ie,rt),Ht===null?Ke=Zt:Ht.sibling=Zt,Ht=Zt);return n&&st.forEach(function(are){return l(ge,are)}),At&&Yl(ge,rt),Ke}function vt(ge,ie,ye,Ee){if(typeof ye=="object"&&ye!==null&&ye.type===H&&ye.key===null&&ye.props.ref===void 0&&(ye=ye.props.children),typeof ye=="object"&&ye!==null){switch(ye.$$typeof){case _:e:{for(var Ke=ye.key;ie!==null;){if(ie.key===Ke){if(Ke=ye.type,Ke===H){if(ie.tag===7){i(ge,ie.sibling),Ee=m(ie,ye.props.children),si(Ee,ye),Ee.return=ge,ge=Ee;break e}}else if(ie.elementType===Ke||typeof Ke=="object"&&Ke!==null&&Ke.$$typeof===G&&_c(Ke)===ie.type){i(ge,ie.sibling),Ee=m(ie,ye.props),si(Ee,ye),Ee.return=ge,ge=Ee;break e}i(ge,ie);break}else l(ge,ie);ie=ie.sibling}ye.type===H?(Ee=Ac(ye.props.children,ge.mode,Ee,ye.key),si(Ee,ye),Ee.return=ge,ge=Ee):(Ee=vb(ye.type,ye.key,ye.props,null,ge.mode,Ee),si(Ee,ye),Ee.return=ge,ge=Ee)}return T(ge);case z:e:{for(Ke=ye.key;ie!==null;){if(ie.key===Ke)if(ie.tag===4&&ie.stateNode.containerInfo===ye.containerInfo&&ie.stateNode.implementation===ye.implementation){i(ge,ie.sibling),Ee=m(ie,ye.children||[]),Ee.return=ge,ge=Ee;break e}else{i(ge,ie);break}else l(ge,ie);ie=ie.sibling}Ee=bN(ye,ge.mode,Ee),Ee.return=ge,ge=Ee}return T(ge);case G:return ye=_c(ye),vt(ge,ie,ye,Ee)}if(ue(ye))return He(ge,ie,ye,Ee);if(ae(ye)){if(Ke=ae(ye),typeof Ke!="function")throw Error(o(150));return ye=Ke.call(ye),We(ge,ie,ye,Ee)}if(typeof ye.then=="function")return vt(ge,ie,Ab(ye),Ee);if(ye.$$typeof===$)return vt(ge,ie,kb(ge,ye),Ee);Rb(ge,ye)}return typeof ye=="string"&&ye!==""||typeof ye=="number"||typeof ye=="bigint"?(ye=""+ye,ie!==null&&ie.tag===6?(i(ge,ie.sibling),Ee=m(ie,ye),Ee.return=ge,ge=Ee):(i(ge,ie),Ee=xN(ye,ge.mode,Ee),Ee.return=ge,ge=Ee),T(ge)):i(ge,ie)}return function(ge,ie,ye,Ee){try{dm=0;var Ke=vt(ge,ie,ye,Ee);return Sd=null,Ke}catch(st){if(st===wd||st===Tb)throw st;var Ht=Ar(29,st,null,ge.mode);return Ht.lanes=Ee,Ht.return=ge,Ht}}}var zc=K_(!0),Y_=K_(!1),ri=!1;function EN(n){n.updateQueue={baseState:n.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function AN(n,l){n=n.updateQueue,l.updateQueue===n&&(l.updateQueue={baseState:n.baseState,firstBaseUpdate:n.firstBaseUpdate,lastBaseUpdate:n.lastBaseUpdate,shared:n.shared,callbacks:null})}function ai(n){return{lane:n,tag:0,payload:null,callback:null,next:null}}function li(n,l,i){var u=n.updateQueue;if(u===null)return null;if(u=u.shared,(on&2)!==0){var m=u.pending;return m===null?l.next=l:(l.next=m.next,m.next=l),u.pending=l,l=bb(n),O_(n,null,i),l}return xb(n,u,l,i),bb(n)}function fm(n,l,i){if(l=l.updateQueue,l!==null&&(l=l.shared,(i&4194048)!==0)){var u=l.lanes;u&=n.pendingLanes,i|=u,l.lanes=i,gc(n,i)}}function RN(n,l){var i=n.updateQueue,u=n.alternate;if(u!==null&&(u=u.updateQueue,i===u)){var m=null,g=null;if(i=i.firstBaseUpdate,i!==null){do{var T={lane:i.lane,tag:i.tag,payload:i.payload,callback:null,next:null};g===null?m=g=T:g=g.next=T,i=i.next}while(i!==null);g===null?m=g=l:g=g.next=l}else m=g=l;i={baseState:u.baseState,firstBaseUpdate:m,lastBaseUpdate:g,shared:u.shared,callbacks:u.callbacks},n.updateQueue=i;return}n=i.lastBaseUpdate,n===null?i.firstBaseUpdate=l:n.next=l,i.lastBaseUpdate=l}var ON=!1;function pm(){if(ON){var n=jd;if(n!==null)throw n}}function mm(n,l,i,u){ON=!1;var m=n.updateQueue;ri=!1;var g=m.firstBaseUpdate,T=m.lastBaseUpdate,V=m.shared.pending;if(V!==null){m.shared.pending=null;var se=V,xe=se.next;se.next=null,T===null?g=xe:T.next=xe,T=se;var we=n.alternate;we!==null&&(we=we.updateQueue,V=we.lastBaseUpdate,V!==T&&(V===null?we.firstBaseUpdate=xe:V.next=xe,we.lastBaseUpdate=se))}if(g!==null){var Re=m.baseState;T=0,we=xe=se=null,V=g;do{var fe=V.lane&-536870913,je=fe!==V.lane;if(je?(Bt&fe)===fe:(u&fe)===fe){fe!==0&&fe===Ic&&(ON=!0),we!==null&&(we=we.next={lane:0,tag:V.tag,payload:V.payload,callback:null,next:null});e:{var He=n,We=V;fe=l;var vt=i;switch(We.tag){case 1:if(He=We.payload,typeof He=="function"){Re=He.call(vt,Re,fe);break e}Re=He;break e;case 3:He.flags=He.flags&-65537|128;case 0:if(He=We.payload,fe=typeof He=="function"?He.call(vt,Re,fe):He,fe==null)break e;Re=I({},Re,fe);break e;case 2:ri=!0}}fe=V.callback,fe!==null&&(n.flags|=64,je&&(n.flags|=8192),je=m.callbacks,je===null?m.callbacks=[fe]:je.push(fe))}else je={lane:fe,tag:V.tag,payload:V.payload,callback:V.callback,next:null},we===null?(xe=we=je,se=Re):we=we.next=je,T|=fe;if(V=V.next,V===null){if(V=m.shared.pending,V===null)break;je=V,V=je.next,je.next=null,m.lastBaseUpdate=je,m.shared.pending=null}}while(!0);we===null&&(se=Re),m.baseState=se,m.firstBaseUpdate=xe,m.lastBaseUpdate=we,g===null&&(m.shared.lanes=0),pi|=T,n.lanes=T,n.memoizedState=Re}}function X_(n,l){if(typeof n!="function")throw Error(o(191,n));n.call(l)}function Q_(n,l){var i=n.callbacks;if(i!==null)for(n.callbacks=null,n=0;ng?g:8;var T=ve.T,V={};V.types=T!==null?T.types:null,ve.T=V,ZN(n,!1,l,i);try{var se=m(),xe=ve.S;if(xe!==null&&xe(V,se),se!==null&&typeof se=="object"&&typeof se.then=="function"){var we=kne(se,u);bm(n,l,we,Jr(n))}else bm(n,l,u,Jr(n))}catch(Re){bm(n,l,{then:function(){},status:"rejected",reason:Re},Jr())}finally{ee.p=g,T!==null&&V.types!==null&&(T.types=V.types),ve.T=T}}function One(){}function XN(n,l,i,u){if(n.tag!==5)throw Error(o(476));var m=EL(n).queue;TL(n,m,l,he,i===null?One:function(){return AL(n),i(u)})}function EL(n){var l=n.memoizedState;if(l!==null)return l;l={memoizedState:he,baseState:he,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Wl,lastRenderedState:he},next:null};var i={};return l.next={memoizedState:i,baseState:i,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Wl,lastRenderedState:i},next:null},n.memoizedState=l,n=n.alternate,n!==null&&(n.memoizedState=l),l}function AL(n){var l=EL(n);l.next===null&&(l=n.alternate.memoizedState),bm(n,l.next.queue,{},Jr())}function QN(){return Xs(qd)}function RL(){return gs().memoizedState}function OL(){return gs().memoizedState}function Mne(n){for(var l=n.return;l!==null;){switch(l.tag){case 24:case 3:var i=Jr();n=ai(i);var u=li(l,n,i);u!==null&&(Dr(u,l,i),fm(u,l,i)),l={cache:NN()},n.payload=l;return}l=l.return}}function Dne(n,l,i){var u=Jr();i={lane:u,revertLane:0,gesture:null,action:i,hasEagerState:!1,eagerState:null,next:null},Hb(n)?DL(l,i):(i=hN(n,l,i,u),i!==null&&(Dr(i,n,u),IL(i,l,u)))}function ML(n,l,i){var u=Jr();bm(n,l,i,u)}function bm(n,l,i,u){var m={lane:u,revertLane:0,gesture:null,action:i,hasEagerState:!1,eagerState:null,next:null};if(Hb(n))DL(l,m);else{var g=n.alternate;if(n.lanes===0&&(g===null||g.lanes===0)&&(g=l.lastRenderedReducer,g!==null))try{var T=l.lastRenderedState,V=g(T,i);if(m.hasEagerState=!0,m.eagerState=V,Yr(V,T))return xb(n,l,m,0),_n===null&&gb(),!1}catch{}if(i=hN(n,l,m,u),i!==null)return Dr(i,n,u),IL(i,l,u),!0}return!1}function ZN(n,l,i,u){if(u={lane:2,revertLane:Uk(),gesture:null,action:u,hasEagerState:!1,eagerState:null,next:null},Hb(n)){if(l)throw Error(o(479))}else l=hN(n,i,u,2),l!==null&&Dr(l,n,2)}function Hb(n){var l=n.alternate;return n===bt||l!==null&&l===bt}function DL(n,l){Nd=Db=!0;var i=n.pending;i===null?l.next=l:(l.next=i.next,i.next=l),n.pending=l}function IL(n,l,i){if((i&4194048)!==0){var u=l.lanes;u&=n.pendingLanes,i|=u,l.lanes=i,gc(n,i)}}var Ub={readContext:Xs,use:_b,useCallback:us,useContext:us,useEffect:us,useImperativeHandle:us,useLayoutEffect:us,useInsertionEffect:us,useMemo:us,useReducer:us,useRef:us,useState:us,useDebugValue:us,useDeferredValue:us,useTransition:us,useSyncExternalStore:us,useId:us,useHostTransitionStatus:us,useFormState:us,useActionState:us,useOptimistic:us,useMemoCache:us,useCacheRefresh:us,useEffectEvent:us},PL={readContext:Xs,use:_b,useCallback:function(n,l){return xr().memoizedState=[n,l===void 0?null:l],n},useContext:Xs,useEffect:bL,useImperativeHandle:function(n,l,i){i=i!=null?i.concat([n]):null,zb(4194308,4,wL.bind(null,l,n),i)},useLayoutEffect:function(n,l){return zb(4194308,4,n,l)},useInsertionEffect:function(n,l){zb(4,2,n,l)},useMemo:function(n,l){var i=xr();l=l===void 0?null:l;var u=n();if(Bc){cs(!0);try{n()}finally{cs(!1)}}return i.memoizedState=[u,l],u},useReducer:function(n,l,i){var u=xr();if(i!==void 0){var m=i(l);if(Bc){cs(!0);try{i(l)}finally{cs(!1)}}}else m=l;return u.memoizedState=u.baseState=m,n={pending:null,lanes:0,dispatch:null,lastRenderedReducer:n,lastRenderedState:m},u.queue=n,n=n.dispatch=Dne.bind(null,bt,n),[u.memoizedState,n]},useRef:function(n){var l=xr();return n={current:n},l.memoizedState=n},useState:function(n){n=$N(n);var l=n.queue,i=ML.bind(null,bt,l);return l.dispatch=i,[n.memoizedState,i]},useDebugValue:KN,useDeferredValue:function(n,l){var i=xr();return YN(i,n,l)},useTransition:function(){var n=$N(!1);return n=TL.bind(null,bt,n.queue,!0,!1),xr().memoizedState=n,[!1,n]},useSyncExternalStore:function(n,l,i){var u=bt,m=xr();if(At){if(i===void 0)throw Error(o(407));i=i()}else{if(i=l(),_n===null)throw Error(o(349));(Bt&127)!==0||nL(u,l,i)}m.memoizedState=i;var g={value:i,getSnapshot:l};return m.queue=g,bL(rL.bind(null,u,g,n),[n]),u.flags|=2048,Cd(9,{destroy:void 0},sL.bind(null,u,g,i,l),null),i},useId:function(){var n=xr(),l=_n.identifierPrefix;if(At){var i=il,u=ol;i=(u&~(1<<32-Et(u)-1)).toString(32)+i,l="_"+l+"R_"+i,i=Ib++,0<\/script>",g=g.removeChild(g.firstChild);break;case"select":g=typeof u.is=="string"?T.createElement("select",{is:u.is}):T.createElement("select"),u.multiple?g.multiple=!0:u.size&&(g.size=u.size);break;default:g=typeof u.is=="string"?T.createElement(m,{is:u.is}):T.createElement(m)}}g[Ms]=l,g[Ys]=u;e:for(T=l.child;T!==null;){if(T.tag===5||T.tag===6)g.appendChild(T.stateNode);else if(T.tag!==4&&T.tag!==27&&T.child!==null){T.child.return=T,T=T.child;continue}if(T===l)break e;for(;T.sibling===null;){if(T.return===null||T.return===l)break e;T=T.return}T.sibling.return=T.return,T=T.sibling}l.stateNode=g;e:switch(Js(g,m,u),m){case"button":case"input":case"select":case"textarea":u=!!u.autoFocus;break e;case"img":u=!0;break e;default:u=!1}u&&eo(l)}}return Hn(l),l.subtreeFlags&=-33554433,uk(l,l.type,n===null?null:n.memoizedProps,l.pendingProps,i),null;case 6:if(n&&l.stateNode!=null)n.memoizedProps!==u&&eo(l);else{if(typeof u!="string"&&l.stateNode===null)throw Error(o(166));if(n=Ae.current,yd(l)){if(n=l.stateNode,i=l.memoizedProps,u=null,m=Hs,m!==null)switch(m.tag){case 27:case 5:u=m.memoizedProps}n[Ms]=l,n=!!(n.nodeValue===i||u!==null&&u.suppressHydrationWarning===!0||r5(n.nodeValue,i)),n||ti(l,!0)}else n=Om(n).createTextNode(u),n[Ms]=l,l.stateNode=n}return Hn(l),null;case 31:if(i=l.memoizedState,n===null||n.memoizedState!==null){if(u=yd(l),i!==null){if(n===null){if(!u)throw Error(o(318));if(n=l.memoizedState,n=n!==null?n.dehydrated:null,!n)throw Error(o(557));n[Ms]=l}else Rc(),(l.flags&128)===0&&(l.memoizedState=null),l.flags|=4;Hn(l),n=!1}else i=jN(),n!==null&&n.memoizedState!==null&&(n.memoizedState.hydrationErrors=i),n=!0;if(!n)return l.flags&256?(Xr(l),l):(Xr(l),null);if((l.flags&128)!==0)throw Error(o(558))}return Hn(l),null;case 13:if(u=l.memoizedState,n===null||n.memoizedState!==null&&n.memoizedState.dehydrated!==null){if(m=yd(l),u!==null&&u.dehydrated!==null){if(n===null){if(!m)throw Error(o(318));if(m=l.memoizedState,m=m!==null?m.dehydrated:null,!m)throw Error(o(317));m[Ms]=l}else Rc(),(l.flags&128)===0&&(l.memoizedState=null),l.flags|=4;Hn(l),m=!1}else m=jN(),n!==null&&n.memoizedState!==null&&(n.memoizedState.hydrationErrors=m),m=!0;if(!m)return l.flags&256?(Xr(l),l):(Xr(l),null)}return Xr(l),(l.flags&128)!==0?(l.lanes=i,l):(i=u!==null,n=n!==null&&n.memoizedState!==null,i&&(u=l.child,m=null,u.alternate!==null&&u.alternate.memoizedState!==null&&u.alternate.memoizedState.cachePool!==null&&(m=u.alternate.memoizedState.cachePool.pool),g=null,u.memoizedState!==null&&u.memoizedState.cachePool!==null&&(g=u.memoizedState.cachePool.pool),g!==m&&(u.flags|=2048)),i!==n&&i&&(l.child.flags|=8192),Kb(l,l.updateQueue),Hn(l),null);case 4:return pe(),n===null&&qk(l.stateNode.containerInfo),l.flags|=67108864,Hn(l),null;case 10:return Ql(l.type),Hn(l),null;case 19:if(PN(l),u=l.memoizedState,u===null)return Hn(l),null;if(m=(l.flags&128)!==0,g=u.rendering,g===null)if(m)ym(u,!1);else{if(ds!==0||n!==null&&(n.flags&128)!==0)for(n=l.child;n!==null;){if(g=Mb(n),g!==null){for(l.flags|=128,ym(u,!1),n=g.updateQueue,l.updateQueue=n,Kb(l,n),l.subtreeFlags=0,n=i,i=l.child;i!==null;)M_(i,n),i=i.sibling;return hm(l,Zs.current&1|2),At&&Yl(l,u.treeForkCount),l.child}n=n.sibling}u.tail!==null&&$t()>av&&(l.flags|=128,m=!0,ym(u,!1),l.lanes=4194304)}else{if(!m)if(n=Mb(g),n!==null){if(l.flags|=128,m=!0,n=n.updateQueue,l.updateQueue=n,Kb(l,n),ym(u,!0),u.tail===null&&u.tailMode!=="collapsed"&&u.tailMode!=="visible"&&!g.alternate&&!At)return Hn(l),null}else 2*$t()-u.renderingStartTime>av&&i!==536870912&&(l.flags|=128,m=!0,ym(u,!1),l.lanes=4194304);u.isBackwards?(g.sibling=l.child,l.child=g):(n=u.last,n!==null?n.sibling=g:l.child=g,u.last=g)}if(u.tail!==null){n=u.tail;e:{for(i=n;i!==null;){if(i.alternate!==null){i=!1;break e}i=i.sibling}i=!0}return u.rendering=n,u.tail=n.sibling,u.renderingStartTime=$t(),n.sibling=null,g=Zs.current,g=m?g&1|2:g&1,u.tailMode==="visible"||u.tailMode==="collapsed"||!i||At?hm(l,g):(i=g,Se(Qs,l),Se(Zs,i),or===null&&(or=l)),At&&Yl(l,u.treeForkCount),n}return Hn(l),null;case 22:case 23:return Xr(l),DN(),u=l.memoizedState!==null,n!==null?n.memoizedState!==null!==u&&(l.flags|=8192):u&&(l.flags|=8192),u?(i&536870912)!==0&&(l.flags&128)===0&&(Hn(l),l.subtreeFlags&6&&(l.flags|=8192)):Hn(l),i=l.updateQueue,i!==null&&Kb(l,i.retryQueue),i=null,n!==null&&n.memoizedState!==null&&n.memoizedState.cachePool!==null&&(i=n.memoizedState.cachePool.pool),u=null,l.memoizedState!==null&&l.memoizedState.cachePool!==null&&(u=l.memoizedState.cachePool.pool),u!==i&&(l.flags|=2048),n!==null&&de(Pc),null;case 24:return i=null,n!==null&&(i=n.memoizedState.cache),l.memoizedState.cache!==i&&(l.flags|=2048),Ql(js),Hn(l),null;case 25:return null;case 30:return l.flags|=33554432,Hn(l),null}throw Error(o(156,l.tag))}function Hne(n,l){switch(vN(l),l.tag){case 1:return n=l.flags,n&65536?(l.flags=n&-65537|128,l):null;case 3:return Ql(js),pe(),n=l.flags,(n&65536)!==0&&(n&128)===0?(l.flags=n&-65537|128,l):null;case 26:case 27:case 5:return Ne(l),null;case 31:if(l.memoizedState!==null){if(Xr(l),l.alternate===null)throw Error(o(340));Rc()}return n=l.flags,n&65536?(l.flags=n&-65537|128,l):null;case 13:if(Xr(l),n=l.memoizedState,n!==null&&n.dehydrated!==null){if(l.alternate===null)throw Error(o(340));Rc()}return n=l.flags,n&65536?(l.flags=n&-65537|128,l):null;case 19:return PN(l),n=l.flags,n&65536?(l.flags=n&-65537|128,n=l.memoizedState,n!==null&&(n.rendering=null,n.tail=null),l.flags|=4,l):null;case 4:return pe(),null;case 10:return Ql(l.type),null;case 22:case 23:return Xr(l),DN(),n!==null&&de(Pc),n=l.flags,n&65536?(l.flags=n&-65537|128,l):null;case 24:return Ql(js),null;case 25:return null;default:return null}}function l3(n,l){switch(vN(l),l.tag){case 3:Ql(js),pe();break;case 26:case 27:case 5:Ne(l);break;case 4:pe();break;case 31:l.memoizedState!==null&&Xr(l);break;case 13:Xr(l);break;case 19:PN(l);break;case 10:Ql(l.type);break;case 22:case 23:Xr(l),DN(),n!==null&&de(Pc);break;case 24:Ql(js)}}function jm(n,l){try{var i=l.updateQueue,u=i!==null?i.lastEffect:null;if(u!==null){var m=u.next;i=m;do{if((i.tag&n)===n){u=void 0;var g=i.create,T=i.inst;u=g(),T.destroy=u}i=i.next}while(i!==m)}}catch(V){bn(l,l.return,V)}}function ui(n,l,i){try{var u=l.updateQueue,m=u!==null?u.lastEffect:null;if(m!==null){var g=m.next;u=g;do{if((u.tag&n)===n){var T=u.inst,V=T.destroy;if(V!==void 0){T.destroy=void 0,m=l;var se=i,xe=V;try{xe()}catch(we){bn(m,se,we)}}}u=u.next}while(u!==g)}}catch(we){bn(l,l.return,we)}}function o3(n){var l=n.updateQueue;if(l!==null){var i=n.stateNode;try{Q_(l,i)}catch(u){bn(n,n.return,u)}}}function i3(n,l,i){i.props=Hc(n.type,n.memoizedProps),i.state=n.memoizedState;try{i.componentWillUnmount()}catch(u){bn(n,l,u)}}function cl(n,l){try{var i=n.ref;if(i!==null){switch(n.tag){case 26:case 27:case 5:var u=n.stateNode;break;case 30:var m=n.stateNode,g=ql(n.memoizedProps,m);(m.ref===null||m.ref.name!==g)&&(m.ref=h5(g)),u=m.ref;break;case 7:if(n.stateNode===null){var T=new ea(n);v(n.child,!1,Rse,T,void 0,void 0),n.stateNode=T}u=n.stateNode;break;default:u=n.stateNode}typeof i=="function"?n.refCleanup=i(u):i.current=u}}catch(V){bn(n,l,V)}}function Ws(n,l){var i=n.ref,u=n.refCleanup;if(i!==null)if(typeof u=="function")try{u()}catch(m){bn(n,l,m)}finally{n.refCleanup=null,n=n.alternate,n!=null&&(n.refCleanup=null)}else if(typeof i=="function")try{i(null)}catch(m){bn(n,l,m)}else i.current=null}function Yb(n,l){if((n.tag===5||n.tag===27||n.tag===6)&&n.alternate===null&&l!==null)for(var i=0;i title"))),Js(u,l,i),u[Ms]=n,ys(u),l=u;break e;case"link":if(g=M5("link","href",m).get(l+(i.href||""))){for(T=0;Tvt&&(T=vt,vt=We,We=T);var ge=y_(V,We),ie=y_(V,vt);if(ge&&ie&&(je.rangeCount!==1||je.anchorNode!==ge.node||je.anchorOffset!==ge.offset||je.focusNode!==ie.node||je.focusOffset!==ie.offset)){var ye=Re.createRange();ye.setStart(ge.node,ge.offset),je.removeAllRanges(),We>vt?(je.addRange(ye),je.extend(ie.node,ie.offset)):(ye.setEnd(ie.node,ie.offset),je.addRange(ye))}}}}for(Re=[],je=V;je=je.parentNode;)je.nodeType===1&&Re.push({element:je,left:je.scrollLeft,top:je.scrollTop});for(typeof V.focus=="function"&&V.focus(),V=0;Vi?32:i,ve.T=null,i=Ok,Ok=null;var g=Fa,T=hl;if(ts=0,$c=Fa=null,hl=0,(on&6)!==0)throw Error(o(331));var V=on;if(on|=4,O3(g.current),E3(g,g.current,T,i),on=V,Em(0,!1),en&&typeof en.onPostCommitFiberRoot=="function")try{en.onPostCommitFiberRoot(Dn,g)}catch{}return!0}finally{ee.p=m,ve.T=u,G3(n,l)}}function K3(n,l,i){l=xa(i,l),l=ek(n.stateNode,l,2),n=li(n,l,2),n!==null&&(Go(n,2),gl(n))}function bn(n,l,i){if(n.tag===3)K3(n,n,i);else for(;l!==null;){if(l.tag===3){K3(l,n,i);break}else if(l.tag===1){var u=l.stateNode;if(typeof l.type.getDerivedStateFromError=="function"||typeof u.componentDidCatch=="function"&&(mi===null||!mi.has(u))){n=xa(i,n),i=FL(2),u=li(l,i,2),u!==null&&($L(i,u,l,n),Go(u,2),gl(u));break}}l=l.return}}function zk(n,l,i){var u=n.pingCache;if(u===null){u=n.pingCache=new $ne;var m=new Set;u.set(l,m)}else m=u.get(l),m===void 0&&(m=new Set,u.set(l,m));m.has(i)||(Ek=!0,m.add(i),n=Wne.bind(null,n,l,i),l.then(n,n))}function Wne(n,l,i){var u=n.pingCache;u!==null&&u.delete(l),n.pingedLanes|=n.suspendedLanes&i,n.warmLanes&=~i,_n===n&&(Bt&i)===i&&((ds===4||ds===3&&(Bt&62914560)===Bt&&300>$t()-rv)&&(on&2)===0?Ld(n,0):sv|=i,Md===Bt&&(Md=0)),gl(n)}function Y3(n,l){l===0&&(l=mr()),n=Ec(n,l),n!==null&&(Go(n,l),gl(n))}function Jne(n){var l=n.memoizedState,i=0;l!==null&&(i=l.retryLane),Y3(n,i)}function ese(n,l){var i=0;switch(n.tag){case 31:case 13:var u=n.stateNode,m=n.memoizedState;m!==null&&(i=m.retryLane);break;case 19:u=n.stateNode;break;case 22:u=n.stateNode._retryCache;break;default:throw Error(o(314))}u!==null&&u.delete(l),Y3(n,i)}function tse(n,l){return ms(n,l)}var Bd=null,Hd=null,Bk=!1,fv=!1,Hk=!1,gi=0;function gl(n){n!==Hd&&n.next===null&&(Hd===null?Bd=Hd=n:Hd=Hd.next=n),fv=!0,Bk||(Bk=!0,sse())}function Em(n,l){if(!Hk&&fv){Hk=!0;do for(var i=!1,u=Bd;u!==null;){if(n!==0){var m=u.pendingLanes;if(m===0)var g=0;else{var T=u.suspendedLanes,V=u.pingedLanes;g=(1<<31-Et(42|n)+1)-1,g&=m&~(T&~V),g=g&201326741?g&201326741|1:g?g|2:0}g!==0&&(i=!0,W3(u,g))}else g=Bt,g=al(u,u===_n?g:0,u.cancelPendingCommit!==null||u.timeoutHandle!==-1),(g&3)===0||ln(u,g)||(i=!0,W3(u,g));u=u.next}while(i);Hk=!1}}function nse(){X3()}function X3(){fv=Bk=!1;var n=0;gi!==0&&mse()&&(n=gi);for(var l=$t(),i=null,u=Bd;u!==null;){var m=u.next,g=Q3(u,l);g===0?(u.next=null,i===null?Bd=m:i.next=m,m===null&&(Hd=i)):(i=u,(n!==0||(g&3)!==0)&&(fv=!0)),u=m}ts!==0&&ts!==5||Em(n),gi!==0&&(gi=0)}function Q3(n,l){for(var i=n.suspendedLanes,u=n.pingedLanes,m=n.expirationTimes,g=n.pendingLanes&-62914561;0V)break;var we=se.transferSize,Re=se.initiatorType;we&&a5(Re)&&(se=se.responseEnd,T+=we*(sexv){We.length=He;break}ye=new Promise(yse.bind(ye)),We.push(ye)}}}if(0"u"?null:document;function E5(n,l,i){var u=Vd;if(u&&typeof l=="string"&&l){var m=gr(l);m='link[rel="'+n+'"][href="'+m+'"]',typeof i=="string"&&(m+='[crossorigin="'+i+'"]'),T5.has(m)||(T5.add(m),n={rel:n,crossOrigin:i,href:l},u.querySelector(m)===null&&(l=u.createElement("link"),Js(l,"link",n),ys(l),u.head.appendChild(l)))}}function Bse(n){no.D(n),E5("dns-prefetch",n,null)}function Hse(n,l){no.C(n,l),E5("preconnect",n,l)}function Use(n,l,i){no.L(n,l,i);var u=Vd;if(u&&n&&l){var m='link[rel="preload"][as="'+gr(l)+'"]';l==="image"&&i&&i.imageSrcSet?(m+='[imagesrcset="'+gr(i.imageSrcSet)+'"]',typeof i.imageSizes=="string"&&(m+='[imagesizes="'+gr(i.imageSizes)+'"]')):m+='[href="'+gr(n)+'"]';var g=m;switch(l){case"style":g=Fd(n);break;case"script":g=$d(n)}if(!(Sa.has(g)||(n=I({rel:"preload",href:l==="image"&&i&&i.imageSrcSet?void 0:n,as:l},i),Sa.set(g,n),u.querySelector(m)!==null||l==="style"&&u.querySelector(Dm(g))||l==="script"&&u.querySelector(Im(g))))){var T=u.createElement("link");Js(T,"link",n),l==="style"&&(T[Ds]=!0,T.onload=T.onerror=function(){$p(T)}),ys(T),u.head.appendChild(T)}}}function Vse(n,l){no.m(n,l);var i=Vd;if(i&&n){var u=l&&typeof l.as=="string"?l.as:"script",m='link[rel="modulepreload"][as="'+gr(u)+'"][href="'+gr(n)+'"]',g=m;switch(u){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":g=$d(n)}if(!Sa.has(g)&&(n=I({rel:"modulepreload",href:n},l),Sa.set(g,n),i.querySelector(m)===null)){switch(u){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(i.querySelector(Im(g)))return}u=i.createElement("link"),Js(u,"link",n),ys(u),i.head.appendChild(u)}}}function Fse(n,l,i){no.S(n,l,i);var u=Vd;if(u&&n){var m=Qo(u).hoistableStyles,g=Fd(n);l=l||"default";var T=m.get(g);if(!T){var V={loading:0,preload:null};if(T=u.querySelector(Dm(g)))V.loading=5;else{n=I({rel:"stylesheet",href:n,"data-precedence":l},i),(i=Sa.get(g))&&oC(n,i);var se=T=u.createElement("link");ys(se),Js(se,"link",n),se._p=new Promise(function(xe,we){se.onload=xe,se.onerror=we}),se.addEventListener("load",function(){V.loading|=1}),se.addEventListener("error",function(){V.loading|=2}),V.loading|=4,hv(T,l,u)}T={type:"stylesheet",instance:T,count:1,state:V},m.set(g,T)}}}function $se(n,l){no.X(n,l);var i=Vd;if(i&&n){var u=Qo(i).hoistableScripts,m=$d(n),g=u.get(m);g||(g=i.querySelector(Im(m)),g||(n=I({src:n,async:!0},l),(l=Sa.get(m))&&iC(n,l),g=i.createElement("script"),ys(g),Js(g,"link",n),i.head.appendChild(g)),g={type:"script",instance:g,count:1,state:null},u.set(m,g))}}function qse(n,l){no.M(n,l);var i=Vd;if(i&&n){var u=Qo(i).hoistableScripts,m=$d(n),g=u.get(m);g||(g=i.querySelector(Im(m)),g||(n=I({src:n,async:!0,type:"module"},l),(l=Sa.get(m))&&iC(n,l),g=i.createElement("script"),ys(g),Js(g,"link",n),i.head.appendChild(g)),g={type:"script",instance:g,count:1,state:null},u.set(m,g))}}function A5(n,l,i,u){var m=(m=Ae.current)?Mm(m):null;if(!m)throw Error(o(446));switch(n){case"meta":case"title":return null;case"style":return typeof i.precedence=="string"&&typeof i.href=="string"?(i=Fd(i.href),l=Qo(m).hoistableStyles,u=l.get(i),u||(u={type:"style",instance:null,count:0,state:null},l.set(i,u)),u):{type:"void",instance:null,count:0,state:null};case"link":if(i.rel==="stylesheet"&&typeof i.href=="string"&&typeof i.precedence=="string"){n=Fd(i.href);var g=Qo(m).hoistableStyles,T=g.get(n);if(T||(m=m.ownerDocument||m,T={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},g.set(n,T),(g=m.querySelector(Dm(n)))?g._p||(T.instance=g,T.state.loading=5):(g=Sa.get(n),g||(g={rel:"preload",as:"style",href:i.href,crossOrigin:i.crossOrigin,integrity:i.integrity,media:i.media,hrefLang:i.hrefLang,referrerPolicy:i.referrerPolicy},Sa.set(n,g)),Gse(m,n,g,T.state))),l&&u===null)throw Error(o(528,""));return T}if(l&&u!==null)throw Error(o(529,""));return null;case"script":return l=i.async,i=i.src,typeof i=="string"&&l&&typeof l!="function"&&typeof l!="symbol"?(i=$d(i),l=Qo(m).hoistableScripts,u=l.get(i),u||(u={type:"script",instance:null,count:0,state:null},l.set(i,u)),u):{type:"void",instance:null,count:0,state:null};default:throw Error(o(444,n))}}function Fd(n){return'href="'+gr(n)+'"'}function Dm(n){return'link[rel="stylesheet"]['+n+"]"}function R5(n){return I({},n,{"data-precedence":n.precedence,precedence:null})}function Gse(n,l,i,u){if(l=n.querySelector('link[rel="preload"][as="style"]['+l+"]")){if(l[Ds]!==!0){u.loading=1;return}}else l=n.createElement("link"),l[Ds]=!0,l.onload=l.onerror=$p.bind(null,l),Js(l,"link",i),ys(l),n.head.appendChild(l);u.preload=l,l.addEventListener("load",function(){return u.loading|=1}),l.addEventListener("error",function(){return u.loading|=2})}function $d(n){return'[src="'+gr(n)+'"]'}function Im(n){return"script[async]"+n}function O5(n,l,i){if(l.count++,l.instance===null)switch(l.type){case"style":var u=n.querySelector('style[data-href~="'+gr(i.href)+'"]');if(u)return l.instance=u,ys(u),u;var m=I({},i,{"data-href":i.href,"data-precedence":i.precedence,href:null,precedence:null});return u=(n.ownerDocument||n).createElement("style"),ys(u),Js(u,"style",m),hv(u,i.precedence,n),l.instance=u;case"stylesheet":m=Fd(i.href);var g=n.querySelector(Dm(m));if(g)return l.state.loading|=4,l.instance=g,ys(g),g;u=R5(i),(m=Sa.get(m))&&oC(u,m),g=(n.ownerDocument||n).createElement("link"),ys(g);var T=g;return T._p=new Promise(function(V,se){T.onload=V,T.onerror=se}),Js(g,"link",u),l.state.loading|=4,hv(g,i.precedence,n),l.instance=g;case"script":return g=$d(i.src),(m=n.querySelector(Im(g)))?(l.instance=m,ys(m),m):(u=i,(m=Sa.get(g))&&(u=I({},i),iC(u,m)),n=n.ownerDocument||n,m=n.createElement("script"),ys(m),Js(m,"link",u),n.head.appendChild(m),l.instance=m);case"void":return null;default:throw Error(o(443,l.type))}else l.type==="stylesheet"&&(l.state.loading&4)===0&&(u=l.instance,l.state.loading|=4,hv(u,i.precedence,n));return l.instance}function hv(n,l,i){for(var u=i.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),m=u.length?u[u.length-1]:null,g=m,T=0;T title"):null)}function Kse(n,l,i){if(i===1||l.itemProp!=null)return!1;switch(n){case"meta":case"title":return!0;case"style":if(typeof l.precedence!="string"||typeof l.href!="string"||l.href==="")break;return!0;case"link":if(typeof l.rel!="string"||typeof l.href!="string"||l.href===""||l.onLoad||l.onError)break;return l.rel==="stylesheet"?(n=l.disabled,typeof l.precedence=="string"&&n==null):!0;case"script":if(l.async&&typeof l.async!="function"&&typeof l.async!="symbol"&&!l.onLoad&&!l.onError&&l.src&&typeof l.src=="string")return!0}return!1}function D5(n,l){return n==="img"&&l.src!=null&&l.src!==""&&l.onLoad==null&&l.loading!=="lazy"}function I5(n){return!(n.type==="stylesheet"&&(n.state.loading&3)===0)}function P5(n){return(n.width||100)*(n.height||100)*(typeof devicePixelRatio=="number"?devicePixelRatio:1)*.25}function _5(n,l){typeof l.decode=="function"&&(n.imgCount++,l.complete||(n.imgBytes+=P5(l),n.suspenseyImages.push(l)),n=Qse.bind(n),l.decode().then(n,n))}function Yse(n,l,i,u){if(i.type==="stylesheet"&&(typeof u.media!="string"||matchMedia(u.media).matches!==!1)&&(i.state.loading&4)===0){if(i.instance===null){var m=Fd(u.href),g=l.querySelector(Dm(m));if(g){l=g._p,l!==null&&typeof l=="object"&&typeof l.then=="function"&&(n.count++,n=Pm.bind(n),l.then(n,n)),i.state.loading|=4,i.instance=g,ys(g);return}g=l.ownerDocument||l,u=R5(u),(m=Sa.get(m))&&oC(u,m),g=g.createElement("link"),ys(g);var T=g;T._p=new Promise(function(V,se){T.onload=V,T.onerror=se}),Js(g,"link",u),i.instance=g}n.stylesheets===null&&(n.stylesheets=new Map),n.stylesheets.set(i,l),(l=i.state.preload)&&(i.state.loading&3)===0&&(n.count++,i=Pm.bind(n),l.addEventListener("load",i),l.addEventListener("error",i))}}var xv=0;function Xse(n,l){return n.stylesheets&&n.count===0&&vv(n,n.stylesheets),0xv?50:800)+l);return n.unsuspend=i,function(){n.unsuspend=null,clearTimeout(u),clearTimeout(m)}}:null}function L5(n){if(n.count===0&&(n.imgCount===0||!n.waitingForImages)){if(n.stylesheets)vv(n,n.stylesheets);else if(n.unsuspend){var l=n.unsuspend;n.unsuspend=null,l()}}}function Pm(){this.count--,L5(this)}function Qse(){this.imgCount--,L5(this)}var bv=null;function vv(n,l){n.stylesheets=null,n.unsuspend!==null&&(n.count++,bv=new Map,l.forEach(Zse,n),bv=null,Pm.call(n))}function Zse(n,l){if(!(l.state.loading&4)){var i=bv.get(n);if(i)var u=i.get(null);else{i=new Map,bv.set(n,i);for(var m=n.querySelectorAll("link[data-precedence],style[data-precedence]"),g=0;g"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}return e(),Ej.exports=I8(),Ej.exports}var jn=P8();const dg=[20,50,100,200],fg=[20,50,100,200,"all"],xu=50,VR="workbuddy-desktop-table-size:";function FR(e){return e==="all"?r("全部"):String(e)}function pg(e,t=fg,a=xu){t.includes(a)||(a=t.includes(xu)?xu:t[0]);let o=null;try{o=localStorage.getItem(VR+e)}catch{return a}if(o===null||o.trim()==="")return a;if(o==="all")return t.includes("all")?"all":a;const c=Number(o);return Number.isFinite(c)&&t.includes(c)?c:a}function mg(e,t){try{localStorage.setItem(VR+e,String(t))}catch{}}function hg(e,t,a={}){const o=a.sizes??fg,c=a.defaultSize??xu,[d,f]=x.useState(()=>pg(t,o,c)),[p,h]=x.useState(1),b=Math.max(1,d==="all"?1:Math.ceil(e/d)),y=Math.min(Math.max(1,p),b);x.useEffect(()=>{y!==p&&h(y)},[y,p]);const v=x.useCallback(k=>{f(k),mg(t,k),h(1)},[t]),j=x.useCallback(k=>{h(Math.min(Math.max(1,Math.round(k)||1),b))},[b]),w=d==="all"?0:(y-1)*d,S=d==="all"?e:Math.min(e,w+d),N=x.useCallback(k=>d==="all"?k.slice():k.slice(w,S),[d,w,S]);return{size:d,page:y,pageCount:b,rangeStart:e?w+1:0,rangeEnd:e?S:0,paged:d!=="all",setSize:v,goto:j,slice:N}}function $i({total:e,page:t,pageCount:a,size:o,sizes:c=fg,onSizeChange:d,onPageChange:f,range:p=null,disabled:h=!1,leading:b,className:y}){const[v,j]=x.useState(String(t));x.useEffect(()=>j(String(t)),[t]);const w=()=>{const k=v.trim();if(!k){j(String(t));return}const A=Number(k);if(!Number.isFinite(A)){j(String(t));return}const P=Math.min(Math.max(1,Math.round(A)),a);j(String(P)),P!==t&&f(P)},S=a<=1,N=e===0?r("共 0 条"):p?r("共 {total} 条 · 当前第 {start}–{end} 条",{total:e,start:p.start,end:p.end}):r("共 {total} 条(全部显示)",{total:e});return s.jsxs("div",{className:y?`panel-foot table-foot-bar ${y}`:"panel-foot table-foot-bar",children:[b,s.jsx("div",{className:"spacer"}),s.jsxs("div",{className:"table-foot-ctl",children:[s.jsx("span",{className:"table-foot-count",children:N}),s.jsxs("span",{className:"table-foot-size",children:[r("每页"),s.jsxs(ut,{value:String(o),onValueChange:k=>d(k==="all"?"all":Number(k)),children:[s.jsx(ft,{className:"h-[26px] w-[86px]",disabled:h,"aria-label":r("每页条数"),children:s.jsx(dt,{children:FR(o)})}),s.jsx(pt,{children:c.map(k=>s.jsx($e,{value:String(k),children:FR(k)},String(k)))})]}),r("条")]}),s.jsxs("span",{className:"table-foot-jump",children:[r("跳至"),s.jsx(Fe,{className:"h-[26px] w-[52px] px-1 text-center",value:v,disabled:h||S,"aria-label":r("跳转到指定页"),title:S?r("当前只有一页"):r("跳转到第 1–{pageCount} 页",{pageCount:a}),inputMode:"numeric",onChange:k=>j(k.target.value),onKeyDown:k=>{k.key==="Enter"?(k.preventDefault(),w()):k.key==="Escape"&&j(String(t))},onBlur:w}),r("页")]}),s.jsx(K6,{page:t,pageCount:a,onPageChange:f,disabled:h})]})]})}const Oj=[{key:"pick",label:r("选择"),align:"center",legacyAlign:"left"},{key:"order",label:r("顺序"),title:r("组内先后:按住左边的把手上下拖动即可调整;整组的先后拖分组带的把手"),align:"center",legacyAlign:"left"},{key:"account",label:r("账号"),align:"center",legacyAlign:"left"},{key:"proxy",label:r("代理"),title:r("该账号出网走的代理(Clash 出口 / 自定义 / 直连);点击可修改"),align:"center"},{key:"connections",label:r("连接数"),title:r("此刻正在使用这个账号的请求数(含还在下发内容的流式请求);为 0 时不显示"),align:"center"},{key:"status",label:r("状态"),align:"center",legacyAlign:"left"},{key:"limits",label:r("限流"),title:r("该账号当前限流中的模型;点徽章看明细"),align:"center",legacyAlign:"left"},{key:"expiry",label:r("有效期"),align:"center",legacyAlign:"left"},{key:"usage",label:r("余额"),align:"center",legacyAlign:"left"},{key:"actions",label:r("操作"),align:"right"}],$R="agent2api-accounts-col-widths",If={pick:47,order:84,proxy:186,connections:56,status:80,limits:148,expiry:80,usage:148,actions:200},Mj=new Set(["account"]),_8=300,Dj=56,L8=new Set([...Object.keys(If),...Mj]),bu=(()=>{try{const e=JSON.parse(localStorage.getItem($R)||"{}"),t={};for(const[a,o]of Object.entries(e||{})){const c=Number(o);L8.has(a)&&Number.isFinite(c)&&c>=Dj&&(t[a]=Math.round(c))}return t}catch{return{}}})();function qR(){try{localStorage.setItem($R,JSON.stringify(bu))}catch{}}function z8(){return{...If,...bu}}function B8(e){let t=0;for(const a of e){const o=bu[a];o?t+=o:Mj.has(a)?t+=_8:If[a]?t+=If[a]:t+=Dj}return Math.round(t)}function GR(e,t){const a=e?.querySelector(`thead th:nth-child(${t+1})`),o=a?.getAttribute("data-col")||a?.className.match(/cell-([a-z]+)/)?.[1];if(!o)return null;const c=e?.querySelector(`colgroup col[data-col="${CSS.escape(o)}"]`)||e?.querySelectorAll("colgroup col")[t];return c?{key:o,col:c}:null}function H8(e,t,a){const o=Math.max(Dj,Math.round(a));e.style.width=o+"px",Mj.has(t)||o!==If[t]?bu[t]=o:delete bu[t]}let Ij=()=>{};function U8(e,t){if(Ij=t,!e)return;let a=null;e.addEventListener("pointerdown",o=>{if(o.button!==0)return;const c=o.target?.closest?.(".col-grip");if(!c)return;o.preventDefault();const d=c.closest("th"),f=d?.closest("table")||null,p=d?.parentElement?[...d.parentElement.children].indexOf(d):-1,h=GR(f,p);if(!h)return;const b=o.clientX,y=h.col.getBoundingClientRect().width;c.classList.add("active"),document.body.classList.add("col-resizing");try{c.setPointerCapture(o.pointerId)}catch{}a={...h,startX:b,startWidth:y,grip:c,pointerId:o.pointerId},window.addEventListener("pointermove",v),window.addEventListener("pointerup",j),window.addEventListener("pointercancel",j),window.addEventListener("blur",j);function v(w){if(a){if(w.buttons===0){j();return}H8(a.col,a.key,a.startWidth+w.clientX-a.startX)}}function j(){if(!a)return;const{grip:w,pointerId:S}=a;a=null,w.classList.remove("active"),document.body.classList.remove("col-resizing");try{w.releasePointerCapture(S)}catch{}window.removeEventListener("pointermove",v),window.removeEventListener("pointerup",j),window.removeEventListener("pointercancel",j),window.removeEventListener("blur",j),qR(),Ij()}}),e.addEventListener("dblclick",o=>{const c=o.target?.closest?.(".col-grip");if(!c)return;const d=c.closest("th"),f=d?.closest("table")||null,p=d?.parentElement?[...d.parentElement.children].indexOf(d):-1,h=GR(f,p);h&&(delete bu[h.key],qR(),Ij())})}function gt(){return window}function Ln(e,t){gt().wbApp?.toast?.(e,t)}function ko(e){return e instanceof Error&&e.message?e.message:String(e??"").trim()||r("未知错误")}function Pj(e){return(gt().wbApp?.getState?.()?.accounts?.accounts||[]).filter(a=>(a?.provider||"workbuddy")===e).length}function KR(e){const t=e?.name||"";if(t)return(gt().wbProviders?.customList?.()||[]).find(a=>(a.name||"")===t)}function V8(e){const t=e?.name||"";return t?(gt().wbPresetProviders?.list||[]).some(a=>a.name===t):!1}async function vu(e){const t=gt().__TAURI_INTERNALS__;if(!t||typeof t.invoke!="function")throw new Error(r("桌面运行时不可用(Tauri 未初始化)"));return t.invoke("api_request",{request:{method:"POST",path:"/api/accounts",body:e}})}function Pf(e){return e?.name||e?.uid||e?.userId||""}function _j(){gt().wbAddAccountModal?.close?.()}async function Co(e,t){_j(),await gt().wbApp?.refresh?.(),Ln(r("✅ {label}账号已添加{name}",{label:t,name:e?`:${e}`:""}))}const yu=new Map;function _r(e){return{defaultValue:yu.get(e)??"",onChange:t=>{const a=t.currentTarget.value;a?yu.set(e,a):yu.delete(e)}}}function To(e,t){t?yu.set(e,t):yu.delete(e);const a=document.getElementById(e);(a instanceof HTMLInputElement||a instanceof HTMLTextAreaElement)&&(a.value=t)}function dr(e){const t=document.getElementById(e);return t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement?t.value.trim():(yu.get(e)??"").trim()}function Lj(e){for(const t of e)To(t,"")}const qi={edition:"cn",loginMode:"embedded",socialRestore:!1},zj=100,F8=256,$8=e=>e.key==="name"?zj:F8,q8={provider:"raccoon",label:"小浣熊",addButton:r("添加小浣熊账号"),webLogin:{noteHtml:r("在内嵌窗口里完成官方登录,成功后自动加入账号列表。"),button:r("打开网页登录"),hint:r("内嵌窗口打开;完成后自动加入列表,关窗即取消等待"),busyText:r("等待小浣熊登录完成…")},manualTitle:r("粘贴 token / refreshToken"),manualNoteHtml:r('refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。'),fields:[{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用凭证里的账号名")},{key:"token",label:"token",rows:3,placeholder:r("粘贴 access_token(一长串 JWT)")},{key:"refreshToken",inputKey:"refresh",label:"refreshToken",rows:2,optional:!0,placeholder:r("可选")}],desktopNote:r("读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后点「刷新 Token」同步。"),fileImport:{accept:".json,application/json",note:r("选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。")}},G8={provider:"catpaw",label:"CatPaw",webLogin:{noteHtml:r("用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。"),button:r("打开 CatPaw 网页登录"),busyText:r("等待 CatPaw 登录完成…"),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("内嵌窗口打开;完成后自动加入列表,关窗即取消等待")},{value:"external",label:r("系统浏览器"),hint:r("系统浏览器打开(复用已登录的美团账号);完成后自动加入列表")}]},manualNote:r("token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。"),fields:[{key:"token",label:"token",rows:3,placeholder:r("CatPaw 的 X-Passport-Token(登录态 Cookie)")},{key:"uid",label:"uid",placeholder:r("必填,CatPaw 账号标识")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用登录名或 uid")}],desktopNote:r("读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后重新导入即可。"),desktopHint:r("读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录"),fileImport:{accept:".json,application/json",note:r("选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。")}},K8={provider:"autoclaw",label:r("AutoClaw 国内版"),manualNote:r("token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。"),smsLogin:{noteHtml:r("用绑定的手机号登录:点「获取验证码」后填入即可。这是国内版官方唯一的登录方式。")},fields:[{key:"token",label:"token",rows:3,placeholder:r("明文 JWT 或 auth.json 里的 enc: 加密值(自动解密)")},{key:"refreshToken",label:"refreshToken",rows:2,optional:!0,placeholder:r("没有则无法自动续期")},{key:"deviceId",label:"deviceId",optional:!0,placeholder:r("可选,续期时带上")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用 userId")}],desktopNote:r("读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。"),desktopHint:r("读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows"),desktopWindowsOnly:!0},Y8={provider:"autoclaw-intl",label:r("AutoClaw 国际版"),manualNote:r("国际版与国内版账号体系独立,请填国际版的凭证。未填 refreshToken 无法自动续期。"),oauthLogin:{title:r("网页登录(Zai / Google)"),noteHtml:r("点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
    这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。"),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)")},{value:"external",label:r("系统浏览器"),hint:r("系统浏览器打开(复用已登录的 Zai / Google 账号);完成后自动加入列表")}]},fields:[{key:"token",label:"token",rows:3,placeholder:r("明文 JWT(国际版账号的 access token)")},{key:"refreshToken",label:"refreshToken",rows:2,optional:!0,placeholder:r("没有则无法自动续期")},{key:"deviceId",label:"deviceId",optional:!0,placeholder:r("可选,续期时带上")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用 userId")}],desktopNote:r("读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。"),desktopHint:r("读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows"),desktopWindowsOnly:!0};function YR(e){const{provider:t,label:a,siteNote:o}=e;return{provider:t,label:a,desktop:!1,webLogin:{noteHtml:r("打开官方授权页完成设备码授权,登录的是{label}({site})的账号。",{label:a,site:o}),button:r("打开 Qoder 网页登录"),busyText:r("等待 Qoder 授权完成…"),edition:t==="qoder"?"cn":"global",modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("内嵌窗口用全新环境,多账号互不影响;关窗即取消等待")},{value:"external",label:r("系统浏览器"),hint:r("系统浏览器打开(复用已登录账号);完成后自动加入列表")}]},manualTitle:r("使用个人访问令牌(PAT)"),manualNote:r("在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。",{label:a}),fields:[{key:"pat",label:"Qoder PAT",rows:3,placeholder:r("粘贴 Qoder 个人访问令牌(pt-…)")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空使用账号昵称或邮箱")}]}}const X8=YR({provider:"qoder",label:r("中国版"),siteNote:"qoder.com.cn"}),Q8=YR({provider:"qoder-intl",label:r("国际版"),siteNote:"qoder.com"});function XR(e){const{provider:t,label:a,poolNote:o}=e;return{provider:t,label:a,desktop:!0,webLogin:{noteHtml:r("打开授权页完成确认,账号自动加入 {label}{note}",{label:a,note:o}),button:r("打开 Cline 授权页"),busyText:r("等待 Cline 授权确认…"),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("内嵌窗口打开;完成后自动加入列表,关窗即取消等待")},{value:"external",label:r("系统浏览器"),hint:r("系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表")}]},manualTitle:r("填写凭证"),manualNote:r("refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。",{label:a}),fields:[{key:"accessToken",label:"accessToken",rows:3,placeholder:r("粘贴 workos:… 开头的令牌(不带前缀也会自动补上)")},{key:"refreshToken",label:"refreshToken",rows:2,optional:!0,placeholder:r("可选,没有则无法自动续期")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空使用账号姓名、邮箱或令牌指纹")}],desktopNote:r("读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。",{label:a}),desktopHint:r("读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录"),fileImport:{accept:".json,application/json",note:r("选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。",{label:a})}}}function QR(e){const{provider:t,label:a,site:o,siteNote:c}=e;return{provider:t,label:a,webLogin:{noteHtml:r("打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。",{label:a,site:o}),button:r("打开 Accio {label}登录页",{label:a}),busyText:r("等待 Accio {label}登录完成…",{label:a}),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待")},{value:"external",label:r("系统浏览器"),hint:r("将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待")}]},manualTitle:r("填写凭证"),manualNoteHtml:r("accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)",{label:a,siteNote:c}),fields:[{key:"accessToken",label:"accessToken",rows:3,placeholder:r("粘贴 Accio 的 accessToken")},{key:"refreshToken",label:"refreshToken",rows:2,optional:!0,placeholder:r("可选,没有则无法自动续期")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空使用账号昵称或邮箱")}]}}function ZR(e){const{provider:t,label:a,site:o,planNote:c}=e;return{provider:t,label:a,desktop:!1,webLogin:{noteHtml:r("打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。",{label:a}),button:r("打开 ZCode {label}授权页",{label:a}),busyText:r("等待 ZCode {label}授权完成…",{label:a}),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下")},{value:"external",label:r("系统浏览器"),hint:r("将用系统默认浏览器打开授权页(会复用浏览器里已登录的 ZCode 账号);授权完成后自动加入账号列表。页面最后可能提示无法打开 zcode:// 链接,属正常现象")}]},manualTitle:r("填写凭证"),manualNoteHtml:r("本家有两个**互不替代**的凭证,按你要用的功能填,至少填一个:编码套餐 API Key 用于转发推理(打 {site})与查询窗口限额(每 N 小时 / 每周的剩余比例);jwt 用于领取套餐与查询余额(打 zcode.z.ai 的 billing 网关,官方叫 Coding Plan JWT,是一串三段点分的字符串)。余额两把都能查、但读到的东西不同:jwt 读到余额桶与套餐到期,API Key 读到窗口限额;而领取只认 jwt、转发只认 API Key。请填写 {label}账号的凭证 —— {plan}(最容易拿到的办法:直接用上方的「网页登录」—— 它会替你把这个 API Key 换好,两个凭证一起拿到。)",{site:o,label:a,plan:c}),fields:[{key:"accessToken",label:r("编码套餐 API Key"),rows:3,optional:!0,placeholder:r("用于转发;形如 apiKey.secret 两段点分(不填则这个账号不能转发)")},{key:"jwt",label:"Coding Plan JWT",inputKey:"jwt",rows:3,optional:!0,placeholder:r("用于领取套餐与查询余额;不填则这个账号不能领取与查余额")},{key:"userId",label:r("用户 ID"),optional:!0,placeholder:r("可选;用于生成账号 id 与展示名")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空自动生成")}]}}const Z8={provider:"codearts",label:"CodeArts",desktop:!1,webLogin:{noteHtml:r("打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
    网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。"),button:r("打开 CodeArts 授权页"),busyText:r("等待 CodeArts 登录完成…"),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待")},{value:"external",label:r("系统浏览器"),hint:r("将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板")}]},manualTitle:r("粘贴登录凭据"),manualNoteHtml:r('整份粘贴官方插件 / CLIProxyAPI 落盘的凭据 JSON(形如 {"codearts_provider_credential":{…}},铺平的也行)。
    必填:access_key_id、secret_access_key、security_token。要能自动续期,必须连 refresh_token 与 oauth_context 一起粘 —— 临时凭据约一小时到期,缺这半块就续不回来,只能重新登录。'),fields:[{key:"credentialJson",label:r("凭据 JSON"),rows:8,jsonExpand:!0,placeholder:'{"codearts_provider_credential":{"access_key_id":"HSTA…","secret_access_key":"…","security_token":"…","expires_at":"2026-09-27T16:17:00.327Z","domain_id":"…","user_id":"…","user_name":"…","refresh_token":"eyJ…","oauth_context":{…}}}'},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空使用凭据里的 user_name")}]},W8={provider:"trae",label:"Trae",desktop:!1,webLogin:{noteHtml:r("打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。"),button:r("打开 Trae 授权页"),busyText:r("等待 Trae 授权完成…"),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)")},{value:"external",label:r("系统浏览器"),hint:r("将用系统默认浏览器打开授权页(会复用浏览器里已登录的 Trae 账号);同机完成后自动加入,Docker / 远程面板按提示粘贴地址栏回调地址。")}]},manualTitle:r("填写凭证"),manualNoteHtml:r("accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
    手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
    没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。"),fields:[{key:"accessToken",label:"accessToken",rows:3,placeholder:r("Cloud-IDE-JWT(三段点分)")},{key:"refreshToken",label:"refreshToken",rows:2,optional:!0,placeholder:r("可选;填了才能到期自动续期")},{key:"uid",label:r("用户 ID"),optional:!0,placeholder:r("可选;用于去重与展示名(留空时网关会问一次上游)")},{key:"machineId",label:"machineId",optional:!0,placeholder:r("可选;UUID 形态,与凭据同生共死")},{key:"deviceId",label:"deviceId",optional:!0,placeholder:r("可选;纯数字串(上游只收 8–24 位数字)")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空自动用昵称")}]},J8={provider:"loomy",label:"Loomy",smsLogin:{noteHtml:r("用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。")},manualNote:r("粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。"),fields:[{key:"session",label:"session",rows:3,placeholder:r("登录 session(一长串)")},{key:"userId",label:"userId",optional:!0,placeholder:r("可选,讯飞侧 userid")},{key:"phone",label:r("手机号"),optional:!0,placeholder:r("可选,展示用")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用脱敏手机号或 userId")}],desktop:!1},eq={provider:"kuku",label:"KukuAI",webLogin:{noteHtml:r("在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。"),button:r("打开网页登录"),busyText:r("等待 KukuAI 登录完成…")},manualTitle:r("粘贴 Cookie"),manualNote:r("BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。"),fields:[{key:"cookie",label:"Cookie",rows:3,placeholder:r("BDUSS=…; STOKEN=…(或 Cookie 编辑器导出的 JSON)")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用账号 uk")}],desktop:!0,desktopHint:r("读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录"),desktopNote:r("读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。"),fileImport:{binary:!0,accept:"",note:r("选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。")}};function WR(e){const{provider:t,label:a,siteNote:o}=e;return{provider:t,label:a,desktop:!1,manualTitle:r("粘贴 session cookie"),manualNoteHtml:r("粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
    {siteNote}
    MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。",{siteNote:o}),fields:[{key:"session",label:"session",rows:3,placeholder:r("monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)")},{key:"imageId",label:r("任务镜像 id"),optional:!0,placeholder:r("可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用上游返回的账号名或账号 id")}]}}const tq=WR({provider:"monkeycode",label:r("MonkeyCode"),siteNote:r("这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。")}),nq=WR({provider:"monkeycode-intl",label:r("MonkeyCode 国际版"),siteNote:r("这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。")}),sq={provider:"commandcode",label:r("Command Code"),desktop:!1,manualTitle:r("粘贴 API Key"),manualNote:r("粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。"),fields:[{key:"apiKey",label:"apiKey",rows:3,placeholder:r("user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用 key 尾号")}]},rq={provider:"antigravity",label:r("Antigravity"),desktop:!1,webLogin:{noteHtml:r("在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。"),button:r("打开 Antigravity 网页登录"),busyText:r("等待 Google 授权完成…"),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("内嵌窗口打开;完成后自动加入列表,关窗即取消等待")},{value:"external",label:r("系统浏览器"),hint:r("系统浏览器打开(复用已登录的 Google 账号);完成后自动加入列表")}]},manualTitle:r("粘贴 Google refresh token"),manualNoteHtml:r("粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
    这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。"),fields:[{key:"refreshToken",label:"refreshToken",rows:2,placeholder:r("1// 开头的 Google refresh token(也可直接粘贴 refresh_token=… 整行)")},{key:"projectId",label:"projectId",optional:!0,placeholder:r("可选(cloudaicompanionProject):留空由网关自动发现")},{key:"email",label:"email",optional:!0,placeholder:r("可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用邮箱或令牌尾号")}]},aq={provider:"officeace",label:r("OfficeAce 果办"),desktop:!1,webLogin:{noteHtml:r("打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。"),button:r("打开授权页"),busyText:r("等待 OfficeAce 登录完成…"),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待")},{value:"external",label:r("系统浏览器"),hint:r("将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表")}]},manualTitle:r("粘贴网关凭据"),manualNoteHtml:r("从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。"),fields:[{key:"baseUrl",label:r("模型网关基址"),placeholder:"https://modelgw-…huaweicloud-agentarts.com/v2"},{key:"modelAppKey",label:r("网关 Key"),placeholder:"model_app_key"},{key:"modelAppSecret",label:r("网关 Secret"),placeholder:"model_app_secret"},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空用「OfficeAce 果办」")}]},lq={provider:"orcaterm",label:"OrcaTerm",webLogin:{noteHtml:r("在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。"),button:r("打开网页登录"),hint:r("内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待"),busyText:r("等待 OrcaTerm 登录完成…")},desktop:!0,desktopHint:r("读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录"),desktopNote:r("读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。"),manualTitle:r("粘贴 accessToken"),manualNoteHtml:r("粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。"),fields:[{key:"accessToken",label:"accessToken",rows:3,placeholder:r("JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用账号昵称或 userId")}]},oq={provider:"chatglm",label:"智谱清言",guestLogin:{noteHtml:r("不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

    注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。"),button:r("添加访客账号"),busyText:r("正在换发访客凭证…"),namePlaceholder:r("可选,给这个访客账号起个备注名")},desktop:!0,desktopWindowsOnly:!0,desktopHint:r("读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录"),desktopNote:r("读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。"),manualTitle:r("粘贴访问令牌"),manualNoteHtml:r("粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。"),fields:[{key:"accessToken",label:"accessToken",rows:3,placeholder:r("JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用账号昵称")}]},JR=[q8,G8,K8,Y8,X8,Q8,XR({provider:"cline-free",label:"Cline Free",poolNote:r("(免费额度池,模型名带 cline-free/ 前缀)。")}),XR({provider:"cline-pass",label:"Cline Pass",poolNote:r("(订阅池,模型名带 cline-pass/ 前缀,需要账号有对应订阅)。")}),QR({provider:"accio",label:r("国际版"),site:"www.accio.com",siteNote:r("国际版与国内版是**两套独立的账号**(同一账号体系的两个站点),凭证不通用。")}),QR({provider:"accio-cn",label:r("国内版"),site:"www.accio-ai.com",siteNote:r("国内版的登录站点是 www.accio-ai.com,与国际版不是同一站。")}),ZR({provider:"zcode",label:r("国内版"),site:"open.bigmodel.cn",planNote:r("国内版与**国际版是两套独立的账号与套餐**,凭证与领取的套餐都不通用。")}),ZR({provider:"zcode-intl",label:r("国际版"),site:"api.z.ai",planNote:r("国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。")}),Z8,W8,J8,eq,tq,nq,sq,rq,aq,lq,oq],Gi="workbuddy",ju="custom",Bj="WorkBuddy";function iq(e){return JR.find(t=>t.provider===e)}const eO=[{id:"guest",label:r("访客模式(免登录)")},{id:"oauth",label:r("网页登录(Zai / Google)")},{id:"sms",label:r("手机验证码登录")},{id:"web",label:r("网页登录")},{id:"manual",label:r("填写凭证")},{id:"file",label:r("导入凭证文件")},{id:"desktop",label:r("导入桌面端登录态")}],Hj=()=>gt().workbuddyDesktop?.platform||"";function tO(e){if(e.desktop===!1)return!1;const t=Hj();return!(t==="web"||e.desktopWindowsOnly&&t!=="windows")}function nO(e,t){return eO.filter(a=>a.id==="guest"?!!e.guestLogin:a.id==="oauth"?!!e.oauthLogin:a.id==="sms"?!!e.smsLogin:a.id==="web"?!!e.webLogin&&(!e.webLogin?.region||t===e.webLogin.region):a.id==="file"?!!e.fileImport:a.id==="desktop"?tO(e):!0).map(a=>a.id)}const cq=e=>e.manualTitle||r("填写凭证添加"),uq=e=>e.manualNoteHtml?{html:e.manualNoteHtml}:{text:e.manualNote||""},dq=e=>e.addButton||r("添加 {name} 账号",{name:e.label}),gg=(e,t)=>`${e.provider}-${t.inputKey||t.key}-input`;function fq(e){return e.source==="subscription"&&(e.port===null||e.port===void 0)}function tr(){return window}function qn(e,t){tr().wbApp?.toast?.(e,t)}function Ra(e){return e instanceof Error?e.message:String(e)}function sO(e){return tr().wbApp?.formatTime?.(e)||""}function Uj(e){const t=Number(e);return!Number.isFinite(t)||t<0?"":t<1e3?`${Math.round(t)}ms`:`${(t/1e3).toFixed(1)}s`}function Vj(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t]||t)}function wu(e){return String(e?.name||e?.id||"")}function rO(e){return e.source==="clash"}function _f(e){return e.source==="subscription"}function xg(e){return rO(e)||_f(e)}function pq(e){return e.localDisabled===!0||e.enabled===!1}function aO(e){return xg(e)&&e.enabled===!1&&e.localDisabled!==!0}function mq(e){return _f(e)&&pq(e)}function bg(e){return _f(e)&&e.orphan===!0}function lO(e){return!xg(e)||bg(e)}const hq=r("来自 Clash Verge 同步(名称、端口跟随 Clash),请到 Clash Verge 中修改;启用可在本页关闭"),gq=r("该节点来自订阅(名称、端口跟随订阅与内核),请在「订阅管理」里修改;启用可在本页关闭"),oO=r("该节点所属订阅已删除,不会再被同步更新,可手动删除清理");function iO(e){return bg(e)?oO:_f(e)?gq:hq}const xq=r("禁用后本端不再用它(引用它的账号回退直连);端口在 Clash Verge 侧,不受影响"),bq=r("禁用后本端不再用它(引用它的账号回退直连),并会关掉它的本地监听端口"),vq=r("已在本页禁用(打开开关即恢复)"),yq=r("这条在 Clash Verge 里是停用的,本页启用不生效 —— 到 Clash Verge 里打开它");function jq(e){if(bg(e))return oO;if(e.localDisabled===!0)return vq;if(aO(e))return yq;if(_f(e))return bq;if(rO(e))return xq}let xs={data:null,loading:!1,error:"",testing:null,pending:new Set,syncing:!1,batchBusy:!1,batchProgress:null,selected:new Set};const Fj=new Set;function cO(e){return Fj.add(e),()=>{Fj.delete(e)}}function uO(){return xs}function rs(e){xs={...xs,...e},Fj.forEach(t=>t())}function vg(){return xs.data?.items??[]}function wq(e){return xs.selected.has(e)}function Sq(e,t){const a=new Set(xs.selected);t?a.add(e):a.delete(e),rs({selected:a})}function Nq(e,t){const a=new Set(xs.selected);for(const o of e)t?a.add(o):a.delete(o);rs({selected:a})}function kq(){rs({selected:new Set})}function Eo(e){if(!e||typeof e!="object"||!Array.isArray(e.items))return;const t=new Set(e.items.map(o=>o.id)),a=new Set([...xs.selected].filter(o=>t.has(o)));rs({data:e,error:"",selected:a})}async function Lf(e=!1){const t=tr().workbuddyDesktop;if(typeof t?.getProxyPool!="function"){rs({error:r("当前环境不支持代理池(桥接方法缺失)")});return}e||rs({loading:!0});try{Eo(await t.getProxyPool())}catch(a){rs({error:Ra(a)}),e||qn(r("代理列表加载失败:{reason}",{reason:Ra(a)}),"err")}finally{rs({loading:!1})}}async function Cq(){const e=tr().workbuddyDesktop;if(typeof e?.syncClashToProxyPool!="function"){qn(r("当前环境不支持同步(桥接方法缺失)"),"err");return}if(!xs.syncing){rs({syncing:!0});try{const t=await e.syncClashToProxyPool();Eo(t),t?.syncError?qn(r("没能同步:{reason}",{reason:t.syncError}),"err"):t?.changes?qn(r("✅ 已同步 Clash Verge 出口({n} 项变更)",{n:t.changes})):qn(r("已是最新,没有需要同步的变更"))}catch(t){qn(r("同步失败:{reason}",{reason:Ra(t)}),"err")}finally{rs({syncing:!1})}}}async function Tq(e){const t=tr().workbuddyDesktop;if(typeof t?.testProxyPoolItem!="function"){qn(r("当前环境不支持代理测试(桥接方法缺失)"),"err");return}if(!xs.testing){rs({testing:e.id});try{const a=await t.testProxyPoolItem(e.id);if(a?.items&&Eo({items:a.items}),a?.success){const o=Uj(a.durationMs);qn(r("✅ 「{name}」出口可用{ip}{took}",{name:wu(e),ip:a.ip?r(" 出口 IP {ip}",{ip:a.ip}):"",took:o?` ${o}`:""}))}else qn(r("❌ 「{name}」{error}",{name:wu(e),error:a?.error||r("连接失败")}),"err");a?.saveError&&qn(r("测试结果没能记下:{reason}",{reason:a.saveError}),"err")}catch(a){qn(r("测试失败:{reason}",{reason:Ra(a)}),"err")}finally{rs({testing:null})}}}async function Eq(e,t){const a=tr().workbuddyDesktop;if(typeof a?.setProxyPoolItemEnabled!="function"){qn(r("当前环境不支持启停(桥接方法缺失)"),"err");return}const o=new Set(xs.pending);o.add(e.id),rs({pending:o});try{const c=await a.setProxyPoolItemEnabled({id:e.id,enabled:t});Eo(c);const d=wu(e),f=c?.listener??"none";qn(t?f==="opening"?r("已启用「{name}」,正在补建监听端口(内核重建中)",{name:d}):r("已启用「{name}」",{name:d}):f==="closing"?r("已禁用「{name}」,监听端口正在关闭(内核重建中)",{name:d}):r("已禁用「{name}」",{name:d})),(f==="closing"||f==="opening")&&Aq(e.id,f)}catch(c){qn(r("保存失败:{reason}",{reason:Ra(c)}),"err")}finally{const c=new Set(xs.pending);c.delete(e.id),rs({pending:c})}}async function Aq(e,t){for(let a=0;a<12;a+=1){await new Promise(d=>setTimeout(d,1500)),await Lf(!0);const o=vg().find(d=>d.id===e);if(!o)return;const c=o.port!==null&&o.port!==void 0;if(t==="closing"?!c:c)return}}async function dO(e){const t=tr().workbuddyDesktop;if(typeof t?.removeProxyPoolItem!="function")return;if(!lO(e)){qn(iO(e),"err");return}const a=e.usedBy||[],o=a.length?`

    ${r("有 {n} 个账号正在使用它({names})—— 删除后这些账号会回退直连。",{n:a.length,names:a.map(f=>Vj(String(f.name||f.id||""))).join(r("、"))})}

    `:"";if(!await tr().wbConfirm?.ask?.({title:r("删除代理"),html:r("确定删除「{name}」?{used}",{name:Vj(wu(e)),used:o}),okText:r("删除"),okClass:"danger"}))return;const d=new Set(xs.pending);d.add(e.id),rs({pending:d});try{const f=await t.removeProxyPoolItem(e.id);Eo(f),qn(r("已删除「{name}」",{name:wu(e)}))}catch(f){qn(r("删除失败:{reason}",{reason:Ra(f)}),"err")}finally{const f=new Set(xs.pending);f.delete(e.id),rs({pending:f})}}async function Rq(){const e=tr().workbuddyDesktop;if(typeof e?.testProxyPoolItem!="function"){qn(r("当前环境不支持代理测试(桥接方法缺失)"),"err");return}if(xs.batchBusy||xs.testing)return;const t=vg().filter(f=>xs.selected.has(f.id));if(!t.length)return;rs({batchBusy:!0,batchProgress:{done:0,total:t.length}});let a=0,o=0,c="";for(const[f,p]of t.entries()){rs({testing:p.id,batchProgress:{done:f,total:t.length}});try{const h=await e.testProxyPoolItem(p.id);h?.items&&Eo({items:h.items}),h?.success?a+=1:o+=1,h?.saveError&&!c&&(c=h.saveError)}catch{o+=1}}rs({testing:null,batchBusy:!1,batchProgress:null});const d=o?r("批量测试完成:{ok} 个可用、{failed} 个失败(共 {total} 个)",{ok:a,failed:o,total:t.length}):r("批量测试完成:{ok} 个可用(共 {total} 个)",{ok:a,total:t.length});qn(o?d:`✅ ${d}`,o?"err":"ok"),c&&qn(r("部分测试结果没能记下:{reason}",{reason:c}),"err")}async function Oq(){const e=tr().workbuddyDesktop;if(typeof e?.removeProxyPoolItem!="function"||xs.batchBusy||xs.testing)return;const t=vg().filter(S=>xs.selected.has(S.id)),a=t.filter(S=>lO(S)),o=t.length-a.length;if(!a.length){qn(o?r("选中的 {n} 条都是同步来的只读条目(Clash / 订阅),不能删除",{n:o}):r("没有可删除的条目"),"err");return}const c=a.slice(0,8).map(S=>Vj(wu(S))).join(r("、")),d=a.length>8?r("{names} 等 {n} 条",{names:c,n:a.length}):c,f=a.filter(S=>(S.usedBy||[]).length).length,p=f?`

    ${r("其中 {n} 条正被账号使用 —— 删除后那些账号会回退直连。",{n:f})}

    `:"",h=o?`

    ${r("另有 {n} 条是同步来的只读条目(Clash / 订阅),将跳过。",{n:o})}

    `:"";if(!await tr().wbConfirm?.ask?.({title:r("批量删除代理"),html:`${r("确定删除选中的 {n} 个代理?",{n:a.length})}

    ${d}

    ${p}${h}`,okText:r("删除"),okClass:"danger"}))return;rs({batchBusy:!0,batchProgress:{done:0,total:a.length}});let y=0,v=0,j="";for(const[S,N]of a.entries()){rs({batchProgress:{done:S,total:a.length}});try{const k=await e.removeProxyPoolItem(N.id);Eo(k),y+=1}catch(k){v+=1,j||(j=Ra(k))}}rs({batchBusy:!1,batchProgress:null});const w=[r("已删除 {n} 个代理",{n:y})];o&&w.push(r("跳过 {n} 个(同步只读条目)",{n:o})),v&&w.push(r("失败 {n} 个({reason})",{n:v,reason:j})),qn(v?w.join(r(",")):`✅ ${w.join(r(","))}`,v?"err":"ok")}async function Mq(e,t){const a=tr().workbuddyDesktop,o=e?{...t,id:e.id}:t,c=e?await a?.updateProxyPoolItem?.(o):await a?.createProxyPoolItem?.(o);return Eo(c),c}async function fO(){const e=tr().workbuddyDesktop;if(typeof e?.getProxySettings!="function")throw new Error(r("当前环境不支持代理设置(桥接方法缺失)"));const t=await e.getProxySettings();if(!t||typeof t!="object")throw new Error(r("代理设置响应异常"));return t}async function Dq(e){const t=tr().workbuddyDesktop;if(typeof t?.saveProxySettings!="function")throw new Error(r("当前环境不支持代理设置(桥接方法缺失)"));const a=await t.saveProxySettings(e);if(!a||typeof a!="object")throw new Error(r("代理设置响应异常"));return a}function nr(){return window}function an(e,t){nr().wbApp?.toast?.(e,t)}function wr(e){return e instanceof Error?e.message:String(e)}let Ft={data:null,loading:!1,error:"",pending:!1,refreshing:null};const $j=new Set;function yg(e){return $j.add(e),()=>{$j.delete(e)}}function jg(){return Ft}function wn(e){Ft={...Ft,...e},$j.forEach(t=>t())}function Iq(){return Ft.data?.items??[]}function Su(){return Ft.data?.kernel??null}function Ki(e){!e||typeof e!="object"||!Array.isArray(e.items)||wn({data:e,error:""})}function Ao(){Lf(!0)}async function Ml(e=!1){const t=nr().workbuddyDesktop;if(typeof t?.getSubscriptions!="function"){wn({error:r("当前环境不支持订阅(桥接方法缺失)")});return}e||wn({loading:!0});try{Ki(await t.getSubscriptions())}catch(a){wn({error:wr(a)})}finally{e||wn({loading:!1})}}const Pq=20;function _q(e){const t=e.kernel;if(!t||t.download.state==="downloading")return!1;if(!t.installed||t.lastError&&!t.running)return!0;const a=t.providerSync??{};return(e.items??[]).filter(o=>o.enabled!==!1).every(o=>!!a[o.id])}async function Yi(){const e=nr().workbuddyDesktop;if(typeof e?.getSubscriptions=="function")for(let t=0;tsetTimeout(o,1500)),Ao();let a;try{a=await e.getSubscriptions()}catch{return}if(Ki(a),t>=1&&a&&Array.isArray(a.items)&&_q(a))return}}async function pO(e){const a=await nr().workbuddyDesktop?.addSubscription?.({enabled:!0,kind:"url",url:"",...e});return Ki(a),an(r("已新增订阅「{name}」,内核正在同步节点…",{name:e.name.trim()})),Ao(),Yi(),a??null}async function mO(e,t){const o=await nr().workbuddyDesktop?.saveSubscription?.({id:e.id,...t});Ki(o),an(r("已保存「{name}」",{name:t.name.trim()})),Ao(),Yi()}async function Lq(e,t){const a=nr().workbuddyDesktop;if(!Ft.pending){wn({pending:!0});try{const o=await a?.saveSubscription?.({id:e.id,name:e.name,url:e.url,kind:e.kind,enabled:t});Ki(o),an(t?r("已启用「{name}」",{name:e.name}):r("已禁用「{name}」",{name:e.name})),Ao(),Yi()}catch(o){an(r("保存失败:{reason}",{reason:wr(o)}),"err")}finally{wn({pending:!1})}}}async function zq(e){const t=nr().workbuddyDesktop;if(!(Ft.refreshing||Ft.pending)){wn({refreshing:e.id});try{const a=await t?.refreshSubscription?.(e.id);Ki(a),an(r("已刷新「{name}」,正在同步节点…",{name:e.name})),Ao(),Yi()}catch(a){an(r("刷新失败:{reason}",{reason:wr(a)}),"err")}finally{wn({refreshing:null})}}}async function Bq(e){const t=nr().workbuddyDesktop;if(await window.wbConfirm?.ask?.({title:r("删除订阅"),html:r("确定删除「{name}」?它在代理池里的节点会一并移除,引用这些节点的账号会回退直连。",{name:e.name||e.id}),okText:r("删除"),okClass:"danger"})&&!Ft.pending){wn({pending:!0});try{const o=await t?.removeSubscription?.(e.id);Ki(o),an(r("已删除「{name}」",{name:e.name||e.id})),Ao(),Yi()}catch(o){an(r("删除失败:{reason}",{reason:wr(o)}),"err")}finally{wn({pending:!1})}}}async function Hq(e){const t=nr().workbuddyDesktop;if(typeof t?.downloadKernel!="function"){an(r("当前环境不支持订阅(桥接方法缺失)"),"err");return}if(!Ft.pending){wn({pending:!0});try{const a=await t.downloadKernel(e);a&&Ft.data&&wn({data:{...Ft.data,kernel:a}}),a?.saved===!1&&an(r("下载线路 / 存放目录没能写进配置(本次下载已按新设置走,重启后会恢复原值)"),"err");for(let c=0;c<300;c+=1){await new Promise(f=>setTimeout(f,1e3));const d=await t.getKernelStatus?.();if(d&&Ft.data&&wn({data:{...Ft.data,kernel:d}}),!d||d.download.state!=="downloading")break}const o=Ft.data?.kernel;o?.download.state==="error"?an(r("内核下载失败:{reason}",{reason:o.download.error||r("未知错误")}),"err"):o?.download.state==="canceled"?an(r("已停止下载内核(未下载完的临时文件已删除)")):o?.download.state==="done"&&(an(r("✅ 内核下载完成,正在启动…")),Yi())}catch(a){an(r("内核下载失败:{reason}",{reason:wr(a)}),"err")}finally{wn({pending:!1})}}}async function Uq(){const e=nr().workbuddyDesktop;if(typeof e?.cancelKernelDownload!="function")return"none";try{const t=await e.cancelKernelDownload();if(t&&Ft.data&&wn({data:{...Ft.data,kernel:t}}),t?.canceled===!1)return"none";for(let o=0;o<20;o+=1){const c=await e.getKernelStatus?.();if(c&&Ft.data&&wn({data:{...Ft.data,kernel:c}}),c&&c.download.state!=="downloading")break;await new Promise(d=>setTimeout(d,250))}const a=Ft.data?.kernel?.download.state;return a==="canceled"?"canceled":a==="done"?"done":"stalled"}catch(t){return an(r("停止下载失败:{reason}",{reason:wr(t)}),"err"),"none"}}async function Vq(){const e=nr().workbuddyDesktop;if(!Ft.pending){wn({pending:!0});try{const t=await e?.restartKernel?.();t&&Ft.data&&wn({data:{...Ft.data,kernel:t}}),an(r("内核正在启动…")),Ao(),Yi()}catch(t){an(r("内核启动失败:{reason}",{reason:wr(t)}),"err")}finally{wn({pending:!1})}}}async function Fq(){const e=nr().workbuddyDesktop;if(typeof e?.stopKernel!="function"){an(r("当前环境不支持订阅(桥接方法缺失)"),"err");return}if(!Ft.pending){wn({pending:!0});try{const t=await e.stopKernel();t&&Ft.data&&wn({data:{...Ft.data,kernel:t}});for(let o=0;o<10;o+=1){const c=await e.getKernelStatus?.();if(c&&Ft.data&&wn({data:{...Ft.data,kernel:c}}),c&&c.running===!1)break;await new Promise(d=>setTimeout(d,500))}const a=Ft.data?.kernel;if(a?.running===!1)an(r("内核已关闭(订阅与端口注册表保留,「启动内核」后原位恢复)"));else{const o=a?.lastError||r("内核没有在超时内停下来");an(r("关闭内核失败:{reason}",{reason:o}),"err")}Ml(!0)}catch(t){an(r("关闭内核失败:{reason}",{reason:wr(t)}),"err"),Ml(!0)}finally{wn({pending:!1})}}}async function $q(){const e=nr().workbuddyDesktop;if(typeof e?.removeKernel!="function"){an(r("当前环境不支持订阅(桥接方法缺失)"),"err");return}const t=Su()?.version;if(!await window.wbConfirm?.ask?.({title:r("删除内核"),html:r("确定删除已下载的内核{version}?内核文件与端口映射会被移除,订阅节点会变成「未分配端口」,引用它们的账号会暂时回退直连;订阅本身保留,重新下载内核后会重新分配端口。",{version:t?`(${t})`:""}),okText:r("删除内核"),okClass:"danger"})||Ft.pending)return;wn({pending:!0});const o=Su()?.lastError||"";try{const c=await e.removeKernel();c&&Ft.data&&wn({data:{...Ft.data,kernel:c}});for(let f=0;f<20;f+=1){const p=await e.getKernelStatus?.();if(p&&Ft.data&&wn({data:{...Ft.data,kernel:p}}),p&&p.installed===!1||p?.lastError&&p.lastError!==o)break;await new Promise(h=>setTimeout(h,500))}const d=Ft.data?.kernel;if(d?.installed===!1)an(r("已删除内核(订阅与账号引用保留,重新下载后会重新分配端口)")),Ao();else{const f=d?.lastError&&d.lastError!==o?d.lastError:r("内核目录没能删掉(可能仍被进程占用),稍后重试一次");an(r("删除内核失败:{reason}",{reason:f}),"err")}Ml(!0)}catch(c){an(r("删除内核失败:{reason}",{reason:wr(c)}),"err"),Ml(!0)}finally{wn({pending:!1})}}async function qq(e){const t=nr().workbuddyDesktop;if(typeof t?.decodeQrImage!="function")throw new Error(r("当前环境不支持订阅(桥接方法缺失)"));const a=await new Promise((d,f)=>{const p=new FileReader;p.onload=()=>d(String(p.result||"")),p.onerror=()=>f(new Error(r("图片读取失败"))),p.readAsDataURL(e)}),c=(await t.decodeQrImage({imageBase64:a}))?.url;if(!c)throw new Error(r("二维码里没有订阅链接"));return c}function hO(e){return new Promise((t,a)=>{const o=new FileReader;o.onload=()=>t(String(o.result||"")),o.onerror=()=>a(new Error(r("文件读取失败"))),o.readAsDataURL(e)})}const Nu="max-w-full flex-wrap [&_[data-slot=segmented-item]]:h-7 [&_[data-slot=segmented-item]]:px-3 [&_[data-slot=segmented-item]]:text-[12.5px]",Gq=r("登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)");let qj=!1;async function ku(e,t){if(!qj){qj=!0,e(!0);try{await t()}finally{qj=!1,e(!1)}}}function Ro({html:e,text:t}){return e?s.jsx("p",{dangerouslySetInnerHTML:{__html:e}}):s.jsx("p",{children:t||""})}function Kq(e,t,a){let o;try{o=JSON.parse(t)}catch(c){return Ln(r("{name} 不是合法 JSON:{reason}",{name:e.label,reason:ko(c)}),"err"),!1}return!o||typeof o!="object"||Array.isArray(o)?(Ln(r("{name}要粘一个 JSON 对象(形如 {sample})",{name:e.label,sample:'{"codearts_provider_credential":{…}}'}),"err"),!1):(Object.assign(a,o),!0)}function Yq({config:e,region:t,visible:a}){const o=e.provider,[c,d]=x.useState(!1),[f,p]=x.useState(""),h=uq(e),b=e.fields.map(v=>gg(e,v));async function y(){const v={provider:e.provider};e.regionOptions?.length&&(v.mode=t);for(const j of e.fields){const w=dr(gg(e,j));if(!j.optional&&!w){Ln(r("请填写 {label}",{label:j.label}),"err");return}if(w){if(j.jsonExpand){if(!Kq(j,w,v))return;continue}v[j.key]=w}}await ku(d,async()=>{try{const j=await vu(v);Lj(b),p(""),await Co(Pf(j?.account),e.label)}catch(j){Ln(r("添加失败:{reason}",{reason:ko(j)}),"err")}})}return s.jsxs(dn,{hidden:!a,onClick:v=>{v.target.closest("[data-raccoon-hint]")&&(v.preventDefault(),p(Gq))},children:[s.jsx("h3",{children:cq(e)}),s.jsx(Ro,{...h}),e.fields.map(v=>{const j=gg(e,v),w=v.optional?"":r("(必填)");return s.jsxs("div",{className:`field-row${v.rows?" stack":""}`,children:[s.jsxs("label",{htmlFor:j,children:[v.label,w]}),v.rows?s.jsx(Mi,{id:j,rows:v.rows,placeholder:v.placeholder,..._r(j)}):s.jsx(Fe,{id:j,type:"text",maxLength:$8(v),placeholder:v.placeholder,..._r(j)})]},j)}),s.jsxs("div",{className:"field-row",children:[s.jsx(ne,{id:`${o}-add-button`,onClick:()=>{y()},disabled:c,children:c?r("添加中…"):dq(e)}),s.jsx("span",{className:"detail",id:`${o}-add-hint`,children:f})]})]})}function Xq({config:e,visible:t}){const a=e.provider,[o,c]=x.useState(!1);if(!tO(e)||!e.desktopNote)return null;async function d(){await ku(c,async()=>{try{const f=await vu({provider:e.provider,importDesktop:!0});await Co(f?.account?.name||"",e.label)}catch(f){Ln(r("导入失败:{reason}",{reason:ko(f)}),"err")}})}return s.jsxs(dn,{hidden:!t,children:[s.jsx("h3",{children:r("从本机导入桌面端登录态")}),s.jsx("p",{children:e.desktopNote}),s.jsx("div",{className:"field-row",children:s.jsx(ne,{id:`${a}-desktop-button`,title:e.desktopHint,onClick:()=>{d()},disabled:o,children:r(o?"导入中…":"从本机导入桌面端登录态")})})]})}function Qq({config:e,visible:t}){const a=e.guestLogin,[o,c]=x.useState(!1);if(!a)return null;const d=gg(e,{key:"name"});async function f(){await ku(c,async()=>{const h=document.getElementById(d)?.value.trim()||"";try{const b=await vu({provider:e.provider,guest:!0,...h?{name:h}:{}});await Co(b?.account?.name||"",e.label)}catch(b){Ln(r("添加失败:{reason}",{reason:ko(b)}),"err")}})}return s.jsxs(dn,{hidden:!t,children:[s.jsx("h3",{children:r("访客模式")}),a.noteHtml?s.jsx(Ro,{html:a.noteHtml}):null,s.jsxs("div",{className:"form-field",children:[s.jsx("label",{htmlFor:d,children:r("备注名")}),s.jsx("input",{id:d,type:"text",maxLength:zj,placeholder:a.namePlaceholder||r("可选"),..._r(d)})]}),s.jsx("div",{className:"field-row",children:s.jsx(ne,{id:`${e.provider}-guest-button`,onClick:()=>{f()},disabled:o,children:o?a.busyText||r("正在添加…"):a.button})})]})}function Zq(e){const t=e.indexOf(",");return t>=0?e.slice(t+1):e}function Wq({config:e,visible:t}){const[a,o]=x.useState(!1),[c,d]=x.useState(""),f=x.useRef(null),p=e.fileImport,h=`${e.provider}-file-name`;if(!p)return null;async function b(y){const v=e.fileImport;v&&await ku(o,async()=>{try{const j=dr(h),w={provider:e.provider,authFileName:y.name};if(v.binary)w.authFileB64=Zq(await hO(y));else{const N=await y.text();let k;try{k=JSON.parse(N)}catch{Ln(r("选择的文件不是合法 JSON"),"err");return}if(!k||typeof k!="object"||Array.isArray(k)){Ln(r("不是有效的凭证文件:内容要是一个 JSON 对象"),"err");return}w.authFileContent=N}j&&(w.name=j);const S=await vu(w);d(""),await Co(Pf(S?.account),e.label)}catch(j){Ln(r("导入失败:{reason}",{reason:ko(j)}),"err")}})}return s.jsxs(dn,{hidden:!t,children:[s.jsx("h3",{children:r("导入凭证文件")}),s.jsx(Ro,{text:p.note}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{className:"lb-inline",htmlFor:h,children:r("备注名")}),s.jsx(Fe,{id:h,type:"text",maxLength:zj,placeholder:r("可选,留空则用文件里的昵称"),..._r(h)})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("input",{ref:f,type:"file",accept:p.accept,hidden:!0,onChange:y=>{const v=y.target.files?.[0];y.target.value="",v&&(d(v.name),b(v))}}),s.jsx(ne,{onClick:()=>f.current?.click(),disabled:a,children:r(a?"导入中…":"选择凭证文件导入")}),s.jsx("span",{className:"detail",children:c})]})]})}function Jq({config:e,mode:t,onModeChange:a,region:o,visible:c}){const d=e.webLogin,f=e.provider,p=x.useRef(t),h=x.useRef(o),b=x.useRef(null);return p.current=t,h.current=o,x.useEffect(()=>{const y=gt().wbWebLogin;if(!y||!d)return;const v=d.modes;b.current=y.create({provider:f,buttonId:`${f}-web-button`,cancelId:`${f}-web-cancel`,hintId:`${f}-web-hint`,busyText:d.busyText,texts:()=>{const j=v?.find(w=>w.value===p.current);return{button:d.button,hint:j?.hint||d.hint||""}},start:()=>gt().workbuddyDesktop?.startLogin(d.edition||h.current||"cn",p.current||"embedded",f),onSuccess:async j=>{_j(),await gt().wbApp?.refresh?.();const w=j?.payload?.warning;w?Ln(r("账号已添加,但请留意:{warning}",{warning:w}),"err"):Ln(r("✅ {label}账号已添加",{label:e.label}))}})??null},[]),x.useEffect(()=>{b.current?.syncTexts()},[t]),d?s.jsxs(dn,{hidden:!c,children:[s.jsx("h3",{children:r("网页登录")}),s.jsx(Ro,{html:d.noteHtml}),d.modes?.length?s.jsxs("div",{className:"flex flex-col gap-2.5 border-l-2 border-border pl-[11px]",children:[s.jsx("span",{className:"text-[12.5px] text-subtle",children:r("打开方式")}),s.jsx(En,{"aria-label":r("{label} 网页登录的打开方式",{label:e.label}),options:d.modes,value:t,onValueChange:a})]}):null,s.jsxs("div",{className:"field-row",children:[s.jsx(ne,{id:`${f}-web-button`,onClick:()=>b.current?.start(),children:d.button}),s.jsx(ne,{id:`${f}-web-cancel`,variant:"outline",hidden:!0,onClick:()=>b.current?.cancel(),children:r("取消等待")}),s.jsx("span",{className:"detail",id:`${f}-web-hint`,children:d.hint||d.modes?.[0]?.hint||""})]})]}):null}function eG({config:e,visible:t}){const a=e.smsLogin,o=e.provider;return x.useEffect(()=>{a&>().wbSmsLogin?.create({provider:o,onSuccess:c=>Co(Pf(c?.account),e.label)})},[]),a?s.jsxs(dn,{hidden:!t,children:[s.jsx("h3",{children:r("手机验证码登录")}),s.jsx(Ro,{html:a.noteHtml,text:r("用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。")}),s.jsxs("div",{className:"sms-form",children:[s.jsxs("div",{className:"sms-field",children:[s.jsxs("label",{className:"lb",htmlFor:`${o}-sms-phone`,children:[r("手机号"),s.jsx("i",{className:"req",children:"*"})]}),s.jsxs(Di,{children:[s.jsx(Pi,{id:`${o}-sms-phone`,type:"text",maxLength:11,placeholder:r("11 位大陆手机号"),..._r(`${o}-sms-phone`)}),s.jsx(Ii,{align:"inline-end",children:s.jsx(O4,{id:`${o}-sms-send`,className:"text-primary-fg",children:r("获取验证码")})})]})]}),s.jsxs("div",{className:"sms-field",children:[s.jsxs("label",{className:"lb",htmlFor:`${o}-sms-code`,children:[r("验证码"),s.jsx("i",{className:"req",children:"*"})]}),s.jsxs("div",{className:"sms-pair",children:[s.jsx(Fe,{id:`${o}-sms-code`,className:"sms-code",type:"text",maxLength:6,placeholder:r("6 位数字")}),s.jsx("label",{className:"lb-inline",htmlFor:`${o}-sms-name`,children:r("备注名")}),s.jsx(Fe,{id:`${o}-sms-name`,className:"sms-name",type:"text",placeholder:r("可选,留空则用脱敏手机号"),..._r(`${o}-sms-name`)})]})]}),s.jsxs("div",{className:"sms-foot",children:[s.jsx(ne,{id:`${o}-sms-submit`,children:r("登录并添加")}),s.jsx("span",{className:"detail sms-hint",id:`${o}-sms-hint`})]})]})]}):null}function tG({config:e,mode:t,onModeChange:a,visible:o}){const c=e.oauthLogin,d=e.provider,f=x.useRef(t),p=x.useRef(null);f.current=t;const h=()=>c?.modes?.find(b=>b.value===f.current)?.hint||c?.hint||"";return x.useEffect(()=>{const b=gt().wbAutoclawOauth;!b||!c||(p.current=b.create({provider:d,mode:()=>f.current||"embedded",hint:h,cancelId:`${d}-oauth-cancel`,onSuccess:()=>Co("",e.label)})??null)},[]),x.useEffect(()=>{p.current?.syncTexts()},[t]),c?s.jsxs(dn,{hidden:!o,children:[s.jsx("h3",{children:c.title||r("网页登录")}),s.jsx(Ro,{html:c.noteHtml}),c.modes?.length?s.jsxs("div",{className:"flex flex-col gap-2.5 border-l-2 border-border pl-[11px]",children:[s.jsx("span",{className:"text-[12.5px] text-subtle",children:r("打开方式")}),s.jsx(En,{"aria-label":r("{label} 网页登录的打开方式",{label:e.label}),options:c.modes,value:t,onValueChange:a})]}):null,s.jsxs("div",{className:"field-row",children:[s.jsx(ne,{id:`${d}-oauth-zai`,children:r("使用 Zai 账号登录")}),s.jsx(ne,{id:`${d}-oauth-google`,variant:"outline",children:r("使用 Google 账号登录")}),s.jsx(ne,{id:`${d}-oauth-cancel`,variant:"outline",hidden:!0,children:r("取消")})]}),s.jsx("div",{className:"field-row",children:s.jsx("span",{className:"detail",id:`${d}-oauth-hint`,children:c.modes?.[0]?.hint||c.hint||""})})]}):null}function nG({config:e,active:t}){const a=e.regionOptions?.[0]?.value??"",[o,c]=x.useState(a),[d,f]=x.useState(()=>nO(e,a)[0]),[p,h]=x.useState(e.webLogin?.modes?.[0]?.value??""),[b,y]=x.useState(e.oauthLogin?.modes?.[0]?.value??""),v=nO(e,o),j=v.includes(d)?d:v[0],w=x.useMemo(()=>eO.filter(S=>v.includes(S.id)).map(S=>({value:S.id,label:S.label})),[v.join(",")]);return s.jsxs("div",{className:"add-provider-block",hidden:!t,children:[e.regionOptions?.length?s.jsxs(dn,{children:[s.jsx("h3",{children:r("地区")}),s.jsx(En,{"aria-label":r("{label} 账号地区",{label:e.label}),className:Nu,options:e.regionOptions,value:o,onValueChange:c})]}):null,s.jsxs(dn,{children:[s.jsx("h3",{children:r("添加方式")}),s.jsx(En,{"aria-label":r("{label} 账号的添加方式",{label:e.label}),className:Nu,options:w,value:j,onValueChange:S=>f(S)})]}),s.jsx(Qq,{config:e,visible:j==="guest"}),s.jsx(tG,{config:e,mode:b,onModeChange:y,visible:j==="oauth"}),s.jsx(eG,{config:e,visible:j==="sms"}),s.jsx(Jq,{config:e,mode:p,onModeChange:h,region:o,visible:j==="web"}),s.jsx(Yq,{config:e,region:o,visible:j==="manual"}),s.jsx(Wq,{config:e,visible:j==="file"}),s.jsx(Xq,{config:e,visible:j==="desktop"})]})}const sG=r("官方客户端凭证目录:Windows 是 C:\\Users\\<你的用户名>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth,macOS 是 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth"),wg="workbuddy-file-name";function Gj(e,t,a){const o=e[a],c=[];o&&typeof o=="object"&&!Array.isArray(o)&&c.push(o),c.push(e);for(const d of c)for(const f of t){const p=d[f];if(typeof p=="string"&&p.trim())return p.trim()}return""}function rG(e){const t=Gj(e,["domain"],"auth");return/workbuddy\.ai/i.test(t)?"intl":/workbuddy\.cn|codebuddy\.cn/i.test(t)?"cn":null}function aG({edition:e,visible:t}){const[a,o]=x.useState(!1),[c,d]=x.useState(""),f=x.useRef(null);async function p(h){await ku(o,async()=>{try{let b;try{b=JSON.parse(await h.text())}catch{Ln(r("选择的文件不是合法 JSON"),"err");return}if(!b||typeof b!="object"||Array.isArray(b)){Ln(r("不是有效的凭证文件:内容要是一个 JSON 对象"),"err");return}const y=b,v=Gj(y,["accessToken","token","access_token"],"auth"),j=Gj(y,["uid"],"account");if(!v||!j){Ln(r("不是有效的凭证文件:需要包含 accessToken 与 uid"),"err");return}const w=rG(y),S=w??e;w&&w!==e&&Ln(r("文件是{edition}凭证,已按该版本导入",{edition:r(w==="intl"?"国际版":"国内版")}),"warn");const N=dr(wg),k={...y,provider:Sg(S)};N&&(k.name=N);const A=await vu(k);d(""),await Co(Pf(A?.account),S==="intl"?"WorkBuddy AI":"WorkBuddy")}catch(b){Ln(r("导入失败:{reason}",{reason:ko(b)}),"err")}})}return s.jsxs(dn,{hidden:!t,children:[s.jsx("h3",{children:r("导入凭证文件")}),s.jsx(Ro,{text:r("选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。")}),s.jsx("p",{className:"detail",children:s.jsx("a",{href:"#",onClick:h=>{h.preventDefault(),d(sG)},children:r("凭证文件在哪?")})}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{className:"lb-inline",htmlFor:wg,children:r("备注名")}),s.jsx(Fe,{id:wg,type:"text",maxLength:100,placeholder:r("可选,留空则用文件里的昵称"),..._r(wg)})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("input",{ref:f,type:"file",accept:".info,.json,application/json",hidden:!0,onChange:h=>{const b=h.target.files?.[0];h.target.value="",b&&p(b)}}),s.jsx(ne,{onClick:()=>f.current?.click(),disabled:a,children:r(a?"导入中…":"选择凭证文件导入")}),s.jsx("span",{className:"detail",children:c})]})]})}function lG({edition:e,visible:t}){const[a,o]=x.useState(!1);async function c(){await ku(o,async()=>{try{const d=await vu({provider:Sg(e),importDesktop:!0});await Co(Pf(d?.account),e==="intl"?"WorkBuddy AI":"WorkBuddy")}catch(d){Ln(r("导入失败:{reason}",{reason:ko(d)}),"err")}})}return s.jsxs(dn,{hidden:!t,children:[s.jsx("h3",{children:r("从本机导入桌面端登录态")}),s.jsx(Ro,{text:r("读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。")}),s.jsx("div",{className:"field-row",children:s.jsx(ne,{onClick:()=>{c()},disabled:a,children:r(a?"导入中…":"从本机导入桌面端登录态")})})]})}function Sg(e){return e==="intl"?"workbuddy-intl":"workbuddy"}function oG({active:e}){const[t,a]=x.useState(qi.edition),[o,c]=x.useState(qi.loginMode),[d,f]=x.useState(qi.socialRestore),[p,h]=x.useState("web"),b=x.useRef(t),y=x.useRef(o),v=x.useRef(d),j=x.useRef(null);b.current=t,y.current=o,v.current=d,x.useEffect(()=>{const C=gt().wbWebLogin;C&&(j.current=C.create({get provider(){return Sg(b.current)},buttonId:"web-login-button",cancelId:"web-login-cancel",hintId:"web-login-hint",busyText:r("等待网页登录…"),texts:()=>{const E=y.current==="external";return{button:r(E?"在浏览器中打开登录页":"打开网页登录"),hint:r(E?"系统浏览器打开(复用已登录账号);完成后自动加入列表":"内嵌窗口打开;完成后自动加入列表,关窗即取消等待")}},start:()=>gt().workbuddyDesktop?.startLogin(b.current,y.current,Sg(b.current),v.current),onSuccess:async()=>{const E=b.current==="intl"?r("国际版"):r("国内版");_j(),await gt().wbApp?.refresh?.(),Ln(r("✅ 登录成功,{edition}账号已加入列表",{edition:E}))}})??null)},[]),x.useEffect(()=>{j.current?.syncTexts()},[t,o]);function w(C){const E=C==="intl"?"external":"embedded";a(C),qi.edition=C,c(E),qi.loginMode=E}function S(C){const E=C==="external"?"external":"embedded";c(E),qi.loginMode=E}const N=t==="intl",A=N&&o==="embedded",P=r(A?"国际版登录页默认只显示邮箱登录,勾选后恢复 Google / GitHub / X 入口":N?"只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页":"国内版登录页没有 Google / GitHub 入口(它用微信 / 手机号 / 邮箱登录)"),R=p==="desktop"&&Hj()==="web"?"web":p;return s.jsxs("div",{className:"add-provider-block",hidden:!e,children:[s.jsxs(dn,{children:[s.jsx("h3",{children:r("账号版本")}),s.jsx("p",{children:r("两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。")}),s.jsx(En,{"aria-label":r("账号版本"),className:Nu,options:[{value:"cn",label:r("国内版(WorkBuddy)")},{value:"intl",label:r("国际版(WorkBuddy AI)")}],value:t,onValueChange:C=>w(C)})]}),s.jsxs(dn,{children:[s.jsx("h3",{children:r("添加方式")}),s.jsx(En,{"aria-label":r("{label} 账号的添加方式",{label:"WorkBuddy"}),className:Nu,options:[{value:"web",label:r("网页登录")},{value:"file",label:r("导入凭证文件")},...Hj()!=="web"?[{value:"desktop",label:r("导入桌面端登录态")}]:[]],value:R,onValueChange:C=>h(C)})]}),s.jsxs(dn,{hidden:R!=="web",children:[s.jsx("h3",{children:r("网页登录")}),s.jsx("p",{children:r("完成登录后自动加入账号列表,昵称取自上游。")}),s.jsxs("div",{className:"field-row",children:[s.jsx("span",{className:"text-[12.5px] whitespace-nowrap text-subtle",children:r("打开方式")}),s.jsx(En,{"aria-label":r("网页登录的打开方式"),className:Nu,options:[{value:"embedded",label:r("内嵌窗口")},{value:"external",label:r("系统默认浏览器")}],value:o,onValueChange:S}),s.jsxs("label",{className:"flex items-center gap-2",title:P,children:[s.jsx(ns,{checked:d,disabled:!A,onCheckedChange:C=>{f(C),qi.socialRestore=C}}),s.jsx("span",{className:"text-[12.5px] text-subtle",children:r("恢复 Google / GitHub 入口")})]})]}),s.jsx("p",{children:r("系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。")}),s.jsxs("div",{className:"field-row",children:[s.jsx(ne,{id:"web-login-button",onClick:()=>j.current?.start(),children:r("打开网页登录")}),s.jsx(ne,{id:"web-login-cancel",variant:"outline",hidden:!0,onClick:()=>j.current?.cancel(),children:r("取消等待")}),s.jsx("span",{className:"detail",id:"web-login-hint",children:r("内嵌窗口打开;完成后自动加入列表,关窗即取消等待")})]})]}),s.jsx(aG,{edition:t,visible:R==="file"}),s.jsx(lG,{edition:t,visible:R==="desktop"})]})}const Kj="proxy",Yj="preset",Ng="custom",iG="import",zf="preset:",Xj="__new__";function cG(e){return e===Yj||e===Ng?e:Kj}const kg={workbuddy:"assets/providers/workbuddy.png","workbuddy-intl":"assets/providers/workbuddy.png",raccoon:"assets/providers/raccoon.png",catpaw:"assets/providers/catpaw.png",autoclaw:"assets/providers/autoclaw.png","autoclaw-intl":"assets/providers/autoclaw.png",qoder:"assets/providers/qoder.png","qoder-intl":"assets/providers/qoder.png","cline-free":"assets/providers/cline.png","cline-pass":"assets/providers/cline.png",accio:"assets/providers/accio.png","accio-cn":"assets/providers/accio.png",zcode:"assets/providers/zcode.png","zcode-intl":"assets/providers/zcode.png",codearts:"assets/providers/codearts.png",trae:"assets/providers/trae.png",loomy:"assets/providers/loomy.png",kuku:"assets/providers/kuku.png",monkeycode:"assets/providers/monkeycode.png","monkeycode-intl":"assets/providers/monkeycode.png",commandcode:"assets/providers/commandcode.png",antigravity:"assets/providers/antigravity.png",officeace:"assets/providers/officeace.png",orcaterm:"assets/providers/orcaterm.png",chatglm:"assets/providers/chatglm.png"},uG=new Set(["cline-pass"]);function dG(e){const t=gt().wbProviders;if(e===Yj)return(gt().wbPresetProviders?.list||[]).map(h=>{const b=KR(h);return{id:zf+h.key,label:h.name,count:b?Pj(b.id):0,preset:!0,builtId:b?.id||""}});if(e===Ng)return(t?.customList?.()||[]).filter(h=>!V8(h)).map(h=>({id:h.id,label:h.name||h.id,count:Pj(h.id),custom:!0}));const a=(t?.all?.()||[]).filter(h=>!uG.has(String(h?.id||""))),o=(h,b)=>h===Gi?Bj:r(gt().wbProviders?.labelOf?.(h)||b),d=(a.length?a.map(h=>{const b=String(h.id||"");return{id:b,label:o(b,String(h.label||h.id||"")),count:Number(h.count)||0}}):[{id:Gi,label:Bj,count:Pj(Gi)}]).filter(h=>h.id!=="workbuddy-intl"),f=d.findIndex(h=>h.id==="autoclaw-intl"),p=d.findIndex(h=>h.id==="autoclaw");return p>=0&&f>p&&d.splice(p+1,0,d.splice(f,1)[0]),d}function fG({item:e}){const t=e.id.startsWith(zf)?gt().wbPresetProviders?.iconOf?.(e.id.slice(zf.length))||"":kg[e.id];if(t)return s.jsx("span",{className:"add-provider-logo has-icon",children:s.jsx("img",{src:t,alt:"",loading:"lazy"})});const a=String(e.label||"?").trim().slice(0,1).toUpperCase()||"?";return s.jsx("span",{className:"add-provider-logo",children:a})}function pG(e){return e.preset?e.builtId?e.count?r("已建过 · {n} 个账号,点击继续添加",{n:e.count}):r("已建过,点击继续添加账号"):r("点开即预填,填 Key 接入"):e.custom?e.count?r("{n} 个账号,点击添加",{n:e.count}):r("点击添加账号"):e.count?r("{n} 个账号",{n:e.count}):r("还没有账号")}function mG({item:e,onPick:t}){return s.jsxs("button",{type:"button",className:"add-provider-card","data-provider":e.id,role:"option",onClick:()=>t(e.id),children:[s.jsx(fG,{item:e}),s.jsxs("span",{className:"add-provider-info",children:[s.jsx("span",{className:"add-provider-name",children:e.label}),s.jsx("span",{className:"add-provider-meta",children:pG(e)})]}),s.jsx("span",{className:"add-provider-go",children:"›"})]})}function hG({onPick:e}){return s.jsxs("button",{type:"button",className:"add-provider-card is-new","data-provider":Xj,role:"option",onClick:()=>e(Xj),children:[s.jsx("span",{className:"add-provider-logo is-new",children:"+"}),s.jsxs("span",{className:"add-provider-info",children:[s.jsx("span",{className:"add-provider-name",children:r("新建自定义提供商")}),s.jsx("span",{className:"add-provider-meta",children:r("接入一个兼容上游")})]}),s.jsx("span",{className:"add-provider-go",children:"›"})]})}function gG({accountType:e,onAccountTypeChange:t,search:a,onSearchChange:o,onPick:c,version:d,importing:f,importPanel:p}){const h=x.useMemo(()=>dG(e),[e,d]),b=a.trim().toLowerCase(),y=h.filter(j=>!b||j.label.toLowerCase().includes(b)),v=e===Ng&&!b;return s.jsxs(dn,{children:[s.jsx("h3",{children:r("选择提供商")}),s.jsxs("div",{className:"add-pick-row",style:f?{display:"none"}:void 0,children:[s.jsx(En,{"aria-label":r("账号类型"),className:Nu,options:[{value:Kj,label:"Agent"},{value:Yj,label:r("预置 API")},{value:Ng,label:r("自定义")}],value:e,onValueChange:j=>t(cG(j))}),s.jsx("span",{id:"add-search-wrap",className:"add-provider-search ml-auto max-w-[340px] min-w-0 flex-[1_1_200px]",children:s.jsxs(Di,{children:[s.jsx(Ii,{children:"⌕"}),s.jsx(Pi,{id:"add-provider-search",type:"search",placeholder:r("搜索提供商…"),autoComplete:"off","aria-label":r("搜索提供商"),value:a,onChange:j=>o(j.currentTarget.value)})]})})]}),p,s.jsxs("div",{className:"add-provider-grid",id:"add-provider-grid",role:"listbox","aria-label":r("选择要添加账号的提供商"),style:f?{display:"none"}:void 0,children:[v?s.jsx(hG,{onPick:c}):null,y.map(j=>s.jsx(mG,{item:j,onPick:c},j.id)),!y.length&&b?s.jsx("div",{className:"add-provider-empty",children:r("没有匹配「{keyword}」的提供商",{keyword:a.trim()})}):null]})]})}function Cg(e,t){return Pe().wbIcons?.icon?.(e,t)||""}function xG({account:e,rank:t}){const a=r("组内第 {rank} 位",{rank:t});return s.jsxs("div",{className:"order-cell",children:[s.jsx("span",{className:"order-grip",role:"button",tabIndex:-1,"aria-label":r("拖动调整组内顺序"),title:r("按住拖动,调整这个账号在本组内的顺序"),dangerouslySetInnerHTML:{__html:Cg("gripVertical",14)}}),s.jsx("span",{className:"order-rank",title:a,children:`#${t}`})]})}function bG({group:e,ids:t,collapsed:a}){const[o,c]=x.useState(!1),d="w-full justify-start px-2 font-normal";return s.jsxs(wf,{open:o,onOpenChange:c,children:[s.jsx(Kh,{render:s.jsx("button",{type:"button",className:"grp-iconbtn",title:r("本组操作")}),children:"⋯"}),s.jsx(Sf,{align:"end",sideOffset:4,className:"w-[196px] p-1.5",children:s.jsxs("div",{className:"flex flex-col gap-0.5",children:[s.jsx(ne,{variant:"ghost",size:"sm",className:d,title:r("启用这一组里的全部账号(含被筛选隐藏的)"),onClick:()=>{c(!1),ER(t,!0)},children:r("全部启用({n})",{n:t.length})}),s.jsx(ne,{variant:"ghost",size:"sm",className:d,title:r("禁用这一组里的全部账号(含被筛选隐藏的)"),onClick:()=>{c(!1),ER(t,!1)},children:r("全部禁用({n})",{n:t.length})}),s.jsx(ne,{variant:"ghost",size:"sm",className:d,title:r("打开批量操作弹窗,并预选这一组的账号(改代理等动作在弹窗里挑参数)"),onClick:()=>{c(!1),kj(t,"proxy")},children:r("批量操作本组")}),s.jsx("div",{className:"my-1 h-px bg-hairline"}),s.jsx(ne,{variant:"ghost",size:"sm",className:d,onClick:()=>{c(!1),iR(e.provider)},children:r(a?"展开这一组":"折叠这一组")})]})})]})}function vG({group:e,collapsed:t,colSpan:a}){const o=kg[e.provider];return s.jsx("tr",{className:"grp-band","data-provider":e.provider,children:s.jsx("td",{colSpan:a,children:s.jsxs("div",{className:"grp-band-inner",children:[s.jsx("span",{className:"queue-grip",role:"button",tabIndex:-1,"aria-label":r("拖动调整整组顺序"),title:r("按住拖动,调整这一家的组顺位(整组一起动)"),dangerouslySetInnerHTML:{__html:Cg("gripVertical",14)}}),s.jsx("span",{className:"grp-rank",title:r("组顺位:越小越先用(拖动分组带调整)"),children:r("第 {rank} 顺位",{rank:e.rank})}),s.jsx("span",{className:"grp-logo",children:o?s.jsx("img",{src:o,alt:"",loading:"lazy"}):s.jsx("span",{className:"grp-logo-txt",children:String(e.label||"?").trim().slice(0,1).toUpperCase()})}),s.jsx("span",{className:"grp-name",children:r(e.label)}),s.jsx("span",{className:"grp-stat",children:r("{total} 个账号 · 启用 {enabled} · 禁用 {disabled}{limited}",{total:e.members.length,enabled:e.enabled,disabled:e.disabled,limited:e.limited>0?r(" · 限流 {n}",{n:e.limited}):""})}),s.jsx("span",{className:"grp-spacer"}),s.jsx("button",{type:"button",className:"grp-iconbtn grp-fold","aria-expanded":!t,title:r(t?"展开这一组":"折叠这一组"),onClick:()=>iR(e.provider),dangerouslySetInnerHTML:{__html:Cg("chevronDown",14)}}),s.jsx(bG,{group:e,ids:e.accounts.map(c=>c.id),collapsed:t})]})})})}function yG({account:e}){const t=ur(e),a=j$(e);!t&&d$(e,Rl(e))&&a.push({text:r("Token 限额 · 已禁用"),kind:"bad",title:r("本周期 Token 消耗达到限制器的上限,账号已被自动禁用(窗口重置也不恢复,需手动启用)")});const o=hu(e)||e.id;return s.jsxs(s.Fragment,{children:[s.jsx(ns,{checked:t,"aria-label":r("{action}{who}",{action:r(t?"禁用":"启用"),who:o}),title:r(t?"已启用,点击禁用(不参与转发)":"已禁用,点击启用"),onCheckedChange:c=>{CR(e.id,c)}}),a.length?s.jsx("div",{className:"status-tags",children:a.map(c=>s.jsx(ze,{shape:"tag",variant:c.kind==="bad"?"destructive":"secondary",title:c.title,children:c.text},c.text))}):null]})}function jG({account:e,open:t}){const a=bj(e);if(!a.length)return Qh(e,Wa(e))?s.jsx(ze,{variant:"warning",shape:"tag",title:r("余额低于限制器的阈值,转发时会跳过该账号(余额回升自动恢复)"),children:r("余额不足")}):fj(e,Rl(e))?s.jsx(ze,{variant:"warning",shape:"tag",title:r("本周期 Token 消耗达到限制器的上限,转发时会跳过该账号(窗口重置自动恢复)"),children:r("Token 限额")}):s.jsxs(ze,{variant:"success",shape:"tag",title:r("当前没有任何模型处于限流中"),children:[s.jsx(my,{}),r("正常")]});const o=jj(a[0].resetAt);return s.jsxs(s.Fragment,{children:[s.jsx(ze,{variant:"warning",shape:"tag",render:s.jsx("button",{type:"button"}),className:t?"ring-2 ring-warning-soft":void 0,title:r("点击{action}各模型的限流明细",{action:r(t?"收起":"查看")}),onClick:()=>dR(e.id,"limits",!t),children:r("{n} 个模型 ▾",{n:a.length})}),s.jsx("span",{className:"lim-sub",title:r("最早恢复"),children:r("最早 {soonest} 恢复",{soonest:o===yj?r("待定"):o})})]})}function wG({account:e}){const t=cr(An(e)),a=MA(t.edition?e.expiresAt:e[t.expiry]);if(!a)return s.jsx("span",{className:"muted",title:r("记录里没有过期时间"),children:"—"});const o=a-Date.now();if(o<=0)return s.jsx(ze,{variant:"destructive",shape:"tag",title:r("凭证已过期,转发时会先刷新"),children:r("已过期")});const c=o<36e5?r("{n} 分钟后",{n:Math.max(1,Math.round(o/6e4))}):o<48*36e5?r("{n} 小时后",{n:(o/36e5).toFixed(1)}):r("{n} 天后",{n:Math.floor(o/24/36e5)}),d=Ls(a);return s.jsx("span",{title:d?r("{text}过期({full})",{text:c,full:d}):r("{text}过期",{text:c}),children:c})}function SG({account:e}){const t=xR(e.id);return t<=0?null:s.jsx("span",{className:"conn-count",title:r("{n} 个请求正在使用该账号(含还在下发内容的流式请求)",{n:t}),children:t})}function NG(e){return!e||typeof e!="object"||Array.isArray(e)?"":gO(e.subscription)}function gO(e){if(!e||typeof e!="object")return"";const t=e,a=[];t.planName&&a.push(r("套餐 {name}",{name:String(t.planName)})),t.status&&a.push(r("状态 {status}",{status:String(t.status)})),t.state&&String(t.state)!=="active"&&a.push(qA(t.state));const o=t.expireAt;if(o!=null&&o!==""){const c=Number(o),d=Number.isFinite(c)&&c>1e11?Ls(c):String(o);d&&a.push(r("到期 {time}",{time:d}))}return Number.isFinite(Number(t.remainQuota))&&a.push(r("余量 {value}",{value:ss(t.remainQuota)})),Number.isFinite(Number(t.totalQuota))&&a.push(r("总量 {value}",{value:ss(t.totalQuota)})),a.join(" ")}function kG(e){if(!e||typeof e!="object"||Array.isArray(e))return[];const t=e.plans;return Array.isArray(t)?t.filter(a=>a&&typeof a=="object"&&String(a.state||"")==="pending"):[]}function CG(e){const t=e.map(a=>{const o=String(a.name||a.planId||r("套餐")),c=Number(a.pendingUntil??a.startsAt);return Number.isFinite(c)&&c>0?r("{name}({time} 起生效)",{name:o,time:Ls(c*1e3)}):o});return r(`已领到、还没到生效时间的套餐(生效后余额列会按它显示): +`+u.stack}}var Kn=Object.prototype.hasOwnProperty,ms=e.unstable_scheduleCallback,Yn=e.unstable_cancelCallback,vs=e.unstable_shouldYield,it=e.unstable_requestPaint,$t=e.unstable_now,Vn=e.unstable_getCurrentPriorityLevel,rn=e.unstable_ImmediatePriority,nt=e.unstable_UserBlockingPriority,Dt=e.unstable_NormalPriority,qt=e.unstable_LowPriority,es=e.unstable_IdlePriority,Fn=e.log,$n=e.unstable_setDisableYieldValue,Dn=null,en=null;function cs(n){if(typeof Fn=="function"&&$n(n),en&&typeof en.setStrictMode=="function")try{en.setStrictMode(Dn,n)}catch{}}var Et=Math.clz32?Math.clz32:wt,xt=Math.log,jt=Math.LN2;function wt(n){return n>>>=0,n===0?32:31-(xt(n)/jt|0)|0}var Tr=256,ar=262144,Fr=4194304;function kn(n){var l=n&42;if(l!==0)return l;switch(n&-n){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 n&-n;case 262144:case 524288:case 1048576:case 2097152:return n&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return n&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return n}}function al(n,l,i){var u=n.pendingLanes;if(u===0)return 0;var m=0,g=n.suspendedLanes,T=n.pingedLanes;n=n.warmLanes;var V=u&134217727;return V!==0?(u=V&~g,u!==0?m=kn(u):(T&=V,T!==0?m=kn(T):i||(i=V&~n,i!==0&&(m=kn(i))))):(V=u&~g,V!==0?m=kn(V):T!==0?m=kn(T):i||(i=u&~n,i!==0&&(m=kn(i)))),m===0?0:l!==0&&l!==m&&(l&g)===0&&(g=m&-m,i=l&-l,g>=i||g===32&&(i&4194048)!==0)?l:m}function ln(n,l){return(n.pendingLanes&~(n.suspendedLanes&~n.pingedLanes)&l)===0}function hc(n,l){(l&8)!==0&&(l|=l&32);var i=n.entangledLanes;if(i!==0)for(n=n.entanglements,i&=l;0i;i++)l.push(n);return l}function Go(n,l){n.pendingLanes|=l,l!==268435456&&(n.suspendedLanes=0,n.pingedLanes=0,n.warmLanes=0)}function Ko(n,l,i,u,m,g){var T=n.pendingLanes;n.pendingLanes=i,n.suspendedLanes=0,n.pingedLanes=0,n.warmLanes=0,n.expiredLanes&=i,n.entangledLanes&=i,n.errorRecoveryDisabledLanes&=i,n.shellSuspendCounter=0;var V=n.entanglements,se=n.expirationTimes,xe=n.hiddenUpdates;for(i=T&~i;0"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),Qt=!1;if(lt)try{var In={};Object.defineProperty(In,"passive",{get:function(){Qt=!0}}),window.addEventListener("test",In,In),window.removeEventListener("test",In,In)}catch{Qt=!1}var gn=null,Pn=null,hs=null;function Er(){if(hs)return hs;var n,l=Pn,i=l.length,u,m="value"in gn?gn.value:gn.textContent,g=m.length;for(n=0;n=tm),c_=" ",u_=!1;function d_(n,l){switch(n){case"keyup":return lne.indexOf(l.keyCode)!==-1;case"keydown":return l.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function f_(n){return n=n.detail,typeof n=="object"&&"data"in n?n.data:null}var pd=!1;function ine(n,l){switch(n){case"compositionend":return f_(l);case"keypress":return l.which!==32?null:(u_=!0,c_);case"textInput":return n=l.data,n===c_&&u_?null:n;default:return null}}function cne(n,l){if(pd)return n==="compositionend"||!aN&&d_(n,l)?(n=Er(),hs=Pn=gn=null,pd=!1,n):null;switch(n){case"paste":return null;case"keypress":if(!(l.ctrlKey||l.altKey||l.metaKey)||l.ctrlKey&&l.altKey){if(l.char&&1"u")return null;try{return n.activeElement||n.body}catch{return n.body}}function y_(n){for(;n&&n.firstChild;)n=n.firstChild;return n}function j_(n,l){var i=y_(n);n=0;for(var u;i;){if(i.nodeType===3){if(u=n+i.textContent.length,n<=l&&u>=l)return{node:i,offset:l-n};n=u}e:{for(;i;){if(i.nextSibling){i=i.nextSibling;break e}i=i.parentNode}i=void 0}i=y_(i)}}function w_(n,l){return n&&l?n===l?!0:n&&n.nodeType===3?!1:l&&l.nodeType===3?w_(n,l.parentNode):"contains"in n?n.contains(l):n.compareDocumentPosition?!!(n.compareDocumentPosition(l)&16):!1:!1}function S_(n){n=n!=null&&n.ownerDocument!=null&&n.ownerDocument.defaultView!=null?n.ownerDocument.defaultView:window;for(var l=iN(n.document);l instanceof n.HTMLIFrameElement;){try{var i=typeof l.contentWindow.location.href=="string"}catch{i=!1}if(i)n=l.contentWindow;else break;l=iN(n.document)}return l}function cN(n){var l=n&&n.nodeName&&n.nodeName.toLowerCase();return l&&(l==="input"&&(n.type==="text"||n.type==="search"||n.type==="tel"||n.type==="url"||n.type==="password")||l==="textarea"||n.contentEditable==="true")}var xne=lt&&"documentMode"in document&&11>=document.documentMode,md=null,uN=null,am=null,dN=!1;function N_(n,l,i){var u=i.window===i?i.document:i.nodeType===9?i:i.ownerDocument;dN||md==null||md!==iN(u)||(u=md,"selectionStart"in u&&cN(u)?u={start:u.selectionStart,end:u.selectionEnd}:(u=(u.ownerDocument&&u.ownerDocument.defaultView||window).getSelection(),u={anchorNode:u.anchorNode,anchorOffset:u.anchorOffset,focusNode:u.focusNode,focusOffset:u.focusOffset}),am&&rm(am,u)||(am=u,u=mv(uN,"onSelect"),0>=T,m-=T,ol=1<<32-Et(l)+m|i<rt?(Ns=st,st=null):Ns=st.sibling;var Zt=fe(ge,st,ye[rt],Ee);if(Zt===null){st===null&&(st=Ns);break}n&&st&&Zt.alternate===null&&l(ge,st),ie=g(Zt,ie,rt),Ht===null?Ke=Zt:Ht.sibling=Zt,Ht=Zt,st=Ns}if(rt===ye.length)return i(ge,st),At&&Xl(ge,rt),Ke;if(st===null){for(;rtrt?(Ns=st,st=null):Ns=st.sibling;var wi=fe(ge,st,Zt.value,Ee);if(wi===null){st===null&&(st=Ns);break}n&&st&&wi.alternate===null&&l(ge,st),ie=g(wi,ie,rt),Ht===null?Ke=wi:Ht.sibling=wi,Ht=wi,st=Ns}if(Zt.done)return i(ge,st),At&&Xl(ge,rt),Ke;if(st===null){for(;!Zt.done;rt++,Zt=ye.next())Zt=Re(ge,Zt.value,Ee),Zt!==null&&(ie=g(Zt,ie,rt),Ht===null?Ke=Zt:Ht.sibling=Zt,Ht=Zt);return At&&Xl(ge,rt),Ke}for(st=u(st);!Zt.done;rt++,Zt=ye.next())Zt=je(st,ge,rt,Zt.value,Ee),Zt!==null&&(n&&(Ns=Zt.alternate,Ns!==null&&st.delete(Ns.key===null?rt:Ns.key)),ie=g(Zt,ie,rt),Ht===null?Ke=Zt:Ht.sibling=Zt,Ht=Zt);return n&&st.forEach(function(ire){return l(ge,ire)}),At&&Xl(ge,rt),Ke}function vt(ge,ie,ye,Ee){if(typeof ye=="object"&&ye!==null&&ye.type===H&&ye.key===null&&ye.props.ref===void 0&&(ye=ye.props.children),typeof ye=="object"&&ye!==null){switch(ye.$$typeof){case _:e:{for(var Ke=ye.key;ie!==null;){if(ie.key===Ke){if(Ke=ye.type,Ke===H){if(ie.tag===7){i(ge,ie.sibling),Ee=m(ie,ye.props.children),si(Ee,ye),Ee.return=ge,ge=Ee;break e}}else if(ie.elementType===Ke||typeof Ke=="object"&&Ke!==null&&Ke.$$typeof===G&&_c(Ke)===ie.type){i(ge,ie.sibling),Ee=m(ie,ye.props),si(Ee,ye),Ee.return=ge,ge=Ee;break e}i(ge,ie);break}else l(ge,ie);ie=ie.sibling}ye.type===H?(Ee=Ac(ye.props.children,ge.mode,Ee,ye.key),si(Ee,ye),Ee.return=ge,ge=Ee):(Ee=vb(ye.type,ye.key,ye.props,null,ge.mode,Ee),si(Ee,ye),Ee.return=ge,ge=Ee)}return T(ge);case z:e:{for(Ke=ye.key;ie!==null;){if(ie.key===Ke)if(ie.tag===4&&ie.stateNode.containerInfo===ye.containerInfo&&ie.stateNode.implementation===ye.implementation){i(ge,ie.sibling),Ee=m(ie,ye.children||[]),Ee.return=ge,ge=Ee;break e}else{i(ge,ie);break}else l(ge,ie);ie=ie.sibling}Ee=bN(ye,ge.mode,Ee),Ee.return=ge,ge=Ee}return T(ge);case G:return ye=_c(ye),vt(ge,ie,ye,Ee)}if(ue(ye))return He(ge,ie,ye,Ee);if(ae(ye)){if(Ke=ae(ye),typeof Ke!="function")throw Error(o(150));return ye=Ke.call(ye),We(ge,ie,ye,Ee)}if(typeof ye.then=="function")return vt(ge,ie,Ab(ye),Ee);if(ye.$$typeof===$)return vt(ge,ie,kb(ge,ye),Ee);Rb(ge,ye)}return typeof ye=="string"&&ye!==""||typeof ye=="number"||typeof ye=="bigint"?(ye=""+ye,ie!==null&&ie.tag===6?(i(ge,ie.sibling),Ee=m(ie,ye),Ee.return=ge,ge=Ee):(i(ge,ie),Ee=xN(ye,ge.mode,Ee),Ee.return=ge,ge=Ee),T(ge)):i(ge,ie)}return function(ge,ie,ye,Ee){try{dm=0;var Ke=vt(ge,ie,ye,Ee);return Sd=null,Ke}catch(st){if(st===wd||st===Tb)throw st;var Ht=Ar(29,st,null,ge.mode);return Ht.lanes=Ee,Ht.return=ge,Ht}}}var zc=Y_(!0),X_=Y_(!1),ri=!1;function EN(n){n.updateQueue={baseState:n.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function AN(n,l){n=n.updateQueue,l.updateQueue===n&&(l.updateQueue={baseState:n.baseState,firstBaseUpdate:n.firstBaseUpdate,lastBaseUpdate:n.lastBaseUpdate,shared:n.shared,callbacks:null})}function ai(n){return{lane:n,tag:0,payload:null,callback:null,next:null}}function li(n,l,i){var u=n.updateQueue;if(u===null)return null;if(u=u.shared,(on&2)!==0){var m=u.pending;return m===null?l.next=l:(l.next=m.next,m.next=l),u.pending=l,l=bb(n),M_(n,null,i),l}return xb(n,u,l,i),bb(n)}function fm(n,l,i){if(l=l.updateQueue,l!==null&&(l=l.shared,(i&4194048)!==0)){var u=l.lanes;u&=n.pendingLanes,i|=u,l.lanes=i,gc(n,i)}}function RN(n,l){var i=n.updateQueue,u=n.alternate;if(u!==null&&(u=u.updateQueue,i===u)){var m=null,g=null;if(i=i.firstBaseUpdate,i!==null){do{var T={lane:i.lane,tag:i.tag,payload:i.payload,callback:null,next:null};g===null?m=g=T:g=g.next=T,i=i.next}while(i!==null);g===null?m=g=l:g=g.next=l}else m=g=l;i={baseState:u.baseState,firstBaseUpdate:m,lastBaseUpdate:g,shared:u.shared,callbacks:u.callbacks},n.updateQueue=i;return}n=i.lastBaseUpdate,n===null?i.firstBaseUpdate=l:n.next=l,i.lastBaseUpdate=l}var ON=!1;function pm(){if(ON){var n=jd;if(n!==null)throw n}}function mm(n,l,i,u){ON=!1;var m=n.updateQueue;ri=!1;var g=m.firstBaseUpdate,T=m.lastBaseUpdate,V=m.shared.pending;if(V!==null){m.shared.pending=null;var se=V,xe=se.next;se.next=null,T===null?g=xe:T.next=xe,T=se;var we=n.alternate;we!==null&&(we=we.updateQueue,V=we.lastBaseUpdate,V!==T&&(V===null?we.firstBaseUpdate=xe:V.next=xe,we.lastBaseUpdate=se))}if(g!==null){var Re=m.baseState;T=0,we=xe=se=null,V=g;do{var fe=V.lane&-536870913,je=fe!==V.lane;if(je?(Bt&fe)===fe:(u&fe)===fe){fe!==0&&fe===Ic&&(ON=!0),we!==null&&(we=we.next={lane:0,tag:V.tag,payload:V.payload,callback:null,next:null});e:{var He=n,We=V;fe=l;var vt=i;switch(We.tag){case 1:if(He=We.payload,typeof He=="function"){Re=He.call(vt,Re,fe);break e}Re=He;break e;case 3:He.flags=He.flags&-65537|128;case 0:if(He=We.payload,fe=typeof He=="function"?He.call(vt,Re,fe):He,fe==null)break e;Re=I({},Re,fe);break e;case 2:ri=!0}}fe=V.callback,fe!==null&&(n.flags|=64,je&&(n.flags|=8192),je=m.callbacks,je===null?m.callbacks=[fe]:je.push(fe))}else je={lane:fe,tag:V.tag,payload:V.payload,callback:V.callback,next:null},we===null?(xe=we=je,se=Re):we=we.next=je,T|=fe;if(V=V.next,V===null){if(V=m.shared.pending,V===null)break;je=V,V=je.next,je.next=null,m.lastBaseUpdate=je,m.shared.pending=null}}while(!0);we===null&&(se=Re),m.baseState=se,m.firstBaseUpdate=xe,m.lastBaseUpdate=we,g===null&&(m.shared.lanes=0),pi|=T,n.lanes=T,n.memoizedState=Re}}function Q_(n,l){if(typeof n!="function")throw Error(o(191,n));n.call(l)}function Z_(n,l){var i=n.callbacks;if(i!==null)for(n.callbacks=null,n=0;ng?g:8;var T=ve.T,V={};V.types=T!==null?T.types:null,ve.T=V,ZN(n,!1,l,i);try{var se=m(),xe=ve.S;if(xe!==null&&xe(V,se),se!==null&&typeof se=="object"&&typeof se.then=="function"){var we=Ene(se,u);bm(n,l,we,Jr(n))}else bm(n,l,u,Jr(n))}catch(Re){bm(n,l,{then:function(){},status:"rejected",reason:Re},Jr())}finally{ee.p=g,T!==null&&V.types!==null&&(T.types=V.types),ve.T=T}}function Ine(){}function XN(n,l,i,u){if(n.tag!==5)throw Error(o(476));var m=AL(n).queue;EL(n,m,l,he,i===null?Ine:function(){return RL(n),i(u)})}function AL(n){var l=n.memoizedState;if(l!==null)return l;l={memoizedState:he,baseState:he,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Jl,lastRenderedState:he},next:null};var i={};return l.next={memoizedState:i,baseState:i,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Jl,lastRenderedState:i},next:null},n.memoizedState=l,n=n.alternate,n!==null&&(n.memoizedState=l),l}function RL(n){var l=AL(n);l.next===null&&(l=n.alternate.memoizedState),bm(n,l.next.queue,{},Jr())}function QN(){return Xs(qd)}function OL(){return gs().memoizedState}function ML(){return gs().memoizedState}function Pne(n){for(var l=n.return;l!==null;){switch(l.tag){case 24:case 3:var i=Jr();n=ai(i);var u=li(l,n,i);u!==null&&(Dr(u,l,i),fm(u,l,i)),l={cache:NN()},n.payload=l;return}l=l.return}}function _ne(n,l,i){var u=Jr();i={lane:u,revertLane:0,gesture:null,action:i,hasEagerState:!1,eagerState:null,next:null},Hb(n)?IL(l,i):(i=hN(n,l,i,u),i!==null&&(Dr(i,n,u),PL(i,l,u)))}function DL(n,l,i){var u=Jr();bm(n,l,i,u)}function bm(n,l,i,u){var m={lane:u,revertLane:0,gesture:null,action:i,hasEagerState:!1,eagerState:null,next:null};if(Hb(n))IL(l,m);else{var g=n.alternate;if(n.lanes===0&&(g===null||g.lanes===0)&&(g=l.lastRenderedReducer,g!==null))try{var T=l.lastRenderedState,V=g(T,i);if(m.hasEagerState=!0,m.eagerState=V,Yr(V,T))return xb(n,l,m,0),_n===null&&gb(),!1}catch{}if(i=hN(n,l,m,u),i!==null)return Dr(i,n,u),PL(i,l,u),!0}return!1}function ZN(n,l,i,u){if(u={lane:2,revertLane:Uk(),gesture:null,action:u,hasEagerState:!1,eagerState:null,next:null},Hb(n)){if(l)throw Error(o(479))}else l=hN(n,i,u,2),l!==null&&Dr(l,n,2)}function Hb(n){var l=n.alternate;return n===bt||l!==null&&l===bt}function IL(n,l){Nd=Db=!0;var i=n.pending;i===null?l.next=l:(l.next=i.next,i.next=l),n.pending=l}function PL(n,l,i){if((i&4194048)!==0){var u=l.lanes;u&=n.pendingLanes,i|=u,l.lanes=i,gc(n,i)}}var Ub={readContext:Xs,use:_b,useCallback:us,useContext:us,useEffect:us,useImperativeHandle:us,useLayoutEffect:us,useInsertionEffect:us,useMemo:us,useReducer:us,useRef:us,useState:us,useDebugValue:us,useDeferredValue:us,useTransition:us,useSyncExternalStore:us,useId:us,useHostTransitionStatus:us,useFormState:us,useActionState:us,useOptimistic:us,useMemoCache:us,useCacheRefresh:us,useEffectEvent:us},_L={readContext:Xs,use:_b,useCallback:function(n,l){return xr().memoizedState=[n,l===void 0?null:l],n},useContext:Xs,useEffect:vL,useImperativeHandle:function(n,l,i){i=i!=null?i.concat([n]):null,zb(4194308,4,SL.bind(null,l,n),i)},useLayoutEffect:function(n,l){return zb(4194308,4,n,l)},useInsertionEffect:function(n,l){zb(4,2,n,l)},useMemo:function(n,l){var i=xr();l=l===void 0?null:l;var u=n();if(Bc){cs(!0);try{n()}finally{cs(!1)}}return i.memoizedState=[u,l],u},useReducer:function(n,l,i){var u=xr();if(i!==void 0){var m=i(l);if(Bc){cs(!0);try{i(l)}finally{cs(!1)}}}else m=l;return u.memoizedState=u.baseState=m,n={pending:null,lanes:0,dispatch:null,lastRenderedReducer:n,lastRenderedState:m},u.queue=n,n=n.dispatch=_ne.bind(null,bt,n),[u.memoizedState,n]},useRef:function(n){var l=xr();return n={current:n},l.memoizedState=n},useState:function(n){n=$N(n);var l=n.queue,i=DL.bind(null,bt,l);return l.dispatch=i,[n.memoizedState,i]},useDebugValue:KN,useDeferredValue:function(n,l){var i=xr();return YN(i,n,l)},useTransition:function(){var n=$N(!1);return n=EL.bind(null,bt,n.queue,!0,!1),xr().memoizedState=n,[!1,n]},useSyncExternalStore:function(n,l,i){var u=bt,m=xr();if(At){if(i===void 0)throw Error(o(407));i=i()}else{if(i=l(),_n===null)throw Error(o(349));(Bt&127)!==0||sL(u,l,i)}m.memoizedState=i;var g={value:i,getSnapshot:l};return m.queue=g,vL(aL.bind(null,u,g,n),[n]),u.flags|=2048,Cd(9,{destroy:void 0},rL.bind(null,u,g,i,l),null),i},useId:function(){var n=xr(),l=_n.identifierPrefix;if(At){var i=il,u=ol;i=(u&~(1<<32-Et(u)-1)).toString(32)+i,l="_"+l+"R_"+i,i=Ib++,0<\/script>",g=g.removeChild(g.firstChild);break;case"select":g=typeof u.is=="string"?T.createElement("select",{is:u.is}):T.createElement("select"),u.multiple?g.multiple=!0:u.size&&(g.size=u.size);break;default:g=typeof u.is=="string"?T.createElement(m,{is:u.is}):T.createElement(m)}}g[Ms]=l,g[Ys]=u;e:for(T=l.child;T!==null;){if(T.tag===5||T.tag===6)g.appendChild(T.stateNode);else if(T.tag!==4&&T.tag!==27&&T.child!==null){T.child.return=T,T=T.child;continue}if(T===l)break e;for(;T.sibling===null;){if(T.return===null||T.return===l)break e;T=T.return}T.sibling.return=T.return,T=T.sibling}l.stateNode=g;e:switch(Js(g,m,u),m){case"button":case"input":case"select":case"textarea":u=!!u.autoFocus;break e;case"img":u=!0;break e;default:u=!1}u&&to(l)}}return Hn(l),l.subtreeFlags&=-33554433,uk(l,l.type,n===null?null:n.memoizedProps,l.pendingProps,i),null;case 6:if(n&&l.stateNode!=null)n.memoizedProps!==u&&to(l);else{if(typeof u!="string"&&l.stateNode===null)throw Error(o(166));if(n=Ae.current,yd(l)){if(n=l.stateNode,i=l.memoizedProps,u=null,m=Hs,m!==null)switch(m.tag){case 27:case 5:u=m.memoizedProps}n[Ms]=l,n=!!(n.nodeValue===i||u!==null&&u.suppressHydrationWarning===!0||a5(n.nodeValue,i)),n||ti(l,!0)}else n=Om(n).createTextNode(u),n[Ms]=l,l.stateNode=n}return Hn(l),null;case 31:if(i=l.memoizedState,n===null||n.memoizedState!==null){if(u=yd(l),i!==null){if(n===null){if(!u)throw Error(o(318));if(n=l.memoizedState,n=n!==null?n.dehydrated:null,!n)throw Error(o(557));n[Ms]=l}else Rc(),(l.flags&128)===0&&(l.memoizedState=null),l.flags|=4;Hn(l),n=!1}else i=jN(),n!==null&&n.memoizedState!==null&&(n.memoizedState.hydrationErrors=i),n=!0;if(!n)return l.flags&256?(Xr(l),l):(Xr(l),null);if((l.flags&128)!==0)throw Error(o(558))}return Hn(l),null;case 13:if(u=l.memoizedState,n===null||n.memoizedState!==null&&n.memoizedState.dehydrated!==null){if(m=yd(l),u!==null&&u.dehydrated!==null){if(n===null){if(!m)throw Error(o(318));if(m=l.memoizedState,m=m!==null?m.dehydrated:null,!m)throw Error(o(317));m[Ms]=l}else Rc(),(l.flags&128)===0&&(l.memoizedState=null),l.flags|=4;Hn(l),m=!1}else m=jN(),n!==null&&n.memoizedState!==null&&(n.memoizedState.hydrationErrors=m),m=!0;if(!m)return l.flags&256?(Xr(l),l):(Xr(l),null)}return Xr(l),(l.flags&128)!==0?(l.lanes=i,l):(i=u!==null,n=n!==null&&n.memoizedState!==null,i&&(u=l.child,m=null,u.alternate!==null&&u.alternate.memoizedState!==null&&u.alternate.memoizedState.cachePool!==null&&(m=u.alternate.memoizedState.cachePool.pool),g=null,u.memoizedState!==null&&u.memoizedState.cachePool!==null&&(g=u.memoizedState.cachePool.pool),g!==m&&(u.flags|=2048)),i!==n&&i&&(l.child.flags|=8192),Kb(l,l.updateQueue),Hn(l),null);case 4:return pe(),n===null&&qk(l.stateNode.containerInfo),l.flags|=67108864,Hn(l),null;case 10:return Zl(l.type),Hn(l),null;case 19:if(PN(l),u=l.memoizedState,u===null)return Hn(l),null;if(m=(l.flags&128)!==0,g=u.rendering,g===null)if(m)ym(u,!1);else{if(ds!==0||n!==null&&(n.flags&128)!==0)for(n=l.child;n!==null;){if(g=Mb(n),g!==null){for(l.flags|=128,ym(u,!1),n=g.updateQueue,l.updateQueue=n,Kb(l,n),l.subtreeFlags=0,n=i,i=l.child;i!==null;)D_(i,n),i=i.sibling;return hm(l,Zs.current&1|2),At&&Xl(l,u.treeForkCount),l.child}n=n.sibling}u.tail!==null&&$t()>av&&(l.flags|=128,m=!0,ym(u,!1),l.lanes=4194304)}else{if(!m)if(n=Mb(g),n!==null){if(l.flags|=128,m=!0,n=n.updateQueue,l.updateQueue=n,Kb(l,n),ym(u,!0),u.tail===null&&u.tailMode!=="collapsed"&&u.tailMode!=="visible"&&!g.alternate&&!At)return Hn(l),null}else 2*$t()-u.renderingStartTime>av&&i!==536870912&&(l.flags|=128,m=!0,ym(u,!1),l.lanes=4194304);u.isBackwards?(g.sibling=l.child,l.child=g):(n=u.last,n!==null?n.sibling=g:l.child=g,u.last=g)}if(u.tail!==null){n=u.tail;e:{for(i=n;i!==null;){if(i.alternate!==null){i=!1;break e}i=i.sibling}i=!0}return u.rendering=n,u.tail=n.sibling,u.renderingStartTime=$t(),n.sibling=null,g=Zs.current,g=m?g&1|2:g&1,u.tailMode==="visible"||u.tailMode==="collapsed"||!i||At?hm(l,g):(i=g,Se(Qs,l),Se(Zs,i),or===null&&(or=l)),At&&Xl(l,u.treeForkCount),n}return Hn(l),null;case 22:case 23:return Xr(l),DN(),u=l.memoizedState!==null,n!==null?n.memoizedState!==null!==u&&(l.flags|=8192):u&&(l.flags|=8192),u?(i&536870912)!==0&&(l.flags&128)===0&&(Hn(l),l.subtreeFlags&6&&(l.flags|=8192)):Hn(l),i=l.updateQueue,i!==null&&Kb(l,i.retryQueue),i=null,n!==null&&n.memoizedState!==null&&n.memoizedState.cachePool!==null&&(i=n.memoizedState.cachePool.pool),u=null,l.memoizedState!==null&&l.memoizedState.cachePool!==null&&(u=l.memoizedState.cachePool.pool),u!==i&&(l.flags|=2048),n!==null&&de(Pc),null;case 24:return i=null,n!==null&&(i=n.memoizedState.cache),l.memoizedState.cache!==i&&(l.flags|=2048),Zl(js),Hn(l),null;case 25:return null;case 30:return l.flags|=33554432,Hn(l),null}throw Error(o(156,l.tag))}function Fne(n,l){switch(vN(l),l.tag){case 1:return n=l.flags,n&65536?(l.flags=n&-65537|128,l):null;case 3:return Zl(js),pe(),n=l.flags,(n&65536)!==0&&(n&128)===0?(l.flags=n&-65537|128,l):null;case 26:case 27:case 5:return Ne(l),null;case 31:if(l.memoizedState!==null){if(Xr(l),l.alternate===null)throw Error(o(340));Rc()}return n=l.flags,n&65536?(l.flags=n&-65537|128,l):null;case 13:if(Xr(l),n=l.memoizedState,n!==null&&n.dehydrated!==null){if(l.alternate===null)throw Error(o(340));Rc()}return n=l.flags,n&65536?(l.flags=n&-65537|128,l):null;case 19:return PN(l),n=l.flags,n&65536?(l.flags=n&-65537|128,n=l.memoizedState,n!==null&&(n.rendering=null,n.tail=null),l.flags|=4,l):null;case 4:return pe(),null;case 10:return Zl(l.type),null;case 22:case 23:return Xr(l),DN(),n!==null&&de(Pc),n=l.flags,n&65536?(l.flags=n&-65537|128,l):null;case 24:return Zl(js),null;case 25:return null;default:return null}}function o3(n,l){switch(vN(l),l.tag){case 3:Zl(js),pe();break;case 26:case 27:case 5:Ne(l);break;case 4:pe();break;case 31:l.memoizedState!==null&&Xr(l);break;case 13:Xr(l);break;case 19:PN(l);break;case 10:Zl(l.type);break;case 22:case 23:Xr(l),DN(),n!==null&&de(Pc);break;case 24:Zl(js)}}function jm(n,l){try{var i=l.updateQueue,u=i!==null?i.lastEffect:null;if(u!==null){var m=u.next;i=m;do{if((i.tag&n)===n){u=void 0;var g=i.create,T=i.inst;u=g(),T.destroy=u}i=i.next}while(i!==m)}}catch(V){bn(l,l.return,V)}}function ui(n,l,i){try{var u=l.updateQueue,m=u!==null?u.lastEffect:null;if(m!==null){var g=m.next;u=g;do{if((u.tag&n)===n){var T=u.inst,V=T.destroy;if(V!==void 0){T.destroy=void 0,m=l;var se=i,xe=V;try{xe()}catch(we){bn(m,se,we)}}}u=u.next}while(u!==g)}}catch(we){bn(l,l.return,we)}}function i3(n){var l=n.updateQueue;if(l!==null){var i=n.stateNode;try{Z_(l,i)}catch(u){bn(n,n.return,u)}}}function c3(n,l,i){i.props=Hc(n.type,n.memoizedProps),i.state=n.memoizedState;try{i.componentWillUnmount()}catch(u){bn(n,l,u)}}function cl(n,l){try{var i=n.ref;if(i!==null){switch(n.tag){case 26:case 27:case 5:var u=n.stateNode;break;case 30:var m=n.stateNode,g=Gl(n.memoizedProps,m);(m.ref===null||m.ref.name!==g)&&(m.ref=g5(g)),u=m.ref;break;case 7:if(n.stateNode===null){var T=new ea(n);v(n.child,!1,Dse,T,void 0,void 0),n.stateNode=T}u=n.stateNode;break;default:u=n.stateNode}typeof i=="function"?n.refCleanup=i(u):i.current=u}}catch(V){bn(n,l,V)}}function Ws(n,l){var i=n.ref,u=n.refCleanup;if(i!==null)if(typeof u=="function")try{u()}catch(m){bn(n,l,m)}finally{n.refCleanup=null,n=n.alternate,n!=null&&(n.refCleanup=null)}else if(typeof i=="function")try{i(null)}catch(m){bn(n,l,m)}else i.current=null}function Yb(n,l){if((n.tag===5||n.tag===27||n.tag===6)&&n.alternate===null&&l!==null)for(var i=0;i title"))),Js(u,l,i),u[Ms]=n,ys(u),l=u;break e;case"link":if(g=D5("link","href",m).get(l+(i.href||""))){for(T=0;Tvt&&(T=vt,vt=We,We=T);var ge=j_(V,We),ie=j_(V,vt);if(ge&&ie&&(je.rangeCount!==1||je.anchorNode!==ge.node||je.anchorOffset!==ge.offset||je.focusNode!==ie.node||je.focusOffset!==ie.offset)){var ye=Re.createRange();ye.setStart(ge.node,ge.offset),je.removeAllRanges(),We>vt?(je.addRange(ye),je.extend(ie.node,ie.offset)):(ye.setEnd(ie.node,ie.offset),je.addRange(ye))}}}}for(Re=[],je=V;je=je.parentNode;)je.nodeType===1&&Re.push({element:je,left:je.scrollLeft,top:je.scrollTop});for(typeof V.focus=="function"&&V.focus(),V=0;Vi?32:i,ve.T=null,i=Ok,Ok=null;var g=Fa,T=hl;if(ts=0,$c=Fa=null,hl=0,(on&6)!==0)throw Error(o(331));var V=on;if(on|=4,M3(g.current),A3(g,g.current,T,i),on=V,Em(0,!1),en&&typeof en.onPostCommitFiberRoot=="function")try{en.onPostCommitFiberRoot(Dn,g)}catch{}return!0}finally{ee.p=m,ve.T=u,K3(n,l)}}function Y3(n,l,i){l=xa(i,l),l=ek(n.stateNode,l,2),n=li(n,l,2),n!==null&&(Go(n,2),gl(n))}function bn(n,l,i){if(n.tag===3)Y3(n,n,i);else for(;l!==null;){if(l.tag===3){Y3(l,n,i);break}else if(l.tag===1){var u=l.stateNode;if(typeof l.type.getDerivedStateFromError=="function"||typeof u.componentDidCatch=="function"&&(mi===null||!mi.has(u))){n=xa(i,n),i=$L(2),u=li(l,i,2),u!==null&&(qL(i,u,l,n),Go(u,2),gl(u));break}}l=l.return}}function zk(n,l,i){var u=n.pingCache;if(u===null){u=n.pingCache=new Kne;var m=new Set;u.set(l,m)}else m=u.get(l),m===void 0&&(m=new Set,u.set(l,m));m.has(i)||(Ek=!0,m.add(i),n=tse.bind(null,n,l,i),l.then(n,n))}function tse(n,l,i){var u=n.pingCache;u!==null&&u.delete(l),n.pingedLanes|=n.suspendedLanes&i,n.warmLanes&=~i,_n===n&&(Bt&i)===i&&((ds===4||ds===3&&(Bt&62914560)===Bt&&300>$t()-rv)&&(on&2)===0?Ld(n,0):sv|=i,Md===Bt&&(Md=0)),gl(n)}function X3(n,l){l===0&&(l=mr()),n=Ec(n,l),n!==null&&(Go(n,l),gl(n))}function nse(n){var l=n.memoizedState,i=0;l!==null&&(i=l.retryLane),X3(n,i)}function sse(n,l){var i=0;switch(n.tag){case 31:case 13:var u=n.stateNode,m=n.memoizedState;m!==null&&(i=m.retryLane);break;case 19:u=n.stateNode;break;case 22:u=n.stateNode._retryCache;break;default:throw Error(o(314))}u!==null&&u.delete(l),X3(n,i)}function rse(n,l){return ms(n,l)}var Bd=null,Hd=null,Bk=!1,fv=!1,Hk=!1,gi=0;function gl(n){n!==Hd&&n.next===null&&(Hd===null?Bd=Hd=n:Hd=Hd.next=n),fv=!0,Bk||(Bk=!0,lse())}function Em(n,l){if(!Hk&&fv){Hk=!0;do for(var i=!1,u=Bd;u!==null;){if(n!==0){var m=u.pendingLanes;if(m===0)var g=0;else{var T=u.suspendedLanes,V=u.pingedLanes;g=(1<<31-Et(42|n)+1)-1,g&=m&~(T&~V),g=g&201326741?g&201326741|1:g?g|2:0}g!==0&&(i=!0,J3(u,g))}else g=Bt,g=al(u,u===_n?g:0,u.cancelPendingCommit!==null||u.timeoutHandle!==-1),(g&3)===0||ln(u,g)||(i=!0,J3(u,g));u=u.next}while(i);Hk=!1}}function ase(){Q3()}function Q3(){fv=Bk=!1;var n=0;gi!==0&&xse()&&(n=gi);for(var l=$t(),i=null,u=Bd;u!==null;){var m=u.next,g=Z3(u,l);g===0?(u.next=null,i===null?Bd=m:i.next=m,m===null&&(Hd=i)):(i=u,(n!==0||(g&3)!==0)&&(fv=!0)),u=m}ts!==0&&ts!==5||Em(n),gi!==0&&(gi=0)}function Z3(n,l){for(var i=n.suspendedLanes,u=n.pingedLanes,m=n.expirationTimes,g=n.pendingLanes&-62914561;0V)break;var we=se.transferSize,Re=se.initiatorType;we&&l5(Re)&&(se=se.responseEnd,T+=we*(sexv){We.length=He;break}ye=new Promise(Sse.bind(ye)),We.push(ye)}}}if(0"u"?null:document;function A5(n,l,i){var u=Vd;if(u&&typeof l=="string"&&l){var m=gr(l);m='link[rel="'+n+'"][href="'+m+'"]',typeof i=="string"&&(m+='[crossorigin="'+i+'"]'),E5.has(m)||(E5.add(m),n={rel:n,crossOrigin:i,href:l},u.querySelector(m)===null&&(l=u.createElement("link"),Js(l,"link",n),ys(l),u.head.appendChild(l)))}}function Vse(n){so.D(n),A5("dns-prefetch",n,null)}function Fse(n,l){so.C(n,l),A5("preconnect",n,l)}function $se(n,l,i){so.L(n,l,i);var u=Vd;if(u&&n&&l){var m='link[rel="preload"][as="'+gr(l)+'"]';l==="image"&&i&&i.imageSrcSet?(m+='[imagesrcset="'+gr(i.imageSrcSet)+'"]',typeof i.imageSizes=="string"&&(m+='[imagesizes="'+gr(i.imageSizes)+'"]')):m+='[href="'+gr(n)+'"]';var g=m;switch(l){case"style":g=Fd(n);break;case"script":g=$d(n)}if(!(Sa.has(g)||(n=I({rel:"preload",href:l==="image"&&i&&i.imageSrcSet?void 0:n,as:l},i),Sa.set(g,n),u.querySelector(m)!==null||l==="style"&&u.querySelector(Dm(g))||l==="script"&&u.querySelector(Im(g))))){var T=u.createElement("link");Js(T,"link",n),l==="style"&&(T[Ds]=!0,T.onload=T.onerror=function(){$p(T)}),ys(T),u.head.appendChild(T)}}}function qse(n,l){so.m(n,l);var i=Vd;if(i&&n){var u=l&&typeof l.as=="string"?l.as:"script",m='link[rel="modulepreload"][as="'+gr(u)+'"][href="'+gr(n)+'"]',g=m;switch(u){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":g=$d(n)}if(!Sa.has(g)&&(n=I({rel:"modulepreload",href:n},l),Sa.set(g,n),i.querySelector(m)===null)){switch(u){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(i.querySelector(Im(g)))return}u=i.createElement("link"),Js(u,"link",n),ys(u),i.head.appendChild(u)}}}function Gse(n,l,i){so.S(n,l,i);var u=Vd;if(u&&n){var m=Qo(u).hoistableStyles,g=Fd(n);l=l||"default";var T=m.get(g);if(!T){var V={loading:0,preload:null};if(T=u.querySelector(Dm(g)))V.loading=5;else{n=I({rel:"stylesheet",href:n,"data-precedence":l},i),(i=Sa.get(g))&&oC(n,i);var se=T=u.createElement("link");ys(se),Js(se,"link",n),se._p=new Promise(function(xe,we){se.onload=xe,se.onerror=we}),se.addEventListener("load",function(){V.loading|=1}),se.addEventListener("error",function(){V.loading|=2}),V.loading|=4,hv(T,l,u)}T={type:"stylesheet",instance:T,count:1,state:V},m.set(g,T)}}}function Kse(n,l){so.X(n,l);var i=Vd;if(i&&n){var u=Qo(i).hoistableScripts,m=$d(n),g=u.get(m);g||(g=i.querySelector(Im(m)),g||(n=I({src:n,async:!0},l),(l=Sa.get(m))&&iC(n,l),g=i.createElement("script"),ys(g),Js(g,"link",n),i.head.appendChild(g)),g={type:"script",instance:g,count:1,state:null},u.set(m,g))}}function Yse(n,l){so.M(n,l);var i=Vd;if(i&&n){var u=Qo(i).hoistableScripts,m=$d(n),g=u.get(m);g||(g=i.querySelector(Im(m)),g||(n=I({src:n,async:!0,type:"module"},l),(l=Sa.get(m))&&iC(n,l),g=i.createElement("script"),ys(g),Js(g,"link",n),i.head.appendChild(g)),g={type:"script",instance:g,count:1,state:null},u.set(m,g))}}function R5(n,l,i,u){var m=(m=Ae.current)?Mm(m):null;if(!m)throw Error(o(446));switch(n){case"meta":case"title":return null;case"style":return typeof i.precedence=="string"&&typeof i.href=="string"?(i=Fd(i.href),l=Qo(m).hoistableStyles,u=l.get(i),u||(u={type:"style",instance:null,count:0,state:null},l.set(i,u)),u):{type:"void",instance:null,count:0,state:null};case"link":if(i.rel==="stylesheet"&&typeof i.href=="string"&&typeof i.precedence=="string"){n=Fd(i.href);var g=Qo(m).hoistableStyles,T=g.get(n);if(T||(m=m.ownerDocument||m,T={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},g.set(n,T),(g=m.querySelector(Dm(n)))?g._p||(T.instance=g,T.state.loading=5):(g=Sa.get(n),g||(g={rel:"preload",as:"style",href:i.href,crossOrigin:i.crossOrigin,integrity:i.integrity,media:i.media,hrefLang:i.hrefLang,referrerPolicy:i.referrerPolicy},Sa.set(n,g)),Xse(m,n,g,T.state))),l&&u===null)throw Error(o(528,""));return T}if(l&&u!==null)throw Error(o(529,""));return null;case"script":return l=i.async,i=i.src,typeof i=="string"&&l&&typeof l!="function"&&typeof l!="symbol"?(i=$d(i),l=Qo(m).hoistableScripts,u=l.get(i),u||(u={type:"script",instance:null,count:0,state:null},l.set(i,u)),u):{type:"void",instance:null,count:0,state:null};default:throw Error(o(444,n))}}function Fd(n){return'href="'+gr(n)+'"'}function Dm(n){return'link[rel="stylesheet"]['+n+"]"}function O5(n){return I({},n,{"data-precedence":n.precedence,precedence:null})}function Xse(n,l,i,u){if(l=n.querySelector('link[rel="preload"][as="style"]['+l+"]")){if(l[Ds]!==!0){u.loading=1;return}}else l=n.createElement("link"),l[Ds]=!0,l.onload=l.onerror=$p.bind(null,l),Js(l,"link",i),ys(l),n.head.appendChild(l);u.preload=l,l.addEventListener("load",function(){return u.loading|=1}),l.addEventListener("error",function(){return u.loading|=2})}function $d(n){return'[src="'+gr(n)+'"]'}function Im(n){return"script[async]"+n}function M5(n,l,i){if(l.count++,l.instance===null)switch(l.type){case"style":var u=n.querySelector('style[data-href~="'+gr(i.href)+'"]');if(u)return l.instance=u,ys(u),u;var m=I({},i,{"data-href":i.href,"data-precedence":i.precedence,href:null,precedence:null});return u=(n.ownerDocument||n).createElement("style"),ys(u),Js(u,"style",m),hv(u,i.precedence,n),l.instance=u;case"stylesheet":m=Fd(i.href);var g=n.querySelector(Dm(m));if(g)return l.state.loading|=4,l.instance=g,ys(g),g;u=O5(i),(m=Sa.get(m))&&oC(u,m),g=(n.ownerDocument||n).createElement("link"),ys(g);var T=g;return T._p=new Promise(function(V,se){T.onload=V,T.onerror=se}),Js(g,"link",u),l.state.loading|=4,hv(g,i.precedence,n),l.instance=g;case"script":return g=$d(i.src),(m=n.querySelector(Im(g)))?(l.instance=m,ys(m),m):(u=i,(m=Sa.get(g))&&(u=I({},i),iC(u,m)),n=n.ownerDocument||n,m=n.createElement("script"),ys(m),Js(m,"link",u),n.head.appendChild(m),l.instance=m);case"void":return null;default:throw Error(o(443,l.type))}else l.type==="stylesheet"&&(l.state.loading&4)===0&&(u=l.instance,l.state.loading|=4,hv(u,i.precedence,n));return l.instance}function hv(n,l,i){for(var u=i.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),m=u.length?u[u.length-1]:null,g=m,T=0;T title"):null)}function Qse(n,l,i){if(i===1||l.itemProp!=null)return!1;switch(n){case"meta":case"title":return!0;case"style":if(typeof l.precedence!="string"||typeof l.href!="string"||l.href==="")break;return!0;case"link":if(typeof l.rel!="string"||typeof l.href!="string"||l.href===""||l.onLoad||l.onError)break;return l.rel==="stylesheet"?(n=l.disabled,typeof l.precedence=="string"&&n==null):!0;case"script":if(l.async&&typeof l.async!="function"&&typeof l.async!="symbol"&&!l.onLoad&&!l.onError&&l.src&&typeof l.src=="string")return!0}return!1}function I5(n,l){return n==="img"&&l.src!=null&&l.src!==""&&l.onLoad==null&&l.loading!=="lazy"}function P5(n){return!(n.type==="stylesheet"&&(n.state.loading&3)===0)}function _5(n){return(n.width||100)*(n.height||100)*(typeof devicePixelRatio=="number"?devicePixelRatio:1)*.25}function L5(n,l){typeof l.decode=="function"&&(n.imgCount++,l.complete||(n.imgBytes+=_5(l),n.suspenseyImages.push(l)),n=Jse.bind(n),l.decode().then(n,n))}function Zse(n,l,i,u){if(i.type==="stylesheet"&&(typeof u.media!="string"||matchMedia(u.media).matches!==!1)&&(i.state.loading&4)===0){if(i.instance===null){var m=Fd(u.href),g=l.querySelector(Dm(m));if(g){l=g._p,l!==null&&typeof l=="object"&&typeof l.then=="function"&&(n.count++,n=Pm.bind(n),l.then(n,n)),i.state.loading|=4,i.instance=g,ys(g);return}g=l.ownerDocument||l,u=O5(u),(m=Sa.get(m))&&oC(u,m),g=g.createElement("link"),ys(g);var T=g;T._p=new Promise(function(V,se){T.onload=V,T.onerror=se}),Js(g,"link",u),i.instance=g}n.stylesheets===null&&(n.stylesheets=new Map),n.stylesheets.set(i,l),(l=i.state.preload)&&(i.state.loading&3)===0&&(n.count++,i=Pm.bind(n),l.addEventListener("load",i),l.addEventListener("error",i))}}var xv=0;function Wse(n,l){return n.stylesheets&&n.count===0&&vv(n,n.stylesheets),0xv?50:800)+l);return n.unsuspend=i,function(){n.unsuspend=null,clearTimeout(u),clearTimeout(m)}}:null}function z5(n){if(n.count===0&&(n.imgCount===0||!n.waitingForImages)){if(n.stylesheets)vv(n,n.stylesheets);else if(n.unsuspend){var l=n.unsuspend;n.unsuspend=null,l()}}}function Pm(){this.count--,z5(this)}function Jse(){this.imgCount--,z5(this)}var bv=null;function vv(n,l){n.stylesheets=null,n.unsuspend!==null&&(n.count++,bv=new Map,l.forEach(ere,n),bv=null,Pm.call(n))}function ere(n,l){if(!(l.state.loading&4)){var i=bv.get(n);if(i)var u=i.get(null);else{i=new Map,bv.set(n,i);for(var m=n.querySelectorAll("link[data-precedence],style[data-precedence]"),g=0;g"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(e)}catch(t){console.error(t)}}return e(),Ej.exports=P8(),Ej.exports}var wn=_8();const dg=[20,50,100,200],fg=[20,50,100,200,"all"],xu=50,VR="workbuddy-desktop-table-size:";function FR(e){return e==="all"?r("全部"):String(e)}function pg(e,t=fg,a=xu){t.includes(a)||(a=t.includes(xu)?xu:t[0]);let o=null;try{o=localStorage.getItem(VR+e)}catch{return a}if(o===null||o.trim()==="")return a;if(o==="all")return t.includes("all")?"all":a;const c=Number(o);return Number.isFinite(c)&&t.includes(c)?c:a}function mg(e,t){try{localStorage.setItem(VR+e,String(t))}catch{}}function hg(e,t,a={}){const o=a.sizes??fg,c=a.defaultSize??xu,[d,f]=x.useState(()=>pg(t,o,c)),[p,h]=x.useState(1),b=Math.max(1,d==="all"?1:Math.ceil(e/d)),y=Math.min(Math.max(1,p),b);x.useEffect(()=>{y!==p&&h(y)},[y,p]);const v=x.useCallback(k=>{f(k),mg(t,k),h(1)},[t]),j=x.useCallback(k=>{h(Math.min(Math.max(1,Math.round(k)||1),b))},[b]),w=d==="all"?0:(y-1)*d,S=d==="all"?e:Math.min(e,w+d),N=x.useCallback(k=>d==="all"?k.slice():k.slice(w,S),[d,w,S]);return{size:d,page:y,pageCount:b,rangeStart:e?w+1:0,rangeEnd:e?S:0,paged:d!=="all",setSize:v,goto:j,slice:N}}function $i({total:e,page:t,pageCount:a,size:o,sizes:c=fg,onSizeChange:d,onPageChange:f,range:p=null,disabled:h=!1,leading:b,className:y}){const[v,j]=x.useState(String(t));x.useEffect(()=>j(String(t)),[t]);const w=()=>{const k=v.trim();if(!k){j(String(t));return}const A=Number(k);if(!Number.isFinite(A)){j(String(t));return}const P=Math.min(Math.max(1,Math.round(A)),a);j(String(P)),P!==t&&f(P)},S=a<=1,N=e===0?r("共 0 条"):p?r("共 {total} 条 · 当前第 {start}–{end} 条",{total:e,start:p.start,end:p.end}):r("共 {total} 条(全部显示)",{total:e});return s.jsxs("div",{className:y?`panel-foot table-foot-bar ${y}`:"panel-foot table-foot-bar",children:[b,s.jsx("div",{className:"spacer"}),s.jsxs("div",{className:"table-foot-ctl",children:[s.jsx("span",{className:"table-foot-count",children:N}),s.jsxs("span",{className:"table-foot-size",children:[r("每页"),s.jsxs(ut,{value:String(o),onValueChange:k=>d(k==="all"?"all":Number(k)),children:[s.jsx(ft,{className:"h-[26px] w-[86px]",disabled:h,"aria-label":r("每页条数"),children:s.jsx(dt,{children:FR(o)})}),s.jsx(pt,{children:c.map(k=>s.jsx($e,{value:String(k),children:FR(k)},String(k)))})]}),r("条")]}),s.jsxs("span",{className:"table-foot-jump",children:[r("跳至"),s.jsx(Fe,{className:"h-[26px] w-[52px] px-1 text-center",value:v,disabled:h||S,"aria-label":r("跳转到指定页"),title:S?r("当前只有一页"):r("跳转到第 1–{pageCount} 页",{pageCount:a}),inputMode:"numeric",onChange:k=>j(k.target.value),onKeyDown:k=>{k.key==="Enter"?(k.preventDefault(),w()):k.key==="Escape"&&j(String(t))},onBlur:w}),r("页")]}),s.jsx(Y6,{page:t,pageCount:a,onPageChange:f,disabled:h})]})]})}const Oj=[{key:"pick",label:r("选择"),align:"center",legacyAlign:"left"},{key:"order",label:r("顺序"),title:r("组内先后:按住左边的把手上下拖动即可调整;整组的先后拖分组带的把手"),align:"center",legacyAlign:"left"},{key:"account",label:r("账号"),align:"center",legacyAlign:"left"},{key:"proxy",label:r("代理"),title:r("该账号出网走的代理(Clash 出口 / 自定义 / 直连);点击可修改"),align:"center"},{key:"connections",label:r("连接数"),title:r("此刻正在使用这个账号的请求数(含还在下发内容的流式请求);为 0 时不显示"),align:"center"},{key:"status",label:r("状态"),align:"center",legacyAlign:"left"},{key:"limits",label:r("限流"),title:r("该账号当前限流中的模型;点徽章看明细"),align:"center",legacyAlign:"left"},{key:"expiry",label:r("有效期"),align:"center",legacyAlign:"left"},{key:"usage",label:r("余额"),align:"center",legacyAlign:"left"},{key:"actions",label:r("操作"),align:"right"}],$R="agent2api-accounts-col-widths",If={pick:47,order:84,proxy:186,connections:56,status:80,limits:148,expiry:80,usage:148,actions:200},Mj=new Set(["account"]),L8=300,Dj=56,z8=new Set([...Object.keys(If),...Mj]),bu=(()=>{try{const e=JSON.parse(localStorage.getItem($R)||"{}"),t={};for(const[a,o]of Object.entries(e||{})){const c=Number(o);z8.has(a)&&Number.isFinite(c)&&c>=Dj&&(t[a]=Math.round(c))}return t}catch{return{}}})();function qR(){try{localStorage.setItem($R,JSON.stringify(bu))}catch{}}function B8(){return{...If,...bu}}function H8(e){let t=0;for(const a of e){const o=bu[a];o?t+=o:Mj.has(a)?t+=L8:If[a]?t+=If[a]:t+=Dj}return Math.round(t)}function GR(e,t){const a=e?.querySelector(`thead th:nth-child(${t+1})`),o=a?.getAttribute("data-col")||a?.className.match(/cell-([a-z]+)/)?.[1];if(!o)return null;const c=e?.querySelector(`colgroup col[data-col="${CSS.escape(o)}"]`)||e?.querySelectorAll("colgroup col")[t];return c?{key:o,col:c}:null}function U8(e,t,a){const o=Math.max(Dj,Math.round(a));e.style.width=o+"px",Mj.has(t)||o!==If[t]?bu[t]=o:delete bu[t]}let Ij=()=>{};function V8(e,t){if(Ij=t,!e)return;let a=null;e.addEventListener("pointerdown",o=>{if(o.button!==0)return;const c=o.target?.closest?.(".col-grip");if(!c)return;o.preventDefault();const d=c.closest("th"),f=d?.closest("table")||null,p=d?.parentElement?[...d.parentElement.children].indexOf(d):-1,h=GR(f,p);if(!h)return;const b=o.clientX,y=h.col.getBoundingClientRect().width;c.classList.add("active"),document.body.classList.add("col-resizing");try{c.setPointerCapture(o.pointerId)}catch{}a={...h,startX:b,startWidth:y,grip:c,pointerId:o.pointerId},window.addEventListener("pointermove",v),window.addEventListener("pointerup",j),window.addEventListener("pointercancel",j),window.addEventListener("blur",j);function v(w){if(a){if(w.buttons===0){j();return}U8(a.col,a.key,a.startWidth+w.clientX-a.startX)}}function j(){if(!a)return;const{grip:w,pointerId:S}=a;a=null,w.classList.remove("active"),document.body.classList.remove("col-resizing");try{w.releasePointerCapture(S)}catch{}window.removeEventListener("pointermove",v),window.removeEventListener("pointerup",j),window.removeEventListener("pointercancel",j),window.removeEventListener("blur",j),qR(),Ij()}}),e.addEventListener("dblclick",o=>{const c=o.target?.closest?.(".col-grip");if(!c)return;const d=c.closest("th"),f=d?.closest("table")||null,p=d?.parentElement?[...d.parentElement.children].indexOf(d):-1,h=GR(f,p);h&&(delete bu[h.key],qR(),Ij())})}function gt(){return window}function Ln(e,t){gt().wbApp?.toast?.(e,t)}function Co(e){return e instanceof Error&&e.message?e.message:String(e??"").trim()||r("未知错误")}function Pj(e){return(gt().wbApp?.getState?.()?.accounts?.accounts||[]).filter(a=>(a?.provider||"workbuddy")===e).length}function KR(e){const t=e?.name||"";if(t)return(gt().wbProviders?.customList?.()||[]).find(a=>(a.name||"")===t)}function F8(e){const t=e?.name||"";return t?(gt().wbPresetProviders?.list||[]).some(a=>a.name===t):!1}async function vu(e){const t=gt().__TAURI_INTERNALS__;if(!t||typeof t.invoke!="function")throw new Error(r("桌面运行时不可用(Tauri 未初始化)"));return t.invoke("api_request",{request:{method:"POST",path:"/api/accounts",body:e}})}function Pf(e){return e?.name||e?.uid||e?.userId||""}function _j(){gt().wbAddAccountModal?.close?.()}async function To(e,t){_j(),await gt().wbApp?.refresh?.(),Ln(r("✅ {label}账号已添加{name}",{label:t,name:e?`:${e}`:""}))}const yu=new Map;function _r(e){return{defaultValue:yu.get(e)??"",onChange:t=>{const a=t.currentTarget.value;a?yu.set(e,a):yu.delete(e)}}}function Eo(e,t){t?yu.set(e,t):yu.delete(e);const a=document.getElementById(e);(a instanceof HTMLInputElement||a instanceof HTMLTextAreaElement)&&(a.value=t)}function dr(e){const t=document.getElementById(e);return t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement?t.value.trim():(yu.get(e)??"").trim()}function Lj(e){for(const t of e)Eo(t,"")}const qi={edition:"cn",loginMode:"embedded",socialRestore:!1},zj=100,$8=256,q8=e=>e.key==="name"?zj:$8,G8={provider:"raccoon",label:"小浣熊",addButton:r("添加小浣熊账号"),webLogin:{noteHtml:r("在内嵌窗口里完成官方登录,成功后自动加入账号列表。"),button:r("打开网页登录"),hint:r("内嵌窗口打开;完成后自动加入列表,关窗即取消等待"),busyText:r("等待小浣熊登录完成…")},manualTitle:r("粘贴 token / refreshToken"),manualNoteHtml:r('refreshToken 可选,填了到期可自动续期;两者都能从小浣熊客户端登录态文件里取到。'),fields:[{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用凭证里的账号名")},{key:"token",label:"token",rows:3,placeholder:r("粘贴 access_token(一长串 JWT)")},{key:"refreshToken",inputKey:"refresh",label:"refreshToken",rows:2,optional:!0,placeholder:r("可选")}],desktopNote:r("读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后点「刷新 Token」同步。"),fileImport:{accept:".json,application/json",note:r("选择小浣熊客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json;macOS / Linux:~/.box-agent/config/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。")}},K8={provider:"catpaw",label:"CatPaw",webLogin:{noteHtml:r("用 CatPaw 账号完成登录(美团 passport),成功后自动加入账号列表。"),button:r("打开 CatPaw 网页登录"),busyText:r("等待 CatPaw 登录完成…"),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("内嵌窗口打开;完成后自动加入列表,关窗即取消等待")},{value:"external",label:r("系统浏览器"),hint:r("系统浏览器打开(复用已登录的美团账号);完成后自动加入列表")}]},manualNote:r("token 是登录态 Cookie;CatPaw 没有刷新机制,过期后需在客户端重新登录。"),fields:[{key:"token",label:"token",rows:3,placeholder:r("CatPaw 的 X-Passport-Token(登录态 Cookie)")},{key:"uid",label:"uid",placeholder:r("必填,CatPaw 账号标识")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用登录名或 uid")}],desktopNote:r("读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。客户端重新登录后重新导入即可。"),desktopHint:r("读取 ~/.meituan-catpaw/auth.json,需已在 CatPaw 客户端登录"),fileImport:{accept:".json,application/json",note:r("选择 CatPaw 客户端的登录态文件 auth.json(Windows:C:\\Users\\<你的用户名>\\.meituan-catpaw\\auth.json;macOS / Linux:~/.meituan-catpaw/auth.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端。")}},Y8={provider:"autoclaw",label:r("AutoClaw 国内版"),manualNote:r("token 支持 enc: 前缀(Windows 上自动解密)。未填 refreshToken 无法自动续期。"),smsLogin:{noteHtml:r("用绑定的手机号登录:点「获取验证码」后填入即可。这是国内版官方唯一的登录方式。")},fields:[{key:"token",label:"token",rows:3,placeholder:r("明文 JWT 或 auth.json 里的 enc: 加密值(自动解密)")},{key:"refreshToken",label:"refreshToken",rows:2,optional:!0,placeholder:r("没有则无法自动续期")},{key:"deviceId",label:"deviceId",optional:!0,placeholder:r("可选,续期时带上")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用 userId")}],desktopNote:r("读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。"),desktopHint:r("读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows"),desktopWindowsOnly:!0},X8={provider:"autoclaw-intl",label:r("AutoClaw 国际版"),manualNote:r("国际版与国内版账号体系独立,请填国际版的凭证。未填 refreshToken 无法自动续期。"),oauthLogin:{title:r("网页登录(Zai / Google)"),noteHtml:r("点按钮后先过一次滑块验证(官方风控),随后打开登录页,登录完成即自动添加账号。
    这是国际版官方唯一的登录方式;已在客户端登录过的,用「导入桌面端登录态」更快。"),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("内嵌窗口打开;关窗即取消等待(Google 账号被拒时改用系统浏览器)")},{value:"external",label:r("系统浏览器"),hint:r("系统浏览器打开(复用已登录的 Zai / Google 账号);完成后自动加入列表")}]},fields:[{key:"token",label:"token",rows:3,placeholder:r("明文 JWT(国际版账号的 access token)")},{key:"refreshToken",label:"refreshToken",rows:2,optional:!0,placeholder:r("没有则无法自动续期")},{key:"deviceId",label:"deviceId",optional:!0,placeholder:r("可选,续期时带上")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用 userId")}],desktopNote:r("读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。用 Zai / Google 登录客户端的用户走这一条。"),desktopHint:r("读取 %APPDATA%/AutoClaw/auth.json 并解密,仅 Windows"),desktopWindowsOnly:!0};function YR(e){const{provider:t,label:a,siteNote:o}=e;return{provider:t,label:a,desktop:!1,webLogin:{noteHtml:r("打开官方授权页完成设备码授权,登录的是{label}({site})的账号。",{label:a,site:o}),button:r("打开 Qoder 网页登录"),busyText:r("等待 Qoder 授权完成…"),edition:t==="qoder"?"cn":"global",modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("内嵌窗口用全新环境,多账号互不影响;关窗即取消等待")},{value:"external",label:r("系统浏览器"),hint:r("系统浏览器打开(复用已登录账号);完成后自动加入列表")}]},manualTitle:r("使用个人访问令牌(PAT)"),manualNote:r("在{label}的 Qoder 账号设置 → Integrations 生成 PAT(别填 Google / GitHub 的令牌)。两个地区的账号与凭证不通用。",{label:a}),fields:[{key:"pat",label:"Qoder PAT",rows:3,placeholder:r("粘贴 Qoder 个人访问令牌(pt-…)")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空使用账号昵称或邮箱")}]}}const Q8=YR({provider:"qoder",label:r("中国版"),siteNote:"qoder.com.cn"}),Z8=YR({provider:"qoder-intl",label:r("国际版"),siteNote:"qoder.com"});function XR(e){const{provider:t,label:a,poolNote:o}=e;return{provider:t,label:a,desktop:!0,webLogin:{noteHtml:r("打开授权页完成确认,账号自动加入 {label}{note}",{label:a,note:o}),button:r("打开 Cline 授权页"),busyText:r("等待 Cline 授权确认…"),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("内嵌窗口打开;完成后自动加入列表,关窗即取消等待")},{value:"external",label:r("系统浏览器"),hint:r("系统浏览器打开(复用已登录的 Cline 账号);完成后自动加入列表")}]},manualTitle:r("填写凭证"),manualNote:r("refreshToken 可选,填了可自动续期;两者都能从客户端登录态文件取到。同一个 Cline 账号两个池都能用,这里归入 {label}。",{label:a}),fields:[{key:"accessToken",label:"accessToken",rows:3,placeholder:r("粘贴 workos:… 开头的令牌(不带前缀也会自动补上)")},{key:"refreshToken",label:"refreshToken",rows:2,optional:!0,placeholder:r("可选,没有则无法自动续期")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空使用账号姓名、邮箱或令牌指纹")}],desktopNote:r("读本机客户端当前登录态,每次实时读取(删掉这条记录不影响客户端登录态)。这一条归入 {label}。",{label:a}),desktopHint:r("读取 ~/.cline/data/settings/providers.json,需已在 Cline 客户端登录"),fileImport:{accept:".json,application/json",note:r("选择 Cline 客户端的登录态文件 providers.json(Windows:C:\\Users\\<你的用户名>\\.cline\\data\\settings\\providers.json;macOS / Linux:~/.cline/data/settings/providers.json)。导入后凭证直接落在网关、可独立转发,不再依赖本机客户端;这一条归入 {label}。",{label:a})}}}function QR(e){const{provider:t,label:a,site:o,siteNote:c}=e;return{provider:t,label:a,webLogin:{noteHtml:r("打开 Accio {label}的官方登录页({site})并用你的 Accio 账号登录:登录成功后官方页面会跳回本机;同机自动完成,Docker / 远程面板按提示粘贴回调地址即可(授权码只在本机传给网关,界面不显示明文 token)。",{label:a,site:o}),button:r("打开 Accio {label}登录页",{label:a}),busyText:r("等待 Accio {label}登录完成…",{label:a}),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待")},{value:"external",label:r("系统浏览器"),hint:r("将用系统默认浏览器打开登录页(会复用浏览器里已登录的 Accio 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待")}]},manualTitle:r("填写凭证"),manualNoteHtml:r("accessToken 是 Accio 的登录凭证(一长串不透明 token,不是 JWT);refreshToken 可选,填了之后到期能自动续期。请填写 {label}账号的凭证 —— {siteNote}(最容易拿到的办法:直接用上方的「网页登录」,不需要手工找 token。)",{label:a,siteNote:c}),fields:[{key:"accessToken",label:"accessToken",rows:3,placeholder:r("粘贴 Accio 的 accessToken")},{key:"refreshToken",label:"refreshToken",rows:2,optional:!0,placeholder:r("可选,没有则无法自动续期")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空使用账号昵称或邮箱")}]}}function ZR(e){const{provider:t,label:a,site:o,planNote:c}=e;return{provider:t,label:a,desktop:!1,webLogin:{noteHtml:r("打开 ZCode {label}的官方授权页并用你的账号登录。授权结果由 ZCode 服务端记录,网关在后台自动取回凭证并加入账号列表。",{label:a}),button:r("打开 ZCode {label}授权页",{label:a}),busyText:r("等待 ZCode {label}授权完成…",{label:a}),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("将打开内嵌窗口;授权完成后自动加入账号列表。关掉窗口即取消等待。授权页最后可能提示无法打开 zcode:// 链接,这是正常的 —— 结果已由服务端记下")},{value:"external",label:r("系统浏览器"),hint:r("将用系统默认浏览器打开授权页(会复用浏览器里已登录的 ZCode 账号);授权完成后自动加入账号列表。页面最后可能提示无法打开 zcode:// 链接,属正常现象")}]},manualTitle:r("填写凭证"),manualNoteHtml:r("本家有两个**互不替代**的凭证,按你要用的功能填,至少填一个:编码套餐 API Key 用于转发推理(打 {site})与查询窗口限额(每 N 小时 / 每周的剩余比例);jwt 用于领取套餐与查询余额(打 zcode.z.ai 的 billing 网关,官方叫 Coding Plan JWT,是一串三段点分的字符串)。余额两把都能查、但读到的东西不同:jwt 读到余额桶与套餐到期,API Key 读到窗口限额;而领取只认 jwt、转发只认 API Key。请填写 {label}账号的凭证 —— {plan}(最容易拿到的办法:直接用上方的「网页登录」—— 它会替你把这个 API Key 换好,两个凭证一起拿到。)",{site:o,label:a,plan:c}),fields:[{key:"accessToken",label:r("编码套餐 API Key"),rows:3,optional:!0,placeholder:r("用于转发;形如 apiKey.secret 两段点分(不填则这个账号不能转发)")},{key:"jwt",label:"Coding Plan JWT",inputKey:"jwt",rows:3,optional:!0,placeholder:r("用于领取套餐与查询余额;不填则这个账号不能领取与查余额")},{key:"userId",label:r("用户 ID"),optional:!0,placeholder:r("可选;用于生成账号 id 与展示名")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空自动生成")}]}}const W8={provider:"codearts",label:"CodeArts",desktop:!1,webLogin:{noteHtml:r("打开华为云 CodeArts 的官方授权页登录:登录完成后官方页面会把浏览器带回网关自己的 /oauth/callback,网关用一次性授权码换取临时凭据并加入账号列表。
    网关跑在另一台机器上时,网页端会提示把地址栏中的最终回调地址直接粘贴回面板。"),button:r("打开 CodeArts 授权页"),busyText:r("等待 CodeArts 登录完成…"),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待")},{value:"external",label:r("系统浏览器"),hint:r("将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);浏览器与网关不在同一台机器时,按上方说明把最终回调地址粘贴回面板")}]},manualTitle:r("粘贴登录凭据"),manualNoteHtml:r('整份粘贴官方插件 / CLIProxyAPI 落盘的凭据 JSON(形如 {"codearts_provider_credential":{…}},铺平的也行)。
    必填:access_key_id、secret_access_key、security_token。要能自动续期,必须连 refresh_token 与 oauth_context 一起粘 —— 临时凭据约一小时到期,缺这半块就续不回来,只能重新登录。'),fields:[{key:"credentialJson",label:r("凭据 JSON"),rows:8,jsonExpand:!0,placeholder:'{"codearts_provider_credential":{"access_key_id":"HSTA…","secret_access_key":"…","security_token":"…","expires_at":"2026-09-27T16:17:00.327Z","domain_id":"…","user_id":"…","user_name":"…","refresh_token":"eyJ…","oauth_context":{…}}}'},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空使用凭据里的 user_name")}]},J8={provider:"trae",label:"Trae",desktop:!1,webLogin:{noteHtml:r("打开 Trae SOLO 的官方授权页(trae.cn)并用你的 Trae 账号登录:授权完成后官方页面会跳回本机的一个临时端口;同机部署自动接收,Docker / 远程面板请按提示复制地址栏回调地址。"),button:r("打开 Trae 授权页"),busyText:r("等待 Trae 授权完成…"),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("将打开内嵌窗口;授权完成后自动加入账号列表。远程面板可粘贴回调地址。关掉窗口即取消等待。链接 5 分钟内有效,超时或未点就会作废(可重新发起)")},{value:"external",label:r("系统浏览器"),hint:r("将用系统默认浏览器打开授权页(会复用浏览器里已登录的 Trae 账号);同机完成后自动加入,Docker / 远程面板按提示粘贴地址栏回调地址。")}]},manualTitle:r("填写凭证"),manualNoteHtml:r("accessToken 是 Trae 的 Cloud-IDE-JWT(三段点分),refreshToken 用于到期自动续期(本家 refreshToken 一次一换:换发一次旧的即作废,所以两份程序别同时刷同一个账号)。
    手工粘贴时请连 machineId / deviceId 一起填:上游把它们与登录时上传的设备公钥绑在一起判设备,凭空换一对会撞 2xxxx 那族设备绑定拒绝。
    没有这三样时的正路是用上方的「网页登录」;容器 / 远程部署形态按提示粘贴地址栏回调地址即可。"),fields:[{key:"accessToken",label:"accessToken",rows:3,placeholder:r("Cloud-IDE-JWT(三段点分)")},{key:"refreshToken",label:"refreshToken",rows:2,optional:!0,placeholder:r("可选;填了才能到期自动续期")},{key:"uid",label:r("用户 ID"),optional:!0,placeholder:r("可选;用于去重与展示名(留空时网关会问一次上游)")},{key:"machineId",label:"machineId",optional:!0,placeholder:r("可选;UUID 形态,与凭据同生共死")},{key:"deviceId",label:"deviceId",optional:!0,placeholder:r("可选;纯数字串(上游只收 8–24 位数字)")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空自动用昵称")}]},eq={provider:"loomy",label:"Loomy",smsLogin:{noteHtml:r("用 Loomy 账号绑定的手机号登录:点「获取验证码」后填入即可。这是本家最省事的入口。")},manualNote:r("粘贴的是登录 session(同时是模型网关的 token)。Loomy 没有续期接口,session 有效期约 14 天,过期后重新登录即可。"),fields:[{key:"session",label:"session",rows:3,placeholder:r("登录 session(一长串)")},{key:"userId",label:"userId",optional:!0,placeholder:r("可选,讯飞侧 userid")},{key:"phone",label:r("手机号"),optional:!0,placeholder:r("可选,展示用")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用脱敏手机号或 userId")}],desktop:!1},tq={provider:"kuku",label:"KukuAI",webLogin:{noteHtml:r("在打开的窗口里用百度账号登录 KukuAI 主站(手机验证码 / 扫码均可),登录成功后自动加入账号列表。网关会向百度换发 KukuAI 业务会话令牌(依赖本机已安装库库AI 客户端)。"),button:r("打开网页登录"),busyText:r("等待 KukuAI 登录完成…")},manualTitle:r("粘贴 Cookie"),manualNote:r("BDUSS 必填;请粘贴**完整 Cookie**(Cookie 编辑器一键导出即可)—— 其中 **PTOKEN 必须保留**,网关要用它向百度换发 KukuAI 业务会话令牌,缺了它模型刷新会报「未登录」。支持整段 Cookie 头、Cookie 编辑器导出的 JSON 数组,或 {BDUSS, STOKEN} 对象。KukuAI 没有刷新机制,登录态过期后重新粘贴即可。"),fields:[{key:"cookie",label:"Cookie",rows:3,placeholder:r("BDUSS=…; STOKEN=…(或 Cookie 编辑器导出的 JSON)")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用账号 uk")}],desktop:!0,desktopHint:r("读取本机 KukuAI 客户端登录态(%APPDATA%\\baidugenflowpro\\Network\\Cookies),需已在 KukuAI 客户端登录"),desktopNote:r("读本机客户端当前登录态(明文 Cookie,无需解密)。客户端运行时 Cookies 文件被独占占用,导入前请先关闭 KukuAI 客户端。"),fileImport:{binary:!0,accept:"",note:r("选择 KukuAI 客户端的 Cookies 文件(%APPDATA%\\baidugenflowpro\\Network\\Cookies):它是**二进制** SQLite 数据库(无扩展名,不是 JSON)。导入时读的是你选中文件的副本、不和运行中的客户端抢占用,因此**无需先关闭客户端**。导入后凭证直接落在网关、可独立转发。")}};function WR(e){const{provider:t,label:a,siteNote:o}=e;return{provider:t,label:a,desktop:!1,manualTitle:r("粘贴 session cookie"),manualNoteHtml:r("粘贴网页登录态 cookie monkeycode_ai_session 的值(在浏览器里复制整段 Cookie 直接粘贴也行,网关会自己剥出值)。
    {siteNote}
    MonkeyCode 没有续期接口:session 有效期约 30 天(硬限制),过期后重新登录网页、再粘贴一次即可。",{siteNote:o}),fields:[{key:"session",label:"session",rows:3,placeholder:r("monkeycode_ai_session 的值(也可直接粘贴整段 Cookie)")},{key:"imageId",label:r("任务镜像 id"),optional:!0,placeholder:r("可选:发起对话必需,留空则由网关从该账号已有任务里自动发现,发现不到时之后手动补")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用上游返回的账号名或账号 id")}]}}const nq=WR({provider:"monkeycode",label:r("MonkeyCode"),siteNote:r("这里填国内站(monkeycode-ai.com)的账号:请粘贴国内站的 session。国际站(monkeycode-ai.net)是另一家 provider、另一套账号,两边的凭证不通用。")}),sq=WR({provider:"monkeycode-intl",label:r("MonkeyCode 国际版"),siteNote:r("这里填国际站(monkeycode-ai.net)的账号:请粘贴国际站的 session。国内站(monkeycode-ai.com)是另一家 provider、另一套账号,两边的凭证不通用。")}),rq={provider:"commandcode",label:r("Command Code"),desktop:!1,manualTitle:r("粘贴 API Key"),manualNote:r("粘贴 Command Code 的 API Key(user_ 开头;从官方 CLI 的 ~/.commandcode/auth.json 或网页 studio 获取)。整行 / 带引号 / 带 Bearer 前缀地粘都行,网关会自己剥成裸 key。key 不过期、也没有续期接口,失效后重新获取即可;添加时网关会做一次轻量探活,失败不影响添加。"),fields:[{key:"apiKey",label:"apiKey",rows:3,placeholder:r("user_ 开头的 API Key(也可直接粘贴 Authorization: Bearer … 整行)")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用 key 尾号")}]},aq={provider:"antigravity",label:r("Antigravity"),desktop:!1,webLogin:{noteHtml:r("在打开的窗口里登录 Google 账号(Antigravity 的授权页),授权完成后自动加入账号列表。"),button:r("打开 Antigravity 网页登录"),busyText:r("等待 Google 授权完成…"),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("内嵌窗口打开;完成后自动加入列表,关窗即取消等待")},{value:"external",label:r("系统浏览器"),hint:r("系统浏览器打开(复用已登录的 Google 账号);完成后自动加入列表")}]},manualTitle:r("粘贴 Google refresh token"),manualNoteHtml:r("粘贴 Antigravity 的 Google refresh token(以 1// 开头;直接粘贴含 refresh_token= 的整行也行)。
    这是网页登录之外的手工入口(例如从已登录的官方 IDE 里导出)。refresh token 不会过期,access token 到期由网关自动续期;令牌丢了只能重新走 Google 授权,请妥善保存。"),fields:[{key:"refreshToken",label:"refreshToken",rows:2,placeholder:r("1// 开头的 Google refresh token(也可直接粘贴 refresh_token=… 整行)")},{key:"projectId",label:"projectId",optional:!0,placeholder:r("可选(cloudaicompanionProject):留空由网关自动发现")},{key:"email",label:"email",optional:!0,placeholder:r("可选:账号邮箱,用于展示与身份识别(换新令牌也能认回同一账号)")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用邮箱或令牌尾号")}]},lq={provider:"officeace",label:r("OfficeAce 果办"),desktop:!1,webLogin:{noteHtml:r("打开华为云授权页登录:登录完成后网关会自动轮询云端取回授权码并加入账号列表(不需要装 OfficeAce 桌面端,也无需手工回填回调地址)。"),button:r("打开授权页"),busyText:r("等待 OfficeAce 登录完成…"),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待")},{value:"external",label:r("系统浏览器"),hint:r("将用系统默认浏览器打开授权页(会复用浏览器里已登录的华为云账号);授权码由网关轮询云端取回,与浏览器跑在哪台机器无关,完成后自动加入账号列表")}]},manualTitle:r("粘贴网关凭据"),manualNoteHtml:r("从别的机器(或 OfficeAce 桌面端 models.json)取三项贴进来:模型网关基址(baseUrl,形如 https://modelgw-…/v2)与 modelAppKey / modelAppSecret。这三项就能转发(网关凭据不过期);控制面临时凭据(accessKeyId 等)可选,留空则额度/自动续期不可用,但不影响转发。"),fields:[{key:"baseUrl",label:r("模型网关基址"),placeholder:"https://modelgw-…huaweicloud-agentarts.com/v2"},{key:"modelAppKey",label:r("网关 Key"),placeholder:"model_app_key"},{key:"modelAppSecret",label:r("网关 Secret"),placeholder:"model_app_secret"},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空用「OfficeAce 果办」")}]},oq={provider:"orcaterm",label:"OrcaTerm",webLogin:{noteHtml:r("在打开的官方授权页里用微信扫码登录(页面会直接显示二维码)。扫码成功后自动加入账号列表。"),button:r("打开网页登录"),hint:r("内嵌窗口打开;扫码完成后自动加入列表,关窗即取消等待"),busyText:r("等待 OrcaTerm 登录完成…")},desktop:!0,desktopHint:r("读取本机 OrcaTerm 客户端登录态(%APPDATA%\\com.orcaterm-desktop.app\\data.bin),需已在 OrcaTerm 客户端登录"),desktopNote:r("读本机客户端当前登录的 OAuth 令牌(明文 JSON,无需解密)。导入后由网关自动续期,无需跟随客户端的 2 小时时效手动换令牌。"),manualTitle:r("粘贴 accessToken"),manualNoteHtml:r("粘贴 OrcaTerm 的 OAuth accessToken(JWT,三段点分;从桌面客户端 data.bin 或 Web 版 Cookie ot_session 获取)。直接粘贴含 Bearer / ot_session= 的整行也行,网关会自己剥成裸 token。添加后由网关自动续期(上游有续期接口),无需跟随 2 小时时效手动更换。"),fields:[{key:"accessToken",label:"accessToken",rows:3,placeholder:r("JWT(三段点分;也可直接粘贴 Bearer 整行或 data.bin 内容)")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用账号昵称或 userId")}]},iq={provider:"chatglm",label:"智谱清言",guestLogin:{noteHtml:r("不需要登录:点下面的按钮,网关会直接在智谱清言换发一份访客凭证并加入账号列表(也可以先填个备注名)。访客凭证可以直接用来对话,免费额度由上游按设备发放。

    注意:访客账号没有积分体系 —— 它与「会员积分」「每日登录积分」「活动领取」相关的接口一律需要登录,因此那些能力对访客会如实跳过(账号页的积分、签到中心都不显示读数)。想要积分就用「填写凭证」粘一个已登录账号的令牌,或「导入桌面端登录态」。"),button:r("添加访客账号"),busyText:r("正在换发访客凭证…"),namePlaceholder:r("可选,给这个访客账号起个备注名")},desktop:!0,desktopWindowsOnly:!0,desktopHint:r("读取本机智谱清言客户端登录态(%APPDATA%\\chatglm\\Network\\Cookies),需已在客户端登录"),desktopNote:r("读本机客户端当前的登录态。客户端的 Cookies 是 DPAPI 加密的(Chromium os_crypt),因此仅 Windows 可用;客户端正在运行时可能读不到该文件,先退出客户端再试即可(也可改用「填写凭证」粘贴令牌)。"),manualTitle:r("粘贴访问令牌"),manualNoteHtml:r("粘贴智谱清言的访问令牌(JWT,三段点分)。获取方式:浏览器打开 chatglm.cn 并登录后,在开发者工具的 Application → Cookies 里复制 chatglm_token 的值(也可以直接粘 chatglm_token=…; 整行)。粘贴后会先向上游校验一次,校验通过才落账号;登录账号的积分、签到能力都能用,之后由网关自动续期(上游有续期接口)。"),fields:[{key:"accessToken",label:"accessToken",rows:3,placeholder:r("JWT(三段点分;也可直接粘贴 chatglm_token=… 整行)")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空则用账号昵称")}]},cq={provider:"atomcode",label:r("AtomCode"),desktop:!1,webLogin:{noteHtml:r("打开 AtomGit 授权页登录:登录完成后网关会自动轮询代理取回令牌并加入账号列表(授权回调由代理终结,不回到本机,因此无需手工回填回调地址)。"),button:r("打开授权页"),busyText:r("等待 AtomCode 登录完成…")},manualTitle:r("粘贴令牌对"),manualNoteHtml:r("在 AtomGit 的 AtomCode 页面登出后取得两项贴进来:accessToken 与 refreshToken。续期令牌建议填写——本家令牌不是 JWT(约 7 天到期,读不到过期时间),没有它账号无法自动续期,到期只能重新登录。"),fields:[{key:"accessToken",label:r("访问令牌"),placeholder:"access_token"},{key:"refreshToken",label:r("续期令牌"),optional:!0,placeholder:"refresh_token"},{key:"userId",label:r("用户 ID"),optional:!0,placeholder:r("可选,用于生成账号标识")},{key:"name",label:r("备注名"),optional:!0,placeholder:r("可选,留空用「AtomCode」")}]},uq={provider:"lobsterai",label:r("LobsterAI"),desktop:!1,webLogin:{noteHtml:r("打开 LobsterAI(有道龙虾)的官方登录页(lobsterai.youdao.com)并用你的 LobsterAI 账号登录:登录成功后官方页面会跳回本机并自动加入账号列表 —— 回调落在网关自己的端口上,所以浏览器要与网关在同一台机器上;授权码只在本机传给网关,界面不显示明文 token。"),button:r("打开 LobsterAI 登录页"),busyText:r("等待 LobsterAI 登录完成…"),modes:[{value:"embedded",label:r("内嵌窗口(推荐)"),hint:r("将打开内嵌窗口;登录完成后自动加入账号列表。关掉窗口即取消等待")},{value:"external",label:r("系统浏览器"),hint:r("将用系统默认浏览器打开登录页(会复用浏览器里已登录的 LobsterAI 账号);完成登录后自动加入账号列表,关掉弹窗即取消等待")}]},fields:[]},JR=[G8,K8,Y8,X8,Q8,Z8,XR({provider:"cline-free",label:"Cline Free",poolNote:r("(免费额度池,模型名带 cline-free/ 前缀)。")}),XR({provider:"cline-pass",label:"Cline Pass",poolNote:r("(订阅池,模型名带 cline-pass/ 前缀,需要账号有对应订阅)。")}),QR({provider:"accio",label:r("国际版"),site:"www.accio.com",siteNote:r("国际版与国内版是**两套独立的账号**(同一账号体系的两个站点),凭证不通用。")}),QR({provider:"accio-cn",label:r("国内版"),site:"www.accio-ai.com",siteNote:r("国内版的登录站点是 www.accio-ai.com,与国际版不是同一站。")}),ZR({provider:"zcode",label:r("国内版"),site:"open.bigmodel.cn",planNote:r("国内版与**国际版是两套独立的账号与套餐**,凭证与领取的套餐都不通用。")}),ZR({provider:"zcode-intl",label:r("国际版"),site:"api.z.ai",planNote:r("国际版的推理站点是 api.z.ai,与国内版不是同一站;套餐也各自独立。")}),W8,J8,eq,tq,nq,sq,rq,aq,lq,oq,iq,cq,uq],Gi="workbuddy",ju="custom",Bj="WorkBuddy";function dq(e){return JR.find(t=>t.provider===e)}const eO=[{id:"guest",label:r("访客模式(免登录)")},{id:"oauth",label:r("网页登录(Zai / Google)")},{id:"sms",label:r("手机验证码登录")},{id:"web",label:r("网页登录")},{id:"manual",label:r("填写凭证")},{id:"file",label:r("导入凭证文件")},{id:"desktop",label:r("导入桌面端登录态")}],Hj=()=>gt().workbuddyDesktop?.platform||"";function tO(e){if(e.desktop===!1)return!1;const t=Hj();return!(t==="web"||e.desktopWindowsOnly&&t!=="windows")}function nO(e,t){return eO.filter(a=>a.id==="guest"?!!e.guestLogin:a.id==="oauth"?!!e.oauthLogin:a.id==="sms"?!!e.smsLogin:a.id==="web"?!!e.webLogin&&(!e.webLogin?.region||t===e.webLogin.region):a.id==="file"?!!e.fileImport:a.id==="desktop"?tO(e):!0).map(a=>a.id)}const fq=e=>e.manualTitle||r("填写凭证添加"),pq=e=>e.manualNoteHtml?{html:e.manualNoteHtml}:{text:e.manualNote||""},mq=e=>e.addButton||r("添加 {name} 账号",{name:e.label}),gg=(e,t)=>`${e.provider}-${t.inputKey||t.key}-input`;function hq(e){return e.source==="subscription"&&(e.port===null||e.port===void 0)}function tr(){return window}function qn(e,t){tr().wbApp?.toast?.(e,t)}function Ra(e){return e instanceof Error?e.message:String(e)}function sO(e){return tr().wbApp?.formatTime?.(e)||""}function Uj(e){const t=Number(e);return!Number.isFinite(t)||t<0?"":t<1e3?`${Math.round(t)}ms`:`${(t/1e3).toFixed(1)}s`}function Vj(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t]||t)}function wu(e){return String(e?.name||e?.id||"")}function rO(e){return e.source==="clash"}function _f(e){return e.source==="subscription"}function xg(e){return rO(e)||_f(e)}function gq(e){return e.localDisabled===!0||e.enabled===!1}function aO(e){return xg(e)&&e.enabled===!1&&e.localDisabled!==!0}function xq(e){return _f(e)&&gq(e)}function bg(e){return _f(e)&&e.orphan===!0}function lO(e){return!xg(e)||bg(e)}const bq=r("来自 Clash Verge 同步(名称、端口跟随 Clash),请到 Clash Verge 中修改;启用可在本页关闭"),vq=r("该节点来自订阅(名称、端口跟随订阅与内核),请在「订阅管理」里修改;启用可在本页关闭"),oO=r("该节点所属订阅已删除,不会再被同步更新,可手动删除清理");function iO(e){return bg(e)?oO:_f(e)?vq:bq}const yq=r("禁用后本端不再用它(引用它的账号回退直连);端口在 Clash Verge 侧,不受影响"),jq=r("禁用后本端不再用它(引用它的账号回退直连),并会关掉它的本地监听端口"),wq=r("已在本页禁用(打开开关即恢复)"),Sq=r("这条在 Clash Verge 里是停用的,本页启用不生效 —— 到 Clash Verge 里打开它");function Nq(e){if(bg(e))return oO;if(e.localDisabled===!0)return wq;if(aO(e))return Sq;if(_f(e))return jq;if(rO(e))return yq}let xs={data:null,loading:!1,error:"",testing:null,pending:new Set,syncing:!1,batchBusy:!1,batchProgress:null,selected:new Set};const Fj=new Set;function cO(e){return Fj.add(e),()=>{Fj.delete(e)}}function uO(){return xs}function rs(e){xs={...xs,...e},Fj.forEach(t=>t())}function vg(){return xs.data?.items??[]}function kq(e){return xs.selected.has(e)}function Cq(e,t){const a=new Set(xs.selected);t?a.add(e):a.delete(e),rs({selected:a})}function Tq(e,t){const a=new Set(xs.selected);for(const o of e)t?a.add(o):a.delete(o);rs({selected:a})}function Eq(){rs({selected:new Set})}function Ao(e){if(!e||typeof e!="object"||!Array.isArray(e.items))return;const t=new Set(e.items.map(o=>o.id)),a=new Set([...xs.selected].filter(o=>t.has(o)));rs({data:e,error:"",selected:a})}async function Lf(e=!1){const t=tr().workbuddyDesktop;if(typeof t?.getProxyPool!="function"){rs({error:r("当前环境不支持代理池(桥接方法缺失)")});return}e||rs({loading:!0});try{Ao(await t.getProxyPool())}catch(a){rs({error:Ra(a)}),e||qn(r("代理列表加载失败:{reason}",{reason:Ra(a)}),"err")}finally{rs({loading:!1})}}async function Aq(){const e=tr().workbuddyDesktop;if(typeof e?.syncClashToProxyPool!="function"){qn(r("当前环境不支持同步(桥接方法缺失)"),"err");return}if(!xs.syncing){rs({syncing:!0});try{const t=await e.syncClashToProxyPool();Ao(t),t?.syncError?qn(r("没能同步:{reason}",{reason:t.syncError}),"err"):t?.changes?qn(r("✅ 已同步 Clash Verge 出口({n} 项变更)",{n:t.changes})):qn(r("已是最新,没有需要同步的变更"))}catch(t){qn(r("同步失败:{reason}",{reason:Ra(t)}),"err")}finally{rs({syncing:!1})}}}async function Rq(e){const t=tr().workbuddyDesktop;if(typeof t?.testProxyPoolItem!="function"){qn(r("当前环境不支持代理测试(桥接方法缺失)"),"err");return}if(!xs.testing){rs({testing:e.id});try{const a=await t.testProxyPoolItem(e.id);if(a?.items&&Ao({items:a.items}),a?.success){const o=Uj(a.durationMs);qn(r("✅ 「{name}」出口可用{ip}{took}",{name:wu(e),ip:a.ip?r(" 出口 IP {ip}",{ip:a.ip}):"",took:o?` ${o}`:""}))}else qn(r("❌ 「{name}」{error}",{name:wu(e),error:a?.error||r("连接失败")}),"err");a?.saveError&&qn(r("测试结果没能记下:{reason}",{reason:a.saveError}),"err")}catch(a){qn(r("测试失败:{reason}",{reason:Ra(a)}),"err")}finally{rs({testing:null})}}}async function Oq(e,t){const a=tr().workbuddyDesktop;if(typeof a?.setProxyPoolItemEnabled!="function"){qn(r("当前环境不支持启停(桥接方法缺失)"),"err");return}const o=new Set(xs.pending);o.add(e.id),rs({pending:o});try{const c=await a.setProxyPoolItemEnabled({id:e.id,enabled:t});Ao(c);const d=wu(e),f=c?.listener??"none";qn(t?f==="opening"?r("已启用「{name}」,正在补建监听端口(内核重建中)",{name:d}):r("已启用「{name}」",{name:d}):f==="closing"?r("已禁用「{name}」,监听端口正在关闭(内核重建中)",{name:d}):r("已禁用「{name}」",{name:d})),(f==="closing"||f==="opening")&&Mq(e.id,f)}catch(c){qn(r("保存失败:{reason}",{reason:Ra(c)}),"err")}finally{const c=new Set(xs.pending);c.delete(e.id),rs({pending:c})}}async function Mq(e,t){for(let a=0;a<12;a+=1){await new Promise(d=>setTimeout(d,1500)),await Lf(!0);const o=vg().find(d=>d.id===e);if(!o)return;const c=o.port!==null&&o.port!==void 0;if(t==="closing"?!c:c)return}}async function dO(e){const t=tr().workbuddyDesktop;if(typeof t?.removeProxyPoolItem!="function")return;if(!lO(e)){qn(iO(e),"err");return}const a=e.usedBy||[],o=a.length?`

    ${r("有 {n} 个账号正在使用它({names})—— 删除后这些账号会回退直连。",{n:a.length,names:a.map(f=>Vj(String(f.name||f.id||""))).join(r("、"))})}

    `:"";if(!await tr().wbConfirm?.ask?.({title:r("删除代理"),html:r("确定删除「{name}」?{used}",{name:Vj(wu(e)),used:o}),okText:r("删除"),okClass:"danger"}))return;const d=new Set(xs.pending);d.add(e.id),rs({pending:d});try{const f=await t.removeProxyPoolItem(e.id);Ao(f),qn(r("已删除「{name}」",{name:wu(e)}))}catch(f){qn(r("删除失败:{reason}",{reason:Ra(f)}),"err")}finally{const f=new Set(xs.pending);f.delete(e.id),rs({pending:f})}}async function Dq(){const e=tr().workbuddyDesktop;if(typeof e?.testProxyPoolItem!="function"){qn(r("当前环境不支持代理测试(桥接方法缺失)"),"err");return}if(xs.batchBusy||xs.testing)return;const t=vg().filter(f=>xs.selected.has(f.id));if(!t.length)return;rs({batchBusy:!0,batchProgress:{done:0,total:t.length}});let a=0,o=0,c="";for(const[f,p]of t.entries()){rs({testing:p.id,batchProgress:{done:f,total:t.length}});try{const h=await e.testProxyPoolItem(p.id);h?.items&&Ao({items:h.items}),h?.success?a+=1:o+=1,h?.saveError&&!c&&(c=h.saveError)}catch{o+=1}}rs({testing:null,batchBusy:!1,batchProgress:null});const d=o?r("批量测试完成:{ok} 个可用、{failed} 个失败(共 {total} 个)",{ok:a,failed:o,total:t.length}):r("批量测试完成:{ok} 个可用(共 {total} 个)",{ok:a,total:t.length});qn(o?d:`✅ ${d}`,o?"err":"ok"),c&&qn(r("部分测试结果没能记下:{reason}",{reason:c}),"err")}async function Iq(){const e=tr().workbuddyDesktop;if(typeof e?.removeProxyPoolItem!="function"||xs.batchBusy||xs.testing)return;const t=vg().filter(S=>xs.selected.has(S.id)),a=t.filter(S=>lO(S)),o=t.length-a.length;if(!a.length){qn(o?r("选中的 {n} 条都是同步来的只读条目(Clash / 订阅),不能删除",{n:o}):r("没有可删除的条目"),"err");return}const c=a.slice(0,8).map(S=>Vj(wu(S))).join(r("、")),d=a.length>8?r("{names} 等 {n} 条",{names:c,n:a.length}):c,f=a.filter(S=>(S.usedBy||[]).length).length,p=f?`

    ${r("其中 {n} 条正被账号使用 —— 删除后那些账号会回退直连。",{n:f})}

    `:"",h=o?`

    ${r("另有 {n} 条是同步来的只读条目(Clash / 订阅),将跳过。",{n:o})}

    `:"";if(!await tr().wbConfirm?.ask?.({title:r("批量删除代理"),html:`${r("确定删除选中的 {n} 个代理?",{n:a.length})}

    ${d}

    ${p}${h}`,okText:r("删除"),okClass:"danger"}))return;rs({batchBusy:!0,batchProgress:{done:0,total:a.length}});let y=0,v=0,j="";for(const[S,N]of a.entries()){rs({batchProgress:{done:S,total:a.length}});try{const k=await e.removeProxyPoolItem(N.id);Ao(k),y+=1}catch(k){v+=1,j||(j=Ra(k))}}rs({batchBusy:!1,batchProgress:null});const w=[r("已删除 {n} 个代理",{n:y})];o&&w.push(r("跳过 {n} 个(同步只读条目)",{n:o})),v&&w.push(r("失败 {n} 个({reason})",{n:v,reason:j})),qn(v?w.join(r(",")):`✅ ${w.join(r(","))}`,v?"err":"ok")}async function Pq(e,t){const a=tr().workbuddyDesktop,o=e?{...t,id:e.id}:t,c=e?await a?.updateProxyPoolItem?.(o):await a?.createProxyPoolItem?.(o);return Ao(c),c}async function fO(){const e=tr().workbuddyDesktop;if(typeof e?.getProxySettings!="function")throw new Error(r("当前环境不支持代理设置(桥接方法缺失)"));const t=await e.getProxySettings();if(!t||typeof t!="object")throw new Error(r("代理设置响应异常"));return t}async function _q(e){const t=tr().workbuddyDesktop;if(typeof t?.saveProxySettings!="function")throw new Error(r("当前环境不支持代理设置(桥接方法缺失)"));const a=await t.saveProxySettings(e);if(!a||typeof a!="object")throw new Error(r("代理设置响应异常"));return a}function nr(){return window}function an(e,t){nr().wbApp?.toast?.(e,t)}function wr(e){return e instanceof Error?e.message:String(e)}let Ft={data:null,loading:!1,error:"",pending:!1,refreshing:null};const $j=new Set;function yg(e){return $j.add(e),()=>{$j.delete(e)}}function jg(){return Ft}function Sn(e){Ft={...Ft,...e},$j.forEach(t=>t())}function Lq(){return Ft.data?.items??[]}function Su(){return Ft.data?.kernel??null}function Ki(e){!e||typeof e!="object"||!Array.isArray(e.items)||Sn({data:e,error:""})}function Ro(){Lf(!0)}async function Ml(e=!1){const t=nr().workbuddyDesktop;if(typeof t?.getSubscriptions!="function"){Sn({error:r("当前环境不支持订阅(桥接方法缺失)")});return}e||Sn({loading:!0});try{Ki(await t.getSubscriptions())}catch(a){Sn({error:wr(a)})}finally{e||Sn({loading:!1})}}const zq=20;function Bq(e){const t=e.kernel;if(!t||t.download.state==="downloading")return!1;if(!t.installed||t.lastError&&!t.running)return!0;const a=t.providerSync??{};return(e.items??[]).filter(o=>o.enabled!==!1).every(o=>!!a[o.id])}async function Yi(){const e=nr().workbuddyDesktop;if(typeof e?.getSubscriptions=="function")for(let t=0;tsetTimeout(o,1500)),Ro();let a;try{a=await e.getSubscriptions()}catch{return}if(Ki(a),t>=1&&a&&Array.isArray(a.items)&&Bq(a))return}}async function pO(e){const a=await nr().workbuddyDesktop?.addSubscription?.({enabled:!0,kind:"url",url:"",...e});return Ki(a),an(r("已新增订阅「{name}」,内核正在同步节点…",{name:e.name.trim()})),Ro(),Yi(),a??null}async function mO(e,t){const o=await nr().workbuddyDesktop?.saveSubscription?.({id:e.id,...t});Ki(o),an(r("已保存「{name}」",{name:t.name.trim()})),Ro(),Yi()}async function Hq(e,t){const a=nr().workbuddyDesktop;if(!Ft.pending){Sn({pending:!0});try{const o=await a?.saveSubscription?.({id:e.id,name:e.name,url:e.url,kind:e.kind,enabled:t});Ki(o),an(t?r("已启用「{name}」",{name:e.name}):r("已禁用「{name}」",{name:e.name})),Ro(),Yi()}catch(o){an(r("保存失败:{reason}",{reason:wr(o)}),"err")}finally{Sn({pending:!1})}}}async function Uq(e){const t=nr().workbuddyDesktop;if(!(Ft.refreshing||Ft.pending)){Sn({refreshing:e.id});try{const a=await t?.refreshSubscription?.(e.id);Ki(a),an(r("已刷新「{name}」,正在同步节点…",{name:e.name})),Ro(),Yi()}catch(a){an(r("刷新失败:{reason}",{reason:wr(a)}),"err")}finally{Sn({refreshing:null})}}}async function Vq(e){const t=nr().workbuddyDesktop;if(await window.wbConfirm?.ask?.({title:r("删除订阅"),html:r("确定删除「{name}」?它在代理池里的节点会一并移除,引用这些节点的账号会回退直连。",{name:e.name||e.id}),okText:r("删除"),okClass:"danger"})&&!Ft.pending){Sn({pending:!0});try{const o=await t?.removeSubscription?.(e.id);Ki(o),an(r("已删除「{name}」",{name:e.name||e.id})),Ro(),Yi()}catch(o){an(r("删除失败:{reason}",{reason:wr(o)}),"err")}finally{Sn({pending:!1})}}}async function Fq(e){const t=nr().workbuddyDesktop;if(typeof t?.downloadKernel!="function"){an(r("当前环境不支持订阅(桥接方法缺失)"),"err");return}if(!Ft.pending){Sn({pending:!0});try{const a=await t.downloadKernel(e);a&&Ft.data&&Sn({data:{...Ft.data,kernel:a}}),a?.saved===!1&&an(r("下载线路 / 存放目录没能写进配置(本次下载已按新设置走,重启后会恢复原值)"),"err");for(let c=0;c<300;c+=1){await new Promise(f=>setTimeout(f,1e3));const d=await t.getKernelStatus?.();if(d&&Ft.data&&Sn({data:{...Ft.data,kernel:d}}),!d||d.download.state!=="downloading")break}const o=Ft.data?.kernel;o?.download.state==="error"?an(r("内核下载失败:{reason}",{reason:o.download.error||r("未知错误")}),"err"):o?.download.state==="canceled"?an(r("已停止下载内核(未下载完的临时文件已删除)")):o?.download.state==="done"&&(an(r("✅ 内核下载完成,正在启动…")),Yi())}catch(a){an(r("内核下载失败:{reason}",{reason:wr(a)}),"err")}finally{Sn({pending:!1})}}}async function $q(){const e=nr().workbuddyDesktop;if(typeof e?.cancelKernelDownload!="function")return"none";try{const t=await e.cancelKernelDownload();if(t&&Ft.data&&Sn({data:{...Ft.data,kernel:t}}),t?.canceled===!1)return"none";for(let o=0;o<20;o+=1){const c=await e.getKernelStatus?.();if(c&&Ft.data&&Sn({data:{...Ft.data,kernel:c}}),c&&c.download.state!=="downloading")break;await new Promise(d=>setTimeout(d,250))}const a=Ft.data?.kernel?.download.state;return a==="canceled"?"canceled":a==="done"?"done":"stalled"}catch(t){return an(r("停止下载失败:{reason}",{reason:wr(t)}),"err"),"none"}}async function qq(){const e=nr().workbuddyDesktop;if(!Ft.pending){Sn({pending:!0});try{const t=await e?.restartKernel?.();t&&Ft.data&&Sn({data:{...Ft.data,kernel:t}}),an(r("内核正在启动…")),Ro(),Yi()}catch(t){an(r("内核启动失败:{reason}",{reason:wr(t)}),"err")}finally{Sn({pending:!1})}}}async function Gq(){const e=nr().workbuddyDesktop;if(typeof e?.stopKernel!="function"){an(r("当前环境不支持订阅(桥接方法缺失)"),"err");return}if(!Ft.pending){Sn({pending:!0});try{const t=await e.stopKernel();t&&Ft.data&&Sn({data:{...Ft.data,kernel:t}});for(let o=0;o<10;o+=1){const c=await e.getKernelStatus?.();if(c&&Ft.data&&Sn({data:{...Ft.data,kernel:c}}),c&&c.running===!1)break;await new Promise(d=>setTimeout(d,500))}const a=Ft.data?.kernel;if(a?.running===!1)an(r("内核已关闭(订阅与端口注册表保留,「启动内核」后原位恢复)"));else{const o=a?.lastError||r("内核没有在超时内停下来");an(r("关闭内核失败:{reason}",{reason:o}),"err")}Ml(!0)}catch(t){an(r("关闭内核失败:{reason}",{reason:wr(t)}),"err"),Ml(!0)}finally{Sn({pending:!1})}}}async function Kq(){const e=nr().workbuddyDesktop;if(typeof e?.removeKernel!="function"){an(r("当前环境不支持订阅(桥接方法缺失)"),"err");return}const t=Su()?.version;if(!await window.wbConfirm?.ask?.({title:r("删除内核"),html:r("确定删除已下载的内核{version}?内核文件与端口映射会被移除,订阅节点会变成「未分配端口」,引用它们的账号会暂时回退直连;订阅本身保留,重新下载内核后会重新分配端口。",{version:t?`(${t})`:""}),okText:r("删除内核"),okClass:"danger"})||Ft.pending)return;Sn({pending:!0});const o=Su()?.lastError||"";try{const c=await e.removeKernel();c&&Ft.data&&Sn({data:{...Ft.data,kernel:c}});for(let f=0;f<20;f+=1){const p=await e.getKernelStatus?.();if(p&&Ft.data&&Sn({data:{...Ft.data,kernel:p}}),p&&p.installed===!1||p?.lastError&&p.lastError!==o)break;await new Promise(h=>setTimeout(h,500))}const d=Ft.data?.kernel;if(d?.installed===!1)an(r("已删除内核(订阅与账号引用保留,重新下载后会重新分配端口)")),Ro();else{const f=d?.lastError&&d.lastError!==o?d.lastError:r("内核目录没能删掉(可能仍被进程占用),稍后重试一次");an(r("删除内核失败:{reason}",{reason:f}),"err")}Ml(!0)}catch(c){an(r("删除内核失败:{reason}",{reason:wr(c)}),"err"),Ml(!0)}finally{Sn({pending:!1})}}async function Yq(e){const t=nr().workbuddyDesktop;if(typeof t?.decodeQrImage!="function")throw new Error(r("当前环境不支持订阅(桥接方法缺失)"));const a=await new Promise((d,f)=>{const p=new FileReader;p.onload=()=>d(String(p.result||"")),p.onerror=()=>f(new Error(r("图片读取失败"))),p.readAsDataURL(e)}),c=(await t.decodeQrImage({imageBase64:a}))?.url;if(!c)throw new Error(r("二维码里没有订阅链接"));return c}function hO(e){return new Promise((t,a)=>{const o=new FileReader;o.onload=()=>t(String(o.result||"")),o.onerror=()=>a(new Error(r("文件读取失败"))),o.readAsDataURL(e)})}const Nu="max-w-full flex-wrap [&_[data-slot=segmented-item]]:h-7 [&_[data-slot=segmented-item]]:px-3 [&_[data-slot=segmented-item]]:text-[12.5px]",Xq=r("登录态文件路径:~/.box-agent/config/auth.json(Windows:C:\\Users\\<你的用户名>\\.box-agent\\config\\auth.json)");let qj=!1;async function ku(e,t){if(!qj){qj=!0,e(!0);try{await t()}finally{qj=!1,e(!1)}}}function Oo({html:e,text:t}){return e?s.jsx("p",{dangerouslySetInnerHTML:{__html:e}}):s.jsx("p",{children:t||""})}function Qq(e,t,a){let o;try{o=JSON.parse(t)}catch(c){return Ln(r("{name} 不是合法 JSON:{reason}",{name:e.label,reason:Co(c)}),"err"),!1}return!o||typeof o!="object"||Array.isArray(o)?(Ln(r("{name}要粘一个 JSON 对象(形如 {sample})",{name:e.label,sample:'{"codearts_provider_credential":{…}}'}),"err"),!1):(Object.assign(a,o),!0)}function Zq({config:e,region:t,visible:a}){const o=e.provider,[c,d]=x.useState(!1),[f,p]=x.useState(""),h=pq(e),b=e.fields.map(v=>gg(e,v));async function y(){const v={provider:e.provider};e.regionOptions?.length&&(v.mode=t);for(const j of e.fields){const w=dr(gg(e,j));if(!j.optional&&!w){Ln(r("请填写 {label}",{label:j.label}),"err");return}if(w){if(j.jsonExpand){if(!Qq(j,w,v))return;continue}v[j.key]=w}}await ku(d,async()=>{try{const j=await vu(v);Lj(b),p(""),await To(Pf(j?.account),e.label)}catch(j){Ln(r("添加失败:{reason}",{reason:Co(j)}),"err")}})}return s.jsxs(dn,{hidden:!a,onClick:v=>{v.target.closest("[data-raccoon-hint]")&&(v.preventDefault(),p(Xq))},children:[s.jsx("h3",{children:fq(e)}),s.jsx(Oo,{...h}),e.fields.map(v=>{const j=gg(e,v),w=v.optional?"":r("(必填)");return s.jsxs("div",{className:`field-row${v.rows?" stack":""}`,children:[s.jsxs("label",{htmlFor:j,children:[v.label,w]}),v.rows?s.jsx(Mi,{id:j,rows:v.rows,placeholder:v.placeholder,..._r(j)}):s.jsx(Fe,{id:j,type:"text",maxLength:q8(v),placeholder:v.placeholder,..._r(j)})]},j)}),s.jsxs("div",{className:"field-row",children:[s.jsx(ne,{id:`${o}-add-button`,onClick:()=>{y()},disabled:c,children:c?r("添加中…"):mq(e)}),s.jsx("span",{className:"detail",id:`${o}-add-hint`,children:f})]})]})}function Wq({config:e,visible:t}){const a=e.provider,[o,c]=x.useState(!1);if(!tO(e)||!e.desktopNote)return null;async function d(){await ku(c,async()=>{try{const f=await vu({provider:e.provider,importDesktop:!0});await To(f?.account?.name||"",e.label)}catch(f){Ln(r("导入失败:{reason}",{reason:Co(f)}),"err")}})}return s.jsxs(dn,{hidden:!t,children:[s.jsx("h3",{children:r("从本机导入桌面端登录态")}),s.jsx("p",{children:e.desktopNote}),s.jsx("div",{className:"field-row",children:s.jsx(ne,{id:`${a}-desktop-button`,title:e.desktopHint,onClick:()=>{d()},disabled:o,children:r(o?"导入中…":"从本机导入桌面端登录态")})})]})}function Jq({config:e,visible:t}){const a=e.guestLogin,[o,c]=x.useState(!1);if(!a)return null;const d=gg(e,{key:"name"});async function f(){await ku(c,async()=>{const h=document.getElementById(d)?.value.trim()||"";try{const b=await vu({provider:e.provider,guest:!0,...h?{name:h}:{}});await To(b?.account?.name||"",e.label)}catch(b){Ln(r("添加失败:{reason}",{reason:Co(b)}),"err")}})}return s.jsxs(dn,{hidden:!t,children:[s.jsx("h3",{children:r("访客模式")}),a.noteHtml?s.jsx(Oo,{html:a.noteHtml}):null,s.jsxs("div",{className:"form-field",children:[s.jsx("label",{htmlFor:d,children:r("备注名")}),s.jsx("input",{id:d,type:"text",maxLength:zj,placeholder:a.namePlaceholder||r("可选"),..._r(d)})]}),s.jsx("div",{className:"field-row",children:s.jsx(ne,{id:`${e.provider}-guest-button`,onClick:()=>{f()},disabled:o,children:o?a.busyText||r("正在添加…"):a.button})})]})}function eG(e){const t=e.indexOf(",");return t>=0?e.slice(t+1):e}function tG({config:e,visible:t}){const[a,o]=x.useState(!1),[c,d]=x.useState(""),f=x.useRef(null),p=e.fileImport,h=`${e.provider}-file-name`;if(!p)return null;async function b(y){const v=e.fileImport;v&&await ku(o,async()=>{try{const j=dr(h),w={provider:e.provider,authFileName:y.name};if(v.binary)w.authFileB64=eG(await hO(y));else{const N=await y.text();let k;try{k=JSON.parse(N)}catch{Ln(r("选择的文件不是合法 JSON"),"err");return}if(!k||typeof k!="object"||Array.isArray(k)){Ln(r("不是有效的凭证文件:内容要是一个 JSON 对象"),"err");return}w.authFileContent=N}j&&(w.name=j);const S=await vu(w);d(""),await To(Pf(S?.account),e.label)}catch(j){Ln(r("导入失败:{reason}",{reason:Co(j)}),"err")}})}return s.jsxs(dn,{hidden:!t,children:[s.jsx("h3",{children:r("导入凭证文件")}),s.jsx(Oo,{text:p.note}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{className:"lb-inline",htmlFor:h,children:r("备注名")}),s.jsx(Fe,{id:h,type:"text",maxLength:zj,placeholder:r("可选,留空则用文件里的昵称"),..._r(h)})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("input",{ref:f,type:"file",accept:p.accept,hidden:!0,onChange:y=>{const v=y.target.files?.[0];y.target.value="",v&&(d(v.name),b(v))}}),s.jsx(ne,{onClick:()=>f.current?.click(),disabled:a,children:r(a?"导入中…":"选择凭证文件导入")}),s.jsx("span",{className:"detail",children:c})]})]})}function nG({config:e,mode:t,onModeChange:a,region:o,visible:c}){const d=e.webLogin,f=e.provider,p=x.useRef(t),h=x.useRef(o),b=x.useRef(null);return p.current=t,h.current=o,x.useEffect(()=>{const y=gt().wbWebLogin;if(!y||!d)return;const v=d.modes;b.current=y.create({provider:f,buttonId:`${f}-web-button`,cancelId:`${f}-web-cancel`,hintId:`${f}-web-hint`,busyText:d.busyText,texts:()=>{const j=v?.find(w=>w.value===p.current);return{button:d.button,hint:j?.hint||d.hint||""}},start:()=>gt().workbuddyDesktop?.startLogin(d.edition||h.current||"cn",p.current||"embedded",f),onSuccess:async j=>{_j(),await gt().wbApp?.refresh?.();const w=j?.payload?.warning;w?Ln(r("账号已添加,但请留意:{warning}",{warning:w}),"err"):Ln(r("✅ {label}账号已添加",{label:e.label}))}})??null},[]),x.useEffect(()=>{b.current?.syncTexts()},[t]),d?s.jsxs(dn,{hidden:!c,children:[s.jsx("h3",{children:r("网页登录")}),s.jsx(Oo,{html:d.noteHtml}),d.modes?.length?s.jsxs("div",{className:"flex flex-col gap-2.5 border-l-2 border-border pl-[11px]",children:[s.jsx("span",{className:"text-[12.5px] text-subtle",children:r("打开方式")}),s.jsx(En,{"aria-label":r("{label} 网页登录的打开方式",{label:e.label}),options:d.modes,value:t,onValueChange:a})]}):null,s.jsxs("div",{className:"field-row",children:[s.jsx(ne,{id:`${f}-web-button`,onClick:()=>b.current?.start(),children:d.button}),s.jsx(ne,{id:`${f}-web-cancel`,variant:"outline",hidden:!0,onClick:()=>b.current?.cancel(),children:r("取消等待")}),s.jsx("span",{className:"detail",id:`${f}-web-hint`,children:d.hint||d.modes?.[0]?.hint||""})]})]}):null}function sG({config:e,visible:t}){const a=e.smsLogin,o=e.provider;return x.useEffect(()=>{a&>().wbSmsLogin?.create({provider:o,onSuccess:c=>To(Pf(c?.account),e.label)})},[]),a?s.jsxs(dn,{hidden:!t,children:[s.jsx("h3",{children:r("手机验证码登录")}),s.jsx(Oo,{html:a.noteHtml,text:r("用 AutoClaw 账号绑定的手机号登录:点击「获取验证码」,收到短信后填入下方并登录。验证码由本机直接提交给官方接口,界面不显示 token。")}),s.jsxs("div",{className:"sms-form",children:[s.jsxs("div",{className:"sms-field",children:[s.jsxs("label",{className:"lb",htmlFor:`${o}-sms-phone`,children:[r("手机号"),s.jsx("i",{className:"req",children:"*"})]}),s.jsxs(Di,{children:[s.jsx(Pi,{id:`${o}-sms-phone`,type:"text",maxLength:11,placeholder:r("11 位大陆手机号"),..._r(`${o}-sms-phone`)}),s.jsx(Ii,{align:"inline-end",children:s.jsx(M4,{id:`${o}-sms-send`,className:"text-primary-fg",children:r("获取验证码")})})]})]}),s.jsxs("div",{className:"sms-field",children:[s.jsxs("label",{className:"lb",htmlFor:`${o}-sms-code`,children:[r("验证码"),s.jsx("i",{className:"req",children:"*"})]}),s.jsxs("div",{className:"sms-pair",children:[s.jsx(Fe,{id:`${o}-sms-code`,className:"sms-code",type:"text",maxLength:6,placeholder:r("6 位数字")}),s.jsx("label",{className:"lb-inline",htmlFor:`${o}-sms-name`,children:r("备注名")}),s.jsx(Fe,{id:`${o}-sms-name`,className:"sms-name",type:"text",placeholder:r("可选,留空则用脱敏手机号"),..._r(`${o}-sms-name`)})]})]}),s.jsxs("div",{className:"sms-foot",children:[s.jsx(ne,{id:`${o}-sms-submit`,children:r("登录并添加")}),s.jsx("span",{className:"detail sms-hint",id:`${o}-sms-hint`})]})]})]}):null}function rG({config:e,mode:t,onModeChange:a,visible:o}){const c=e.oauthLogin,d=e.provider,f=x.useRef(t),p=x.useRef(null);f.current=t;const h=()=>c?.modes?.find(b=>b.value===f.current)?.hint||c?.hint||"";return x.useEffect(()=>{const b=gt().wbAutoclawOauth;!b||!c||(p.current=b.create({provider:d,mode:()=>f.current||"embedded",hint:h,cancelId:`${d}-oauth-cancel`,onSuccess:()=>To("",e.label)})??null)},[]),x.useEffect(()=>{p.current?.syncTexts()},[t]),c?s.jsxs(dn,{hidden:!o,children:[s.jsx("h3",{children:c.title||r("网页登录")}),s.jsx(Oo,{html:c.noteHtml}),c.modes?.length?s.jsxs("div",{className:"flex flex-col gap-2.5 border-l-2 border-border pl-[11px]",children:[s.jsx("span",{className:"text-[12.5px] text-subtle",children:r("打开方式")}),s.jsx(En,{"aria-label":r("{label} 网页登录的打开方式",{label:e.label}),options:c.modes,value:t,onValueChange:a})]}):null,s.jsxs("div",{className:"field-row",children:[s.jsx(ne,{id:`${d}-oauth-zai`,children:r("使用 Zai 账号登录")}),s.jsx(ne,{id:`${d}-oauth-google`,variant:"outline",children:r("使用 Google 账号登录")}),s.jsx(ne,{id:`${d}-oauth-cancel`,variant:"outline",hidden:!0,children:r("取消")})]}),s.jsx("div",{className:"field-row",children:s.jsx("span",{className:"detail",id:`${d}-oauth-hint`,children:c.modes?.[0]?.hint||c.hint||""})})]}):null}function aG({config:e,active:t}){const a=e.regionOptions?.[0]?.value??"",[o,c]=x.useState(a),[d,f]=x.useState(()=>nO(e,a)[0]),[p,h]=x.useState(e.webLogin?.modes?.[0]?.value??""),[b,y]=x.useState(e.oauthLogin?.modes?.[0]?.value??""),v=nO(e,o),j=v.includes(d)?d:v[0],w=x.useMemo(()=>eO.filter(S=>v.includes(S.id)).map(S=>({value:S.id,label:S.label})),[v.join(",")]);return s.jsxs("div",{className:"add-provider-block",hidden:!t,children:[e.regionOptions?.length?s.jsxs(dn,{children:[s.jsx("h3",{children:r("地区")}),s.jsx(En,{"aria-label":r("{label} 账号地区",{label:e.label}),className:Nu,options:e.regionOptions,value:o,onValueChange:c})]}):null,s.jsxs(dn,{children:[s.jsx("h3",{children:r("添加方式")}),s.jsx(En,{"aria-label":r("{label} 账号的添加方式",{label:e.label}),className:Nu,options:w,value:j,onValueChange:S=>f(S)})]}),s.jsx(Jq,{config:e,visible:j==="guest"}),s.jsx(rG,{config:e,mode:b,onModeChange:y,visible:j==="oauth"}),s.jsx(sG,{config:e,visible:j==="sms"}),s.jsx(nG,{config:e,mode:p,onModeChange:h,region:o,visible:j==="web"}),s.jsx(Zq,{config:e,region:o,visible:j==="manual"}),s.jsx(tG,{config:e,visible:j==="file"}),s.jsx(Wq,{config:e,visible:j==="desktop"})]})}const lG=r("官方客户端凭证目录:Windows 是 C:\\Users\\<你的用户名>\\AppData\\Local\\CodeBuddyExtension\\Data\\Public\\auth,macOS 是 ~/Library/Application Support/CodeBuddyExtension/Data/Public/auth"),wg="workbuddy-file-name";function Gj(e,t,a){const o=e[a],c=[];o&&typeof o=="object"&&!Array.isArray(o)&&c.push(o),c.push(e);for(const d of c)for(const f of t){const p=d[f];if(typeof p=="string"&&p.trim())return p.trim()}return""}function oG(e){const t=Gj(e,["domain"],"auth");return/workbuddy\.ai/i.test(t)?"intl":/workbuddy\.cn|codebuddy\.cn/i.test(t)?"cn":null}function iG({edition:e,visible:t}){const[a,o]=x.useState(!1),[c,d]=x.useState(""),f=x.useRef(null);async function p(h){await ku(o,async()=>{try{let b;try{b=JSON.parse(await h.text())}catch{Ln(r("选择的文件不是合法 JSON"),"err");return}if(!b||typeof b!="object"||Array.isArray(b)){Ln(r("不是有效的凭证文件:内容要是一个 JSON 对象"),"err");return}const y=b,v=Gj(y,["accessToken","token","access_token"],"auth"),j=Gj(y,["uid"],"account");if(!v||!j){Ln(r("不是有效的凭证文件:需要包含 accessToken 与 uid"),"err");return}const w=oG(y),S=w??e;w&&w!==e&&Ln(r("文件是{edition}凭证,已按该版本导入",{edition:r(w==="intl"?"国际版":"国内版")}),"warn");const N=dr(wg),k={...y,provider:Sg(S)};N&&(k.name=N);const A=await vu(k);d(""),await To(Pf(A?.account),S==="intl"?"WorkBuddy AI":"WorkBuddy")}catch(b){Ln(r("导入失败:{reason}",{reason:Co(b)}),"err")}})}return s.jsxs(dn,{hidden:!t,children:[s.jsx("h3",{children:r("导入凭证文件")}),s.jsx(Oo,{text:r("选择官方客户端的登录态凭证文件(workbuddy-desktop.info / workbuddy-desktop-ai.info)或 wb2api 导出的账号 JSON(嵌套 / 扁平形均可)。")}),s.jsx("p",{className:"detail",children:s.jsx("a",{href:"#",onClick:h=>{h.preventDefault(),d(lG)},children:r("凭证文件在哪?")})}),s.jsxs("div",{className:"field-row",children:[s.jsx("label",{className:"lb-inline",htmlFor:wg,children:r("备注名")}),s.jsx(Fe,{id:wg,type:"text",maxLength:100,placeholder:r("可选,留空则用文件里的昵称"),..._r(wg)})]}),s.jsxs("div",{className:"field-row",children:[s.jsx("input",{ref:f,type:"file",accept:".info,.json,application/json",hidden:!0,onChange:h=>{const b=h.target.files?.[0];h.target.value="",b&&p(b)}}),s.jsx(ne,{onClick:()=>f.current?.click(),disabled:a,children:r(a?"导入中…":"选择凭证文件导入")}),s.jsx("span",{className:"detail",children:c})]})]})}function cG({edition:e,visible:t}){const[a,o]=x.useState(!1);async function c(){await ku(o,async()=>{try{const d=await vu({provider:Sg(e),importDesktop:!0});await To(Pf(d?.account),e==="intl"?"WorkBuddy AI":"WorkBuddy")}catch(d){Ln(r("导入失败:{reason}",{reason:Co(d)}),"err")}})}return s.jsxs(dn,{hidden:!t,children:[s.jsx("h3",{children:r("从本机导入桌面端登录态")}),s.jsx(Oo,{text:r("读本机官方客户端(Tencent WorkBuddy / WorkBuddy AI)当前登录的账号:自动定位其凭证目录里最新的登录态文件(需已在本机官方客户端登录)。导入后由网关自动续期,无需跟随 2 小时时效手动换令牌。")}),s.jsx("div",{className:"field-row",children:s.jsx(ne,{onClick:()=>{c()},disabled:a,children:r(a?"导入中…":"从本机导入桌面端登录态")})})]})}function Sg(e){return e==="intl"?"workbuddy-intl":"workbuddy"}function uG({active:e}){const[t,a]=x.useState(qi.edition),[o,c]=x.useState(qi.loginMode),[d,f]=x.useState(qi.socialRestore),[p,h]=x.useState("web"),b=x.useRef(t),y=x.useRef(o),v=x.useRef(d),j=x.useRef(null);b.current=t,y.current=o,v.current=d,x.useEffect(()=>{const C=gt().wbWebLogin;C&&(j.current=C.create({get provider(){return Sg(b.current)},buttonId:"web-login-button",cancelId:"web-login-cancel",hintId:"web-login-hint",busyText:r("等待网页登录…"),texts:()=>{const E=y.current==="external";return{button:r(E?"在浏览器中打开登录页":"打开网页登录"),hint:r(E?"系统浏览器打开(复用已登录账号);完成后自动加入列表":"内嵌窗口打开;完成后自动加入列表,关窗即取消等待")}},start:()=>gt().workbuddyDesktop?.startLogin(b.current,y.current,Sg(b.current),v.current),onSuccess:async()=>{const E=b.current==="intl"?r("国际版"):r("国内版");_j(),await gt().wbApp?.refresh?.(),Ln(r("✅ 登录成功,{edition}账号已加入列表",{edition:E}))}})??null)},[]),x.useEffect(()=>{j.current?.syncTexts()},[t,o]);function w(C){const E=C==="intl"?"external":"embedded";a(C),qi.edition=C,c(E),qi.loginMode=E}function S(C){const E=C==="external"?"external":"embedded";c(E),qi.loginMode=E}const N=t==="intl",A=N&&o==="embedded",P=r(A?"国际版登录页默认只显示邮箱登录,勾选后恢复 Google / GitHub / X 入口":N?"只有「内嵌窗口」能恢复第三方入口:系统浏览器里我们无法改动登录页":"国内版登录页没有 Google / GitHub 入口(它用微信 / 手机号 / 邮箱登录)"),R=p==="desktop"&&Hj()==="web"?"web":p;return s.jsxs("div",{className:"add-provider-block",hidden:!e,children:[s.jsxs(dn,{children:[s.jsx("h3",{children:r("账号版本")}),s.jsx("p",{children:r("两版账号可同时保存,各自一份模型清单(国内版与国际版是两家提供商,可分别启用与映射)。")}),s.jsx(En,{"aria-label":r("账号版本"),className:Nu,options:[{value:"cn",label:r("国内版(WorkBuddy)")},{value:"intl",label:r("国际版(WorkBuddy AI)")}],value:t,onValueChange:C=>w(C)})]}),s.jsxs(dn,{children:[s.jsx("h3",{children:r("添加方式")}),s.jsx(En,{"aria-label":r("{label} 账号的添加方式",{label:"WorkBuddy"}),className:Nu,options:[{value:"web",label:r("网页登录")},{value:"file",label:r("导入凭证文件")},...Hj()!=="web"?[{value:"desktop",label:r("导入桌面端登录态")}]:[]],value:R,onValueChange:C=>h(C)})]}),s.jsxs(dn,{hidden:R!=="web",children:[s.jsx("h3",{children:r("网页登录")}),s.jsx("p",{children:r("完成登录后自动加入账号列表,昵称取自上游。")}),s.jsxs("div",{className:"field-row",children:[s.jsx("span",{className:"text-[12.5px] whitespace-nowrap text-subtle",children:r("打开方式")}),s.jsx(En,{"aria-label":r("网页登录的打开方式"),className:Nu,options:[{value:"embedded",label:r("内嵌窗口")},{value:"external",label:r("系统默认浏览器")}],value:o,onValueChange:S}),s.jsxs("label",{className:"flex items-center gap-2",title:P,children:[s.jsx(ns,{checked:d,disabled:!A,onCheckedChange:C=>{f(C),qi.socialRestore=C}}),s.jsx("span",{className:"text-[12.5px] text-subtle",children:r("恢复 Google / GitHub 入口")})]})]}),s.jsx("p",{children:r("系统浏览器可复用已有登录态(国际版推荐);内嵌窗口更干净。")}),s.jsxs("div",{className:"field-row",children:[s.jsx(ne,{id:"web-login-button",onClick:()=>j.current?.start(),children:r("打开网页登录")}),s.jsx(ne,{id:"web-login-cancel",variant:"outline",hidden:!0,onClick:()=>j.current?.cancel(),children:r("取消等待")}),s.jsx("span",{className:"detail",id:"web-login-hint",children:r("内嵌窗口打开;完成后自动加入列表,关窗即取消等待")})]})]}),s.jsx(iG,{edition:t,visible:R==="file"}),s.jsx(cG,{edition:t,visible:R==="desktop"})]})}const Kj="proxy",Yj="preset",Ng="custom",dG="import",zf="preset:",Xj="__new__";function fG(e){return e===Yj||e===Ng?e:Kj}const kg={workbuddy:"assets/providers/workbuddy.png","workbuddy-intl":"assets/providers/workbuddy.png",raccoon:"assets/providers/raccoon.png",catpaw:"assets/providers/catpaw.png",autoclaw:"assets/providers/autoclaw.png","autoclaw-intl":"assets/providers/autoclaw.png",qoder:"assets/providers/qoder.png","qoder-intl":"assets/providers/qoder.png","cline-free":"assets/providers/cline.png","cline-pass":"assets/providers/cline.png",accio:"assets/providers/accio.png","accio-cn":"assets/providers/accio.png",zcode:"assets/providers/zcode.png","zcode-intl":"assets/providers/zcode.png",codearts:"assets/providers/codearts.png",trae:"assets/providers/trae.png",loomy:"assets/providers/loomy.png",kuku:"assets/providers/kuku.png",monkeycode:"assets/providers/monkeycode.png","monkeycode-intl":"assets/providers/monkeycode.png",commandcode:"assets/providers/commandcode.png",antigravity:"assets/providers/antigravity.png",officeace:"assets/providers/officeace.png",orcaterm:"assets/providers/orcaterm.png",chatglm:"assets/providers/chatglm.png",atomcode:"assets/providers/atomcode.png",lobsterai:"assets/providers/lobsterai.png"},pG=new Set(["cline-pass"]);function mG(e){const t=gt().wbProviders;if(e===Yj)return(gt().wbPresetProviders?.list||[]).map(h=>{const b=KR(h);return{id:zf+h.key,label:h.name,count:b?Pj(b.id):0,preset:!0,builtId:b?.id||""}});if(e===Ng)return(t?.customList?.()||[]).filter(h=>!F8(h)).map(h=>({id:h.id,label:h.name||h.id,count:Pj(h.id),custom:!0}));const a=(t?.all?.()||[]).filter(h=>!pG.has(String(h?.id||""))),o=(h,b)=>h===Gi?Bj:r(gt().wbProviders?.labelOf?.(h)||b),d=(a.length?a.map(h=>{const b=String(h.id||"");return{id:b,label:o(b,String(h.label||h.id||"")),count:Number(h.count)||0}}):[{id:Gi,label:Bj,count:Pj(Gi)}]).filter(h=>h.id!=="workbuddy-intl"),f=d.findIndex(h=>h.id==="autoclaw-intl"),p=d.findIndex(h=>h.id==="autoclaw");return p>=0&&f>p&&d.splice(p+1,0,d.splice(f,1)[0]),d}function hG({item:e}){const t=e.id.startsWith(zf)?gt().wbPresetProviders?.iconOf?.(e.id.slice(zf.length))||"":kg[e.id];if(t)return s.jsx("span",{className:"add-provider-logo has-icon",children:s.jsx("img",{src:t,alt:"",loading:"lazy"})});const a=String(e.label||"?").trim().slice(0,1).toUpperCase()||"?";return s.jsx("span",{className:"add-provider-logo",children:a})}function gG(e){return e.preset?e.builtId?e.count?r("已建过 · {n} 个账号,点击继续添加",{n:e.count}):r("已建过,点击继续添加账号"):r("点开即预填,填 Key 接入"):e.custom?e.count?r("{n} 个账号,点击添加",{n:e.count}):r("点击添加账号"):e.count?r("{n} 个账号",{n:e.count}):r("还没有账号")}function xG({item:e,onPick:t}){return s.jsxs("button",{type:"button",className:"add-provider-card","data-provider":e.id,role:"option",onClick:()=>t(e.id),children:[s.jsx(hG,{item:e}),s.jsxs("span",{className:"add-provider-info",children:[s.jsx("span",{className:"add-provider-name",children:e.label}),s.jsx("span",{className:"add-provider-meta",children:gG(e)})]}),s.jsx("span",{className:"add-provider-go",children:"›"})]})}function bG({onPick:e}){return s.jsxs("button",{type:"button",className:"add-provider-card is-new","data-provider":Xj,role:"option",onClick:()=>e(Xj),children:[s.jsx("span",{className:"add-provider-logo is-new",children:"+"}),s.jsxs("span",{className:"add-provider-info",children:[s.jsx("span",{className:"add-provider-name",children:r("新建自定义提供商")}),s.jsx("span",{className:"add-provider-meta",children:r("接入一个兼容上游")})]}),s.jsx("span",{className:"add-provider-go",children:"›"})]})}function vG({accountType:e,onAccountTypeChange:t,search:a,onSearchChange:o,onPick:c,version:d,importing:f,importPanel:p}){const h=x.useMemo(()=>mG(e),[e,d]),b=a.trim().toLowerCase(),y=h.filter(j=>!b||j.label.toLowerCase().includes(b)),v=e===Ng&&!b;return s.jsxs(dn,{children:[s.jsx("h3",{children:r("选择提供商")}),s.jsxs("div",{className:"add-pick-row",style:f?{display:"none"}:void 0,children:[s.jsx(En,{"aria-label":r("账号类型"),className:Nu,options:[{value:Kj,label:"Agent"},{value:Yj,label:r("预置 API")},{value:Ng,label:r("自定义")}],value:e,onValueChange:j=>t(fG(j))}),s.jsx("span",{id:"add-search-wrap",className:"add-provider-search ml-auto max-w-[340px] min-w-0 flex-[1_1_200px]",children:s.jsxs(Di,{children:[s.jsx(Ii,{children:"⌕"}),s.jsx(Pi,{id:"add-provider-search",type:"search",placeholder:r("搜索提供商…"),autoComplete:"off","aria-label":r("搜索提供商"),value:a,onChange:j=>o(j.currentTarget.value)})]})})]}),p,s.jsxs("div",{className:"add-provider-grid",id:"add-provider-grid",role:"listbox","aria-label":r("选择要添加账号的提供商"),style:f?{display:"none"}:void 0,children:[v?s.jsx(bG,{onPick:c}):null,y.map(j=>s.jsx(xG,{item:j,onPick:c},j.id)),!y.length&&b?s.jsx("div",{className:"add-provider-empty",children:r("没有匹配「{keyword}」的提供商",{keyword:a.trim()})}):null]})]})}function Cg(e,t){return Pe().wbIcons?.icon?.(e,t)||""}function yG({account:e,rank:t}){const a=r("组内第 {rank} 位",{rank:t});return s.jsxs("div",{className:"order-cell",children:[s.jsx("span",{className:"order-grip",role:"button",tabIndex:-1,"aria-label":r("拖动调整组内顺序"),title:r("按住拖动,调整这个账号在本组内的顺序"),dangerouslySetInnerHTML:{__html:Cg("gripVertical",14)}}),s.jsx("span",{className:"order-rank",title:a,children:`#${t}`})]})}function jG({group:e,ids:t,collapsed:a}){const[o,c]=x.useState(!1),d="w-full justify-start px-2 font-normal";return s.jsxs(wf,{open:o,onOpenChange:c,children:[s.jsx(Kh,{render:s.jsx("button",{type:"button",className:"grp-iconbtn",title:r("本组操作")}),children:"⋯"}),s.jsx(Sf,{align:"end",sideOffset:4,className:"w-[196px] p-1.5",children:s.jsxs("div",{className:"flex flex-col gap-0.5",children:[s.jsx(ne,{variant:"ghost",size:"sm",className:d,title:r("启用这一组里的全部账号(含被筛选隐藏的)"),onClick:()=>{c(!1),ER(t,!0)},children:r("全部启用({n})",{n:t.length})}),s.jsx(ne,{variant:"ghost",size:"sm",className:d,title:r("禁用这一组里的全部账号(含被筛选隐藏的)"),onClick:()=>{c(!1),ER(t,!1)},children:r("全部禁用({n})",{n:t.length})}),s.jsx(ne,{variant:"ghost",size:"sm",className:d,title:r("打开批量操作弹窗,并预选这一组的账号(改代理等动作在弹窗里挑参数)"),onClick:()=>{c(!1),kj(t,"proxy")},children:r("批量操作本组")}),s.jsx("div",{className:"my-1 h-px bg-hairline"}),s.jsx(ne,{variant:"ghost",size:"sm",className:d,onClick:()=>{c(!1),iR(e.provider)},children:r(a?"展开这一组":"折叠这一组")})]})})]})}function wG({group:e,collapsed:t,colSpan:a}){const o=kg[e.provider];return s.jsx("tr",{className:"grp-band","data-provider":e.provider,children:s.jsx("td",{colSpan:a,children:s.jsxs("div",{className:"grp-band-inner",children:[s.jsx("span",{className:"queue-grip",role:"button",tabIndex:-1,"aria-label":r("拖动调整整组顺序"),title:r("按住拖动,调整这一家的组顺位(整组一起动)"),dangerouslySetInnerHTML:{__html:Cg("gripVertical",14)}}),s.jsx("span",{className:"grp-rank",title:r("组顺位:越小越先用(拖动分组带调整)"),children:r("第 {rank} 顺位",{rank:e.rank})}),s.jsx("span",{className:"grp-logo",children:o?s.jsx("img",{src:o,alt:"",loading:"lazy"}):s.jsx("span",{className:"grp-logo-txt",children:String(e.label||"?").trim().slice(0,1).toUpperCase()})}),s.jsx("span",{className:"grp-name",children:r(e.label)}),s.jsx("span",{className:"grp-stat",children:r("{total} 个账号 · 启用 {enabled} · 禁用 {disabled}{limited}",{total:e.members.length,enabled:e.enabled,disabled:e.disabled,limited:e.limited>0?r(" · 限流 {n}",{n:e.limited}):""})}),s.jsx("span",{className:"grp-spacer"}),s.jsx("button",{type:"button",className:"grp-iconbtn grp-fold","aria-expanded":!t,title:r(t?"展开这一组":"折叠这一组"),onClick:()=>iR(e.provider),dangerouslySetInnerHTML:{__html:Cg("chevronDown",14)}}),s.jsx(jG,{group:e,ids:e.accounts.map(c=>c.id),collapsed:t})]})})})}function SG({account:e}){const t=ur(e),a=w$(e);!t&&f$(e,Rl(e))&&a.push({text:r("Token 限额 · 已禁用"),kind:"bad",title:r("本周期 Token 消耗达到限制器的上限,账号已被自动禁用(窗口重置也不恢复,需手动启用)")});const o=hu(e)||e.id;return s.jsxs(s.Fragment,{children:[s.jsx(ns,{checked:t,"aria-label":r("{action}{who}",{action:r(t?"禁用":"启用"),who:o}),title:r(t?"已启用,点击禁用(不参与转发)":"已禁用,点击启用"),onCheckedChange:c=>{CR(e.id,c)}}),a.length?s.jsx("div",{className:"status-tags",children:a.map(c=>s.jsx(ze,{shape:"tag",variant:c.kind==="bad"?"destructive":"secondary",title:c.title,children:c.text},c.text))}):null]})}function NG({account:e,open:t}){const a=bj(e);if(!a.length)return Qh(e,Wa(e))?s.jsx(ze,{variant:"warning",shape:"tag",title:r("余额低于限制器的阈值,转发时会跳过该账号(余额回升自动恢复)"),children:r("余额不足")}):fj(e,Rl(e))?s.jsx(ze,{variant:"warning",shape:"tag",title:r("本周期 Token 消耗达到限制器的上限,转发时会跳过该账号(窗口重置自动恢复)"),children:r("Token 限额")}):s.jsxs(ze,{variant:"success",shape:"tag",title:r("当前没有任何模型处于限流中"),children:[s.jsx(my,{}),r("正常")]});const o=jj(a[0].resetAt);return s.jsxs(s.Fragment,{children:[s.jsx(ze,{variant:"warning",shape:"tag",render:s.jsx("button",{type:"button"}),className:t?"ring-2 ring-warning-soft":void 0,title:r("点击{action}各模型的限流明细",{action:r(t?"收起":"查看")}),onClick:()=>dR(e.id,"limits",!t),children:r("{n} 个模型 ▾",{n:a.length})}),s.jsx("span",{className:"lim-sub",title:r("最早恢复"),children:r("最早 {soonest} 恢复",{soonest:o===yj?r("待定"):o})})]})}function kG({account:e}){const t=cr(An(e)),a=MA(t.edition?e.expiresAt:e[t.expiry]);if(!a)return s.jsx("span",{className:"muted",title:r("记录里没有过期时间"),children:"—"});const o=a-Date.now();if(o<=0)return s.jsx(ze,{variant:"destructive",shape:"tag",title:r("凭证已过期,转发时会先刷新"),children:r("已过期")});const c=o<36e5?r("{n} 分钟后",{n:Math.max(1,Math.round(o/6e4))}):o<48*36e5?r("{n} 小时后",{n:(o/36e5).toFixed(1)}):r("{n} 天后",{n:Math.floor(o/24/36e5)}),d=Ls(a);return s.jsx("span",{title:d?r("{text}过期({full})",{text:c,full:d}):r("{text}过期",{text:c}),children:c})}function CG({account:e}){const t=xR(e.id);return t<=0?null:s.jsx("span",{className:"conn-count",title:r("{n} 个请求正在使用该账号(含还在下发内容的流式请求)",{n:t}),children:t})}function TG(e){return!e||typeof e!="object"||Array.isArray(e)?"":gO(e.subscription)}function gO(e){if(!e||typeof e!="object")return"";const t=e,a=[];t.planName&&a.push(r("套餐 {name}",{name:String(t.planName)})),t.status&&a.push(r("状态 {status}",{status:String(t.status)})),t.state&&String(t.state)!=="active"&&a.push(qA(t.state));const o=t.expireAt;if(o!=null&&o!==""){const c=Number(o),d=Number.isFinite(c)&&c>1e11?Ls(c):String(o);d&&a.push(r("到期 {time}",{time:d}))}return Number.isFinite(Number(t.remainQuota))&&a.push(r("余量 {value}",{value:ss(t.remainQuota)})),Number.isFinite(Number(t.totalQuota))&&a.push(r("总量 {value}",{value:ss(t.totalQuota)})),a.join(" ")}function EG(e){if(!e||typeof e!="object"||Array.isArray(e))return[];const t=e.plans;return Array.isArray(t)?t.filter(a=>a&&typeof a=="object"&&String(a.state||"")==="pending"):[]}function AG(e){const t=e.map(a=>{const o=String(a.name||a.planId||r("套餐")),c=Number(a.pendingUntil??a.startsAt);return Number.isFinite(c)&&c>0?r("{name}({time} 起生效)",{name:o,time:Ls(c*1e3)}):o});return r(`已领到、还没到生效时间的套餐(生效后余额列会按它显示): {lines}`,{lines:t.join(` -`)})}function TG(e){if(e===void 0)return{text:r("未查询"),kind:"muted",title:r("尚未查询该账号的余额")};if(e===null)return{text:r("查询中…"),kind:"muted",title:r("正在查询")};const t=gu(e);if(t)return t.notConfigured?{text:r("未配置"),kind:"muted",title:r("{message}(去该账号的「设置」里填上查询凭证即可)",{message:t.message})}:{text:r("查询失败"),kind:"bad",title:t.message};if(typeof e!="object"||e===null)return{text:r("无数据"),kind:"muted",title:String(e)};const a=e;if(Object.prototype.hasOwnProperty.call(a,"totalLeft")){const o=a.unlimited?"∞":ss(a.totalLeft);return{text:r("可用 {total}",{total:o}),kind:"ok",title:r("总剩余 {total} · 套餐 {plan} · 奖励 {bonus}",{total:o,plan:ss(a.planLeft),bonus:ss(a.bonusLeft)})}}if(Object.prototype.hasOwnProperty.call(a,"available")||Array.isArray(a.wallets)){const o=String(a.unit||r("积分")),c=Array.isArray(a.wallets)?a.wallets:[],d=c.map(j=>`${j?.displayName||j?.type||r("明细")} ${j?.balanceView?String(j.balanceView):ss(j?.balance)}`).join(" · "),f=gO(a.subscription),p=[];if(c.length===0&&Array.isArray(a.plans)&&a.plans.length>0){const j=xO(a);p.push(j&&bO(j)?r("读数里没有额度桶,这里按套餐权益里的已用 / 总量显示(上游给的数)"):r("上游的余额读数里没有这个套餐的额度桶,剩余量读不到;余额列显示的是套餐发放的权益额度,不是剩余量"))}String(a.walletsFrom||"")==="monitor"&&p.push(r("额度窗口读数来自开放平台监控通道(套餐 JWT 那条通道没有额度桶):读的是每 N 小时 / 每周窗口的剩余比例"));const h=a.available==null?c.find(j=>j?.balance!=null):void 0,b=a.availableView?String(a.availableView):h?r("可用 {value} {unit}",{value:ss(h.balance),unit:String(h.unit||o)}):r("可用 {value} {unit}",{value:ss(a.available),unit:o}),y=[a.statisticsError?r("订阅统计未读到:{error}",{error:String(a.statisticsError)}):"",a.benefitError?r("福利网关未读到:{error}",{error:String(a.benefitError)}):""].filter(Boolean),v=a.benefitAbsent?[r("该账号没有福利模型额度(福利按活动下发,不是每个账号都有)")]:[];return{text:b+(y.length?" ⚠":""),kind:y.length?"warn":"ok",title:[b,d,f,...p,...v,...y].filter(Boolean).join(" · ")}}return{text:r("无数据"),kind:"muted",title:r("未返回可识别的余额数据")}}function xO(e){const t=Array.isArray(e.plans)?e.plans:[],a=o=>String(o?.state||"");return t.find(o=>a(o)==="active")||t.find(o=>a(o)==="pending")||t[0]||null}function EG(e){const t=Array.isArray(e.entitlements)?e.entitlements:[];let a=null;for(const o of t){const c=Number(o?.grantUnits);if(!Number.isFinite(c)||c<=0)continue;const d=String(o?.unitType||"").trim();(!a||c>a.units)&&(a={units:c,unit:d})}return a}function bO(e){const t=Array.isArray(e.entitlements)?e.entitlements:[];for(const a of t){const o=Sr(a?.totalUnits);if(o===null||o<=0)continue;const c=Sr(a?.usedUnits),d=Sr(a?.remainingUnits),f=c===null?d===null?null:Math.max(0,o-d):c;if(f!==null)return{used:f,total:o,unit:String(a?.unitType||"").trim()}}return null}function AG(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const t=e,a=Array.isArray(t.wallets)?t.wallets:[];let o=null;for(const p of a){const h=Number(p?.total);!Number.isFinite(h)||h<=0||(!o||h>Number(o.total))&&(o=p)}const c=t.subscription&&typeof t.subscription=="object"?t.subscription:{};if(!o){let p=null;for(const j of a){const w=Sr(j?.remainingPercent);w!==null&&(!p||w0?r("重置 {date}",{date:Ls(w)}):"";o.push({name:b,view:j,tone:v===null?"muted":v<20?"warn":"ok",percent:v,over:!1,title:[d||[b,h].filter(Boolean).join(" "),j,S].filter(Boolean).join(" · "),variant:"compact",percentText:v===null?"":`${Math.round(v)}%`,windowShort:h,id:String(c.key||"").trim()||b})}return o}function MG(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const t=e.subscription&&typeof e.subscription=="object"?e.subscription:{},a=String(t.planName||"").trim();return a?{text:KA(a),free:hj(a)}:null}function Oo(e){const t=Number.isFinite(e)?Number(e.toFixed(2)):e,a=window.wbUnits;return a?.formatTokens?a.formatTokens(t):String(t)}function DG(e){return e.includes("福利")?r("福利日额度"):e.includes("对话")?r("对话 token"):e}function IG(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const t=e,a=Array.isArray(t.wallets)?t.wallets:[];if(!a.length)return null;const o=String(t.unit||r("积分")),c=[],d=[];for(const h of a){const b=String(h.displayName||h.type||r("明细")),y=Sr(h.creditRemaining),v=Sr(h.creditTotal),j=String(h.balanceView||"")||ss(h.balance),w=Sr(h.usedPercent);if(y!==null){const S=v!==null&&v>0,N=S?Math.max(0,Math.min(100,y/v*100)):null;c.push({name:b,view:S?r("剩 {remaining} / {total}",{remaining:ss(y),total:ss(v)}):r("剩 {remaining}",{remaining:ss(y)}),tone:y<=0?"muted":N!==null&&N<20?"warn":"ok",percent:N,over:!1,title:S?void 0:r("上游未给总量,画不出剩余比例")});continue}if(w!==null||Sr(h.allowanceTokens)!==null){const S=Sr(h.usedTokens)??0,N=Sr(h.allowanceTokens),k=N!==null&&N>1,A=k&&S>N,P=h.unlimited===!0,R=k?Math.max(0,Math.min(100,(1-S/N)*100)):null,C=P?r("无上限 · 已用 {used}",{used:Oo(S)}):k?r("剩 {remaining} / {total}",{remaining:Oo(Math.max(0,N-S)),total:Oo(N)}):S>0?r("已用 {used}",{used:Oo(S)}):r("未使用");d.push({name:DG(b),view:C,tone:A?"bad":!k&&!P?"muted":R!==null&&R<20?"warn":"ok",percent:R,over:A,title:[j,P?r("无上限按日计(上游到达上限后仍继续计数,超额如实显示)"):null].filter(Boolean).join(" · ")});continue}d.push({name:b,view:j,tone:"muted",percent:null,over:!1,title:j})}const f=t.subscription&&typeof t.subscription=="object"?t.subscription:{},p=[t.statisticsError?r("订阅统计未读到:{error}",{error:String(t.statisticsError)}):"",t.benefitError?r("福利网关未读到:{error}",{error:String(t.benefitError)}):""].filter(Boolean);return{credit:c,token:d,total:Sr(t.available),unit:o,plan:KA(f.planName),free:hj(f.planName),resetDate:String(f.resetDate||""),missing:p,benefitAbsent:t.benefitAbsent===!0}}function PG(e){const t=Rl(e);let a=null;for(const v of c$(e)){if(v.enabled===!1)continue;const j=Nf(v,t);if(!j)continue;const w=v.threshold>0?j.used/v.threshold:0;if(w>=1){a={rule:v,reading:j,ratio:w};break}(!a||w>a.ratio)&&(a={rule:v,reading:j,ratio:w})}if(!a)return null;const{rule:o,reading:c}=a,d=c.used>=o.threshold,f=pj(o),p=d?"":` · ${Zh(f.remainingMs,f.daily)}`,h=f.daily?r("今日已用"):r("周期 Token"),b=o.reset==="daily"?r("今天(本地 0 点重置)"):r("{period}窗口",{period:HA(o.period??0)}),y=r("限制器:{window}已用 {used} / {threshold} Token",{window:b,used:Math.round(c.used).toLocaleString("en-US"),threshold:Math.round(o.threshold).toLocaleString("en-US")})+(d?r(",已达到上限"):r(",{countdown}重置",{countdown:Zh(f.remainingMs,f.daily)}));return s.jsxs("span",{className:"usage-token-sub",title:y,children:[h," ",s.jsx("span",{className:d?"hit":void 0,children:El(c.used)})," ","/ ",El(o.threshold),p]})}function _G({account:e}){if(!Ea(e))return s.jsx("span",{className:"muted",title:r("该提供商没有余额查询"),children:"—"});const t=Wa(e),a=TG(t),o=Qh(e,t)?s.jsx(ze,{variant:"warning",shape:"tag",title:r("余额低于限制器的阈值,转发时会跳过该账号(余额回升自动恢复)"),children:r("余额不足 · 已跳过")}):fj(e,Rl(e))?s.jsx(ze,{variant:"warning",shape:"tag",title:r("本周期 Token 消耗达到限制器的上限,转发时会跳过该账号(窗口重置自动恢复)"),children:r("Token 限额 · 已跳过")}):null,c=PG(e),d=a.kind==="ok"||a.kind==="warn"?kG(t):[],f=d.length?s.jsx(ze,{variant:"warning",shape:"tag",title:CG(d),children:r("待生效 {n} 份",{n:d.length})}):null,p=a.kind==="ok"||a.kind==="warn"?Q6(t):[],h=GA(e)&&p.length?{rows:p,subscriptionLine:NG(t),kind:a.kind,title:a.title}:null,b=a.kind==="ok"||a.kind==="warn"?OG(t):[];if(b.length){const j=MG(t);return s.jsxs("span",{className:"usage-cell",title:a.title,children:[j?s.jsx("span",{className:"usage-plan",children:s.jsx("span",{className:`plan-chip${j.free?" free":""}`,children:j.text})}):null,b.map(w=>s.jsx(vO,{row:w},w.id||w.name)),o,f,c]})}if(x$(e)){const w=a.kind==="ok"||a.kind==="warn"?IG(t):null;return w?s.jsx(LG,{summary:a,detail:w,blockedBadge:o,tokenSub:c}):s.jsxs("span",{className:"usage-sum-wrap",children:[s.jsx("span",{className:`usage-sum ${a.kind}`,title:a.title,children:a.text}),o,f,c]})}const y=a.kind==="ok"||a.kind==="warn"?AG(t):null;if(!y)return s.jsxs("span",{className:"usage-sum-wrap",children:[h?s.jsx(AA,{variant:"sum",...h,children:a.text}):s.jsx("span",{className:`usage-sum ${a.kind}`,title:a.title,children:a.text}),o,f,c]});const v=s.jsxs(s.Fragment,{children:[y.percent!==null?s.jsx(cf,{value:y.percent,className:"usage-pool-bar"}):null,s.jsx("span",{className:`usage-pool-view ${a.kind}`,children:y.text})]});return s.jsxs("span",{className:"usage-pool",title:a.title,children:[y.planName?s.jsx("span",{className:"usage-pool-name",children:y.planName}):null,h?s.jsx(AA,{variant:"pool",...h,children:v}):s.jsx("span",{className:"usage-pool-line",children:v}),o,f,c]})}function LG({summary:e,detail:t,blockedBadge:a,tokenSub:o}){const[c,d]=x.useState(!1),f=e.kind==="ok"||e.kind==="warn";return s.jsxs("span",{className:c?"usage-cell open":"usage-cell",children:[s.jsxs("span",{className:"usage-plan",children:[t.plan?s.jsx("span",{className:`plan-chip${t.free?" free":""}`,children:t.plan}):null,t.resetDate?s.jsx("span",{className:"plan-sub",title:r("套餐积分的重置日期"),children:r("重置 {date}",{date:t.resetDate})}):null]}),s.jsxs(wf,{open:c,onOpenChange:d,children:[s.jsxs(Kh,{render:s.jsx("button",{type:"button",className:`usage-toggle ${e.kind}`,title:e.title,"aria-label":r("积分明细")}),children:[s.jsx("span",{className:"val",children:e.text}),s.jsx("span",{className:"caret","aria-hidden":!0,children:"▾"})]}),s.jsxs(Sf,{align:"end",sideOffset:6,className:"usage-pop w-[288px]",children:[f&&t.missing.length?t.missing.map(p=>s.jsx("div",{className:"usage-pop-missing",title:p,children:p},p)):null,t.credit.length?s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"usage-pop-group",children:r("积分")}),t.credit.map(p=>s.jsx(zG,{row:p},p.name))]}):null,t.total!==null?s.jsxs("div",{className:"usage-pop-total",children:[s.jsx("span",{className:"k",children:r("可用合计")}),s.jsxs("span",{className:"v",children:[ss(t.total)," ",s.jsx("small",{children:t.unit})]})]}):null,!t.credit.length&&t.total===null?s.jsx("div",{className:"usage-pop-empty",title:r("该账号的额度只有福利 token 池(见余额格那两行),订阅统计没有积分类计量表"),children:r("该账号没有积分类读数(额度只有福利 token 池)")}):null]})]}),t.token.map(p=>s.jsx(vO,{row:p},p.name)),t.benefitAbsent?s.jsxs("span",{className:"usage-flat-line",title:r("福利按限时活动下发,不是每个账号都有 —— 正常状态,不是查询失败"),children:[s.jsx("span",{className:"usage-flat-name",children:r("福利日额度")}),s.jsx("span",{className:"usage-flat-view muted",children:r("无福利额度")})]}):null,a,o]})}function zG({row:e}){return s.jsxs("div",{className:"usage-pop-row",title:e.title,children:[s.jsxs("div",{className:"usage-pop-line",children:[s.jsx("span",{className:"usage-pop-name",children:e.name}),s.jsx("span",{className:`usage-pop-view ${e.tone}`,children:e.view})]}),e.percent!==null?s.jsx("span",{className:`usage-pop-track${e.over?" over":""}`,children:s.jsx("i",{style:{width:`${e.percent}%`},"aria-label":r("剩余 {percent}%",{percent:Math.round(e.percent)})})}):null]})}function BG({account:e,namesHidden:t}){const a=Xh(e),o=cr(An(e)),c=hu(e)||r("未命名账号"),d=String(e.email||"").trim(),f=String(e.nickname||"").trim(),p=String(e.name||"").trim(),h=w=>t?L$(w):w,b=[d&&d!==c?r("邮箱 {email}",{email:h(d)}):"",a?`${o.identifier} ${a}`:"",!e.nameCustom&&p&&p!==d&&p!==f?r("备注名 {name}",{name:h(p)}):"",f&&f!==c&&f!==d?r("昵称 {name}",{name:h(f)}):"",kf(e)?r("桌面端实时登录态(凭证每次从客户端登录态文件读取)"):"",e.updatedAt?r("更新于 {time}",{time:Ls(e.updatedAt)}):"",e.source?r("来源 {source}",{source:e.source==="imported"?r("旧数据导入"):r("手动添加")}):""].filter(Boolean),y=d&&d!==c,v=e.proxy?.error,j=b.length?s.jsx(dV,{delay:0,children:s.jsxs(Ui,{children:[s.jsx(Vi,{render:s.jsx("div",{className:"acct-name"}),children:s.jsx("span",{className:"name",children:h(c)})}),s.jsxs(Fi,{children:[s.jsx(fV,{}),b.map((w,S)=>s.jsx("div",{children:w},S))]})]})}):s.jsx("div",{className:"acct-name",children:s.jsx("span",{className:"name",children:h(c)})});return s.jsxs(s.Fragment,{children:[j,y?s.jsx("div",{className:"acct-sub",children:s.jsx("span",{className:"acct-email",title:r("账号邮箱"),children:h(d)})}):null,v?s.jsx("div",{className:"acct-note bad",title:r("代理不可用:{error}",{error:v}),children:r("代理不可用:{error}",{error:v})}):null]})}function HG({account:e}){const t=e.proxy,a=t?.config?.source||t?.source,o=t?.label||r(a==="custom"?"自定义代理":"已设置"),c=t?.error,d=H$(),f=Array.isArray(d)?d:[];let p="";a==="pool"&&t?.config?.proxyId?p=`${Qa}${t.config.proxyId}`:t&&(p=cg);const h=[{value:"",label:r("直连")}];for(const v of f)h.push({value:`${Qa}${v.id}`,label:du(v)});if(d===null?h.push({value:"__hint_pool__",label:r("正在读取代理列表…"),disabled:!0}):f.length||h.push({value:"__hint_pool__",label:U$()?r("代理列表读取失败(重试中)"):r("还没有代理(去「网络代理」页添加)"),disabled:!0}),p===cg){const v=a==="clash"?r("Clash 出口:"):a==="custom"?r("自定义:"):"";h.push({value:cg,label:`${v}${o}${c?r("(不可用)"):""}`})}else a==="pool"&&!f.some(v=>`${Qa}${v.id}`===p)&&h.push({value:p,label:`${o}${c?r("(不可用)"):""}`});h.push({value:AR,label:r("自定义代理…")});const b=c?r("代理不可用:{error}(转发时会回退直连);选「自定义代理…」去修改",{error:c}):a==="rotate"?r("当前:{label}(每条请求换一个出口);「自定义代理…」打开完整设置",{label:o}):a==="clash"||a==="custom"?r("当前:{label}(未经过代理池 —— 可在「网络代理」页把出口同步进池后来这里改选)",{label:o}):r("当前:{label};选项来自「网络代理」页;「自定义代理…」打开完整设置",{label:t?o:r("直连")}),y=h.find(v=>v.value===p);return s.jsxs(ut,{value:p,onValueChange:v=>{W$(e.id,String(v),p)},children:[s.jsx(ft,{className:Ue("w-full",c&&"border-destructive-bd"),title:b,"aria-label":r("出网代理"),children:s.jsx(dt,{children:y?.label||r("直连")})}),s.jsx(pt,{children:h.map(v=>s.jsx($e,{value:v.value,disabled:v.disabled,children:v.label},v.value))})]})}function UG({account:e,atFront:t}){const[a,o]=x.useState(!1),c=Ea(e),d=FA(e);return s.jsxs("div",{className:"acct-actions",children:[d?s.jsx(ne,{variant:"outline",size:"xs",title:h$(e)?g$(e):r("查看可领取的套餐与名下已有的套餐(每天一期,领取需过一次人机验证)"),onClick:()=>RR(e.id),children:r("领套餐")}):null,c?s.jsx(ne,{variant:"outline",size:"xs",disabled:a,title:r("查询该账号剩余余额(读数显示在余额列)"),onClick:()=>{o(!0),Q$(e.id).finally(()=>o(!1))},children:r("余额")}):null,s.jsx(ne,{variant:"outline",size:"xs",title:r("备注名 / 启用 / 代理"),onClick:()=>lg(e.id),children:r("设置")}),s.jsx(VG,{account:e,atFront:t})]})}function VG({account:e,atFront:t}){const[a,o]=x.useState(!1),c=ur(e),d=Number(e.maxConcurrent)||0,f="w-full justify-start px-2 font-normal",p=`${f} text-destructive hover:text-destructive`;return s.jsxs(wf,{open:a,onOpenChange:o,children:[s.jsx(Kh,{render:s.jsx(ne,{variant:"outline",size:"xs",title:r("更多操作")}),children:"⋯"}),s.jsx(Sf,{align:"end",sideOffset:4,className:"w-[172px] p-1.5",children:s.jsxs("div",{className:"flex flex-col gap-0.5",children:[s.jsx(ne,{variant:"ghost",size:"sm",className:p,onClick:()=>{o(!1),CR(e.id,!c)},children:r(c?"禁用":"启用")}),s.jsx(ne,{variant:"ghost",size:"sm",className:f,disabled:t,title:r(t?"已在全局队列第一位":"仅将优先级调整到全局第一位,不改变启用状态"),onClick:()=>{o(!1),Pe().wbApp?.runAccountAction?.("switch",e.id)},children:r("设为首选")}),s.jsx(ne,{variant:"ghost",size:"sm",className:f,title:r("在账号设置的「基本」分组里设置该账号同时最多处理的请求数"),onClick:()=>{o(!1),lg(e.id)},children:r("并发上限:{value}",{value:d>0?d:r("不限")})}),e.hasRefreshToken?s.jsx(ne,{variant:"ghost",size:"sm",className:f,onClick:()=>{o(!1),Pe().wbApp?.runAccountAction?.("refresh",e.id)},children:r("刷新 Token")}):null,s.jsx("div",{className:"my-1 h-px bg-hairline"}),s.jsx(ne,{variant:"ghost",size:"sm",className:p,title:kf(e)?r("删除这条账号记录(不会影响客户端自己的登录态;之后可再点「导入桌面端登录态」加回来)"):void 0,onClick:()=>{o(!1),Pe().wbApp?.runAccountAction?.("remove",e.id)},children:r("删除账号")})]})})]})}function FG({account:e,onClose:t,onClear:a}){const o=bj(e),c=s.jsx(ne,{variant:"ghost",size:"icon-xs",className:"panel-close",title:r("收起"),onClick:t,children:"✕"});return o.length?s.jsxs("div",{className:"row-panel limit-panel",children:[c,s.jsxs("div",{className:"lp-head",children:[s.jsx("b",{children:hu(e)}),s.jsx("span",{className:"muted",children:r("{n} 个模型限流中 · 记录来自上游 429 / 限额码,到恢复时间自动解除",{n:o.length})}),s.jsx(ne,{variant:"outline",size:"xs",title:r("清掉该账号全部模型的限流标记"),onClick:()=>a(""),children:r("全部清除")})]}),o.map(d=>{const f=jj(d.resetAt),p=d.message||(d.status?r("上游返回 {status}",{status:d.status}):"");return s.jsxs("div",{className:"lp-row",children:[s.jsx("span",{className:"lp-model",title:d.model,children:d.model}),s.jsx(ze,{variant:"warning",shape:"tag",children:r("限流中")}),s.jsx("span",{className:"lp-reset",title:r("到恢复时间后自动解除,无需手动操作"),children:f===yj?r("恢复时间未知"):r("{time} 恢复",{time:f})}),s.jsx("span",{className:"lp-reason",title:p,children:p}),s.jsx(ne,{variant:"outline",size:"2xs",title:r("清掉本机的限流标记,立刻重新尝试该模型(上游若仍在限流会再次被标记)"),onClick:()=>a(d.model),children:r("清除标记")})]},d.model)})]}):s.jsxs("div",{className:"row-panel limit-panel",children:[r("当前没有限流中的模型。"),c]})}function $G({account:e,colSpan:t,limitsOpen:a,onClear:o}){return a?s.jsx("tr",{className:"acct-panels","data-panels-for":e.id,children:s.jsx("td",{colSpan:t,children:s.jsx(FG,{account:e,onClose:()=>dR(e.id,"limits",!1),onClear:c=>o(e.id,c)})})}):null}function qG({namesHidden:e,allPicked:t,somePicked:a,disabled:o,onToggleAll:c}){const d=s.jsx("span",{className:"col-grip",title:r("拖动调整列宽(双击还原)")});return s.jsx("thead",{children:s.jsxs("tr",{children:[s.jsx("th",{className:"cell-pick","data-col":"pick",children:s.jsx(Pr,{id:"acct-select-all",checked:t,indeterminate:!t&&a,disabled:o,"aria-label":r("全选当前筛选结果"),title:r("全选 / 取消全选当前筛选结果(与批量栏同一个选择)"),onCheckedChange:f=>c(f)})}),s.jsxs("th",{className:"cell-order","data-col":"order",children:[s.jsx("span",{className:"th-label",title:r("组内先后:按住左边的把手上下拖动即可调整;整组的先后拖分组带的把手"),children:r("顺序")}),d]}),s.jsxs("th",{className:"cell-account","data-col":"account",children:[s.jsxs("span",{className:"th-label",children:[r("账号"),s.jsx(GG,{hidden:e})]}),d]}),s.jsxs("th",{className:"cell-proxy","data-col":"proxy",children:[s.jsx("span",{className:"th-label",title:r("该账号出网走的代理(Clash 出口 / 自定义 / 直连);点击可修改"),children:r("代理")}),d]}),s.jsxs("th",{className:"cell-connections","data-col":"connections",children:[s.jsx("span",{className:"th-label",title:r("此刻正在使用这个账号的请求数(含还在下发内容的流式请求);为 0 时不显示"),children:r("连接数")}),d]}),s.jsxs("th",{className:"cell-status","data-col":"status",children:[s.jsx("span",{className:"th-label",children:r("状态")}),d]}),s.jsxs("th",{className:"cell-limits","data-col":"limits",children:[s.jsx("span",{className:"th-label",title:r("该账号当前限流中的模型;点徽章看明细"),children:r("限流")}),d]}),s.jsxs("th",{className:"cell-expiry","data-col":"expiry",children:[s.jsx("span",{className:"th-label",children:r("有效期")}),d]}),s.jsxs("th",{className:"cell-usage","data-col":"usage",children:[s.jsx("span",{className:"th-label",children:r("余额")}),d]}),s.jsx("th",{className:"cell-actions","data-col":"actions",children:s.jsx("span",{className:"th-label",children:r("操作")})})]})})}function GG({hidden:e}){const t=r(e?"显示账号名":"隐藏账号名(名字显示为星号)");return s.jsx("button",{type:"button",className:`name-eye${e?" on":""}`,title:t,"aria-label":t,"aria-pressed":e,onClick:()=>_$(),dangerouslySetInnerHTML:{__html:Cg(e?"eyeOff":"eye",13)}})}const yO="dragging",Bf=["drop-before","drop-after"];let Ja=null;function jO(e){e.querySelectorAll(`.${Bf[0]}, .${Bf[1]}`).forEach(t=>{t.classList.remove(...Bf)})}function KG(e,t){if(t==="group"){const o=e.parentElement;return o?[...o.children].filter(c=>c.classList.contains("acct-group")):[]}const a=e.parentElement;return a?[...a.children].filter(o=>o.classList.contains("acct-row")):[]}function wO(e,t){for(let a=0;aa!==e.item);jO(e.container),t.length&&(e.index>=t.length?t[t.length-1].classList.add(Bf[1]):t[e.index].classList.add(Bf[0]))}function SO(e){if(!Ja)return;if(e.buttons===0){Tg(!1);return}const t=Ja.items.filter(o=>o!==Ja.item),a=wO(t,e.clientY);a!==Ja.index&&(Ja.index=a,YG(Ja))}function Tg(e){const t=Ja;t&&(Ja=null,window.removeEventListener("pointermove",SO),window.removeEventListener("pointerup",NO),window.removeEventListener("pointercancel",kO),window.removeEventListener("keydown",CO),document.body.classList.remove("queue-dragging"),t.item.classList.remove(yO),jO(t.container),e&&(t.kind==="group"?Qj?.onDrop({kind:"group",provider:t.provider,index:t.index}):Qj?.onDrop({kind:"row",provider:t.provider,accountId:t.accountId,index:t.index})))}function NO(){Tg(!0)}function kO(){Tg(!1)}function CO(e){e.key==="Escape"&&Tg(!1)}let Qj=null;function XG(e,t){Qj=t,e&&e.dataset.queueDragBound!=="1"&&(e.dataset.queueDragBound="1",e.addEventListener("pointerdown",a=>{if(a.button!==0||Ja)return;const o=a.target,c=o?.closest?.(".queue-grip"),d=o?.closest?.(".order-grip");if(!c&&!d)return;if(a.preventDefault(),c&&t.isLocked()){t.onLockedAttempt();return}const f=(c??d).closest("tbody.acct-group, tr.acct-row");if(!f)return;const p=c?"group":"row",h=f.dataset.provider||f.closest("tbody.acct-group")?.dataset.provider||"",b=f.dataset.id||"";if(p==="row"&&!b)return;const y=KG(f,p);if(y.length<2)return;const v=y.filter(j=>j!==f);Ja={kind:p,provider:h,accountId:b,item:f,container:f.parentElement||e,items:y,index:wO(v,a.clientY)},f.classList.add(yO),document.body.classList.add("queue-dragging"),window.addEventListener("pointermove",SO),window.addEventListener("pointerup",NO),window.addEventListener("pointercancel",kO),window.addEventListener("keydown",CO)}))}let Eg=null,TO=!1,Zj=0,Ag=null,EO="";function AO(){return Eg?Eg.apply(Oj):Oj}function RO(){window.wbAccountsView?.render?.()}function QG(){const e=AO(),t=`${Ag?"T":"F"}|${e.map(o=>`${o.key}:${o.align}`).join("|")}`;if(t===EO)return;EO=t,Pe().wbColSettings?.syncStaticHead("accounts",Ag);const a=[...Ag?.querySelectorAll("thead th")||[]];a.forEach((o,c)=>{const d=o.querySelector(".col-grip");d&&(d.style.display=c===a.length-1?"none":"")})}function ZG(){const e=x.useSyncExternalStore(sR,Gs),t=x.useRef(null),[,a]=x.useReducer(B=>B+1,0),o=!!Sj(),c=Al(),{groups:d,allGroups:f}=k$(Wa,Rl),p=C$(Wa,Rl),h=[{value:"all",label:r("全部({n})",{n:c.length})}].concat(rR().map(B=>({value:B.id,label:r("{label}({count})",{label:r(B.label),count:B.count})}))),b=d.flatMap(B=>B.accounts),y=T$(),v=AO(),j=hg(b.length,"accounts",{defaultSize:"all"}),w=j.paged?j.slice(b):b,S=ng()||j.paged,N=ng()?r("清除筛选"):r("把每页调回「全部」");x.useEffect(()=>{V$()}),x.useEffect(()=>{R$()},[e.version]),x.useLayoutEffect(()=>{if(!TO){if(!Pe().wbColSettings?.register){window.clearTimeout(Zj),Zj=window.setTimeout(()=>k(B=>B+1),50);return}TO=!0,Eg=Pe().wbColSettings?.register({id:"accounts",label:r("账号表"),columns:Oj.map(B=>({key:B.key,label:B.label,align:B.align,legacyAlign:B.legacyAlign})),mount:()=>document.querySelector("#batch-bar .batch-actions"),onChange:RO})||null,Eg&&a();return}QG()});const[,k]=x.useState(0);x.useEffect(()=>()=>window.clearTimeout(Zj),[]),x.useEffect(()=>{U8(t.current,RO)},[]);const A=b.map(B=>B.id),P=A.length>0&&A.every(B=>e.selected.has(B)),R=A.some(B=>e.selected.has(B)),C=[...e.selected].filter(B=>!A.includes(B)).length,E=e.selected.size>0,D=f.length>0&&f.every(B=>e.collapsed.has(B.provider)),I=new Map;for(const B of d)I.set(B.provider,w.filter(L=>An(L)===B.provider).map(L=>L.id));const O=x.useRef({displayed:d,all:f,renderedRows:I});x.useEffect(()=>{O.current={displayed:d,all:f,renderedRows:I}});const _=x.useRef({locked:!1,lockReason:"",onDrop:B=>{}}),z=x.useCallback(B=>{const{displayed:L,all:F}=O.current;if(B.kind==="group"){const Z=F.findIndex(te=>te.provider===B.provider);if(Z<0)return;const ae=F[Z],ce=F.filter((te,ke)=>ke!==Z),W=B.indexte.provider===W),ve=W===null||ue<0?ce.length:ue;if(ve===Z)return;const he=[...ce.slice(0,ve),ae,...ce.slice(ve)].flatMap(te=>te.members.map(ke=>ke.id));TR(he,r("{name} 移到第 {rank} 顺位",{name:ae.label,rank:ve+1}));return}const $=F.find(Z=>Z.provider===B.provider);if(!$)return;const X=$.members.findIndex(Z=>Z.id===B.accountId);if(X<0)return;const K=$.members[X],q=$.members.filter((Z,ae)=>ae!==X),Q=O.current.renderedRows.get(B.provider)||[],G=B.indexZ.id===G),Y=G===null||J<0?q.length:J;if(Y===X)return;const re=[...q.slice(0,Y),K,...q.slice(Y)],U=F.map(Z=>Z.provider===B.provider?{...Z,members:re}:Z).flatMap(Z=>Z.members.map(ae=>ae.id));TR(U,r("{name} 移到 {group} 组内第 {rank} 位",{name:String(K.name||K.id),group:r($.label),rank:Y+1}))},[]);x.useEffect(()=>{_.current={locked:S,lockReason:N,onDrop:z}}),x.useEffect(()=>{XG(t.current,{isLocked:()=>_.current.locked,onLockedAttempt:()=>Le(r("当前视图不能调整组顺位:{reason} 之后再拖动",{reason:_.current.lockReason}),"err"),onDrop:B=>_.current.onDrop(B)})},[]);function H(B,L,F){switch(B){case"pick":return s.jsx(Pr,{checked:F.picked,"data-pick":L.id,title:r("勾选后可批量操作"),"aria-label":r("勾选后可批量操作"),onCheckedChange:$=>D$(L.id,$)});case"order":return s.jsx(xG,{account:L,rank:F.rank});case"account":return s.jsx(BG,{account:L,namesHidden:e.namesHidden});case"proxy":return s.jsx(HG,{account:L});case"connections":return s.jsx(SG,{account:L});case"status":return s.jsx(yG,{account:L});case"limits":return s.jsx(jG,{account:L,open:F.limitsOpen});case"expiry":return s.jsx(wG,{account:L});case"usage":return s.jsx(_G,{account:L});case"actions":return s.jsx(UG,{account:L,atFront:F.seat.position<=1});default:return null}}return s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"panel account-table",children:[s.jsxs("div",{className:"toolbar",children:[s.jsxs("div",{className:"group","data-provider-group":"1",children:[s.jsx("span",{className:"label",children:r("提供商")}),s.jsxs(ut,{value:e.filter.provider,onValueChange:B=>A$(String(B)),children:[s.jsx(ft,{className:"max-w-none min-w-0 w-full min-[768px]:max-w-[200px] min-[768px]:min-w-[130px] min-[768px]:w-auto","aria-label":r("按提供商筛选账号"),children:s.jsx(dt,{children:h.find(B=>B.value===e.filter.provider)?.label||r("全部")})}),s.jsx(pt,{children:h.map(B=>s.jsx($e,{value:B.value,children:B.label},B.value))})]})]}),s.jsx("div",{className:"divider","data-provider-divider":"1"}),s.jsxs("div",{className:"group",children:[s.jsx("span",{className:"label",children:r("搜索")}),s.jsx(Fe,{className:"acct-search",type:"search",value:e.filter.q,placeholder:r("账号名 / 邮箱 / 备注"),"aria-label":r("搜索账号"),onChange:B=>E$(B.currentTarget.value)})]}),s.jsx("div",{className:"divider"}),s.jsxs("div",{className:"group",children:[s.jsx("span",{className:"label",children:r("状态")}),s.jsx(En,{options:[{value:"all",label:r("全部"),count:p.enabledAll??0},{value:"enabled",label:r("启用"),count:p.enabled??0},{value:"disabled",label:r("禁用"),count:p.disabled??0}],value:e.filter.enabled,onValueChange:B=>aR("enabled",B),"aria-label":r("启用状态")})]}),s.jsxs("div",{className:"group",children:[s.jsx("span",{className:"label",children:r("限额")}),s.jsx(En,{options:[{value:"all",label:r("全部"),count:p.limitAll??0},{value:"normal",label:r("正常"),count:p.normal??0,disabled:e.filter.enabled==="disabled"},{value:"limited",label:r("已限流"),count:p.limited??0,disabled:e.filter.enabled==="disabled"}],value:e.filter.limit,onValueChange:B=>aR("limit",B),"aria-label":r("限额状态")})]}),s.jsxs("div",{className:"actions",children:[s.jsx(ne,{id:"btn-fold-groups",variant:"outline",disabled:!f.length,title:r(D?"展开全部分组":"折叠全部分组(只留组头)"),onClick:()=>O$(f.map(B=>B.provider),!D),children:r(D?"展开全部":"折叠全部")}),s.jsx(ne,{id:"btn-add-account-2",variant:"default",title:r("登录 / 导入一个新账号"),onClick:()=>{Pe().wbAddAccountModal?.open?.(),Pe().wbAccountAddForms?.syncAddProvider?.()},children:r("添加账号")}),s.jsx(ne,{id:"btn-query-usage",variant:"outline",disabled:e.usageBusy||!c.some(Ea),title:r("查询全部账号的余额(含已禁用账号 —— 禁用只表示不参与转发)"),onClick:()=>{NR()},children:e.usageBusy?r("查询中…"):r("查询余额")})]})]}),s.jsxs("div",{className:Ue("batch-bar",E&&"active"),id:"batch-bar",children:[s.jsxs("label",{className:"batch-select-all",children:[s.jsx(Pr,{id:"batch-select-all",checked:P,indeterminate:!P&&R,disabled:!A.length,"aria-label":r("全选当前筛选结果"),onCheckedChange:B=>cR(A,B)}),s.jsx("span",{id:"batch-select-label",children:A.length?r("全选当前筛选结果({n} 个)",{n:A.length}):r("没有可全选的账号")})]}),s.jsxs("span",{className:"batch-count",children:[r("已选")," ",s.jsx("b",{id:"batch-count",children:e.selected.size})," ",r("个 · 共")," ",s.jsx("b",{id:"accounts-count",children:c.length})," ",r("个")]}),s.jsx("span",{className:"batch-summary",id:"accounts-group-summary",children:d.length?r("{groups} 家 · 显示 {rows} 行",{groups:d.length,rows:A.length}):""}),S?s.jsx("span",{className:"batch-hidden",title:r("筛选 / 搜索 / 分页生效时组顺位不可拖动(别家的组整组不可见,落点没法定位)"),children:r("组顺位已锁定")}):null,ng()?s.jsx(ne,{id:"btn-clear-filter",variant:"ghost",size:"xs",title:r("清空搜索与三个筛选档位,回到全部账号"),onClick:()=>lR(),children:r("清除筛选")}):null,C?s.jsx("span",{className:"batch-hidden",id:"batch-hidden-hint",children:r("另有 {n} 个已勾选账号被当前筛选隐藏,仍会参与操作",{n:C})}):null,s.jsxs("div",{className:"batch-actions",children:[s.jsx(ne,{id:"btn-batch-open",variant:"outline",disabled:!E,onClick:()=>kj([...e.selected],"enable"),children:r("批量操作")}),s.jsx(ne,{id:"btn-batch-clear",variant:"outline",disabled:!E,onClick:()=>I$(),children:r("取消选择")})]})]}),s.jsx("div",{className:"acct-scroll",id:"account-list",ref:t,children:o?c.length?b.length?s.jsxs("table",{className:"acct-table",ref:WG,style:{minWidth:`${B8(v.map(B=>B.key))}px`},children:[s.jsx(JG,{}),s.jsx(qG,{namesHidden:e.namesHidden,allPicked:P,somePicked:R,disabled:!A.length,onToggleAll:B=>cR(A,B)}),d.map(B=>{const L=e.collapsed.has(B.provider),F=w.filter($=>An($)===B.provider);return s.jsxs("tbody",{className:"acct-group","data-provider":B.provider,children:[s.jsx(vG,{group:B,collapsed:L,colSpan:v.length}),L?null:F.map($=>{const X=B.members.findIndex(q=>q.id===$.id)+1,K=J$($,y,X>0?X:1);return s.jsxs(x.Fragment,{children:[s.jsx("tr",{className:Ue("acct-row",!ur($)&&"disabled",K.picked&&"selected"),"data-id":$.id,"data-provider":B.provider,"data-desktop":kf($)?"1":void 0,children:v.map(q=>s.jsx("td",{className:`cell-${q.key} ta-${q.align}`,children:H(q.key,$,K)},q.key))}),s.jsx($G,{account:$,colSpan:v.length,limitsOpen:uR($.id,"limits"),onClear:(q,Q)=>{Z$(q,Q)}})]},$.id)})]},B.provider)})]}):s.jsxs("div",{className:"empty",children:[r("当前筛选条件下没有账号"),ng()?s.jsx("button",{type:"button",className:"link-btn",onClick:()=>lR(),children:r("清除筛选")}):null]}):s.jsx("div",{className:"empty",children:r("暂无账号,请点击右上角「添加账号」")}):s.jsxs("div",{className:"empty",children:[s.jsx("span",{className:"spinner"}),r("正在加载…")]})}),s.jsx($i,{total:b.length,range:j.paged?{start:j.rangeStart,end:j.rangeEnd}:null,page:j.page,pageCount:j.pageCount,size:j.size,onSizeChange:j.setSize,onPageChange:j.goto})]}),s.jsx(O8,{})]})}function WG(e){Ag=e}function JG(){const e=z8(),t=a=>s.jsx("col",{"data-col":a,style:e[a]?{width:`${e[a]}px`}:void 0},a);return s.jsxs("colgroup",{children:[t("pick"),t("order"),t("account"),t("proxy"),t("connections"),t("status"),t("limits"),t("expiry"),t("usage"),t("actions")]})}const OO='.page[data-page="accounts"]';let Wj=null;function MO(){if(Wj)return;const e=document.querySelector(OO);e&&(e.replaceChildren(),Wj=jn.createRoot(e),Ts.flushSync(()=>{Wj.render(s.jsx(ZG,{}))}))}t8(),G$(),document.querySelector(OO)?MO():document.addEventListener("DOMContentLoaded",MO,{once:!0});const e7=64,t7=100,n7=r("OpenAI 兼容填到 /v1;Anthropic 填根地址"),s7=r("Anthropic 协议填根地址,不要带 /v1"),r7="https://open.bigmodel.cn/api/paas/v4",a7="https://api.anthropic.com",l7=[{value:"chat_completions",label:"OpenAI - Chat Completions"},{value:"responses",label:"OpenAI - Responses"},{value:"anthropic",label:"Anthropic - Messages"}],Cu="custom-protocol-select",Tu="custom-name-input",Eu="custom-baseurl-input",Hf="custom-apikey-input",Uf="custom-noauth-check",Au="custom-existing-select",Vf="custom-existing-apikey-input",Ff="custom-existing-noauth-check",$f="custom-existing-name-input",o7="custom-create-button",i7="custom-existing-button",c7="custom-existing-remove";let Jj=!1;const DO=()=>gt().wbProviders?.PROTOCOL_OPTIONS||l7,ew=()=>gt().wbProviders?.customList?.()||[],tw="custom-balance",nw="custom-existing-balance",qf=(e,t)=>`${e}-${t}-input`;function IO(e){if(!e)return"";const t=gt().wbPresetProviders?.presetOf?.(e);return String(t?.balance?.scheme||"")}function PO(e,t){const a=Mf(e);if(!a||!a.fields.length)return null;const o={};for(const c of a.fields){const d=dr(qf(t,c.key));d&&(o[c.key]=d)}return Object.keys(o).length?o:null}function _O(e,t){const a=Mf(e);if(a)for(const o of a.fields)To(qf(t,o.key),"")}function LO({scheme:e,prefix:t}){const a=Mf(e);return a?s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{children:r("余额查询")}),s.jsxs("div",{className:"col-start-2 row-start-1 flex h-[30px] items-center",children:[s.jsx("span",{className:"text-xs text-subtle",children:r("方案:{label}",{label:a.label})}),s.jsx("span",{className:"ml-1.5 text-xs text-subtle",children:a.endpoint})]}),s.jsx("span",{className:"hint",children:a.note})]}),a.fields.map(o=>s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{htmlFor:qf(t,o.key),children:o.label}),s.jsx(Fe,{id:qf(t,o.key),type:o.secret?"password":"text",autoComplete:o.secret?"new-password":"off",placeholder:o.placeholder,..._r(qf(t,o.key))}),s.jsx("span",{className:"hint",children:o.hint})]},o.key))]}):null}async function sw(e,t){if(!Jj){Jj=!0,e(!0);try{await t()}finally{Jj=!1,e(!1)}}}async function zO(e){gt().wbAddAccountModal?.close?.(),gt().wbProviders?.refreshCustom?.(),await gt().wbApp?.refresh?.(),Ln(e)}function Gf(e,t){To(e,t?"1":"")}function Rg(e){return dr(e)==="1"}function BO({checked:e,note:t,onChange:a}){return s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{children:r("鉴权")}),s.jsx("div",{className:"col-start-2 row-start-1 flex h-[30px] items-center",children:s.jsxs("label",{className:"inline-flex cursor-pointer items-center gap-2.5",children:[s.jsx(Pr,{checked:e,"aria-label":r("该上游无需鉴权"),onCheckedChange:o=>a(o===!0)}),s.jsx("span",{className:"text-xs text-subtle",children:r("该上游无需鉴权(不发送鉴权头)")})]})}),s.jsx("span",{className:"hint",children:t})]})}function u7({mode:e,providerHint:t,presetKey:a,showToken:o,version:c}){const d=DO(),[f,p]=x.useState(()=>dr(Cu)||d[0].value),[h,b]=x.useState(""),[y,v]=x.useState(""),[j,w]=x.useState(()=>dr(Au)),[S,N]=x.useState(()=>Rg(Uf)),[k,A]=x.useState(()=>Rg(Ff)),P=x.useMemo(()=>ew(),[c]),C=(t&&P.some(B=>B.id===t)?t:"")||(P.some(B=>B.id===j)?j:P[0]?.id||""),E=P.find(B=>B.id===C)?.name||r("该提供商");x.useEffect(()=>{To(Au,C)},[C]),x.useEffect(()=>{if(!a)return;const B=gt().wbPresetProviders?.presetOf?.(a);if(!B)return;const L=B.account?.noAuth===!0;if(e!=="create"){A(L),Gf(Ff,L);return}To(Tu,B.name||""),To(Eu,B.baseUrl||""),B.protocol&&(p(B.protocol),To(Cu,B.protocol)),N(L),Gf(Uf,L),b(B.hint||""),v(B.placeholder||"")},[e,a,t,o]);const D=f==="anthropic",I=h||(D?s7:n7),O=y||(D?a7:r7),_=e==="create"?IO(a):"",z=e==="existing"?String(P.find(B=>B.id===C)?.balanceScheme||""):"",H=(B,L)=>F=>{L(F),Gf(B,F)};return s.jsxs(s.Fragment,{children:[s.jsxs(dn,{hidden:e!=="create",children:[s.jsxs("div",{className:"add-panel-head",children:[s.jsx("h3",{children:r("上游信息")}),s.jsx("span",{children:r("创建这个提供商,并同时建立它的第一个账号。")})]}),s.jsxs("div",{className:"add-form",children:[s.jsxs("div",{className:"add-field",children:[s.jsxs(Je,{htmlFor:Tu,children:[r("名称"),s.jsx("i",{className:"req","aria-hidden":"true",children:"*"})]}),s.jsx(Fe,{id:Tu,type:"text",maxLength:e7,"aria-required":"true",placeholder:r("如:智谱 GLM"),..._r(Tu)}),s.jsx("span",{className:"hint",children:r("1~64 个字符,账号列表里按它分组显示")})]}),s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{htmlFor:Cu,children:r("协议")}),s.jsxs(ut,{value:f,onValueChange:B=>{const L=String(B);p(L),To(Cu,L),b("")},children:[s.jsx(ft,{id:Cu,className:"col-start-2 row-start-1 w-full",children:s.jsx(dt,{children:d.find(B=>B.value===f)?.label||f})}),s.jsx(pt,{children:d.map(B=>s.jsx($e,{value:B.value,children:B.label},B.value))})]})]}),s.jsxs("div",{className:"add-field",children:[s.jsxs(Je,{htmlFor:Eu,children:["Base URL",s.jsx("i",{className:"req","aria-hidden":"true",children:"*"})]}),s.jsx(Fe,{id:Eu,type:"text","aria-required":"true",placeholder:O,..._r(Eu)}),s.jsx("span",{className:"hint",id:"custom-baseurl-hint",children:I})]}),s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{htmlFor:Hf,children:"API Key"}),s.jsx(Fe,{id:Hf,type:"password",autoComplete:"new-password",placeholder:"sk-…",disabled:S,..._r(Hf)}),s.jsx("span",{className:"hint",children:r(S?"不需要 Key:转发与拉取模型都不发送鉴权头":"上游不要鉴权时留空,并勾选下面一项(留空又不勾会被当成未配置凭证)")})]}),s.jsx(BO,{checked:S,note:r(S?"已声明无需鉴权:账号会被正常选路,出网时不带任何鉴权头":"本地 Ollama、OpenCode Zen 免费档这类上游要勾上,否则账号不可用"),onChange:H(Uf,N)}),s.jsx(LO,{scheme:_,prefix:tw})]})]}),s.jsxs(dn,{hidden:e!=="existing",children:[s.jsxs("div",{className:"add-panel-head",children:[s.jsx("h3",{children:r("账号信息")}),s.jsxs("span",{children:[r("添加到 "),s.jsx("b",{children:E}),r(" 同一家可以放多把 key,按优先级轮换。")]})]}),s.jsxs("div",{className:"add-form",children:[s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{htmlFor:Au,children:r("提供商")}),s.jsxs(ut,{value:C,onValueChange:B=>w(String(B)),children:[s.jsx(ft,{id:Au,className:"col-start-2 row-start-1 w-full",children:s.jsx(dt,{children:E})}),s.jsx(pt,{children:P.map(B=>s.jsx($e,{value:B.id,children:B.name||B.id},B.id))})]})]}),s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{htmlFor:Vf,children:"API Key"}),s.jsx(Fe,{id:Vf,type:"password",autoComplete:"new-password",placeholder:"sk-…",disabled:k,..._r(Vf)}),s.jsx("span",{className:"hint",children:r(k?"不需要 Key:转发与拉取模型都不发送鉴权头":"上游不要鉴权时留空,并勾选下面一项(留空又不勾会被当成未配置凭证)")})]}),s.jsx(BO,{checked:k,note:r(k?"已声明无需鉴权:账号会被正常选路,出网时不带任何鉴权头":"同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)"),onChange:H(Ff,A)}),s.jsx(LO,{scheme:z,prefix:nw}),s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{htmlFor:$f,children:r("备注名")}),s.jsx(Fe,{id:$f,type:"text",maxLength:t7,placeholder:r("可选"),..._r($f)}),s.jsx("span",{className:"hint",children:r("留空则用提供商名称")})]})]})]})]})}function HO(e,t){const a=ko(t);Ln(r("添加失败:{reason}",{reason:a}),"err"),e.setHint(a)}async function d7(e){const t=dr(Tu),a=dr(Cu)||DO()[0].value,o=dr(Eu),c=dr(Hf),d=Rg(Uf);if(!t){Ln(r("请填写名称"),"err");return}if(!o){Ln(r("请填写 Base URL"),"err");return}await sw(e.setBusy,async()=>{const f={name:t,protocol:a,baseUrl:o},p=e.presetKey?gt().wbPresetProviders?.presetOf?.(e.presetKey):null,h=p?.quirks||{};h.urlSuffix&&(f.urlSuffix=h.urlSuffix),h.headers&&Object.keys(h.headers).length&&(f.headers={...h.headers}),h.anthropicToolType&&(f.anthropicToolType=h.anthropicToolType),p?.clientEmulation&&(f.clientEmulation=p.clientEmulation);const b=IO(e.presetKey);b&&(f.balanceScheme=b);const y=PO(b,tw);y&&(f.balanceQuery=y),d?f.noAuth=!0:c&&(f.apiKey=c),e.setHint("");try{const v=await gt().wbProviders?.customRequest?.("POST","/api/custom-providers",f);Lj([Tu,Eu,Hf]),_O(b,tw),Gf(Uf,!1);const j=v?.provider?.name||t;await zO(r("✅ 已创建自定义提供商「{name}」并添加账号",{name:j}))}catch(v){HO(e,v)}})}async function f7(e){const t=dr(Au);if(!t){Ln(r("请先选择一个自定义提供商"),"err");return}const a=dr(Vf),o=Rg(Ff),c=dr($f),d=String(ew().find(f=>f.id===t)?.balanceScheme||"");await sw(e.setBusy,async()=>{const f={provider:t};o?f.noAuth=!0:a&&(f.apiKey=a),c&&(f.name=c);const p=PO(d,nw);p&&(f.balanceQuery=p),e.setHint("");try{const h=await gt().wbProviders?.customRequest?.("POST","/api/accounts",f);Lj([Vf,$f]),_O(d,nw),Gf(Ff,!1);const b=h?.account?.name||ew().find(j=>j.id===t)?.name||"",y=b?`:${b}`:"",v=h?.created!==!1?r("✅ {label}账号已添加{name}",{label:"",name:y}):r("✅ 已更新已有账号{name}(同一把 API Key 只保留一条,重复添加会合并)",{name:y});await zO(v)}catch(h){HO(e,h)}})}async function p7(){const e=dr(Au);if(!e){Ln(r("请先选择一个自定义提供商"),"err");return}const t=gt().wbCustomProvidersUi?.remove;if(typeof t!="function"){Ln(r("删除功能不可用(脚本未就绪)"),"err");return}await sw(()=>{},async()=>{await t(e)&>().wbAddAccountModal?.close?.()})}function m7({mode:e,presetKey:t}){const[a,o]=x.useState(!1),[c,d]=x.useState(""),f={presetKey:t,setBusy:o,setHint:d};return s.jsxs(s.Fragment,{children:[s.jsx("span",{className:`add-foot-hint${c?" err":""}`,id:"add-form-foot-hint",children:c}),s.jsxs("span",{className:"add-foot-actions",id:"add-form-foot-actions",children:[e==="existing"?s.jsx(ne,{id:c7,variant:"destructive",title:r("级联删除名下全部账号,不可恢复"),onClick:()=>{p7()},children:r("删除此提供商")}):null,s.jsx(ne,{id:o7,hidden:e!=="create",disabled:a,onClick:()=>{d7(f)},children:r(a&&e==="create"?"提交中…":"创建并添加账号")}),s.jsx(ne,{id:i7,hidden:e!=="existing",disabled:a,onClick:()=>{f7(f)},children:r(a&&e==="existing"?"提交中…":"添加账号")})]})]})}const rw={step:"pick",accountType:Kj,search:"",provider:Gi,providerHint:"",presetKey:"",showToken:0},UO=()=>gt().wbProviders?.customList?.()||[];function h7({onClose:e,onReady:t}){const[a,o]=x.useState(rw),[c,d]=x.useState(0),f=x.useCallback(()=>d(R=>R+1),[]),p=x.useCallback(()=>{o(rw);const R=gt().wbProviders;(R?.all?.()||[]).length||R?.load?.().then(f),R?.refreshCustom?.().then(f)},[f]),h=x.useCallback(()=>{p(),o({...rw,step:"form",provider:ju})},[p]);x.useEffect(()=>(t({reset:p,openNewCustomForm:h}),()=>t(null)),[t,p,h]),x.useEffect(()=>{gt().wbWebLogin?.refresh?.()},[]),x.useEffect(()=>{gt().wbProviders?.refreshCustom?.().then(f)},[f]),x.useEffect(()=>{a.step!=="form"||a.provider!==ju||gt().wbProviders?.refreshCustom?.().then(f)},[a.step,a.provider,a.showToken,f]);const b=iq(a.provider),y=a.provider===ju,v=a.provider===Gi||y||!!b,j=a.providerHint?"existing":"create",w=a.providerHint?UO().find(R=>R.id===a.providerHint)?.name||"":a.presetKey&>().wbPresetProviders?.presetOf?.(a.presetKey)?.name||"",S=a.provider===Gi?Bj:r(gt().wbProviders?.labelOf?.(a.provider)||a.provider),N=y?a.providerHint||a.presetKey?r("登录 / 添加 {name} 账号",{name:w||r("自定义提供商")}):r("新建自定义提供商"):r("登录 / 添加 {name} 账号",{name:S});function k(R){if(!R)return;const C={step:"form",showToken:a.showToken+1};if(R===Xj)o(E=>({...E,...C,provider:ju,providerHint:"",presetKey:""}));else if(R.startsWith(zf)){const E=R.slice(zf.length),D=KR(gt().wbPresetProviders?.presetOf?.(E));o(I=>({...I,...C,provider:ju,providerHint:D?.id||"",presetKey:E}))}else UO().some(E=>E.id===R)?o(E=>({...E,...C,provider:ju,providerHint:R,presetKey:""})):o(E=>({...E,...C,provider:R,providerHint:"",presetKey:""}))}const A=()=>o(R=>({...R,step:"pick"})),P=a.accountType===iG;return s.jsx(Yt,{open:!0,onOpenChange:R=>{R||e()},children:s.jsxs(Xt,{className:"w-[min(1100px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("添加账号")})}),s.jsx(Lt,{children:s.jsx(gG,{accountType:a.accountType,onAccountTypeChange:R=>o(C=>({...C,accountType:R})),search:a.search,onSearchChange:R=>o(C=>({...C,search:R})),onPick:k,version:c,importing:P,importPanel:void 0})}),null,s.jsx(Yt,{open:a.step==="form",onOpenChange:R=>{R||A()},children:s.jsxs(Xt,{keepMounted:!0,overlayForceRender:!0,className:"w-[min(620px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:N})}),s.jsxs(Lt,{children:[s.jsx(oG,{active:a.provider===Gi}),JR.map(R=>s.jsx(nG,{config:R,active:a.provider===R.provider},R.provider)),s.jsx("div",{className:"add-provider-block",style:y?void 0:{display:"none"},children:s.jsx(u7,{mode:j,providerHint:a.providerHint,presetKey:a.presetKey,showToken:a.showToken,version:c})}),v?null:s.jsx("div",{className:"add-provider-block",children:s.jsxs(dn,{children:[s.jsx("h3",{children:r("该提供商账号添加功能即将上线")}),s.jsx("p",{children:r("「{name}」的账号添加功能还在开发中,敬请期待。",{name:S})})]})})]}),y?s.jsx(tn,{children:s.jsx(m7,{mode:j,presetKey:a.presetKey})}):null]})})]})})}let Mo=null,Ru=null,Xi=null,Og=null;function g7(){Mo&&(Mo.unmount(),Mo=null),Ru&&(Ru.remove(),Ru=null),Xi=null}function VO(){Ru=document.createElement("div"),document.body.append(Ru),Mo=jn.createRoot(Ru),Mo.render(s.jsx(h7,{onClose:FO,onReady:e=>{if(Xi=e,!e)return;const t=Og;Og=null,t?.()}}))}function FO(){gt().wbWebLogin?.cancelIfActive?.(""),gt().wbAutoclawOauth?.cancel?.(),g7()}function aw(){if(Mo){Xi?.reset();return}Og=()=>Xi?.reset(),VO()}function x7(){if(Mo){Xi?.openNewCustomForm();return}Og=()=>Xi?.openNewCustomForm(),VO()}for(const e of["btn-add-account","btn-add-account-2"])document.getElementById(e)?.addEventListener("click",aw);const $O=window;$O.wbAddAccountModal={open:aw,close:FO},$O.wbAccountAddForms={syncAddProvider:()=>{Mo?Xi?.reset():aw()},openNewCustomForm:x7};const b7=3065;let Ou=null;function qO(){return window.workbuddyDesktop}const v7="same-origin";let Mg=null;function y7(e){const t={};if(!e||typeof e!="object")return t;for(const[a,o]of Object.entries(e))typeof o=="string"&&o&&(t[a]=o);return t}async function j7(e=!1){if(Mg&&!e)return Mg;let t={};try{t=y7(await qO()?.directCredentials?.())}catch{}return Mg=t,t}async function GO(e=!1){if(Ou&&!e)return Ou;const t=qO();if(t?.platform==="web"){const o=typeof window>"u"?"":window.location?.origin;if(o&&o!=="null")return Ou=o,Ou}let a=0;try{const o=await t?.getBackendStatus?.(),c=Number(o?.port);Number.isFinite(c)&&c>0&&(a=c)}catch{}return a>0?(Ou=`http://127.0.0.1:${a}`,Ou):`http://127.0.0.1:${b7}`}async function KO(){const t=await window.workbuddyDesktop?.getModelManage?.(),a=Array.isArray(t?.models)?t.models:[],o=Array.isArray(t?.mappings)?t.mappings:[],c=new Set,d=[],f=new Set;for(const p of a){if(p.enabled!==!0)continue;const h=String(p.provider??""),b=String(p.id??"");!h||!b||(c.add(`${h}${b}`),f.has(b)||(f.add(b),d.push(b)))}for(const p of o){if(p.enabled!==!0||p.isDefault===!0)continue;const h=String(p.provider??""),b=String(p.target??""),y=String(p.alias??"");(!y||!f.has(y)&&c.has(`${h}${b}`))&&(f.add(y),d.push(y))}return d}const lw=`你是 Agent2API 的内置助手,帮用户管理本机的模型转发配置。 +`)})}function RG(e){if(e===void 0)return{text:r("未查询"),kind:"muted",title:r("尚未查询该账号的余额")};if(e===null)return{text:r("查询中…"),kind:"muted",title:r("正在查询")};const t=gu(e);if(t)return t.notConfigured?{text:r("未配置"),kind:"muted",title:r("{message}(去该账号的「设置」里填上查询凭证即可)",{message:t.message})}:{text:r("查询失败"),kind:"bad",title:t.message};if(typeof e!="object"||e===null)return{text:r("无数据"),kind:"muted",title:String(e)};const a=e;if(Object.prototype.hasOwnProperty.call(a,"totalLeft")){const o=a.unlimited?"∞":ss(a.totalLeft);return{text:r("可用 {total}",{total:o}),kind:"ok",title:r("总剩余 {total} · 套餐 {plan} · 奖励 {bonus}",{total:o,plan:ss(a.planLeft),bonus:ss(a.bonusLeft)})}}if(Object.prototype.hasOwnProperty.call(a,"available")||Array.isArray(a.wallets)){const o=String(a.unit||r("积分")),c=Array.isArray(a.wallets)?a.wallets:[],d=c.map(j=>`${j?.displayName||j?.type||r("明细")} ${j?.balanceView?String(j.balanceView):ss(j?.balance)}`).join(" · "),f=gO(a.subscription),p=[];if(c.length===0&&Array.isArray(a.plans)&&a.plans.length>0){const j=xO(a);p.push(j&&bO(j)?r("读数里没有额度桶,这里按套餐权益里的已用 / 总量显示(上游给的数)"):r("上游的余额读数里没有这个套餐的额度桶,剩余量读不到;余额列显示的是套餐发放的权益额度,不是剩余量"))}String(a.walletsFrom||"")==="monitor"&&p.push(r("额度窗口读数来自开放平台监控通道(套餐 JWT 那条通道没有额度桶):读的是每 N 小时 / 每周窗口的剩余比例"));const h=a.available==null?c.find(j=>j?.balance!=null):void 0,b=a.availableView?String(a.availableView):h?r("可用 {value} {unit}",{value:ss(h.balance),unit:String(h.unit||o)}):r("可用 {value} {unit}",{value:ss(a.available),unit:o}),y=[a.statisticsError?r("订阅统计未读到:{error}",{error:String(a.statisticsError)}):"",a.benefitError?r("福利网关未读到:{error}",{error:String(a.benefitError)}):""].filter(Boolean),v=a.benefitAbsent?[r("该账号没有福利模型额度(福利按活动下发,不是每个账号都有)")]:[];return{text:b+(y.length?" ⚠":""),kind:y.length?"warn":"ok",title:[b,d,f,...p,...v,...y].filter(Boolean).join(" · ")}}return{text:r("无数据"),kind:"muted",title:r("未返回可识别的余额数据")}}function xO(e){const t=Array.isArray(e.plans)?e.plans:[],a=o=>String(o?.state||"");return t.find(o=>a(o)==="active")||t.find(o=>a(o)==="pending")||t[0]||null}function OG(e){const t=Array.isArray(e.entitlements)?e.entitlements:[];let a=null;for(const o of t){const c=Number(o?.grantUnits);if(!Number.isFinite(c)||c<=0)continue;const d=String(o?.unitType||"").trim();(!a||c>a.units)&&(a={units:c,unit:d})}return a}function bO(e){const t=Array.isArray(e.entitlements)?e.entitlements:[];for(const a of t){const o=Sr(a?.totalUnits);if(o===null||o<=0)continue;const c=Sr(a?.usedUnits),d=Sr(a?.remainingUnits),f=c===null?d===null?null:Math.max(0,o-d):c;if(f!==null)return{used:f,total:o,unit:String(a?.unitType||"").trim()}}return null}function MG(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const t=e,a=Array.isArray(t.wallets)?t.wallets:[];let o=null;for(const p of a){const h=Number(p?.total);!Number.isFinite(h)||h<=0||(!o||h>Number(o.total))&&(o=p)}const c=t.subscription&&typeof t.subscription=="object"?t.subscription:{};if(!o){let p=null;for(const j of a){const w=Sr(j?.remainingPercent);w!==null&&(!p||w0?r("重置 {date}",{date:Ls(w)}):"";o.push({name:b,view:j,tone:v===null?"muted":v<20?"warn":"ok",percent:v,over:!1,title:[d||[b,h].filter(Boolean).join(" "),j,S].filter(Boolean).join(" · "),variant:"compact",percentText:v===null?"":`${Math.round(v)}%`,windowShort:h,id:String(c.key||"").trim()||b})}return o}function PG(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const t=e.subscription&&typeof e.subscription=="object"?e.subscription:{},a=String(t.planName||"").trim();return a?{text:KA(a),free:hj(a)}:null}function Mo(e){const t=Number.isFinite(e)?Number(e.toFixed(2)):e,a=window.wbUnits;return a?.formatTokens?a.formatTokens(t):String(t)}function _G(e){return e.includes("福利")?r("福利日额度"):e.includes("对话")?r("对话 token"):e}function LG(e){if(!e||typeof e!="object"||Array.isArray(e))return null;const t=e,a=Array.isArray(t.wallets)?t.wallets:[];if(!a.length)return null;const o=String(t.unit||r("积分")),c=[],d=[];for(const h of a){const b=String(h.displayName||h.type||r("明细")),y=Sr(h.creditRemaining),v=Sr(h.creditTotal),j=String(h.balanceView||"")||ss(h.balance),w=Sr(h.usedPercent);if(y!==null){const S=v!==null&&v>0,N=S?Math.max(0,Math.min(100,y/v*100)):null;c.push({name:b,view:S?r("剩 {remaining} / {total}",{remaining:ss(y),total:ss(v)}):r("剩 {remaining}",{remaining:ss(y)}),tone:y<=0?"muted":N!==null&&N<20?"warn":"ok",percent:N,over:!1,title:S?void 0:r("上游未给总量,画不出剩余比例")});continue}if(w!==null||Sr(h.allowanceTokens)!==null){const S=Sr(h.usedTokens)??0,N=Sr(h.allowanceTokens),k=N!==null&&N>1,A=k&&S>N,P=h.unlimited===!0,R=k?Math.max(0,Math.min(100,(1-S/N)*100)):null,C=P?r("无上限 · 已用 {used}",{used:Mo(S)}):k?r("剩 {remaining} / {total}",{remaining:Mo(Math.max(0,N-S)),total:Mo(N)}):S>0?r("已用 {used}",{used:Mo(S)}):r("未使用");d.push({name:_G(b),view:C,tone:A?"bad":!k&&!P?"muted":R!==null&&R<20?"warn":"ok",percent:R,over:A,title:[j,P?r("无上限按日计(上游到达上限后仍继续计数,超额如实显示)"):null].filter(Boolean).join(" · ")});continue}d.push({name:b,view:j,tone:"muted",percent:null,over:!1,title:j})}const f=t.subscription&&typeof t.subscription=="object"?t.subscription:{},p=[t.statisticsError?r("订阅统计未读到:{error}",{error:String(t.statisticsError)}):"",t.benefitError?r("福利网关未读到:{error}",{error:String(t.benefitError)}):""].filter(Boolean);return{credit:c,token:d,total:Sr(t.available),unit:o,plan:KA(f.planName),free:hj(f.planName),resetDate:String(f.resetDate||""),missing:p,benefitAbsent:t.benefitAbsent===!0}}function zG(e){const t=Rl(e);let a=null;for(const v of u$(e)){if(v.enabled===!1)continue;const j=Nf(v,t);if(!j)continue;const w=v.threshold>0?j.used/v.threshold:0;if(w>=1){a={rule:v,reading:j,ratio:w};break}(!a||w>a.ratio)&&(a={rule:v,reading:j,ratio:w})}if(!a)return null;const{rule:o,reading:c}=a,d=c.used>=o.threshold,f=pj(o),p=d?"":` · ${Zh(f.remainingMs,f.daily)}`,h=f.daily?r("今日已用"):r("周期 Token"),b=o.reset==="daily"?r("今天(本地 0 点重置)"):r("{period}窗口",{period:HA(o.period??0)}),y=r("限制器:{window}已用 {used} / {threshold} Token",{window:b,used:Math.round(c.used).toLocaleString("en-US"),threshold:Math.round(o.threshold).toLocaleString("en-US")})+(d?r(",已达到上限"):r(",{countdown}重置",{countdown:Zh(f.remainingMs,f.daily)}));return s.jsxs("span",{className:"usage-token-sub",title:y,children:[h," ",s.jsx("span",{className:d?"hit":void 0,children:El(c.used)})," ","/ ",El(o.threshold),p]})}function BG({account:e}){if(!Ea(e))return s.jsx("span",{className:"muted",title:r("该提供商没有余额查询"),children:"—"});const t=Wa(e),a=RG(t),o=Qh(e,t)?s.jsx(ze,{variant:"warning",shape:"tag",title:r("余额低于限制器的阈值,转发时会跳过该账号(余额回升自动恢复)"),children:r("余额不足 · 已跳过")}):fj(e,Rl(e))?s.jsx(ze,{variant:"warning",shape:"tag",title:r("本周期 Token 消耗达到限制器的上限,转发时会跳过该账号(窗口重置自动恢复)"),children:r("Token 限额 · 已跳过")}):null,c=zG(e),d=a.kind==="ok"||a.kind==="warn"?EG(t):[],f=d.length?s.jsx(ze,{variant:"warning",shape:"tag",title:AG(d),children:r("待生效 {n} 份",{n:d.length})}):null,p=a.kind==="ok"||a.kind==="warn"?Z6(t):[],h=GA(e)&&p.length?{rows:p,subscriptionLine:TG(t),kind:a.kind,title:a.title}:null,b=a.kind==="ok"||a.kind==="warn"?IG(t):[];if(b.length){const j=PG(t);return s.jsxs("span",{className:"usage-cell",title:a.title,children:[j?s.jsx("span",{className:"usage-plan",children:s.jsx("span",{className:`plan-chip${j.free?" free":""}`,children:j.text})}):null,b.map(w=>s.jsx(vO,{row:w},w.id||w.name)),o,f,c]})}if(b$(e)){const w=a.kind==="ok"||a.kind==="warn"?LG(t):null;return w?s.jsx(HG,{summary:a,detail:w,blockedBadge:o,tokenSub:c}):s.jsxs("span",{className:"usage-sum-wrap",children:[s.jsx("span",{className:`usage-sum ${a.kind}`,title:a.title,children:a.text}),o,f,c]})}const y=a.kind==="ok"||a.kind==="warn"?MG(t):null;if(!y)return s.jsxs("span",{className:"usage-sum-wrap",children:[h?s.jsx(AA,{variant:"sum",...h,children:a.text}):s.jsx("span",{className:`usage-sum ${a.kind}`,title:a.title,children:a.text}),o,f,c]});const v=s.jsxs(s.Fragment,{children:[y.percent!==null?s.jsx(cf,{value:y.percent,className:"usage-pool-bar"}):null,s.jsx("span",{className:`usage-pool-view ${a.kind}`,children:y.text})]});return s.jsxs("span",{className:"usage-pool",title:a.title,children:[y.planName?s.jsx("span",{className:"usage-pool-name",children:y.planName}):null,h?s.jsx(AA,{variant:"pool",...h,children:v}):s.jsx("span",{className:"usage-pool-line",children:v}),o,f,c]})}function HG({summary:e,detail:t,blockedBadge:a,tokenSub:o}){const[c,d]=x.useState(!1),f=e.kind==="ok"||e.kind==="warn";return s.jsxs("span",{className:c?"usage-cell open":"usage-cell",children:[s.jsxs("span",{className:"usage-plan",children:[t.plan?s.jsx("span",{className:`plan-chip${t.free?" free":""}`,children:t.plan}):null,t.resetDate?s.jsx("span",{className:"plan-sub",title:r("套餐积分的重置日期"),children:r("重置 {date}",{date:t.resetDate})}):null]}),s.jsxs(wf,{open:c,onOpenChange:d,children:[s.jsxs(Kh,{render:s.jsx("button",{type:"button",className:`usage-toggle ${e.kind}`,title:e.title,"aria-label":r("积分明细")}),children:[s.jsx("span",{className:"val",children:e.text}),s.jsx("span",{className:"caret","aria-hidden":!0,children:"▾"})]}),s.jsxs(Sf,{align:"end",sideOffset:6,className:"usage-pop w-[288px]",children:[f&&t.missing.length?t.missing.map(p=>s.jsx("div",{className:"usage-pop-missing",title:p,children:p},p)):null,t.credit.length?s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"usage-pop-group",children:r("积分")}),t.credit.map(p=>s.jsx(UG,{row:p},p.name))]}):null,t.total!==null?s.jsxs("div",{className:"usage-pop-total",children:[s.jsx("span",{className:"k",children:r("可用合计")}),s.jsxs("span",{className:"v",children:[ss(t.total)," ",s.jsx("small",{children:t.unit})]})]}):null,!t.credit.length&&t.total===null?s.jsx("div",{className:"usage-pop-empty",title:r("该账号的额度只有福利 token 池(见余额格那两行),订阅统计没有积分类计量表"),children:r("该账号没有积分类读数(额度只有福利 token 池)")}):null]})]}),t.token.map(p=>s.jsx(vO,{row:p},p.name)),t.benefitAbsent?s.jsxs("span",{className:"usage-flat-line",title:r("福利按限时活动下发,不是每个账号都有 —— 正常状态,不是查询失败"),children:[s.jsx("span",{className:"usage-flat-name",children:r("福利日额度")}),s.jsx("span",{className:"usage-flat-view muted",children:r("无福利额度")})]}):null,a,o]})}function UG({row:e}){return s.jsxs("div",{className:"usage-pop-row",title:e.title,children:[s.jsxs("div",{className:"usage-pop-line",children:[s.jsx("span",{className:"usage-pop-name",children:e.name}),s.jsx("span",{className:`usage-pop-view ${e.tone}`,children:e.view})]}),e.percent!==null?s.jsx("span",{className:`usage-pop-track${e.over?" over":""}`,children:s.jsx("i",{style:{width:`${e.percent}%`},"aria-label":r("剩余 {percent}%",{percent:Math.round(e.percent)})})}):null]})}function VG({account:e,namesHidden:t}){const a=Xh(e),o=cr(An(e)),c=hu(e)||r("未命名账号"),d=String(e.email||"").trim(),f=String(e.nickname||"").trim(),p=String(e.name||"").trim(),h=w=>t?z$(w):w,b=[d&&d!==c?r("邮箱 {email}",{email:h(d)}):"",a?`${o.identifier} ${a}`:"",!e.nameCustom&&p&&p!==d&&p!==f?r("备注名 {name}",{name:h(p)}):"",f&&f!==c&&f!==d?r("昵称 {name}",{name:h(f)}):"",kf(e)?r("桌面端实时登录态(凭证每次从客户端登录态文件读取)"):"",e.updatedAt?r("更新于 {time}",{time:Ls(e.updatedAt)}):"",e.source?r("来源 {source}",{source:e.source==="imported"?r("旧数据导入"):r("手动添加")}):""].filter(Boolean),y=d&&d!==c,v=e.proxy?.error,j=b.length?s.jsx(fV,{delay:0,children:s.jsxs(Ui,{children:[s.jsx(Vi,{render:s.jsx("div",{className:"acct-name"}),children:s.jsx("span",{className:"name",children:h(c)})}),s.jsxs(Fi,{children:[s.jsx(pV,{}),b.map((w,S)=>s.jsx("div",{children:w},S))]})]})}):s.jsx("div",{className:"acct-name",children:s.jsx("span",{className:"name",children:h(c)})});return s.jsxs(s.Fragment,{children:[j,y?s.jsx("div",{className:"acct-sub",children:s.jsx("span",{className:"acct-email",title:r("账号邮箱"),children:h(d)})}):null,v?s.jsx("div",{className:"acct-note bad",title:r("代理不可用:{error}",{error:v}),children:r("代理不可用:{error}",{error:v})}):null]})}function FG({account:e}){const t=e.proxy,a=t?.config?.source||t?.source,o=t?.label||r(a==="custom"?"自定义代理":"已设置"),c=t?.error,d=U$(),f=Array.isArray(d)?d:[];let p="";a==="pool"&&t?.config?.proxyId?p=`${Qa}${t.config.proxyId}`:t&&(p=cg);const h=[{value:"",label:r("直连")}];for(const v of f)h.push({value:`${Qa}${v.id}`,label:du(v)});if(d===null?h.push({value:"__hint_pool__",label:r("正在读取代理列表…"),disabled:!0}):f.length||h.push({value:"__hint_pool__",label:V$()?r("代理列表读取失败(重试中)"):r("还没有代理(去「网络代理」页添加)"),disabled:!0}),p===cg){const v=a==="clash"?r("Clash 出口:"):a==="custom"?r("自定义:"):"";h.push({value:cg,label:`${v}${o}${c?r("(不可用)"):""}`})}else a==="pool"&&!f.some(v=>`${Qa}${v.id}`===p)&&h.push({value:p,label:`${o}${c?r("(不可用)"):""}`});h.push({value:AR,label:r("自定义代理…")});const b=c?r("代理不可用:{error}(转发时会回退直连);选「自定义代理…」去修改",{error:c}):a==="rotate"?r("当前:{label}(每条请求换一个出口);「自定义代理…」打开完整设置",{label:o}):a==="clash"||a==="custom"?r("当前:{label}(未经过代理池 —— 可在「网络代理」页把出口同步进池后来这里改选)",{label:o}):r("当前:{label};选项来自「网络代理」页;「自定义代理…」打开完整设置",{label:t?o:r("直连")}),y=h.find(v=>v.value===p);return s.jsxs(ut,{value:p,onValueChange:v=>{J$(e.id,String(v),p)},children:[s.jsx(ft,{className:Ue("w-full",c&&"border-destructive-bd"),title:b,"aria-label":r("出网代理"),children:s.jsx(dt,{children:y?.label||r("直连")})}),s.jsx(pt,{children:h.map(v=>s.jsx($e,{value:v.value,disabled:v.disabled,children:v.label},v.value))})]})}function $G({account:e,atFront:t}){const[a,o]=x.useState(!1),c=Ea(e),d=FA(e);return s.jsxs("div",{className:"acct-actions",children:[d?s.jsx(ne,{variant:"outline",size:"xs",title:g$(e)?x$(e):r("查看可领取的套餐与名下已有的套餐(每天一期,领取需过一次人机验证)"),onClick:()=>RR(e.id),children:r("领套餐")}):null,c?s.jsx(ne,{variant:"outline",size:"xs",disabled:a,title:r("查询该账号剩余余额(读数显示在余额列)"),onClick:()=>{o(!0),Z$(e.id).finally(()=>o(!1))},children:r("余额")}):null,s.jsx(ne,{variant:"outline",size:"xs",title:r("备注名 / 启用 / 代理"),onClick:()=>lg(e.id),children:r("设置")}),s.jsx(qG,{account:e,atFront:t})]})}function qG({account:e,atFront:t}){const[a,o]=x.useState(!1),c=ur(e),d=Number(e.maxConcurrent)||0,f="w-full justify-start px-2 font-normal",p=`${f} text-destructive hover:text-destructive`;return s.jsxs(wf,{open:a,onOpenChange:o,children:[s.jsx(Kh,{render:s.jsx(ne,{variant:"outline",size:"xs",title:r("更多操作")}),children:"⋯"}),s.jsx(Sf,{align:"end",sideOffset:4,className:"w-[172px] p-1.5",children:s.jsxs("div",{className:"flex flex-col gap-0.5",children:[s.jsx(ne,{variant:"ghost",size:"sm",className:p,onClick:()=>{o(!1),CR(e.id,!c)},children:r(c?"禁用":"启用")}),s.jsx(ne,{variant:"ghost",size:"sm",className:f,disabled:t,title:r(t?"已在全局队列第一位":"仅将优先级调整到全局第一位,不改变启用状态"),onClick:()=>{o(!1),Pe().wbApp?.runAccountAction?.("switch",e.id)},children:r("设为首选")}),s.jsx(ne,{variant:"ghost",size:"sm",className:f,title:r("在账号设置的「基本」分组里设置该账号同时最多处理的请求数"),onClick:()=>{o(!1),lg(e.id)},children:r("并发上限:{value}",{value:d>0?d:r("不限")})}),e.hasRefreshToken?s.jsx(ne,{variant:"ghost",size:"sm",className:f,onClick:()=>{o(!1),Pe().wbApp?.runAccountAction?.("refresh",e.id)},children:r("刷新 Token")}):null,s.jsx("div",{className:"my-1 h-px bg-hairline"}),s.jsx(ne,{variant:"ghost",size:"sm",className:p,title:kf(e)?r("删除这条账号记录(不会影响客户端自己的登录态;之后可再点「导入桌面端登录态」加回来)"):void 0,onClick:()=>{o(!1),Pe().wbApp?.runAccountAction?.("remove",e.id)},children:r("删除账号")})]})})]})}function GG({account:e,onClose:t,onClear:a}){const o=bj(e),c=s.jsx(ne,{variant:"ghost",size:"icon-xs",className:"panel-close",title:r("收起"),onClick:t,children:"✕"});return o.length?s.jsxs("div",{className:"row-panel limit-panel",children:[c,s.jsxs("div",{className:"lp-head",children:[s.jsx("b",{children:hu(e)}),s.jsx("span",{className:"muted",children:r("{n} 个模型限流中 · 记录来自上游 429 / 限额码,到恢复时间自动解除",{n:o.length})}),s.jsx(ne,{variant:"outline",size:"xs",title:r("清掉该账号全部模型的限流标记"),onClick:()=>a(""),children:r("全部清除")})]}),o.map(d=>{const f=jj(d.resetAt),p=d.message||(d.status?r("上游返回 {status}",{status:d.status}):"");return s.jsxs("div",{className:"lp-row",children:[s.jsx("span",{className:"lp-model",title:d.model,children:d.model}),s.jsx(ze,{variant:"warning",shape:"tag",children:r("限流中")}),s.jsx("span",{className:"lp-reset",title:r("到恢复时间后自动解除,无需手动操作"),children:f===yj?r("恢复时间未知"):r("{time} 恢复",{time:f})}),s.jsx("span",{className:"lp-reason",title:p,children:p}),s.jsx(ne,{variant:"outline",size:"2xs",title:r("清掉本机的限流标记,立刻重新尝试该模型(上游若仍在限流会再次被标记)"),onClick:()=>a(d.model),children:r("清除标记")})]},d.model)})]}):s.jsxs("div",{className:"row-panel limit-panel",children:[r("当前没有限流中的模型。"),c]})}function KG({account:e,colSpan:t,limitsOpen:a,onClear:o}){return a?s.jsx("tr",{className:"acct-panels","data-panels-for":e.id,children:s.jsx("td",{colSpan:t,children:s.jsx(GG,{account:e,onClose:()=>dR(e.id,"limits",!1),onClear:c=>o(e.id,c)})})}):null}function YG({namesHidden:e,allPicked:t,somePicked:a,disabled:o,onToggleAll:c}){const d=s.jsx("span",{className:"col-grip",title:r("拖动调整列宽(双击还原)")});return s.jsx("thead",{children:s.jsxs("tr",{children:[s.jsx("th",{className:"cell-pick","data-col":"pick",children:s.jsx(Pr,{id:"acct-select-all",checked:t,indeterminate:!t&&a,disabled:o,"aria-label":r("全选当前筛选结果"),title:r("全选 / 取消全选当前筛选结果(与批量栏同一个选择)"),onCheckedChange:f=>c(f)})}),s.jsxs("th",{className:"cell-order","data-col":"order",children:[s.jsx("span",{className:"th-label",title:r("组内先后:按住左边的把手上下拖动即可调整;整组的先后拖分组带的把手"),children:r("顺序")}),d]}),s.jsxs("th",{className:"cell-account","data-col":"account",children:[s.jsxs("span",{className:"th-label",children:[r("账号"),s.jsx(XG,{hidden:e})]}),d]}),s.jsxs("th",{className:"cell-proxy","data-col":"proxy",children:[s.jsx("span",{className:"th-label",title:r("该账号出网走的代理(Clash 出口 / 自定义 / 直连);点击可修改"),children:r("代理")}),d]}),s.jsxs("th",{className:"cell-connections","data-col":"connections",children:[s.jsx("span",{className:"th-label",title:r("此刻正在使用这个账号的请求数(含还在下发内容的流式请求);为 0 时不显示"),children:r("连接数")}),d]}),s.jsxs("th",{className:"cell-status","data-col":"status",children:[s.jsx("span",{className:"th-label",children:r("状态")}),d]}),s.jsxs("th",{className:"cell-limits","data-col":"limits",children:[s.jsx("span",{className:"th-label",title:r("该账号当前限流中的模型;点徽章看明细"),children:r("限流")}),d]}),s.jsxs("th",{className:"cell-expiry","data-col":"expiry",children:[s.jsx("span",{className:"th-label",children:r("有效期")}),d]}),s.jsxs("th",{className:"cell-usage","data-col":"usage",children:[s.jsx("span",{className:"th-label",children:r("余额")}),d]}),s.jsx("th",{className:"cell-actions","data-col":"actions",children:s.jsx("span",{className:"th-label",children:r("操作")})})]})})}function XG({hidden:e}){const t=r(e?"显示账号名":"隐藏账号名(名字显示为星号)");return s.jsx("button",{type:"button",className:`name-eye${e?" on":""}`,title:t,"aria-label":t,"aria-pressed":e,onClick:()=>L$(),dangerouslySetInnerHTML:{__html:Cg(e?"eyeOff":"eye",13)}})}const yO="dragging",Bf=["drop-before","drop-after"];let Ja=null;function jO(e){e.querySelectorAll(`.${Bf[0]}, .${Bf[1]}`).forEach(t=>{t.classList.remove(...Bf)})}function QG(e,t){if(t==="group"){const o=e.parentElement;return o?[...o.children].filter(c=>c.classList.contains("acct-group")):[]}const a=e.parentElement;return a?[...a.children].filter(o=>o.classList.contains("acct-row")):[]}function wO(e,t){for(let a=0;aa!==e.item);jO(e.container),t.length&&(e.index>=t.length?t[t.length-1].classList.add(Bf[1]):t[e.index].classList.add(Bf[0]))}function SO(e){if(!Ja)return;if(e.buttons===0){Tg(!1);return}const t=Ja.items.filter(o=>o!==Ja.item),a=wO(t,e.clientY);a!==Ja.index&&(Ja.index=a,ZG(Ja))}function Tg(e){const t=Ja;t&&(Ja=null,window.removeEventListener("pointermove",SO),window.removeEventListener("pointerup",NO),window.removeEventListener("pointercancel",kO),window.removeEventListener("keydown",CO),document.body.classList.remove("queue-dragging"),t.item.classList.remove(yO),jO(t.container),e&&(t.kind==="group"?Qj?.onDrop({kind:"group",provider:t.provider,index:t.index}):Qj?.onDrop({kind:"row",provider:t.provider,accountId:t.accountId,index:t.index})))}function NO(){Tg(!0)}function kO(){Tg(!1)}function CO(e){e.key==="Escape"&&Tg(!1)}let Qj=null;function WG(e,t){Qj=t,e&&e.dataset.queueDragBound!=="1"&&(e.dataset.queueDragBound="1",e.addEventListener("pointerdown",a=>{if(a.button!==0||Ja)return;const o=a.target,c=o?.closest?.(".queue-grip"),d=o?.closest?.(".order-grip");if(!c&&!d)return;if(a.preventDefault(),c&&t.isLocked()){t.onLockedAttempt();return}const f=(c??d).closest("tbody.acct-group, tr.acct-row");if(!f)return;const p=c?"group":"row",h=f.dataset.provider||f.closest("tbody.acct-group")?.dataset.provider||"",b=f.dataset.id||"";if(p==="row"&&!b)return;const y=QG(f,p);if(y.length<2)return;const v=y.filter(j=>j!==f);Ja={kind:p,provider:h,accountId:b,item:f,container:f.parentElement||e,items:y,index:wO(v,a.clientY)},f.classList.add(yO),document.body.classList.add("queue-dragging"),window.addEventListener("pointermove",SO),window.addEventListener("pointerup",NO),window.addEventListener("pointercancel",kO),window.addEventListener("keydown",CO)}))}let Eg=null,TO=!1,Zj=0,Ag=null,EO="";function AO(){return Eg?Eg.apply(Oj):Oj}function RO(){window.wbAccountsView?.render?.()}function JG(){const e=AO(),t=`${Ag?"T":"F"}|${e.map(o=>`${o.key}:${o.align}`).join("|")}`;if(t===EO)return;EO=t,Pe().wbColSettings?.syncStaticHead("accounts",Ag);const a=[...Ag?.querySelectorAll("thead th")||[]];a.forEach((o,c)=>{const d=o.querySelector(".col-grip");d&&(d.style.display=c===a.length-1?"none":"")})}function e7(){const e=x.useSyncExternalStore(sR,Gs),t=x.useRef(null),[,a]=x.useReducer(B=>B+1,0),o=!!Sj(),c=Al(),{groups:d,allGroups:f}=C$(Wa,Rl),p=T$(Wa,Rl),h=[{value:"all",label:r("全部({n})",{n:c.length})}].concat(rR().map(B=>({value:B.id,label:r("{label}({count})",{label:r(B.label),count:B.count})}))),b=d.flatMap(B=>B.accounts),y=E$(),v=AO(),j=hg(b.length,"accounts",{defaultSize:"all"}),w=j.paged?j.slice(b):b,S=ng()||j.paged,N=ng()?r("清除筛选"):r("把每页调回「全部」");x.useEffect(()=>{F$()}),x.useEffect(()=>{O$()},[e.version]),x.useLayoutEffect(()=>{if(!TO){if(!Pe().wbColSettings?.register){window.clearTimeout(Zj),Zj=window.setTimeout(()=>k(B=>B+1),50);return}TO=!0,Eg=Pe().wbColSettings?.register({id:"accounts",label:r("账号表"),columns:Oj.map(B=>({key:B.key,label:B.label,align:B.align,legacyAlign:B.legacyAlign})),mount:()=>document.querySelector("#batch-bar .batch-actions"),onChange:RO})||null,Eg&&a();return}JG()});const[,k]=x.useState(0);x.useEffect(()=>()=>window.clearTimeout(Zj),[]),x.useEffect(()=>{V8(t.current,RO)},[]);const A=b.map(B=>B.id),P=A.length>0&&A.every(B=>e.selected.has(B)),R=A.some(B=>e.selected.has(B)),C=[...e.selected].filter(B=>!A.includes(B)).length,E=e.selected.size>0,D=f.length>0&&f.every(B=>e.collapsed.has(B.provider)),I=new Map;for(const B of d)I.set(B.provider,w.filter(L=>An(L)===B.provider).map(L=>L.id));const O=x.useRef({displayed:d,all:f,renderedRows:I});x.useEffect(()=>{O.current={displayed:d,all:f,renderedRows:I}});const _=x.useRef({locked:!1,lockReason:"",onDrop:B=>{}}),z=x.useCallback(B=>{const{displayed:L,all:F}=O.current;if(B.kind==="group"){const Z=F.findIndex(te=>te.provider===B.provider);if(Z<0)return;const ae=F[Z],ce=F.filter((te,ke)=>ke!==Z),W=B.indexte.provider===W),ve=W===null||ue<0?ce.length:ue;if(ve===Z)return;const he=[...ce.slice(0,ve),ae,...ce.slice(ve)].flatMap(te=>te.members.map(ke=>ke.id));TR(he,r("{name} 移到第 {rank} 顺位",{name:ae.label,rank:ve+1}));return}const $=F.find(Z=>Z.provider===B.provider);if(!$)return;const X=$.members.findIndex(Z=>Z.id===B.accountId);if(X<0)return;const K=$.members[X],q=$.members.filter((Z,ae)=>ae!==X),Q=O.current.renderedRows.get(B.provider)||[],G=B.indexZ.id===G),Y=G===null||J<0?q.length:J;if(Y===X)return;const re=[...q.slice(0,Y),K,...q.slice(Y)],U=F.map(Z=>Z.provider===B.provider?{...Z,members:re}:Z).flatMap(Z=>Z.members.map(ae=>ae.id));TR(U,r("{name} 移到 {group} 组内第 {rank} 位",{name:String(K.name||K.id),group:r($.label),rank:Y+1}))},[]);x.useEffect(()=>{_.current={locked:S,lockReason:N,onDrop:z}}),x.useEffect(()=>{WG(t.current,{isLocked:()=>_.current.locked,onLockedAttempt:()=>Le(r("当前视图不能调整组顺位:{reason} 之后再拖动",{reason:_.current.lockReason}),"err"),onDrop:B=>_.current.onDrop(B)})},[]);function H(B,L,F){switch(B){case"pick":return s.jsx(Pr,{checked:F.picked,"data-pick":L.id,title:r("勾选后可批量操作"),"aria-label":r("勾选后可批量操作"),onCheckedChange:$=>I$(L.id,$)});case"order":return s.jsx(yG,{account:L,rank:F.rank});case"account":return s.jsx(VG,{account:L,namesHidden:e.namesHidden});case"proxy":return s.jsx(FG,{account:L});case"connections":return s.jsx(CG,{account:L});case"status":return s.jsx(SG,{account:L});case"limits":return s.jsx(NG,{account:L,open:F.limitsOpen});case"expiry":return s.jsx(kG,{account:L});case"usage":return s.jsx(BG,{account:L});case"actions":return s.jsx($G,{account:L,atFront:F.seat.position<=1});default:return null}}return s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"panel account-table",children:[s.jsxs("div",{className:"toolbar",children:[s.jsxs("div",{className:"group","data-provider-group":"1",children:[s.jsx("span",{className:"label",children:r("提供商")}),s.jsxs(ut,{value:e.filter.provider,onValueChange:B=>R$(String(B)),children:[s.jsx(ft,{className:"max-w-none min-w-0 w-full min-[768px]:max-w-[200px] min-[768px]:min-w-[130px] min-[768px]:w-auto","aria-label":r("按提供商筛选账号"),children:s.jsx(dt,{children:h.find(B=>B.value===e.filter.provider)?.label||r("全部")})}),s.jsx(pt,{children:h.map(B=>s.jsx($e,{value:B.value,children:B.label},B.value))})]})]}),s.jsx("div",{className:"divider","data-provider-divider":"1"}),s.jsxs("div",{className:"group",children:[s.jsx("span",{className:"label",children:r("搜索")}),s.jsx(Fe,{className:"acct-search",type:"search",value:e.filter.q,placeholder:r("账号名 / 邮箱 / 备注"),"aria-label":r("搜索账号"),onChange:B=>A$(B.currentTarget.value)})]}),s.jsx("div",{className:"divider"}),s.jsxs("div",{className:"group",children:[s.jsx("span",{className:"label",children:r("状态")}),s.jsx(En,{options:[{value:"all",label:r("全部"),count:p.enabledAll??0},{value:"enabled",label:r("启用"),count:p.enabled??0},{value:"disabled",label:r("禁用"),count:p.disabled??0}],value:e.filter.enabled,onValueChange:B=>aR("enabled",B),"aria-label":r("启用状态")})]}),s.jsxs("div",{className:"group",children:[s.jsx("span",{className:"label",children:r("限额")}),s.jsx(En,{options:[{value:"all",label:r("全部"),count:p.limitAll??0},{value:"normal",label:r("正常"),count:p.normal??0,disabled:e.filter.enabled==="disabled"},{value:"limited",label:r("已限流"),count:p.limited??0,disabled:e.filter.enabled==="disabled"}],value:e.filter.limit,onValueChange:B=>aR("limit",B),"aria-label":r("限额状态")})]}),s.jsxs("div",{className:"actions",children:[s.jsx(ne,{id:"btn-fold-groups",variant:"outline",disabled:!f.length,title:r(D?"展开全部分组":"折叠全部分组(只留组头)"),onClick:()=>M$(f.map(B=>B.provider),!D),children:r(D?"展开全部":"折叠全部")}),s.jsx(ne,{id:"btn-add-account-2",variant:"default",title:r("登录 / 导入一个新账号"),onClick:()=>{Pe().wbAddAccountModal?.open?.(),Pe().wbAccountAddForms?.syncAddProvider?.()},children:r("添加账号")}),s.jsx(ne,{id:"btn-query-usage",variant:"outline",disabled:e.usageBusy||!c.some(Ea),title:r("查询全部账号的余额(含已禁用账号 —— 禁用只表示不参与转发)"),onClick:()=>{NR()},children:e.usageBusy?r("查询中…"):r("查询余额")})]})]}),s.jsxs("div",{className:Ue("batch-bar",E&&"active"),id:"batch-bar",children:[s.jsxs("label",{className:"batch-select-all",children:[s.jsx(Pr,{id:"batch-select-all",checked:P,indeterminate:!P&&R,disabled:!A.length,"aria-label":r("全选当前筛选结果"),onCheckedChange:B=>cR(A,B)}),s.jsx("span",{id:"batch-select-label",children:A.length?r("全选当前筛选结果({n} 个)",{n:A.length}):r("没有可全选的账号")})]}),s.jsxs("span",{className:"batch-count",children:[r("已选")," ",s.jsx("b",{id:"batch-count",children:e.selected.size})," ",r("个 · 共")," ",s.jsx("b",{id:"accounts-count",children:c.length})," ",r("个")]}),s.jsx("span",{className:"batch-summary",id:"accounts-group-summary",children:d.length?r("{groups} 家 · 显示 {rows} 行",{groups:d.length,rows:A.length}):""}),S?s.jsx("span",{className:"batch-hidden",title:r("筛选 / 搜索 / 分页生效时组顺位不可拖动(别家的组整组不可见,落点没法定位)"),children:r("组顺位已锁定")}):null,ng()?s.jsx(ne,{id:"btn-clear-filter",variant:"ghost",size:"xs",title:r("清空搜索与三个筛选档位,回到全部账号"),onClick:()=>lR(),children:r("清除筛选")}):null,C?s.jsx("span",{className:"batch-hidden",id:"batch-hidden-hint",children:r("另有 {n} 个已勾选账号被当前筛选隐藏,仍会参与操作",{n:C})}):null,s.jsxs("div",{className:"batch-actions",children:[s.jsx(ne,{id:"btn-batch-open",variant:"outline",disabled:!E,onClick:()=>kj([...e.selected],"enable"),children:r("批量操作")}),s.jsx(ne,{id:"btn-batch-clear",variant:"outline",disabled:!E,onClick:()=>P$(),children:r("取消选择")})]})]}),s.jsx("div",{className:"acct-scroll",id:"account-list",ref:t,children:o?c.length?b.length?s.jsxs("table",{className:"acct-table",ref:t7,style:{minWidth:`${H8(v.map(B=>B.key))}px`},children:[s.jsx(n7,{}),s.jsx(YG,{namesHidden:e.namesHidden,allPicked:P,somePicked:R,disabled:!A.length,onToggleAll:B=>cR(A,B)}),d.map(B=>{const L=e.collapsed.has(B.provider),F=w.filter($=>An($)===B.provider);return s.jsxs("tbody",{className:"acct-group","data-provider":B.provider,children:[s.jsx(wG,{group:B,collapsed:L,colSpan:v.length}),L?null:F.map($=>{const X=B.members.findIndex(q=>q.id===$.id)+1,K=e8($,y,X>0?X:1);return s.jsxs(x.Fragment,{children:[s.jsx("tr",{className:Ue("acct-row",!ur($)&&"disabled",K.picked&&"selected"),"data-id":$.id,"data-provider":B.provider,"data-desktop":kf($)?"1":void 0,children:v.map(q=>s.jsx("td",{className:`cell-${q.key} ta-${q.align}`,children:H(q.key,$,K)},q.key))}),s.jsx(KG,{account:$,colSpan:v.length,limitsOpen:uR($.id,"limits"),onClear:(q,Q)=>{W$(q,Q)}})]},$.id)})]},B.provider)})]}):s.jsxs("div",{className:"empty",children:[r("当前筛选条件下没有账号"),ng()?s.jsx("button",{type:"button",className:"link-btn",onClick:()=>lR(),children:r("清除筛选")}):null]}):s.jsx("div",{className:"empty",children:r("暂无账号,请点击右上角「添加账号」")}):s.jsxs("div",{className:"empty",children:[s.jsx("span",{className:"spinner"}),r("正在加载…")]})}),s.jsx($i,{total:b.length,range:j.paged?{start:j.rangeStart,end:j.rangeEnd}:null,page:j.page,pageCount:j.pageCount,size:j.size,onSizeChange:j.setSize,onPageChange:j.goto})]}),s.jsx(M8,{})]})}function t7(e){Ag=e}function n7(){const e=B8(),t=a=>s.jsx("col",{"data-col":a,style:e[a]?{width:`${e[a]}px`}:void 0},a);return s.jsxs("colgroup",{children:[t("pick"),t("order"),t("account"),t("proxy"),t("connections"),t("status"),t("limits"),t("expiry"),t("usage"),t("actions")]})}const OO='.page[data-page="accounts"]';let Wj=null;function MO(){if(Wj)return;const e=document.querySelector(OO);e&&(e.replaceChildren(),Wj=wn.createRoot(e),Ts.flushSync(()=>{Wj.render(s.jsx(e7,{}))}))}n8(),K$(),document.querySelector(OO)?MO():document.addEventListener("DOMContentLoaded",MO,{once:!0});const s7=64,r7=100,a7=r("OpenAI 兼容填到 /v1;Anthropic 填根地址"),l7=r("Anthropic 协议填根地址,不要带 /v1"),o7="https://open.bigmodel.cn/api/paas/v4",i7="https://api.anthropic.com",c7=[{value:"chat_completions",label:"OpenAI - Chat Completions"},{value:"responses",label:"OpenAI - Responses"},{value:"anthropic",label:"Anthropic - Messages"}],Cu="custom-protocol-select",Tu="custom-name-input",Eu="custom-baseurl-input",Hf="custom-apikey-input",Uf="custom-noauth-check",Au="custom-existing-select",Vf="custom-existing-apikey-input",Ff="custom-existing-noauth-check",$f="custom-existing-name-input",u7="custom-create-button",d7="custom-existing-button",f7="custom-existing-remove";let Jj=!1;const DO=()=>gt().wbProviders?.PROTOCOL_OPTIONS||c7,ew=()=>gt().wbProviders?.customList?.()||[],tw="custom-balance",nw="custom-existing-balance",qf=(e,t)=>`${e}-${t}-input`;function IO(e){if(!e)return"";const t=gt().wbPresetProviders?.presetOf?.(e);return String(t?.balance?.scheme||"")}function PO(e,t){const a=Mf(e);if(!a||!a.fields.length)return null;const o={};for(const c of a.fields){const d=dr(qf(t,c.key));d&&(o[c.key]=d)}return Object.keys(o).length?o:null}function _O(e,t){const a=Mf(e);if(a)for(const o of a.fields)Eo(qf(t,o.key),"")}function LO({scheme:e,prefix:t}){const a=Mf(e);return a?s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{children:r("余额查询")}),s.jsxs("div",{className:"col-start-2 row-start-1 flex h-[30px] items-center",children:[s.jsx("span",{className:"text-xs text-subtle",children:r("方案:{label}",{label:a.label})}),s.jsx("span",{className:"ml-1.5 text-xs text-subtle",children:a.endpoint})]}),s.jsx("span",{className:"hint",children:a.note})]}),a.fields.map(o=>s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{htmlFor:qf(t,o.key),children:o.label}),s.jsx(Fe,{id:qf(t,o.key),type:o.secret?"password":"text",autoComplete:o.secret?"new-password":"off",placeholder:o.placeholder,..._r(qf(t,o.key))}),s.jsx("span",{className:"hint",children:o.hint})]},o.key))]}):null}async function sw(e,t){if(!Jj){Jj=!0,e(!0);try{await t()}finally{Jj=!1,e(!1)}}}async function zO(e){gt().wbAddAccountModal?.close?.(),gt().wbProviders?.refreshCustom?.(),await gt().wbApp?.refresh?.(),Ln(e)}function Gf(e,t){Eo(e,t?"1":"")}function Rg(e){return dr(e)==="1"}function BO({checked:e,note:t,onChange:a}){return s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{children:r("鉴权")}),s.jsx("div",{className:"col-start-2 row-start-1 flex h-[30px] items-center",children:s.jsxs("label",{className:"inline-flex cursor-pointer items-center gap-2.5",children:[s.jsx(Pr,{checked:e,"aria-label":r("该上游无需鉴权"),onCheckedChange:o=>a(o===!0)}),s.jsx("span",{className:"text-xs text-subtle",children:r("该上游无需鉴权(不发送鉴权头)")})]})}),s.jsx("span",{className:"hint",children:t})]})}function p7({mode:e,providerHint:t,presetKey:a,showToken:o,version:c}){const d=DO(),[f,p]=x.useState(()=>dr(Cu)||d[0].value),[h,b]=x.useState(""),[y,v]=x.useState(""),[j,w]=x.useState(()=>dr(Au)),[S,N]=x.useState(()=>Rg(Uf)),[k,A]=x.useState(()=>Rg(Ff)),P=x.useMemo(()=>ew(),[c]),C=(t&&P.some(B=>B.id===t)?t:"")||(P.some(B=>B.id===j)?j:P[0]?.id||""),E=P.find(B=>B.id===C)?.name||r("该提供商");x.useEffect(()=>{Eo(Au,C)},[C]),x.useEffect(()=>{if(!a)return;const B=gt().wbPresetProviders?.presetOf?.(a);if(!B)return;const L=B.account?.noAuth===!0;if(e!=="create"){A(L),Gf(Ff,L);return}Eo(Tu,B.name||""),Eo(Eu,B.baseUrl||""),B.protocol&&(p(B.protocol),Eo(Cu,B.protocol)),N(L),Gf(Uf,L),b(B.hint||""),v(B.placeholder||"")},[e,a,t,o]);const D=f==="anthropic",I=h||(D?l7:a7),O=y||(D?i7:o7),_=e==="create"?IO(a):"",z=e==="existing"?String(P.find(B=>B.id===C)?.balanceScheme||""):"",H=(B,L)=>F=>{L(F),Gf(B,F)};return s.jsxs(s.Fragment,{children:[s.jsxs(dn,{hidden:e!=="create",children:[s.jsxs("div",{className:"add-panel-head",children:[s.jsx("h3",{children:r("上游信息")}),s.jsx("span",{children:r("创建这个提供商,并同时建立它的第一个账号。")})]}),s.jsxs("div",{className:"add-form",children:[s.jsxs("div",{className:"add-field",children:[s.jsxs(Je,{htmlFor:Tu,children:[r("名称"),s.jsx("i",{className:"req","aria-hidden":"true",children:"*"})]}),s.jsx(Fe,{id:Tu,type:"text",maxLength:s7,"aria-required":"true",placeholder:r("如:智谱 GLM"),..._r(Tu)}),s.jsx("span",{className:"hint",children:r("1~64 个字符,账号列表里按它分组显示")})]}),s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{htmlFor:Cu,children:r("协议")}),s.jsxs(ut,{value:f,onValueChange:B=>{const L=String(B);p(L),Eo(Cu,L),b("")},children:[s.jsx(ft,{id:Cu,className:"col-start-2 row-start-1 w-full",children:s.jsx(dt,{children:d.find(B=>B.value===f)?.label||f})}),s.jsx(pt,{children:d.map(B=>s.jsx($e,{value:B.value,children:B.label},B.value))})]})]}),s.jsxs("div",{className:"add-field",children:[s.jsxs(Je,{htmlFor:Eu,children:["Base URL",s.jsx("i",{className:"req","aria-hidden":"true",children:"*"})]}),s.jsx(Fe,{id:Eu,type:"text","aria-required":"true",placeholder:O,..._r(Eu)}),s.jsx("span",{className:"hint",id:"custom-baseurl-hint",children:I})]}),s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{htmlFor:Hf,children:"API Key"}),s.jsx(Fe,{id:Hf,type:"password",autoComplete:"new-password",placeholder:"sk-…",disabled:S,..._r(Hf)}),s.jsx("span",{className:"hint",children:r(S?"不需要 Key:转发与拉取模型都不发送鉴权头":"上游不要鉴权时留空,并勾选下面一项(留空又不勾会被当成未配置凭证)")})]}),s.jsx(BO,{checked:S,note:r(S?"已声明无需鉴权:账号会被正常选路,出网时不带任何鉴权头":"本地 Ollama、OpenCode Zen 免费档这类上游要勾上,否则账号不可用"),onChange:H(Uf,N)}),s.jsx(LO,{scheme:_,prefix:tw})]})]}),s.jsxs(dn,{hidden:e!=="existing",children:[s.jsxs("div",{className:"add-panel-head",children:[s.jsx("h3",{children:r("账号信息")}),s.jsxs("span",{children:[r("添加到 "),s.jsx("b",{children:E}),r(" 同一家可以放多把 key,按优先级轮换。")]})]}),s.jsxs("div",{className:"add-form",children:[s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{htmlFor:Au,children:r("提供商")}),s.jsxs(ut,{value:C,onValueChange:B=>w(String(B)),children:[s.jsx(ft,{id:Au,className:"col-start-2 row-start-1 w-full",children:s.jsx(dt,{children:E})}),s.jsx(pt,{children:P.map(B=>s.jsx($e,{value:B.id,children:B.name||B.id},B.id))})]})]}),s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{htmlFor:Vf,children:"API Key"}),s.jsx(Fe,{id:Vf,type:"password",autoComplete:"new-password",placeholder:"sk-…",disabled:k,..._r(Vf)}),s.jsx("span",{className:"hint",children:r(k?"不需要 Key:转发与拉取模型都不发送鉴权头":"上游不要鉴权时留空,并勾选下面一项(留空又不勾会被当成未配置凭证)")})]}),s.jsx(BO,{checked:k,note:r(k?"已声明无需鉴权:账号会被正常选路,出网时不带任何鉴权头":"同一家可以混着放:有 Key 的账号与无鉴权账号各按各的规则走;无需鉴权的账号也能并存多条(各自可配不同的出网代理)"),onChange:H(Ff,A)}),s.jsx(LO,{scheme:z,prefix:nw}),s.jsxs("div",{className:"add-field",children:[s.jsx(Je,{htmlFor:$f,children:r("备注名")}),s.jsx(Fe,{id:$f,type:"text",maxLength:r7,placeholder:r("可选"),..._r($f)}),s.jsx("span",{className:"hint",children:r("留空则用提供商名称")})]})]})]})]})}function HO(e,t){const a=Co(t);Ln(r("添加失败:{reason}",{reason:a}),"err"),e.setHint(a)}async function m7(e){const t=dr(Tu),a=dr(Cu)||DO()[0].value,o=dr(Eu),c=dr(Hf),d=Rg(Uf);if(!t){Ln(r("请填写名称"),"err");return}if(!o){Ln(r("请填写 Base URL"),"err");return}await sw(e.setBusy,async()=>{const f={name:t,protocol:a,baseUrl:o},p=e.presetKey?gt().wbPresetProviders?.presetOf?.(e.presetKey):null,h=p?.quirks||{};h.urlSuffix&&(f.urlSuffix=h.urlSuffix),h.headers&&Object.keys(h.headers).length&&(f.headers={...h.headers}),h.anthropicToolType&&(f.anthropicToolType=h.anthropicToolType),p?.clientEmulation&&(f.clientEmulation=p.clientEmulation);const b=IO(e.presetKey);b&&(f.balanceScheme=b);const y=PO(b,tw);y&&(f.balanceQuery=y),d?f.noAuth=!0:c&&(f.apiKey=c),e.setHint("");try{const v=await gt().wbProviders?.customRequest?.("POST","/api/custom-providers",f);Lj([Tu,Eu,Hf]),_O(b,tw),Gf(Uf,!1);const j=v?.provider?.name||t;await zO(r("✅ 已创建自定义提供商「{name}」并添加账号",{name:j}))}catch(v){HO(e,v)}})}async function h7(e){const t=dr(Au);if(!t){Ln(r("请先选择一个自定义提供商"),"err");return}const a=dr(Vf),o=Rg(Ff),c=dr($f),d=String(ew().find(f=>f.id===t)?.balanceScheme||"");await sw(e.setBusy,async()=>{const f={provider:t};o?f.noAuth=!0:a&&(f.apiKey=a),c&&(f.name=c);const p=PO(d,nw);p&&(f.balanceQuery=p),e.setHint("");try{const h=await gt().wbProviders?.customRequest?.("POST","/api/accounts",f);Lj([Vf,$f]),_O(d,nw),Gf(Ff,!1);const b=h?.account?.name||ew().find(j=>j.id===t)?.name||"",y=b?`:${b}`:"",v=h?.created!==!1?r("✅ {label}账号已添加{name}",{label:"",name:y}):r("✅ 已更新已有账号{name}(同一把 API Key 只保留一条,重复添加会合并)",{name:y});await zO(v)}catch(h){HO(e,h)}})}async function g7(){const e=dr(Au);if(!e){Ln(r("请先选择一个自定义提供商"),"err");return}const t=gt().wbCustomProvidersUi?.remove;if(typeof t!="function"){Ln(r("删除功能不可用(脚本未就绪)"),"err");return}await sw(()=>{},async()=>{await t(e)&>().wbAddAccountModal?.close?.()})}function x7({mode:e,presetKey:t}){const[a,o]=x.useState(!1),[c,d]=x.useState(""),f={presetKey:t,setBusy:o,setHint:d};return s.jsxs(s.Fragment,{children:[s.jsx("span",{className:`add-foot-hint${c?" err":""}`,id:"add-form-foot-hint",children:c}),s.jsxs("span",{className:"add-foot-actions",id:"add-form-foot-actions",children:[e==="existing"?s.jsx(ne,{id:f7,variant:"destructive",title:r("级联删除名下全部账号,不可恢复"),onClick:()=>{g7()},children:r("删除此提供商")}):null,s.jsx(ne,{id:u7,hidden:e!=="create",disabled:a,onClick:()=>{m7(f)},children:r(a&&e==="create"?"提交中…":"创建并添加账号")}),s.jsx(ne,{id:d7,hidden:e!=="existing",disabled:a,onClick:()=>{h7(f)},children:r(a&&e==="existing"?"提交中…":"添加账号")})]})]})}const rw={step:"pick",accountType:Kj,search:"",provider:Gi,providerHint:"",presetKey:"",showToken:0},UO=()=>gt().wbProviders?.customList?.()||[];function b7({onClose:e,onReady:t}){const[a,o]=x.useState(rw),[c,d]=x.useState(0),f=x.useCallback(()=>d(R=>R+1),[]),p=x.useCallback(()=>{o(rw);const R=gt().wbProviders;(R?.all?.()||[]).length||R?.load?.().then(f),R?.refreshCustom?.().then(f)},[f]),h=x.useCallback(()=>{p(),o({...rw,step:"form",provider:ju})},[p]);x.useEffect(()=>(t({reset:p,openNewCustomForm:h}),()=>t(null)),[t,p,h]),x.useEffect(()=>{gt().wbWebLogin?.refresh?.()},[]),x.useEffect(()=>{gt().wbProviders?.refreshCustom?.().then(f)},[f]),x.useEffect(()=>{a.step!=="form"||a.provider!==ju||gt().wbProviders?.refreshCustom?.().then(f)},[a.step,a.provider,a.showToken,f]);const b=dq(a.provider),y=a.provider===ju,v=a.provider===Gi||y||!!b,j=a.providerHint?"existing":"create",w=a.providerHint?UO().find(R=>R.id===a.providerHint)?.name||"":a.presetKey&>().wbPresetProviders?.presetOf?.(a.presetKey)?.name||"",S=a.provider===Gi?Bj:r(gt().wbProviders?.labelOf?.(a.provider)||a.provider),N=y?a.providerHint||a.presetKey?r("登录 / 添加 {name} 账号",{name:w||r("自定义提供商")}):r("新建自定义提供商"):r("登录 / 添加 {name} 账号",{name:S});function k(R){if(!R)return;const C={step:"form",showToken:a.showToken+1};if(R===Xj)o(E=>({...E,...C,provider:ju,providerHint:"",presetKey:""}));else if(R.startsWith(zf)){const E=R.slice(zf.length),D=KR(gt().wbPresetProviders?.presetOf?.(E));o(I=>({...I,...C,provider:ju,providerHint:D?.id||"",presetKey:E}))}else UO().some(E=>E.id===R)?o(E=>({...E,...C,provider:ju,providerHint:R,presetKey:""})):o(E=>({...E,...C,provider:R,providerHint:"",presetKey:""}))}const A=()=>o(R=>({...R,step:"pick"})),P=a.accountType===dG;return s.jsx(Yt,{open:!0,onOpenChange:R=>{R||e()},children:s.jsxs(Xt,{className:"w-[min(1100px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("添加账号")})}),s.jsx(Lt,{children:s.jsx(vG,{accountType:a.accountType,onAccountTypeChange:R=>o(C=>({...C,accountType:R})),search:a.search,onSearchChange:R=>o(C=>({...C,search:R})),onPick:k,version:c,importing:P,importPanel:void 0})}),null,s.jsx(Yt,{open:a.step==="form",onOpenChange:R=>{R||A()},children:s.jsxs(Xt,{keepMounted:!0,overlayForceRender:!0,className:"w-[min(620px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:N})}),s.jsxs(Lt,{children:[s.jsx(uG,{active:a.provider===Gi}),JR.map(R=>s.jsx(aG,{config:R,active:a.provider===R.provider},R.provider)),s.jsx("div",{className:"add-provider-block",style:y?void 0:{display:"none"},children:s.jsx(p7,{mode:j,providerHint:a.providerHint,presetKey:a.presetKey,showToken:a.showToken,version:c})}),v?null:s.jsx("div",{className:"add-provider-block",children:s.jsxs(dn,{children:[s.jsx("h3",{children:r("该提供商账号添加功能即将上线")}),s.jsx("p",{children:r("「{name}」的账号添加功能还在开发中,敬请期待。",{name:S})})]})})]}),y?s.jsx(tn,{children:s.jsx(x7,{mode:j,presetKey:a.presetKey})}):null]})})]})})}let Do=null,Ru=null,Xi=null,Og=null;function v7(){Do&&(Do.unmount(),Do=null),Ru&&(Ru.remove(),Ru=null),Xi=null}function VO(){Ru=document.createElement("div"),document.body.append(Ru),Do=wn.createRoot(Ru),Do.render(s.jsx(b7,{onClose:FO,onReady:e=>{if(Xi=e,!e)return;const t=Og;Og=null,t?.()}}))}function FO(){gt().wbWebLogin?.cancelIfActive?.(""),gt().wbAutoclawOauth?.cancel?.(),v7()}function aw(){if(Do){Xi?.reset();return}Og=()=>Xi?.reset(),VO()}function y7(){if(Do){Xi?.openNewCustomForm();return}Og=()=>Xi?.openNewCustomForm(),VO()}for(const e of["btn-add-account","btn-add-account-2"])document.getElementById(e)?.addEventListener("click",aw);const $O=window;$O.wbAddAccountModal={open:aw,close:FO},$O.wbAccountAddForms={syncAddProvider:()=>{Do?Xi?.reset():aw()},openNewCustomForm:y7};const j7=3065;let Ou=null;function qO(){return window.workbuddyDesktop}const w7="same-origin";let Mg=null;function S7(e){const t={};if(!e||typeof e!="object")return t;for(const[a,o]of Object.entries(e))typeof o=="string"&&o&&(t[a]=o);return t}async function N7(e=!1){if(Mg&&!e)return Mg;let t={};try{t=S7(await qO()?.directCredentials?.())}catch{}return Mg=t,t}async function GO(e=!1){if(Ou&&!e)return Ou;const t=qO();if(t?.platform==="web"){const o=typeof window>"u"?"":window.location?.origin;if(o&&o!=="null")return Ou=o,Ou}let a=0;try{const o=await t?.getBackendStatus?.(),c=Number(o?.port);Number.isFinite(c)&&c>0&&(a=c)}catch{}return a>0?(Ou=`http://127.0.0.1:${a}`,Ou):`http://127.0.0.1:${j7}`}async function KO(){const t=await window.workbuddyDesktop?.getModelManage?.(),a=Array.isArray(t?.models)?t.models:[],o=Array.isArray(t?.mappings)?t.mappings:[],c=new Set,d=[],f=new Set;for(const p of a){if(p.enabled!==!0)continue;const h=String(p.provider??""),b=String(p.id??"");!h||!b||(c.add(`${h}${b}`),f.has(b)||(f.add(b),d.push(b)))}for(const p of o){if(p.enabled!==!0||p.isDefault===!0)continue;const h=String(p.provider??""),b=String(p.target??""),y=String(p.alias??"");(!y||!f.has(y)&&c.has(`${h}${b}`))&&(f.add(y),d.push(y))}return d}const lw=`你是 Agent2API 的内置助手,帮用户管理本机的模型转发配置。 说话简洁,别铺垫。配置项一律用等宽字写出准确的 ID(模型名、账号名、提供商名),不要用「那个模型」这类含糊说法。 调用改动类工具之前,先用一句话说清将要改动什么;确认卡由界面渲染,不要跳过它直接执行。 -拿不准的配置先去读(获取模型列表 / 查询账号余额),不要凭印象改。`;function YO(e,t){if(typeof e=="string"&&e.trim())return e.trim();if(e&&typeof e=="object"){const a=e.message;if(typeof a=="string"&&a.trim())return a.trim();try{return JSON.stringify(e)}catch{}}return`HTTP ${t}`}function w7(e,t){const a=e instanceof Error?e.message:String(e);return new Error(r("连接网关失败({base}):{message}",{base:t,message:a}))}async function ow(e,t,a=!1){const o=await GO(),c={...await j7()},d=t.body===void 0?void 0:JSON.stringify(t.body);d!==void 0&&(c["Content-Type"]="application/json");let f;try{f=await fetch(o+e,{method:t.method,headers:c,body:d,signal:t.signal,credentials:v7})}catch(p){throw t.signal?.aborted?p:w7(p,o)}return f.status===401&&!a?(await f.text().catch(()=>""),Mg=null,ow(e,t,!0)):f}async function XO(e,t,a){const o=await ow(t,{method:e,body:a}),c=await o.text();let d=null;try{d=c?JSON.parse(c):null}catch{d=null}const f=d;if(!o.ok||f?.success===!1){const h=f?.error??d?.error,b=YO(h,o.status);throw o.status===404?new Error(r("这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}",{message:b})):new Error(b)}return f&&typeof f=="object"&&"data"in f?f.data:d}async function QO(){const t=(await XO("GET","/api/assistant/config"))?.config;if(!t||typeof t!="object")throw new Error(r("配置响应不完整"));const a=typeof t.model=="string"?t.model:t.model?.id;return{model:typeof a=="string"&&a.trim()?a:null,systemPromptEnabled:t.systemPromptEnabled!==!1,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:"",tools:t.tools&&typeof t.tools=="object"?t.tools:{}}}async function ZO(e){const t=await XO("PUT","/api/assistant/config",{config:e});return t?.config&&typeof t.config=="object"?t.config:e}function S7(e){return e.map(t=>{if(t.role==="tool")return{role:"tool",tool_call_id:t.toolCallId,content:t.content};if(t.role==="assistant"){const a={role:"assistant",content:t.content||""};return t.toolCalls?.length&&(a.tool_calls=t.toolCalls.map(o=>({id:o.id,type:"function",function:{name:o.name,arguments:o.arguments}}))),a}return{role:"user",content:t.content}})}async function N7(e){const t={messages:S7(e.messages)};e.tools.length&&(t.tools=e.tools);const a=await ow("/api/assistant/chat",{method:"POST",body:t,signal:e.signal});if(!a.ok||!a.body){const y=await a.text().catch(()=>"");let v=null;try{v=y?JSON.parse(y):null}catch{}const j=v?.error,w=YO(j,a.status);throw a.status===404?new Error(r("这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}",{message:w})):new Error(w)}const o=a.body.getReader(),c=new TextDecoder;let d="";const f=new Map;let p="";const h=y=>{for(const v of y.split(` +拿不准的配置先去读(获取模型列表 / 查询账号余额),不要凭印象改。`;function YO(e,t){if(typeof e=="string"&&e.trim())return e.trim();if(e&&typeof e=="object"){const a=e.message;if(typeof a=="string"&&a.trim())return a.trim();try{return JSON.stringify(e)}catch{}}return`HTTP ${t}`}function k7(e,t){const a=e instanceof Error?e.message:String(e);return new Error(r("连接网关失败({base}):{message}",{base:t,message:a}))}async function ow(e,t,a=!1){const o=await GO(),c={...await N7()},d=t.body===void 0?void 0:JSON.stringify(t.body);d!==void 0&&(c["Content-Type"]="application/json");let f;try{f=await fetch(o+e,{method:t.method,headers:c,body:d,signal:t.signal,credentials:w7})}catch(p){throw t.signal?.aborted?p:k7(p,o)}return f.status===401&&!a?(await f.text().catch(()=>""),Mg=null,ow(e,t,!0)):f}async function XO(e,t,a){const o=await ow(t,{method:e,body:a}),c=await o.text();let d=null;try{d=c?JSON.parse(c):null}catch{d=null}const f=d;if(!o.ok||f?.success===!1){const h=f?.error??d?.error,b=YO(h,o.status);throw o.status===404?new Error(r("这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}",{message:b})):new Error(b)}return f&&typeof f=="object"&&"data"in f?f.data:d}async function QO(){const t=(await XO("GET","/api/assistant/config"))?.config;if(!t||typeof t!="object")throw new Error(r("配置响应不完整"));const a=typeof t.model=="string"?t.model:t.model?.id;return{model:typeof a=="string"&&a.trim()?a:null,systemPromptEnabled:t.systemPromptEnabled!==!1,systemPrompt:typeof t.systemPrompt=="string"?t.systemPrompt:"",tools:t.tools&&typeof t.tools=="object"?t.tools:{}}}async function ZO(e){const t=await XO("PUT","/api/assistant/config",{config:e});return t?.config&&typeof t.config=="object"?t.config:e}function C7(e){return e.map(t=>{if(t.role==="tool")return{role:"tool",tool_call_id:t.toolCallId,content:t.content};if(t.role==="assistant"){const a={role:"assistant",content:t.content||""};return t.toolCalls?.length&&(a.tool_calls=t.toolCalls.map(o=>({id:o.id,type:"function",function:{name:o.name,arguments:o.arguments}}))),a}return{role:"user",content:t.content}})}async function T7(e){const t={messages:C7(e.messages)};e.tools.length&&(t.tools=e.tools);const a=await ow("/api/assistant/chat",{method:"POST",body:t,signal:e.signal});if(!a.ok||!a.body){const y=await a.text().catch(()=>"");let v=null;try{v=y?JSON.parse(y):null}catch{}const j=v?.error,w=YO(j,a.status);throw a.status===404?new Error(r("这一版网关还没有 AI 助手接口(HTTP 404):后端还是旧的,重新编译 / 重启客户端后再试。{message}",{message:w})):new Error(w)}const o=a.body.getReader(),c=new TextDecoder;let d="";const f=new Map;let p="";const h=y=>{for(const v of y.split(` `)){const j=v.trimEnd();if(!j.startsWith("data:"))continue;const w=j.slice(5).trim();if(!w)continue;if(w==="[DONE]")return!0;let S;try{S=JSON.parse(w)}catch{continue}const N=Array.isArray(S.choices)?S.choices[0]:void 0,k=N?.delta||{},A=typeof k.content=="string"?k.content:"";if(A&&e.onEvent({type:"delta",text:A}),Array.isArray(k.tool_calls))for(const P of k.tool_calls){const R=Number(P.index??0),C=f.get(R)||{id:"",name:"",arguments:""};typeof P.id=="string"&&P.id&&(C.id=P.id);const E=P.function||{};typeof E.name=="string"&&E.name&&(C.name=E.name),typeof E.arguments=="string"&&(C.arguments+=E.arguments),f.set(R,C)}typeof N?.finish_reason=="string"&&(p=N.finish_reason)}return!1};for(;;){const{done:y,value:v}=await o.read();if(y)break;d+=c.decode(v,{stream:!0});let j=d.indexOf(` `);for(;j>=0;){const w=d.slice(0,j);if(d=d.slice(j+2),h(w)){await o.cancel().catch(()=>{}),d="",j=-1;break}j=d.indexOf(` -`)}}const b=[...f.entries()].sort((y,v)=>y[0]-v[0]).map(([,y])=>y);b.length&&e.onEvent({type:"toolCalls",calls:b}),e.onEvent({type:"done",finishReason:p})}function iw(e){const t=String(e||"").trim();if(!t)return{};try{const a=JSON.parse(t);return a&&typeof a=="object"&&!Array.isArray(a)?a:{}}catch{return{}}}function el(){const e=window.workbuddyDesktop;if(!e)throw new Error(r("桌面桥不可用(本页需要在 Agent2API 客户端里打开)"));return e}function Mu(){window.wbApp?.refresh?.()}function at(e,t=256){return typeof e=="string"?e.trim().slice(0,t):""}function Qi(e){return typeof e=="boolean"?e:void 0}function Kf(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function Du(e){return Array.isArray(e)?e.filter(t=>!!t&&typeof t=="object"):[]}function Dl(e){return(e instanceof Error?e.message:String(e)).replace(/^Error:\s*/,"")}async function Dg(){const e=await el().getState?.();return(Array.isArray(e?.accounts)?e.accounts:[]).slice().sort((a,o)=>Number(a.priority??0)-Number(o.priority??0))}function Yf(e){return at(e.name)||at(e.nickname)||at(e.id)||r("未命名账号")}function cw(e,t){const a=t.trim();if(!a)throw new Error(r("没给账号名"));const o=e.find(p=>at(p.id)===a);if(o)return o;const c=e.filter(p=>Yf(p)===a);if(c.length===1)return c[0];const d=e.filter(p=>Yf(p).includes(a));if(d.length===1)return d[0];const f=(c.length?c:d).map(Yf).slice(0,6);throw f.length?new Error(r("「{name}」匹配到多个账号:{list},请用完整账号名重试",{name:a,list:f.join("、")})):new Error(r("没有找到账号「{name}」",{name:a}))}const k7={id:"list_models",label:r("获取模型列表"),kind:"read",desc:"读取各提供商的模型清单(模型 ID、所属提供商、是否启用、已挂的映射名)以及全部映射。回答「有哪些模型」「哪些模型在多家重复」「某个名字挂在哪」这类问题之前先调用它。",params:{type:"object",properties:{provider:{type:"string",description:"只看某一家(提供商 id,如 codearts / workbuddy);不填 = 全部"},keyword:{type:"string",description:"按模型 ID 或映射名做子串过滤;不填 = 不过滤"},enabledOnly:{type:"boolean",description:"true = 只看已启用的模型"}}},run:async e=>{const t=at(e.provider,64).toLowerCase(),a=at(e.keyword,128).toLowerCase(),o=Qi(e.enabledOnly)===!0,c=await el().getModelManage?.(),f=(Array.isArray(c?.models)?c.models:[]).filter(p=>!(t&&at(p.provider).toLowerCase()!==t||o&&p.enabled!==!0||a&&![at(p.id),at(p.name),...Array.isArray(p.aliases)?p.aliases.map(b=>at(b)):[]].join(" ").toLowerCase().includes(a))).map(p=>({provider:at(p.provider),id:at(p.id),enabled:p.enabled===!0,aliases:Array.isArray(p.aliases)?p.aliases.map(h=>at(h)):[]}));return{text:JSON.stringify({total:f.length,models:f.slice(0,400)}),summary:r("{n} 个模型",{n:f.length})}}},C7={id:"list_account_balance",label:r("查询账号余额"),kind:"read",desc:"查询各账号的余额 / 剩余额度(会向各上游发起真实的余额查询)。回答「还剩多少」「哪个账号快用完了」这类问题之前先调用它。",params:{type:"object",properties:{account:{type:"string",description:"只查某个账号(账号名或 id);不填 = 全部账号"}}},run:async e=>{const t=at(e.account,128),a=t?at(cw(await Dg(),t).id):"",o=await el().getAllBalances?.(a||void 0),c=Array.isArray(o?.results)?o.results:o,d=Array.isArray(c)?c:[];return{text:JSON.stringify({accounts:d.slice(0,60)}).slice(0,12e3),summary:r("{n} 个账号",{n:d.length})}}},T7={id:"set_model_enabled",label:r("启用 / 禁用模型"),kind:"write",desc:"开启或关闭某几个模型在网关上的可用性(粒度是「提供商 × 模型」)。禁用后下游请求不再路由到它。一次可以改多个。",params:{type:"object",properties:{items:{type:"array",description:"要改的模型列表",items:{type:"object",properties:{provider:{type:"string",description:"提供商 id"},id:{type:"string",description:"模型 ID"},enabled:{type:"boolean",description:"true = 启用,false = 禁用"}},required:["provider","id","enabled"]}}},required:["items"]},plan:e=>{const t=Du(e.items);return{lead:r("将修改 {n} 个模型的启停状态:",{n:t.length}),table:{head:[r("提供商"),r("模型"),r("改成")],rows:t.map(a=>[at(a.provider),at(a.id),Qi(a.enabled)===!1?r("禁用"):r("启用")])},note:r("改动立即生效;禁用后指向它的请求会落到下一个候选。")}},run:async e=>{const t=el(),a=Du(e.items),o=[];for(const c of a){const d=at(c.provider),f=at(c.id),p=Qi(c.enabled);if(!d||!f||p===void 0)throw new Error(r("参数不完整(需要 provider / id / enabled)"));await t.setModelState?.({provider:d,id:f,enabled:p}),o.push(`${p?"启用":"禁用"} ${d}/${f}`)}return Mu(),{text:JSON.stringify({ok:!0,changed:o}),summary:r("已修改 {n} 个模型",{n:o.length})}}},E7={id:"set_model_mapping",label:r("设置模型映射 ID"),kind:"write",desc:"给模型新增或删除映射名(对外名)。同一对外名可以在多个提供商各挂一条:下游用同一个名字请求,网关按账号优先级主备切换;也可以给不同家的同名模型各加前缀(如 ca/claude-sonnet-4.5)来精确指定某一家。一次可以改多条。删掉的映射名只是不再对外可用,不删任何模型。",params:{type:"object",properties:{items:{type:"array",description:"要改的映射列表",items:{type:"object",properties:{provider:{type:"string",description:"提供商 id"},id:{type:"string",description:"上游模型 ID"},alias:{type:"string",description:"要新增 / 删除的映射名(对外名)"},action:{type:"string",enum:["add","remove"],description:"add = 新增映射,remove = 删除映射"}},required:["provider","id","alias","action"]}}},required:["items"]},plan:e=>{const t=Du(e.items),a=t.filter(d=>at(d.action)!=="remove").length,o=t.length-a,c=[];return a&&c.push(r("新增 {n} 条",{n:a})),o&&c.push(r("删除 {n} 条",{n:o})),{lead:r("将改动 {n} 条映射({parts}):",{n:t.length,parts:c.join("、")}),table:{head:[r("提供商"),r("上游模型"),r("操作"),r("映射名")],rows:t.map(d=>[at(d.provider),at(d.id),at(d.action)==="remove"?r("删除"):r("新增"),at(d.alias)])},note:r("只改映射名,不动上游模型 ID;映射立即生效(新对话即可用)。")}},run:async e=>{const t=el(),a=Du(e.items),o=[];for(const c of a){const d=at(c.provider),f=at(c.id),p=at(c.alias,128);if(!d||!f||!p)throw new Error(r("参数不完整(需要 provider / id / alias)"));at(c.action)==="remove"?(await t.removeModelMapping?.(p,f,d),o.push(`删除 ${p} → ${d}/${f}`)):(await t.addModelMapping?.(p,f,d),o.push(`新增 ${p} → ${d}/${f}`))}return Mu(),{text:JSON.stringify({ok:!0,changed:o}),summary:r("已改动 {n} 条映射",{n:o.length})}}},WO=["maxInputTokens","maxOutputTokens","supportsToolCall","supportsImages","supportsVideo","supportsReasoning"],uw={maxInputTokens:r("上下文窗口"),maxOutputTokens:r("最大输出 Token"),supportsToolCall:r("工具调用"),supportsImages:r("图片识别"),supportsVideo:r("视频识别"),supportsReasoning:r("深度推理")},A7={id:"set_model_capability",label:r("修改模型能力"),kind:"write",desc:"修改某个模型对下游声明的能力位:上下文窗口、最大输出 Token、工具调用 / 图片 / 视频 / 深度推理四个布尔位。只传要改的键;布尔位给 null 表示恢复上游原值。",params:{type:"object",properties:{provider:{type:"string",description:"提供商 id"},id:{type:"string",description:"模型 ID"},capabilities:{type:"object",description:"要改的能力位(只写要改的键)",properties:{maxInputTokens:{type:["number","null"],description:"上下文窗口(token 数)"},maxOutputTokens:{type:["number","null"],description:"最大输出 token 数"},supportsToolCall:{type:["boolean","null"],description:"是否支持工具调用;null = 恢复上游原值"},supportsImages:{type:["boolean","null"],description:"是否支持图片识别;null = 恢复上游原值"},supportsVideo:{type:["boolean","null"],description:"是否支持视频识别;null = 恢复上游原值"},supportsReasoning:{type:["boolean","null"],description:"是否支持深度推理;null = 恢复上游原值"}}}},required:["provider","id","capabilities"]},plan:e=>{const t=e.capabilities&&typeof e.capabilities=="object"?e.capabilities:{},a=WO.filter(o=>o in t);return{lead:r("将修改 {model} 的 {n} 项能力:",{model:`${at(e.provider)} / ${at(e.id)}`,n:a.length}),table:{head:[r("能力"),r("改成")],rows:a.map(o=>{const c=t[o],d=c===null?r("恢复上游原值"):typeof c=="boolean"?r(c?"支持":"不支持"):String(c);return[uw[o]||o,d]})},note:r("能力位决定下游看到的声明(能不能贴图、能塞多少上下文),不改上游的真实行为。")}},run:async e=>{const t=at(e.provider),a=at(e.id),o=e.capabilities&&typeof e.capabilities=="object"?e.capabilities:{},c={};for(const d of WO){if(!(d in o))continue;const f=o[d];if(f===null){c[d]=null;continue}if(d==="maxInputTokens"||d==="maxOutputTokens"){const p=Kf(f);if(p===void 0)throw new Error(r("{key} 需要数字",{key:uw[d]}));c[d]=Math.round(p)}else{const p=Qi(f);if(p===void 0)throw new Error(r("{key} 需要 true / false / null",{key:uw[d]}));c[d]=p}}if(!t||!a||!Object.keys(c).length)throw new Error(r("参数不完整(需要 provider / id / capabilities)"));return await el().setModelCapabilities?.(t,a,c),Mu(),{text:JSON.stringify({ok:!0,provider:t,id:a,capabilities:c}),summary:r("已修改 {n} 项能力",{n:Object.keys(c).length})}}},R7={id:"test_model",label:r("发起模型测试"),kind:"run",desc:"对某个模型发一次真实的对话请求,回报连通性、耗时与首字延迟。会消耗该账号的额度。回答「这个模型通不通 / 快不快」时用它。",params:{type:"object",properties:{provider:{type:"string",description:"提供商 id"},id:{type:"string",description:"模型 ID"},prompt:{type:"string",description:"测试用的用户提示词;不填 = 默认一句问候(最小请求)"}},required:["provider","id"]},plan:e=>({lead:r("将对 {model} 发起一次真实请求:",{model:`${at(e.provider)} / ${at(e.id)}`}),table:{head:[r("项"),r("值")],rows:[[r("提示词"),at(e.prompt)||r("(默认问候)")],[r("代价"),r("消耗该账号额度")]]},note:r("这是真实的上游请求,与生产走同一条链路(会记账,报表排除)。")}),run:async e=>{const t={provider:at(e.provider),model:at(e.id)},a=at(e.prompt,4e3);a&&(t.prompt=a);const o=await el().testModel?.(t),c=String(o?.status??""),d=o?.success===!0,f={ok:d,status:c,error:o?.error??null,reply:String(o?.reply??"").slice(0,1500),duration_ms:o?.duration_ms??null,ttfb_ms:o?.ttfb_ms??null,account:o?.account_name??null};return{text:JSON.stringify(f).slice(0,6e3),summary:d?r("连通 · {ms}ms",{ms:String(f.duration_ms??"")}):r("失败 · {status}",{status:c||"—"})}}},O7={id:"set_provider_order",label:r("调整提供商顺位"),kind:"write",desc:"调整某个提供商在账号全局队列里的**整组顺位**(第几组),决定路由时先试哪一家。这一家的所有账号会作为一整块移动。位置从 1 开始数。",params:{type:"object",properties:{provider:{type:"string",description:"提供商 id"},position:{type:"number",description:"目标组顺位(1 = 最前)"}},required:["provider","position"]},plan:e=>({lead:r("将把「{provider}」整组移到第 {position} 顺位:",{provider:at(e.provider)||r("(未指定)"),position:String(Kf(e.position)??"?")}),note:r("队列是一次性整表提交(后端按新顺序重编优先级),移动会连带调整其它组的相对次序。")}),run:async e=>{const t=at(e.provider).toLowerCase(),a=Kf(e.position);if(!t||a===void 0)throw new Error(r("参数不完整(需要 provider / position)"));const o=await Dg(),c=[];for(const h of o){const b=at(h.id),y=at(h.provider).toLowerCase();if(!b)continue;const v=c[c.length-1];v&&v.provider===y?v.ids.push(b):c.push({provider:y,ids:[b]})}const d=c.findIndex(h=>h.provider===t);if(d<0)throw new Error(r("没有找到提供商「{provider}」的账号",{provider:t}));const f=Math.max(0,Math.min(c.length-1,Math.round(a)-1)),[p]=c.splice(d,1);return c.splice(f,0,p),await el().reorderAccounts?.(c.flatMap(h=>h.ids)),Mu(),{text:JSON.stringify({ok:!0,provider:t,from:d+1,to:f+1,moved:p.ids.length}),summary:r("已移到第 {n} 顺位",{n:f+1})}}},M7={id:"set_account_order",label:r("调整账号优先级"),kind:"write",desc:"把某个账号移动到账号全局队列的第 N 顺位(1 = 最前)。位置决定主备与切换顺序:越靠前越先被使用。",params:{type:"object",properties:{account:{type:"string",description:"账号名或账号 id"},position:{type:"number",description:"目标顺位(1 = 最前)"}},required:["account","position"]},plan:e=>({lead:r("将把账号「{account}」移到第 {position} 顺位:",{account:at(e.account)||r("(未指定)"),position:String(Kf(e.position)??"?")}),note:r("队列是一次性整表提交;位置越靠前,路由时越先被选中。")}),run:async e=>{const t=at(e.account,128),a=Kf(e.position);if(!t||a===void 0)throw new Error(r("参数不完整(需要 account / position)"));const o=await Dg(),c=cw(o,t),d=at(c.id),f=o.map(b=>at(b.id)).filter(Boolean),p=f.indexOf(d);if(p<0)throw new Error(r("账号不在当前队列里"));f.splice(p,1);const h=Math.max(0,Math.min(f.length,Math.round(a)-1));return f.splice(h,0,d),await el().reorderAccounts?.(f),Mu(),{text:JSON.stringify({ok:!0,account:Yf(c),from:p+1,to:h+1}),summary:r("已移到第 {n} 顺位",{n:h+1})}}},D7={id:"set_account_enabled",label:r("启用 / 禁用账号"),kind:"write",desc:"开启或关闭某几个账号的转发资格。禁用只是把它移出队列(余额仍可查),但**如果某条线路只剩它**,禁用会直接断供。一次可以改多个。",params:{type:"object",properties:{items:{type:"array",description:"要改的账号列表",items:{type:"object",properties:{account:{type:"string",description:"账号名或账号 id"},enabled:{type:"boolean",description:"true = 启用,false = 禁用"}},required:["account","enabled"]}}},required:["items"]},plan:e=>{const t=Du(e.items),a=t.filter(o=>Qi(o.enabled)===!1).length;return{lead:r("将修改 {n} 个账号的启用状态(其中禁用 {k} 个):",{n:t.length,k:a}),table:{head:[r("账号"),r("改成")],rows:t.map(o=>[at(o.account),Qi(o.enabled)===!1?r("禁用"):r("启用")])},note:r("禁用会把账号移出转发队列;若这条线路没有别的可用账号,请求会直接失败。")}},run:async e=>{const t=el(),a=Du(e.items),o=await Dg(),c=[];for(const d of a){const f=Qi(d.enabled);if(f===void 0)throw new Error(r("参数不完整(需要 account / enabled)"));const p=cw(o,at(d.account,128));await t.updateAccount?.(at(p.id),{enabled:f}),c.push(`${f?"启用":"禁用"} ${Yf(p)}`)}return Mu(),{text:JSON.stringify({ok:!0,changed:c}),summary:r("已修改 {n} 个账号",{n:c.length})}}},tl=[k7,C7,T7,E7,A7,R7,O7,M7,D7];function dw(e){return tl.find(t=>t.id===e)}function I7(e){return tl.filter(t=>e[t.id]).map(t=>({type:"function",function:{name:t.id,description:t.desc,parameters:t.params}}))}const JO=[{id:"list_models",label:r("获取模型列表"),desc:r("读各提供商的模型清单、映射名与启用状态。")},{id:"list_account_balance",label:r("查询账号余额"),desc:r("读各账号的额度 / 剩余用量。")},{id:"set_model_enabled",label:r("启用 / 禁用模型"),desc:r("开关模型的对外可用性,不改其它属性。")},{id:"set_model_mapping",label:r("设置模型映射 ID"),desc:r("新增 / 删除映射名,支持批量加前缀与同名合并到多家。")},{id:"set_model_capability",label:r("修改模型能力"),desc:r("改上下文窗口 / 最大输出 Token / 工具调用等能力位。")},{id:"test_model",label:r("发起模型测试"),desc:r("对指定模型发一次真实请求,回报连通性与耗时。"),tag:"cost"},{id:"set_provider_order",label:r("调整提供商顺位"),desc:r("改某一家的整组顺位(先试哪一家)。")},{id:"set_account_order",label:r("调整账号优先级"),desc:r("改账号在全局队列里的名次,决定主备与切换顺序。")},{id:"set_account_enabled",label:r("启用 / 禁用账号"),desc:r("禁用会把账号移出转发队列,线路可能因此断供。"),tag:"danger"}],eM=12;function Xf(e,t){return window.wbIcons?.icon?.(e,t)||""}function P7(e){const t=window.wbMarkdown;return t?.render?t.render(e):e.replace(/[&<>]/g,a=>({"&":"&","<":"<",">":">"})[a]||a)}function _7({tone:e}){return e==="plain"?s.jsx("span",{className:"asst-tc-ico",dangerouslySetInnerHTML:{__html:Xf("spark",13)}}):s.jsx("span",{className:Ue("asst-tc-ico",e),children:e==="warn"?"!":"✕"})}function L7({item:e,onConfirm:t}){const a=dw(e.call.name),o=a?.label||e.call.name,c=iw(e.call.arguments),d=Object.keys(c).length?JSON.stringify(c,null,2):"";return s.jsxs("div",{className:Ue("asst-tool-card",e.state==="confirm"&&"confirm",e.state==="confirm"&&a?.kind==="run"&&"danger",e.state==="failed"&&"failed",e.state==="running"&&"running"),children:[s.jsxs("div",{className:"asst-tc-head",children:[e.state==="running"?s.jsx("span",{className:"asst-tc-ico",children:s.jsx("span",{className:"spin"})}):s.jsx(_7,{tone:e.state==="failed"?"bad":e.state==="confirm"?"warn":"plain"}),s.jsx("span",{className:"asst-tc-name",children:o}),e.state==="confirm"&&s.jsx("span",{className:"badge tag warn",children:r("待确认")}),e.state==="done"&&s.jsxs("span",{className:"asst-tc-state",children:["✓ ",e.summary||r("完成")]}),e.state==="failed"&&s.jsx("span",{className:"asst-tc-state bad",children:r("失败")}),e.state==="cancelled"&&s.jsx("span",{className:"asst-tc-state plain",children:r("已取消")})]}),e.state==="confirm"&&e.plan&&s.jsxs("div",{className:"asst-tc-plan",children:[e.plan.lead,e.plan.table&&s.jsxs("table",{className:"asst-mini-table",children:[s.jsx("thead",{children:s.jsx("tr",{children:e.plan.table.head.map((f,p)=>s.jsx("th",{children:f},p))})}),s.jsx("tbody",{children:e.plan.table.rows.slice(0,20).map((f,p)=>s.jsx("tr",{children:f.map((h,b)=>s.jsx("td",{className:b===f.length-1?"mono hl":"",children:h},b))},p))})]}),e.plan.table&&e.plan.table.rows.length>20&&s.jsx("div",{className:"hint",children:r("(仅显示前 20 条,共 {n} 条)",{n:e.plan.table.rows.length})}),e.plan.note&&s.jsx("div",{className:"hint",children:e.plan.note})]}),e.state==="failed"&&e.error&&s.jsx("div",{className:"asst-tc-error",children:e.error}),e.state==="done"&&e.detail&&s.jsxs("details",{children:[s.jsx("summary",{className:"asst-tc-result",style:{cursor:"pointer"},children:r("查看结果")}),s.jsx("pre",{className:"asst-tc-json",children:e.detail})]}),e.state!=="confirm"&&d&&s.jsxs("details",{children:[s.jsx("summary",{className:"asst-tc-result",style:{cursor:"pointer"},children:r("查看参数")}),s.jsx("pre",{className:"asst-tc-json",children:d})]}),e.state==="confirm"&&s.jsxs("div",{className:"asst-tc-foot",children:[s.jsx("span",{className:Ue("hint",a?.kind==="run"&&"danger"),children:a?.kind==="run"?r("这会真实发起上游请求并消耗额度"):r("确认后立即生效")}),s.jsx(ne,{size:"sm",variant:a?.kind==="run"?"destructive":"default",onClick:()=>t(!0),children:r("执行")}),s.jsx(ne,{size:"sm",variant:"outline",onClick:()=>t(!1),children:r("取消")})]})]})}function z7(){const[e,t]=x.useState(null),[a,o]=x.useState([]),[c,d]=x.useState([]),[f,p]=x.useState(!1),[h,b]=x.useState(!1),[y,v]=x.useState(""),[j,w]=x.useState(""),[S,N]=x.useState(!1),k=x.useRef([]),A=x.useRef([]),P=x.useRef(null),R=x.useRef(null),C=x.useRef(null),E=x.useRef(!1),D=x.useRef(0),I=x.useRef(null),O=x.useRef(0),_=x.useCallback(()=>{d(k.current.slice())},[]);x.useEffect(()=>{const M=C.current;if(!M)return;M.scrollHeight-M.scrollTop-M.clientHeight<160&&(M.scrollTop=M.scrollHeight)},[c]);const z=x.useCallback(async()=>{if(!E.current){E.current=!0;try{const M=await QO();t(M),N(!1),D.current=0}catch(M){N(!0),w(r("加载助手设置失败:{message}",{message:Dl(M)})),D.current<3&&(D.current+=1,E.current=!1,setTimeout(()=>{z()},4e3))}try{o(await KO())}catch(M){w(U=>U||r("读取模型清单失败:{message}",{message:Dl(M)}))}}},[]);x.useEffect(()=>{const M=window;return M.wbAssistant={activate:()=>{z()}},z(),()=>{delete M.wbAssistant}},[z]);const H=x.useMemo(()=>{const M={};for(const U of tl)M[U.id]=e?.tools?.[U.id]??!1;return M},[e]),B=x.useMemo(()=>I7(H),[H]),L=Object.values(H).filter(Boolean).length,F=e?.model??null,$=x.useCallback(async(M,U,Z)=>{for(let ae=U;ae{if(O.current>=eM){k.current.push({kind:"notice",text:r("这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。",{n:eM}),bad:!0}),_();return}O.current+=1,p(!0),w("");const M=new AbortController;R.current=M;const U=k.current.push({kind:"bot",text:"",streaming:!0})-1;_();let Z=[],ae=!1;try{await N7({messages:A.current,tools:B,signal:M.signal,onEvent:ue=>{if(ue.type==="delta"){const ve=k.current[U];ve&&ve.kind==="bot"&&(k.current[U]={...ve,text:ve.text+ue.text},_())}ue.type==="toolCalls"&&(Z=ue.calls)}})}catch(ue){ae=!0;const ve=Dl(ue),ee=k.current[U],he=ee&&ee.kind==="bot"?ee.text:"";he?k.current[U]={kind:"bot",text:he}:k.current.splice(U,1);const te=M.signal.aborted;k.current.push({kind:"notice",text:te?r("已停止这次回答。"):r("请求失败:{message}",{message:ve}),bad:!te}),_()}finally{R.current=null}if(ae){p(!1);return}const ce=k.current[U];ce&&ce.kind==="bot"&&(k.current[U]={...ce,streaming:!1});const W=ce&&ce.kind==="bot"?ce.text:"";if(A.current.push({role:"assistant",content:W,toolCalls:Z.length?Z:void 0}),_(),!Z.length){p(!1);return}await $(Z,0,[])},[B,$,_]);I.current=X;const K=x.useCallback(async M=>{const U=P.current;if(!U)return;P.current=null,b(!1);const Z=U.calls[U.index],ae=dw(Z.name),ce=iw(Z.arguments);if(!M||!ae)k.current[U.cardIndex]={kind:"tool",call:Z,state:"cancelled"},U.collected.push({toolCallId:Z.id,content:"用户取消了这次操作,没有做任何改动。"}),_();else{k.current[U.cardIndex]={kind:"tool",call:Z,state:"running"},p(!0),_();try{const W=await ae.run(ce);k.current[U.cardIndex]={kind:"tool",call:Z,state:"done",summary:W.summary,detail:W.text},U.collected.push({toolCallId:Z.id,content:W.text})}catch(W){const ue=Dl(W);k.current[U.cardIndex]={kind:"tool",call:Z,state:"failed",error:ue},U.collected.push({toolCallId:Z.id,content:`工具执行失败:${ue}`})}_()}await $(U.calls,U.index+1,U.collected)},[$,_]),q=x.useCallback(async M=>{const U=M.trim();if(!(!U||f||h)){if(O.current=0,k.current.push({kind:"user",text:U}),A.current.push({role:"user",content:U}),v(""),_(),!e){k.current.push({kind:"notice",text:r("助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。"),bad:!0}),_();return}if(!e.model){k.current.push({kind:"notice",text:r("还没有选择对话模型:先在下方选一个(或去「设置 → AI 助手」里选)。"),bad:!0}),_();return}await X()}},[h,f,e,X,_]),Q=x.useCallback(()=>{R.current?.abort()},[]),G=x.useCallback(()=>{R.current?.abort(),P.current=null,k.current=[],A.current=[],b(!1),p(!1),w(""),_()},[_]),J=x.useCallback(async M=>{if(!M)return;if(!e){w(r("助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。"));return}const U={...e,model:M};t(U);try{await ZO(U)}catch(Z){w(r("保存对话模型失败:{message}",{message:Dl(Z)}))}},[e]),Y=f||h,re=x.useRef(null);return s.jsxs("div",{className:"asst-layout",children:[s.jsxs("div",{className:"asst-top",children:[s.jsxs(ut,{value:F??"",onValueChange:M=>{J(String(M))},children:[s.jsx(ft,{className:"asst-model-pick","aria-label":r("对话模型"),children:s.jsx(dt,{children:F?s.jsxs("span",{className:"flex items-center gap-2",children:[s.jsx("span",{className:Ue("dot","ok")}),s.jsx("span",{className:"mono",children:F})]}):r("选择模型…")})}),s.jsxs(pt,{children:[a.length===0&&s.jsx($e,{value:"none",disabled:!0,children:r("没有可用的模型")}),a.map(M=>s.jsx($e,{value:M,children:M},M))]})]}),s.jsx("span",{className:"asst-top-hint",children:r("助手走本地网关,消耗所选账号的额度")}),s.jsx("span",{className:"spacer"}),s.jsx(ne,{variant:"ghost",size:"sm",onClick:G,disabled:f||h||!c.length,children:r("新对话")})]}),s.jsxs("div",{className:"asst-scroll",ref:C,children:[c.length===0&&s.jsxs("div",{className:"asst-empty",children:[s.jsx("div",{className:"empty-logo",dangerouslySetInnerHTML:{__html:Xf("spark",26)||""}}),s.jsx("h3",{children:r("AI 助手")}),s.jsxs("p",{children:[r("用一句话让我查或改本机的模型转发配置。"),s.jsx("br",{}),r("我会在动手前把改动列给你确认。")]}),s.jsx("div",{className:"asst-suggests",children:[r("看看哪些模型在多家重复了"),r("测一下当前最快的模型通不通"),r("CodeArts 的账号余额还剩多少?"),r("把重复的模型加上家名前缀区分开")].map(M=>s.jsxs("button",{type:"button",onClick:()=>{q(M)},children:["“",M,"”"]},M))}),e!==null&&L===0&&s.jsx("div",{className:"asst-empty-warn",children:r("当前一个工具都没勾选,助手只能纯聊天。去「设置 → AI 助手 → 工具」里勾上需要的工具。")}),S&&s.jsx("div",{className:"asst-empty-warn bad",children:j||r("助手设置读取失败,工具可能已过时。")})]}),c.map((M,U)=>M.kind==="user"?s.jsx("div",{className:"asst-msg user",children:s.jsx("div",{className:"bubble",children:M.text})},U):M.kind==="bot"?!M.text&&M.streaming?s.jsxs("div",{className:"asst-msg bot",children:[s.jsxs("div",{className:"who",children:[s.jsx("span",{className:"ico",dangerouslySetInnerHTML:{__html:Xf("spark",14)||""}})," ",r("助手")]}),s.jsx("div",{className:"body",children:s.jsx("span",{className:"spin"})})]},U):s.jsxs("div",{className:"asst-msg bot",children:[s.jsxs("div",{className:"who",children:[s.jsx("span",{className:"ico",dangerouslySetInnerHTML:{__html:Xf("spark",14)||""}})," ",r("助手")]}),s.jsxs("div",{className:"body",children:[s.jsx("span",{dangerouslySetInnerHTML:{__html:P7(M.text)}}),M.streaming&&s.jsx("span",{className:"asst-caret"})]})]},U):M.kind==="notice"?s.jsx("div",{className:"asst-msg bot",children:s.jsx("div",{className:Ue("asst-empty-warn",M.bad&&"bad"),style:{margin:0},children:M.text})},U):s.jsx(L7,{item:M,onConfirm:Z=>{K(Z)}},U))]}),s.jsxs("div",{className:"asst-composer",children:[s.jsxs("div",{className:"asst-ta-wrap",children:[s.jsx(Mi,{ref:re,rows:1,value:y,placeholder:r(Y?"等这一轮跑完…":"直接说要改什么 / 查什么…(Enter 发送,Shift+Enter 换行)"),disabled:Y,onChange:M=>{v(M.currentTarget.value);const U=M.currentTarget;U.style.height="auto",U.style.height=`${Math.min(U.scrollHeight,132)}px`},onKeyDown:M=>{M.key==="Enter"&&!M.shiftKey&&!M.nativeEvent.isComposing&&(M.preventDefault(),q(y))}}),f?s.jsx("button",{type:"button",className:"asst-send stop",title:r("停止"),onClick:Q,children:"■"}):s.jsx("button",{type:"button",className:"asst-send",title:r("发送"),disabled:Y||!y.trim(),onClick:()=>{q(y)},children:"➤"})]}),s.jsxs("div",{className:"asst-composer-foot",children:[s.jsxs("span",{className:"tools-state",children:[s.jsx("span",{className:"ico",dangerouslySetInnerHTML:{__html:Xf("settings",12)||""}}),r("已注入 {n} / {total} 个工具 · 系统提示词:{state}",{n:L,total:tl.length,state:e?.systemPromptEnabled?r("开"):r("关")})]}),s.jsx("span",{className:"spacer"}),j?s.jsxs("span",{className:"hint",children:[j,S&&s.jsx("button",{type:"button",className:"asst-tc-more",onClick:()=>{z()},children:r("重试")})]}):F?s.jsx("span",{className:"hint",children:r("回复由 {model} 生成 · 走本地网关",{model:F})}):null]})]})]})}const tM='.page[data-page="assistant"]';let fw=null;function nM(){if(fw)return;const e=document.querySelector(tM);e&&(e.replaceChildren(),fw=jn.createRoot(e),fw.render(s.jsx(z7,{})))}document.querySelector(tM)?nM():document.addEventListener("DOMContentLoaded",nM,{once:!0}),GO().catch(()=>{});function B7(e,t){return window.wbIcons?.icon?.(e,t)||""}function H7({id:e,label:t,checked:a,disabled:o,onCheckedChange:c}){return s.jsxs("label",{className:"switch",children:[s.jsx(ns,{id:e,checked:a,disabled:o,onCheckedChange:d=>c(d)}),s.jsx("span",{children:t})]})}function U7(){const[e,t]=x.useState(null),[a,o]=x.useState([]),[c,d]=x.useState(""),[f,p]=x.useState(!1),[h,b]=x.useState(!1),[y,v]=x.useState(!1),[j,w]=x.useState("");x.useEffect(()=>{let C=!0;return(async()=>{try{const E=await QO();if(!C)return;t(E)}catch(E){if(!C)return;p(!0),d(r("加载助手设置失败:{message}",{message:Dl(E)}))}try{const E=await KO();if(!C)return;o(E)}catch(E){if(!C)return;d(D=>D||r("读取模型清单失败:{message}",{message:Dl(E)}))}})(),()=>{C=!1}},[]);const S=x.useCallback(async(C,E)=>{t(C),b(!0);try{const D=await ZO(C);t(D),p(!1),d(E||r("已保存"))}catch(D){p(!0),d(r("保存失败:{message}",{message:Dl(D)}))}finally{b(!1)}},[]),N=e!==null,k=x.useMemo(()=>{const C={};for(const E of tl)C[E.id]=e?.tools?.[E.id]??!1;return C},[e]),A=Object.values(k).filter(Boolean).length,P=!!e?.systemPrompt?.trim(),R=(C,E)=>{if(!e)return;const D=JO.find(O=>O.id===C)?.label||C,I={...e,tools:{...e.tools,[C]:E}};S(I,E?r("已勾选「{label}」",{label:D}):r("已取消「{label}」",{label:D}))};return s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("对话模型")}),s.jsx("span",{className:"panel-sub",children:r("助手自己用哪个模型生成回复")})]}),s.jsx("div",{className:"panel-body",children:s.jsxs("div",{className:"asst-row",children:[s.jsx("label",{children:r("模型")}),s.jsxs(ut,{value:e?.model??"",disabled:!N||h,onValueChange:C=>{if(!e)return;const E=String(C);E&&S({...e,model:E},r("对话模型已切换"))},children:[s.jsx(ft,{className:"asst-model-pick","aria-label":r("对话模型"),children:s.jsx(dt,{children:e?.model??r("选择模型…")})}),s.jsxs(pt,{children:[a.length===0&&s.jsx($e,{value:"none",disabled:!0,children:r("没有可用的模型")}),a.map(C=>s.jsx($e,{value:C,children:C},C))]})]}),s.jsx("span",{className:"hint",children:r("列表 = 模型管理页里所有已启用的模型。助手走本地网关发起对话,消耗所选账号的额度。")})]})})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("系统提示词")}),s.jsx("span",{className:"panel-sub",children:r("作为 system 消息注入给助手")})]}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"asst-row",children:[s.jsx(H7,{id:"settings-assistant-prompt",label:r("注入系统提示词"),checked:e?.systemPromptEnabled!==!1,disabled:!N||h,onCheckedChange:C=>{e&&S({...e,systemPromptEnabled:C},r(C?"系统提示词已开启":"系统提示词已关闭"))}}),s.jsxs(ne,{variant:"outline",size:"sm",disabled:!N,onClick:()=>{w(e?.systemPrompt?.trim()?e.systemPrompt:lw),v(!0)},children:[s.jsx("span",{dangerouslySetInnerHTML:{__html:B7("settings",13)||""}}),r("编辑")]}),s.jsx("span",{className:"hint",children:r("点「编辑」弹窗改内容;开关关掉就整段不发。")})]}),s.jsx("div",{className:"settings-state",children:e?.systemPromptEnabled===!1?r("当前:已关闭(助手不收到任何系统提示词,工具定义照发)"):P?r("当前:已开启 · 自定义内容 {n} 字",{n:e?.systemPrompt.trim().length??0}):r("当前:已开启 · 使用内置默认提示词(还没自定义)")}),s.jsxs("div",{className:"asst-matrix",children:[s.jsx("div",{className:"mt-title",children:r("四种组合的实际效果")}),s.jsxs("table",{className:"asst-mini-table",children:[s.jsx("thead",{children:s.jsxs("tr",{children:[s.jsx("th",{children:r("提示词开关")}),s.jsx("th",{children:r("已勾选工具")}),s.jsx("th",{children:r("实际注入")})]})}),s.jsxs("tbody",{children:[s.jsxs("tr",{children:[s.jsx("td",{children:r("开")}),s.jsx("td",{children:r("有")}),s.jsx("td",{children:r("系统提示词 + 工具定义")})]}),s.jsxs("tr",{children:[s.jsx("td",{children:r("开")}),s.jsx("td",{children:s.jsx("b",{children:r("无")})}),s.jsx("td",{children:s.jsx("b",{children:r("都不注入(助手退化为纯聊天)")})})]}),s.jsxs("tr",{children:[s.jsx("td",{children:r("关")}),s.jsx("td",{children:r("有")}),s.jsx("td",{children:r("只注入工具定义")})]}),s.jsxs("tr",{children:[s.jsx("td",{children:r("关")}),s.jsx("td",{children:r("无")}),s.jsx("td",{children:r("都不注入")})]})]})]}),s.jsx("p",{className:"hint",children:r("「工具全不勾选 → 不注入系统提示词」:想当纯聊天用时,不必再去关上面的开关。")})]})]})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("工具")}),s.jsx("span",{className:"panel-sub",children:r("勾选即注入该工具;不勾选 = 助手看不到它")})]}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"asst-tools-bar",children:[s.jsx("span",{className:"badge brand",children:r("已勾选 {n} / {total}",{n:A,total:tl.length})}),s.jsx("span",{className:"spacer"}),s.jsx(ne,{variant:"ghost",size:"sm",disabled:!N||h,onClick:()=>{if(!e)return;const C={};for(const E of tl)C[E.id]=!1;S({...e,tools:C},r("已全部取消勾选"))},children:r("全不选")}),s.jsx(ne,{variant:"ghost",size:"sm",disabled:!N||h,onClick:()=>{if(!e)return;const C={};for(const E of tl)C[E.id]=E.kind==="read";for(const E of["set_model_enabled","set_model_mapping","set_model_capability","test_model"])C[E]=!0;S({...e,tools:C},r("已恢复推荐勾选"))},children:r("恢复推荐")})]}),["read","write"].map(C=>s.jsxs(x.Fragment,{children:[s.jsx("div",{className:"asst-tool-group",children:C==="read"?s.jsxs(s.Fragment,{children:[r("只读工具")," ",s.jsx("span",{className:"muted",children:r("· 不会改动任何配置")})]}):s.jsxs(s.Fragment,{children:[r("改动类工具")," ",s.jsx("span",{className:"muted",children:r("· 每次执行前列改动清单,等你确认")})]})}),JO.filter(E=>{const I=tl.find(O=>O.id===E.id)?.kind==="read";return C==="read"?I:!I}).map(E=>{const D=tl.find(O=>O.id===E.id),I=k[E.id]===!0;return s.jsxs("label",{className:"asst-tool",children:[s.jsx("input",{type:"checkbox",checked:I,disabled:!N||h,onChange:O=>R(E.id,O.currentTarget.checked)}),s.jsxs("span",{className:"tt",children:[s.jsxs("span",{className:"tt-name",children:[E.label,D?.kind==="read"&&s.jsx("span",{className:"badge tag plain",children:r("只读")}),E.tag==="cost"&&s.jsx("span",{className:"badge tag",children:r("消耗额度")}),E.tag==="danger"&&s.jsx("span",{className:"badge tag bad",children:r("高危")}),D?.kind==="write"&&!E.tag&&s.jsx("span",{className:"badge tag warn",children:r("影响转发")})]}),s.jsx("span",{className:"tt-desc",children:E.desc})]})]},E.id)})]},C)),s.jsx("div",{className:Ue("settings-state",f&&"text-destructive"),children:c})]})]}),s.jsx(Yt,{open:y,onOpenChange:C=>{C||v(!1)},children:s.jsxs(Xt,{className:"w-[min(680px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("编辑系统提示词")})}),s.jsxs(Lt,{children:[s.jsx(dn,{children:s.jsx("p",{children:r("这段文本作为 system 消息排在对话最前面。工具的定义与当前配置概览由网关自动附加,不用写在这里。")})}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"assistant-system-prompt",children:r("提示词正文")}),s.jsx(Mi,{id:"assistant-system-prompt",rows:14,className:"asst-prompt-ta min-h-[300px]",spellCheck:!1,value:j,onChange:C=>w(C.currentTarget.value)}),s.jsx("div",{className:"hint",children:r("清空 = 回到内置默认(与「恢复默认」等价)。")})]})]}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"outline",className:"mr-auto",onClick:()=>w(lw),children:r("恢复默认")}),s.jsx(ne,{variant:"outline",onClick:()=>v(!1),children:r("取消")}),s.jsx(ne,{variant:"default",onClick:()=>{if(!e)return;const C=j.trim()===lw.trim()?"":j;S({...e,systemPrompt:C},r("系统提示词已保存")),v(!1)},children:r("保存")})]})]})})]})}function Lr(){return Pe().workbuddyDesktop??{}}function sM(){return window}const rM="workbuddy-desktop-checkin-tab",V7=["center","schedule","records"];function F7(){try{const e=localStorage.getItem(rM);return V7.includes(e)?e:"center"}catch{return"center"}}function aM(e){if(e!==bs.tab){try{localStorage.setItem(rM,e)}catch{}sr({tab:e})}}async function Ig(e){aM(e),bs.loaded||await Il()}let bs={version:0,tab:F7(),snapshot:null,loaded:!1,loadError:"",expanded:new Set,runningAll:!1,autoSaving:!1,autoTimeDraft:null,keepaliveSaving:!1,keepaliveDraft:null,signing:new Set,onboarding:new Map,growth:new Map};const pw=new Set;function $7(){for(const e of pw)e()}function sr(e){bs={...bs,...e,version:bs.version+1},$7()}function fr(){return bs}function lM(e){return pw.add(e),()=>pw.delete(e)}function q7(e){const t=typeof e?.key=="string"?e.key:"";return t?{key:t,title:typeof e.title=="string"&&e.title?e.title:t,group:typeof e.group=="string"&&e.group?e.group:"",points:Number(e.points)||0,done:e.done===!0,blocked:e.blocked===!0}:null}function mw(e){return(Array.isArray(e)?e:[]).map(q7).filter(a=>a!==null)}function ps(e){return Number(e)||0}async function Il(){try{const e=await Lr().getCheckinCenter?.();if(!e)throw new Error(r("后端未返回签到中心数据"));sr({snapshot:e,loaded:!0,loadError:"",autoTimeDraft:null,keepaliveDraft:null}),G7(e.extras?.onboarding??[])}catch(e){sr({loaded:!0,loadError:mt(e)})}}function G7(e){for(const t of e){const a=t.settled;if(!a||typeof a!="object")continue;const o=bs.onboarding.get(t.id);if(o?.status==="loaded"&&o.unclaimed>0)continue;const c=mw(a.tasks),d=ps(a.at);Do(t.id,{status:"loaded",error:void 0,checkedAt:void 0,settled:!0,settledAt:d>0?d:void 0,tasks:c,unclaimed:0,earned:ps(a.earned),total:ps(a.total)})}}function oM(e){const t=new Set(bs.expanded);t.has(e)?t.delete(e):t.add(e),sr({expanded:t})}function hw(e){const t=`onb:${e}`,a=new Set(bs.expanded);a.has(t)?a.delete(t):a.add(t),sr({expanded:a})}function iM(e){return bs.expanded.has(`onb:${e}`)}async function K7(){if(!bs.runningAll){sr({runningAll:!0});try{const e=await Lr().runAutoCheckinNow?.();if(e){const t=ps(e.succeeded),a=ps(e.active),o=ps(e.total),c=ps(e.failedCount),d=a>0?r(",日活保活 {n} 个",{n:a}):"";c>0?Le(r("签到完成:{succeeded}/{total} 成功{active},{failed} 个失败",{succeeded:t,total:o,active:d,failed:c}),"err"):Le(r("✅ 签到完成:{succeeded}/{total} 个账号成功领取{active}",{succeeded:t,total:o,active:d})),sM().wbAccountsView?.refreshUsageAfterCheckin?.(null)}}catch(e){Le(r("签到失败:{error}",{error:mt(e)}),"err")}finally{sr({runningAll:!1}),await Il(),cM(fr().snapshot?.extras.onboarding??[]),Pe().wbApp?.refresh?.()}}}async function Pg(e,t="checkin"){const a=`${e}:${t}`;if(bs.signing.has(a))return;const o=new Set(bs.signing);o.add(a),sr({signing:o});try{let c;t==="checkin"?c=(await Lr().checkinAllAccounts?.(e))?.results?.[0]:c=await Lr().runCheckinActivity?.(e,t)??void 0;const d=c?.claim,f=c?.activity,p=f?.report?.succeeded===!0,h=f?.webConversation?.succeeded===!0;c?.error?Le(r("签到失败:{error}",{error:String(c.error)}),"err"):t==="keepalive"?f?.pokeSucceeded===!0||h||p?Le(r("✅ {message}",{message:d?.msg||r("活跃保活完成")})):Le(d?.msg||r("活跃保活失败"),"err"):d?.success===!0?Le(r("✅ {message}",{message:d.msg||r("签到成功")})):d?.alreadyCompleted===!0?Le(d?.msg||r("今日已领取"),"err"):p?Le(r("✅ {message}",{message:d?.msg||r("活跃上报完成,但活动未开启")})):f?.pokeSucceeded===!0||h?Le(r("✅ {message}",{message:d?.msg||r("活跃保活完成,但活动未开启")})):Le(d?.msg||r("未领取"),"err"),sM().wbAccountsView?.refreshUsageAfterCheckin?.(e)}catch(c){Le(r("签到失败:{error}",{error:mt(c)}),"err")}finally{const c=new Set(fr().signing);c.delete(a),sr({signing:c}),await Il();const d=(fr().snapshot?.extras.onboarding??[]).filter(f=>f.id===e);cM(d),Pe().wbApp?.refresh?.()}}function _g(e){sr({keepaliveDraft:e.map(t=>t.trim()).filter(Boolean)})}async function Y7(e){const t=e.map(a=>a.trim()).filter(Boolean);if(!fr().keepaliveSaving){sr({keepaliveSaving:!0});try{const a=await Lr().saveCheckinKeepalive?.(t);if(a){const o=fr().snapshot;sr({snapshot:o&&{...o,keepalive:a},keepaliveDraft:null}),Le(r("已保存:保活模型链"))}}catch(a){Le(r("保存失败:{error}",{error:mt(a)}),"err")}finally{sr({keepaliveSaving:!1})}}}async function cM(e){for(const t of e)X7(t.id)||await gw(t.id);for(const t of e){const a=fr().onboarding.get(t.id);a?.status!=="loaded"||a.claiming||a.settled||(t.provider==="officeace"||a.unclaimed>0)&&await uM(t.id)}}function X7(e){return bs.onboarding.get(e)?.settled===!0}function Iu(e){return bs.onboarding.get(e)??{status:"idle",tasks:[],unclaimed:0,earned:0,total:0,claiming:!1}}function Do(e,t){const a=new Map(bs.onboarding);a.set(e,{...Iu(e),...t}),sr({onboarding:a})}async function gw(e,t){const a=Iu(e);if(!(a.status==="loading"||a.claiming)){Do(e,{status:"loading",error:void 0,settled:!1});try{const o=await Lr().getOnboardingTasks?.(e,t?.refresh===!0),c=mw(o?.tasks),d=o?.settled===!0;Do(e,{status:"loaded",checkedAt:Date.now(),tasks:c,unclaimed:c.filter(f=>!f.done&&!f.blocked).length,earned:ps(o?.earned),total:ps(o?.total),settled:d,settledAt:d&&ps(o?.settledAt)||void 0}),t?.expand===!0&&c.length>0&&!iM(e)&&hw(e)}catch(o){Do(e,{status:"error",error:mt(o),settled:!1})}}}async function uM(e){const t=Iu(e);if(!(t.claiming||t.status!=="loaded")){Do(e,{claiming:!0});try{Do(e,{tasks:Iu(e).tasks.map(v=>v.done?v:{...v,claiming:!0,error:void 0})});const a=await Lr().claimOnboardingTasks?.(e),o=new Map,c=Array.isArray(a?.results)?a.results:[];for(const v of c){const j=typeof v?.key=="string"?v.key:"";j&&v?.ok!==!0&&o.set(j,String(v?.error||r("领取失败")))}const d=mw(a?.tasks),f=(d.length?d:Iu(e).tasks).map(v=>({...v,claiming:!1,error:o.get(v.key)})),p=f.filter(v=>v.done&&!o.has(v.key)).length,h=a?.settled===!0;Do(e,{claiming:!1,checkedAt:Date.now(),tasks:f,unclaimed:f.filter(v=>!v.done&&!v.blocked).length,earned:ps(a?.earned),total:ps(a?.total),settled:h,settledAt:h&&ps(a?.settledAt)||void 0});const b=f.filter(v=>v.error&&!v.done).length,y=typeof a?.message=="string"?a.message.trim():"";b?Le(r("新手福利有 {n} 项领取失败,可重试",{n:b}),"err"):y?Le(y,"ok"):p&&Le(r("✅ 新手福利领取完成,累计 {n} 积分",{n:ps(a?.earned)}))}catch(a){const o=mt(a);Do(e,{claiming:!1,tasks:Iu(e).tasks.map(c=>({...c,claiming:!1,error:c.done?void 0:o}))}),Le(r("领取失败:{error}",{error:o}),"err")}finally{await Il()}}}function dM(e){return(Array.isArray(e)?e:[]).map(a=>{const o=a??{},c=typeof o.code=="string"?o.code:"";return c?{code:c,title:typeof o.title=="string"&&o.title?o.title:c,description:typeof o.description=="string"?o.description:"",status:typeof o.status=="string"?o.status:"",current:ps(o.current),target:ps(o.target),locked:o.locked===!0,unlockAt:o.unlockAt,supported:o.supported===!0,unforgeable:o.unforgeable===!0,unforgeableReason:typeof o.unforgeableReason=="string"?o.unforgeableReason:"",kind:typeof o.kind=="string"?o.kind:"",source:typeof o.source=="string"?o.source:""}:null}).filter(a=>a!==null)}function fM(e){return(Array.isArray(e)?e:[]).map(a=>{const o=a??{},c=typeof o.code=="string"?o.code:"";return c?{code:c,outcome:typeof o.outcome=="string"?o.outcome:"failed",message:typeof o.message=="string"?o.message:"",credit:ps(o.credit),energy:ps(o.energy)}:null}).filter(a=>a!==null)}function Zi(e){return bs.growth.get(e)??{status:"idle",energy:null,streak:null,tasks:[],travel:null,school:null,running:!1,runningSchool:!1,runningTravel:!1}}function zr(e,t){const a=new Map(bs.growth);a.set(e,{...Zi(e),...t}),sr({growth:a})}function Q7(e){return bs.expanded.has(`growth:${e}`)}function pM(e){const t=`growth:${e}`,a=new Set(bs.expanded);a.has(t)?a.delete(t):a.add(t),sr({expanded:a})}async function Lg(e){const t=Zi(e);if(!(t.status==="loading"||t.running)){zr(e,{status:"loading",error:void 0});try{const a=await Lr().getGrowthStatus?.(e),o=a?.tasks??{},c=a?.travel??null,d=a?.school??null;zr(e,{status:"loaded",checkedAt:Date.now(),energy:a?.energy===null||a?.energy===void 0?null:ps(a.energy),streak:a?.streak===null||a?.streak===void 0?null:ps(a.streak),tasks:dM(o.tasks),travel:c,school:d?{...d,tasks:dM(d.tasks)}:null,error:typeof o.error=="string"?o.error:void 0})}catch(a){zr(e,{status:"error",error:mt(a)})}}}async function mM(e,t){const a=Zi(e);if(a.running)return;const o=(a.tasks??[]).filter(h=>!h.supported||h.unforgeable===!0||h.locked===!0?!1:t==="claim"?h.status==="completed"||h.current>=h.target&&h.status!=="claimed":h.status!=="claimed");if(o.length===0){Le(r("没有需要执行的任务"));return}zr(e,{running:!0,results:[],error:void 0});let c=0,d=0,f=0,p=0;for(const h of o)try{const b=await Lr().runGrowthTask?.(e,h.code,t),y=fM([b])[0];y&&(zr(e,{results:[...Zi(e).results??[],y]}),c+=y.credit??0,d+=y.energy??0,(y.outcome==="done"||y.outcome==="already")&&(f+=1),y.outcome==="failed"&&(p+=1))}catch(b){p+=1,zr(e,{results:[...Zi(e).results??[],{code:h.code,outcome:"failed",message:mt(b),credit:0,energy:0}]})}zr(e,{running:!1}),p>0?Le(r("成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}",{done:f,failed:p,credit:c,energy:d}),"err"):Le(r("✅ 成长任务执行完成:积分 +{credit} 能量 +{energy}",{credit:c,energy:d})),await Lg(e)}async function Z7(e,t){if(!Zi(e).runningSchool){zr(e,{runningSchool:!0,error:void 0});try{const o=await Lr().runGrowthSchool?.(e,t),c=fM(o?.results),d=o?.lottery??null;zr(e,{runningSchool:!1,results:c,schoolText:c.length?c.map(f=>`${f.code}:${f.message}`).join(";"):void 0,lotteryText:d?String(d.message??r("已抽 {n} 次",{n:ps(d.drawn)})):void 0,checkedAt:Date.now()}),Le(d?r("开学季执行完成:{message}",{message:String(d.message??"")}):r("✅ 开学季任务执行完成")),await Lg(e)}catch(o){zr(e,{runningSchool:!1,error:mt(o)}),Le(r("开学季执行失败:{error}",{error:mt(o)}),"err")}}}async function W7(e){if(!Zi(e).runningTravel){zr(e,{runningTravel:!0,error:void 0});try{const a=await Lr().runGrowthTravel?.(e),o=String(a?.message??""),c=String(a?.outcome??"");zr(e,{runningTravel:!1,checkedAt:Date.now()}),c==="failed"?Le(r("猫猫旅行失败:{message}",{message:o}),"err"):Le(r("猫猫旅行:{message}",{message:o})),await Lg(e)}catch(a){zr(e,{runningTravel:!1,error:mt(a)}),Le(r("猫猫旅行失败:{error}",{error:mt(a)}),"err")}}}function J7(e){return e.totalCount?e.doneCount>=e.totalCount?"done":e.doneCount>0?"part":"todo":"none"}function e9(e){const a=(Array.isArray(e?.schedules)?e.schedules:[]).filter(o=>o.enabled===!0).map(o=>Number(o.nextRunAt)).filter(o=>Number.isFinite(o)&&o>0);return a.length?Math.min(...a):null}let Io={version:0,data:null,loaded:!1,loadError:"",saving:!1,runningId:null,editingId:null};const xw=new Set;function t9(){for(const e of xw)e()}function Br(e){Io={...Io,...e,version:Io.version+1},t9()}function hM(){return Io}function n9(e){return xw.add(e),()=>xw.delete(e)}function Pu(){return Pe().workbuddyDesktop??{}}async function gM(){try{const e=await Pu().getCheckinSchedules?.();if(!e)throw new Error(r("后端未返回定时签到数据"));Br({data:e,loaded:!0,loadError:""})}catch(e){Br({loaded:!0,loadError:mt(e)})}}function zg(e){e&&Br({data:e,loadError:""}),Il()}async function s9(e){if(!Io.saving){Br({saving:!0});try{const t=await Pu().saveAutoCheckin?.({enabled:e});zg(t),Le(r(e?"✅ 定时签到已开启":"定时签到已关闭"))}catch(t){Le(r("保存失败:{error}",{error:mt(t)}),"err")}finally{Br({saving:!1})}}}async function xM(e){if(!Io.saving){Br({saving:!0});try{const t=await Pu().saveCheckinSchedule?.(e);zg(t),Br({editingId:null}),Le(r("已保存:定时条目"))}catch(t){Le(r("保存失败:{error}",{error:mt(t)}),"err")}finally{Br({saving:!1})}}}async function r9(e,t){await xM({id:e.id,label:e.label??"",enabled:t,time:e.time??"",providers:Array.isArray(e.providers)?e.providers:[]})}async function a9(e){if(!Io.saving){Br({saving:!0});try{await Pu().deleteCheckinSchedule?.(e),zg(await Pu().getCheckinSchedules?.()),Le(r("已删除定时条目"))}catch(t){Le(r("删除失败:{error}",{error:mt(t)}),"err")}finally{Br({saving:!1})}}}async function l9(e){if(!Io.runningId){Br({runningId:e});try{const t=await Pu().runCheckinSchedule?.(e)??void 0,a=o=>Number(o)||0;if(t){const o=a(t.succeeded),c=a(t.active),d=a(t.total),f=a(t.failedCount),p=c>0?r(",日活保活 {n} 个",{n:c}):"";f>0?Le(r("签到完成:{succeeded}/{total} 成功{active},{failed} 个失败",{succeeded:o,total:d,active:p,failed:f}),"err"):Le(r("✅ 签到完成:{succeeded}/{total} 个账号成功领取{active}",{succeeded:o,total:d,active:p})),zg(t)}}catch(t){Le(r("执行失败:{error}",{error:mt(t)}),"err")}finally{Br({runningId:null}),await Il()}}}function bw(e){Br({editingId:e})}const o9=r("到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活并在网页通道建会话点亮日活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。");function i9(){const[e,t]=x.useState(hM());return x.useEffect(()=>n9(()=>t(hM())),[]),e}function c9(){const[e,t]=x.useState(fr());return x.useEffect(()=>lM(()=>t(fr())),[]),e}function bM(e,t){const a=Array.isArray(e.providers)?e.providers:[];return t.filter(o=>a.includes(o.id)).map(o=>o.label)}function u9(e,t){const a=bM(e,t);return a.length?t.length>0&&a.length>=t.length?r("全部 {n} 家",{n:t.length}):a.join(r("、")):r("未勾选提供商")}function d9(e,t){const a=bM(e,t);return a.length?a.join(r("、")):r("未勾选提供商")}function f9(e){const t=e.lastResult;if(!t)return r("还没执行过");const a=f=>Number(f)||0,o=[r("成功 {n}",{n:a(t.succeeded)})],c=a(t.active);c>0&&o.push(r("保活 {n}",{n:c}));const d=a(t.failedCount);return d>0&&o.push(r("失败 {n}",{n:d})),`${t.at?Ls(t.at):"—"}${t.reason?` · ${t.reason}`:""} —— ${o.join(r(" · "))}`}function vM({entry:e,store:t}){const a=Array.isArray(t.data?.providerOptions)?t.data.providerOptions:[],o=e===null,[c,d]=x.useState(e?.label??""),[f,p]=x.useState(e?.time??"00:01"),[h,b]=x.useState(new Set(o?a.map(S=>S.id):e?.providers??[]));x.useEffect(()=>{o&&a.length>0&&b(new Set(a.map(S=>S.id)))},[]);const y=f.trim()!==""&&h.size>0,v=()=>b(new Set(a.map(S=>S.id))),j=()=>b(new Set),w=async()=>{await xM({id:e?.id,label:c.trim(),enabled:e?.enabled??!0,time:f.trim(),providers:a.map(S=>S.id).filter(S=>h.has(S))})};return s.jsxs("div",{className:"ck-sched-editor",children:[s.jsxs("div",{className:"ck-set-line",children:[s.jsx("span",{className:"ck-set-k",children:r("名称")}),s.jsx("span",{className:"ck-set-v",children:s.jsx(Fe,{type:"text",className:"max-w-[220px]",placeholder:r("可选 · 缺省显示时刻"),value:c,disabled:t.saving,onChange:S=>d(S.currentTarget.value)})})]}),s.jsxs("div",{className:"ck-set-line",children:[s.jsx("span",{className:"ck-set-k",children:r("每天时刻")}),s.jsxs("span",{className:"ck-set-v flex items-center gap-2",children:[s.jsx(Fe,{type:"time",className:"w-[96px] max-w-[96px] font-mono tabular-nums",value:f,disabled:t.saving,onChange:S=>p(S.currentTarget.value)}),s.jsx("span",{className:"text-subtle text-xs",children:r("北京时间 · 错过时点开机后会自动补签")})]})]}),s.jsxs("div",{className:"ck-set-line ck-set-line-top",children:[s.jsx("span",{className:"ck-set-k",children:r("签到范围")}),s.jsxs("span",{className:"ck-set-v",children:[s.jsxs("div",{className:"ck-mini-row",children:[s.jsx("span",{className:"ck-mini-note",children:r("到点时逐个签到,已勾 {n} / {total} 家",{n:h.size,total:a.length})}),s.jsx("button",{type:"button",className:"ck-mini-btn",disabled:t.saving,onClick:v,children:r("全选")}),s.jsx("button",{type:"button",className:"ck-mini-btn",disabled:t.saving||h.size===0,onClick:j,children:r("清空")})]}),s.jsx("div",{className:"ck-range",children:a.map(S=>s.jsxs("label",{className:"ck-range-item",children:[s.jsx(Pr,{checked:h.has(S.id),disabled:t.saving,onCheckedChange:N=>{const k=new Set(h);N===!0?k.add(S.id):k.delete(S.id),b(k)}}),s.jsx("span",{className:"ck-range-name",title:S.label,children:S.label})]},S.id))}),h.size===0?s.jsx("div",{className:"ck-mini-warn",children:r("至少勾一家才能保存(不勾等于这条条目不签到)")}):null]})]}),s.jsxs("div",{className:"ck-sched-actions",children:[s.jsx(ne,{size:"sm",variant:"outline",disabled:t.saving,onClick:()=>bw(null),children:r("取消")}),s.jsx(ne,{size:"sm",variant:"default",disabled:!y||t.saving,onClick:()=>{w()},children:t.saving?r("保存中…"):r("保存")})]})]})}function p9({store:e}){return s.jsxs("div",{className:"ck-sched-row open ck-sched-row-new",children:[s.jsxs("div",{className:"ck-sched-main",children:[s.jsx("span",{className:"ck-new-dot","aria-hidden":!0,children:"+"}),s.jsxs("div",{className:"ck-prov-info",children:[s.jsx("div",{className:"ck-prov-name",children:r("新建定时条目")}),s.jsx("div",{className:"ck-prov-desc",children:r("填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)")})]})]}),s.jsx(vM,{entry:null,store:e})]})}function m9({entry:e,store:t}){const a=Array.isArray(t.data?.providerOptions)?t.data.providerOptions:[],o=t.editingId===e.id,c=t.runningId===e.id,d=t.data?.enabled===!0,f=Number(e.nextRunAt),p=async()=>{const h=Pe().wbConfirm?.ask;if(!h)return;const y=(e.displayLabel||e.time||e.id).replace(/&/g,"&").replace(//g,">");await h({title:r("删除定时条目"),html:r("确定删除「{name}」?删除后到点不再执行。",{name:y}),okText:r("删除"),okClass:"danger"})&&await a9(e.id)};return s.jsxs("div",{className:o?"ck-sched-row open":"ck-sched-row",children:[s.jsxs("div",{className:"ck-sched-main",children:[s.jsx(ns,{checked:e.enabled===!0,disabled:t.saving,onCheckedChange:h=>{r9(e,h===!0)}}),s.jsxs("div",{className:"ck-prov-info",children:[s.jsxs("div",{className:"ck-prov-name",children:[e.displayLabel||e.time||e.id,e.enabled===!0?null:s.jsx(ze,{variant:"outline",shape:"tag",children:r("已停用")})]}),s.jsx("div",{className:"ck-prov-desc ck-sched-scope",title:d9(e,a),children:r("每天 {time} · {scope}",{time:e.time??"—",scope:u9(e,a)})}),s.jsx("div",{className:"ck-prov-desc",children:r("上次执行:{line}",{line:f9(e)})})]}),s.jsxs("div",{className:"ck-prov-right",children:[s.jsx("span",{className:"ck-sched-next",children:d&&e.enabled===!0&&Number.isFinite(f)&&f>0?r("下次 {date}",{date:Ls(f)}):"—"}),s.jsx(ne,{size:"sm",variant:"ghost",disabled:c||t.runningId!==null,onClick:()=>{l9(e.id)},children:r(c?"执行中…":"立即执行")}),s.jsx(ne,{size:"sm",variant:"ghost",onClick:()=>bw(o?null:e.id),children:r(o?"收起":"编辑")}),s.jsx(ne,{size:"sm",variant:"ghost",className:"text-destructive",disabled:t.saving,onClick:()=>{p()},children:r("删除")})]})]}),o?s.jsx(vM,{entry:e,store:t}):null]})}function h9(e,t){return e.length===t.length&&e.every((a,o)=>a===t[o])}let Qf=null;function g9(e,t){for(let a=0;ah!==t.row),d=g9(c,e.clientY);if(d===t.from)return;const f=[...t.list],[p]=f.splice(t.from,1);f.splice(d,0,p),t.list=f,t.from=d,t.onChange(f)}function Zf(){const e=Qf;Qf=null,window.removeEventListener("pointermove",yM),window.removeEventListener("pointerup",Zf),window.removeEventListener("pointercancel",Zf),document.body.classList.remove("ck-chain-dragging"),e?.row.classList.remove("dragging")}function x9(e,t,a,o){if(e.button!==0||Qf||a.length<2)return;const c=e.currentTarget.closest(".ck-chain-row");c&&(e.preventDefault(),Qf={list:[...a],from:t,row:c,onChange:o},c.classList.add("dragging"),document.body.classList.add("ck-chain-dragging"),window.addEventListener("pointermove",yM),window.addEventListener("pointerup",Zf),window.addEventListener("pointercancel",Zf))}function b9({models:e,defaultModels:t,dirty:a,saving:o}){const[c,d]=x.useState(""),f=()=>{const p=c.trim();if(p){if(e.includes(p)){Le(r("清单里已经有「{name}」",{name:p}),"err");return}_g([...e,p]),d("")}};return s.jsxs("section",{className:"panel ck-chain-panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("保活模型链")}),s.jsx("span",{className:"panel-sub",children:r("WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关")}),s.jsx(ne,{size:"sm",variant:"default",className:"ml-auto",disabled:!a||o,onClick:()=>{Y7(e)},children:r(o?"保存中…":"保存")})]}),s.jsxs("div",{className:"p-4",children:[s.jsxs("div",{className:"ck-set-line ck-set-line-top",children:[s.jsx("span",{className:"ck-set-k",children:r("模型清单")}),s.jsxs("span",{className:"ck-set-v",children:[s.jsxs("div",{className:"ck-mini-row",children:[s.jsx("span",{className:"ck-mini-note",children:r("按序逐个尝试,共 {n} 个",{n:e.length})}),s.jsx("button",{type:"button",className:"ck-mini-btn",disabled:o||t.length===0,title:r("把缺省链填进清单,再点「保存」生效"),onClick:()=>_g([...t]),children:r("填入缺省链")})]}),s.jsxs("div",{className:"ck-chain",children:[e.map((p,h)=>s.jsxs("div",{className:"ck-chain-row",children:[s.jsx("span",{className:"ck-chain-grip",role:"button",tabIndex:-1,"aria-label":r("按住拖动调整顺序"),title:r("按住拖动,调整这个模型在链里的顺序"),onPointerDown:b=>x9(b,h,e,_g),dangerouslySetInnerHTML:{__html:Pe().wbIcons?.icon?.("gripVertical",14)||""}}),s.jsx("span",{className:"ck-chain-name",title:p,children:p}),s.jsxs("span",{className:"ck-chain-index",children:["#",h+1]}),s.jsx("button",{type:"button",className:"ck-chain-del",disabled:o,"aria-label":r("移除 {name}",{name:p}),title:r("从清单里移除"),onClick:()=>_g(e.filter((b,y)=>y!==h)),children:"×"})]},p)),e.length===0?s.jsx("div",{className:"ck-chain-empty",children:r("清单为空 —— 保存后恢复缺省链")}):null]}),s.jsxs("div",{className:"ck-chain-add",children:[s.jsx(Fe,{type:"text",className:"max-w-[260px] font-mono text-xs",placeholder:r("WorkBuddy 国际版保活模型,按顺序回退"),value:c,disabled:o,onChange:p=>d(p.currentTarget.value),onKeyDown:p=>{p.key==="Enter"&&(p.preventDefault(),f())}}),s.jsx(ne,{size:"sm",variant:"outline",disabled:o||c.trim()==="",onClick:f,children:r("添加模型")}),a?s.jsx("span",{className:"ck-chain-dirty",children:r("有未保存的改动")}):null]})]})]}),s.jsx("div",{className:"ck-note",children:r("「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。",{defaults:t.join(r("、"))})})]})]})}function v9(){const e=i9(),t=c9();if(x.useEffect(()=>{gM()},[]),!e.loaded)return s.jsxs("div",{className:"ck-loading",children:[s.jsx(of,{className:"size-5"}),s.jsx("span",{children:r("正在读取定时签到数据…")})]});const a=e.data,o=a?.enabled===!0,c=Array.isArray(a?.schedules)?a.schedules:[],d=t.snapshot?.keepalive,f=Array.isArray(d?.models)?d.models:[],p=Array.isArray(d?.defaultModels)?d.defaultModels:[],h=t.keepaliveDraft,b=h??f,y=h!==null&&!h9(h,f);return s.jsxs("div",{children:[e.loadError?s.jsxs("div",{className:"ck-page-error",children:[s.jsx("span",{children:r("读取定时签到数据失败:{error}",{error:e.loadError})}),s.jsx(ne,{size:"sm",variant:"outline",onClick:()=>{gM()},children:r("重试")})]}):null,s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("定时签到")}),s.jsx("span",{className:"tip-q","data-tip":o9}),s.jsx("span",{className:"panel-sub",children:r("每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签")}),s.jsx(ne,{size:"sm",variant:"outline",className:"ml-auto flex-none",disabled:e.saving,onClick:()=>bw(e.editingId==="new"?null:"new"),children:e.editingId==="new"?r("收起新建"):r("添加条目")})]}),s.jsx("div",{className:"p-4",children:s.jsxs("div",{className:"ck-set-line",children:[s.jsx("span",{className:"ck-set-k",children:r("总开关")}),s.jsxs("span",{className:"ck-set-v flex items-center gap-2",children:[s.jsx(ns,{checked:o,disabled:e.saving,onCheckedChange:v=>{s9(v===!0)}}),s.jsx("span",{className:"text-subtle text-xs",children:r(o?"开着:启用的条目按各自时刻执行":"关着:所有条目都不执行")})]})]})}),s.jsxs("div",{children:[e.editingId==="new"?s.jsx(p9,{store:e}):null,c.map(v=>s.jsx(m9,{entry:v,store:e},v.id)),c.length===0?s.jsx("div",{className:"ck-empty",children:r("还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。")}):null]})]}),s.jsx(b9,{models:b,defaultModels:p,dirty:y,saving:t.keepaliveSaving})]})}const Bg="checkin-records",jM=["today","7","30","month","all"],_u="all",y9=[{value:"today",label:r("今天")},{value:"7",label:r("7 天")},{value:"30",label:r("30 天")},{value:"month",label:r("本月")},{value:"all",label:r("全部")}],wM="workbuddy-desktop-checkin-records-range";function Hg(e){return new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime()}function SM(e){const t=new Date;switch(e){case"today":return Hg(t);case"7":return Hg(new Date(t.getFullYear(),t.getMonth(),t.getDate()-6));case"30":return Hg(new Date(t.getFullYear(),t.getMonth(),t.getDate()-29));case"month":return Hg(new Date(t.getFullYear(),t.getMonth(),1));default:return null}}function j9(){try{const e=localStorage.getItem(wM);return e!==null&&jM.includes(e)?e:_u}catch{return _u}}function NM(e){try{localStorage.setItem(wM,e)}catch{}}function kM(e){return typeof e=="number"&&e>0?e:xu}const vw={success:{label:"成功领取",variant:"success"},already:{label:"今日已领",variant:"brand"},pending:{label:"等待窗口",variant:"warning"},failed:{label:"失败",variant:"destructive"}},w9={schedule:"定时",startup:"补签",manual:"手动",single:"单签",activity:"活动"},yw=[{key:"time",label:r("时间")},{key:"account",label:r("账号")},{key:"provider",label:r("提供商")},{key:"source",label:r("来源")},{key:"outcome",label:r("结果")},{key:"detail",label:r("说明")}];let jw=null;function CM(){return jw?jw.apply([...yw]):yw.map(e=>({...e,align:e.align??"left"}))}function TM(){window.wbTableColumns?.repaint?.(Bg)}function EM(e){return(fr().snapshot?.auto?.providerOptions??[]).find(a=>a.id===e)?.label??e}function S9(e,t){const a=`ck-col-${t.key} ta-${t.align}`;switch(t.key){case"time":return s.jsxs("td",{className:a,children:[e.ts?Ls(e.ts):"—",s.jsx("span",{className:"ck-record-date",children:e.date||""})]},t.key);case"account":return s.jsx("td",{className:a,children:e.accountName||e.accountId||"—"},t.key);case"provider":return s.jsx("td",{className:a,children:e.provider?EM(e.provider):"—"},t.key);case"source":return s.jsxs("td",{className:a,children:[s.jsx("span",{className:"ck-record-source",children:w9[e.source??""]??e.source??"—"}),e.reason?s.jsx("span",{className:"ck-record-reason",children:e.reason}):null]},t.key);case"outcome":{const o=vw[e.outcome??""]??{label:e.outcome||"—",variant:"outline"};return s.jsxs("td",{className:a,children:[s.jsx(ze,{variant:o.variant,shape:"tag",children:o.label}),e.keptActive?s.jsx(ze,{variant:"outline",shape:"tag",className:"ml-1",children:r("保活")}):null]},t.key)}case"detail":return s.jsx("td",{className:e.error?`${a} ck-record-detail-fail`:a,children:e.error||e.message||"—"},t.key);default:return null}}function N9(){const[e,t]=x.useState([]),[a,o]=x.useState(0),[c,d]=x.useState(1),[f,p]=x.useState(()=>j9()),[h,b]=x.useState(()=>kM(pg(Bg,dg))),[y,v]=x.useState({outcome:"",provider:"",keyword:""}),[j,w]=x.useState(""),[S,N]=x.useState(!1),[k,A]=x.useState(""),[P,R]=x.useState(0),C=x.useRef(null),E=x.useRef(0),[,D]=x.useReducer(Q=>Q+1,0),I=fr().snapshot?.auto?.providerOptions??[],O=CM(),_=Math.max(1,Math.ceil(a/h)),z=Math.min(c,_),H=y.outcome!==""||y.provider!==""||y.keyword!==""||f!==_u,B=a?{start:(z-1)*h+1,end:Math.min(a,z*h)}:null,L=x.useCallback(async Q=>{const G=E.current+1;E.current=G,N(!0),A("");try{const Y=await Pe().workbuddyDesktop?.getCheckinRecords?.({limit:Q.size,offset:(Q.page-1)*Q.size,outcome:Q.filters.outcome,provider:Q.filters.provider,keyword:Q.filters.keyword,start:SM(Q.range)});if(G!==E.current)return;t(Array.isArray(Y?.records)?Y.records:[]),o(Number(Y?.total)||0)}catch(J){if(G!==E.current)return;A(J instanceof Error?J.message:String(J))}finally{G===E.current&&N(!1)}},[]);x.useEffect(()=>{L({page:c,size:h,range:f,filters:y})},[L,c,h,f,y]),x.useEffect(()=>{c>_&&d(_)},[c,_]),x.useLayoutEffect(()=>{const Q=Pe().wbColSettings;if(!Q?.register){const G=window.setTimeout(()=>R(J=>J+1),50);return()=>window.clearTimeout(G)}jw=Q.register({id:Bg,label:r("签到记录表"),columns:yw.map(G=>({key:G.key,label:G.label,align:G.align??"left"})),mount:()=>C.current,onChange:()=>{Ts.flushSync(()=>D()),TM()}}),D()},[P]),x.useLayoutEffect(()=>{TM()}),x.useEffect(()=>()=>{Pe().wbColSettings?.close()},[]);const F=Q=>{v(Q),d(1)},$=Q=>{const G=jM.includes(Q)?Q:_u;G!==f&&(NM(G),p(G),d(1))},X=Q=>{b(kM(Q)),mg(Bg,Q),d(1)},K=()=>{w(""),NM(_u),p(_u),F({outcome:"",provider:"",keyword:""})},q=async()=>{const Q=Pe().wbConfirm?.ask;if(!Q)return;const G=r(H?"确定清空当前筛选出的签到记录?清空后无法恢复。":"确定清空全部签到记录?清空后无法恢复。");if(await Q({title:r("清空签到记录"),html:G,okText:r("清空"),okClass:"danger"}))try{await Pe().workbuddyDesktop?.clearCheckinRecords?.(H?{outcome:y.outcome,provider:y.provider,keyword:y.keyword,start:SM(f)}:{all:"1"}),AM("签到记录已清空"),await L({page:1,size:h,range:f,filters:y}),d(1)}catch(J){AM(r("清空失败:{error}",{error:J instanceof Error?J.message:String(J)}),"err")}};return s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("签到记录")}),s.jsx("span",{className:"panel-sub",children:r("每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准")}),s.jsxs("div",{ref:C,className:"ml-auto flex items-center gap-2",children:[s.jsx(ne,{size:"sm",variant:"outline",disabled:S,onClick:()=>{L({page:c,size:h,range:f,filters:y})},children:r(S?"刷新中…":"刷新")}),s.jsx(ne,{size:"sm",variant:"ghost",className:"text-destructive",onClick:()=>{q()},children:r("清空")})]})]}),s.jsxs("div",{className:"ck-records-filters",children:[s.jsx(En,{options:y9,value:f,onValueChange:$,"aria-label":r("签到记录时间范围"),className:"shrink-0"}),s.jsxs(ut,{value:y.outcome,onValueChange:Q=>F({...y,outcome:Q??""}),children:[s.jsx(ft,{className:"w-[128px] shrink-0","aria-label":r("按结果筛选"),children:s.jsx(dt,{children:y.outcome?vw[y.outcome]?.label??y.outcome:r("全部结果")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:r("全部结果")}),Object.entries(vw).map(([Q,G])=>s.jsx($e,{value:Q,children:G.label},Q))]})]}),s.jsxs(ut,{value:y.provider,onValueChange:Q=>F({...y,provider:Q??""}),children:[s.jsx(ft,{className:"w-[148px] shrink-0","aria-label":r("按提供商筛选"),children:s.jsx(dt,{children:y.provider?EM(y.provider):r("全部提供商")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:r("全部提供商")}),I.map(Q=>s.jsx($e,{value:Q.id,children:Q.label},Q.id))]})]}),s.jsx("div",{className:"spacer"}),s.jsx(Fe,{type:"search",className:"ck-records-search w-[200px] basis-[200px] min-w-[150px] grow-0 shrink",placeholder:r("搜账号 / 收益 / 错误 / 触发原因"),value:j,onChange:Q=>w(Q.currentTarget.value),onBlur:()=>{j!==y.keyword&&F({...y,keyword:j.trim()})},onKeyDown:Q=>{Q.key==="Enter"&&Q.currentTarget.blur(),Q.key==="Escape"&&(w(y.keyword),Q.currentTarget.blur())}}),H?s.jsx(ne,{size:"sm",variant:"ghost",onClick:K,children:r("清除筛选")}):null,s.jsx("span",{className:"panel-sub",children:S?r("读取中…"):r("{n} 条",{n:a})})]}),k?s.jsxs("div",{className:"ck-page-error",children:[s.jsx("span",{children:r("读取签到记录失败:{error}",{error:k})}),s.jsx(ne,{size:"sm",variant:"outline",onClick:()=>{L({page:c,size:h,range:f,filters:y})},children:r("重试")})]}):null,e.length===0&&!S?s.jsx("div",{className:"ck-empty",children:r(H?"没有符合筛选条件的签到记录":"还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。")}):s.jsx("div",{className:"ck-records-scroll",children:s.jsxs("table",{className:"ck-table ck-records-table",children:[s.jsx("colgroup",{children:O.map(Q=>s.jsx("col",{"data-col":Q.key,className:`ck-col-${Q.key}`},Q.key))}),s.jsx("thead",{children:s.jsx("tr",{children:O.map(Q=>s.jsx("th",{"data-col":Q.key,className:`ck-col-${Q.key} ta-${Q.align}`,children:Q.label},Q.key))})}),s.jsx("tbody",{children:e.map(Q=>s.jsx("tr",{children:O.map(G=>S9(Q,G))},Q.rowId))})]})}),s.jsx($i,{total:a,range:B,page:z,pageCount:_,size:h,sizes:dg,onSizeChange:X,onPageChange:d,disabled:S})]})}function AM(e,t){Pe().wbApp?.toast?.(e,t)}window.wbCheckinRecords={visibleColumns:()=>CM().map(e=>({key:e.key}))};const RM={workbuddy:r("腾讯每日签到接口 · 仅国内版"),"workbuddy-intl":r("每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行"),raccoon:r("桌面端每日积分链路"),autoclaw:r("官方客户端的每日签到任务"),"autoclaw-intl":r("与国内版同一套任务接口 · 站点不同"),qoder:r("活动(campaign)领取 · 每天 10:00 刷新"),"qoder-intl":r("带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件"),trae:r("SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新"),loomy:r("无独立签到接口 · 每天替账号打一次首次登录积分"),kuku:r("「免费领积分」的每日任务 · 逐个领取"),officeace:r("一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证"),codearts:r("每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置"),chatglm:r("每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)")},k9=[{value:"center",label:r("签到中心")},{value:"schedule",label:r("定时签到")},{value:"records",label:r("签到记录")}];function C9(e){return r(e==="raccoon"?"小浣熊":e==="loomy"?"Loomy":e==="codearts"?"CodeArts":e==="officeace"?"OfficeAce 果办":e)}function T9(){const[e,t]=x.useState(fr());return x.useEffect(()=>lM(()=>t(fr())),[]),e}function E9({group:e}){const t=J7(e);return t==="none"?s.jsx(ze,{variant:"outline",shape:"tag",children:r("无账号")}):t==="done"?s.jsx(ze,{variant:"success",shape:"tag",children:r("已签")}):t==="part"?s.jsx(ze,{variant:"warning",shape:"tag",children:r("部分")}):s.jsx(ze,{variant:"brand",shape:"tag",children:r("待签")})}function Wf({id:e,label:t}){const a=kg[e==="workbuddy-intl"?"workbuddy":e];return s.jsx("span",{className:"ck-prov-logo",children:a?s.jsx("img",{src:a,alt:""}):t.slice(0,1)||"·"})}function A9({group:e}){const t=fr().signing,a=(o,c)=>t.has(`${o}:${c}`);return s.jsxs("table",{className:"ck-table",children:[s.jsx("thead",{children:s.jsxs("tr",{children:[s.jsx("th",{children:r("账号")}),s.jsx("th",{children:r("今日签到")}),s.jsx("th",{children:r("上次签到时间")}),s.jsx("th",{"aria-label":r("操作")})]})}),s.jsx("tbody",{children:e.accounts.map(o=>{const c=e.id==="workbuddy-intl";return s.jsxs("tr",{children:[s.jsx("td",{children:o.name||o.id}),s.jsx("td",{children:o.checkedInToday?s.jsx(ze,{variant:"success",shape:"tag",children:r("已签")}):s.jsx(ze,{variant:"brand",shape:"tag",children:r("待签")})}),s.jsx("td",{className:"text-subtle",children:o.checkinAt?Ls(o.checkinAt):"—"}),s.jsx("td",{className:"text-right",children:c?s.jsxs("span",{className:"inline-flex items-center gap-1",children:[s.jsx(ne,{size:"sm",variant:"ghost",disabled:a(o.id,"keepalive"),title:r("用免费模型与网页会话维持账号活跃 · 不领取奖励"),onClick:()=>{Pg(o.id,"keepalive")},children:a(o.id,"keepalive")?r("保活中…"):r("保活")}),s.jsx(ne,{size:"sm",variant:"ghost",disabled:a(o.id,"claim"),title:r("探测活动并领取日活奖励 · 不做保活"),onClick:()=>{Pg(o.id,"claim")},children:a(o.id,"claim")?r("领取中…"):r("领取")}),s.jsx(ne,{size:"sm",variant:"ghost",disabled:a(o.id,"full"),title:r("探测 + 领取 + 保活 · 与定时签到同一套组合"),onClick:()=>{Pg(o.id,"full")},children:a(o.id,"full")?r("执行中…"):r("保活+领取")})]}):s.jsx(ne,{size:"sm",variant:"ghost",disabled:a(o.id,"checkin"),onClick:()=>{Pg(o.id)},children:a(o.id,"checkin")?r("签到中…"):o.checkedInToday?r("重签"):r("签到")})})]},o.id)})})]})}function R9({group:e,expanded:t}){const a=e.totalCount>0;return s.jsxs("div",{className:t?"ck-prov-row open":"ck-prov-row",children:[s.jsxs("div",{className:"ck-prov-main",onClick:()=>a&&oM(e.id),onKeyDown:o=>{a&&(o.key==="Enter"||o.key===" ")&&(o.preventDefault(),oM(e.id))},role:a?"button":void 0,tabIndex:a?0:-1,"aria-expanded":a?t:void 0,children:[s.jsx(Wf,{id:e.id,label:e.label}),s.jsxs("div",{className:"ck-prov-info",children:[s.jsx("div",{className:"ck-prov-name",children:e.label}),s.jsx("div",{className:"ck-prov-desc",children:RM[e.id]??""})]}),s.jsxs("div",{className:"ck-prov-right",children:[s.jsx("span",{className:"ck-prov-count",children:a?r("{total} 账号 · {done} 已签",{total:e.totalCount,done:e.doneCount}):"—"}),s.jsx(E9,{group:e}),a?s.jsx("span",{className:"ck-chev","aria-hidden":!0,children:"›"}):null]})]}),e.id==="qoder-intl"?s.jsx(OM,{}):null,t?s.jsx("div",{className:"ck-prov-detail",children:s.jsx(A9,{group:e})}):null]})}function OM(){const[e,t]=x.useState(null),a=x.useCallback(async()=>{try{t(await Lr().getQoderUmid?.()??null)}catch{}},[]);x.useEffect(()=>{a()},[a]);const o=e?.installing===!0&&e.available!==!0;if(x.useEffect(()=>{if(!o)return;const d=window.setInterval(()=>{a()},3e3);return()=>{window.clearInterval(d)}},[o,a]),!e||e.available)return null;const c=async()=>{try{t(d=>d&&{...d,installing:!0}),await Lr().installQoderUmid?.(),Pe().wbApp?.toast?.(r("✅ UMID 组件已安装,下轮签到即可领取国际版积分"))}catch(d){Pe().wbApp?.toast?.(r("UMID 组件安装失败:{error}",{error:d instanceof Error?d.message:String(d)}),"err")}finally{a()}};return s.jsxs("div",{className:"flex flex-wrap items-center gap-2 pb-2.5 pl-[58px] pr-4 text-[12px] text-muted-foreground",children:[s.jsx("span",{children:r("国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。")}),e.installSupported?s.jsx(ne,{variant:"outline",size:"sm",disabled:e.installing===!0,onClick:()=>{c()},children:e.installing?r("安装中…"):r("一键安装组件")}):s.jsx("span",{children:r("安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。")})]})}function O9({row:e}){const t=e.provider??"loomy",a=fr().onboarding.get(e.id),o=a?.tasks??[],c=a?.unclaimed??0,d=a?.status==="loaded"&&o.length>0,f=d&&iM(e.id);return s.jsxs("div",{className:f?"ck-prov-row open":"ck-prov-row",children:[s.jsxs("div",{className:"ck-prov-main",onClick:d?()=>hw(e.id):void 0,onKeyDown:p=>{d&&(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),hw(e.id))},role:d?"button":void 0,tabIndex:d?0:-1,"aria-expanded":d?f:void 0,children:[s.jsx(Wf,{id:t,label:C9(t)}),s.jsxs("div",{className:"ck-prov-info",children:[s.jsx("div",{className:"ck-prov-name",children:e.name||e.id}),s.jsxs("div",{className:"ck-prov-desc",children:[a?.status==="loaded"?r("已领 {claimed}/{total} · 累计 {earned} 积分",{claimed:o.length-c,total:o.length,earned:a.total?`${a.earned} / ${a.total}`:a.earned}):r("尚未查询任务状态"),a?.settled&&a.settledAt?` · ${r("结算于 {time}",{time:Ls(a.settledAt)})}`:a?.checkedAt?` · ${r("查询于 {time}",{time:Ls(a.checkedAt)})}`:""]})]}),s.jsxs("div",{className:"ck-prov-right",onClick:p=>p.stopPropagation(),children:[a?.status==="loaded"&&c>0?s.jsx(ze,{variant:"warning",shape:"tag",children:r("{n} 项待领",{n:c})}):a?.status==="loaded"?s.jsx(ze,{variant:"success",shape:"tag",children:r("全部领取")}):a?.status==="error"?s.jsx(ze,{variant:"destructive",shape:"tag",children:r("查询失败")}):null,s.jsx(ne,{size:"sm",variant:"outline",disabled:a?.status==="loading"||a?.claiming===!0,onClick:()=>{gw(e.id,{expand:!0,refresh:!0})},children:a?.status==="loading"?r("查询中…"):r("查询任务")}),s.jsx(ne,{size:"sm",variant:"outline",disabled:a?.status!=="loaded"||t!=="officeace"&&c===0||a?.claiming===!0,onClick:()=>{uM(e.id)},children:a?.claiming?r("领取中…"):c>0?r("一键领取({n})",{n:c}):r("领取")}),d?s.jsx("span",{className:"ck-chev","aria-hidden":!0,children:"›"}):null]})]}),a?.status==="error"?s.jsx("div",{className:"ck-fold-note",children:r("查询失败:{error}",{error:a.error??""})}):null,f?s.jsx("div",{className:"ck-prov-detail",children:o.map(p=>s.jsxs("div",{className:"ck-task-line",children:[s.jsx("span",{className:p.done?"ck-task-tick done":"ck-task-tick",children:p.done?"✓":"·"}),s.jsx("span",{children:p.title}),s.jsxs("span",{className:"ck-task-pts",children:["+",p.points]}),s.jsx("span",{className:"ck-task-st",children:p.claiming?s.jsx(ze,{variant:"warning",shape:"tag",children:r("领取中")}):p.done?s.jsx(ze,{variant:"success",shape:"tag",children:r("已领取")}):p.blocked?s.jsx(ze,{variant:"outline",shape:"tag",children:r("暂不可领")}):p.error?s.jsx(ze,{variant:"destructive",shape:"tag",children:p.error}):s.jsx(ze,{variant:"brand",shape:"tag",children:r("可领取")})})]},p.key))}):null]})}function M9({row:e}){const t=eg({claimPlans:e.claimPlans}),a=t.length>0;return s.jsxs("div",{className:"ck-welfare-row",children:[s.jsx(Wf,{id:"zcode",label:"ZCode"}),s.jsxs("div",{className:"ck-prov-info",children:[s.jsxs("div",{className:"ck-prov-name",children:[e.name||e.id,a?s.jsx(ze,{variant:"success",shape:"tag",title:r("本地台账里今天(北京时间)领过的套餐:{plans}",{plans:t.join(r("、"))}),children:r("今天已领 {n} 份",{n:t.length})}):s.jsx(ze,{variant:"outline",shape:"tag",title:r("本地台账里今天还没领过;活动期内每天发一份新套餐"),children:r("今天未领")})]}),s.jsx("div",{className:"ck-prov-desc",children:a?r("点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领"):e.claimAt?r("上次领取 {time} · 领取需通过滑块验证码",{time:Ls(e.claimAt)}):r("限时体验套餐(start-plan),活动期内每天一份 · 领取需滑块验证码")})]}),s.jsx("div",{className:"ck-prov-right",children:s.jsx(ne,{size:"sm",variant:"outline",onClick:()=>RR(e.id,e.name),children:r(a?"套餐明细":"去领取")})})]})}function D9({row:e}){const t=fr().growth.get(e.id),a=t?.tasks??[],o=a.filter(v=>v.supported&&!v.unforgeable),c=o.filter(v=>v.status!=="claimed"&&v.current0,f=d&&Q7(e.id),p=t?.travel,h=t?.school,b=t?.results??[],y=v=>b.find(j=>j.code===v);return s.jsxs("div",{className:f?"ck-prov-row open":"ck-prov-row",children:[s.jsxs("div",{className:"ck-prov-main",onClick:d?()=>pM(e.id):void 0,onKeyDown:v=>{d&&(v.key==="Enter"||v.key===" ")&&(v.preventDefault(),pM(e.id))},role:d?"button":void 0,tabIndex:d?0:-1,"aria-expanded":d?f:void 0,children:[s.jsx(Wf,{id:"workbuddy",label:"WorkBuddy"}),s.jsxs("div",{className:"ck-prov-info",children:[s.jsx("div",{className:"ck-prov-name",children:e.name||e.id}),s.jsxs("div",{className:"ck-prov-desc",children:[t?.status==="loaded"?r("{total} 项任务 · {pending} 项可完成",{total:o.length,pending:c.length}):r("查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)"),t?.checkedAt?` · ${r("查询于 {time}",{time:Ls(t.checkedAt)})}`:""]})]}),s.jsxs("div",{className:"ck-prov-right",onClick:v=>v.stopPropagation(),children:[t?.status==="loaded"?c.length>0?s.jsx(ze,{variant:"warning",shape:"tag",children:r("{n} 项可完成",{n:c.length})}):s.jsx(ze,{variant:"success",shape:"tag",children:r("全部完成")}):t?.status==="error"?s.jsx(ze,{variant:"destructive",shape:"tag",children:r("查询失败")}):null,s.jsx(ne,{size:"sm",variant:"outline",disabled:t?.status==="loading"||t?.running===!0,onClick:()=>{Lg(e.id)},children:t?.status==="loading"?r("查询中…"):r("查询任务")}),s.jsx(ne,{size:"sm",variant:"outline",disabled:t?.status!=="loaded"||t?.running===!0,title:r("接单 + 点亮 + 领奖(只处理已实测沉淀的任务)"),onClick:()=>{mM(e.id,"full")},children:t?.running?r("执行中…"):r("一键完成")}),s.jsx(ne,{size:"sm",variant:"ghost",disabled:t?.status!=="loaded"||t?.running===!0,title:r("只领取已完成任务的奖励(幂等,重复点不会重复入账)"),onClick:()=>{mM(e.id,"claim")},children:r("领取")}),d?s.jsx("span",{className:"ck-chev","aria-hidden":!0,children:"›"}):null]})]}),t?.status==="error"?s.jsx("div",{className:"ck-fold-note",children:r("查询失败:{error}",{error:t.error??""})}):null,f?s.jsxs("div",{className:"ck-prov-detail",children:[s.jsxs("div",{className:"ck-fold-note",children:[t?.energy!==null&&t?.energy!==void 0?r("能量 {n}",{n:t.energy}):"",t?.streak!==null&&t?.streak!==void 0?` · ${r("连登 {n} 天",{n:t.streak})}`:"",t?.energy===null&&t?.streak===null?r("(能量 / 连登读数本次未取到)"):""]}),a.map(v=>{const j=y(v.code);return s.jsxs("div",{className:"ck-task-line",children:[s.jsx("span",{className:v.status==="claimed"?"ck-task-tick done":"ck-task-tick",children:v.status==="claimed"?"✓":"·"}),s.jsxs("span",{title:v.description||v.source||v.code,children:[v.title||v.code,v.target>1?` (${v.current}/${v.target})`:""]}),j?s.jsx("span",{className:"ck-task-pts",children:j.message}):null,s.jsx("span",{className:"ck-task-st",children:v.unforgeable?s.jsx(ze,{variant:"outline",shape:"tag",children:r("需真实操作")}):v.supported?v.status==="claimed"?s.jsx(ze,{variant:"success",shape:"tag",children:r("已领取")}):v.status==="completed"?s.jsx(ze,{variant:"brand",shape:"tag",children:r("可领取")}):v.locked?s.jsx(ze,{variant:"outline",shape:"tag",children:r("未解锁")}):s.jsx(ze,{variant:"warning",shape:"tag",children:r("可完成")}):s.jsx(ze,{variant:"outline",shape:"tag",children:r("暂不支持")})})]},v.code)}),s.jsxs("div",{className:"ck-task-line",children:[s.jsx("span",{className:"ck-task-tick",children:"🐾"}),s.jsxs("span",{children:[r("猫猫旅行"),p&&p.hasBuddy===!0?`:${p.buddyName||r("已领养")} · ${p.state==="traveling"?r("在途"):p.state==="arrived"?r("可领奖"):p.state==="idle"?p.dailyLimitReached?r("今日已派出"):r("可派出"):p.state??""}`:p&&p.hasBuddy===!1?`:${r("未领养(领养需先完成当日对话量)")}`:":"+(p?.error?String(p.error):r("尚未查询"))]}),s.jsx("span",{className:"ck-task-st",children:s.jsx(ne,{size:"sm",variant:"ghost",disabled:t?.runningTravel===!0||t?.status!=="loaded",onClick:()=>{W7(e.id)},children:t?.runningTravel?r("执行中…"):r("巡检一次")})})]}),s.jsxs("div",{className:"ck-task-line",children:[s.jsx("span",{className:"ck-task-tick",children:"🎓"}),s.jsxs("span",{children:[r("开学季活动"),h&&h.inPeriod===!0?`:${r("进行中")}${h.lottery?.balance!==void 0&&h.lottery?.balance!==null?` · ${r("抽奖机会 {n} 次",{n:h.lottery.balance})}`:""}`:h&&h.inPeriod===!1?`:${r("非活动期")}`:":"+(h?.error?String(h.error):r("尚未查询"))]}),s.jsx("span",{className:"ck-task-st",children:s.jsx(ne,{size:"sm",variant:"ghost",disabled:t?.runningSchool===!0||t?.status!=="loaded",title:r("执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)"),onClick:()=>{Z7(e.id,!0)},children:t?.runningSchool?r("执行中…"):r("执行并抽奖")})})]}),t?.schoolText?s.jsx("div",{className:"ck-fold-note",children:t.schoolText}):null,t?.lotteryText?s.jsx("div",{className:"ck-fold-note",children:t.lotteryText}):null]}):null]})}function I9({group:e}){return s.jsxs("div",{className:"ck-prov-row",children:[s.jsxs("div",{className:"ck-prov-main",style:{cursor:"default"},children:[s.jsx(Wf,{id:e.id,label:e.label}),s.jsxs("div",{className:"ck-prov-info",children:[s.jsx("div",{className:"ck-prov-name",children:e.label}),s.jsx("div",{className:"ck-prov-desc",children:RM[e.id]??""})]}),s.jsx("div",{className:"ck-prov-right",children:s.jsx(ze,{variant:"outline",shape:"tag",children:r("无账号")})})]}),e.id==="qoder-intl"?s.jsx(OM,{}):null]})}function P9({store:e}){const t=e.snapshot,[a,o]=x.useState(null);x.useEffect(()=>{let N=!0;return(async()=>{try{const A=await Pe().workbuddyDesktop?.getCheckinRecords?.({limit:1});N&&o(Number(A?.total)||0)}catch{}})(),()=>{N=!1}},[e.version]);const c=t?.daily,d=t?.auto,f=(c?.outOfScope??[]).reduce((N,k)=>N+(Number(k.count)||0),0),p=t?.extras.onboarding??[],h=p.filter(N=>e.onboarding.get(N.id)?.status==="loaded"),b=p.reduce((N,k)=>N+(e.onboarding.get(k.id)?.unclaimed??0),0),y=t?.extras.plans??[],v=t?.extras.growth??[],j=Array.isArray(d?.schedules)?d.schedules:[],w=j.filter(N=>N.enabled===!0).length,S=e9(d);return s.jsxs("div",{children:[s.jsxs("div",{className:"ck-stats",children:[s.jsxs("div",{className:"ck-stat",children:[s.jsx("div",{className:"ck-stat-label",children:r("今日签到进度")}),s.jsx("div",{className:"ck-stat-value",children:c?s.jsxs(s.Fragment,{children:[" ",c.todayDone,s.jsxs("small",{children:[" / ",c.todayEligible]})]}):s.jsx("span",{className:"ck-pending",children:"—"})}),s.jsx(cf,{className:"ck-stat-bar",value:c&&c.todayEligible>0?c.todayDone/c.todayEligible*100:0}),s.jsx("div",{className:"ck-stat-foot",children:f>0?r("另有 {n} 个账号不参与每日签到",{n:f}):r("全部账号均可签到")})]}),s.jsxs("div",{className:"ck-stat ck-stat-link",onClick:()=>{Ig("schedule")},role:"button",tabIndex:0,onKeyDown:N=>{N.key==="Enter"&&Ig("schedule")},children:[s.jsx("div",{className:"ck-stat-label",children:r("定时签到")}),s.jsx("div",{className:"ck-stat-value",children:d?s.jsxs(s.Fragment,{children:[w,s.jsxs("small",{children:[" / ",j.length," ",r("条启用")]})]}):s.jsx("span",{className:"ck-pending",children:"—"})}),s.jsx("div",{className:"ck-stat-foot",children:d?.enabled!==!0?r("总开关已关闭 · 在「定时签到」页打开"):S?r("下次 {date} · 点此管理条目",{date:Ls(S)}):r("暂无启用的条目 · 点此添加")})]}),s.jsxs("div",{className:"ck-stat",children:[s.jsx("div",{className:"ck-stat-label",children:r("待领新手福利")}),s.jsx("div",{className:"ck-stat-value",children:p.length?s.jsxs(s.Fragment,{children:[b,s.jsxs("small",{children:[" ",r("项")]})]}):s.jsx("span",{className:"ck-pending",children:r("无")})}),s.jsx("div",{className:"ck-stat-foot",children:p.length?r("已有结果 {checked} / 共 {total} 个账号",{checked:h.length,total:p.length}):r("没有支持新手福利的账号")})]}),s.jsxs("div",{className:"ck-stat ck-stat-link",onClick:()=>{Ig("records")},role:"button",tabIndex:0,onKeyDown:N=>{N.key==="Enter"&&Ig("records")},children:[s.jsx("div",{className:"ck-stat-label",children:r("累计签到记录")}),s.jsx("div",{className:"ck-stat-value",children:a===null?s.jsx("span",{className:"ck-pending",children:"—"}):a}),s.jsx("div",{className:"ck-stat-foot",children:r("每账号一条明细 · 点此查看「签到记录」")})]})]}),s.jsxs("div",{className:"ck-main",children:[s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("每日签到")}),s.jsx("span",{className:"panel-sub",children:r("点击行展开账号明细 · 单账号签到不受范围限制")}),s.jsx(ne,{size:"sm",variant:"default",className:"ml-auto",disabled:e.runningAll||d?.running===!0,onClick:()=>{K7()},children:e.runningAll||d?.running===!0?r("签到中…"):r("立即全部签到")})]}),s.jsxs("div",{children:[(c?.providers??[]).map(N=>N.totalCount>0?s.jsx(R9,{group:N,expanded:e.expanded.has(N.id)},N.id):s.jsx(I9,{group:N},N.id)),(c?.outOfScope??[]).length>0?s.jsx("div",{className:"ck-fold-note",children:r("不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。",{list:c.outOfScope.map(N=>r("{label} ×{count}({reason})",{label:N.label,count:N.count,reason:N.reason})).join(r(";"))})}):null]})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("新手福利")}),s.jsx("span",{className:"panel-sub",children:r("一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单")}),p.length>0?s.jsx(ne,{size:"sm",variant:"outline",className:"ml-auto",onClick:()=>{for(const N of p)N.settled&&typeof N.settled=="object"||gw(N.id)},children:r("全部查询")}):null]}),p.length?s.jsx("div",{children:p.map(N=>s.jsx(O9,{row:N},N.id))}):s.jsx("div",{className:"ck-empty",children:r("没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。")})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("活动福利")}),s.jsx("span",{className:"panel-sub",children:r("限时体验套餐 · 领取需滑块验证码 · 逐份查看与领取")})]}),y.length>0?s.jsx("div",{children:y.map(N=>s.jsx(M9,{row:N},N.id))}):s.jsx("div",{className:"ck-empty",children:r("没有可领套餐的账号(ZCode 未添加)。")})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("成长任务")}),s.jsx("span",{className:"panel-sub",children:r("WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细")})]}),v.length>0?s.jsx("div",{children:v.map(N=>s.jsx(D9,{row:N},N.id))}):s.jsx("div",{className:"ck-empty",children:r("没有支持成长任务的账号(需要 WorkBuddy 国内版)。")})]})]})]})}function _9(){const e=T9(),t=e.snapshot;if(x.useEffect(()=>{Il()},[]),x.useEffect(()=>{window.wbApp?.renderTopbarStatus?.()},[e.version]),!e.loaded)return s.jsxs("div",{className:"ck-loading",children:[s.jsx(of,{className:"size-5"}),s.jsx("span",{children:r("正在读取签到数据…")})]});const a=t?.auto,o=a?.enabled===!0;return s.jsxs("div",{children:[e.loadError?s.jsxs("div",{className:"ck-page-error",children:[s.jsx("span",{children:r("读取签到数据失败:{error}",{error:e.loadError})}),s.jsx(ne,{size:"sm",variant:"outline",onClick:()=>{Il()},children:r("重试")})]}):null,s.jsxs("div",{className:"ck-tabs",children:[s.jsx(En,{options:k9,value:e.tab,onValueChange:c=>aM(c),"aria-label":r("签到中心分区")}),s.jsx(ze,{id:"checkin-badge",className:"ml-auto",variant:a?o?"success":"outline":"destructive","data-tone":a?o?"ok":"":"bad",children:a?o?a?.lastFiredToday?r("今日已执行"):r("已开启"):r("已关闭"):r("不可用")})]}),e.tab==="center"?s.jsx(P9,{store:e}):null,e.tab==="schedule"?s.jsx(v9,{}):null,e.tab==="records"?s.jsx(N9,{}):null]})}const MM='.page[data-page="checkin"]';let DM=!1;function IM(){if(DM)return;const e=document.querySelector(MM);e&&(DM=!0,e.replaceChildren(),jn.createRoot(e).render(s.jsx(_9,{})))}const PM=()=>Il();window.wbCheckinPanel={load:PM},document.querySelector(MM)?IM():document.addEventListener("DOMContentLoaded",IM,{once:!0}),Pe().wbApp?.currentPage==="checkin"&&PM();function L9(e){return String(e??"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}function z9({options:e,onSettle:t}){const{title:a="确认操作",html:o="",text:c="",okText:d="确定",okClass:f="primary",bodyClass:p="danger-zone",cancelText:h="取消"}=e,b=x.useRef(null);return x.useEffect(()=>{function y(v){v.key==="Escape"&&t(!1)}return document.addEventListener("keydown",y),()=>document.removeEventListener("keydown",y)},[t]),x.useEffect(()=>{function y(v){v.target?.dataset.slot==="alert-dialog-overlay"&&t(!1)}return document.addEventListener("click",y),()=>document.removeEventListener("click",y)},[t]),s.jsx(PV,{open:!0,onOpenChange:y=>{y||t(!1)},children:s.jsxs(BV,{initialFocus:b,showCloseButton:!0,children:[s.jsx(HV,{className:"pr-10",children:s.jsx(LV,{children:a})}),s.jsx(UV,{children:s.jsx(FV,{tone:p==="danger-zone"?"danger":"neutral",dangerouslySetInnerHTML:{__html:o||L9(c).replace(/\n/g,"
    ")}})}),s.jsxs(VV,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{ref:b,variant:"outline",onClick:()=>t(!1),children:h}),s.jsx(ne,{variant:f==="danger"?"destructive":"default",onClick:()=>t(!0),children:d})]})]})})}let Jf=null,Lu=null,Ug=null;function B9(){Jf&&(Jf.unmount(),Jf=null),Lu&&(Lu.remove(),Lu=null)}function _M(e){const t=Ug;Ug=null,B9(),t?.(e)}function H9(e={}){return new Promise(t=>{Ug&&_M(!1),Ug=t,Lu=document.createElement("div"),document.body.append(Lu),Jf=jn.createRoot(Lu),Jf.render(s.jsx(z9,{options:e,onSettle:_M}))})}window.wbConfirm={ask:H9};const U9=r("Base URL 填进客户端的「API 地址 / Base URL」栏;对话协议按客户端支持的类型三选一(Chat Completions / Responses / Anthropic Messages),三者共用同一套模型与账号池,可随时切换;鉴权用「网关 Key」页里任一启用 Key。");function ep({id:e,name:t,note:a}){return s.jsxs("div",{className:"ep-row",children:[s.jsxs("div",{className:"ep-head",children:[s.jsx("span",{className:"ep-name",children:t}),s.jsx("span",{className:"ep-note",children:a})]}),s.jsxs("div",{className:"ep-line",children:[s.jsx("code",{className:"ep-value",id:e,children:"—"}),s.jsx(ne,{variant:"ghost",size:"icon-xs",className:"copy-btn size-[21px] rounded-[var(--r-xs)]","data-copy-from":e,title:r("复制地址"),children:"⧉"})]})]})}function V9(){return s.jsxs("section",{className:"panel ep-panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("接口地址")}),s.jsx("span",{className:"tip-q","data-tip":U9})]}),s.jsxs("div",{className:"ep-list",children:[s.jsx(ep,{id:"api-base",name:"Base URL",note:r("填进客户端的「API 地址 / Base URL」")}),s.jsx("div",{className:"ep-group",children:r("对话协议(按客户端支持的类型选一行)")}),s.jsx(ep,{id:"api-chat",name:"OpenAI Chat Completions",note:r("多数 OpenAI 兼容客户端的默认协议")}),s.jsx(ep,{id:"api-responses",name:"OpenAI Responses",note:r("OpenAI 新协议(Codex、新版 SDK)")}),s.jsx(ep,{id:"api-messages",name:"Anthropic Messages",note:r("Claude Code / Anthropic SDK(token 计数:/v1/messages/count_tokens)")}),s.jsx("div",{className:"ep-group",children:r("模型清单")}),s.jsx(ep,{id:"api-models",name:r("模型列表"),note:r("客户端自动拉取可用模型;不受鉴权限制,配 Key 之前就能拉")})]}),s.jsx("div",{className:"panel-foot",children:s.jsxs("span",{children:[r("鉴权用「网关 Key」页里任一"),s.jsx("b",{children:r("启用")}),r("的 Key:请求带 "),s.jsx("code",{children:"Authorization: Bearer "}),r(" 或 "),s.jsx("code",{children:"x-api-key: "}),r("。一把启用的 Key 都没有时不鉴权(默认网关只监听 127.0.0.1;在设置页开启「局域网访问」后监听所有网卡,鉴权与安全闸门随之启用)。")]})})]})}const LM='.page[data-page="docs"]';let zM=!1;function BM(){if(zM)return;const e=document.querySelector(LM);e&&(zM=!0,e.replaceChildren(),jn.createRoot(e).render(s.jsx(V9,{})))}document.querySelector(LM)?BM():document.addEventListener("DOMContentLoaded",BM,{once:!0});function da(){return window}function ww(e){return e instanceof Error?e.message:String(e)}function tp(e,t){da().wbApp?.toast?.(e,t)}function F9(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t]??t)}const Sw=[{key:"name",label:r("名称")},{key:"key",label:"Key"},{key:"time",label:r("创建时间")},{key:"state",label:r("启用")},{key:"act",label:r("操作"),align:"right"}],$9={name:"cell-name",key:"cell-key",time:"cell-time",state:"cell-state state",act:"cell-act r"},HM='.page[data-page="keys"]';let Vg=null,Nw=null;function UM(){return Vg?Vg.apply([...Sw]):Sw.map(e=>({...e,align:e.align??"left"}))}function VM(){da().wbColSettings?.syncStaticHead?.("keys",document.querySelector("table.keys-table"))}function q9(){if(Vg)return;const e=da().wbColSettings?.register({id:"keys",label:r("网关 Key 表"),columns:Sw,mount:()=>document.querySelector('.page[data-page="keys"] .panel-head .head-actions'),onChange:()=>{VM(),Nw?.()}});e&&(Vg=e,VM())}function G9(e){return Array.isArray(e?.keys)?e.keys:[]}function K9(e){return Array.isArray(e?.providers)?e.providers:[]}function Y9(e){const t=e?.modelsByProvider;return t&&typeof t=="object"?t:{}}function kw(e){return r(da().wbProviders?.labelOf?.(e)||e)}function X9(e,t,a){const o=new Map,c=f=>{const p=String(f??"").trim();if(!p)return;const h=p.toLowerCase();o.has(h)||o.set(h,p)};t.forEach(f=>{const p=e[f]??e[String(f).toLowerCase()];Array.isArray(p)&&p.forEach(c)}),a.forEach(c);const d=[...o.values()].map(f=>({value:f,label:r(f)}));return d.sort((f,p)=>f.value.toLowerCase()String(t)):[]}function $M(e,t){const a=new Map(t.map(c=>[c.value.toLowerCase(),c.value])),o=new Set;return e.forEach(c=>{const d=a.get(String(c).toLowerCase());d&&o.add(d)}),t.filter(c=>o.has(c.value)).map(c=>c.value)}function J9({target:e,providers:t,modelsByProvider:a,onClose:o,onSaved:c}){const d=e?.id??null,f=!!d,[p,h]=x.useState(e?.name??""),[b,y]=x.useState(""),[v,j]=x.useState(""),[w,S]=x.useState(!1),N=x.useRef(!1),[k,A]=x.useState(()=>FM(e?.allowedProviders)),[P,R]=x.useState(()=>FM(e?.allowedModels)),C=x.useMemo(()=>t.map(H=>({value:String(H.id),label:r(String(H.label??H.id))})),[t]),E=x.useMemo(()=>$M(k,C),[k,C]),D=x.useMemo(()=>X9(a,E,P),[a,E,P]),I=x.useMemo(()=>$M(P,D),[P,D]),O=D.length?r("没有匹配的选项"):E.length?r("这几家当前没有可用模型(账号未登录或清单为空)"):r("请先在上面选择可用提供商");function _(){N.current=!1,S(!1)}async function z(){if(!N.current){N.current=!0,S(!0),j(r("保存中…"));try{const H=da().workbuddyDesktop;if(!H)throw new Error(r("后端桥不可用"));if(d){const F=await H.updateKey(d,{allowedProviders:E,allowedModels:I});_(),c(F),o(),tp(r("✅ 可用范围已保存"));return}const B=b.trim();if(B&&B.length<8){j(r("Key 至少需要 8 个字符"));return}const L=await H.createKey({name:p.trim(),key:B||void 0,allowedProviders:E,allowedModels:I});_(),c(L,L?.created?.id),o(),tp(r("✅ Key 已创建,记得复制给客户端"))}catch(H){j(r("保存失败:{reason}",{reason:ww(H)}))}finally{_()}}}return s.jsx(Yt,{open:!0,onOpenChange:(H,B)=>{if(!H){if(N.current){B.cancel();return}o()}},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:f?r("可用范围 · {name}",{name:e?.name||r("未命名")}):r("新建 API Key")})}),s.jsxs(Lt,{children:[s.jsxs(dn,{children:[s.jsxs("div",{className:"flex flex-wrap items-center gap-2.5",children:[s.jsx(Je,{htmlFor:"key-name",className:"text-[12.5px] whitespace-nowrap text-subtle",children:r("名称")}),s.jsx(Fe,{id:"key-name",type:"text",maxLength:60,placeholder:r("例如 Cursor / 公司电脑"),autoComplete:"off",value:p,disabled:f,onChange:H=>h(H.currentTarget.value)})]}),!f&&s.jsxs("div",{className:"flex flex-wrap items-center gap-2.5",children:[s.jsx(Je,{htmlFor:"key-value",className:"text-[12.5px] whitespace-nowrap text-subtle",children:"Key"}),s.jsx(Fe,{id:"key-value",type:"text",placeholder:r("留空自动生成;手填至少 8 个字符"),autoComplete:"off",spellCheck:!1,value:b,onChange:H=>y(H.currentTarget.value),onKeyDown:H=>{H.key==="Enter"&&z()}})]}),s.jsxs("div",{className:"flex flex-wrap items-center gap-2.5",children:[s.jsx(Je,{className:"text-[12.5px] whitespace-nowrap text-subtle",children:r("可用提供商")}),s.jsx(Gh,{value:E,onValueChange:A,options:C,placeholder:r("留空 = 不限制"),searchPlaceholder:r("搜索提供商…"),"aria-label":r("可用提供商"),className:"flex-auto min-w-[180px] max-w-[260px]"})]}),s.jsxs("div",{className:"flex flex-wrap items-center gap-2.5",children:[s.jsx(Je,{className:"text-[12.5px] whitespace-nowrap text-subtle",children:r("可用模型")}),s.jsx(Gh,{value:I,onValueChange:R,options:D,placeholder:r("留空 = 不限制"),emptyHint:O,searchPlaceholder:r("搜索模型…"),"aria-label":r("可用模型"),className:"flex-auto min-w-[180px] max-w-[260px]"})]}),s.jsx("p",{children:r("留空表示不限制;同时设置时请求需同时满足两个条件(模型在白名单内且路由到允许的提供商)。 「可用模型」的候选跟着上面勾选的提供商走:没勾提供商时它是空的(还没有约束范围), 勾了几家就列出这几家能收的全部对外名。")}),s.jsx("p",{id:"key-restrict-summary",children:W9(E,I)})]}),s.jsx("div",{className:"min-h-[18px] text-[11.5px] text-muted-foreground",children:v})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:o,disabled:w,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:w,onClick:()=>{z()},children:r(f?"保存范围":"创建")})]})]})})}let Fg=null;async function eK(){await Fg?.load()}function tK(){Fg?.render()}function nK(){const[e,t]=x.useState(null),[a,o]=x.useState(()=>new Set),[c,d]=x.useState(()=>new Set),[f,p]=x.useState(null),[,h]=x.useState(0),b=x.useRef(new Set),y=x.useRef(!1),v=x.useCallback(z=>{t(z)},[]),j=x.useCallback(z=>{z&&Array.isArray(z.keys)&&v(z)},[v]),w=x.useCallback(async()=>{if(!y.current){y.current=!0;try{const z=da().workbuddyDesktop;if(!z)throw new Error(r("后端桥不可用"));v(await z.getKeys()??null)}catch(z){tp(r("读取 Key 列表失败:{reason}",{reason:ww(z)}),"err")}finally{y.current=!1}}},[v]);async function S(z,H,B){if(!b.current.has(z)){b.current.add(z),d(new Set(b.current));try{j(await H()),B&&tp(B)}catch(L){tp(r("操作失败:{reason}",{reason:ww(L)}),"err")}finally{b.current.delete(z),d(new Set(b.current))}}}function N(z){o(H=>{const B=new Set(H);return B.has(z)?B.delete(z):B.add(z),B})}async function k(z){const H=da().workbuddyDesktop,B=da().wbConfirm?.ask;if(!H||!B)return;const L=F9(z.name||z.masked||z.id);await B({title:r("删除网关 Key"),html:r("确定删除 Key「{name}」?使用它的客户端会立刻无法访问。",{name:L}),okText:r("删除"),okClass:"danger"})&&S(z.id,()=>H.deleteKey(z.id),r("Key 已删除"))}function A(z,H){const B=da().workbuddyDesktop;B&&S(z.id,()=>B.updateKey(z.id,{enabled:H}),r(H?"Key 已启用":"Key 已停用"))}x.useLayoutEffect(()=>(Fg={load:w,render:()=>h(z=>z+1)},Nw=()=>h(z=>z+1),q9(),h(z=>z+1),()=>{Fg=null,Nw=null}),[w]),x.useEffect(()=>{da().wbApp?.currentPage==="keys"&&w()},[w]),x.useEffect(()=>{da().wbApp?.renderTopbarStatus?.()},[e]);const P=G9(e),R=hg(P.length,"keys"),C=R.paged?R.slice(P):P,E=UM(),D=e?.authRequired===!0,I=C.filter(z=>z.enabled).length,O=D?r("已启用鉴权 · {n} 把 Key 生效",{n:I}):r("未启用鉴权");function _(z,H,B){switch(z){case"name":return s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mid",children:s.jsx("span",{className:"t",children:H.name||r("未命名")})}),s.jsx("div",{className:"mname",title:Z9(H),children:Q9(H)})]});case"key":{const L=a.has(H.id);return s.jsxs("div",{className:"keycell",children:[s.jsx("code",{className:"kv",children:L?H.key:H.masked}),s.jsx(ne,{size:"sm",variant:"ghost",onClick:()=>N(H.id),children:r(L?"隐藏":"显示")}),s.jsx(ne,{size:"sm",variant:"ghost","data-copy":H.key,title:r("复制 Key"),children:r("复制")})]})}case"time":return s.jsx("span",{className:"muted",children:H.createdAt?da().wbApp?.formatTime?.(H.createdAt):"—"});case"state":return s.jsx(ns,{checked:H.enabled===!0,disabled:B,"aria-label":r("启用「{name}」",{name:H.name||r("未命名")}),onCheckedChange:L=>A(H,L)});case"act":return s.jsxs("div",{className:"row-actions",children:[s.jsx(ne,{size:"sm",variant:"ghost",disabled:B,onClick:()=>p({key:H}),children:r("可用范围")}),s.jsx(ne,{size:"sm",variant:"destructive",disabled:B,onClick:()=>{k(H)},children:r("删除")})]});default:return null}}return s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("API Key 列表")}),s.jsx(ze,{id:"keys-status",variant:D?"success":"warning","data-tone":D?"ok":"warn",children:O}),s.jsx("div",{className:"head-actions",children:s.jsx(ne,{variant:"default",onClick:()=>p({key:null}),children:r("+ 新建 Key")})})]}),s.jsx("div",{className:"models-table-wrap",children:s.jsxs("table",{className:"models-table keys-table",children:[s.jsxs("colgroup",{children:[s.jsx("col",{className:"k-name","data-col":"name"}),s.jsx("col",{className:"k-key","data-col":"key"}),s.jsx("col",{className:"k-time","data-col":"time"}),s.jsx("col",{className:"k-state","data-col":"state"}),s.jsx("col",{className:"k-act","data-col":"act"})]}),s.jsx("thead",{children:s.jsxs("tr",{children:[s.jsx("th",{"data-col":"name",children:r("名称")}),s.jsx("th",{"data-col":"key",children:"Key"}),s.jsx("th",{"data-col":"time",children:r("创建时间")}),s.jsx("th",{"data-col":"state",children:r("启用")}),s.jsx("th",{className:"r","data-col":"act",children:r("操作")})]})}),s.jsx("tbody",{children:C.length?C.map(z=>{const H=c.has(z.id);return s.jsx("tr",{className:z.enabled?"":"off","data-id":z.id,children:E.map(B=>s.jsx("td",{className:`${$9[B.key]??""} ta-${B.align}`,children:_(B.key,z,H)},B.key))},z.id)}):s.jsx("tr",{children:s.jsx("td",{colSpan:E.length,className:"empty",children:r(e?"还没有 Key,当前不鉴权":"加载中…")})})})]})}),s.jsx($i,{leading:s.jsxs("span",{children:[r("客户端请求需带 "),s.jsx("code",{children:"Authorization: Bearer "}),r(" 或 "),s.jsx("code",{children:"x-api-key: "}),r("; 修改后立即生效,本程序自身会自动使用第一把启用的 Key。每把 Key 可单独限制"),s.jsx("b",{children:r("可用提供商")}),r("与"),s.jsx("b",{children:r("可用模型")}),r("(行内「可用范围」):留空 = 不限制,两个都设时 按交集生效 —— 被限制的模型对这把 Key 表现为「不存在」(拉 /v1/models 也看不到它), 提供它的家不在可用列表里时请求同样被拒。")]}),total:P.length,range:R.paged?{start:R.rangeStart,end:R.rangeEnd}:null,page:R.page,pageCount:R.pageCount,size:R.size,onSizeChange:R.setSize,onPageChange:R.goto}),f?s.jsx(J9,{target:f.key,providers:K9(e),modelsByProvider:Y9(e),onClose:()=>p(null),onSaved:(z,H)=>{H&&o(B=>new Set(B).add(H)),j(z)}}):null]})}let qM=!1;function GM(){if(qM)return;const e=document.querySelector(HM);e&&(qM=!0,e.replaceChildren(),jn.createRoot(e).render(s.jsx(nK,{})))}document.querySelector(HM)?GM():document.addEventListener("DOMContentLoaded",GM,{once:!0}),window.wbKeysPanel={load:eK,render:tK,visibleColumns:UM};const sK="login-app",rK=x.createElement("panel-captcha",{id:"captcha"});function aK(){return s.jsxs("div",{className:"w-[min(400px,100%)] rounded-lg border border-border bg-card p-[36px_34px_30px] shadow-3",children:[s.jsxs("div",{className:"mb-[26px] flex flex-col items-center gap-3 text-center",children:[s.jsxs("svg",{viewBox:"0 0 24 24",role:"img","aria-label":"Agent2API",className:"size-[52px] rounded-[12px] shadow-[0_6px_18px_rgba(0,122,255,0.35)]",children:[s.jsx("rect",{width:"24",height:"24",rx:"5.4",fill:"#007AFF"}),s.jsxs("g",{fill:"none",stroke:"#fff",strokeWidth:"2.2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("path",{d:"M5.23 9.24h10.71"}),s.jsx("path",{d:"M15.94 6.72 18.77 9.24 15.94 11.76"}),s.jsx("path",{d:"M18.77 14.76H8.06"}),s.jsx("path",{d:"M8.06 12.24 5.23 14.76 8.06 17.28"})]})]}),s.jsxs("div",{children:[s.jsx("h1",{className:"m-0 text-[21px] font-bold tracking-[0.2px]",children:"Agent2API"}),s.jsx("div",{className:"text-[12.5px] text-muted-foreground",children:r("OpenAI 兼容网关 · 管理面板")})]})]}),s.jsx("div",{id:"mode-tip",className:"mb-[18px] text-center text-[13px] text-muted-foreground"}),s.jsxs("form",{id:"form",autoComplete:"on",children:[s.jsx(Je,{htmlFor:"username",className:"mt-3.5 mb-[5px] text-[12.5px]",children:r("账号")}),s.jsx(Fe,{id:"username",autoComplete:"username",placeholder:r("管理员账号")}),s.jsx(Je,{htmlFor:"password",className:"mt-3.5 mb-[5px] text-[12.5px]",children:r("密码")}),s.jsx(Fe,{id:"password",type:"password",autoComplete:"new-password",placeholder:r("至少 8 位")}),rK,s.jsx(ne,{id:"submit",type:"submit",size:"lg",className:"w-full",children:r("继续")}),s.jsx("div",{id:"error",className:"mt-3 min-h-5 text-center text-[13px] whitespace-pre-wrap text-destructive"})]}),s.jsx("div",{className:"mt-[22px] text-center text-[12px] text-muted-foreground",children:r("登录后可在「网关 Key」页为 API 客户端创建密钥")})]})}function lK(){const e=document.getElementById(sK);e&&Ts.flushSync(()=>{jn.createRoot(e).render(s.jsx(aK,{}))})}lK();function Hr(){return window}function Cw(e){return e instanceof Error?e.message:String(e)}function $g(e,t){Hr().wbApp?.toast?.(e,t)}const KM=1e3,oK=fg,iK=500,YM="workbuddy-desktop-logs-range",Tw="workbuddy-desktop-logs-filters",cK=300,uK="~/.agent2api/logs.jsonl",Ew=["today","7","30","month","all"],Aw="all",dK={today:r("今天"),7:r("7 天"),30:r("30 天"),month:r("本月"),all:r("全部")},fK=Ew.map(e=>({value:e,label:dK[e]})),Rw=[{value:"",label:r("全部级别")},{value:"debug",label:r("debug 及以上")},{value:"info",label:r("info 及以上")},{value:"warn",label:r("warn 及以上")},{value:"error",label:r("仅 error")}],pK=r("全部级别"),XM=r("全部分类"),mK={debug:r("调试"),info:r("信息"),warn:r("警告"),error:r("错误")};let qg=KM,Gg=!0,Ow=!1,Mw=!1,Dw=!1,Iw=null,Po=null,Pw=null,Kg=null,_w=!1,Lw=!1;const zw={level:"",category:"",keyword:""};function hK(){const e=Hr().wbFilterMemory;if(!e)return{...zw};_w=!0;const t=e.load(Tw,{...zw});return{level:Rw.some(a=>a.value===t.level)?t.level:"",category:t.category,keyword:t.keyword}}function Yg(e){Hr().wbFilterMemory?.save(Tw,{level:e.level||"",category:e.category||"",keyword:e.keyword.trim()||""})}function gK(){try{const e=localStorage.getItem(YM);return e!==null&&Ew.includes(e)?e:Aw}catch{return Aw}}function xK(e){try{localStorage.setItem(YM,e)}catch{}}function Xg(e){return new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime()}function bK(e){const t=new Date;switch(e){case"today":return Xg(t);case"7":return Xg(new Date(t.getFullYear(),t.getMonth(),t.getDate()-6));case"30":return Xg(new Date(t.getFullYear(),t.getMonth(),t.getDate()-29));case"month":return Xg(new Date(t.getFullYear(),t.getMonth(),1));default:return null}}function vK(e){if(!e)return"—";const t=new Date(e),a=d=>String(d).padStart(2,"0"),o=`${a(t.getHours())}:${a(t.getMinutes())}:${a(t.getSeconds())}`,c=`${a(t.getMonth()+1)}-${a(t.getDate())}`;return t.getFullYear()===new Date().getFullYear()?`${c} ${o}`:`${t.getFullYear()}-${c} ${o}`}function yK(e){const t=e.data;if(!t)return"";const a=[];return(t.from||t.to)&&a.push([t.from,t.to].filter(Boolean).join(" → ")),t.model&&a.push(r("模型 {model}",{model:t.model})),t.resetAtText?a.push(r("{time} 恢复",{time:t.resetAtText})):t.status&&!t.from&&!t.to&&a.push(`HTTP ${t.status}`),a.length?a.join(" · "):""}function Bw(e){return Array.isArray(e?.entries)?e.entries:[]}function Qg(e,t,a){const o=Math.max(1,a==="all"?1:Math.ceil(t.length/a));return Math.min(Math.max(1,e),o)}function Hw(e,t){return Object.hasOwn(e,t)}function jK(e){const t={};if(e&&typeof e=="object")for(const[a,o]of Object.entries(e))typeof o=="string"&&(t[a]=o);return t}function QM(e,t){const a=new URLSearchParams,o=e.keyword.trim(),c=bK(t);return e.level&&a.set("level",e.level),e.category&&a.set("category",e.category),o&&a.set("keyword",o),c!==null&&a.set("start",String(c)),a}function wK(e,t){const a=QM(e,t);return a.set("limit",String(iK)),a.toString()}function SK(e,t){return QM(e,t).toString()}function NK(e){Iw=e||null,Hr().wbApp?.updateLogsBadge?.(Iw)}async function kK(){if(!Ow)try{const e=await Hr().workbuddyDesktop?.getScheduledTasks(),t=(e?.tasks||[]).find(a=>a.id==="logsAutoRefresh");Uw(t||null),Ow=Array.isArray(e?.tasks)}catch(e){console.warn("读取日志自动刷新间隔失败,按默认 10 秒:",Cw(e)),Uw(null)}}function Uw(e){Ow=!0;const t=Number(e?.interval);!!e&&typeof e=="object"&&Number.isFinite(t)&&t>0&&(e.unit==="seconds"||e.unit==="minutes")?(Gg=e.enabled!==!1,qg=e.unit==="minutes"?t*6e4:t*1e3):(Gg=!0,qg=KM),Po?.applyAutoConfig(qg,Gg)}async function CK(e={}){await Po?.load(e)}function TK(e){if(!Po){Kg={data:e??null};return}Po.render(e)}function EK(){return Iw}async function AK(e){if(!Po){Pw=e;return}await Po.showCategory(e)}const RK={entries:[],total:0,matched:0,file:uK,categories:{},loaded:!1};function Vw(e,t){return{entries:Bw(e),total:Number(e?.total)||0,matched:Number(e?.matched)||0,file:e?.file||t.file,categories:Object.keys(t.categories).length?t.categories:jK(e?.categories),loaded:!0}}function OK(e){return e.loaded?e.total?r("没有符合筛选条件的日志"):r("暂无日志"):r("正在加载日志…")}function MK(){const[e,t]=x.useState(RK),[a,o]=x.useState(hK),[c,d]=x.useState(gK),[f,p]=x.useState(1),[h,b]=x.useState(()=>pg("logs")),[y,v]=x.useState(null),[j,w]=x.useState(()=>({ms:qg,enabled:Gg})),S=x.useRef(null),N=x.useRef(null),k=x.useRef(null),A=x.useRef(null),P=x.useRef(a),R=x.useRef(c),C=x.useRef(f),E=x.useRef(h),D=x.useRef(e),I=x.useCallback(le=>{const de={...P.current,...le};return P.current=de,o(de),de},[]),O=x.useCallback(le=>{R.current=le,d(le)},[]),_=x.useCallback(le=>{C.current=le,p(le)},[]),z=x.useCallback(le=>{E.current=le,b(le),mg("logs",le)},[]),H=x.useCallback(le=>{D.current=le,t(le)},[]),B=x.useCallback(()=>{if(_w)return;const le=Hr().wbFilterMemory;if(!le||(_w=!0,Lw))return;const de=le.load(Tw,{...zw});I({level:de.level,category:de.category,keyword:de.keyword})},[I]),L=x.useCallback(async(le={})=>{const{silent:de=!1,resetPage:Se=!1}=le;Se&&_(1),kK(),B();const Te=wK(P.current,R.current),_e=C.current;try{const Ae=Hr().workbuddyDesktop;if(!Ae)throw new Error(r("后端桥不可用"));const[oe,me]=await Promise.all([Ae.getLogs(Te),Ae.getLogStats()]),pe=Se?0:S.current?.scrollTop??0,Ce=Vw(oe,D.current);H(Ce),_(Qg(C.current,Bw(oe),E.current)),N.current=C.current===_e?pe:0,NK(me),Object.keys(Ce.categories).length&&P.current.category&&!Hw(Ce.categories,P.current.category)&&(Yg(I({category:""})),L({resetPage:!0}))}catch(Ae){de||(console.warn("读取运行日志失败:",Cw(Ae)),H(Vw(null,D.current)),_(Qg(C.current,[],E.current)))}},[H,_,B]),F=x.useCallback(le=>{le!==void 0&&(H(Vw(le,D.current)),_(Qg(C.current,Bw(le),E.current)))},[H,_]),$=x.useCallback(async le=>{if(Hw(D.current.categories,le)||await L({silent:!0}),!Hw(D.current.categories,le))return;const de=I({category:le});Yg(de),_(1),await L({resetPage:!0}),Hr().wbApp?.showPage?.("logs",{persist:!0})},[I,_,L]);x.useEffect(()=>{if(Po={load:L,render:F,showCategory:$,applyAutoConfig:(de,Se)=>w({ms:de,enabled:Se})},Kg){const de=Kg;Kg=null,F(de.data)}const le=Pw;return le!==null?(Pw=null,$(le)):L({silent:!0}),()=>{Po=null,k.current!==null&&window.clearTimeout(k.current),k.current=null}},[L,F,$]),x.useEffect(()=>{if(!j.enabled||j.ms<=0)return;const le=window.setInterval(()=>{document.hidden||Hr().wbApp?.currentPage!=="logs"||Mw||(Mw=!0,L({silent:!0}).finally(()=>{Mw=!1}))},j.ms);return()=>{window.clearInterval(le)}},[j,L]),x.useLayoutEffect(()=>{const le=N.current;if(le===null)return;N.current=null;const de=S.current;de&&(de.scrollTop=le)});async function X(le,de,Se){if(!Dw){Dw=!0,v({which:le,label:de});try{await Se()}catch(Te){$g(r("操作失败:{error}",{error:Cw(Te)}),"err")}finally{Dw=!1,v(null)}}}async function K(){const le=SK(P.current,R.current),de=le.length>0,Se=de?r("确定清空当前筛选出的 {count} 条日志?清空后无法恢复。",{count:Number(D.current.matched)||0}):r("确定清空全部运行日志?清空后无法恢复。"),Te=Hr().wbConfirm?.ask;Te&&await Te({title:r("清空运行日志"),html:Se,okText:r("清空"),okClass:"danger"})&&await X("clear",r("清空中…"),async()=>{const _e=Hr().workbuddyDesktop;if(!_e)throw new Error(r("后端桥不可用"));await _e.clearLogs(de?le:"all=1"),await L({resetPage:!0}),$g(r("运行日志已清空"))})}async function q(){await X("export",r("导出中…"),async()=>{const le=Hr().workbuddyDesktop;if(!le)throw new Error(r("后端桥不可用"));const de=await le.exportLogs();if(!(!de||de.canceled)){if(de.count===0){$g(r("暂无日志可导出"),"err");return}$g(r("✅ 已导出 {count} 条日志到 {file}",{count:String(de.count),file:String(de.file)}))}})}function Q(le){const de=Qg(le,D.current.entries,E.current);de!==C.current&&(_(de),N.current=0)}function G(le){if(le===E.current)return;const de=D.current.entries.length,Se=de?(C.current-1)*(E.current==="all"?de:E.current):0;z(le),_(le==="all"?1:Math.floor(Se/le)+1),N.current=0}function J(le){Lw=!0,Yg(I(le)),L({resetPage:!0})}function Y(le){Lw=!0,I({keyword:le}),k.current!==null&&window.clearTimeout(k.current),k.current=window.setTimeout(()=>{k.current=null,Yg(P.current),L({resetPage:!0})},cK)}function re(le){const de=Ew.includes(le)?le:Aw;de!==R.current&&(O(de),xK(de),L({resetPage:!0}))}const M=e.entries,U=h!=="all",Z=Math.max(1,U?Math.ceil(M.length/h):1),ae=Math.min(Math.max(1,f),Z),ce=U?M.slice((ae-1)*h,ae*h):M,W=M.length?U?(ae-1)*h+1:1:0,ue=M.length?U?W+ce.length-1:M.length:0,ve=e.categories,ee=e.matched===e.total?r("{n} 条",{n:e.total}):r("{matched} / {total} 条",{matched:e.matched,total:e.total});x.useEffect(()=>{const le=e.loaded?ee:"—";A.current!==le&&(A.current=le,Hr().wbApp?.renderTopbarStatus?.())},[e.loaded,ee]);function he(le,de){const Se=le.level||"",Te=yK(le);return s.jsxs("div",{className:`log-row ${Se}`,children:[s.jsx("span",{className:"log-rail",children:s.jsx("span",{className:`log-dot ${Se}`})}),s.jsxs("div",{className:"log-main",children:[s.jsxs("div",{className:"log-line",children:[s.jsx("span",{className:"log-cat",children:ve[le.category||""]||le.category}),s.jsx("span",{className:`log-lvl ${Se}`,children:mK[Se]||le.level}),s.jsx("span",{className:"msg",children:le.message})]}),Te?s.jsx("div",{className:"log-extra-wrap",children:s.jsx("span",{className:"extra",children:Te})}):null]}),s.jsx("span",{className:"time",children:vK(le.ts)})]},le.id??de)}const te=y?.which==="export",ke=y?.which==="clear";return s.jsxs("section",{className:"panel",id:"logs-panel-events",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("系统事件")}),s.jsx(ze,{id:"logs-badge",variant:"outline","data-tone":"",children:e.loaded?ee:"—"}),s.jsxs("div",{className:"head-actions",children:[s.jsx(ne,{id:"btn-logs-export",variant:"outline",disabled:y!==null,onClick:()=>{q()},children:te?y?.label:r("导出")}),s.jsx(ne,{id:"btn-logs-clear",variant:"destructive",disabled:y!==null,onClick:()=>{K()},children:ke?y?.label:r("清空")})]})]}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"log-filters",children:[s.jsx(En,{options:fK,value:c,onValueChange:re,"aria-label":r("事件日志时间范围"),className:"shrink-0"}),s.jsxs(ut,{value:a.level,onValueChange:le=>J({level:String(le??"")}),children:[s.jsx(ft,{id:"logs-level",className:"min-w-[120px]",title:r("按最低级别筛选"),"aria-label":r("按最低级别筛选"),children:s.jsx(dt,{children:Rw.find(le=>le.value===a.level)?.label??pK})}),s.jsx(pt,{children:Rw.map(le=>s.jsx($e,{value:le.value,children:le.label},le.value))})]}),s.jsxs(ut,{value:a.category,onValueChange:le=>J({category:String(le??"")}),children:[s.jsx(ft,{id:"logs-category",className:"min-w-[120px]",title:r("按分类筛选"),"aria-label":r("按分类筛选"),children:s.jsx(dt,{children:a.category?ve[a.category]||a.category:XM})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:XM}),Object.entries(ve).map(([le,de])=>s.jsx($e,{value:le,children:de},le))]})]}),s.jsxs(Di,{className:"w-auto flex-auto",children:[s.jsx(Pi,{id:"logs-keyword",type:"search",placeholder:r("搜索消息关键词…"),autoComplete:"off","aria-label":r("搜索消息关键词"),value:a.keyword,onChange:le=>Y(le.currentTarget.value)}),s.jsx(Ii,{"aria-hidden":"true",children:"⌕"})]})]}),s.jsx("div",{className:"log-list",id:"log-list",ref:S,children:ce.length?ce.map((le,de)=>he(le,de)):s.jsx("div",{className:"log-empty",children:OK(e)})})]}),s.jsx($i,{total:M.length,range:U?{start:W,end:ue}:null,page:ae,pageCount:Z,size:h,sizes:oK,onSizeChange:G,onPageChange:Q})]})}const ZM='.page[data-page="logs"]';let WM=!1;function JM(){if(WM)return;const e=document.querySelector(ZM);e&&(WM=!0,e.replaceChildren(),jn.createRoot(e).render(s.jsx(MK,{})))}document.querySelector(ZM)?JM():document.addEventListener("DOMContentLoaded",JM,{once:!0}),window.wbLogsPanel={load:CK,render:TK,lastStats:EK,applyAutoRefresh:Uw,showCategory:AK};const zu=["maxInputTokens","maxOutputTokens","supportsToolCall","supportsImages","supportsVideo","supportsReasoning"],np=["maxInputTokens","maxOutputTokens"],e2=["supportsToolCall","supportsImages","supportsVideo","supportsReasoning"],Bu={maxInputTokens:r("上下文窗口"),maxOutputTokens:r("最大输出 Token"),supportsToolCall:r("工具调用"),supportsImages:r("图片识别"),supportsVideo:r("视频识别"),supportsReasoning:r("支持思考")};r("工具"),r("图片"),r("视频"),r("思考");const DK=1e8;function Fw(e){return np.includes(e)}function $w(e,t){if(Fw(e)){const a=typeof t=="number"?t:Number(t);return!Number.isFinite(a)||a<1||a>DK?null:Math.trunc(a)}return typeof t=="boolean"?t:null}function t2(e){const t={};if(!e||typeof e!="object")return t;const a=e;for(const o of zu){const c=$w(o,a[o]);c!==null&&(t[o]=c)}return t}function qw(e){return Array.isArray(e)?zu.filter(t=>e.some(a=>a===t)):[]}function Gw(e){return t2(e?.capabilities)}function n2(e){const t=Number(e);if(!Number.isFinite(t)||t<=0)return"—";const a=o=>o.toFixed(1).replace(/\.0$/,"");return t>=1e6?`${a(t/1e6)}M`:t>=1e3?`${a(t/1e3)}K`:String(Math.round(t))}function sp(e){const t=Number(e);return!Number.isFinite(t)||t<=0?r("未声明"):Math.round(t).toLocaleString("en-US")}function Kw(e){return e===!0?"on":e===!1?"off":"unset"}function IK(e){const t=e?.reasoningLevels;if(!Array.isArray(t))return null;const a=t.filter(c=>typeof c=="string"&&c.trim()!=="");if(!a.length)return null;const o=e?.reasoningDefaultLevel;return{levels:a,defaultLevel:typeof o=="string"?o.trim():""}}function PK(e,t,a,o=""){const c=Bu[e],d=a?r("(已被手动覆盖,弹窗里可恢复继承)"):"",f=_K(o);if(Fw(e))return t==null?r("{label}:上游未声明{suffix}",{label:c,suffix:d}):r("{label}:{value}(对下游声明的精确值){origin}{suffix}",{label:c,value:sp(t),origin:f,suffix:d});const p=Kw(t);return p==="on"?r("{label}:支持{origin}{suffix}",{label:c,origin:f,suffix:d}):p==="off"?r("{label}:不支持{origin}{suffix}",{label:c,origin:f,suffix:d}):r("{label}:上游未声明(下游会按不支持处理){suffix}",{label:c,suffix:d})}function Hu(e,t){const a=e?.capSources?.[t];return a==="upstream"||a==="modelsdev"||a==="user"?a:""}function _K(e){return e==="modelsdev"?r("(由 models.dev 补齐)"):e==="user"?r("(手动设定)"):""}function LK(e){const t={};for(const a of zu){const o=e[a];Fw(a)?t[a]=$w(a,o.trim()):t[a]=o==="on"?!0:o==="off"?!1:null}return t}function rp(){return window}const s2=e=>String(e??"").trim().toLowerCase(),Ks=(e,t)=>s2(e)===s2(t);function as(e){return typeof e=="string"&&e.startsWith("custom-")}function Wi(){return(rp().wbProviders?.customList?.()||[]).filter(e=>as(e?.id))}function r2(){return rp().wbProviders?.customList?.()||[]}function _o(e){return Wi().find(t=>t.id===e)||null}function zK(e){const t=t2(e),a={};for(const o of zu){const c=t[o];(typeof c=="number"||typeof c=="boolean")&&(a[o]=c)}return Object.keys(a).length?a:void 0}function a2(e){const t=(Array.isArray(e?.models)?e.models:[]).map(c=>{const d={id:String(c?.id??"").trim(),enabled:c?.enabled!==!1,reasoning:typeof c?.reasoning=="string"?c.reasoning:""};return d.capabilities=zK(c?.capabilities),d}).filter(c=>c.id),a=(Array.isArray(e?.mappings)?e.mappings:[]).map(c=>({alias:String(c?.alias??"").trim(),target:String(c?.target??"").trim(),enabled:c?.enabled!==!1,reasoning:typeof c?.reasoning=="string"?c.reasoning:""})).filter(c=>c.alias&&c.target);for(const c of t){const d=a.find(f=>Ks(f.alias,c.id)&&Ks(f.target,c.id));d&&(c.enabled=c.enabled&&d.enabled,c.reasoning=d.reasoning||c.reasoning)}const o=a.filter(c=>!(Ks(c.alias,c.target)&&t.some(d=>Ks(d.id,c.target))));return{models:t,mappings:o}}function l2(e){const t=_o(e);if(!t)return null;const a=a2(t),o=t.name||e,c=[],d=[];for(const f of a.models)c.push({id:f.id,name:"",provider:e,providerLabel:o,source:"",enabled:f.enabled,aliases:[],capabilities:f.capabilities||{},capOverrides:[]}),d.push({alias:f.id,target:f.id,provider:e,enabled:f.enabled,reasoning:f.reasoning,isDefault:!0,dangling:!1,carried:!0});for(const f of a.mappings){const p=c.find(h=>Ks(h.id,f.target));d.push({alias:f.alias,target:f.target,provider:e,enabled:f.enabled,reasoning:f.reasoning,isDefault:!1,dangling:!p,carried:!!p}),p&&p.aliases.push(f.alias)}return{models:c,mappings:d}}async function Ji(e,t){const a=rp().wbProviders;if(!a?.customRequest)throw new Error(r("目录模块未就绪"));const o=_o(e);if(!o)throw new Error(r("该自定义提供商已不存在(可能已被删除),请刷新后重试"));const c=a2(o),d=t(c);return await a.customRequest("POST","/api/custom-providers/models",{providerId:e,models:c.models,mappings:c.mappings}),await a.refreshCustom?.(),d}async function o2(e,t,a,o={}){const{enabled:c,reasoning:d}=o;await Ji(e,f=>{if(Ks(t,a)){const h=f.models.find(b=>Ks(b.id,a));if(!h)throw new Error(r("该提供商的清单里没有模型「{name}」",{name:a}));return c!==void 0&&(h.enabled=!!c),d!==void 0&&(h.reasoning=String(d??"")),null}const p=f.mappings.find(h=>Ks(h.alias,t)&&Ks(h.target,a));if(p)return c!==void 0&&(p.enabled=!!c),d!==void 0&&(p.reasoning=String(d??"")),null;if(c===!1)throw new Error(r("映射「{alias} → {target}」不存在",{alias:t,target:a}));return f.mappings.push({alias:t,target:a,enabled:c??!0,reasoning:String(d??"")}),null})}async function i2(e,t,a){await Ji(e,o=>{const c=o.mappings.length;if(o.mappings=o.mappings.filter(d=>!(Ks(d.alias,t)&&Ks(d.target,a))),o.mappings.length===c)throw new Error(r("映射「{alias} → {target}」不存在",{alias:t,target:a}));return null})}async function c2(e,t){const a=String(t??"").trim();if(!a)throw new Error(r("请填写模型 ID"));await Ji(e,o=>{if(o.models.some(c=>Ks(c.id,a)))throw new Error(r("模型「{name}」已存在(忽略大小写判重)",{name:a}));return o.models.push({id:a,enabled:!0,reasoning:""}),null})}async function BK(e,t){const a=(Array.isArray(t)?t:[]).map(o=>String(o??"").trim()).filter(Boolean);if(!a.length)throw new Error(r("没有选中任何模型"));return Ji(e,o=>{let c=0;for(const d of a)o.models.some(f=>Ks(f.id,d))||(o.models.push({id:d,enabled:!0,reasoning:""}),c++);return c})}async function u2(e,t,a){await Ji(e,o=>{const c=o.models.find(f=>Ks(f.id,t));if(!c)throw new Error(r("该提供商的清单里没有模型「{name}」",{name:t}));const d={...c.capabilities||{}};for(const[f,p]of Object.entries(a))p==null?delete d[f]:d[f]=p;return c.capabilities=Object.keys(d).length?d:void 0,null})}async function d2(e,t){return Ji(e,a=>{const o=a.mappings.length;a.mappings=a.mappings.filter(d=>!Ks(d.target,t));const c=o-a.mappings.length;return a.models=a.models.filter(d=>!Ks(d.id,t)),{removedMappings:c}})}async function HK(e){const t=rp().wbProviders;if(!t?.customRequest)throw new Error(r("目录模块未就绪"));const a=await t.customRequest("POST","/api/custom-providers/fetch-models",{providerId:e}),o=(Array.isArray(a?.models)?a.models:[]).map(d=>String(d??"").trim()).filter(Boolean),c=await Ji(e,d=>{let f=0;for(const p of o)d.models.some(h=>Ks(h.id,p))||(d.models.push({id:p,enabled:!0,reasoning:""}),f++);return f});return{total:o.length,added:Number(c)||0}}rp().wbModelsCustom={isCustom:as,list:Wi,listSource:r2,record:_o,buildView:l2,setBinding:o2,removeMapping:i2,addModel:c2,addModels:BK,removeModel:d2,setCapabilities:u2,fetchModels:HK};const fa="__custom__",UK=["off","none","minimal","low","medium","high","xhigh","max"];function ap(e,t){const a=f2(t);if(a.length)return a;const o=e?.reasoningLevels;return Array.isArray(o)&&o.length?o.filter(c=>typeof c=="string"):[...UK]}function f2(e){const t=e?.reasoningLevels;return Array.isArray(t)?t.filter(a=>typeof a=="string"&&a.trim()!==""):[]}function VK(e,t){const a=t.map(f2).filter(f=>f.length>0);if(!a.length||a.length!==t.length)return ap(e);const[o,...c]=a,d=o.filter(f=>c.every(p=>p.includes(f)));return d.length?d:ap(e)}function p2(e,t,a){return`${String(e??"").trim().toLowerCase()}${String(t??"").trim().toLowerCase()}${String(a??"").trim().toLowerCase()}`}function FK(e){const t=new Map;for(const a of Array.isArray(e)?e:[]){const o=typeof a?.reasoning=="string"?a.reasoning.trim():"";o&&t.set(p2(a.alias,a.target,a.provider),o)}return(a,o,c)=>t.get(p2(a,o,c))||""}function Wt(){return window}function Lo(e){return e instanceof Error?e.message:String(e)}function Nr(e,t){Wt().wbApp?.toast?.(e,t)}function Yw(e){const t=Wt().wbApp?.esc;return t?t(e):String(e??"").replace(/[&<>"']/g,a=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[a]||a)}function $K(e){const t=Wt().formatTime??Wt().wbApp?.formatTime;return String(t?.(e)??"")}const m2=e=>String(e??"").trim().toLowerCase(),zo=(e,t)=>m2(e)===m2(t),h2=8,g2="workbuddy-desktop-models-filters",Xw=["all","enabled","disabled","mapped"],qK={all:r("全部"),enabled:r("已启用"),disabled:r("已禁用"),mapped:r("有映射")},GK=Xw.map(e=>({value:e,label:qK[e]})),Qw=["default","rate-asc","rate-desc"],KK={default:r("默认"),"rate-asc":r("倍率升序"),"rate-desc":r("倍率降序")},YK=Qw.map(e=>({value:e,label:KK[e]})),Zw=[{key:"check",label:r("选择"),align:"center"},{key:"model",label:r("上游模型")},{key:"rate",label:r("倍率")},{key:"source",label:r("来源")},{key:"budget",label:r("上下文 / 输出")},{key:"caps",label:r("能力")},{key:"alias",label:r("模型映射")},{key:"act",label:r("操作"),align:"right"}],x2=new Set(["rate","source"]),lp="custom-all",Ww={models:[],mappings:[]},b2={provider:"all",state:"all",search:"",sort:"default"};function v2(){const e=Wt().wbFilterMemory;if(!e)return{...b2};const t=e.load(g2,{...b2});return{provider:String(t.provider||"all"),state:Xw.includes(t.state)?t.state:"all",search:String(t.search||""),sort:Qw.includes(t.sort)?t.sort:"default"}}const Zg=v2();let y2=!!Wt().wbFilterMemory,Wg=!1,ls={provider:Zg.provider,stateFilter:Zg.state,search:Zg.search,sort:Zg.sort,data:null,expanded:new Set,pending:new Set,mapping:null,customModel:null,capability:null};const Jw=new Set;function op(){return ls}function XK(e){return Jw.add(e),()=>{Jw.delete(e)}}function zs(e){const t={...ls,...e},a=ex(t),o=ex(ls)!==a;ls=a===t.provider?t:{...t,provider:a},ZK(),o&&tx();for(const c of Jw)c()}function Jg(e){Wt().wbFilterMemory?.save(g2,e)}function j2(){if(y2||Wg||!Wt().wbFilterMemory)return;y2=!0;const e=v2();(e.provider!==ls.provider||e.state!==ls.stateFilter||e.search!==ls.search||e.sort!==ls.sort)&&zs(e)}function ex(e){const t=e.provider;return t==="all"||t===lp||Wi().some(a=>a.id===t)||Array.isArray(e.data?.models)&&e.data.models.some(a=>(a.provider||"")===t)||!e.data||!e0()?t:"all"}function Uu(){return ex(ls)}function e0(){return!!Wt().wbProviders}function Vu(){return Uu()===lp}function w2(){return Vu()||as(Uu())}let ip=null;function t0(e){const t=_o(e);return(!ip||ip.id!==e||ip.record!==t)&&(ip={id:e,record:t,view:l2(e)||Ww}),ip.view}let cp=null;function QK(){const e=r2();if(cp&&cp.directory===e)return cp.view;const t=[],a=[];for(const o of Wi()){const c=t0(String(o.id||""));t.push(...c.models||[]),a.push(...c.mappings||[])}return cp={directory:e,view:t.length||a.length?{models:t,mappings:a}:Ww},cp.view}function n0(){const e=Uu();return as(e)?t0(e):e===lp?QK():ls.data||Ww}function up(){const e=n0();return Array.isArray(e.models)?e.models:[]}function S2(){const e=n0();return Array.isArray(e.mappings)?e.mappings:[]}let N2=(e,t,a)=>"";function ZK(){N2=FK(S2())}function s0(){const e=new Map,t=Array.isArray(ls.data?.models)?ls.data.models:[];for(const a of t){const o=a.provider||"";if(!o)continue;const c=e.get(o)||{label:a.providerLabel||o,n:0};c.n++,e.set(o,c)}return e}function WK(){const e=new Map;for(const t of Wi()){const a=String(t.id||"");if(!a)continue;const o=t0(a);e.set(a,{label:String(t.name||a),n:(o.models||[]).length})}return e}function JK(){return[...s0().keys()]}function eY(e){const a=(Array.isArray(ls.data?.models)?ls.data.models:[]).find(o=>o.provider===e);return Number(a?.refreshedAt)||0}function r0(){const e=new Map;for(const t of up()){const a=t.provider||"";a&&!e.has(a)&&e.set(a,r(t.providerLabel||a))}return[...e].map(([t,a])=>({id:t,label:a}))}function k2(e){return e?up().filter(t=>(t.provider||"")===e).sort((t,a)=>+(t.enabled===!1)-+(a.enabled===!1)).map(t=>({id:t.id,label:t.name&&t.name!==t.id?r("{id}({name})",{id:t.id,name:t.name}):t.id,off:t.enabled===!1})):[]}function tY(){const e=[],t=Wt().wbProviders?.all?.();if(Array.isArray(t))for(const a of t){const o=String(a?.id||"");o&&e.push({id:o,label:r(String(a?.label||o))})}for(const a of Wi()){const o=String(a.id||"");o&&e.push({id:o,label:String(a.name||o)})}return e.length?e:r0()}let dp=null,a0=null;function C2(){const e=dp?dp.apply(Zw):Zw;return w2()?e.filter(t=>!x2.has(t.key)):e}function tx(){Wt().wbColSettings?.syncStaticHead("models",a0,w2()?x2:null)}function nY(){if(dp)return;dp=Wt().wbColSettings?.register({id:"models",label:r("模型管理表"),columns:Zw,mount:()=>document.querySelector('.page[data-page="gateway"] .panel-head .head-actions'),buttonPlacement:"last",onChange:()=>{tx(),ec()}})||null,dp&&ec()}let nx=0,l0=!1;async function sx({force:e=!1}={}){if(l0&&!e)return;const t=++nx;l0=!0,j2();try{const a=await Wt().workbuddyDesktop?.getModelManage();if(t!==nx)return;zs({data:a&&Array.isArray(a.models)?a:null})}catch(a){if(t!==nx)return;ec(),Nr(r("读取模型清单失败:{message}",{message:Lo(a)}),"err")}finally{t===nx&&(l0=!1)}}async function T2(){try{await Wt().wbProviders?.refreshCustom?.()}catch{}await sx({force:!0})}function ec(){zs({})}function tc(e){const t=e;if(t&&Array.isArray(t.models)){zs({data:t});return}ec()}async function rx(e,t,a,o){if(as(e))return o2(e,t,a,o);const c=Wt().workbuddyDesktop;if(!c)throw new Error(r("后端桥不可用"));return c.addModelMapping(t,a,e,o.reasoning,o.enabled)}async function sY(e,t,a){if(as(e))return i2(e,t,a);const o=Wt().workbuddyDesktop;if(!o)throw new Error(r("后端桥不可用"));return o.removeModelMapping(t,a,e)}async function rY(e,t){if(as(e))return c2(e,t);const a=Wt().workbuddyDesktop;if(!a)throw new Error(r("后端桥不可用"));return a.addCustomModel(e,t)}async function E2(e,t){if(as(e))return d2(e,t);const a=Wt().workbuddyDesktop;if(!a)throw new Error(r("后端桥不可用"));return a.removeCustomModel(e,t)}async function aY(e,t,a){if(as(e))return u2(e,t,a);const o=Wt().workbuddyDesktop;if(!o)throw new Error(r("后端桥不可用"));return o.setModelCapabilities(e,t,a)}async function o0(e,t,a){if(!ls.pending.has(e)){zs({pending:new Set(ls.pending).add(e)});try{tc(await t()),a&&Nr(a)}catch(o){Nr(r("操作失败:{message}",{message:Lo(o)}),"err")}finally{const o=new Set(ls.pending);o.delete(e),zs({pending:o})}}}function fp(e){return Fu(e).some(t=>t.enabled!==!1)}function Fu(e){const t=e.provider||"",a=S2().filter(p=>zo(p.target,e.id)&&(!p.provider||zo(p.provider,t))),o=new Map;for(const p of a){const h=String(p.alias).toLowerCase();(!o.has(h)||p.provider)&&o.set(h,p)}const c=String(e.id).toLowerCase(),d=o.get(c);o.delete(c);const f=[{alias:e.id,target:e.id,provider:t,enabled:e.enabled!==!1&&d?.enabled!==!1,isDefault:!0}];return f.push(...o.values()),f}const nc=e=>`${e.provider||""}:${e.id}`,i0=(e,t,a)=>`${e}:${t}:${a||""}`;function lY(e){zs({expanded:new Set(ls.expanded).add(e)})}function oY(e){const t=new Set(ls.expanded);t.delete(e),zs({expanded:t})}function iY(e){const t=Xw.includes(e)?e:"all";t!==ls.stateFilter&&(Wg=!0,zs({stateFilter:t}),Jg({state:t}))}function cY(e){const t=Qw.includes(e)?e:"default";t!==ls.sort&&(Wg=!0,zs({sort:t}),Jg({sort:t}))}function uY(e){Wg=!0,zs({search:e}),Jg({search:e})}function pp(e){zs({provider:String(e??"").trim()||"all"}),Jg({provider:ls.provider}),tx()}async function dY(e){const t=Wt().wbCustomProvidersUi;if(!t?.remove){Nr(r("删除入口未就绪,请重试或重启应用"),"err");return}await t.remove(e)&&await sx({force:!0})}function fY(){Wt().wbAccountAddForms?.openNewCustomForm?.()}function A2(e){zs({mapping:e})}function pY(){zs({mapping:null})}function mY(){const e=Uu(),t=as(e);zs({customModel:{provider:t||e!=="all"&&e!==lp?e:"",locked:t}})}function hY(){zs({customModel:null})}function R2(e,t){zs({capability:{provider:e,id:t}})}function gY(){zs({capability:null})}function O2(e,t){return up().find(a=>(a.provider||"")===e&&zo(a.id,t))||null}function c0(e){if(as(e)){const t=_o(e)?.name;return typeof t=="string"&&t||e}return r(Wt().wbProviders?.labelOf?.(e)||e)}function M2(){const e=Uu(),t=as(e),a=t?_o(e)?.name||e:r("内置提供商");Wt().wbModelsFetchModal?.open({providerId:e===lp?"":e,custom:t,name:a,onDone:()=>ec(),onRefreshed:()=>{sx({force:!0})}})}window.wbModelsPanel={render:ec,load:sx,refreshAll:T2,refreshModels:M2,visibleColumns:C2,selectProvider:pp,builtinProviders:JK,providerRefreshedAt:eY};function u0(e,t,a){return N2(e,t,a)}function xY(e){a0=e}function bY(){const e=a0;if(!e)return;const t=o=>Object.keys(o).some(c=>c.startsWith("__reactFiber$")||c.startsWith("__reactInternalInstance$"));for(const o of[...e.children])o.tagName==="TBODY"?o.id!=="models"&&o.remove():o.tagName==="TR"&&!t(o)&&o.remove();const a=e.querySelector("tbody#models");if(a)for(const o of[...a.children])t(o)||o.remove()}const vY=e=>[{value:"inherit",label:e},{value:"on",label:r("支持")},{value:"off",label:r("不支持")}];function yY(e){const t=Kw(e);return r(t==="on"?"支持":t==="off"?"不支持":"未声明")}function D2(e,t,a,o,c=""){const d=t!=null,f=np.includes(e)?sp(t):yY(t);return a?o?r("已填写:对下游声明 {value}",{value:f}):r("已覆盖:对下游声明 {value}",{value:f}):o?r("未填写(下游按未声明处理)"):d?c==="modelsdev"?r("models.dev 补齐:对下游声明 {value}",{value:f}):r("继承上游:{value}",{value:f}):r("上游未声明(下游会按不支持处理)")}function jY(e,t,a){return a?zu.filter(o=>{const c=e[o];return c!=null}):t}function wY(e,t){const a={};for(const o of zu){const c=t.includes(o),d=e[o];np.includes(o)?a[o]=c&&typeof d=="number"?String(d):"":a[o]=c?d===!0?"on":"off":"inherit"}return a}function SY({context:e,onClose:t}){const a=as(e.provider),o=O2(e.provider,e.id),c=IK(o),d=Gw(o),f=jY(d,qw(o?.capOverrides),a),[p,h]=x.useState(()=>wY(d,f)),[b,y]=x.useState(""),[v,j]=x.useState(!1);if(!o)return s.jsx(Yt,{open:!0,onOpenChange:E=>{E||t()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("模型能力")})}),s.jsx(Lt,{children:s.jsx("p",{className:"text-sm leading-[1.7] text-subtle",children:r("这一行已不在当前清单里(模型被移除、或目录刷新后上游不再提供它)。关闭后刷新列表再试。")})}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:t,children:r("关闭")})]})]})});const w=o.id,S=o.providerLabel||c0(e.provider),N=r(a?"未声明":"继承上游"),k=r(a?"清除":"恢复继承");function A(E){const D=d[E];return P(E)?"":a?r("未填写"):typeof D=="number"?r("继承上游 {value}",{value:sp(D)}):r("上游未声明")}function P(E){return f.includes(E)}function R(E,D){h(I=>({...I,[E]:D}))}async function C(){if(!v){for(const E of np){const D=p[E].trim();if(D&&$w(E,D)===null){y(r("{label}要填 1 ~ 1 亿之间的整数(留空 = {inherit})",{label:Bu[E],inherit:N}));return}}j(!0),y(r("保存中…"));try{tc(await aY(e.provider,e.id,LK(p))),t(),Nr(r("✅ 已保存 {id} 的能力位({provider})",{id:w,provider:S}))}catch(E){y(r("保存失败:{message}",{message:Lo(E)})),j(!1)}}}return s.jsx(Yt,{open:!0,onOpenChange:(E,D)=>{if(!E){if(v){D.cancel();return}t()}},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("模型能力")})}),s.jsxs(Lt,{children:[s.jsxs("div",{className:"rounded-md border border-border bg-surface-inset px-3 py-2.5 font-mono text-[12px] text-subtle",children:[s.jsx("b",{className:"text-primary-fg",children:o.id}),r("({provider})",{provider:S})]}),np.map(E=>s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsxs("div",{className:"flex items-center gap-[7px]",children:[s.jsx(Je,{htmlFor:`cap-${E}`,children:Bu[E]}),P(E)?s.jsx(ne,{variant:"ghost",size:"2xs",disabled:v,title:r(a?"清除这一项(回到未声明)":"清除覆盖,回到上游声明值"),onClick:()=>R(E,""),children:k}):null]}),s.jsx(Fe,{id:`cap-${E}`,inputMode:"numeric",autoComplete:"off",spellCheck:!1,placeholder:A(E),disabled:v,value:p[E],onChange:D=>R(E,D.currentTarget.value),onKeyDown:D=>{D.key==="Enter"&&(D.preventDefault(),C())}}),s.jsx("p",{className:"text-xs leading-[1.6] text-subtle",children:D2(E,d[E]??null,P(E),a,Hu(o,E))})]},E)),e2.map(E=>s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{children:Bu[E]}),s.jsx(En,{options:vY(N),value:p[E],"aria-label":Bu[E],className:"self-start",onValueChange:D=>R(E,String(D))}),s.jsx("p",{className:"text-xs leading-[1.6] text-subtle",children:D2(E,d[E]??null,P(E),a,Hu(o,E))})]},E)),c?s.jsxs("div",{className:"rounded-md border border-border bg-surface-inset px-3 py-2.5 text-xs leading-[1.7] text-subtle",children:[s.jsxs("div",{className:"flex flex-wrap items-center gap-x-2 gap-y-1",children:[s.jsx("span",{children:r("可配思考档位")}),s.jsx("span",{className:"font-mono text-[12px] text-primary-fg",children:c.levels.join(" / ")}),c.defaultLevel?s.jsxs("span",{children:[r("(默认 "),s.jsx("span",{className:"font-mono text-[12px] text-primary-fg",children:c.defaultLevel}),r(")")]}):null]}),s.jsx("div",{className:"mt-1",children:r("这是模型自己能配的档位(来自上游目录),只读;要改某一档请到映射弹窗里选。")})]}):null,s.jsxs("p",{className:"text-xs leading-[1.65] text-subtle",children:[r("这几项是给"),s.jsx("b",{children:r("下游客户端")}),r("看的能力声明("),s.jsx("code",{children:"/v1/models"}),r(" 里的 "),s.jsx("code",{children:"max_input_tokens"})," / ",s.jsx("code",{children:"supports_*"}),r(" 与 "),s.jsx("code",{children:"input_modalities"}),r("),下游按它们决定发不发图片、按多大的窗口堆历史。"),s.jsx("b",{children:r("不影响网关的转发与路由")}),r("。上游清单给的值不准时,在这里改;"),r(a?"这家的清单是你自己登记的,不填的项下游按未声明处理。":"「恢复继承」会把这一项交还给上游清单的值。")]}),s.jsx("div",{className:"min-h-[18px] text-xs text-subtle",children:b})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",disabled:v,onClick:t,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:v,onClick:()=>{C()},children:r("保存")})]})]})})}function mp(){return window}const NY="你好",I2=4e3,kY=[{value:"chat_completions",label:"OpenAI - Chat Completions"},{value:"responses",label:"OpenAI - Responses"},{value:"anthropic",label:"Anthropic - Messages"}],CY={chat_completions:"Chat Completions",responses:"Responses",anthropic:"Anthropic Messages"},TY="direct",EY={remote:r("远程目录"),builtin:r("内置清单"),manual:r("手动登记")},AY=[{test:e=>e===429,hint:r("该账号对这个模型正在限额冷却,等一会儿自己会好;也可以先测别的账号。")},{test:e=>e===401||e===403,hint:r("上游拒绝了这条登录态:重新登录一次,或把这条账号删掉。")},{test:e=>e===404,hint:r("上游不认识这个模型名:先点「获取模型」刷新清单,或核对默认绑定指向的上游模型。")},{test:e=>e===504,hint:r("上游没在预算内给完回答。可以只勾一个账号再测一次,看是普遍慢还是某一个账号慢。")},{test:e=>e>=500,hint:r("多半是上游自己的问题,过一会儿再试。同一个错误出现在所有账号上时,先查模型目录与映射。")}];function P2(){return mp().wbApp?.getState?.()?.accounts?.accounts||[]}function _2(e){return P2().filter(t=>(t.provider||pu)===e&&t.available!==!1&&t.enabled!==!1).sort(Wh)}function L2(e){const t=String(e.email||"").trim();return t&&cr(e.provider).emailAsName?t:hu(e)||t||r("未命名账号")}function RY(){const e=globalThis.crypto?.randomUUID?.();if(e)return e;const t=a=>Array.from({length:a},()=>Math.floor(Math.random()*16).toString(16)).join("");return`${t(8)}-${t(4)}-4${t(3)}-a${t(3)}-${t(12)}`}function d0(e){const t=Number(e);return!Number.isFinite(t)||t<=0?"—":t<1e3?`${Math.round(t)} ms`:`${(t/1e3).toFixed(t<1e4?2:1)} s`}function OY(e,t){return _2(e).length?"":r("该提供商没有可用账号(要在账号页启用一个、且凭证完整),一行都发不出去")}function z2({children:e,plain:t=!1}){return s.jsxs("p",{className:t?"flex items-center gap-2 text-xs leading-[1.6] text-subtle":"flex gap-2 rounded-md border border-border bg-surface-2 px-3 py-2.5 text-xs leading-[1.65] text-subtle",children:[s.jsx("span",{"aria-hidden":"true",className:t?"size-4 flex-none rounded-full border border-border-strong text-center text-[10px] leading-[14px]":"mt-px size-4 flex-none rounded-full border border-border-strong text-center text-[10px] leading-[14px]",children:"i"}),s.jsx("span",{children:e})]})}function MY({open:e,label:t,text:a,onToggle:o}){return s.jsxs("div",{className:"overflow-hidden rounded-sm border border-border",children:[s.jsxs(ne,{variant:"ghost",size:"xs",className:"w-full","aria-expanded":e,onClick:o,children:[s.jsx("span",{className:"truncate",children:t}),s.jsx("span",{"aria-hidden":"true",className:"ml-auto text-[10px]",children:e?"▴":"▾"})]}),e?s.jsx("div",{className:"border-t border-hairline bg-surface px-2.5 py-2",children:s.jsx("pre",{className:"whitespace-pre-wrap font-mono text-[11.5px] leading-[1.65] text-subtle",children:a})}):null]})}function DY(e){const[t,a]=x.useState(()=>Date.now());return x.useEffect(()=>{if(!e)return;a(Date.now());const o=window.setInterval(()=>a(Date.now()),400);return()=>window.clearInterval(o)},[e]),t}function B2(e){if(e.transport||!e.result||e.result.error)return!1;const t=Number(e.result.status)||0;return t>=200&&t<300}function IY({target:e,onClose:t}){const a=e.provider,o=O2(a,e.id),c=_2(a),d=vj(P2()),[f,p]=x.useState(""),[h,b]=x.useState(NY),[y,v]=x.useState("chat_completions"),[j,w]=x.useState(""),[S,N]=x.useState(null),[k,A]=x.useState(""),[P,R]=x.useState(""),[C,E]=x.useState(!0),[D,I]=x.useState(()=>{const te=c.find(ke=>!Cf(ke,e.id))||c[0];return te?[te.id]:[]}),[O,_]=x.useState([]),[z,H]=x.useState(()=>new Set),B=x.useRef(0),L=x.useRef([]);L.current=O;const F=O.some(te=>te.state==="running"),$=DY(F);x.useEffect(()=>{let te=!0;return(async()=>{try{const ke=mp().workbuddyDesktop?.getProxyPool;if(typeof ke!="function")throw new Error(r("当前环境不支持代理池"));const le=await ke();if(!te)return;if(!le||typeof le!="object"||!Array.isArray(le.items))throw new Error(r("代理池响应异常"));N(le.items),A("")}catch(ke){if(!te)return;N([]),A(Lo(ke))}})(),()=>{te=!1}},[]);const X=x.useCallback((te=!1)=>{const ke=L.current.filter(le=>le.state==="running");B.current+=1;for(const le of ke)mp().workbuddyDesktop?.terminateStatsRequest?.(le.testId)?.catch(()=>{});_([]),ke.length&&!te&&Nr(r("已中止在途测试({n} 个账号)",{n:ke.length}))},[]);x.useEffect(()=>()=>{X(!0)},[X]);async function K(te,ke){const le={provider:a,model:e.id,account_id:te.accountId,prompt:h.trim(),system_prompt:f.trim(),reasoning:P,stream:C,protocol:y,...j?{proxy:j}:{},test_id:te.testId},de=Se=>{B.current===ke&&_(Te=>Te.map(_e=>_e.testId===te.testId?{..._e,...Se}:_e))};try{const Se=await mp().workbuddyDesktop?.testModel(le);if(!Se){de({state:"done",transport:r("桥接调用返回空(本机网关没有响应这次测试)")});return}de({state:"done",result:Se})}catch(Se){de({state:"done",transport:Lo(Se)})}}function q(){const te=c.filter(Se=>D.includes(Se.id));if(!te.length){Nr(r("至少选一个测试账号"),"err");return}const ke=B.current+1;B.current=ke;const le=Date.now(),de=te.map(Se=>({accountId:Se.id,label:L2(Se),position:d.get(Se.id)?.position||0,testId:RY(),startedAt:le,state:"running"}));H(new Set),_(de);for(const Se of de)K(Se,ke)}function Q(){F?X():_([])}const G=s.jsx("span",{className:"ml-2 font-mono text-[12px] font-normal text-subtle",children:e.id});if(!o)return s.jsx(Yt,{open:!0,onOpenChange:te=>{te||t()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("测试模型")})}),s.jsx(Lt,{children:s.jsx("p",{className:"text-sm leading-[1.7] text-subtle",children:r("这一行已不在当前清单里(模型被移除、或目录刷新后上游不再提供它)。关闭后刷新列表再试。")})}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:t,children:r("关闭")})]})]})});const J=Fu(o).find(te=>te.isDefault)?.enabled===!1,Y=u0(o.id,o.id,a),re=Fu(o).filter(te=>!te.isDefault).map(te=>te.alias),M=ap(op().data,o).filter(te=>te!=="off"&&te!=="none"),U=J?r("跟随映射(该行未启用,本次不注入)"):Y?r("跟随映射(当前 {level})",{level:Y}):r("跟随映射(未绑定等级)"),Z=[r("以模型本名直发"),re.length?r("另有 {n} 条别名映射({aliases}),别名不参与本次",{n:re.length,aliases:re.join(r("、"))}):"",J?r("该行当前未启用:测试照常按本名直发(「先测通、再决定要不要启用」正是这颗按钮的用法),但本行的思考等级绑定不参与本次,生产路由也要等绑定打开后才会放行"):Y?r("映射上绑定的思考等级是 {level},「跟随映射」按它注入",{level:Y}):r("映射上未绑定思考等级,「跟随映射」等于这次不注入")].filter(Boolean).join(r(";"))+r("。"),ae=mp().wbProviders?.PROTOCOL_OPTIONS||kY,ce=ae.find(te=>te.value===y)?.label||y,W=[{value:"",label:r("跟随账号配置")},{value:TY,label:r("直连(不使用代理)")},...(S??[]).map(te=>({value:`${Qa}${te.id}`,label:du(te)}))];S===null?W.push({value:"__hint_pool__",label:r("正在读取代理列表…"),disabled:!0}):S.length||W.push({value:"__hint_pool__",label:r(k?"代理列表读取失败":"还没有代理(去「网络代理」页添加)"),disabled:!0});const ue=W.find(te=>te.value===j)?.label||r("跟随账号配置"),ve=c.map(te=>({value:te.id,label:`#${d.get(te.id)?.position||0} ${L2(te)}`+(Cf(te,e.id)?r(" · 限额中"):"")}));function ee(te){const ke=(Ae,oe)=>s.jsxs("div",{className:"flex items-center gap-2.5 border-b border-hairline px-3 py-2",children:[Ae,s.jsx("b",{className:"min-w-0 truncate text-[12.5px] text-foreground",title:te.label,children:te.label}),te.position?s.jsxs("span",{className:"flex-none text-xs text-subtle",children:["#",te.position]}):null,s.jsx("span",{className:"ml-auto flex-none font-mono text-[11.5px] tabular-nums text-subtle",children:oe})]},"head");if(te.state==="running"){const Ae=($-te.startedAt)/1e3;return s.jsxs("div",{className:"flex items-center gap-2.5 rounded-md border border-border bg-surface-2 px-3 py-2.5 text-xs text-subtle",children:[s.jsx(of,{className:"flex-none"}),s.jsxs("span",{className:"min-w-0 truncate",children:[r("账号 "),s.jsx("b",{className:"text-foreground",children:te.label}),r(" 已发出,等待上游首帧…")]}),s.jsx("span",{className:"ml-auto flex-none font-mono tabular-nums",children:r("已等待 {seconds}s",{seconds:Ae.toFixed(1)})})]},te.testId)}const le=te.result;if(!B2(te)){const Ae=Number(le?.status)||0,oe=AY.find(me=>me.test(Ae))?.hint??(te.transport?r("确认桌面端还在运行,再重试一次。"):"");return s.jsxs("div",{className:"rounded-md border border-border bg-surface-2",children:[ke(s.jsx(ze,{shape:"tag",variant:"destructive",className:"flex-none",children:Ae||r("失败")}),r("用时 {time}",{time:d0(le?.duration_ms)})),s.jsxs("div",{className:"flex flex-col gap-1.5 px-3 py-2.5",children:[s.jsxs("div",{className:"text-[12.5px] leading-[1.7]",children:[te.transport?s.jsxs(s.Fragment,{children:[s.jsx("b",{className:"text-destructive",children:r("本机网关没有返回结论")}),r(":"),te.transport]}):s.jsxs(s.Fragment,{children:[s.jsx("b",{className:"text-destructive",children:r("测试未通过")}),r(":"),le?.error||r("上游没有给出可读的错误说明")]}),Number(le?.attempts)>1?r("(中间共尝试 {n} 次)",{n:Number(le?.attempts)}):""]}),oe?s.jsx("p",{className:"text-xs leading-[1.65] text-subtle",children:oe}):null]})]},te.testId)}const de=String(le?.reply??""),Se=String(le?.reasoning??""),Te=`think-${te.testId}`,_e=[le?.upstream_model&&le.upstream_model!==e.id?r("上游 {model}",{model:le.upstream_model}):"",le?.upstream_reasoning?r("思考等级 {level}",{level:le.upstream_reasoning}):"",le?.protocol&&le.protocol!=="chat_completions"?CY[le.protocol]||le.protocol:"",le?.proxy?r("节点 {proxy}",{proxy:le.proxy}):"",Number(le?.attempts)>1?r("尝试 {n} 次",{n:Number(le?.attempts)}):""].filter(Boolean).join(" · ");return s.jsxs("div",{className:"rounded-md border border-border bg-surface-2",children:[ke(s.jsx(ze,{shape:"tag",variant:"success",className:"flex-none",children:Number(le?.status)||200}),r("用时 {time} · 首字 {ttfb}",{time:d0(le?.duration_ms),ttfb:d0(le?.ttfb_ms)})),s.jsxs("div",{className:"flex flex-col gap-2 px-3 py-2.5",children:[s.jsxs("div",{className:"flex items-center gap-2 text-[11.5px] text-subtle",children:[s.jsx("span",{className:"flex-none",children:r("回复")}),_e?s.jsx("span",{className:"min-w-0 truncate",title:_e,children:_e}):null,s.jsx(ne,{variant:"ghost",size:"2xs",className:"ml-auto flex-none","data-copy":de,children:r("复制")})]}),s.jsx("div",{className:"max-h-[220px] overflow-y-auto whitespace-pre-wrap rounded-sm border border-border bg-surface px-3 py-2 text-[12.5px] leading-[1.7]",children:de||s.jsx("span",{className:"text-subtle",children:r("(上游这一次返回了空正文)")})}),Se?s.jsx(MY,{open:z.has(Te),label:r("思考过程({n} 字)",{n:Se.length}),text:Se,onToggle:()=>H(Ae=>{const oe=new Set(Ae);return oe.has(Te)?oe.delete(Te):oe.add(Te),oe})}):null]})]},te.testId)}function he(){if(F){const ke=O.length-O.filter(le=>le.state==="running").length;return s.jsxs(s.Fragment,{children:[O.length>1?s.jsxs("p",{className:"text-xs text-subtle",children:[r("正在测 "),s.jsx("b",{className:"text-foreground",children:O.length}),r(" 个账号"),ke?s.jsxs(s.Fragment,{children:[r(",已完成 "),s.jsx("b",{className:"text-foreground",children:ke})]}):null,r("。")]}):null,O.map(le=>ee(le))]})}const te=O.filter(B2).length;return s.jsxs(s.Fragment,{children:[O.length>1?s.jsxs("p",{className:"text-xs text-subtle",children:[s.jsx("b",{className:te?"text-success":"text-foreground",children:te}),r(" 成功 ·"),s.jsxs("b",{className:O.length-te?"text-destructive":"text-foreground",children:[" ",O.length-te]}),r(" 失败")]}):null,O.map(ke=>ee(ke)),te===0?s.jsxs(z2,{children:[r("两边原因不一样时各自处理。如果"),s.jsx("b",{className:"text-foreground",children:r("同一条错误出现在所有账号上")}),r(",通常就不是账号问题了:先确认这个模型还在上游目录里(「获取模型」刷一次),再看默认绑定指向的上游模型对不对。")]}):null]})}return s.jsx(Yt,{open:!0,onOpenChange:te=>{te||t()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsxs(Ut,{children:[r("测试模型"),G]})}),s.jsxs(Lt,{children:[s.jsxs("div",{className:"flex items-center gap-2 rounded-md border border-border bg-surface-inset px-3 py-2.5",children:[s.jsx("span",{className:"min-w-0 flex-1 truncate font-mono text-[12.5px] font-semibold text-primary-fg",title:o.id,children:o.id}),s.jsx(ze,{shape:"tag",variant:"brand",children:c0(a)}),s.jsx(ze,{shape:"tag",variant:"outline",children:as(a)?r("自定义家"):EY[o.source]||r("来源未知")}),s.jsxs(Ui,{children:[s.jsx(Vi,{render:s.jsx("span",{className:"tip-q",tabIndex:0,"aria-label":r("本次测试的发送口径")}),children:"?"}),s.jsx(Fi,{children:Z})]})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"model-test-system",children:r("系统提示词")}),s.jsx(Mi,{id:"model-test-system",rows:2,maxLength:I2,placeholder:r("留空则不携带;设置页的提示词模式照常生效"),value:f,onChange:te=>p(te.currentTarget.value)})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"model-test-prompt",children:r("用户提示词")}),s.jsx(Mi,{id:"model-test-prompt",rows:2,maxLength:I2,placeholder:r("留空用默认问候「你好」"),value:h,onChange:te=>b(te.currentTarget.value)})]}),s.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsxs("div",{className:"flex items-center gap-1.5",children:[s.jsx(Je,{htmlFor:"model-test-protocol",children:r("对话协议")}),s.jsxs(Ui,{children:[s.jsx(Vi,{render:s.jsx("span",{className:"tip-q",tabIndex:0,"aria-label":r("对话协议的说明")}),children:"?"}),s.jsx(Fi,{children:r("按所选网关对话协议组装请求并校验响应转换(Chat Completions / Responses / Anthropic Messages)。")})]})]}),s.jsxs(ut,{value:y,onValueChange:te=>v(String(te)),children:[s.jsx(ft,{id:"model-test-protocol",className:"w-full",children:s.jsx(dt,{className:"min-w-0 truncate",children:ce})}),s.jsx(pt,{children:ae.map(te=>s.jsx($e,{value:te.value,children:te.label},te.value))})]})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsxs("div",{className:"flex items-center gap-1.5",children:[s.jsx(Je,{htmlFor:"model-test-proxy",children:r("测试节点")}),s.jsxs(Ui,{children:[s.jsx(Vi,{render:s.jsx("span",{className:"tip-q",tabIndex:0,"aria-label":r("测试节点的说明")}),children:"?"}),s.jsx(Fi,{children:r("仅本次测试临时生效,不修改账号保存的代理配置。可选「网络代理」页里的节点或强制直连。")})]})]}),s.jsxs(ut,{value:j,onValueChange:te=>w(String(te)),children:[s.jsx(ft,{id:"model-test-proxy",className:"w-full",children:s.jsx(dt,{className:"min-w-0 truncate",children:ue})}),s.jsx(pt,{children:W.map(te=>s.jsx($e,{value:te.value,disabled:te.disabled,children:te.label},te.value))})]})]})]}),s.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"model-test-reasoning",children:r("思考等级")}),s.jsxs(ut,{value:P,onValueChange:te=>R(String(te)),children:[s.jsx(ft,{id:"model-test-reasoning",className:"w-full",children:s.jsx(dt,{children:P?r("{level}(本次指定)",{level:P}):U})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:U}),M.map(te=>s.jsx($e,{value:te,children:r("{level}(本次指定)",{level:te})},te))]})]})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsxs("div",{className:"flex items-center gap-1.5",children:[s.jsx(Je,{htmlFor:"model-test-stream",children:r("流式请求")}),s.jsxs(Ui,{children:[s.jsx(Vi,{render:s.jsx("span",{className:"tip-q",tabIndex:0,"aria-label":r("流式请求的说明")}),children:"?"}),s.jsx(Fi,{children:r("默认开,与真实请求一致:可同时验证 SSE 链路与首字延迟。")})]})]}),s.jsx(ns,{id:"model-test-stream",checked:C,onCheckedChange:te=>E(!!te)})]})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(Je,{htmlFor:"model-test-accounts",children:r("测试账号")}),s.jsxs(Ui,{children:[s.jsx(Vi,{render:s.jsx("span",{className:"tip-q",tabIndex:0,"aria-label":r("测试账号的说明")}),children:"?"}),s.jsx(Fi,{children:r("勾几个就并行测几次;默认选队列里最靠前的一个。")})]}),s.jsx(ne,{variant:"ghost",size:"xs",className:"ml-auto",disabled:!c.length||D.length===c.length,onClick:()=>I(c.map(te=>te.id)),children:r("全选({n})",{n:c.length})})]}),s.jsx(Gh,{id:"model-test-accounts",value:D,onValueChange:I,options:ve,placeholder:r("选一个账号(至少一个)"),searchPlaceholder:r("搜索账号…"),emptyHint:r("这家没有可用账号(要在账号页启用一个、且凭证完整)")})]}),s.jsx(z2,{plain:!0,children:r("真实链路 · 消耗少量额度 · 日志带「测试」标记,不计报表")})]}),s.jsxs(tn,{className:"justify-end",children:[s.jsx(ne,{variant:"outline",onClick:t,children:r("关闭")}),s.jsx(ne,{variant:"default",disabled:!D.length||!c.length,onClick:q,children:r("开始测试")})]}),s.jsx(Yt,{open:O.length>0,onOpenChange:te=>{te||Q()},children:s.jsxs(Xt,{overlayForceRender:!0,className:"w-[min(680px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsxs(Ut,{children:[r(F?"测试中…":"测试结果"),G]})}),s.jsx(Lt,{children:he()}),s.jsx(tn,{className:"justify-end",children:F?s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"mr-auto text-xs text-subtle",children:r("关闭即中止")}),s.jsx(ne,{variant:"outline",onClick:()=>X(),children:r("中止测试")}),s.jsx(ne,{variant:"default",disabled:!0,children:r("测试中…")})]}):s.jsxs(s.Fragment,{children:[s.jsx(ne,{variant:"outline",onClick:Q,children:r("关闭")}),s.jsx(ne,{variant:"default",onClick:q,children:r("再测一次")})]})})]})})]})})}const H2=[{value:"enable",label:r("启用")},{value:"disable",label:r("禁用")},{value:"reasoning",label:r("设置思考等级")},{value:"delete",label:r("删除")}];function U2(e){return e.source==="manual"||as(e.provider)}function PY(e){return(e.name&&e.name!==e.id?r("{name}({id})",{name:e.name,id:e.id}):e.id)||e.id}function _Y({models:e,onClose:t,onDone:a}){const[o,c]=x.useState("enable"),d=VK(op().data,e),[f,p]=x.useState(()=>({select:d[0]||"",custom:""})),[h,b]=x.useState(!1),[y,v]=x.useState(""),j=f.select===fa?f.custom.trim():f.select,w=e.filter(U2).length,S=H2.find(A=>A.value===o)?.label||r("操作");async function N(A){return Promise.resolve(Wt().wbConfirm?.ask?.({title:r("删除选中的模型"),html:r("确定删除选中的 {count} 个模型登记?删除后 /v1/models 不再广告它们、请求也会被拒。",{count:A})+(e.length>A?"
    "+r("另有 {n} 个非手动登记的模型将被跳过。",{n:e.length-A}):""),okText:r("删除"),okClass:"danger"})??!1)}async function k(){if(h)return;if(!e.length){Nr(r("没有选中的模型"),"err");return}if(o==="reasoning"&&f.select===fa&&!j){v(s.jsx("span",{className:"text-destructive",children:r("请填写自定义思考等级,或改选候选档 / 「清除」")}));return}if(o==="delete"){if(w===0){v(s.jsx("span",{className:"text-destructive",children:r("选中的模型都不是手动登记,无法删除(开关映射才是它们的手段)")}));return}if(!await N(w))return}b(!0),v("");let A=0,P=0,R=0,C="";for(const E of e){const D=E.provider||"";try{if(o==="delete"){if(!U2(E)){P+=1;continue}tc(await E2(D,E.id))}else if(o==="enable"||o==="disable"){const I=o==="enable";for(const O of Fu(E))tc(await rx(D,O.alias,E.id,{enabled:I}))}else for(const I of Fu(E))tc(await rx(D,I.alias,E.id,{reasoning:j}));A+=1}catch(I){R+=1,C||(C=Lo(I)),v(s.jsx("span",{className:"text-destructive",children:r("失败 {n} 个 · 首个失败:{error}",{n:R,error:C})}))}}R===0?(Nr(P?r("✅ 批量{action}完成:共 {n} 个模型(跳过不可删除的 {skipped} 个)",{action:S,n:A,skipped:P}):r("✅ 批量{action}完成:共 {n} 个模型",{action:S,n:A})),a()):(Nr(r("批量{action}完成:成功 {ok} 个,失败 {failed} 个",{action:S,ok:A,failed:R}),"err"),v(s.jsxs("span",{className:"text-destructive",children:[r("成功 {ok} 个,失败 {failed} 个",{ok:A,failed:R}),P?r(",跳过 {n} 个",{n:P}):"",C?r(";首个失败:{error}",{error:C}):""]}))),b(!1)}return s.jsx(Yt,{open:!0,onOpenChange:A=>{A||h||t()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("批量操作 · 已选 {n} 个模型",{n:e.length})})}),s.jsxs(Lt,{children:[s.jsxs(dn,{children:[s.jsx("h3",{children:r("将作用于以下模型")}),s.jsx("p",{style:{maxHeight:84,overflowY:"auto"},children:e.map(PY).join(r("、"))})]}),s.jsxs(dn,{children:[s.jsx("h3",{children:r("操作")}),s.jsx(Ph,{value:o,onValueChange:A=>c(A),className:"flex-row flex-wrap items-center gap-5","aria-label":r("批量动作"),children:H2.map(A=>s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2 font-normal",children:[s.jsx(yo,{value:A.value}),A.label]},A.value))}),o==="reasoning"?s.jsxs("div",{className:"mt-2.5",children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:"batch-model-reasoning",children:r("思考等级")}),s.jsxs(ut,{value:f.select,onValueChange:A=>p(P=>({...P,select:String(A)})),children:[s.jsx(ft,{id:"batch-model-reasoning",className:"min-w-[220px]",children:s.jsx(dt,{children:f.select===fa?r("自定义等级…"):f.select||r("清除(不覆盖)")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:r("清除(不覆盖)")}),d.map(A=>s.jsx($e,{value:A,children:A},A)),s.jsx($e,{value:fa,children:r("自定义等级…")})]})]})]}),f.select===fa?s.jsxs("div",{className:"field-row mt-2",children:[s.jsx("label",{htmlFor:"batch-model-reasoning-custom",children:r("自定义等级")}),s.jsx(Fe,{id:"batch-model-reasoning-custom",maxLength:32,value:f.custom,onChange:A=>p(P=>({...P,custom:A.currentTarget.value}))})]}):null,s.jsxs("p",{className:"detail mt-1.5",children:[r("设置 / 清除会应用到选中模型的"),s.jsx("b",{children:r("全部映射")}),r("(默认绑定与别名 chips);「清除」= 不给这些映射指定等级(选「不覆盖」的口径与映射弹窗一致)")]})]}):null,o==="delete"?s.jsx("p",{className:"detail mt-2",children:w===0?r("选中的都不是手动登记的模型,删除不可用(上游目录带回来的行由清单决定存在性)"):r("可删除 {n} 个(手动登记 / 自定义家);",{n:w})+(e.length>w?r("其余 {n} 个会被跳过。",{n:e.length-w}):"")}):null]}),s.jsx("div",{className:"detail",style:{minHeight:18},children:y})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",disabled:h,onClick:t,children:r("关闭")}),s.jsx(ne,{variant:o==="delete"?"destructive":"default",disabled:h,onClick:()=>{k()},children:h?r("执行中…"):r("执行{action}",{action:S})})]})]})})}function rr(){return window}const V2={intl:"fm-table-intl",custom:"fm-table-custom"},F2={intl:"fetch-models",custom:"fetch-models-custom"},f0=["provider","source","state","count","note","updated","op"],p0=["pick","model","state"],LY={provider:"w-[13%]",source:"w-[21%]",state:"w-[9%]",count:"w-[6%]",note:"w-[20%]",updated:"w-[16%]",op:"w-[15%]"},zY={pick:"w-[8%]",model:"w-[74%]",state:"w-[18%]"},BY={pick:"",provider:r("提供商"),source:r("模型来源"),state:r("状态"),count:r("模型数"),note:r("说明"),updated:r("更新日期"),model:r("上游模型"),op:r("操作")},HY={refreshed:{variant:"brand",label:r("已刷新")},failed:{variant:"destructive",label:r("失败")},skipped:{variant:"outline",label:r("跳过")}},$2=new Set(["cline-free","cline-pass"]),q2="workbuddy-desktop-fetch-model-source";function hp(e){return String(e??"").trim().toLowerCase()}function m0(e,t){rr().wbApp?.toast?.(e,t)}function h0(e){return e instanceof Error?e.message:String(e)}function G2(e){const t=rr().formatTime??rr().wbApp?.formatTime;return String(t?.(e)??"")}function K2(){return rr().wbApp?.getState?.()?.accounts?.accounts||[]}function UY(e){const a=(rr().wbProviders?.customList?.()||[]).find(o=>o.id===e)?.models;return new Set((Array.isArray(a)?a:[]).map(o=>hp(o?.id)).filter(Boolean))}function Y2(e){if(e?.nameCustom===!0&&e?.name)return e.name;const t=String(e?.email||"").trim();return rr().wbAccountsModel?.providerFeatures?.(e?.provider)?.emailAsName&&t?t:String(e?.nickname||e?.name||t||e?.id||"").trim()||r("未命名账号")}function VY(e){if($2.has(e))return[];const t=rr().wbAccountsModel?.byPriorityOrder,a=K2().filter(o=>(o?.provider||"workbuddy")===e&&o?.available!==!1&&o?.enabled!==!1);return t?a.sort(t):a}function X2(){const e=new Set(rr().wbModelsPanel?.builtinProviders?.()||[]);for(const t of K2())t?.available===!1||t?.enabled===!1||e.add(t?.provider||"workbuddy");return[...e]}function FY(){const e=new Set(X2());return(rr().wbProviders?.all?.()||[]).filter(t=>e.has(t?.id??""))}function $Y(e){return G2(Number(e.refreshedAt)||0)||"—"}function qY(e){return HY[String(e.status)]??{variant:"outline",label:String(e.status||r("未知"))}}function GY(){try{const e=JSON.parse(localStorage.getItem(q2)||"{}");if(!e||typeof e!="object")return{};const t={};for(const[a,o]of Object.entries(e))typeof o=="string"&&o&&(t[a]=o);return t}catch{return{}}}function KY(e){rr().wbFilterMemory?.save?.(q2,e)}function YY({options:e,onClose:t}){const a=e.providerId,o=!!e.custom,c=e.name||a,{onDone:d,onRefreshed:f}=e,[p,h]=x.useState("idle"),[b,y]=x.useState(""),[v,j]=x.useState(""),[w,S]=x.useState(null),[N,k]=x.useState(""),A=w!==null,[P,R]=x.useState(!1),[C,E]=x.useState([]),[D,I]=x.useState(()=>new Set),[O,_]=x.useState(()=>new Set),[z,H]=x.useState([]),[B,L]=x.useState(""),[F,$]=x.useState(GY);x.useEffect(()=>{rr().wbTableColumns?.repaint?.(o?F2.custom:F2.intl)},[o]);const X=o?[]:FY(),K=o?[]:J(),q=B.trim().toLowerCase(),Q=C.filter(oe=>!q||oe.toLowerCase().includes(q)),G=o?P?r("已选 {picked} 个 · 上游共 {upstream} 个 · 清单已有 {managed} 个",{picked:O.size,upstream:C.length,managed:D.size}):r("尚未获取"):Y(K);function J(){const oe=new Map(z.map(Ce=>[String(Ce.provider||""),Ce])),me=X.map(Ce=>{const Ne=String(Ce.id||"");return{provider:Ne,label:String(Ce.label||Ne),item:oe.get(Ne)}}),pe=new Set(me.map(Ce=>Ce.provider));for(const Ce of z){const Ne=String(Ce.provider||"");pe.has(Ne)||me.push({provider:Ne,label:String(Ce.providerLabel||Ne),item:Ce})}return me}function Y(oe){if(p==="loading")return r("正在刷新…");if(p==="error")return"";const me=oe.map(Ne=>Ne.item).filter(Ne=>!!Ne);if(!me.length)return P?r("刷新完成,但没有得到任何结果"):oe.length?r("共 {n} 家 · 点「获取模型」开始",{n:oe.length}):r("0 家");const pe=me.filter(Ne=>Ne.status==="refreshed").length,Ce=me.filter(Ne=>Ne.status==="failed").length;return Ce?r("{total} 家 · 成功 {done} · 失败 {failed}",{total:oe.length,done:pe,failed:Ce}):r("{total} 家 · 成功 {done}",{total:oe.length,done:pe})}function re(oe,me){const pe=VY(oe);if(!pe.length)return{list:pe,selected:""};const Ce=String(F[oe]||""),Ne=String(me||""),be=De=>pe.some(Ge=>String(Ge.id)===De);return{list:pe,selected:be(Ce)?Ce:be(Ne)?Ne:String(pe[0].id)}}function M(oe){const me={};for(const[pe,Ce]of Object.entries(F))Ce&&(me[pe]=Ce);for(const pe of oe){const{selected:Ce}=re(pe.provider,pe.reported);Ce&&(me[pe.provider]=Ce)}return me}const U=K.map(oe=>({provider:oe.provider,reported:oe.item?.accountId}));function Z(oe,me){$(pe=>({...pe,[oe]:me})),KY({[oe]:me})}async function ae(){if(!A){S("load"),j(""),y(""),h("loading");try{if(o){const me=await rr().wbProviders?.customRequest?.("POST","/api/custom-providers/fetch-models",{providerId:a}),pe=new Set,Ce=(Array.isArray(me?.models)?me.models:[]).map(be=>String(be??"").trim()).filter(be=>{const De=hp(be);return!be||pe.has(De)?!1:(pe.add(De),!0)}),Ne=UY(a);E(Ce),I(Ne),_(new Set(Ce.filter(be=>!Ne.has(hp(be))))),R(!0),h("ready");return}const oe=await rr().workbuddyDesktop?.refreshModels({accounts:M(U),providers:X2()});H(Array.isArray(oe?.results)?oe.results:[]),R(!0),h("ready"),Number(oe?.refreshed)>0&&f?.()}catch(oe){const me=h0(oe);y(me),h("error"),j(me)}finally{S(null)}}}async function ce(oe){if(!(A||!oe.provider)){S("row"),k(oe.provider),j("");try{const me=await rr().workbuddyDesktop?.refreshModels({accounts:M([{provider:oe.provider,reported:oe.item?.accountId}]),providers:[oe.provider]});H(pe=>W(pe,Array.isArray(me?.results)?me.results:[])),R(!0),h("ready"),Number(me?.refreshed)>0&&f?.()}catch(me){const pe=h0(me);j(pe),m0(r("获取失败:{message}",{message:pe}),"err")}finally{S(null),k("")}}}function W(oe,me){const pe=new Map(me.map(be=>[String(be.provider||""),be])),Ce=oe.map(be=>pe.get(String(be.provider||""))??be),Ne=new Set(Ce.map(be=>String(be.provider||"")));for(const be of me)Ne.has(String(be.provider||""))||Ce.push(be);return Ce}async function ue(){if(!o||A||!O.size)return;const oe=[...O];S("import"),j("");try{const me=Number(await rr().wbModelsCustom?.addModels?.(a,oe))||0;m0(r("✅ 已导入 {added} 个模型({skipped} 个已存在,跳过)",{added:me,skipped:oe.length-me})),S(null),t(),d?.()}catch(me){const pe=h0(me);m0(r("导入失败:{message}",{message:pe}),"err"),j(pe),S(null)}}function ve(){A||t()}function ee(){const oe=new Set(O);for(const me of C)D.has(hp(me))||oe.add(me);_(oe)}function he(oe){return s.jsx(Cl,{children:s.jsx(Tn,{colSpan:o?p0.length:f0.length,className:"py-[26px] text-center text-muted-foreground",children:oe})})}function te(oe){return s.jsx("span",{className:"font-mono text-[11.5px] tabular-nums text-muted-foreground",children:oe})}function ke(oe){return s.jsx("div",{className:"truncate font-mono text-[12px] font-semibold",children:oe})}function le(oe,me){const{list:pe,selected:Ce}=re(oe,me);if(!pe.length){const be=$2.has(oe)?r("这家的模型清单是全局的,不跟账号走"):r("这家还没有可用账号,刷新会使用默认登录态");return s.jsx("span",{className:"text-[11.5px] text-muted-foreground",title:be,children:"—"})}const Ne=pe.find(be=>String(be.id)===Ce);return s.jsxs(ut,{value:Ce,onValueChange:be=>Z(oe,String(be)),children:[s.jsx(ft,{className:"w-full",title:r("用哪个账号去拉这家的模型清单"),"aria-label":r("模型来源"),children:s.jsx(dt,{children:Ne?Y2(Ne):"—"})}),s.jsx(pt,{children:pe.map(be=>s.jsx($e,{value:String(be.id),children:Y2(be)},String(be.id)))})]})}function de(oe,me){_(pe=>{const Ce=new Set(pe);return me?Ce.add(oe):Ce.delete(oe),Ce})}function Se(oe,me,pe){if(pe||oe.target?.closest('[data-slot="checkbox"], input[type="checkbox"]'))return;const Ne=window.getSelection();Ne&&!Ne.isCollapsed&&oe.currentTarget.contains(Ne.anchorNode)||de(me,!O.has(me))}function Te(){return p==="idle"?he(r("点右侧「获取模型」从上游拉取这家的模型清单")):p==="loading"?he(r("正在获取…")):p==="error"?he(r("获取失败:{message}",{message:b})):Q.length?Q.map(oe=>{const me=D.has(hp(oe));return s.jsxs(Cl,{className:me?"opacity-50":"cursor-pointer",onClick:pe=>Se(pe,oe,me),children:[s.jsx(Tn,{children:s.jsx(Pr,{checked:O.has(oe),disabled:me,"aria-label":oe,onCheckedChange:pe=>de(oe,!!pe)})}),s.jsx(Tn,{children:ke(oe)}),s.jsx(Tn,{children:me?s.jsx(ze,{variant:"brand",title:r("这个模型已经在这家的清单里,不重复导入"),children:r("已添加")}):s.jsx(ze,{variant:"outline",title:r("勾上它,导入后即进入这家的清单"),children:r("未添加")})})]},oe)}):he(C.length?r("没有匹配「{keyword}」的模型",{keyword:q}):r("上游没有返回任何模型"))}function _e(oe){const me=w==="row"&&N===oe.provider;return s.jsx(ne,{variant:"outline",size:"sm",className:"w-full",disabled:A,title:r("只刷新这一家的模型清单"),onClick:()=>{ce(oe)},children:me?r("获取中…"):oe.item?r("重新获取"):r("获取")})}function Ae(){return p==="loading"?he(r("正在获取…")):p==="error"?he(r("获取失败:{message}",{message:b})):K.length?K.map(oe=>{const me=oe.item;if(!me){const be=G2(Number(rr().wbModelsPanel?.providerRefreshedAt?.(oe.provider))||0);return s.jsxs(Cl,{children:[s.jsx(Tn,{children:ke(oe.label)}),s.jsx(Tn,{children:le(oe.provider)}),s.jsx(Tn,{children:s.jsx(ze,{variant:"outline",children:r("待获取")})}),s.jsx(Tn,{children:te("—")}),s.jsx(Tn,{children:te("—")}),s.jsx(Tn,{children:te(be||"—")}),s.jsx(Tn,{children:_e(oe)})]},oe.provider)}const pe=qY(me),Ce=Number(me.count),Ne=me.status==="refreshed"?r("远程目录已更新,「来源」列会显示为「远程」"):me.message||(me.fixed?r("这家用固定模型清单,无可刷新"):r("本次没有取到新内容"));return s.jsxs(Cl,{children:[s.jsx(Tn,{children:ke(oe.label)}),s.jsx(Tn,{children:le(oe.provider,me.accountId)}),s.jsx(Tn,{children:s.jsx(ze,{variant:pe.variant,children:pe.label})}),s.jsx(Tn,{children:te(Ce?r("{n} 个",{n:Ce}):"—")}),s.jsx(Tn,{children:s.jsx("span",{className:"text-[11.5px] leading-[1.5] text-muted-foreground",children:Ne})}),s.jsx(Tn,{children:te($Y(me))}),s.jsx(Tn,{children:_e(oe)})]},oe.provider)}):he(r("还没有可刷新的提供商:先在账号页添加一个账号"))}return s.jsx(Yt,{open:!0,onOpenChange:(oe,me)=>{if(!oe){if(A){me.cancel();return}t()}},children:s.jsxs(Xt,{className:o?"w-[min(607px,calc(100vw-48px))]":"w-[min(1012px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("获取模型 — {name}",{name:c})})}),s.jsxs(Lt,{children:[s.jsxs("div",{className:"flex flex-wrap items-center gap-2.5",children:[s.jsx("span",{className:"text-[11.5px] text-muted-foreground",children:G}),o&&s.jsxs(Di,{className:"w-[240px] flex-none",children:[s.jsx(Pi,{type:"search",placeholder:r("搜索模型 ID…"),autoComplete:"off",value:B,onChange:oe=>L(oe.currentTarget.value)}),s.jsx(Ii,{"aria-hidden":"true",children:"⌕"})]}),s.jsx("div",{className:"mr-auto"}),o&&s.jsxs(s.Fragment,{children:[s.jsx(ne,{variant:"ghost",size:"sm",onClick:ee,children:r("全选未添加")}),s.jsx(ne,{variant:"ghost",size:"sm",onClick:()=>_(new Set),children:r("清空")})]}),s.jsx(ne,{variant:"outline",size:"sm",title:r("刷新全部家(逐家打上游)"),disabled:A,onClick:()=>{ae()},children:r(w==="load"?"获取中…":P?"重新获取":"获取模型")})]}),s.jsxs(Zy,{id:o?V2.custom:V2.intl,className:"table-fixed",containerClassName:"max-h-[min(48vh,460px)]",children:[s.jsx("colgroup",{children:(o?p0:f0).map(oe=>s.jsx("col",{"data-col":oe,className:o?zY[oe]:LY[oe]},oe))}),s.jsx(AE,{children:s.jsx(Cl,{children:(o?p0:f0).map(oe=>s.jsx(RE,{"data-col":oe,children:BY[oe]},oe))})}),s.jsx(Wy,{className:"[&_tr:last-child>td]:border-b-0",children:o?Te():Ae()})]})]}),s.jsxs(tn,{children:[s.jsx("span",{className:"min-w-0 text-[11.5px] text-muted-foreground",children:v}),s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:ve,children:r(o?"取消":"完成")}),o&&s.jsx(ne,{variant:"default",disabled:!O.size||A,onClick:()=>{ue()},children:w==="import"?r("导入中…"):O.size?r("导入选中的 {n} 个模型",{n:O.size}):r("导入")})]})]})})}let gp=null,$u=null;function Q2(){gp&&(gp.unmount(),gp=null),$u&&($u.remove(),$u=null)}function XY(e){e?.providerId&&(Q2(),$u=document.createElement("div"),document.body.append($u),gp=jn.createRoot($u),gp.render(s.jsx(YY,{options:e,onClose:Q2})))}window.wbModelsFetchModal={open:XY};const Bo=(e,t)=>t?`${e} ta-${t}`:e;function Z2(e,t){const a=(t||"").trim();if(a)return up().find(o=>zo(o.provider,e)&&zo(o.id,a))}const QY="[.pv-row:hover_&_[data-slot=nav-item-count]]:invisible";function ZY(e){const t=/x\s*([\d.]+)/i.exec(String(e||""));return t?`${t[1]}x`:String(e||"")}function W2(e){const t=/x\s*([\d.]+)/i.exec(String(e||""));if(!t)return null;const a=Number(t[1]);return Number.isFinite(a)?a:null}const WY={workbuddy:250,"workbuddy-intl":295,raccoon:60,catpaw:85,autoclaw:250,"autoclaw-intl":295,qoder:330,"qoder-intl":350,"cline-free":200,"cline-pass":200,codearts:210,loomy:155,kuku:15,accio:320,"accio-intl":335,trae:170,zcode:285};function JY(e){const t=WY[e];if(t!==void 0)return t;let a=0;for(let o=0;o>>0;return a%360}function eX(e){const t=String(e||"").trim();if(!t)return"?";const a=t.match(/[A-Za-z]/);return(a?a[0]:t[t.length-1]).toUpperCase()}const tX=e=>({"--av-h":String(JY(e))}),g0=s.jsx("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:s.jsxs("g",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("rect",{x:"3.5",y:"3.5",width:"7",height:"7",rx:"1.5"}),s.jsx("rect",{x:"13.5",y:"3.5",width:"7",height:"7",rx:"1.5"}),s.jsx("rect",{x:"13.5",y:"13.5",width:"7",height:"7",rx:"1.5"}),s.jsx("rect",{x:"3.5",y:"13.5",width:"7",height:"7",rx:"1.5"})]})});function nX(e){if(!e)return"";const t=Wt().wbPresetProviders?.list;if(!Array.isArray(t))return"";const a=t.find(o=>o.name===e);return a&&Wt().wbPresetProviders?.iconOf?.(String(a.key||""))||""}function sX(e,t){return as(e)?nX(t):kg[e==="workbuddy-intl"?"workbuddy":e]||""}function qu(e,t,a){const o=a?"":sX(e,t),c=e==="all"||e==="custom-all";return s.jsx("span",{className:Ue("pv-ico",c&&"mute",o&&"img"),style:o?void 0:tX(e),"aria-hidden":"true",children:o?s.jsx("img",{src:o,alt:""}):a??eX(t)})}const rX={supportsToolCall:s.jsx("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:s.jsx("path",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",d:"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"})}),supportsImages:s.jsx("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:s.jsxs("g",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("rect",{x:"3",y:"3",width:"18",height:"18",rx:"2.5"}),s.jsx("circle",{cx:"8.5",cy:"8.5",r:"1.5"}),s.jsx("path",{d:"M21 15l-5-5L5 21"})]})}),supportsVideo:s.jsx("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:s.jsxs("g",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("circle",{cx:"12",cy:"12",r:"9"}),s.jsx("path",{d:"M10 8.5l6 3.5-6 3.5z"})]})}),supportsReasoning:s.jsx("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:s.jsxs("g",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("path",{d:"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"}),s.jsx("path",{d:"M9 18h6M10 22h4"})]})})};function aX(e,t,a,o){return o==="enabled"&&!fp(e)||o==="disabled"&&fp(e)||o==="mapped"&&!(e.aliases||[]).length||a!=="all"&&(e.provider||"")!==a?!1:t?[e.id,e.name,...e.aliases||[]].join(" ").toLowerCase().includes(t):!0}function lX(){const e=x.useSyncExternalStore(XK,op),t=ex(e),a=as(t)||t==="custom-all",[o,c]=x.useState(null),[d,f]=x.useState(!1),[p,h]=x.useState(new Set),b=a&&!Vu()&&e0()&&!_o(t);x.useEffect(()=>{nY(),tx(),j2(),Wt().wbApp?.currentPage==="gateway"&&T2().then(()=>Wt().wbProviders?.refreshCustom?.()).then(()=>{ec()})},[]),x.useEffect(()=>{bY()});const y=n0(),v=Array.isArray(y.models)?y.models:[],j=e.search.trim().toLowerCase(),w=v.filter(J=>aX(J,j,t,e.stateFilter)),S=e.sort,N=C2(),k=N.length,A=t==="all"?r("全部"):t==="custom-all"?r("自定义 · 全部"):a?String(_o(t)?.name||t):s0().get(t)?.label||c0(t),P=v.filter(fp).length,R=v.filter(J=>(J.aliases||[]).length>0).length,C=hg(w.length,"models"),E=v.filter(J=>p.has(nc(J))),D=(J,Y)=>{const re=new Set(p);Y?re.add(J):re.delete(J),h(re)},I=w.map(nc),O=I.length>0&&I.every(J=>p.has(J)),_=I.some(J=>p.has(J)),z=s.jsx(Pr,{checked:O,indeterminate:!O&&_,disabled:!I.length,"aria-label":r("全选当前筛选结果"),title:r("全选当前筛选出的模型(跨分页);再点取消"),onCheckedChange:J=>{const Y=new Set(p);for(const re of I)J===!0?Y.add(re):Y.delete(re);h(Y)}});function H(){const J=s0(),Y=Wi(),re=WK(),M=[...J.values()].reduce((ce,W)=>ce+W.n,0),U=r("全部({n} 家)",{n:J.size}),Z=[...re.values()].reduce((ce,W)=>ce+W.n,0),ae=r("全部({n} 家)",{n:[...re.values()].filter(ce=>ce.n>0).length});return s.jsxs("aside",{className:"prov-rail",id:"prov-rail","aria-label":r("按提供商选择"),children:[s.jsxs("div",{className:"rail-scroll",children:[s.jsx("div",{className:"rail-label",children:r("内置提供商")}),s.jsx(uu,{active:t==="all","data-provider":"all",title:U,count:M,icon:qu("all","",g0),className:"shadow-none",onClick:()=>pp("all"),children:U}),[...J].map(([ce,W])=>s.jsx(uu,{active:t===ce,"data-provider":ce,title:W.label,count:W.n,icon:qu(ce,W.label),className:"shadow-none",onClick:()=>pp(ce),children:W.label},ce)),s.jsx("div",{className:"rail-label",children:r("自定义提供商")}),Y.length?[...Z>0?[s.jsx(uu,{active:Vu(),"data-provider":"custom-all",title:ae,count:Z,icon:qu("custom-all","",g0),className:"shadow-none",onClick:()=>pp("custom-all"),children:ae},"custom-all")]:[],...Y.map(ce=>{const W=String(ce.id||""),ue=String(ce.name||W);return s.jsxs("div",{className:"pv-row",children:[s.jsx(uu,{active:t===W,"data-provider":W,title:ue,count:re.get(W)?.n??0,icon:qu(W,ue),className:Ue("shadow-none",QY),onClick:()=>pp(W),children:ue}),s.jsx("button",{type:"button",className:"pv-del","aria-label":r("删除自定义提供商"),title:r("删除这个自定义提供商(连同名下账号)"),onClick:()=>{dY(W)},children:"×"})]},W)})]:s.jsx("div",{className:"rail-empty",children:r("还没有自定义提供商")})]}),s.jsx("div",{className:"rail-foot",children:s.jsx(uu,{variant:"add",id:"rail-add-custom",title:r("新建一个自定义提供商(同时创建它的第一个账号)"),onClick:()=>fY(),children:r("+ 新建自定义提供商")})})]})}function B(J,Y,re,M){const U=u0(J,Y,re);return s.jsx(ne,{variant:U?"secondary":"dashed",size:"2xs",disabled:M,className:"font-mono font-bold shadow-none",title:U?r("思考等级 {level}(点击修改)",{level:U}):r("设置思考等级(当前未绑定)"),onClick:()=>A2({alias:J,target:Y,provider:re}),children:U||r("+等级")})}function L(J){const Y=J.provider||"",re=Fu(J),[M,...U]=re;async function Z(ce){await Wt().wbConfirm?.ask?.({title:r("删除映射"),html:r("确定删除映射「{alias} → {target}」?",{alias:Yw(ce),target:Yw(J.id)}),okText:r("删除"),okClass:"danger"})&&o0(i0(ce,J.id,Y),()=>sY(Y,ce,J.id),r("映射已删除"))}const ae=ce=>{const W=ce.alias,ue=ce.enabled!==!1,ve=e.pending.has(i0(W,J.id,Y));return s.jsxs("span",{className:Ue("alias",!ue&&"map-off"),children:[s.jsx(ns,{size:"sm",checked:ue,disabled:ve,title:r(ue?"映射已启用,点击关闭":"映射已关闭,点击启用"),"aria-label":r("{action}映射 {alias}",{action:r(ue?"关闭":"启用"),alias:W}),onCheckedChange:ee=>{o0(i0(W,J.id,Y),()=>rx(Y,W,J.id,{enabled:ee}),r(ee?"映射已启用":"映射已关闭"))}}),s.jsx("span",{className:"t","data-copy":W,title:r("点击复制:{name}",{name:W}),children:W}),ce.isDefault?s.jsx("span",{className:"binding-default",children:r("默认")}):null,B(W,J.id,Y,ve),ce.isDefault?null:s.jsx(ne,{variant:"ghost",size:"icon-2xs",className:"text-[12px] leading-none",disabled:ve,title:r("删除映射 {alias}",{alias:W}),onClick:()=>{Z(W)},children:"×"})]},`${W}${ce.isDefault?"d":"m"}`)};return s.jsxs("div",{className:"aliases",children:[s.jsxs("div",{className:"alias-row",children:[M?ae(M):null,s.jsx(ne,{variant:"dashed",size:"xs",onClick:()=>A2({target:J.id,provider:Y}),children:r("+ 映射")})]}),U.map(ae)]})}function F(J){if(J.source==="manual")return s.jsxs("span",{className:"src manual",title:r("手动登记的上游模型;移除它会直接删掉这条登记"),children:[s.jsx("i",{"aria-hidden":"true"}),r("手动")]});if(!J.source&&as(J.provider))return s.jsxs("span",{className:"src manual",title:r("用户登记的自定义模型;整份清单只属于这一家"),children:[s.jsx("i",{"aria-hidden":"true"}),r("自定义")]});if(J.source!=="remote"&&J.source!=="builtin")return s.jsx("span",{className:"rate",children:"—"});const Y=J.source==="remote",re=$K(Number(J.refreshedAt)||0),M=Y?re?r("来自上游目录接口,清单拉取于 {at};刷新失败时保留上一份成功结果",{at:re}):r("来自上游目录接口;刷新失败时保留上一份成功结果"):r("上游目录尚未拉到,用的是内置静态清单;点「刷新模型清单」可重试");return s.jsxs("span",{className:Ue("src",Y?"remote":"builtin"),title:M,children:[s.jsx("i",{"aria-hidden":"true"}),r(Y?"远程":"内置")]})}function $(J){const Y=Gw(J),re=qw(J.capOverrides),M=typeof Y.maxInputTokens=="number"?Y.maxInputTokens:null,U=typeof Y.maxOutputTokens=="number"?Y.maxOutputTokens:null,Z=r("上下文窗口 {input} / 最大输出 Token {output} —— 点击修改模型能力",{input:sp(M),output:sp(U)})+X(J);return s.jsxs("button",{type:"button",className:"caps-open",title:Z,onClick:()=>R2(J.provider||"",J.id),children:[s.jsxs("span",{className:Ue("cap-num",M===null&&"unset",Hu(J,"maxInputTokens")==="modelsdev"&&"filled"),children:[n2(M),re.includes("maxInputTokens")?s.jsx("i",{className:"cap-mark",title:r("已覆盖上游值")}):null]}),s.jsx("span",{className:"cap-sep",children:"/"}),s.jsxs("span",{className:Ue("cap-num",U===null&&"unset",Hu(J,"maxOutputTokens")==="modelsdev"&&"filled"),children:[n2(U),re.includes("maxOutputTokens")?s.jsx("i",{className:"cap-mark",title:r("已覆盖上游值")}):null]})]})}function X(J){const Y=["maxInputTokens","maxOutputTokens"].filter(re=>Hu(J,re)==="modelsdev").map(re=>Bu[re]);return Y.length?r(";其中 {keys} 由 models.dev 补齐",{keys:Y.join(r("、"))}):""}function K(J){const Y=Gw(J),re=qw(J.capOverrides);return s.jsx("button",{type:"button",className:"caps-open caps-badges",title:r("对下游声明的能力(工具调用 / 图片识别 / 视频识别 / 支持思考)—— 点击修改"),onClick:()=>R2(J.provider||"",J.id),children:e2.map(M=>{const U=Y[M]??null,Z=Kw(U),ae=re.includes(M),ce=Hu(J,M);return s.jsxs("span",{className:Ue("cap-dot",Z,ce==="modelsdev"&&"filled",ae&&"marked"),title:PK(M,U,ae,ce),children:[rX[M],ae?s.jsx("i",{className:"cap-mark"}):null]},M)})})}function q(J,Y,re){async function M(){await Wt().wbConfirm?.ask?.({title:r("移除自定义模型"),html:r("确定移除自定义模型「{id}」?这条登记会被直接移除,之后 /v1/models 不再广告它、请求它也会被拒。",{id:Yw(Y.id)}),okText:r("移除"),okClass:"danger"})&&o0(nc(Y),()=>E2(Y.provider||"",Y.id),as(Y.provider||"")?r("模型已移除"):r("自定义模型已移除"))}switch(J.key){case"check":return s.jsx("td",{className:Bo("cell-check",J.align),children:s.jsx(Pr,{checked:p.has(nc(Y)),title:r("勾选后可批量操作"),"aria-label":r("勾选后可批量操作"),onCheckedChange:U=>D(nc(Y),U===!0)})});case"model":return s.jsxs("td",{className:Bo("cell-model",J.align),children:[s.jsx("div",{className:"mid",children:s.jsx("span",{className:"t","data-copy":Y.id,title:r("点击复制:{name}",{name:Y.id}),children:Y.id})}),Y.name&&Y.name!==Y.id?s.jsx("div",{className:"mname",children:Y.name}):null]});case"rate":return s.jsx("td",{className:Bo("cell-rate",J.align),children:s.jsx("span",{className:"rate",children:Y.credits?ZY(Y.credits):"—"})});case"source":return s.jsx("td",{className:Bo("cell-source",J.align),children:F(Y)});case"budget":return s.jsx("td",{className:Bo("cell-budget",J.align),children:$(Y)});case"caps":return s.jsx("td",{className:Bo("cell-caps",J.align),children:K(Y)});case"alias":return s.jsx("td",{className:Bo("cell-alias",J.align),children:L(Y)});case"act":{const U=OY(Y.provider||""),Z=Y.source==="manual"||as(Y.provider||"");return s.jsx("td",{className:Bo("cell-act r",J.align),children:s.jsxs("div",{className:"row-actions",children:[s.jsx(ne,{variant:"ghost",size:"sm",disabled:!!U,title:U||r("以这个上游模型发一次最小请求,走真实转发链路"),onClick:()=>c({provider:Y.provider||"",id:Y.id}),children:r("测试")}),Z?s.jsx(ne,{variant:"ghost",size:"sm",className:"text-destructive",disabled:re,onClick:()=>{M()},children:r("移除")}):null]})})}default:return null}}function Q(J){const Y=e.pending.has(nc(J)),re=p.has(nc(J));return s.jsx("tr",{className:Ue(re&&"sel"),children:N.map(M=>s.jsx(x.Fragment,{children:q(M,J,Y)},M.key))},`${J.provider||""}${J.id}`)}function G(){if(!v.length){const Z=a?e0()?Vu()?r("自定义提供商还没有模型:先在左栏选一家,点「添加模型」登记,或「获取上游模型」从上游拉取"):r(b?"该提供商已不存在(可能已被删除),请刷新列表":"这家还没有模型:点「添加模型」登记,或「获取上游模型」从上游拉取"):r("加载中…"):e.data?r("暂无模型(请先添加账号)"):r("加载中…");return s.jsx("tr",{children:s.jsx("td",{colSpan:k,className:"empty",children:Z})})}if(!w.length)return s.jsx("tr",{children:s.jsx("td",{colSpan:k,className:"empty",children:j?r("没有匹配「{keyword}」的模型",{keyword:j}):r("没有匹配当前筛选的模型")})});const J=t==="all"||Vu(),Y=J&&!j&&e.stateFilter==="all"&&!C.paged,re=new Map;for(const Z of w){const ae=Z.provider||"";re.has(ae)||re.set(ae,{label:Z.providerLabel||ae||r("未知"),items:[]}),re.get(ae)?.items.push(Z)}const M=[];let U=0;for(const[Z,ae]of re){ae.items.sort((te,ke)=>{const le=+!fp(te)-+!fp(ke);if(le!==0||S==="default")return le;const de=W2(te.credits),Se=W2(ke.credits);return de===null||Se===null?de===Se?0:de===null?1:-1:S==="rate-asc"?de-Se:Se-de});const ce=e.expanded.has(Z)||!Y,W=ce?ae.items:ae.items.slice(0,h2),ue=ae.items.length-W.length,ve=C.paged?Math.max(0,C.rangeStart-1-U):0,ee=C.paged?Math.min(W.length,C.rangeEnd-U):W.length,he=ve0?r("(续)"):"",r("{n} 个模型",{n:ae.items.length})]})},`group:${Z}`));for(const te of he)M.push(Q(te));U+=W.length,ue>0?M.push(s.jsx("tr",{className:"tr-more",children:s.jsx("td",{colSpan:k,children:s.jsx(ne,{variant:"ghost",size:"sm",onClick:()=>lY(Z),children:r("展开其余 {n} 个模型 ▾",{n:ue})})})},`more:${Z}`)):ce&&Y&&ae.items.length>h2&&M.push(s.jsx("tr",{className:"tr-more",children:s.jsx("td",{colSpan:k,children:s.jsx(ne,{variant:"ghost",size:"sm",onClick:()=>oY(Z),children:r("收起 ▴")})})},`collapse:${Z}`))}return M}return s.jsxs(s.Fragment,{children:[s.jsx("section",{className:"panel",children:s.jsxs("div",{className:"mm",children:[H(),s.jsxs("div",{className:"mm-main",children:[s.jsxs("div",{className:"panel-head",children:[s.jsxs("div",{className:"mm-titlebar",children:[s.jsxs("div",{className:"mm-title",children:[t==="all"||t==="custom-all"?qu(t,"",g0):qu(t,A),s.jsx("h2",{children:A}),s.jsxs("span",{className:"mm-meta",children:[s.jsx("b",{children:v.length}),r(" 个模型 · 已启用 "),s.jsx("b",{children:P}),r(" · 有映射 "),s.jsx("b",{children:R})]})]}),s.jsxs("div",{className:"head-actions",children:[s.jsx(ne,{id:"btn-refresh-models",title:as(t)?r("从这一家的上游拉一份模型清单,勾选要哪些再导入(已添加的不会重复导入)"):r("刷新模型管理页里各提供商的远程模型目录,逐家结果列在弹窗里"),onClick:()=>M2(),children:r("获取模型")}),s.jsx(ne,{id:"btn-add-custom-model",variant:"outline",size:"sm",onClick:()=>mY(),children:r("+ 添加模型")})]})]}),s.jsxs("div",{className:"mm-filters",children:[s.jsx(En,{options:GK,value:e.stateFilter,onValueChange:iY,"aria-label":r("按状态筛选"),className:"shrink-0"}),s.jsx(En,{options:YK,value:e.sort,onValueChange:cY,"aria-label":r("按倍率排序"),className:"shrink-0"}),s.jsxs(Di,{className:"w-full min-[768px]:w-[240px] flex-none min-[768px]:flex-none",id:"models-search",children:[s.jsx(Pi,{type:"search",placeholder:r("搜索模型 ID / 名称 / 映射名…"),"aria-label":r("搜索模型"),autoComplete:"off",value:e.search,onChange:J=>uY(J.currentTarget.value)}),s.jsx(Ii,{"aria-hidden":"true",children:"⌕"})]}),E.length>0?s.jsxs("span",{className:"batch-chip",children:[r("已选 "),s.jsx("b",{children:E.length}),r(" 个"),s.jsx(ne,{id:"btn-batch-models",variant:"outline",size:"xs",title:r("对选中的 {n} 个模型执行批量操作",{n:E.length}),onClick:()=>f(!0),children:r("批量操作")})]}):null]})]}),s.jsx("div",{className:"models-table-wrap",children:s.jsxs("table",{className:"models-table",ref:J=>xY(J),children:[s.jsxs("colgroup",{children:[s.jsx("col",{className:"c-check","data-col":"check"}),s.jsx("col",{className:"c-model","data-col":"model"}),s.jsx("col",{className:"c-rate","data-col":"rate"}),s.jsx("col",{className:"c-source","data-col":"source"}),s.jsx("col",{className:"c-budget","data-col":"budget"}),s.jsx("col",{className:"c-caps","data-col":"caps"}),s.jsx("col",{className:"c-alias","data-col":"alias"}),s.jsx("col",{className:"c-act","data-col":"act"})]}),s.jsx("thead",{children:s.jsxs("tr",{children:[s.jsx("th",{"data-col":"check",children:z}),s.jsx("th",{"data-col":"model",children:r("上游模型")}),s.jsx("th",{"data-col":"rate",children:r("倍率")}),s.jsx("th",{"data-col":"source",children:r("来源")}),s.jsxs("th",{"data-col":"budget",children:[r("上下文 / 输出"),s.jsx("span",{className:"th-help","aria-hidden":"true",title:r("数字带虚线底 = 由 models.dev 补齐;右上角小点 = 你手动覆盖过;— = 未声明。点任意一格可改。"),children:s.jsx("svg",{viewBox:"0 0 24 24",children:s.jsxs("g",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",children:[s.jsx("circle",{cx:"12",cy:"12",r:"9"}),s.jsx("path",{d:"M12 16v-4"}),s.jsx("path",{d:"M12 8h.01"})]})})})]}),s.jsxs("th",{"data-col":"caps",children:[r("能力"),s.jsx("span",{className:"th-help","aria-hidden":"true",title:r("圆点带内侧描边 = 由 models.dev 补齐;右上角小点 = 你手动覆盖过;虚线圈 = 未声明。悬停某一枚可看逐项出处。"),children:s.jsx("svg",{viewBox:"0 0 24 24",children:s.jsxs("g",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",children:[s.jsx("circle",{cx:"12",cy:"12",r:"9"}),s.jsx("path",{d:"M12 16v-4"}),s.jsx("path",{d:"M12 8h.01"})]})})})]}),s.jsxs("th",{"data-col":"alias",children:[r("模型映射"),s.jsx("span",{className:"th-help","aria-hidden":"true",title:r("每条映射 = 对外名 → 上游模型;原始 ID 与别名有独立开关,默认绑定永远存在(没有映射时是合成的那条)。"),children:s.jsx("svg",{viewBox:"0 0 24 24",children:s.jsxs("g",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",children:[s.jsx("circle",{cx:"12",cy:"12",r:"9"}),s.jsx("path",{d:"M12 16v-4"}),s.jsx("path",{d:"M12 8h.01"})]})})})]}),s.jsx("th",{className:"r","data-col":"act",children:r("操作")})]})}),s.jsx("tbody",{id:"models",children:G()})]})}),s.jsx($i,{className:"models-panel-foot",leading:Vu()?s.jsxs(s.Fragment,{children:[r("这里是"),s.jsx("b",{children:r("全部自定义提供商")}),r("的模型合集:每家的清单互相独立,别名也只在自己那家内生效;要改名称 / 协议 / Base URL,到账号页该家账号的「设置」→ 提供商一栏。")]}):a?s.jsxs(s.Fragment,{children:[r("自定义提供商的清单"),s.jsx("b",{children:r("只属于这一家")}),r(":这里的模型不会出现在其他家,别名也只在这一家内生效。改名称 / 协议 / Base URL 在账号页该家账号的「设置」→ 提供商一栏;整家不要了,鼠标移到左栏这家上点 × 删除(连同名下账号)。")]}):void 0,total:w.length,range:C.paged?{start:C.rangeStart,end:C.rangeEnd}:null,page:C.page,pageCount:C.pageCount,size:C.size,onSizeChange:C.setSize,onPageChange:C.goto})]})]})}),e.mapping?s.jsx(iX,{context:e.mapping,onClose:pY}):null,e.customModel?s.jsx(cX,{initial:e.customModel,onClose:hY}):null,e.capability?s.jsx(SY,{context:e.capability,onClose:gY}):null,o?s.jsx(IY,{target:o,onClose:()=>c(null)}):null,d?s.jsx(_Y,{models:E,onClose:()=>f(!1),onDone:()=>{h(new Set),f(!1)}}):null]})}const oX=r("绑定在「对外名 → 上游模型」这一条映射上的思考等级(列表照抄 OmniProxy 的手动绑定),选「不覆盖」表示不给这条映射指定等级。它会跟着这条映射注入转发,由承载的那家翻译成自己的档位字段(CatPaw 归并成 low/high/max,Qoder 按模型声明的档位归一)。以下几种情况故意不注入:① off / none(关闭思考)—— 本项目没有安全的表达方式;② 表外的自定义等级(各家能力范围不同,无法判断上游收不收);③ 客户端请求体里已经自己指定了档位(那是更明确的意图,绑定不覆盖);④ 这家上游不认识档位字段(如 WorkBuddy / 小浣熊 / AutoClaw / Cline)。注入与跳过都会写进详细日志。");function J2(e,t,a){const o=k2(e),c=(t||"").trim(),d=o.find(f=>f.id.toLowerCase()===c.toLowerCase());return d?d.id:c||o[0]?.id||""}function iX({context:e,onClose:t}){const a=!!e.alias,[o,c]=x.useState(()=>e.provider||r0()[0]?.id||""),[d,f]=x.useState(e.alias||""),[p,h]=x.useState(()=>J2(o,e.target)),[b,y]=x.useState(""),[v,j]=x.useState(!1),[w,S]=x.useState(()=>{const _=ap(op().data,Z2(o,p)),z=a?u0(e.alias,e.target,e.provider):"";return z&&_.includes(z)?{select:z,custom:""}:{select:z?fa:"",custom:z}}),N=ap(op().data,Z2(o,p)),k=(()=>{const _=r0();for(const z of[e.provider,o])z&&!_.some(H=>H.id===z)&&_.push({id:z,label:r(Wt().wbProviders?.labelOf?.(z)||z)});return _})(),A=(()=>{const _=k2(o),z=(p||"").trim();return z&&!_.some(H=>H.id.toLowerCase()===z.toLowerCase())&&_.unshift({id:z,label:r("{name}(不在该家当前清单里)",{name:z}),off:!0}),_})(),P=k.find(_=>_.id===o)?.label||o||r("(全局)"),R=w.select&&w.select!==fa&&!N.includes(w.select)?{select:fa,custom:w.custom||w.select}:w,C=R.select===fa?R.custom.trim():R.select,E=R.select===fa;function D(_){c(_),h(J2(_,p))}async function I(){if(v)return;const _=a?e.alias||"":d.trim(),z=p;if(!_){y(r("请填写对外映射名"));return}if(!z){y(r("该提供商当前没有可选的上游模型"));return}if(!o){y(r("请选择提供商"));return}if(!a&&zo(_,z)&&up().some(H=>zo(H.id,z)&&zo(H.provider,o))){y(r("原始 ID 已作为默认绑定,请直接使用该绑定的开关或等级按钮"));return}j(!0),y(r("保存中…"));try{tc(await rx(o,_,z,{reasoning:C})),j(!1),t();const H=C?r(" · 思考等级 {level}",{level:C}):"",B=r(Wt().wbProviders?.labelOf?.(o)||o);Nr(a?r("✅ 已更新 {name} 的思考等级",{name:_}):r("✅ 已添加映射 {name} → {target}({label}){suffix}",{name:_,target:z,label:B,suffix:H}))}catch(H){y(r("保存失败:{message}",{message:Lo(H)})),j(!1)}}const O=_=>{_.key==="Enter"&&(_.preventDefault(),I())};return s.jsx(Yt,{open:!0,onOpenChange:(_,z)=>{if(!_){if(v){z.cancel();return}t()}},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r(a?"设置思考等级":"添加模型映射")})}),s.jsxs(Lt,{children:[s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"mapping-provider",children:r("提供商")}),s.jsxs(ut,{value:o,disabled:!0,onValueChange:_=>D(String(_)),children:[s.jsx(ft,{id:"mapping-provider",className:"w-full",children:s.jsx(dt,{children:k.find(_=>_.id===o)?.label||o})}),s.jsx(pt,{children:k.map(_=>s.jsx($e,{value:_.id,children:_.label},_.id))})]})]}),s.jsxs("div",{className:"grid grid-cols-[1fr_24px_1fr] items-end gap-2.5",children:[s.jsxs("div",{className:"flex min-w-0 flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"mapping-alias",children:r("对外映射名(下游请求时用)")}),s.jsx(Fe,{id:"mapping-alias",maxLength:128,placeholder:r("例如 gpt-4o"),autoComplete:"off",autoFocus:!a,value:d,disabled:a,onChange:_=>f(_.currentTarget.value),onKeyDown:O})]}),s.jsx("div",{className:"pb-2 text-center text-muted-foreground",children:"→"}),s.jsxs("div",{className:"flex min-w-0 flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"mapping-upstream",children:r("转发到上游模型")}),s.jsxs(ut,{value:p,disabled:!0,onValueChange:_=>h(String(_)),children:[s.jsx(ft,{id:"mapping-upstream",className:"w-full",children:s.jsx(dt,{children:A.find(_=>_.id===p)?.label||p})}),s.jsx(pt,{children:A.map(_=>s.jsxs($e,{value:_.id,children:[_.label,_.off?r("(已禁用)"):""]},_.id))})]})]})]}),s.jsxs("div",{className:"rounded-md border border-border bg-surface-inset px-3 py-2.5 font-mono text-[12px] text-subtle",children:[r("下游请求 "),s.jsx("b",{className:"text-primary-fg",children:d.trim()||r("<对外名>")}),r(" → 转发")," ",s.jsx("b",{className:"text-primary-fg",children:p||r("<上游模型>")}),r("({label})",{label:P}),C?s.jsxs(s.Fragment,{children:[r(" · 思考等级 "),s.jsx("b",{className:"text-primary-fg",children:C})]}):null]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsxs("div",{className:"flex items-center gap-[7px]",children:[s.jsx(Je,{htmlFor:"mapping-reasoning",children:r("思考等级")}),s.jsxs(Ui,{children:[s.jsx(Vi,{render:s.jsx("span",{className:"tip-q",tabIndex:0,"aria-label":r("思考等级的注入规则")}),children:"?"}),s.jsx(Fi,{children:oX})]})]}),s.jsxs(ut,{value:R.select,onValueChange:_=>{const z=String(_);S(H=>({...H,select:z}))},children:[s.jsx(ft,{id:"mapping-reasoning",className:"w-full",autoFocus:a,children:s.jsx(dt,{children:R.select===fa?r("自定义等级…"):R.select||r("不覆盖")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:r("不覆盖")}),N.map(_=>s.jsx($e,{value:_,children:_},_)),s.jsx($e,{value:fa,children:r("自定义等级…")})]})]}),E?s.jsx(Fe,{id:"mapping-reasoning-custom",maxLength:32,autoFocus:!0,placeholder:r("自定义等级(例如 custom-high)"),autoComplete:"off",disabled:v,value:R.custom,onChange:_=>S(z=>({...z,custom:_.currentTarget.value})),onKeyDown:O}):null]}),s.jsxs("p",{className:"text-xs leading-[1.65] text-subtle",children:[r("对外名可自由命名,允许与上游模型 ID 同名(同名时该上游的原生路由优先,映射作兜底); 支持字母、数字与 "),s.jsx("code",{children:"- _ . / :"}),r("。同一对外名可在多个提供商各添加一条: 下游用同一个名字请求,网关按账号优先级主备切换,失败自动落到下一个提供商。")]}),s.jsx("div",{className:"min-h-[18px] text-xs text-subtle",children:b})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:t,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:v,onClick:()=>{I()},children:r(a?"保存等级":"保存映射")})]})]})})}function cX({initial:e,onClose:t}){const[a,o]=x.useState(e.provider),[c,d]=x.useState(""),[f,p]=x.useState(""),[h,b]=x.useState(!1),y=(()=>{const N=tY();return a&&!N.some(k=>k.id===a)&&N.push({id:a,label:r(Wt().wbProviders?.labelOf?.(a)||a)}),N})(),v=y.some(N=>N.id===a)?a:y[0]?.id||"",j=y.find(N=>N.id===v)?.label||v||r("(未选)");async function w(){if(h)return;const N=c.trim();if(!v){p(r("请选择提供商"));return}if(!N){p(r("请填写上游模型 ID"));return}b(!0),p(r("保存中…"));try{const k=await rY(v,N);tc(k),b(!1),t();const A=y.find(P=>P.id===v)?.label||v;if(as(Uu()))Nr(r("✅ 已登记模型 {name}({label})",{name:N,label:A}));else{const P=k;Array.isArray(P?.models)&&P.models.some(C=>C.id===N&&(C.provider||"")===v)?Nr(r("✅ 已登记自定义模型 {name}({label})",{name:N,label:A})):Nr(r("✅ 已登记 {name}({label}),但该提供商还没有可用账号,这一行要加上账号后才会显示",{name:N,label:A}),"err")}}catch(k){p(r("保存失败:{message}",{message:Lo(k)})),b(!1)}}const S=N=>{N.key==="Enter"&&(N.preventDefault(),w())};return s.jsx(Yt,{open:!0,onOpenChange:(N,k)=>{if(!N){if(h){k.cancel();return}t()}},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("添加自定义模型")})}),s.jsxs(Lt,{children:[s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"custom-model-provider",children:r("提供商")}),s.jsxs(ut,{value:v,disabled:e.locked,onValueChange:N=>o(String(N)),children:[s.jsx(ft,{id:"custom-model-provider",className:"w-full",children:s.jsx(dt,{children:j})}),s.jsx(pt,{children:y.map(N=>s.jsx($e,{value:N.id,children:N.label},N.id))})]})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"custom-model-id",children:r("上游模型 ID")}),s.jsx(Fe,{id:"custom-model-id",maxLength:128,placeholder:r("例如 gpt-5.5-preview"),autoComplete:"off",spellCheck:!1,autoFocus:!0,value:c,onChange:N=>d(N.currentTarget.value),onKeyDown:S})]}),s.jsxs("div",{className:"rounded-md border border-border bg-surface-inset px-3 py-2.5 font-mono text-[12px] text-subtle",children:[r("在 "),s.jsx("b",{className:"text-primary-fg",children:j}),r(" 上登记上游模型")," ",s.jsx("b",{className:"text-primary-fg",children:c.trim()||r("<上游模型 ID>")}),r("(登记后即可用这个名字请求)")]}),s.jsxs("p",{className:"text-xs leading-[1.65] text-subtle",children:[r("填上游真正认识的那个模型 ID(不是给下游用的名字)。登记后它会进入该家的模型清单: 表格里出现这一行、"),s.jsx("code",{children:"/v1/models"}),r(" 会广告它、请求它也会被转发到这家。 支持字母、数字与 "),s.jsx("code",{children:"- _ . / :"}),r("。")]}),s.jsxs("p",{className:"text-xs leading-[1.65] text-subtle",children:[r("「来源」列会标成"),s.jsx("b",{children:r("手动")}),r(",与远程目录 / 内置清单区分开。删除自定义模型是"),s.jsx("b",{children:r("直接移除")}),r("这条登记(不像内置模型那样只是隐藏),因为它的存在完全由这次登记决定。")]}),s.jsx("div",{className:"min-h-[18px] text-xs text-subtle",children:f})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:t,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:h,onClick:()=>{w()},children:r("保存")})]})]})})}const eD='.page[data-page="gateway"]';let x0=null;function tD(){if(x0)return;const e=document.querySelector(eD);e&&(e.replaceChildren(),x0=jn.createRoot(e),x0.render(s.jsx(lX,{})))}document.querySelector(eD)?tD():document.addEventListener("DOMContentLoaded",tD,{once:!0});const nD=3065;function uX(){if(nl().workbuddyDesktop?.platform!=="web")return null;const e=typeof window>"u"?"":window.location?.origin;return e&&e!=="null"?e:null}function sD(e,t){return uX()??(e?`http://127.0.0.1:${e}`:t)}function nl(){return window}function ax(e){return e instanceof Error?e.message:String(e)}function Gu(e,t){nl().wbApp?.toast?.(e,t)}let xp={backend:null,gatewayBase:sD(0,`http://127.0.0.1:${nD}`)};const b0=new Set;function v0(){return xp}function dX(e){return b0.add(e),()=>{b0.delete(e)}}function fX(e){xp=e;for(const t of b0)t()}function rD(){return xp.gatewayBase.replace(/^https?:\/\//,"")}function pX(){return xp.backend?.ready===!0}function aD(e){const t=(a,o)=>{const c=document.getElementById(a);c&&(c.textContent=o)};t("api-base",`${e}/v1`),t("api-chat",`POST ${e}/v1/chat/completions`),t("api-responses",`POST ${e}/v1/responses`),t("api-messages",`POST ${e}/v1/messages`),t("api-models",`GET ${e}/v1/models`)}let y0=!1;async function Ku(){if(!y0){y0=!0;try{const e=nl().workbuddyDesktop;if(!e)throw new Error("后端桥不可用");const t=await e.getBackendStatus(),a=Number(t?.port)||0,o=v0(),c={backend:t??null,gatewayBase:sD(a,o.gatewayBase)};c.gatewayBase!==o.gatewayBase&&aD(c.gatewayBase),fX(c),nl().wbApp?.renderTopbarStatus?.()}catch(e){console.warn("读取后端状态失败:",ax(e))}finally{y0=!1}}}function mX(){aD(xp.gatewayBase),Ku()}async function lD(e){Gu(e||r("正在重启…"));try{await nl().workbuddyDesktop?.restartApp()}catch(t){Gu(r("重启失败:{reason}",{reason:ax(t)}),"err")}}const hX={idle:r("结束占用进程"),querying:r("查询中…"),ending:r("结束中…")};function gX({container:e}){const{backend:t,gatewayBase:a}=x.useSyncExternalStore(dX,v0),[o,c]=x.useState("idle"),d=t?.failure||null,f=rD(),p=t?.ready;let h="live off",b=r("正在检查… · {port}",{port:f});p===!0?(h="live pulse",b=r("网关运行中 · {port}",{port:f})):d?(h="live bad",b=r("{label} · {port}",{label:d.label||r("网关启动失败"),port:f})):p===!1&&(b=r("网关启动中… · {port}",{port:f}));const y=d?d.message??"":p===!0?r("本地网关正在监听 {url},可直接调用 OpenAI 兼容接口",{url:a}):r("正在确认网关是否已就绪");x.useEffect(()=>{Ku()},[]),x.useEffect(()=>{const j=nl().workbuddyDesktop?.onBackendError?.(()=>{Ku()});return typeof j=="function"?j:void 0},[]),x.useEffect(()=>{e.title=y},[e,y]);async function v(){if(o!=="idle")return;const j=nl().workbuddyDesktop;if(j){c("querying");try{const w=await j.getPortOccupant(),S=w?.occupant;if(!S){Gu(r("端口 {port} 上没有查到监听进程,请改用「更换端口」",{port:w?.port??""}),"err");return}const N=[r("即将结束以下进程:"),"",r("进程名:{name}",{name:String(S.name)}),r("PID:{pid}",{pid:String(S.pid)}),r("路径:{path}",{path:S.path||r("(未知)")}),"",r("结束它会强制退出该程序未保存的数据。确认继续?")],k=nl().wbConfirm?.ask;if(!k||!await k({title:r("结束占用端口的进程"),text:N.join(` -`),okText:r("结束进程"),okClass:"danger"}))return;c("ending"),(await j.endPortOccupant())?.released?(Gu(r("已结束进程 {name}(PID {pid}),端口已释放",{name:String(S.name),pid:String(S.pid)})),await k({title:r("重启程序"),text:r("端口已释放。现在重启程序让网关用这个端口启动?"),okText:r("重启")})?await lD(r("重启中…")):await Ku()):(Gu(r("进程已结束,但端口仍被占用,建议改用「更换端口」"),"err"),await Ku())}catch(w){Gu(r("结束失败:{reason}",{reason:ax(w)}),"err")}finally{c("idle")}}}return s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"status-line",children:[s.jsx("span",{className:h}),s.jsx("span",{className:"txt",children:b})]}),d?.conflict?s.jsxs("div",{className:"status-actions",children:[d.canEndOccupant===!0?s.jsx(ne,{size:"sm",disabled:o!=="idle",onClick:()=>{v()},children:hX[o]}):null,s.jsx(ne,{size:"sm",onClick:()=>{vX()},children:r("更换端口")})]}):null]})}let j0=null;function oD(){if(j0)return;const e=document.getElementById("sidebar-status");e&&(e.replaceChildren(),j0=jn.createRoot(e),j0.render(s.jsx(gX,{container:e})))}const xX={idle:r("保存并重启"),checking:r("校验中…"),saving:r("保存中…")};function bX({status:e,onClose:t}){const a=Number(e?.port)||nD,[o,c]=x.useState(()=>a<65535?String(a+1):String(a-1)),[d,f]=x.useState("idle"),[p,h]=x.useState(""),[b,y]=x.useState(""),v=x.useRef(!0),j=Number(o)||0,w=e?.failure?.message||r("当前端口 {port},网关运行正常。",{port:a});async function S(){if(d!=="idle")return;const N=nl().workbuddyDesktop;if(!N)return;const k=Number(o)||0;if(!k||k<1024||k>65535){y(r("端口需在 1024-65535 之间"));return}f("checking"),y(""),h("");try{const A=await N.checkPort(k);if(!A?.ok){h(A?.message||r("该端口不可用"));return}if(A.same){h(A.message??"");return}if(f("saving"),(await N.changePort(k))?.changed===!1){h(r("与当前端口相同,无需重启"));return}t(),await lD(r("端口已改为 {port},正在重启…",{port:k}))}catch(A){y(ax(A))}finally{f("idle")}}return s.jsx(Yt,{open:!0,onOpenChange:N=>{N||t()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("更换网关端口")})}),s.jsxs(Lt,{children:[s.jsxs(dn,{children:[s.jsx("h3",{children:r("当前状态")}),s.jsx("p",{children:w})]}),s.jsxs(dn,{children:[s.jsx("h3",{children:r("新端口")}),s.jsxs("div",{className:"flex flex-wrap items-center gap-2.5",children:[s.jsx(Je,{htmlFor:"port-input",className:"text-[12.5px] whitespace-nowrap text-subtle",children:r("端口号")}),s.jsx(Fe,{id:"port-input",type:"number",min:1024,max:65535,step:1,placeholder:r("例如 3066"),autoComplete:"off",className:"max-w-[130px]",value:o,onChange:N=>{c(N.currentTarget.value)},onKeyDown:N=>{N.key==="Enter"&&S()},autoFocus:!0,onFocus:N=>{v.current&&(v.current=!1,N.currentTarget.select())}})]}),p?s.jsx("p",{children:p}):null,s.jsx("p",{children:r("端口需在 1024-65535 之间。1023 及以下是系统保留范围,普通程序无法监听。")})]}),s.jsxs(dn,{children:[s.jsx("h3",{children:r("换端口后要改的地方")}),s.jsxs("p",{children:[r("网关地址会变成 "),s.jsx("code",{children:`http://127.0.0.1:${j||"—"}`}),r("。已经把这个地址填进其它工具(Claude Code、Cherry Studio 等)的,需要一并改成新地址,否则那些工具会连不上。")]})]}),s.jsx("p",{className:"min-h-[18px] text-[11.5px] text-muted-foreground",children:b})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:t,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:d!=="idle",onClick:()=>{S()},children:xX[d]})]})]})})}let bp=null,Yu=null;function iD(){bp&&(bp.unmount(),bp=null),Yu&&(Yu.remove(),Yu=null)}async function vX(){let e=v0().backend;e||(e=await nl().workbuddyDesktop?.getBackendStatus().catch(()=>null)??null),iD(),Yu=document.createElement("div"),document.body.append(Yu),bp=jn.createRoot(Yu),bp.render(s.jsx(bX,{status:e,onClose:iD}))}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",oD,{once:!0}):oD(),window.wbPortPanel={render:mX,sync:Ku,portLabel:rD,isReady:pX};function nn(){return window}function Es(e,t){nn().wbApp?.toast?.(e,t)}function pr(e){return e instanceof Error?e.message:String(e)}function cD(e){const t=String(e||"").trim();return/^https?:\/\//i.test(t)?t:""}async function uD(e){const t=cD(e);if(!t){Es(r("链接地址不受支持"),"err");return}try{await nn().workbuddyDesktop?.openReleasePage(t)}catch(a){Es(r("打开链接失败:{error}",{error:pr(a)}),"err")}}const lx="1127656296",yX=`assets/qq-group/${lx}.jpg`;function dD(e){return nn().wbMarkdown?.render?.(e)||""}function Xu(e){const a=e.target?.closest?.("[data-external]");a&&(e.preventDefault(),uD(a.dataset.external||""))}async function jX(){try{const e=await nn().workbuddyDesktop?.getUpdateStatus?.();e&&e.checked!==!1&&nn().wbApp?.updateUpdateBadge?.(e)}catch(e){console.warn("读取版本检查结果失败:",pr(e))}}const vp="__proxy_other__",fD={proxyChoice:null,pool:null,poolError:""};async function wX(){const e=nn().workbuddyDesktop,t={...fD};if(!e?.getUpdateProxy)return t;try{t.proxyChoice=(await e.getUpdateProxy())?.proxy??null}catch{}try{if(typeof e.getProxyPool!="function")throw new Error("桥未提供代理池方法");const a=(await e.getProxyPool())?.items;if(!Array.isArray(a))throw new Error("代理池响应异常");t.pool=a}catch(a){t.pool=[],t.poolError=pr(a)}return t}async function SX(e){if(e===vp)return null;const t=nn().workbuddyDesktop;if(!t?.setUpdateProxy)return null;const a=e===""?null:{source:"pool",proxyId:e.slice(Qa.length)};try{const o=await t.setUpdateProxy({proxy:a}),c=o?.proxy??null,d=o?.saved!==!1;return d?Es(c?r("✅ 更新出网已切换为 {label}",{label:c.label||r("指定代理")}):r("✅ 更新出网已切换为直连")):Es(r("出网线路已切换,但写入磁盘失败(重启后会恢复原线路)"),"err"),{choice:c,saved:d}}catch(o){return Es(r("保存失败:{error}",{error:pr(o)}),"err"),null}}function pD(e){const t=e.proxyChoice,a=t?.config?.source||t?.source,o=t?.label||r(a==="custom"?"自定义代理":"已设置"),c=!!t?.error,d=Array.isArray(e.pool)?e.pool:[];let f="";a==="pool"&&t?.config?.proxyId?f=`${Qa}${t.config.proxyId}`:t&&(f=vp);const p=[{value:"",label:r("直连")}];for(const h of d)p.push({value:`${Qa}${h.id}`,label:du(h)});if(e.pool===null?p.push({value:"__hint_pool__",label:r("正在读取代理列表…"),disabled:!0}):d.length||p.push({value:"__hint_pool__",label:e.poolError?r("代理列表读取失败"):r("还没有代理(去「网络代理」页添加)"),disabled:!0}),f===vp){const h=a==="clash"?r("Clash 出口:"):a==="custom"?r("自定义:"):"";p.push({value:vp,label:`${h}${o}${c?r("(不可用)"):""}`})}else a==="pool"&&!d.some(h=>`${Qa}${h.id}`===f)&&p.push({value:f,label:`${o}${c?r("(不可用)"):""}`});return{current:f,items:p,selected:p.find(h=>h.value===f),broken:c,title:c?r("当前线路不可用:{error};请重新选择代理或切回直连",{error:String(t?.error||"")}):r("检查更新与下载安装包走哪条线路(当前:{current});选择即保存。选项来自「网络代理」页;直连失败时会自动借 Clash 混合端口重试一次,选定指定代理后只走它",{current:t?o:r("直连")})}}const Ho="https://proxylane.dev/zh-cn/isp-proxies?utm_source=agent2api&utm_medium=partnership&utm_campaign=agent2api_202610&utm_content=client",NX="assets/proxylane-logo-for-light-bg.png",kX="assets/proxylane-logo-for-dark-bg.png";function mD({children:e}){return s.jsx("a",{className:"font-semibold text-primary-fg no-underline hover:underline",href:Ho,"data-external":Ho,onClick:Xu,children:e})}function CX({item:e,onClose:t}){const a=e!==null,[o,c]=x.useState(e?.name||""),[d,f]=x.useState(e?.protocol==="socks5"?"socks5":"http"),[p,h]=x.useState(e?.host||""),[b,y]=x.useState(e?.port?String(e.port):""),[v,j]=x.useState(e?.username||""),[w,S]=x.useState(e?.password||""),[N,k]=x.useState(e?e.enabled:!0),[A,P]=x.useState(!1),[R,C]=x.useState(!1),[E,D]=x.useState(null);function I(){const z=o.trim();if(!z)return{error:r("请填写代理名称")};const H=p.trim();if(!H)return{error:r("请填写代理主机地址")};const B=Number(b);return!Number.isInteger(B)||B<1||B>65535?{error:r("代理端口必须是 1-65535 的整数")}:{payload:{name:z,source:"manual",protocol:d,host:H,port:B,username:v.trim(),password:w,enabled:N}}}async function O(){if(R)return;const z=I();if("error"in z){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",z.error]}));return}const H=z.payload,B={source:"custom",protocol:H.protocol,host:H.host,port:H.port,username:H.username,password:H.password};C(!0),D(r("正在连接上游…"));try{const L=await tr().workbuddyDesktop?.testProxy?.({proxy:B});if(L?.success){const F=Uj(L.durationMs);D(s.jsxs("span",{className:"text-success",children:[r("✅ 出口可用"),L.ip?r(" 出口 IP {ip}",{ip:L.ip}):"",F?` ${F}`:""]}))}else D(s.jsxs("span",{className:"text-destructive",children:["❌ ",L?.error||r("连接失败")]}))}catch(L){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",Ra(L)]}))}finally{C(!1)}}async function _(){if(A)return;const z=I();if("error"in z){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",z.error]}));return}P(!0),D(null);try{await Mq(e,z.payload),qn(a?r("已保存「{name}」",{name:o.trim()}):r("已新增「{name}」",{name:o.trim()})),t()}catch(H){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",Ra(H)]})),P(!1)}}return s.jsx(Yt,{open:!0,onOpenChange:z=>{!z&&!A&&t()},children:s.jsxs(Xt,{className:"max-w-[560px]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r(a?"编辑代理":"新增代理")})}),s.jsxs(Lt,{className:"flex flex-col gap-3.5",children:[s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-name",children:r("名称")}),s.jsx(Fe,{id:"proxy-name",maxLength:60,autoComplete:"off",spellCheck:!1,placeholder:r("例如:香港 HTTP 出口"),value:o,onChange:z=>c(z.currentTarget.value)})]}),s.jsxs("div",{className:"flex flex-row items-end gap-2.5",children:[s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-protocol",children:r("协议")}),s.jsxs(ut,{value:d,onValueChange:z=>f(z),children:[s.jsx(ft,{id:"proxy-protocol",className:"min-w-[110px]",children:s.jsx(dt,{children:d==="socks5"?"SOCKS5":"HTTP"})}),s.jsxs(pt,{children:[s.jsx($e,{value:"http",children:"HTTP"}),s.jsx($e,{value:"socks5",children:"SOCKS5"})]})]})]}),s.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-host",children:r("主机")}),s.jsx(Fe,{id:"proxy-host",placeholder:"127.0.0.1",autoComplete:"off",spellCheck:!1,value:p,onChange:z=>h(z.currentTarget.value)})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-port",children:r("端口")}),s.jsx(Fe,{id:"proxy-port",type:"number",min:1,max:65535,className:"max-w-[110px]",placeholder:"7890",value:b,onChange:z=>y(z.currentTarget.value)})]})]}),s.jsxs("div",{className:"flex flex-row items-end gap-2.5",children:[s.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-user",children:r("用户名")}),s.jsx(Fe,{id:"proxy-user",placeholder:r("可选"),autoComplete:"off",value:v,onChange:z=>j(z.currentTarget.value)})]}),s.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-pass",children:r("密码")}),s.jsx(Fe,{id:"proxy-pass",placeholder:r("可选"),autoComplete:"off",value:w,onChange:z=>S(z.currentTarget.value)})]})]}),s.jsxs("div",{className:"flex flex-row items-center gap-2.5",children:[s.jsx(ns,{checked:N,onCheckedChange:k,"aria-label":r("启用这条代理")}),s.jsx("span",{className:"text-[12.5px]",children:r("启用(禁用的条目不会被账号使用,被引用时账号回退直连;仍可测试)")})]}),s.jsxs("p",{className:"text-xs leading-[1.65] text-subtle",children:[r("需要干净的出口?"),s.jsx(mD,{children:"ProxyLane"}),r(" AI 账号专用静态住宅 IP:Scamalytics 低欺诈评分,不限流量,首单 75 折(AGENT2API25)")," ",s.jsx(mD,{children:r("去获取")})]}),s.jsx("div",{className:"min-h-[18px] text-xs text-subtle",children:E})]}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"outline",disabled:R||A,onClick:()=>{O()},children:r(R?"测试中…":"测试出口")}),s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",disabled:A,onClick:t,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:A,onClick:()=>{_()},children:r(A?"保存中…":"保存")})]})]})})}function TX({onClose:e}){const[t,a]=x.useState(!0),[o,c]=x.useState(!1),[d,f]=x.useState(""),[p,h]=x.useState(""),[b,y]=x.useState(""),[v,j]=x.useState({}),[w,S]=x.useState(""),[N,k]=x.useState(!1);x.useEffect(()=>{let C=!0;return(async()=>{try{const E=await fO();if(!C||E===null)return;f(String(E.listenerPortBase??"")),h(String(E.listenerPortEnd??"")),y(String(E.testUrl??"")),j(E.defaults??{}),a(!1)}catch(E){if(!C)return;S(Ra(E)),a(!1)}})(),()=>{C=!1}},[]);function A(){const C=Number(d),E=Number(p);if(!Number.isInteger(C)||C<1||C>65535||!Number.isInteger(E)||E<1||E>65535)return S(r("端口范围必须是 1-65535 的整数")),null;if(C>E)return S(r("起始端口不能大于结束端口")),null;const D=b.trim();return D&&!D.startsWith("http://")&&!D.startsWith("https://")?(S(r("测试网址要以 http:// 或 https:// 开头")),null):(S(""),{listenerPortBase:C,listenerPortEnd:E,testUrl:D})}async function P(){if(o||t)return;const C=A();if(C){c(!0);try{await Dq(C),k(!0)}catch(E){S(Ra(E))}finally{c(!1)}}}function R(){f(String(v.listenerPortBase??"")),h(String(v.listenerPortEnd??"")),y(String(v.testUrl??"")),k(!1),S("")}return s.jsx(Yt,{open:!0,onOpenChange:C=>{!C&&!o&&e()},children:s.jsxs(Xt,{className:"max-w-[560px]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("代理设置")})}),s.jsx(Lt,{className:"flex flex-col gap-3.5",children:t?s.jsx("p",{className:"text-xs text-subtle",children:r("加载中…")}):s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"flex flex-row items-end gap-2.5",children:[s.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-settings-base",children:r("节点端口范围(起始)")}),s.jsx(Fe,{id:"proxy-settings-base",type:"number",min:1,max:65535,placeholder:String(v.listenerPortBase??""),autoComplete:"off",value:d,onChange:C=>{f(C.currentTarget.value),k(!1)}})]}),s.jsx("span",{"aria-hidden":"true",className:"flex-none pb-[7px] text-subtle",children:"—"}),s.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-settings-end",children:r("节点端口范围(结束)")}),s.jsx(Fe,{id:"proxy-settings-end",type:"number",min:1,max:65535,placeholder:String(v.listenerPortEnd??""),autoComplete:"off",value:p,onChange:C=>{h(C.currentTarget.value),k(!1)}})]})]}),s.jsx("p",{className:"text-xs leading-[1.65] text-subtle",children:r("订阅节点会在本机各占一个监听端口,从该范围里挑空闲的分配;节点多的大订阅把默认范围({base}-{end})用尽时会报「没有可用的节点端口」,这时把范围调大再重启内核。已分配出去的端口不会因改范围而失效。",{base:v.listenerPortBase??"",end:v.listenerPortEnd??""})}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-settings-url",children:r("出口测试网址")}),s.jsx(Fe,{id:"proxy-settings-url",placeholder:v.testUrl||"",autoComplete:"off",spellCheck:!1,className:"font-mono text-[12px]",value:b,onChange:C=>{y(C.currentTarget.value),k(!1)}})]}),s.jsx("p",{className:"text-xs leading-[1.65] text-subtle",children:r("「测试」与「批量测试」访问的目标;留空用默认(打 WorkBuddy 上游)。判据是能否拿到 HTTP 响应,不用考虑该网址返回什么内容。")}),s.jsxs("div",{className:"min-h-[18px] text-xs",children:[w?s.jsxs("span",{className:"text-destructive",children:["❌ ",w]}):null,!w&&N?s.jsxs("span",{className:"text-success",children:["✅ ",r("已保存。重启内核后,新分配的节点端口才会按新范围来。")]}):null]})]})}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"ghost",disabled:t||o,onClick:R,children:r("恢复默认")}),s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",disabled:o,onClick:e,children:r("关闭")}),s.jsx(ne,{variant:"default",disabled:t||o,onClick:()=>{P()},children:r(o?"保存中…":"保存")})]})]})})}function hD(e){return(Math.max(0,Number(e)||0)/1024/1024).toFixed(1)}function EX(e){return/^[A-Za-z]:[\\/]/.test(e)||e.startsWith("\\\\")||e.startsWith("/")}function AX({onClose:e}){const t=x.useSyncExternalStore(yg,jg),a=Su(),o=a?.download,c=o?.state==="downloading",d=c&&o&&o.total>0?Math.round(o.received/o.total*100):null,[f,p]=x.useState(null),[h,b]=x.useState(""),[y,v]=x.useState(null),[j,w]=x.useState(a?.kernelDir||""),[S,N]=x.useState(!1),[k,A]=x.useState(!1),[P,R]=x.useState("");x.useEffect(()=>{let H=!0;return(async()=>{try{const B=(await nr().workbuddyDesktop?.getProxyPool?.())?.items;if(!Array.isArray(B))throw new Error(r("代理池响应异常"));H&&p(B)}catch(B){if(!H)return;p([]),b(wr(B))}})(),()=>{H=!1}},[]),x.useEffect(()=>{a?.installed&&e()},[a?.installed]);const C=pD({proxyChoice:a?.downloadProxy??null,pool:f,poolError:h}),E=y??C.current,D=C.items.find(H=>H.value===E)??C.selected,I=a?.kernelDirDefault||"";async function O(){if(S)return;const H=j.trim();if(H&&!EX(H)){R(r("存放位置要填绝对路径,例如 D:\\proxy-kernel"));return}R(""),N(!0);try{const B={dir:H};E!==vp&&(B.proxy=E===""?null:{source:"pool",proxyId:E.slice(Qa.length)}),Hq(B)}finally{N(!1)}}async function _(){if(!k){A(!0);try{const H=await Uq();H==="done"?an(r("下载已经完成,停止没赶上(内核已安装)")):H==="none"?an(r("当前没有正在进行的下载"),"err"):H==="stalled"&&an(r("停止请求已发出,但下载还没收手,稍后再看一次进度"),"err")}finally{A(!1)}}}async function z(){const H=nr().workbuddyDesktop;if(typeof H?.pickFolder!="function"){an(r("当前环境不支持选择文件夹,直接填写绝对路径即可"),"err");return}try{const B=await H.pickFolder(j.trim()||I||null);if(B?.unsupported){an(r("当前环境不支持选择文件夹,直接填写绝对路径即可"),"err");return}if(B?.canceled||!B?.path)return;w(B.path)}catch(B){an(r("打开文件夹选择框失败:{reason}",{reason:wr(B)}),"err")}}return s.jsx(Yt,{open:!0,onOpenChange:H=>{H||e()},children:s.jsxs(Xt,{className:"w-[min(520px,calc(100vw-48px))]",children:[s.jsxs(Vt,{children:[s.jsx(Ut,{children:r(c?"正在下载内核":"下载内核")}),s.jsx("span",{className:"text-[11.5px] text-subtle",children:r("内核 {version}({asset})",{version:a?.version||"—",asset:a?.asset||"—"})})]}),c?s.jsxs(Lt,{className:"flex flex-col gap-3",children:[s.jsx(cf,{value:d??null}),s.jsxs("div",{className:"flex items-center justify-between text-[12px]",children:[s.jsx("span",{className:"text-text-2",children:d!==null?r("{percent}%({received} / {total} MB)",{percent:d,received:hD(o?.received),total:hD(o?.total)}):r("正在连接 GitHub…")}),s.jsx("span",{className:"text-subtle",children:r("存放位置 {dir}",{dir:a?.kernelDir||"—"})})]}),s.jsx("p",{className:"text-xs leading-[1.65] text-subtle",children:r("关掉这个窗口不会中断下载(它在后台继续跑)—— 要停下就点下面的「停止下载」,未下载完的临时文件会被删除。")})]}):s.jsxs(Lt,{className:"flex flex-col gap-3.5",children:[s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"kernel-proxy",children:r("下载线路")}),s.jsxs(ut,{value:E,onValueChange:H=>v(String(H)),children:[s.jsx(ft,{id:"kernel-proxy",className:Ue("w-full",C.broken&&"border-destructive-bd"),children:s.jsx(dt,{className:"min-w-0 truncate",children:D?.label||r("直连")})}),s.jsx(pt,{children:C.items.map(H=>s.jsx($e,{value:H.value,disabled:H.disabled,children:H.label},H.value))})]}),s.jsx("div",{className:"text-[12px] text-subtle",children:C.broken?r("当前线路不可用:{error};重新选一条或切回直连",{error:String(a?.downloadProxy?.error||"")}):r("选项来自「网络代理」页(Clash Verge 同步的出口在这里可用;订阅节点要等内核起来才有端口)。选「直连」时连不上会自动借 Clash 混合端口重试一次;选定某条线路后只走它。")})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"kernel-dir",children:r("存放位置")}),s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(Fe,{id:"kernel-dir",className:"min-w-0 flex-1 font-mono text-[12px]",autoComplete:"off",spellCheck:!1,value:j,placeholder:I,onChange:H=>w(H.currentTarget.value)}),s.jsx(ne,{variant:"outline",size:"sm",className:"flex-none",onClick:()=>{z()},children:r("选择目录…")}),I&&j.trim()!==I?s.jsx(ne,{variant:"ghost",size:"sm",className:"flex-none",title:I,onClick:()=>w(I),children:r("恢复默认")}):null]}),s.jsx("div",{className:"text-[12px] text-subtle",children:r("内核二进制 / 配置 / 端口映射都会放在这里,默认 {dir};目录不存在会在下载开始时自动创建。",{dir:I||"—"})})]}),o?.state==="error"&&o.error?s.jsx("span",{className:"text-[12px] text-destructive",children:r("上次下载失败:{reason}",{reason:o.error})}):null,o?.state==="canceled"?s.jsx("span",{className:"text-[12px] text-subtle",children:r("上次下载已停止,未下载完的临时文件已删除")}):null,P?s.jsx("span",{className:"text-[12px] text-destructive",children:P}):null]}),s.jsx(tn,{children:c?s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:e,children:r("后台继续")}),s.jsx(ne,{variant:"destructive",disabled:k,onClick:()=>{_()},children:r(k?"正在停止…":"停止下载")})]}):s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:e,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:S||t.pending,onClick:()=>{O()},children:r(S?"正在开始…":"下载")})]})})]})})}function gD({showPortRange:e=!1}){const t=x.useSyncExternalStore(yg,jg),a=Su(),[o,c]=x.useState(null),[d,f]=x.useState(!1);x.useEffect(()=>{let b=!0;return(async()=>{try{const y=await fO();if(!b||!y)return;const v=Number(y.listenerPortBase),j=Number(y.listenerPortEnd);Number.isFinite(v)&&Number.isFinite(j)&&v>0&&j>0&&c({base:v,end:j})}catch{}})(),()=>{b=!1}},[]);const p=a?.download.state==="downloading",h=p&&a.download.total>0?Math.round(a.download.received/a.download.total*100):null;return s.jsxs("div",{className:"flex flex-col gap-2 rounded-md border border-border bg-surface-2 px-3 py-2",children:[s.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[s.jsx(ze,{variant:a?.running?"default":"secondary",children:a?.running?r("内核运行中"):a?.installed?a.halted?r("内核已手动关闭"):r("内核已停止"):r("内核未安装")}),s.jsxs("span",{className:"text-[12.5px] text-text-2",children:[a?.installed?r("内核 {version}",{version:a.version})+" · ":"",a?.running?r("控制端口 {port} · {n} 个节点监听器",{port:a?.controllerPort??"—",n:a?.listenerCount??0}):a?.installed?a.halted?r("内核已手动关闭(点「启动内核」拉起)"):r("内核已停止,点「启动内核」拉起"):r("按需下载(约 20-40MB)")]}),e&&o?s.jsx("span",{className:"text-[11.5px] text-subtle",children:r("节点端口范围 {base}–{end}",{base:o.base,end:o.end})}):null,s.jsx("div",{className:"ml-auto flex items-center gap-2",children:a?.installed?s.jsxs(s.Fragment,{children:[s.jsx(ne,{size:"sm",variant:"outline",disabled:t.pending||p,onClick:()=>{Vq()},children:a?.running?r("重启内核"):r("启动内核")}),a?.running?s.jsx(ne,{size:"sm",variant:"outline",disabled:t.pending||p,onClick:()=>{Fq()},children:r("关闭内核")}):null,s.jsx(ne,{size:"sm",variant:"destructive",disabled:t.pending||p,onClick:()=>{$q()},children:r("删除内核")})]}):s.jsx(ne,{size:"sm",variant:"outline",disabled:t.pending&&!p,onClick:()=>f(!0),children:r(p?"下载中…":"下载内核")})})]}),p?s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsx(cf,{className:"min-w-[200px] flex-1",value:h??null}),s.jsx("span",{className:"text-[11px] text-subtle",children:h!==null?r("{percent}%({received} / {total} MB)",{percent:h,received:xD(a.download.received),total:xD(a.download.total)}):r("下载中…")})]}):null,a?.lastError?s.jsx("span",{className:"text-[12px] text-destructive",children:a.lastError}):null,d?s.jsx(AX,{onClose:()=>f(!1)}):null]})}function RX({onClose:e}){const t=x.useSyncExternalStore(yg,jg),a=x.useSyncExternalStore(cO,uO),o=Iq(),c=t.data!==null,[d,f]=x.useState(null);return x.useEffect(()=>{Ml()},[]),s.jsxs(Yt,{open:!0,onOpenChange:p=>{p||e()},children:[s.jsxs(Xt,{className:"w-[min(880px,calc(100vw-48px))]",children:[s.jsxs(Vt,{children:[s.jsx(Ut,{children:r("订阅管理")}),s.jsxs("span",{className:"text-[11.5px] text-subtle",children:[c?r("{n} 条订阅",{n:o.length}):r("加载中…")," · ",r("节点由内核起成本地监听器,镜像进代理池")]})]}),s.jsxs(Lt,{className:"flex flex-col gap-3",children:[s.jsx(gD,{}),s.jsx("div",{className:"models-table-wrap",children:s.jsxs("table",{className:"models-table",children:[s.jsxs("colgroup",{children:[s.jsx("col",{className:"w-[18%]"}),s.jsx("col",{className:"w-[31%]"}),s.jsx("col",{className:"w-[9%]"}),s.jsx("col",{className:"w-[14%]"}),s.jsx("col",{className:"w-[8%]"}),s.jsx("col",{className:"w-[20%]"})]}),s.jsx("thead",{children:s.jsxs("tr",{children:[s.jsx("th",{children:r("名称")}),s.jsx("th",{children:r("订阅来源")}),s.jsx("th",{className:"ta-center",children:r("节点")}),s.jsx("th",{children:r("上次同步")}),s.jsx("th",{className:"ta-center",children:r("启用")}),s.jsx("th",{className:"ta-right",children:r("操作")})]})}),s.jsx("tbody",{children:o.length?o.map(p=>{const h=Su()?.providerSync?.[p.id];return s.jsxs("tr",{className:p.enabled?"":"off",children:[s.jsx("td",{children:s.jsx("b",{children:p.name||r("未命名")})}),s.jsx("td",{className:"group",children:s.jsx("div",{className:"flex min-w-0 items-center gap-1",children:p.kind==="local"?s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"min-w-0 truncate font-mono text-[12px] text-text-2",title:p.fileName,children:p.fileName||r("本地 YAML 文件")}),s.jsx(ze,{variant:"secondary",children:r("本地文件")})]}):s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"min-w-0 truncate font-mono text-[12px] text-text-2",title:p.url,children:OX(p.url)}),s.jsx(ne,{variant:"ghost",size:"icon-xs",className:"copy-btn size-[21px] rounded-[var(--r-xs)] opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100","data-copy":p.url,title:r("复制订阅链接"),children:"⧉"})]})})}),s.jsx("td",{className:"ta-center",children:bD(h,p)>0?s.jsx(ze,{variant:"secondary",children:r("{n} 个",{n:bD(h,p)})}):s.jsx("span",{className:"text-subtle",children:r("—")})}),s.jsx("td",{children:h?.at?s.jsx("span",{className:"text-[12px] text-text-2",children:sO(h.at)}):s.jsx("span",{className:"text-subtle",children:r("未同步")})}),s.jsx("td",{className:"ta-center",children:s.jsx(ns,{checked:p.enabled,disabled:t.pending,"aria-label":r("启用订阅「{name}」",{name:p.name||r("未命名")}),onCheckedChange:b=>{Lq(p,b)}})}),s.jsx("td",{children:s.jsxs("div",{className:"row-actions",children:[s.jsx(ne,{size:"sm",variant:"ghost",disabled:t.pending||t.refreshing!==null||p.kind==="local",title:p.kind==="local"?r("本地文件订阅无需更新(要换内容请编辑后重新选择文件)"):void 0,onClick:()=>{zq(p)},children:t.refreshing===p.id?r("更新中…"):r("立即更新")}),s.jsx(ne,{size:"sm",variant:"ghost",disabled:t.pending,onClick:()=>f({item:p}),children:r("编辑")}),s.jsx(ne,{size:"sm",variant:"destructive",disabled:t.pending,onClick:()=>{Bq(p)},children:r("删除")})]})})]},p.id)}):s.jsx("tr",{children:s.jsx("td",{colSpan:6,className:"empty",children:c?s.jsxs("div",{className:"flex flex-col items-center gap-2 py-3",children:[s.jsx("span",{children:r("还没有订阅:粘贴订阅链接、导入本地 YAML 文件,或从图片识别二维码。")}),s.jsx(ne,{size:"sm",variant:"outline",disabled:t.pending,onClick:()=>f({item:null}),children:r("+ 新增订阅")}),s.jsx("span",{className:"text-[11px] text-subtle",children:r("订阅里的节点会自动同步进代理池,账号即可引用。")})]}):r("加载中…")})})})]})}),s.jsx("p",{className:"text-xs leading-[1.65] text-subtle",children:r("订阅里的节点会被内核起成本地监听器,镜像到代理池(账号按 id 引用,与本页其它出口同一用法)。内核只在用到订阅时按需下载(约 20-40MB),不随安装包分发。")}),t.error?s.jsx("span",{className:"text-destructive",children:r("读取失败:{reason}",{reason:t.error})}):null]}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"default",disabled:t.pending,onClick:()=>f({item:null}),children:r("+ 新增订阅")}),s.jsx(ne,{variant:"outline",disabled:a.syncing||a.batchBusy,onClick:()=>{Cq()},children:a.syncing?r("同步中…"):r("同步 Clash Verge")}),s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:e,children:r("完成")})]})]}),d?s.jsx(DX,{item:d.item,onClose:()=>f(null)}):null]})}function xD(e){return!Number.isFinite(e)||e<=0?"—":(e/1e3/1e3).toFixed(1)}function OX(e){try{const t=new URL(e);return t.host+(t.pathname!=="/"?"/…":"")}catch{return e.slice(0,60)}}function bD(e,t){return e?.count??t.nodeCount}function MX(){return s.jsxs("div",{className:"flex flex-col gap-2.5 rounded-lg border border-border bg-surface-2 px-3.5 py-3",children:[s.jsxs("a",{href:Ho,"data-external":Ho,onClick:Xu,className:"inline-flex w-fit items-center no-underline",children:[s.jsx("img",{src:NX,alt:"ProxyLane",className:"h-[24px] w-auto dark:hidden"}),s.jsx("img",{src:kX,alt:"ProxyLane",className:"hidden h-[24px] w-auto dark:block"})]}),s.jsxs("p",{className:"text-xs leading-[1.7] text-text-2",children:[s.jsx("a",{className:"font-semibold text-primary-fg no-underline hover:underline",href:Ho,"data-external":Ho,onClick:Xu,children:"ProxyLane"}),r(" 提供 AI 账号专用的原生静态住宅 IP(Static Residential ISP):IP 来自真实家庭宽带运营商,Scamalytics 低欺诈评分,每个 IP 独享,不限流量,$3.90 起。")]}),s.jsx("p",{className:"text-xs leading-[1.7] text-text-2",children:r("首次购买使用优惠码 AGENT2API25 享 75 折。")}),s.jsx("div",{children:s.jsx(ne,{size:"sm",render:s.jsx("a",{href:Ho,"data-external":Ho,onClick:Xu}),children:r("去获取")})})]})}function DX({item:e,onClose:t}){const a=e!==null,[o,c]=x.useState(e?.kind==="local"?"local":"url"),[d,f]=x.useState(e?.name||""),[p,h]=x.useState(e?.url||""),[b,y]=x.useState(e?.fileName||""),[v,j]=x.useState(""),[w,S]=x.useState(e?e.enabled:!0),[N,k]=x.useState(!1),[A,P]=x.useState(!1),[R,C]=x.useState(!1),[E,D]=x.useState(null),I=x.useRef(null);async function O(){if(N)return;const L=d.trim();if(!L){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",r("请填写订阅名称")]}));return}const F=p.trim();if(o==="url"&&!(F.startsWith("http://")||F.startsWith("https://"))){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",r("订阅链接必须是 http:// 或 https:// 开头")]}));return}if(!a&&o==="local"&&!v){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",r("请选择要导入的 YAML 文件")]}));return}k(!0);try{a&&o==="local"?await mO(e,{name:L,enabled:w,kind:"local",fileName:b||void 0,...v?{contentB64:v}:{}}):a?await mO(e,{name:L,url:F,enabled:w,kind:"url"}):o==="local"?await pO({name:L,kind:"local",fileName:b||void 0,contentB64:v}):await pO({name:L,kind:"url",url:F}),t()}catch($){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",wr($)]})),k(!1)}}async function _(L){if(!A){P(!0),D(r("正在识别二维码…"));try{const F=await qq(L);h(F),D(s.jsxs("span",{className:"text-success",children:["✅ ",r("已识别订阅链接")]}))}catch(F){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",wr(F)]}))}finally{P(!1)}}}async function z(L){if(!R){C(!0),D(null);try{const F=await hO(L);j(F),y(L.name),D(s.jsxs("span",{className:"text-success",children:["✅ ",r("已选择「{name}」,保存后导入",{name:L.name})]}))}catch(F){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",wr(F)]}))}finally{C(!1)}}}function H(L){if(o!=="url")return;const $=Array.from(L.clipboardData?.files||[]).find(X=>X.type.startsWith("image/"));$&&(L.preventDefault(),_($))}function B(L){L!==o&&(c(L),D(null))}return s.jsx(Yt,{open:!0,onOpenChange:L=>{!L&&!N&&t()},children:s.jsxs(Xt,{className:"max-w-[560px]",overlayForceRender:!0,onPaste:H,children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r(a?"编辑订阅":"新增订阅")})}),s.jsxs(Lt,{className:"flex flex-col gap-3.5",children:[o==="residential"?null:s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"sub-name",children:r("名称")}),s.jsx(Fe,{id:"sub-name",maxLength:60,autoComplete:"off",spellCheck:!1,placeholder:r("例如:我的机场"),value:d,onChange:L=>f(L.currentTarget.value)})]}),a?null:s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{children:r("订阅来源")}),s.jsx(En,{options:[{value:"url",label:r("订阅链接")},{value:"local",label:r("本地 YAML 文件")},{value:"residential",label:r("家庭宽带")}],value:o,onValueChange:B,"aria-label":r("订阅来源")})]}),o==="residential"?s.jsx(MX,{}):o==="url"?s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"sub-url",children:r("订阅链接")}),s.jsx(Fe,{id:"sub-url",autoComplete:"off",spellCheck:!1,placeholder:"https://…",value:p,onChange:L=>h(L.currentTarget.value)}),s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(ne,{size:"sm",variant:"outline",disabled:A,onClick:()=>I.current?.click(),children:r(A?"识别中…":"从图片识别二维码")}),s.jsx("span",{className:"text-[11px] text-subtle",children:r("也可以把二维码截图直接粘贴到弹窗里(Ctrl+V)")}),s.jsx("input",{ref:I,type:"file",accept:"image/*",className:"hidden",onChange:L=>{const F=L.currentTarget.files?.[0];F&&_(F),L.currentTarget.value=""}})]})]}):s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"sub-file",children:r("YAML 文件")}),s.jsxs("div",{className:"flex min-w-0 items-center gap-2",children:[s.jsx(ne,{size:"sm",variant:"outline",disabled:R,className:"shrink-0",onClick:()=>I.current?.click(),children:r(R?"读取中…":a?"重新选择文件…":"选择 YAML 文件…")}),s.jsx("span",{className:"min-w-0 truncate font-mono text-[12px] text-text-2",title:b||void 0,children:b||r(a?"保持原文件":"未选择文件")})]}),a?s.jsx("span",{className:"text-[11px] text-subtle",children:r("本地文件不会自动更新:要更新节点,重新选择文件后保存;不重选则内容保持不变。")}):null,s.jsx("input",{ref:I,type:"file",accept:".yaml,.yml,text/yaml,application/yaml,text/plain",className:"hidden",onChange:L=>{const F=L.currentTarget.files?.[0];F&&z(F),L.currentTarget.value=""}})]}),o==="residential"?null:s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"flex flex-row items-center gap-2.5",children:[s.jsx(ns,{checked:w,onCheckedChange:S,"aria-label":r("启用这条订阅")}),s.jsx("span",{className:"text-[12.5px]",children:r("启用(禁用的订阅不参与内核同步,它的节点会从代理池移除)")})]}),s.jsx("p",{className:"text-xs leading-[1.65] text-subtle",children:r(o==="url"?"订阅内容由内核定时刷新(每 24 小时);节点数与同步状态在保存后由轮询刷新,稍等几秒。":"本地文件订阅的内容在导入时固定:节点会由内核起成本地监听器并镜像进代理池,节点数稍等几秒由轮询刷新。")}),s.jsx("div",{className:"min-h-[18px] text-xs text-subtle",children:E})]})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",disabled:N,onClick:t,children:r("取消")}),o==="residential"?null:s.jsx(ne,{variant:"default",disabled:N||A||R,onClick:()=>{O()},children:r(N?"保存中…":"保存")})]})]})})}const w0=[{key:"pick",label:r("选择"),align:"center"},{key:"name",label:r("节点")},{key:"addr",label:r("地址")},{key:"state",label:r("启用"),align:"center"},{key:"test",label:r("连通性")},{key:"act",label:r("操作"),align:"right"}],IX={pick:"p-pick",name:"",addr:"p-addr",state:"p-state",test:"p-test",act:"p-act"},vD="proxies";function S0(){return window}let ox=null,yD=null;function jD(){return ox?ox.apply([...w0]):w0.map(e=>({...e,align:e.align??"left"}))}function PX(){S0().wbTableColumns?.repaint?.(vD)}function _X(e){if(yD=e,ox)return!0;const t=S0().wbColSettings;return t?.register?(ox=t.register({id:vD,label:r("代理池表"),columns:w0.map(a=>({key:a.key,label:a.label,align:a.align??"left"})),mount:()=>document.querySelector('.page[data-page="proxies"] .panel-head .head-actions'),onChange:()=>yD?.()}),!0):!1}function LX(){S0().wbColSettings?.close?.()}function zX(){const e=x.useSyncExternalStore(cO,uO),t=x.useSyncExternalStore(yg,jg).data?.items??[],[a,o]=x.useState(null),[c,d]=x.useState(!1),[f,p]=x.useState(!1),[h,b]=x.useState(""),[y,v]=x.useState("all"),[j,w]=x.useState("all"),[S,N]=x.useState("all"),[k,A]=x.useState(0),[,P]=x.useReducer(W=>W+1,0),R=vg(),C=e.data!==null,E=e.selected.size,D=R.filter(W=>e.selected.has(W.id)&&xg(W)).length,I=e.batchProgress;x.useEffect(()=>{Ml()},[]),x.useLayoutEffect(()=>{if(!_X(()=>P())){const W=window.setTimeout(()=>A(ue=>ue+1),50);return()=>window.clearTimeout(W)}P()},[k]),x.useLayoutEffect(()=>{PX()}),x.useEffect(()=>()=>{LX()},[]);const O=h.trim().toLowerCase(),_=UX(R,t);function z(W){return j==="all"?!0:j==="manual"||j==="clash"||j==="subscription"?sl(W)===j:j===ND?!!W.orphan:W.source==="subscription"&&!W.orphan&&W.providerId===j}function H(W){return!(y!=="all"&&sl(W)!==y||!z(W)||S!=="all"&&SD(W)!==S||O&&![W.name,Yh(W),W.host,W.protocol,String(W.port??"")].join(" ").toLowerCase().includes(O))}function B(W){const ue=W??"all";w(ue),v(ue==="all"?"all":ue==="manual"||ue==="clash"?ue:"subscription")}function L(W){v(W),w("all")}const F=R.filter(H),$=F.map(W=>W.id),X=$.length>0&&$.every(W=>e.selected.has(W)),K=$.some(W=>e.selected.has(W))&&!X,q=jD(),Q=[];for(const W of wD())Q.push(...F.filter(ue=>sl(ue)===W.key));const G=hg(F.length,"proxies"),J=G.paged?G.slice(Q):Q,Y=new Map;for(const W of F)Y.set(sl(W),(Y.get(sl(W))??0)+1);x.useEffect(()=>{G.goto(1)},[O,y,j,S]);function re(W){const ue=W.lastTest;if(!ue||!ue.at)return s.jsx("span",{className:"text-subtle",children:r("未测试")});const ve=Uj(ue.durationMs);return s.jsxs("div",{className:"flex flex-col gap-0.5 leading-[1.5] min-w-0",children:[s.jsxs("span",{className:ue.success?"text-success":"text-destructive",children:[ue.success?r("✅ {ip}",{ip:ue.ip||r("连接成功")}):r("❌ 连接失败"),ve?` ${ve}`:"",s.jsxs("span",{className:"text-[11px] text-subtle",children:[" ",sO(ue.at)]})]}),ue.success?null:s.jsx("span",{className:"overflow-hidden text-ellipsis whitespace-nowrap text-[11px] text-subtle",title:String(ue.error||r("连接失败")),children:String(ue.error||r("连接失败"))})]})}function M(W){const ue=W.usedBy||[],ve=[];return W.protocol&&ve.push(W.protocol),ue.length&&ve.push(s.jsx("span",{className:"text-warn",title:ue.map(ee=>String(ee.name||ee.id||"")).join(r("、")),children:r("{n} 个账号在用",{n:ue.length})})),ve.length?s.jsx("span",{className:"text-[11px] text-subtle",children:ve.map((ee,he)=>s.jsxs(x.Fragment,{children:[he?" · ":"",ee]},he))}):null}function U(W){if(mq(W)&&(W.port===null||W.port===void 0))return s.jsx("span",{className:"text-subtle",children:r("已禁用(未开监听端口)")});if(W.resolveError){const ve=W.source==="subscription"&&(W.port===null||W.port===void 0);return s.jsx("span",{className:ve?"text-subtle":"text-destructive",title:W.resolveError,children:r(ve?"未分配端口":"解析失败")})}if(fq(W)){const ve=Su()?.installed===!1;return s.jsx("span",{className:"text-subtle",title:r(ve?"代理内核未安装(在「订阅管理」里下载内核后,节点会自动分配监听端口)":"还没有监听端口(还没有账号引用它 —— 在账号里选它并保存后,内核会自动开端口)"),children:Yh(W)||r("未分配端口")})}const ue=Yh(W);return s.jsxs("div",{className:"flex min-w-0 items-center gap-1",children:[s.jsx("span",{className:"min-w-0 flex-1 font-mono text-[12px] leading-[1.45] text-text-2 break-all",title:ue||void 0,children:ue||"—"}),ue?s.jsx(ne,{variant:"ghost",size:"icon-xs",className:"copy-btn flex-none","data-copy":ue,title:r("复制地址"),children:"⧉"}):null]})}function Z(W,ue,ve,ee){const he=ve||e.batchBusy;switch(W){case"pick":return s.jsx(Pr,{checked:ee,title:r("勾选后可批量操作"),"aria-label":r("勾选「{name}」",{name:ue.name||r("未命名")}),onCheckedChange:te=>Sq(ue.id,te)});case"name":return s.jsxs("div",{className:"flex flex-col gap-0.5 leading-[1.5] min-w-0",children:[s.jsx("b",{children:ue.name||r("未命名")}),M(ue)]});case"addr":return U(ue);case"state":{const te=aO(ue);return s.jsx(ns,{checked:ue.enabled!==!1,disabled:he||te,"aria-label":r("启用「{name}」",{name:ue.name||r("未命名")}),title:jq(ue),onCheckedChange:ke=>{Eq(ue,ke)}})}case"test":return re(ue);case"act":{const te=bg(ue);xg(ue);const ke=iO(ue),le=e.testing===ue.id;return s.jsxs("div",{className:"row-actions",children:[s.jsx(ne,{size:"sm",variant:"ghost",disabled:he||e.testing!==null,onClick:()=>{Tq(ue)},children:r(le?"测试中…":"测试")}),ue.source!=="clash"&&ue.source!=="subscription"?s.jsxs(s.Fragment,{children:[s.jsx(ne,{size:"sm",variant:"ghost",disabled:he,onClick:()=>o({item:ue}),children:r("编辑")}),s.jsx(ne,{size:"sm",variant:"destructive",disabled:he,onClick:()=>{dO(ue)},children:r("删除")})]}):null,te?s.jsx(ne,{size:"sm",variant:"destructive",disabled:he,title:ke,onClick:()=>{dO(ue)},children:r("删除")}):null]})}default:return null}}function ae(W){const ue=e.pending.has(W.id),ve=wq(W.id);return s.jsx("tr",{className:Ue(W.enabled?"":"off",ve&&"bg-primary-soft"),"data-id":W.id,children:q.map(ee=>s.jsx("td",{className:`ta-${ee.align}`,children:Z(ee.key,W,ue,ve)},ee.key))},W.id)}function ce(){if(!R.length)return s.jsx("tr",{children:s.jsx("td",{colSpan:q.length,className:"empty",children:r(C?"还没有代理。点右上角「+ 新增代理」手填一个,或在「订阅管理」里导入订阅节点、同步 Clash Verge 的出口。":"加载中…")})});if(!F.length)return s.jsx("tr",{children:s.jsx("td",{colSpan:q.length,className:"empty",children:O?r("没有匹配「{kw}」的出口",{kw:h.trim()}):r("没有匹配当前筛选的出口")})});const W=[];let ue="";for(const ve of J){const ee=sl(ve);if(ee!==ue){ue=ee;const he=wD().find(te=>te.key===ee);W.push(s.jsx("tr",{className:"tr-group",children:s.jsxs("td",{colSpan:q.length,children:[s.jsx("span",{className:"prov-tag",children:he?.label??ee}),r("{n} 个",{n:Y.get(ee)??0}),he?.note?s.jsxs("span",{className:"font-normal",children:[" · ",he.note]}):null]})},`group:${ee}:${W.length}`))}W.push(ae(ve))}return s.jsx(s.Fragment,{children:W})}return s.jsx(s.Fragment,{children:s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("代理池")}),s.jsx("span",{className:"panel-sub",children:C?r("{n} 个出口",{n:R.length}):r("加载中…")}),s.jsx("span",{className:"text-[11px] text-subtle",children:r("订阅与 Clash 的出口自动镜像进来")}),s.jsxs("div",{className:"head-actions",children:[s.jsx(ne,{variant:"outline",disabled:e.batchBusy,onClick:()=>d(!0),children:r("设置")}),s.jsx(ne,{variant:"outline",disabled:e.batchBusy,onClick:()=>p(!0),children:t.length?r("订阅管理({n})",{n:t.length}):r("订阅管理")}),s.jsx(ne,{variant:"outline",disabled:e.loading||e.batchBusy,onClick:()=>{Lf(),Ml()},children:e.loading?r("刷新中…"):r("刷新")}),s.jsx(ne,{variant:"default",disabled:e.batchBusy,onClick:()=>o({item:null}),children:r("+ 新增代理")})]})]}),s.jsx("div",{className:"px-3 pt-3",children:s.jsx(gD,{showPortRange:!0})}),E>0?s.jsxs("div",{className:"batch-bar active",children:[s.jsxs("span",{className:"batch-count",children:[r("已选")," ",s.jsx("b",{children:E})," ",r("个 · 共")," ",s.jsx("b",{children:F.length})," ",r("个")]}),D?s.jsx("span",{className:"batch-hidden",children:r("其中 {n} 条是同步来的只读条目(Clash / 订阅):可参与批量测试,删除会跳过",{n:D})}):null,s.jsxs("div",{className:"batch-actions",children:[s.jsx(ne,{variant:"outline",disabled:e.batchBusy,onClick:()=>{Rq()},children:I?r("测试中 {done}/{total}",{done:I.done,total:I.total}):r("批量测试")}),s.jsx(ne,{variant:"destructive",disabled:e.batchBusy,onClick:()=>{Oq()},children:r("批量删除")}),s.jsx(ne,{variant:"outline",disabled:e.batchBusy,onClick:()=>kq(),children:r("取消选择")})]})]}):s.jsxs("div",{className:"flex flex-wrap items-center gap-2.5 border-b border-border px-4 py-[10px]",children:[s.jsxs(Di,{className:"w-[clamp(150px,22vw,220px)]",children:[s.jsx(Pi,{type:"search",value:h,"aria-label":r("搜索节点"),placeholder:r("搜索名称 / 地址 / 端口"),onChange:W=>b(W.currentTarget.value)}),s.jsx(Ii,{align:"inline-start","aria-hidden":"true",children:s.jsx(VX,{})})]}),s.jsxs(ut,{value:j,onValueChange:B,children:[s.jsx(ft,{className:"w-[clamp(150px,18vw,210px)]","aria-label":r("按提供方筛选"),children:s.jsx(dt,{children:_.find(W=>W.value===j)?.label??r("全部提供方")})}),s.jsx(pt,{children:_.map(W=>s.jsx($e,{value:W.value,children:s.jsxs("span",{className:"flex w-full items-center gap-2",children:[s.jsx("span",{className:"min-w-0 flex-1 truncate"+(W.count?"":" text-subtle"),children:W.label}),s.jsx("span",{className:"flex-none text-[11px] text-subtle",children:W.count})]})},W.value))})]}),s.jsx(En,{options:BX(R),value:y,onValueChange:L,"aria-label":r("按来源筛选")}),s.jsx(En,{options:HX(R),value:S,onValueChange:N,"aria-label":r("按状态筛选")}),s.jsx("span",{className:"ml-auto text-[11px] text-subtle",children:r("显示 {n} / 共 {total} 个出口",{n:F.length,total:R.length})})]}),s.jsx("div",{className:"models-table-wrap",children:s.jsxs("table",{className:"models-table proxies-table",children:[s.jsx("colgroup",{children:q.map(W=>s.jsx("col",{"data-col":W.key,className:IX[W.key]||void 0},W.key))}),s.jsx("thead",{children:s.jsx("tr",{children:q.map(W=>s.jsx("th",{"data-col":W.key,className:`ta-${W.align}`,children:W.key==="pick"?s.jsx(Pr,{checked:X,indeterminate:K,disabled:!$.length,title:r("全选当前筛选出的出口"),"aria-label":r("全选当前筛选出的出口"),onCheckedChange:ue=>Nq($,ue)}):W.label},W.key))})}),s.jsx("tbody",{children:ce()})]})}),s.jsx($i,{leading:s.jsxs(s.Fragment,{children:[e.data?.clash&&e.data.clash.available===!1?s.jsx("span",{className:"text-subtle",children:e.data.clash.error?r("未检测到 Clash Verge:{reason}",{reason:e.data.clash.error}):r("未检测到 Clash Verge")}):null,e.error?s.jsx("span",{className:"text-destructive",children:r("读取失败:{reason}",{reason:e.error})}):null]}),total:F.length,page:G.page,pageCount:G.pageCount,size:G.size,onSizeChange:G.setSize,onPageChange:G.goto,range:G.paged?{start:G.rangeStart,end:G.rangeEnd}:null}),a?s.jsx(CX,{item:a.item,onClose:()=>o(null)}):null,c?s.jsx(TX,{onClose:()=>d(!1)}):null,f?s.jsx(RX,{onClose:()=>p(!1)}):null]})})}function sl(e){return e.source==="clash"?"clash":e.source==="subscription"?"subscription":"manual"}function wD(){return[{key:"manual",label:r("手动"),note:r("本页可编辑、可删除")},{key:"clash",label:"Clash Verge",note:r("名字与端口在 Clash Verge 里改;启用可在本页关闭")},{key:"subscription",label:r("订阅"),note:r("名字与端口跟随订阅与内核;启用可在本页关闭")}]}function BX(e){const t=a=>e.filter(o=>sl(o)===a).length;return[{value:"all",label:r("全部"),count:e.length},{value:"manual",label:r("手动"),count:t("manual")},{value:"clash",label:"Clash",count:t("clash")},{value:"subscription",label:r("订阅"),count:t("subscription")}]}function SD(e){return e.enabled===!1?"off":e.port===null||e.port===void 0?"wait":e.lastTest?.at?e.lastTest.success?"ok":"fail":"none"}function HX(e){const t=a=>e.filter(o=>SD(o)===a).length;return[{value:"all",label:r("全部状态"),count:e.length},{value:"ok",label:r("可用"),count:t("ok")},{value:"fail",label:r("失败"),count:t("fail")},{value:"wait",label:r("未就绪"),count:t("wait")},{value:"off",label:r("已禁用"),count:t("off")}]}const ND="__orphan";function UX(e,t){const a=e.filter(d=>sl(d)==="subscription"),o=t.map(d=>({value:d.id,label:d.name||d.id,count:a.filter(f=>!f.orphan&&f.providerId===d.id).length})).sort((d,f)=>f.count-d.count||d.label.localeCompare(f.label)),c=a.filter(d=>d.orphan).length;return[{value:"all",label:r("全部提供方"),count:e.length},{value:"manual",label:r("手动"),count:e.filter(d=>sl(d)==="manual").length},{value:"clash",label:"Clash Verge",count:e.filter(d=>sl(d)==="clash").length},{value:"subscription",label:r("全部订阅"),count:a.length},...o,...c?[{value:ND,label:r("已移除的订阅"),count:c}]:[]]}function VX(){return s.jsxs("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[s.jsx("circle",{cx:"11",cy:"11",r:"8"}),s.jsx("path",{d:"m21 21-4.3-4.3"})]})}window.wbProxiesPanel={load:async()=>{await Lf(),await Ml()},visibleColumns:()=>jD().map(e=>({key:e.key}))};const kD='.page[data-page="proxies"]';function CD(){const e=document.querySelector(kD);!e||e.dataset.mounted==="1"||(e.dataset.mounted="1",e.replaceChildren(),jn.createRoot(e).render(s.jsx(zX,{})),tr().wbApp?.currentPage==="proxies"&&(Lf(),Ml()))}document.querySelector(kD)?CD():document.addEventListener("DOMContentLoaded",CD,{once:!0});const ix=["today","7","30","month","all","custom"],N0="7",k0="custom",TD="workbuddy-desktop-report-range",ED="workbuddy-desktop-report-custom-range",AD={today:r("今天"),7:r("近 7 天"),30:r("近 30 天"),month:r("本月"),all:r("全部"),custom:r("自定义区间")},FX={today:r("今天"),7:r("7 天"),30:r("30 天"),month:r("本月"),all:r("全部"),custom:r("自定义")};function $X(){try{const e=localStorage.getItem(TD);return e!==null&&ix.includes(e)?e:N0}catch{return N0}}function C0(e){const t=a=>String(a).padStart(2,"0");return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())}`}function qX(){const e=new Date;return{start:C0(new Date(e.getFullYear(),e.getMonth(),e.getDate()-6)),end:C0(e)}}const cx=/^\d{4}-\d{2}-\d{2}$/;function GX(){try{const e=localStorage.getItem(ED);if(e){const t=JSON.parse(e),a=typeof t?.start=="string"?t.start:"",o=typeof t?.end=="string"?t.end:"";if(cx.test(a)&&cx.test(o)&&a<=o)return{start:a,end:o}}}catch{}return qX()}function KX(e){try{localStorage.setItem(ED,JSON.stringify(e))}catch{}}const YX=[[0,r("一")],[2,r("三")],[4,r("五")]],RD=400;function sc(e){const t=T0();return t.formatInt?t.formatInt(e):(Number(e)||0).toLocaleString("zh-CN")}function T0(){return window.wbUnits||{}}function os(e){const t=T0();return t.formatTokens?t.formatTokens(e):String(Number(e)||0)}function OD(e,t){const a=T0();return a.formatAxis?a.formatAxis(e,t):String(e)}function rc(e){return`${((Number(e)||0)*100).toFixed(1)}%`}function zt(e){return Math.round(e*10)/10}function ux(e){return[...e].reduce((t,a)=>t+(/[\u2e80-\u9fff]/.test(a)?9.7:5.4),0)}function dx(e){const[t,a,o]=String(e||"").split("-").map(Number);return new Date(t||1970,(a||1)-1,o||1)}const XX=[r("日"),r("一"),r("二"),r("三"),r("四"),r("五"),r("六")];function fx(e){const t=dx(e);return r("{m}月{d}日 周{wd}",{m:t.getMonth()+1,d:t.getDate(),wd:XX[t.getDay()]})}function MD(e){const t=String(e||"").slice(11,13);return t?`${t}:00`:"—"}const QX=8;function DD(e,t){const a=(Array.isArray(e)?e:[]).map(d=>({name:String(d?.label??d?.model??"").trim()||r("未知模型"),tokens:Number(d?.tokens)||0})).filter(d=>d.tokens>0);if(!a.length)return[];const o=a.slice(0,QX),c=o.map(d=>`${t?`[${t(d.name)}]`:""}${r("{model}:{tokens} tokens",{model:d.name,tokens:os(d.tokens)})}`);return a.length>o.length&&c.push(r("…还有 {n} 个模型",{n:a.length-o.length})),c}function ID(e,t){return[fx(e.date),r("总 Token:{tokens}({n} 次请求)",{tokens:os(e.tokens),n:sc(e.requests)}),...DD(e.models,t)].join(` -`)}function ZX(e,t){return[`${fx(String(e.hour).slice(0,10))} ${MD(e.hour)}`,r("命中率 {rate}(命中 {hit} / 输入 {input})",{rate:rc(e.rate),hit:os(e.hitTokens),input:os(e.inputTokens)}),r("总 Token:{tokens}",{tokens:os(e.totalTokens)}),...DD(e.models,t)].join(` -`)}const WX=[1,1.5,2,2.5,3,4,5,6,8,10];function JX(e){if(!(e>0))return 1;const t=10**Math.floor(Math.log10(e)),a=e/t;return(WX.find(c=>c>=a-1e-9)??10)*t}function E0(e){return JX(e/4)*4}function eQ(e,t,a){const o=e||{},c=o.topModel,d=Number(o.requests)||0,f=Number(o.successful)||0,p=d?`${(f/d*100).toFixed(1)}%`:"—",h=[{key:"requests",label:r("总请求数"),value:sc(d),sub:AD[t]||"",mono:!1},{key:"successful",label:r("成功请求数"),value:sc(f),sub:r("成功率 {rate}",{rate:p}),mono:!1},{key:"tokens",label:r("总 Token"),value:os(o.tokens),sub:r("输入 + 输出"),mono:!1},{key:"activeDays",label:r("活跃天数"),value:sc(o.activeDays),sub:r("区间共 {n} 天",{n:a}),mono:!1},{key:"streak",label:r("当前连续天数"),value:sc(o.streak),sub:r("含今天在内往前数"),mono:!1}];return h.push(c?{key:"topModel",label:r("Top 模型"),value:String(c.model??""),sub:r("{tokens} tokens · 占 {pct}",{tokens:os(c.tokens),pct:rc(c.percentage)}),mono:!0}:{key:"topModel",label:r("Top 模型"),value:"—",sub:r("所选范围内还没有模型用量"),mono:!1}),h}function PD(e){if(!Array.isArray(e))return null;const t=e.map(c=>({id:String(c?.id??"").trim(),label:String(c?.label??"").trim(),tokens:Number(c?.totalTokens??c?.tokens)||0})).filter(c=>c.tokens>0);if(!t.length)return[];const a=t.reduce((c,d)=>c+d.tokens,0),o=c=>a?c/a*100:0;return[...t].sort((c,d)=>d.tokens-c.tokens).map((c,d)=>{const f=o(c.tokens),p=os(c.tokens),h=c.label||c.id||r("未知"),b=c.id?r("{name}({id})",{name:h,id:c.id}):h;return{key:`${d}-${c.id||h}`,id:c.id,name:h,full:b,tip:r("{name}:{tokens} tokens · 占 {pct}%",{name:b,tokens:p,pct:f.toFixed(1)}),tokensText:p,percentText:`${f.toFixed(1)}%`,barWidth:`${f.toFixed(1)}%`}})}function _D(e){return`${os(e.reduce((t,a)=>t+(Number(a?.totalTokens??a?.tokens)||0),0))} tokens`}const px=["#f73b00","#f7bb07","#4aaa4d","#1872cb","#3444a3","#13c2c2","#722ed1","#eb2f96","#a0d911","#34d399","#d946ef","#ff85c0","#b37feb","#9d174d","#ffa39e","#69b1ff"],tQ={size:220,inner:.62,outer:.88,padAngle:2};function nQ(e,t,a,o,c,d){const f=P=>(P-90)*Math.PI/180,p=(P,R)=>[zt(e+P*Math.cos(f(R))),zt(t+P*Math.sin(f(R)))],h=d-c;if(h>=359.999){const[P,R]=p(a,0),[C,E]=p(a,180),[D,I]=p(o,0),[O,_]=p(o,180);return`M ${P} ${R} A ${a} ${a} 0 1 1 ${C} ${E} A ${a} ${a} 0 1 1 ${P} ${R} Z M ${D} ${I} A ${o} ${o} 0 1 0 ${O} ${_} A ${o} ${o} 0 1 0 ${D} ${I} Z`}const[b,y]=p(a,c),[v,j]=p(a,d),[w,S]=p(o,d),[N,k]=p(o,c),A=h>180?1:0;return`M ${b} ${y} A ${a} ${a} 0 ${A} 1 ${v} ${j} L ${w} ${S} A ${o} ${o} 0 ${A} 0 ${N} ${k} Z`}const sQ="#9aa0a6";function LD(e){const t=new Map;for(const a of Array.isArray(e)?e:[]){const o=String(a?.label??a?.model??a?.id??"").trim();!o||t.has(o)||(Number(a?.totalTokens??a?.tokens)||0)>0&&t.set(o,px[t.size%px.length])}return t}function zD(e,t){const a=(Array.isArray(e)?e:[]).map(k=>({label:String(k?.label??k?.model??k?.id??"").trim()||t,tokens:Number(k?.totalTokens??k?.tokens)||0,requests:Number(k?.requests)||0})).filter(k=>k.tokens>0);if(!a.length)return null;const o=a.reduce((k,A)=>k+A.tokens,0);if(!o)return null;const c=LD(e),{size:d,inner:f,outer:p,padAngle:h}=tQ,b=d/2,y=d/2,v=d/2*p,j=d/2*f;let w=0;const S=[],N=[];return a.forEach((k,A)=>{const P=k.tokens/o,R=P*360,C=a.length>1?Math.min(h/2,R/4):0,E=w+C,D=w+R-C;w+=R;const I=c.get(k.label)??px[A%px.length];let O=null;if(P>=.03){const _=(E+D)/2,z=j+(v-j)*.6,H=(_-90)*Math.PI/180;O={x:zt(b+z*Math.cos(H)),y:zt(y+z*Math.sin(H)),text:`${Math.round(P*100)}%`}}D>E&&S.push({key:`${A}-${k.label}`,d:nQ(b,y,v,j,E,D),fill:I,evenOdd:R>=359.999,label:O,tip:`${k.label} · ${os(k.tokens)} tokens · ${rc(P)}`}),N.push({key:`${A}-${k.label}`,fill:I,name:k.label,tokensText:os(k.tokens),percentText:rc(P),tip:r("{name} · {tokens} tokens · {n} 次请求 · {pct}",{name:k.label,tokens:os(k.tokens),n:sc(k.requests),pct:rc(P)})})}),{size:d,slices:S,legend:N,totalText:os(o)}}const rQ=[1,1.25,1.5,2,2.5,3,4,5,6,8,10];function aQ(e){if(!(e>0))return 1;const t=10**Math.floor(Math.log10(e)),a=e/t;let o=1;for(const c of rQ)c<=a+1e-9&&(o=c);return o*t}function lQ(e){const t=Number(e)||0;if(t<=0)return[0,1,2,3,1/0];const a=Math.max(1,aQ(t/4));return[0,a,a*2,a*3,1/0]}function oQ(e,t){if(e===0)return"0";const a=t[e-1],o=t[e];return Number.isFinite(o)?a===0?`≤${os(o)}`:`${os(a)}–${os(o)}`:`>${os(a)}`}function BD(e){const t=Array.isArray(e)?e:[];return lQ(t.reduce((a,o)=>Math.max(a,Number(o?.tokens)||0),0))}function iQ(e,t){const a=Array.isArray(e)?e:[];if(!a.length)return null;const o=(dx(a[0].date).getDay()+6)%7,c=new Array(o).fill(null).concat(a),d=Math.ceil(c.length/7),f=3,p=20,h=t-p,b=Math.floor((h-f*(d-1))/d),y=Math.max(7,Math.min(26,b)),v=y+3,j=y+f,w=p+d*j-f,S=Math.max(0,Math.round((t-w)/2)),N=v+7*j-f+2,k=Math.max(w,Math.round(t)),A=BD(a),P=O=>A.findIndex(_=>Number(O)<=_),R=[];for(let O=0;O({key:_,x:S+p-6,y:zt(v+O*j+y/2),text:_})),E=[];let D=-1,I=-9;for(let O=0;O({level:t,text:oQ(t,e)}))}function uQ(e){const t=e||{};return[["last10m",r("近 10 分钟")],["last1h",r("近 1 小时")],["last24h",r("近 24 小时")],["last7d",r("近 7 天")]].map(([o,c])=>{const d=t[o]||{},f=Number(d.inputTokens)||0;return{key:o,label:c,value:f?rc(d.rate):"—",sub:r("命中 {hit} / 输入 {input}",{hit:os(d.hitTokens),input:os(f)})}})}function dQ(e,t,a){const o=Array.isArray(e)?e:[];if(!o.length)return null;const c=230,d={left:44,right:56,top:26,bottom:24},f=Math.max(40,t-d.left-d.right),p=c-d.top-d.bottom,h=o.map(K=>Number(K.rate)||0),b=o.map(K=>Number(K.totalTokens)||0),y=Math.max(1,E0(Math.max(...h))),v=Math.max(...b),j=E0(v),w=v>0,S=K=>d.left+(o.length>1?f*K/(o.length-1):f/2),N=K=>d.top+p-p*K/y,k=K=>d.top+p-p*K/j,A=[],P=[],R=[];for(let K=0;K<=4;K+=1){const q=y*K/4,Q=zt(N(q)),G=q*100;A.push({key:`g${K}`,y:Q}),P.push({key:`r${K}`,x:d.left-8,y:Q,anchor:"end",text:`${Number.isInteger(G)?G:G.toFixed(1)}%`}),w&&R.push({key:`t${K}`,x:zt(d.left+f+8),y:Q,anchor:"start",text:OD(j*K/4,j)})}const C=o.length===1?4.5:2.5,E=(K,q)=>({points:K.map((Q,G)=>`${zt(S(G))},${zt(q(Q))}`).join(" "),dots:K.map((Q,G)=>({key:`d${G}`,cx:zt(S(G)),cy:zt(q(Q))}))}),D=[],I=(K,q,Q,G)=>{const J=ux(Q),Y=G==="middle"?K-J/2:G==="end"?K-J:K,re={left:Y,right:Y+J,top:q-8.5,bottom:q+2.5};return D.some(M=>re.leftM.left-2&&re.topM.top-1)?null:(D.push(re),{key:`${Q}-${K}-${q}`,x:zt(K),y:zt(q),text:Q,anchor:G})},O=(K,q)=>{const Q=ux(q)/2;return K-Qd.left+f+6?"end":"middle"},_=[];w&&o.forEach((K,q)=>{if(!(Number(K.totalTokens)>0))return;const Q=k(b[q]),G=Q-12>=d.top-10,J=os(b[q]),Y=I(S(q),G?Q-12:Q+13,J,O(S(q),J));Y&&_.push({...Y,key:`tl${q}`})});const z=[];o.forEach((K,q)=>{if(!((Number(K.inputTokens)||0)+(Number(K.hitTokens)||0)>0))return;const G=N(h[q]),J=G+15<=d.top+p+8,Y=rc(h[q]),re=I(S(q),J?G+13:G-7,Y,O(S(q),Y));re&&z.push({...re,key:`rl${q}`})});const H=o.length>1?f/(o.length-1):f,B=d.left,L=d.left+f,F=o.length<=RD?o.map((K,q)=>{const Q=Math.max(B,S(q)-H/2),G=Math.min(L,S(q)+H/2);return{key:`h${q}`,x:zt(Q),width:zt(G-Q),tip:ZX(K,a)}}):null,$=Math.max(1,Math.ceil(o.length/8)),X=[];for(let K=0;K=o.length;X.push({key:`x${K}`,x:zt(S(K)),y:c-8,anchor:K===0?"start":q?"end":"middle",text:MD(o[K].hour)})}return{width:t,height:c,plotLeft:B,plotW:f,plotH:p,plotTop:d.top,grid:A,rateTicks:P,tokenTicks:R,rateLine:E(h,N),tokenLine:w?E(b,k):null,dotR:C,rateLabels:z,tokenLabels:_,hourTicks:X,hits:F}}const fQ=13;function pQ(e,t,a){const o=Array.isArray(e)?e:[];if(!o.length)return null;const c=240,d={left:50,right:14,top:26,bottom:26},f=Math.max(40,t-d.left-d.right),p=f/o.length,h=o.map($=>Number($.tokens)>0?os($.tokens):""),b=Math.max(0,...h.map(ux)),y=h.every($=>!$)||b+4<=p,v=!y&&p>=fQ;v&&(d.top=10+Math.ceil(b*1.25));const j=c-d.top-d.bottom,w=o.map($=>Number($.tokens)||0),S=Math.max(...w),N=E0(S),k=Math.max(1,Math.min(28,p*.68)),A=d.top+j,P=$=>d.top+j-j*$/N,R=o.length<=RD,C=S<=0,E=[],D=[];if(!C)for(let $=0;$<=4;$+=1){const X=N*$/4,K=zt(P(X));E.push({key:`g${$}`,x1:d.left,y:K,x2:zt(d.left+f)}),D.push({key:`v${$}`,x:d.left-8,y:K,anchor:"end",text:OD(X,N)})}const I=[];o.forEach(($,X)=>{const K=Number($.tokens)||0;if(K<=0)return;const q=P(K);I.push({key:String($.date??X),x:zt(d.left+p*X+(p-k)/2),y:zt(q),width:zt(k),height:zt(Math.max(1,A-q)),rx:k>4?2:1,tip:ID($),nativeTip:!R})});const O=[],_=[],z=($,X)=>{const K=h[$],q=P(Number(o[$].tokens)||0),Q=zt(d.left+p*$+p/2);if(v)return{key:`b${$}`,x:Q,y:zt(q-6),anchor:"start",text:K,rotate:!0};const G=q-6,J=G-9M.leftU.left-2&&M.topU.top-1))return null;_.push(M)}return{key:`b${$}`,x:Q,y:Y,anchor:"middle",text:K}};if(y||v)h.forEach(($,X)=>{if(!$)return;const K=z(X,!1);K&&O.push(K)});else{const $=o.map((X,K)=>({index:K,value:Number(X.tokens)||0})).filter(X=>X.value>0).sort((X,K)=>K.value-X.value);for(const X of $){if(O.length>=8)break;const K=z(X.index,!0);K&&O.push(K)}}const H=R?o.map(($,X)=>({key:`h${X}`,x:zt(d.left+p*X),width:zt(p),tip:ID($,a)})):null,B=Math.max(1,Math.ceil(o.length/6)),L=[];for(let $=0;${const X=o.length===1,K=!X&&$===o.length-1,q=dx(o[$].date);return{key:`d${$}`,x:X?d.left+f/2:K?d.left+f:zt(d.left+p*$+p/2),y:c-8,anchor:X?"middle":K?"end":$===0?"start":"middle",text:`${q.getMonth()+1}/${q.getDate()}`}});return{width:t,height:c,plotLeft:d.left,plotW:f,plotH:j,plotTop:d.top,grid:E,valueTicks:D,bars:I,barLabels:O,dateTicks:F,hits:H,blank:C}}function ac(){return window}const HD=1e3,mQ=ix.map(e=>({value:e,label:FX[e]})),hQ=r("所选范围内每个账号各消耗了多少 Token,按 Token 用量从多到少排。账号是比提供商更细的一维:同一家可以挂多个账号,转发时按优先级在候选链上依次尝试,所以这里反映的是「具体哪个登录态在出力」。百分比按本区间的总 Token 数算(这一块只看用量,请求次数与成功率在概览卡片里)。「未知账号」是指那批请求没有账号身份:走默认登录态转发(未配置账号列表)时会这样;这一维上线前写出的旧聚合行也没有账号明细,但它们会在升级导入旧数据时按明细自动补算回来 —— 只有明细已被手动清理掉的那几天补不回来,仍留在「未知账号」里,那是确实无从恢复的部分。"),gQ=r("所选范围内每个提供商各消耗了多少 Token,按 Token 用量从多到少排。同一个模型名可能有多家都能提供,网关按设置页「转发路由」的优先级依次尝试,所以占比也反映了实际落到谁身上。百分比按本区间的总 Token 数算(这一块只看用量,请求次数与成功率在概览卡片里)。「未知」是指那批请求没记下承载它的提供商 —— 主要是记录提供商这个功能上线之前的旧明细,其次是请求在选定上游之前就失败了(请求体非法、模型不存在、没有可用账号)。这一维与账号那一维不同:旧明细里根本没有「哪家承载」的信息(早期版本只有单一上游,但那是配置事实而非逐条记录),所以无法回算,旧数据会一直留在「未知」里;此后新产生的请求都会记上具体的提供商。"),xQ=r("所选范围内每个模型各消耗了多少 Token,按用量从多到少排,扇区角度即占比。中心是这一维的合计(等于概览里的总 Token),右侧每行给出该模型的 Token 用量与百分比。扇区内的百分比只在 ≥3% 时标出,更小的会挤成一团 —— 完整读数在右侧列表与悬停气泡里。颜色按用量排名分配:最大的那段是红色,依次往后;同一份报表里换个维度看,配色口径不变。"),bQ=r("所选范围内每个提供商各消耗了多少 Token,按用量从多到少排,扇区角度即占比。同一个模型名可能有多家都能提供,网关按设置页「转发路由」的优先级依次尝试,所以占比也反映了实际落到谁身上。中心是这一维的合计(等于概览里的总 Token),右侧每行给出该提供商的 Token 用量与百分比。「未知」是指那批请求没记下承载它的提供商 —— 主要是记录提供商这个功能上线之前的旧明细,其次是请求在选定上游之前就失败了。"),vQ=r("命中率 = 缓存读取 Token / 输入 Token(promptTokens)。窗口越短越贴近「此刻」:上游把缓存读取与输入分开上报时,该比值合法地可能超过 100%,此处不夹取值、如实展示。"),yQ=r("每天的总 Token 用量(输入 + 输出)。柱顶标出当天的读数;区间很长、柱子挤到放不下时只标最高的那几天,其余悬停柱子即可读到。");let mx=HD,hx=!0,A0=!1,R0=!1,O0=0,Uo=null,lc=null,gx=null;function oc(e,t="empty"){return s.jsx("div",{className:t,style:{padding:"14px 0"},children:e})}function xx({text:e}){return s.jsx("span",{className:"tip-q","data-tip":e})}function jQ({accountId:e}){const t=(ac().wbApp?.getState?.()?.accounts?.accounts||[]).find(f=>f?.id===e),a=typeof t?.provider=="string"?t.provider:"";if(!a)return null;const o=ac().wbProviders?.labelOf?.(a)||a,c=t&&ac().wbAccountsModel?.editionSuffix?.(t)||"",d=c?`${o} ${c}`:o;return s.jsx("span",{className:`pbadge p-${a}`,title:r("提供商:{text}",{text:d}),children:d})}function wQ({summary:e,range:t}){const a=Array.isArray(e.dailyTrend)?e.dailyTrend:[];return s.jsx(s.Fragment,{children:eQ(e.overview,t,a.length).map(o=>s.jsxs("div",{className:"field",children:[s.jsx("div",{className:"label",children:o.label}),s.jsxs("div",{className:o.mono?"value mono":"value",children:[o.value,o.sub?s.jsx("div",{className:"sub",children:o.sub}):null]})]},o.key))})}function UD({panelId:e,listId:t,labelId:a,title:o,tip:c,rows:d,totalText:f,withBadge:p,emptyText:h}){return s.jsxs("section",{className:"panel",id:e,children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:o}),s.jsx(xx,{text:c}),s.jsx("span",{className:"panel-sub",id:a,children:f})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{className:"rank-shares",id:t,children:d.length?d.map(b=>s.jsxs("div",{className:"rank-row","data-tip":b.tip,children:[s.jsxs("span",{className:"name",title:b.full,children:[p?s.jsx(jQ,{accountId:b.id}):null,b.name]}),s.jsx("span",{className:"track",children:s.jsx("span",{className:"bar",style:{width:b.barWidth}})}),s.jsx("span",{className:"num",title:`${b.tokensText} tokens`,children:b.tokensText}),s.jsx("span",{className:"pct",children:b.percentText})]},b.key)):oc(h)})})]})}function VD({panelId:e,listId:t,title:a,tip:o,ariaLabel:c,view:d,emptyText:f}){return s.jsxs("section",{className:"panel",id:e,children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:a}),s.jsx(xx,{text:o})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{id:t,children:d?s.jsxs("div",{className:"donut-layout",children:[s.jsxs("div",{className:"donut-wrap",children:[s.jsx("svg",{className:"donut-svg",width:d.size,height:d.size,viewBox:`0 0 ${d.size} ${d.size}`,role:"img","aria-label":c,children:d.slices.map(p=>s.jsxs(x.Fragment,{children:[s.jsx("path",{className:"donut-slice",d:p.d,fill:p.fill,fillRule:p.evenOdd?"evenodd":void 0,tabIndex:-1,"data-tip":p.tip}),p.label?s.jsx("text",{className:"donut-label",x:p.label.x,y:p.label.y,textAnchor:"middle",dominantBaseline:"central",children:p.label.text}):null]},p.key))}),s.jsxs("div",{className:"donut-center",children:[s.jsx("div",{className:"donut-center-value",children:d.totalText}),s.jsx("div",{className:"donut-center-label",children:r("总 Token")})]})]}),s.jsx("div",{className:"donut-legend",children:d.legend.map(p=>s.jsxs("div",{className:"donut-legend-row","data-tip":p.tip,children:[s.jsx("span",{className:"donut-dot",style:{background:p.fill}}),s.jsx("span",{className:"donut-legend-name",title:p.name,children:p.name}),s.jsx("span",{className:"donut-legend-tokens",children:p.tokensText}),s.jsx("span",{className:"donut-legend-percent",children:p.percentText})]},p.key))})]}):oc(f)})})]})}function SQ({view:e}){return s.jsxs("svg",{className:"report-svg",width:e.width,height:e.height,viewBox:`0 0 ${e.width} ${e.height}`,role:"img","aria-label":r("近 365 天活跃热力图"),children:[e.months.map(t=>s.jsx("text",{className:"hm-axis",x:t.x,y:10,children:t.text},t.key)),e.weekdays.map(t=>s.jsx("text",{className:"hm-axis",x:t.x,y:t.y,textAnchor:"end",dominantBaseline:"middle",children:t.text},t.key)),e.cells.map(t=>s.jsx("rect",{className:`hm-cell l${t.level}`,x:t.x,y:t.y,width:t.size,height:t.size,rx:2,tabIndex:-1,"data-tip":t.tip},t.key))]})}function FD({line:e,cls:t,radius:a}){return s.jsxs(s.Fragment,{children:[s.jsx("polyline",{className:`chart-line ${t}`,points:e.points}),e.dots.map(o=>s.jsx("circle",{className:`chart-dot ${t}`,cx:o.cx,cy:o.cy,r:a},o.key))]})}function M0({items:e,cls:t}){return s.jsx(s.Fragment,{children:e.map(a=>s.jsx("text",{className:`chart-label ${t}`,x:a.x,y:a.y,textAnchor:a.anchor,transform:a.rotate?`rotate(-90 ${a.x} ${a.y})`:void 0,dominantBaseline:a.rotate?"central":void 0,children:a.text},a.key))})}function NQ({view:e}){const{width:t,height:a,plotLeft:o,plotW:c,plotTop:d,plotH:f}=e;return s.jsxs(s.Fragment,{children:[s.jsxs("svg",{className:"report-svg",width:t,height:a,viewBox:`0 0 ${t} ${a}`,role:"img","aria-label":r("近 24 小时缓存命中率与总 Token 趋势"),children:[e.grid.map(p=>s.jsx("line",{className:"chart-grid",x1:o,y1:p.y,x2:zt(o+c),y2:p.y},p.key)),e.rateTicks.map(p=>s.jsx("text",{className:"chart-axis",x:p.x,y:p.y,textAnchor:p.anchor,dominantBaseline:"middle",children:p.text},p.key)),e.tokenTicks.map(p=>s.jsx("text",{className:"chart-axis",x:p.x,y:p.y,textAnchor:p.anchor,dominantBaseline:"middle",children:p.text},p.key)),e.tokenLine?s.jsx(FD,{line:e.tokenLine,cls:"tokens",radius:e.dotR}):null,s.jsx(FD,{line:e.rateLine,cls:"rate",radius:e.dotR}),s.jsx(M0,{items:e.tokenLabels,cls:"tokens"}),s.jsx(M0,{items:e.rateLabels,cls:"rate"}),e.hourTicks.map(p=>s.jsx("text",{className:"chart-axis",x:p.x,y:p.y,textAnchor:p.anchor,children:p.text},p.key))]}),e.hits?s.jsx("svg",{className:"report-svg chart-hit-layer",width:t,height:a,viewBox:`0 0 ${t} ${a}`,"aria-hidden":"true",children:e.hits.map(p=>s.jsx("rect",{className:"chart-hit",x:p.x,y:d,width:p.width,height:zt(f),tabIndex:-1,"data-tip":p.tip,"data-tip-variant":"plain"},p.key))}):null]})}function kQ({view:e}){const{width:t,height:a,plotLeft:o,plotW:c,plotTop:d,plotH:f}=e;return s.jsxs(s.Fragment,{children:[s.jsxs("svg",{className:"report-svg",width:t,height:a,viewBox:`0 0 ${t} ${a}`,role:"img","aria-label":r("按天 Token 趋势"),children:[e.grid.map(p=>s.jsx("line",{className:"chart-grid",x1:p.x1,y1:p.y,x2:p.x2,y2:p.y},p.key)),e.valueTicks.map(p=>s.jsx("text",{className:"chart-axis",x:p.x,y:p.y,textAnchor:p.anchor,dominantBaseline:"middle",children:p.text},p.key)),e.bars.map(p=>p.nativeTip?s.jsx("rect",{className:"bar",x:p.x,y:p.y,width:p.width,height:p.height,rx:p.rx,children:s.jsx("title",{children:p.tip})},p.key):s.jsx("rect",{className:"bar",x:p.x,y:p.y,width:p.width,height:p.height,rx:p.rx},p.key)),s.jsx(M0,{items:e.barLabels,cls:"tokens"}),e.dateTicks.map(p=>s.jsx("text",{className:"chart-axis",x:p.x,y:p.y,textAnchor:p.anchor,children:p.text},p.key)),e.blank?s.jsx("text",{className:"chart-empty",x:zt(o+c/2),y:zt(d+f/2),textAnchor:"middle",children:r("所选范围内暂无请求")}):null]}),e.hits?s.jsx("svg",{className:"report-svg chart-hit-layer",width:t,height:a,viewBox:`0 0 ${t} ${a}`,"aria-hidden":"true",children:e.hits.map(p=>s.jsx("rect",{className:"chart-hit",x:p.x,y:d,width:p.width,height:zt(f),tabIndex:-1,"data-tip":p.tip,"data-tip-variant":"plain"},p.key))}):null]})}const CQ=680;function D0(){const e=x.useRef(null),[t,a]=x.useState(0);return x.useLayoutEffect(()=>{const o=e.current;if(!o)return;let c=o.clientWidth;if(c&&a(c),typeof ResizeObserver!="function")return;const d=new ResizeObserver(()=>{const f=o.clientWidth;!f||f===c||(c=f,a(f))});return d.observe(o),()=>d.disconnect()},[]),[e,t||CQ]}async function $D(){if(!A0)try{const e=await ac().workbuddyDesktop?.getScheduledTasks(),t=(e?.tasks||[]).find(a=>a.id==="reportAutoRefresh");I0(t||null),A0=Array.isArray(e?.tasks)}catch(e){console.warn("读取报表自动刷新间隔失败,按默认 1 秒:",e instanceof Error?e.message:e),I0(null)}}function I0(e){A0=!0;const t=Number(e?.interval);!!e&&typeof e=="object"&&Number.isFinite(t)&&t>0&&(e.unit==="seconds"||e.unit==="minutes")?(hx=e.enabled!==!1,mx=e.unit==="minutes"?t*6e4:t*1e3):(hx=!0,mx=HD),lc?.applyAutoConfig(mx,hx)}const TQ={summary:null,error:null};function EQ(){const[e,t]=x.useState(TQ),[a,o]=x.useState($X),[c,d]=x.useState(GX),[f,p]=x.useState(()=>({ms:mx,enabled:hx})),[,h]=x.useState(0),b=x.useRef(a),y=x.useRef(c),v=x.useRef(""),j=x.useCallback((Y,re)=>{v.current=re,t(Y)},[]),w=x.useCallback(async(Y={})=>{$D();const re=++O0,M=b.current,U=M===k0?y.current:void 0;try{const Z=ac().workbuddyDesktop;if(!Z)throw new Error("后端桥不可用");const ae=await Z.getStatsSummary(M,U);if(re!==O0)return Uo;if(!ae||typeof ae!="object")throw new Error("后端未返回报表数据");Uo=ae;const ce=JSON.stringify(ae);return ce!==v.current&&j({summary:ae,error:null},ce),Uo}catch(Z){if(re!==O0)return Uo;const ae=Z instanceof Error?Z.message:"";return Uo=null,Y.silent||console.warn("读取报表数据失败:",ae),j({summary:null,error:ae||r("未知错误")},""),null}},[j]),S=x.useCallback(Y=>{Y!==void 0&&(Uo=Y||null,Y&&j({summary:Y,error:null},JSON.stringify(Y)))},[j]),N=x.useCallback(Y=>{const re=ix.includes(Y)?Y:N0;if(re!==b.current){b.current=re,o(re);try{localStorage.setItem(TD,re)}catch{}w()}},[w]),k=x.useCallback(Y=>{const re={...y.current,...Y};if(!cx.test(re.start)||!cx.test(re.end))return;const M=re.start<=re.end?re:Y.start?{start:re.start,end:re.start}:{start:re.end,end:re.end};y.current=M,d(M),KX(M),b.current===k0&&w()},[w]);x.useEffect(()=>{if(lc={load:w,render:S,applyAutoConfig:(Y,re)=>p({ms:Y,enabled:re})},gx){const Y=gx;gx=null,S(Y.data)}return ac().wbApp?.currentPage==="overview"&&w({silent:!0}),()=>{lc=null}},[w,S]),x.useEffect(()=>{if(!f.enabled||f.ms<=0)return;const Y=window.setInterval(()=>{document.hidden||ac().wbApp?.currentPage!=="overview"||R0||(R0=!0,w({silent:!0}).finally(()=>{R0=!1}))},f.ms);return()=>{window.clearInterval(Y)}},[f,w]),x.useEffect(()=>{const Y=()=>h(re=>re+1);return window.addEventListener("wb-units-changed",Y),()=>window.removeEventListener("wb-units-changed",Y)},[]);const A=D0(),P=D0(),R=D0(),{summary:C,error:E}=e,D=C&&ix.includes(C.range??"")?C.range:a,I=C&&Array.isArray(C.heatmap)?C.heatmap:[],O=C&&Array.isArray(C.cacheTrend24h)?C.cacheTrend24h:[],_=C&&Array.isArray(C.dailyTrend)?C.dailyTrend:[],z=C&&Array.isArray(C.providers)?C.providers:null,H=C&&Array.isArray(C.accounts)?C.accounts:null,B=C&&Array.isArray(C.models)?C.models:null,L=E?oc(r("读取报表失败:{error}",{error:E}),"empty report-error"):null,F=oc(r("正在加载…")),$=L??F,X=x.useMemo(()=>LD(B||[]),[B]),K=x.useCallback(Y=>X.get(Y)||sQ,[X]),q=C?iQ(I,A[1]):null,Q=C?dQ(O,P[1],K):null,G=C?pQ(_,R[1],K):null,J=C0(new Date);return s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("统计概览")}),s.jsx("span",{className:"panel-sub",id:"report-range-label",children:AD[D]||""}),s.jsxs("div",{className:"head-actions",children:[s.jsx(En,{options:mQ,value:a,onValueChange:N,"aria-label":r("报表时间范围")}),a===k0?s.jsxs("span",{className:"report-date-range",children:[s.jsx(Fe,{type:"date",className:"report-date-input w-[132px]",value:c.start,max:c.end||J,"aria-label":r("开始日期"),title:r("起始日期(含当天)"),onChange:Y=>k({start:Y.target.value})}),s.jsx("span",{className:"report-date-sep","aria-hidden":"true",children:"~"}),s.jsx(Fe,{type:"date",className:"report-date-input w-[132px]",value:c.end,min:c.start||void 0,max:J,"aria-label":r("结束日期"),title:r("结束日期(含当天)"),onChange:Y=>k({end:Y.target.value})})]}):null]})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{className:"field-grid",id:"report-overview",children:C?s.jsx(wQ,{summary:C,range:D}):$})})]}),s.jsxs("div",{className:"report-rank-grid",children:[H?s.jsx(UD,{panelId:"report-accounts-panel",listId:"report-accounts",labelId:"report-accounts-label",title:r("Top 账号"),tip:hQ,rows:PD(H)||[],totalText:_D(H),withBadge:!0,emptyText:r("所选范围内还没有账号用量")}):null,z?s.jsx(UD,{panelId:"report-providers-panel",listId:"report-providers",labelId:"report-providers-label",title:r("Top 提供商"),tip:gQ,rows:PD(z)||[],totalText:_D(z),withBadge:!1,emptyText:r("所选范围内还没有请求记录")}):null]}),s.jsxs("div",{className:"report-donut-grid",children:[B?s.jsx(VD,{panelId:"report-models-panel",listId:"report-models-donut",title:r("模型用量"),tip:xQ,ariaLabel:r("模型用量占比"),view:zD(B,r("未知模型")),emptyText:r("所选范围内还没有模型用量")}):null,z?s.jsx(VD,{panelId:"report-providers-pie-panel",listId:"report-providers-donut",title:r("提供商用"),tip:bQ,ariaLabel:r("提供商用占比"),view:zD(z,r("未知")),emptyText:r("所选范围内还没有提供商用量")}):null]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("活跃热力图")}),s.jsx("span",{className:"panel-sub",children:r("近 365 天,按天 Token 用量着色(档位按本窗口峰值自动定)")})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{className:"heat-wrap",id:"report-heatmap",ref:A[0],children:C?q?s.jsx(SQ,{view:q}):oc(r("暂无热力图数据")):$})}),s.jsxs("div",{className:"panel-foot",children:[s.jsx("span",{className:"heat-scale-label",children:r("Token")}),s.jsx("span",{className:"heat-legend",id:"report-heat-legend",children:C?cQ(BD(I)).map(Y=>s.jsxs("span",{className:"heat-legend-item",children:[s.jsx("span",{className:`l${Y.level}`}),Y.text]},Y.level)):null}),s.jsx("div",{className:"spacer"}),s.jsx("span",{children:r("方格为本地自然日,无请求显示为空槽")})]})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("缓存命中率")}),s.jsx(xx,{text:vQ})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{className:"field-grid",id:"report-cache-rates",children:C?uQ(C.cacheRates).map(Y=>s.jsxs("div",{className:"field rate",children:[s.jsx("div",{className:"label",children:Y.label}),s.jsxs("div",{className:"value big",children:[Y.value,s.jsx("div",{className:"sub",children:Y.sub})]})]},Y.key)):$})})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("近 24 小时缓存命中率趋势")}),s.jsx("span",{className:"panel-sub",children:r("按本地整点,无请求的整点不标注")})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{className:"chart-wrap",id:"report-cache-trend",ref:P[0],children:C?Q?s.jsx(NQ,{view:Q}):oc(r("暂无缓存趋势数据")):$})}),s.jsxs("div",{className:"panel-foot",children:[s.jsxs("span",{className:"chart-legend",children:[s.jsx("span",{className:"swatch rate"}),r("缓存命中率"),s.jsx("span",{className:"axis-hint",children:r("左轴")})]}),s.jsxs("span",{className:"chart-legend",children:[s.jsx("span",{className:"swatch tokens"}),r("总 Token"),s.jsx("span",{className:"axis-hint",children:r("右轴")})]}),s.jsx("div",{className:"spacer"}),s.jsx("span",{children:r("无请求的整点按 0% 计,但不写读数")})]})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("按天 Token 趋势")}),s.jsx(xx,{text:yQ}),s.jsx("span",{className:"panel-sub",id:"report-trend-label",children:C?`${C.startDate||"—"} ~ ${C.endDate||"—"}`:"—"})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{className:"chart-wrap tall",id:"report-daily-trend",ref:R[0],children:C?G?s.jsx(kQ,{view:G}):oc(r("暂无趋势数据")):$})})]})]})}async function AQ(e={}){return lc?lc.load(e):Uo}function RQ(e){if(e!==void 0){if(!lc){gx={data:e??null};return}lc.render(e)}}function OQ(){return Uo}window.wbReport={load:AQ,render:RQ,lastSummary:OQ,applyAutoRefresh:I0};const qD='.page[data-page="overview"]';let GD=!1;function KD(){if(GD)return;const e=document.querySelector(qD);e&&(GD=!0,e.replaceChildren(),jn.createRoot(e).render(s.jsx(EQ,{})))}document.querySelector(qD)?KD():document.addEventListener("DOMContentLoaded",KD,{once:!0}),$D();const P0={all:{title:r("全部删除"),desc:r("删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。")},raw:{title:r("仅清空报文原文"),desc:r("保留统计行与报表,只抹掉请求 / 响应正文。清理后详情弹窗的「预览对话」将无原文可看,统计数字分毫不动。")}},MQ=["all","raw"],DQ=3e3;function Pl(){return window}let yp=!1;function jp(e,t){Pl().wbApp?.toast?.(e,t)}function IQ(e){const t=Math.max(0,Number(e)||0);if(t<1024)return`${Math.round(t)} B`;const a=t/1024;if(a<1024)return`${a.toFixed(1)} KB`;const o=a/1024;return o<1024?`${o.toFixed(1)} MB`:`${(o/1024).toFixed(2)} GB`}function _0(){const e=Pl().wbRequestsPanel?.clearParams?.();return typeof e=="string"?e:""}function YD(e){return{all:Number(e?.all)||0,raw:Number(e?.raw)||0,dbBytes:Number(e?.dbBytes)||0,vacuumRunning:e?.vacuumRunning===!0}}function L0(e){return e instanceof Error?e.message:String(e)}function XD(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t]??t)}function PQ({onClose:e}){const[t,a]=x.useState("all"),[o,c]=x.useState({status:"loading"}),[d,f]=x.useState(""),[p,h]=x.useState(!1),[b,y]=x.useState(!1),v=x.useRef(!0),j=x.useRef(null),w=x.useCallback(D=>{yp=D,y(D)},[]),S=x.useCallback(()=>{j.current!==null&&(window.clearInterval(j.current),j.current=null)},[]),N=x.useCallback(D=>{c({status:"ready",all:D.all,raw:D.raw,dbBytes:D.dbBytes}),h(D.vacuumRunning)},[]),k=x.useCallback(async()=>{const D=Pl().workbuddyDesktop;if(!D)return;let I;try{I=await D.getStatsClearPreview(_0())}catch{return}if(!v.current)return;const O=YD(I);N(O),!O.vacuumRunning&&(S(),f(""),jp(r("✅ 压缩完成")),Pl().wbRequestsPanel?.load?.({silent:!0}))},[N,S]),A=x.useCallback(()=>{j.current===null&&(h(!0),j.current=window.setInterval(()=>{k()},DQ))},[k]);async function P(){c({status:"loading"});try{const D=Pl().workbuddyDesktop;if(!D)throw new Error(r("后端桥不可用"));const I=await D.getStatsClearPreview(_0());if(!v.current)return;const O=YD(I);N(O),O.vacuumRunning&&A()}catch(D){if(!v.current)return;c({status:"failed"}),f(r("预览读取失败:{error}",{error:L0(D)}))}}x.useEffect(()=>(v.current=!0,P(),()=>{v.current=!1,S(),yp=!1}),[]);async function R(){const D=Pl().workbuddyDesktop;if(D){h(!0);try{await D.compactStatsDb(),f(r("压缩已在后台开始,完成后会自动提示")),A()}catch(I){const O=L0(I);if(/409|正在进行/.test(O)){jp(r("压缩已在进行中")),f(r("压缩已在进行中,接上它的进度等待完成")),A();return}h(!1),f(r("压缩失败:{message}",{message:O})),jp(r("压缩失败:{message}",{message:O}),"err")}}}async function C(){if(yp)return;const D=Pl().workbuddyDesktop,I=Pl().wbConfirm?.ask;if(!D||!I)return;const O=P0[t].title,_=_0(),z=_.length>0,H=o.status==="ready"?t==="raw"?o.raw:o.all:0;if(await I({title:r("清理请求日志"),html:z?r("确定按当前筛选执行「{label}」?将处理 {count} 条,此操作无法恢复。",{label:XD(O),count:H}):r("确定对全部请求日志执行「{label}」?将处理 {count} 条,此操作无法恢复。",{label:XD(O),count:H}),okText:r(t==="raw"?"抹掉原文":"删除"),okClass:"danger"})){w(!0);try{const L=z?`mode=${t}&${_}`:`mode=${t}&all=1`,F=await D.clearStatsRequests(L),$=Number(F?.deleted)||0;jp(t==="raw"?r("已抹掉 {count} 条报文原文",{count:$}):r("已删除 {count} 条",{count:$})),w(!1),e(),Pl().wbRequestsPanel?.notifyCleared?.()}catch(L){const F=L0(L);f(r("清理失败:{message}",{message:F})),jp(r("清理失败:{message}",{message:F}),"err"),w(!1)}}}const E=o.status==="ready"?r("将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}",{count:o.all,raw:o.raw,db:IQ(o.dbBytes)}):o.status==="failed"?r("预览读取失败"):r("正在统计…");return s.jsx(Yt,{open:!0,onOpenChange:(D,I)=>{if(!D){if(yp){I.cancel();return}e()}},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("清理请求日志")})}),s.jsxs(Lt,{children:[s.jsx(Ph,{"aria-label":r("清理方式"),value:t,onValueChange:D=>a(D==="raw"?"raw":"all"),children:MQ.map(D=>{const I=t===D;return s.jsxs("label",{"data-checked":I?"":void 0,className:"group/card grid cursor-pointer grid-cols-[auto_minmax(0,1fr)] items-center gap-x-2.5 gap-y-0.5 rounded-md border border-border px-3 py-2.5 transition-colors duration-150 ease-out hover:border-border-strong data-checked:border-primary data-checked:bg-primary-soft",children:[s.jsx(yo,{value:D,className:"row-span-2 self-center"}),s.jsx("span",{className:"text-[13px] font-semibold text-foreground group-data-checked/card:text-primary-fg",children:P0[D].title}),s.jsx("span",{className:"text-[11.5px] leading-[1.55] text-subtle",children:P0[D].desc})]},D)})}),s.jsx("p",{className:"text-[11.5px] text-muted-foreground",children:E})]}),s.jsxs(tn,{children:[s.jsx("span",{className:"min-w-0 text-[11.5px] text-muted-foreground",children:d}),s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",size:"sm",onClick:()=>{R()},disabled:p,title:r(p?"压缩正在后台执行,完成后自动恢复":"回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)"),children:r(p?"压缩中…":"压缩数据库")}),s.jsx(ne,{variant:"outline",onClick:e,disabled:b,children:r("取消")}),s.jsx(ne,{variant:"destructive",onClick:()=>{C()},disabled:b,children:r(b?"清理中…":"执行清理")})]})]})})}let wp=null,Qu=null;function QD(){wp&&(wp.unmount(),wp=null),Qu&&(Qu.remove(),Qu=null)}function _Q(){yp||QD()}function LQ(){QD(),Qu=document.createElement("div"),document.body.append(Qu),wp=jn.createRoot(Qu),wp.render(s.jsx(PQ,{onClose:_Q}))}window.wbRequestClearModal={open:LQ};const ZD=[{value:"detail",label:r("请求详情")},{value:"preview",label:r("预览对话")},{value:"raw",label:r("原始报文")}],zQ="detail",WD="border-b border-hairline px-2.5 py-[7px] align-top",Vo="border border-hairline px-2 py-1 align-top",BQ="border border-hairline bg-surface-2 px-2 py-1 text-[11.5px] tracking-normal",bx="hover:[&>td]:bg-transparent";function Zu(){return window}function z0(){const e=Zu().workbuddyDesktop;if(!e)throw new Error(r("后端桥不可用"));return e}let B0=0;function HQ(e){if(!e)return"—";const t=new Date(e),a=o=>String(o).padStart(2,"0");return`${t.getFullYear()}-${a(t.getMonth()+1)}-${a(t.getDate())} ${a(t.getHours())}:${a(t.getMinutes())}:${a(t.getSeconds())}`}function JD(e){const t=Math.round(Number(e)||0);if(t<1e3)return`${t}ms`;const a=Math.floor(t/1e3);if(a>=60)return r("{m}分{s}秒",{m:Math.floor(a/60),s:a%60});const o=t%1e3;return o>0?r("{s}秒{ms}ms",{s:a,ms:o}):r("{n}秒",{n:a})}function UQ(e){const t=Number(e);return!Number.isFinite(t)||t<=0?"-":JD(t)}const VQ=r("生成速度:输出 token ÷ 流式段用时(总用时 − 首响);流式段不足总用时一半时按总用时计");function FQ(e){const t=Number(e.completionTokens)||0,a=Number(e.durationMs)||0;if(t<=0||a<=0)return"";const o=Number(e.firstResponseMs),c=Number.isFinite(o)&&o>0&&o=500&&c*2>=a,f=t/((d?c:a)/1e3);return`${f>=100?Math.round(f):f.toFixed(1)} tok/s`}function vx(e){return Zu().wbUnits?.formatTokens?.(e)??(Number(e)||0).toLocaleString("zh-CN")}function $Q(e){const t=Math.max(0,Date.now()-(Number(e)||0));return r("{n}秒",{n:Math.max(1,Math.floor(t/1e3))})}function qQ(e){const t=Number(e.status)||0;return t>=200&&t<300&&!e.error}function yx(e){return(Number(e?.status)||0)===0&&!e?.error}function eI(e){const t=String(e.provider??"").trim();return String(e.providerLabel??"").trim()||(t?Zu().wbProviders?.labelOf?.(t)||t:"")}function Sp(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function H0(e){if(e==null)return"";if(typeof e=="string")return e;try{return JSON.stringify(e,null,2)}catch{return""}}function U0(e){return e instanceof Error?e.message:String(e)}const tI=[{label:r("请求头"),pick:e=>H0(e.requestHeaders)},{label:r("请求体"),pick:e=>H0(e.requestBody)},{label:r("响应头"),pick:e=>H0(e.responseHeaders)},{label:r("响应体"),pick:e=>e.responseBody?String(e.responseBody):""}];function jx({text:e,loading:t=!1}){return s.jsxs("div",{className:"flex items-center justify-center gap-2 py-6 text-center text-[12.5px] text-muted-foreground",children:[t?s.jsx(of,{className:"size-3.5"}):null,e]})}function pa({label:e,children:t}){return s.jsxs(Cl,{className:bx,children:[s.jsx("th",{scope:"row",className:`w-[76px] whitespace-nowrap text-left text-[12.5px] font-semibold text-muted-foreground ${WD}`,children:e}),s.jsx(Tn,{className:WD,children:t})]})}function GQ({row:e}){if(yx(e)){const c=Zu().wbRequestPhase;if(!c?.badgeHtml)return s.jsxs(ze,{variant:"info",shape:"tag",title:r("请求正在转发中,用时列显示的是已用时"),children:[s.jsx(my,{className:"req-live-dot"}),r("进行中")]});const d=c.elapsedLineHtml?.(e)??"";return s.jsxs("span",{className:"inline-flex flex-wrap items-center gap-1.5",children:[s.jsx("span",{dangerouslySetInnerHTML:{__html:c.badgeHtml(e)}}),d?s.jsx("span",{className:"text-[11.5px] text-muted-foreground",dangerouslySetInnerHTML:{__html:d}}):null]})}const t=Number(e.status)||0,a=qQ(e),o=!a&&t>=200&&t<300?r("HTTP {status},但响应体阶段出错",{status:t}):"";return s.jsx(ze,{variant:a?"success":"destructive",shape:"tag",title:o||void 0,children:t||r("失败")})}function KQ({row:e}){const t=String(e.clientModel??"").trim(),a=String(e.upstreamModel??"").trim(),o=String(e.model??"").trim(),c=String(e.clientReasoning??"").trim(),d=String(e.upstreamReasoning??"").trim(),f=(b,y)=>y?`${b}(${y})`:b;if(!t||!a||a.toLowerCase()===t.toLowerCase())return s.jsx(s.Fragment,{children:f(o,d||c)||"—"});const p=f(a,d),h=f(t,c);return s.jsxs("span",{className:"flex flex-col gap-0.5",children:[s.jsxs("span",{title:r("转发到上游的模型:{model}",{model:p}),children:["⬆️ ",p]}),s.jsxs("span",{className:"text-muted-foreground",title:r("下游请求的模型:{model}",{model:h}),children:["⬇️ ",h]})]})}function YQ(e){const t=Number(e?.status),a=e?.status!==null&&e?.status!==void 0&&Number.isFinite(t),o=e?.error?String(e.error):"";if(o){const c=a?r("失败({status}):{error}",{status:String(t),error:o}):r("失败:{error}",{error:o});return s.jsx("span",{className:"break-words text-destructive",children:c})}return a?s.jsx("span",{className:"text-success",children:r("成功({status})",{status:String(t)})}):s.jsx("span",{className:"text-muted-foreground",children:r("无结果记录")})}function XQ({row:e}){const t=Array.isArray(e.attemptDetails)?e.attemptDetails:[];if(!t.length)return s.jsx(s.Fragment,{children:"-"});const a=Number(e.attempts)||1;return s.jsxs(Zy,{className:"text-[11.5px]",children:[s.jsx(AE,{children:s.jsx(Cl,{className:bx,children:[r("轮次"),r("提供商"),r("账号"),r("结果"),r("体积"),r("内部重试"),r("提示")].map(o=>s.jsx(RE,{className:BQ,children:o},o))})}),s.jsxs(Wy,{children:[t.map((o,c)=>{const d=Array.isArray(o?.retries)?o.retries:[],f=d.map(v=>{const j=Number(v?.delayMs),w=Number.isFinite(j)&&j>0?j>=1e3?r(",{n}秒后重试",{n:Math.round(j/1e3)}):r(",{n}毫秒后重试",{n:Math.round(j)}):"";return`${String(v?.reason||r("未知原因"))}${w}`}).join(` -`),p=String(o?.notice??"").trim(),h=Number(o?.bodyBytes),b=Number.isFinite(h)&&h>0?h>=1024*1024?`${(h/1024/1024).toFixed(1)} MB`:h>=1024?`${Math.round(h/1024)} KB`:`${h} B`:"—",y=Number.isFinite(h)&&h>0?r("实际发给上游 {bytes} 字节",{bytes:h.toLocaleString()}):void 0;return s.jsxs(Cl,{className:bx,children:[s.jsx(Tn,{className:Vo,children:c+1}),s.jsx(Tn,{className:Vo,children:eI(o)||(o?.provider?String(o.provider):r("未知"))}),s.jsx(Tn,{className:Vo,children:o?.account?String(o.account):"—"}),s.jsx(Tn,{className:Vo,children:YQ(o)}),s.jsx(Tn,{className:`${Vo} whitespace-nowrap`,title:y,children:b}),s.jsx(Tn,{className:`${Vo} whitespace-nowrap`,title:f||void 0,children:d.length?r("↻ {n} 次",{n:d.length}):"-"}),s.jsx(Tn,{className:Vo,children:p||"—"})]},c)}),t.lengths.jsxs("li",{className:"py-0.5",children:[s.jsxs("b",{children:[y+1,". ",String(b.name||b.id||"—")]}),s.jsxs("span",{className:"text-muted-foreground",children:[" ",String(b.reason||"")]})]},String(b.id??y)))}):null,p.length?s.jsxs("div",{className:"mt-1 text-muted-foreground",children:[r("被跳过:"),p.map(b=>String(b.name||b.id||"")).join("、")]}):null,h?s.jsx("div",{className:"mt-1",children:r("最终承载:{picked}",{picked:h})}):null]})}function ZQ({row:e}){if(!e)return s.jsx(jx,{text:r("列表数据已刷新,请重试(关闭后重新打开这条详情)")});const t=String(e.accountName??"").trim(),a=FQ(e);return s.jsx(Zy,{className:"text-[12.5px] text-subtle [&_tr:last-child>td]:border-b-0 [&_tr:last-child>th]:border-b-0",children:s.jsxs(Wy,{children:[s.jsx(pa,{label:r("时间"),children:HQ(e.ts)}),s.jsx(pa,{label:r("状态"),children:s.jsx(GQ,{row:e})}),s.jsx(pa,{label:r("耗时"),children:yx(e)?s.jsxs(s.Fragment,{children:[$Q(e.ts),s.jsx("span",{className:"text-[11.5px] text-muted-foreground",children:r("(请求仍在转发中)")})]}):r("{duration} / 首帧 {first}",{duration:JD(e.durationMs),first:UQ(e.firstResponseMs)})}),s.jsx(pa,{label:r("重试次数"),children:String(Number(e.attempts)||1)}),s.jsx(pa,{label:r("模型"),children:s.jsx(KQ,{row:e})}),s.jsxs(pa,{label:r("提供商"),children:[eI(e)||"—",t?s.jsx("span",{className:"text-[11.5px] text-muted-foreground",children:r("(账号:{account})",{account:t})}):null]}),s.jsxs(pa,{label:r("令牌"),children:[r("输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}",{input:vx(e.promptTokens),output:vx(e.completionTokens),total:vx(e.totalTokens),cache:vx(e.cacheReadTokens)}),a?s.jsxs("span",{title:VQ,children:[" · ",a]}):null]}),e.isTest?s.jsx(pa,{label:r("来源"),children:s.jsx(ze,{variant:"brand",shape:"tag",title:r("模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计"),children:r("模型测试")})}):null,s.jsx(pa,{label:r("错误"),children:e.error?s.jsx("span",{className:"text-destructive",children:String(e.error)}):"—"}),s.jsx(pa,{label:r("尝试明细"),children:s.jsx(XQ,{row:e})}),e.route?s.jsx(pa,{label:r("路由回放"),children:s.jsx(QQ,{row:e})}):null,s.jsx(pa,{label:r("敏感词"),children:Array.isArray(e.sensitiveHits)&&e.sensitiveHits.length?s.jsx("span",{className:"inline-flex flex-wrap gap-1.5",children:e.sensitiveHits.map((o,c)=>s.jsxs(ze,{variant:"sensitive",shape:"tag",children:[String(o?.word??"")," × ",String(Number(o?.count)||0)]},c))}):"—"})]})})}function WQ({state:e}){if(!e)return s.jsx(jx,{text:r("正在读取…"),loading:!0});const t=e==="gone"?null:e.body,a=t&&Zu().wbConversationPreview?.render?.(t.requestBody,t.responseBody)||"";return a?s.jsxs("div",{children:[e!=="gone"&&e.truncated?s.jsx("div",{className:"mb-2 text-[11.5px] text-warning",children:r("正文超过单条上限,可能已被截断")}):null,s.jsx("div",{dangerouslySetInnerHTML:{__html:a}})]}):s.jsx(jx,{text:r("无报文原文(未开启「调试模式」,或已被清理)")})}function JQ({data:e}){const t=e.status===null||e.status===void 0?"-":String(e.status),a=[e.url?`URL: ${e.url}`:"",e.provider?r("提供商: {provider}",{provider:String(e.provider)}):"",r("上游状态码: {status}",{status:t})].filter(Boolean);return s.jsxs("div",{className:"flex flex-col gap-4",children:[a.length?s.jsx("div",{className:"border-b border-hairline pb-2.5 text-xs leading-[1.7] break-all text-subtle",children:a.map((o,c)=>s.jsx("div",{children:o},c))}):null,tI.map(o=>{const c=o.pick(e)||"";return s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx("h3",{className:"text-[11.5px] font-semibold tracking-[.02em] text-muted-foreground",children:o.label}),c?s.jsx("pre",{className:"max-h-80 overflow-auto rounded-md border border-border bg-surface-2 p-[10px_12px] font-mono text-[11.5px] leading-[1.6] break-words whitespace-pre-wrap text-subtle",children:c}):s.jsx(jx,{text:r("这条请求没有保存这一段报文")})]},o.label)})]})}function eZ({id:e,row:t,token:a,onClose:o}){const[c,d]=x.useState(zQ),[f,p]=x.useState(null),[h,b]=x.useState(null),[y,v]=x.useState("—"),[j,w]=x.useState("idle"),S=x.useRef(!0),N=x.useRef(null),k=x.useRef({detail:0,preview:0,raw:0}),A=x.useCallback(()=>!S.current||a!==B0,[a]);async function P(){try{const O=await z0().getStatsRequestRaw(e);if(A())return;const _=Sp(O)?O:null,z=String(_?.requestBody??"").trim()!==""||String(_?.responseBody??"").trim()!=="";p(_&&z?{body:_,truncated:!!_.truncated}:"gone")}catch(O){if(A())return;p("gone"),console.warn("读取原始正文失败(预览对话显示空态):",U0(O))}}async function R(){try{const O=await z0().getDebugTraffic(e);if(A())return;const _=Sp(O)?O:{};if(!tI.some(z=>(z.pick(_)||"").trim())){b("gone");return}b({data:_}),v(_.truncated?r("报文超过单条上限,请求体 / 响应体已按上限截断"):r("内容按原样保存,请求头中的凭据字段已替换为 [redacted]"))}catch(O){if(A())return;b("gone"),v(r("在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文")),console.warn("读取调试报文失败(原始报文标签隐藏):",U0(O))}}x.useEffect(()=>(S.current=!0,P(),R(),()=>{S.current=!1}),[]),x.useLayoutEffect(()=>{const O=N.current;O&&(O.scrollTop=k.current[c])},[c]);function C(O){if(O==="raw"&&!Sp(h)||O===c)return;const _=N.current;_&&(k.current[c]=_.scrollTop),d(O)}async function E(){if(!e||!yx(t))return;const O=Zu().wbConfirm?.ask;if(!(!O||!await O({title:r("终止请求"),html:r("确定终止这条正在转发的请求?
    上游连接会立即断开,明细将记为「请求已被手动终止」。"),okText:r("终止"),okClass:"danger"}))){w("pending");try{if(await z0().terminateStatsRequest(e),!S.current)return;w("accepted"),v(r("已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」"))}catch(z){if(!S.current)return;w("idle"),v(r("终止失败:{error}",{error:U0(z)}))}}}const D=Sp(h),I=x.useMemo(()=>D?ZD:ZD.filter(O=>O.value!=="raw"),[D]);return s.jsx(Yt,{open:!0,onOpenChange:O=>{O||o()},children:s.jsxs(Xt,{className:"w-[min(920px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("请求详情")})}),s.jsxs(Lt,{ref:N,className:"gap-3",children:[s.jsx(En,{options:I,value:c,onValueChange:C,"aria-label":r("详情内容"),className:"self-start"}),s.jsx("section",{hidden:c!=="detail",children:s.jsx(ZQ,{row:t})}),s.jsx("section",{hidden:c!=="preview",children:s.jsx(WQ,{state:f})}),D?s.jsx("section",{hidden:c!=="raw",children:s.jsx(JQ,{data:h.data})}):null]}),s.jsxs(tn,{children:[s.jsx("span",{className:"min-w-0 text-[11.5px] text-muted-foreground",children:y}),s.jsx("div",{className:"mr-auto"}),yx(t)?s.jsx(ne,{variant:"destructive",disabled:j!=="idle",onClick:()=>{E()},title:r("断开上游连接,明细记为「请求已被手动终止」"),children:r("终止请求")}):null,s.jsx(ne,{variant:"outline",onClick:o,children:r("关闭")})]})]})})}let Np=null,Wu=null;function nI(){Np&&(Np.unmount(),Np=null),Wu&&(Wu.remove(),Wu=null)}function sI(){B0+=1,nI()}function tZ(e,t){const a=String(e??"").trim();if(!a)return;nI();const o=++B0;Wu=document.createElement("div"),document.body.append(Wu),Np=jn.createRoot(Wu),Np.render(s.jsx(eZ,{id:a,row:Sp(t)?t:null,token:o,onClose:sI}))}window.wbRequestDetail={open:tZ,close:sI};const nZ=150,sZ=80,ic=8,wx=8,rZ=520,aZ=220,lZ=1.1,oZ=900,V0=12,F0="req-hover-panel",iZ=["fixed z-[35] pointer-events-none","max-h-[min(360px,60vh)] overflow-x-hidden overflow-y-auto","rounded-md p-[10px_12px] text-[12px] leading-[1.6] break-normal","bg-surface-2 text-foreground shadow-3","border-0 dark:border dark:border-border-strong dark:bg-surface-3"].join(" "),kp="rh-row rh-dim min-w-0 break-words text-muted-foreground",cZ="rh-row min-w-0 break-words";function uZ(){return window}function $0(e){return typeof e=="object"&&e!==null?e:null}const rI=e=>Array.isArray(e?.attemptDetails)?e.attemptDetails:[],aI=e=>Array.isArray(e?.retries)?e.retries:[],dZ=e=>Array.isArray(e?.sensitiveHits)?e.sensitiveHits:[];function q0(e){const t=String(e??"").trim();return t?uZ().wbProviders?.labelOf?.(t)||t:""}function fZ(e){const t=$0(e);return(Number(t?.attempts)||1)>1?!0:rI(t).some(a=>aI(a).length>0||!!a?.notice)}function lI(e){return e.dataset.reqHover==="sensitive"?"sensitive":"chain"}function pZ({details:e}){if(e.length<2)return null;const t=e.map(a=>q0(a.provider)||r("未知"));return s.jsxs("div",{className:"rh-chain mb-1.5 border-b border-border pb-1.5 break-words",children:[s.jsx("span",{className:"rh-chain-k font-semibold text-muted-foreground",children:r("切换路径:")}),s.jsx("span",{className:"rh-chain-v font-semibold text-foreground",children:t.join(" → ")})]})}function mZ({item:e}){const t=aI(e);return t.length?s.jsxs("div",{className:"rh-retry mt-0.5 mb-1 ml-2.5 border-l-2 border-border-strong pl-2",children:[s.jsx("div",{className:"rh-retry-head text-[11.5px] font-semibold text-warning",children:r("↻ 重试 {n} 次",{n:t.length})}),t.map((a,o)=>{const c=String(a?.reason??"").trim(),d=Number(a?.status),f=a?.status!==null&&a?.status!==void 0&&Number.isFinite(d),p=Number(a?.delayMs),h=Number.isFinite(p)&&p>0?p>=1e3?r(",{n}秒后重试",{n:Math.round(p/1e3)}):r(",{n}毫秒后重试",{n:Math.round(p)}):"";return s.jsxs("div",{className:"rh-retry-row min-w-0 break-words",children:[s.jsx("span",{className:"rh-retry-why mr-1 text-subtle [overflow-wrap:anywhere]",children:c||r("未知原因")}),f?s.jsx("span",{className:"rh-retry-status mr-1 text-muted-foreground tabular-nums whitespace-nowrap",children:`HTTP ${String(d)}`}):null,h?s.jsx("span",{className:"rh-dim text-muted-foreground",children:h}):null]},o)})]}):null}function hZ({item:e,index:t,inFlight:a}){const o=q0(e.provider),c=String(e.account??"").trim(),d=Number(e.status),f=e.status!==null&&e.status!==void 0&&Number.isFinite(d),p=e.error?String(e.error):"",h=String(e.notice??"").trim();return s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:cZ,children:[s.jsx("span",{className:"rh-no mr-1 text-muted-foreground",children:r("尝试 {n} ·",{n:t+1})}),s.jsx("span",{className:"rh-who mr-1 font-semibold text-foreground",children:o||r("未知")}),c?s.jsx("span",{className:"rh-account mr-1 text-muted-foreground [overflow-wrap:anywhere]",children:c}):null,s.jsx("span",{className:"rh-arrow mr-1 text-muted-foreground",children:"→"}),p?s.jsx("span",{className:"rh-bad font-medium text-destructive [overflow-wrap:anywhere]",children:f?r("失败({status}):{error}",{status:String(d),error:p}):r("失败:{error}",{error:p})}):f?s.jsx("span",{className:"rh-ok font-medium text-success",children:r("成功({status})",{status:String(d)})}):a?s.jsx("span",{className:"rh-dim text-muted-foreground",children:r("进行中…")}):s.jsx("span",{className:"rh-dim text-muted-foreground",children:r("无结果记录")})]}),h?s.jsx("div",{className:"rh-notice mt-0.5 mb-1 ml-2.5 border-l-2 border-border-strong pl-2 text-warning [overflow-wrap:anywhere]",children:`⚠️ ${h}`}):null,s.jsx(mZ,{item:e})]})}function gZ({entry:e}){const t=Number(e.attempts)||1,a=rI(e);if(!a.length){const c=q0(e.provider),d=c?r("共 {n} 次尝试,最终由 {provider} 承载",{n:t,provider:c}):r("共 {n} 次尝试",{n:t});return s.jsxs(s.Fragment,{children:[s.jsx("div",{className:kp,children:d}),s.jsx("div",{className:kp,children:r("这条记录的尝试明细未采集(该字段上线前的旧数据,或请求在转发前就失败)")})]})}const o=(Number(e.status)||0)===0&&!e.error;return s.jsxs(s.Fragment,{children:[s.jsx(pZ,{details:a}),a.map((c,d)=>s.jsx(hZ,{item:c,index:d,inFlight:o&&d===a.length-1},d)),a.lengths.jsxs("div",{className:"rh-row rh-hit flex items-baseline justify-between gap-2.5",children:[s.jsx("span",{className:"rh-hit-w break-words text-foreground",children:String(a?.word??"")}),s.jsx("span",{className:"rh-hit-c text-muted-foreground tabular-nums whitespace-nowrap",children:`× ${String(Number(a?.count)||0)}`})]},o)),t.length>V0?s.jsx("div",{className:kp,children:r("另有 {n} 个词命中",{n:t.length-V0})}):null]}):s.jsx("div",{className:kp,children:r("这条记录命中了敏感词,但没有留下命中明细")})}function bZ({kind:e,entry:t,panelRef:a}){return s.jsx("div",{ref:a,id:F0,role:"tooltip","data-kind":e,className:iZ,children:e==="sensitive"?s.jsx(xZ,{entry:t}):s.jsx(gZ,{entry:t})})}let On=null,Fo=0,Cp=0,Tp=null,Sx=null,G0=null,K0=null,cc=null,uc=null,Ep=null,oI="chain";function Y0(){window.clearTimeout(Fo),window.clearTimeout(Cp),Fo=0,Cp=0,Tp=null}function vZ(){const e=Ep?.current,t=On;if(!e||!t)return;const a=t.getBoundingClientRect(),o=window.innerWidth-ic*2;e.style.maxWidth="none",e.style.width="max-content";const c=e.offsetWidth,d=oI==="sensitive",p=Math.max(d?aZ:rZ,c)*(d?lZ:1),h=Math.min(p,o,oZ);e.style.maxWidth=`${h}px`,e.style.width=d?`${h}px`:"auto";const b=e.offsetWidth,y=e.offsetHeight,v=window.innerHeight-a.bottom-wx-ic,j=a.top-wx-ic,w=vv,S=a.left+a.width/2,N=Math.max(ic,Math.min(S-b/2,window.innerWidth-ic-b)),k=w?a.top-wx-y:a.bottom+wx;e.style.left=`${Math.round(N)}px`,e.style.top=`${Math.round(Math.max(ic,Math.min(k,window.innerHeight-ic-y)))}px`}function iI(e){if(!On)return;const t=e.target;t instanceof Node&&(t===On||On.contains(t)||Oa())}function cI(){Oa()}function uI(){Oa()}let Nx=!1;function yZ(){Nx||(Nx=!0,document.addEventListener("click",iI,!0),window.addEventListener("scroll",cI,!0),window.addEventListener("resize",uI))}function jZ(){Nx&&(Nx=!1,document.removeEventListener("click",iI,!0),window.removeEventListener("scroll",cI,!0),window.removeEventListener("resize",uI))}function wZ(){jZ(),uc&&(uc.unmount(),uc=null),cc&&(cc.remove(),cc=null),Ep=null}function dI(e,t){oI=e,(!cc||!uc||!Ep)&&(cc=document.createElement("div"),document.body.append(cc),Ep=x.createRef(),uc=jn.createRoot(cc));const a=uc,o=Ep;Ts.flushSync(()=>{a.render(s.jsx(bZ,{kind:e,entry:t,panelRef:o}))}),yZ(),vZ()}function fI(e){if(!e.isConnected)return;const t=$0(G0?.(e));t&&(Y0(),On&&On!==e&&Oa(),On=e,dI(lI(e),t),e.classList.add("active"),e.setAttribute("aria-describedby",F0))}function Oa(){Y0(),On&&(On.classList.remove("active"),On.removeAttribute("aria-describedby"),On=null),wZ()}function SZ(){if(!On)return;const e=String(On.dataset.reqHover||""),t=String(On.dataset.reqId||"");K0=!!(e&&t)&&On.matches(":hover")&&!Cp?{action:"move",kind:e,key:t}:{action:"close"}}function NZ(){const e=K0;if(K0=null,!e||!On)return;if(e.action==="close"){Oa();return}const t=Sx?kZ(Sx,e.kind,e.key):null,a=t?$0(G0?.(t)):null;if(!t||!a){Oa();return}t!==On&&(On.classList.remove("active"),On.removeAttribute("aria-describedby"),On=t,dI(lI(t),a),t.classList.add("active"),t.setAttribute("aria-describedby",F0))}function kZ(e,t,a){for(const o of e.querySelectorAll("[data-req-hover]"))if(String(o.dataset.reqHover||"")===t&&String(o.dataset.reqId||"")===a)return o;return null}function kx(e){const t=e.target;return t instanceof Element?t.closest("[data-req-hover]"):null}function CZ(e){const t=kx(e);if(!t||!Sx?.contains(t)||(Y0(),On===t&&uc))return;const a=On?0:nZ;On&&Oa(),Tp=t,Fo=window.setTimeout(()=>{Fo=0,Tp=null,fI(t)},a)}function TZ(e){const t=kx(e);if(t){if(Tp===t){window.clearTimeout(Fo),Fo=0,Tp=null;return}On===t&&(window.clearTimeout(Fo),Fo=0,Cp=window.setTimeout(()=>{Cp=0,On===t&&Oa()},sZ))}}function EZ(e){const t=kx(e);t&&fI(t)}function AZ(e){const t=kx(e);t&&On===t&&Oa()}function RZ(e){e.key!=="Escape"||!On||Oa()}function OZ(e){const t=e?.host;t&&(Sx=t,G0=e?.entryOf??null,t.addEventListener("pointerover",CZ),t.addEventListener("pointerout",TZ),t.addEventListener("focusin",EZ),t.addEventListener("focusout",AZ),t.addEventListener("keydown",RZ))}window.wbRequestHover={bind:OZ,close:Oa,hasProcessFacts:fZ,beforeListRedraw:SZ,afterListRedraw:NZ};function As(){return window}function X0(e){return e instanceof Error?e.message:String(e)}function Cx(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t]??t)}const Tx={connecting:{label:r("连接中"),cls:"phase-connecting"},waiting:{label:r("等待响应"),cls:"phase-waiting"},streaming:{label:r("响应中"),cls:"phase-streaming"},retrying:{label:r("重试中"),cls:"phase-retrying"},queued:{label:r("排队中"),cls:"phase-queued"}},pI=r("进行中"),MZ="running",mI={connecting:r("请求已受理,正在选路、取凭证、建立上游连接"),waiting:r("上游请求已发出,正在等第一个字节到达(模型的思考时间也在这段)"),streaming:r("首帧已到,上游内容正在下发"),retrying:r("本轮尝试失败,正在退避等待或切换到下一个账号"),queued:r("上游模型繁忙,请求已排进上游队列;网关正按上游建议的时长等待后重发(不是登录态或额度问题)"),"":r("请求正在转发中,用时列显示的是已用时")};function Q0(e){const t=String(e?.phase??"").trim().toLowerCase();return Object.prototype.hasOwnProperty.call(Tx,t)?t:""}function hI(e){const t=Q0(e);return t?Tx[t].label:pI}function gI(e){if(e==null||e==="")return"";const t=Number(e);if(!Number.isFinite(t)||t<0)return"";const a=Math.max(1,Math.floor(t/1e3));return a<60?r("{n}秒",{n:a}):r("{m}分{s}秒",{m:Math.floor(a/60),s:a%60})}function DZ(e){const t=e?.phaseElapsedMs;if(t!=null&&t!==""){const c=Number(t);if(Number.isFinite(c)&&c>=0)return c}const a=Number(e?.phaseStartedAt);if(!Number.isFinite(a)||a<=0)return null;const o=Date.now()-a;return Number.isFinite(o)?Math.max(0,o):null}function IZ(e){const t=Q0(e),a=t?Tx[t].label:pI,o=t?Tx[t].cls:MZ,c=mI[t]||mI[""];return`${Cx(a)}`}function PZ(e){const t=gI(DZ(e));if(!t)return"";const a=hI(e),o=r("进入「{phase}」阶段已持续 {time}",{phase:a,time:t});return`${Cx(t)}`}const Z0=[{key:"time",label:r("时间"),sel:".req-time",track:"92px"},{key:"target",label:r("提供商 / 账号"),sel:".req-target",track:"minmax(0, 1.1fr)"},{key:"retry",label:r("重试"),sel:".req-retry",track:"52px"},{key:"status",label:r("状态"),sel:".req-status",track:"96px"},{key:"model",label:r("模型"),sel:".req-model",track:"minmax(0, 1.3fr)"},{key:"client",label:r("客户端"),sel:".req-client",track:"110px"},{key:"dur",label:r("用时"),sel:".req-dur",track:"96px",align:"right"},{key:"usage",label:r("用量"),sel:".req-usage",track:"minmax(0, 1.6fr)"},{key:"error",label:r("错误"),sel:".req-error-cell",track:"minmax(0, 1.2fr)"},{key:"detail",label:r("详情"),sel:".req-detail",track:"60px",align:"right"}],xI=1e3,_Z=dg;function bI(e){return typeof e=="number"&&e>0?e:xu}const vI="workbuddy-desktop-logs-requests-range",yI="workbuddy-desktop-requests-filters",LZ=3e4,W0=["today","7","30","month","all"],J0="all",zZ={today:r("今天"),7:r("近 7 天"),30:r("近 30 天"),month:r("本月"),all:r("全部")},BZ={today:r("今天"),7:r("7 天"),30:r("30 天"),month:r("本月"),all:r("全部")},HZ=W0.map(e=>({value:e,label:BZ[e]})),jI=[{value:"",label:r("全部状态")},{value:"ok",label:r("成功")},{value:"error",label:r("失败")}],UZ=r("全部状态"),wI=r("全部提供商"),SI=r("全部模型"),Ex={status:"",provider:"",model:""};function Ax(e){return new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime()}function VZ(e){const t=new Date;switch(e){case"today":return Ax(t);case"7":return Ax(new Date(t.getFullYear(),t.getMonth(),t.getDate()-6));case"30":return Ax(new Date(t.getFullYear(),t.getMonth(),t.getDate()-29));case"month":return Ax(new Date(t.getFullYear(),t.getMonth(),1));default:return null}}const sn={filters:{...Ex},range:FZ(),runningOnly:!1,offset:0,size:bI(pg("requests",dg))};let Rx={...Ex},eS=!1,NI=!1,tS=[],Ox=xI,Mx=!0,nS=!1,sS=!1,kI=0,rS=0,aS=null,CI=!1,Ju=null;function FZ(){try{const e=localStorage.getItem(vI);return e!==null&&W0.includes(e)?e:J0}catch{return J0}}function $Z(e){try{localStorage.setItem(vI,e)}catch{}}function TI(){const e=As().wbFilterMemory;return e?(eS=!0,Rx=e.load(yI,{...Ex}),sn.filters={...Rx},{...Rx}):{...Ex}}function qZ(e){Rx={...e},As().wbFilterMemory?.save(yI,{status:e.status||"",provider:e.provider||"",model:e.model||""})}function EI(){const e=new URLSearchParams,t=VZ(sn.range);t!==null&&e.set("start",String(t));const a=sn.runningOnly?"running":sn.filters.status;return a&&e.set("status",a),sn.filters.provider&&e.set("provider",sn.filters.provider),sn.filters.model&&e.set("model",sn.filters.model),e}function GZ(){const e=EI();return e.set("offset",String(sn.offset)),e.set("limit",String(sn.size)),e.toString()}function lS(e){const t=Math.round(Number(e)||0);if(t<1e3)return`${t}ms`;const a=Math.floor(t/1e3);if(a>=60)return r("{m}分{s}秒",{m:Math.floor(a/60),s:a%60});const o=t%1e3;return o>0?r("{s}秒{ms}ms",{s:a,ms:o}):r("{n}秒",{n:a})}const KZ=r("生成速度:输出 token ÷ 流式段用时(总用时 − 首响);流式段不足总用时一半时按总用时计"),YZ=500;function XZ(e){const t=Number(e.completionTokens)||0,a=Number(e.durationMs)||0;if(t<=0||a<=0)return"";const o=Number(e.firstResponseMs),c=Number.isFinite(o)&&o>0&&o=YZ&&c*2>=a,f=t/((d?c:a)/1e3);return`${f>=100?Math.round(f):f.toFixed(1)} tok/s`}function QZ(e){const t=Number(e);return!Number.isFinite(t)||t<=0?"-":lS(t)}function Dx(e){return As().wbUnits?.formatTokens?.(e)??(Number(e)||0).toLocaleString("zh-CN")}function ZZ(e){const t=Math.max(0,Date.now()-(Number(e)||0));return r("{n}秒",{n:Math.max(1,Math.floor(t/1e3))})}function WZ(e){const t=Number(e.promptTokens)||0;return t<=0?"-":`${((Number(e.cacheReadTokens)||0)/t*100).toFixed(2)}%`}function oS(e){const t=Number(e.status)||0;return t>=200&&t<300&&!e.error}function Ap(e){return(Number(e?.status)||0)===0&&!e?.error}const AI=e=>String(e?.id||e?.ts||"");function JZ(e){return(Array.isArray(e?.attemptDetails)?e.attemptDetails:[]).reduce((a,o)=>{const c=o?.retries;return a+(Array.isArray(c)?c.length:0)},0)}function RI(e){return r(As().wbProviders?.labelOf?.(e)||e)}function eW(e){const t=new Set;return e.map((a,o)=>{const c=AI(a)||`i${o}`;let d=c,f=1;for(;t.has(d);)d=`${c}#${f++}`;return t.add(d),d})}async function OI(){if(!(Date.now()-rS({value:String(o?.id||""),label:r(String(o?.label||o?.id||""))})).filter(o=>o.value),a=(Array.isArray(e?.models)?e.models:[]).map(o=>String(o||"")).filter(Boolean);Ju?.setFilterOptions(t,a)}catch(e){console.warn("读取请求日志筛选清单失败,筛选项退化为「全部」:",X0(e))}}}async function tW(){if(!nS)try{const e=await As().workbuddyDesktop?.getScheduledTasks(),t=(e?.tasks||[]).find(a=>a.id==="requestsAutoRefresh");iS(t||null),nS=Array.isArray(e?.tasks)}catch(e){console.warn("读取请求日志自动刷新间隔失败,按默认 1 秒:",X0(e)),iS(null)}}function iS(e){nS=!0;const t=Number(e?.interval),a=e?.unit;!!e&&typeof e=="object"&&Number.isFinite(t)&&t>0&&(a==="seconds"||a==="minutes")?(Mx=e.enabled!==!1,Ox=a==="minutes"?t*6e4:t*1e3):(Mx=!0,Ox=xI),Ju?.setAuto({ms:Ox,enabled:Mx})}function nW(){if(CI)return;const e=As().wbColSettings;e&&(CI=!0,aS=e.register({id:"requests",label:r("请求日志表"),columns:Z0.map(({key:t,label:a,align:o})=>({key:t,label:a,align:o})),mount:()=>document.querySelector('.page[data-page="requests"] .panel-head .head-actions'),onChange:()=>{Ts.flushSync(()=>Ju?.repaint()),As().wbTableColumns?.repaint?.("requests")}}))}function cS(){return(aS?aS.apply(Z0):Z0).map(t=>({...t,align:t.align||"left"}))}const sW={entries:[],total:0,matched:0,running:0,loaded:!1,error:""};function rW(e){return e.loaded?e.total?r("没有符合筛选条件的请求"):r("暂无请求日志,网关还没有转发过请求"):r("正在加载请求日志…")}function aW(e){const t=Number(e.firstResponseMs);return!Number.isFinite(t)||t<=0?null:s.jsx("span",{className:"req-dur-line sub",title:r("首响:上游首帧到达的耗时(请求仍在转发中)"),children:r("首响 {time}",{time:lS(t)})})}function lW(e,t){const{align:a,key:o}=t,c=t.sel.slice(1)+(o==="dur"?" req-num":""),d=`${c} ta-${a}`;switch(o){case"time":{if(!e.ts)return s.jsx("span",{className:d,children:"—"},o);const f=new Date(e.ts),p=h=>String(h).padStart(2,"0");return s.jsxs("span",{className:d,children:[s.jsx("span",{children:`${f.getFullYear()}-${p(f.getMonth()+1)}-${p(f.getDate())}`}),s.jsx("span",{children:`${p(f.getHours())}:${p(f.getMinutes())}:${p(f.getSeconds())}`})]},o)}case"target":{const f=String(e.provider??"").trim(),p=String(e.providerLabel??"").trim(),h=p?r(p):f?RI(f):"";return s.jsxs("span",{className:d,children:[h?s.jsx("span",{className:"req-provider",title:f&&f!==h?r("{name}({id})",{name:h,id:f}):h,children:h}):s.jsx("span",{className:"req-provider is-empty",title:r("这条明细没有记录提供商(旧数据或请求未走到转发)"),children:"—"}),e.accountName?s.jsx("span",{className:"req-acct",title:e.accountId||e.accountName,children:e.accountName}):s.jsx("span",{className:"req-acct is-empty",title:r("请求在任何账号接手之前就失败了"),children:"—"})]},o)}case"retry":{const f=Number(e.attempts)||1,p=AI(e),h=[];if(e.isTest&&h.push(s.jsx(ze,{variant:"brand",shape:"tag",title:r("这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计"),children:r("测试")},"test")),As().wbRequestHover?.hasProcessFacts?.(e)??f>1){const y=f>1?f:JZ(e);h.push(s.jsx(ze,{variant:"warning",shape:"tag",render:s.jsx("button",{type:"button"}),className:"req-hover-tag","data-req-hover":"chain","data-req-id":p,children:y>1?r("重试 {n}",{n:y}):r("重试")},"chain"))}return Array.isArray(e.sensitiveHits)&&e.sensitiveHits.length&&h.push(s.jsx(ze,{variant:"sensitive",shape:"tag",render:s.jsx("button",{type:"button"}),className:"req-hover-tag","data-req-hover":"sensitive","data-req-id":p,"aria-label":r("命中了敏感词"),children:r("敏")},"sensitive")),h.length?s.jsx("span",{className:d,children:h},o):s.jsx("span",{className:`req-none ta-${a}`,children:"-"},o)}case"status":{if(Ap(e)){const b=window.wbRequestPhase;if(!b?.badgeHtml)return s.jsx("span",{className:d,children:s.jsx("span",{className:"req-status-badge",children:s.jsxs(ze,{shape:"tag",variant:"info",title:r("请求正在转发中,用时列显示的是已用时"),children:[s.jsx(my,{className:"req-live-dot","aria-hidden":"true"}),r("进行中")]})})},o);const y=b.elapsedLineHtml?.(e)??"";return s.jsxs("span",{className:d,children:[s.jsx("span",{className:"req-status-badge",dangerouslySetInnerHTML:{__html:b.badgeHtml(e)}}),y?s.jsx("span",{className:"req-status-line",dangerouslySetInnerHTML:{__html:y}}):null]},o)}const f=Number(e.status)||0,p=oS(e),h=!p&&f>=200&&f<300?r("HTTP {status},但响应体阶段出错:见错误列",{status:f}):void 0;return s.jsx("span",{className:d,children:s.jsx(ze,{shape:"tag",variant:p?"success":"destructive",title:h,children:f||r("失败")})},o)}case"model":{const f=String(e.clientModel??"").trim(),p=String(e.upstreamModel??"").trim(),h=String(e.model??"").trim(),b=String(e.clientReasoning??"").trim(),y=String(e.upstreamReasoning??"").trim(),v=(S,N)=>N?`${S}(${N})`:S;if(!f||!p||p.toLowerCase()===f.toLowerCase()){const S=v(h,y||b);return s.jsx("span",{className:d,title:S,children:S||"—"},o)}const j=v(p,y),w=v(f,b);return s.jsxs("span",{className:`${d} req-model-split`,children:[s.jsxs("span",{className:"req-model-line",title:r("转发到上游的模型:{model}",{model:j}),children:["⬆️ ",j]}),s.jsxs("span",{className:"req-model-line sub",title:r("下游请求的模型:{model}",{model:w}),children:["⬇️ ",w]})]},o)}case"client":{const f=String(e.client??"").trim();if(!f)return s.jsx("span",{className:`${d} is-empty`,title:r("这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)"),children:"—"},o);const p=f==="other"?r("其它"):f;return s.jsx("span",{className:d,title:f==="other"?r("识别到 User-Agent,但不属于已知的客户端类型:{ua}",{ua:f}):r("来源客户端:{client}",{client:p}),children:p},o)}case"dur":return Ap(e)?s.jsxs("span",{className:d,children:[s.jsx("span",{className:"req-dur-line",title:r("已用时(请求仍在转发中)"),children:ZZ(e.ts)}),aW(e)]},o):s.jsxs("span",{className:d,children:[s.jsx("span",{className:"req-dur-line",children:lS(e.durationMs)}),s.jsx("span",{className:"req-dur-line sub",title:r("首响:上游首帧到达的耗时"),children:r("首响 {time}",{time:QZ(e.firstResponseMs)})})]},o);case"usage":{if(Ap(e))return s.jsx("span",{className:d},o);if(!oS(e))return s.jsxs("span",{className:d,title:r("失败请求不记录用量"),children:[s.jsx("span",{className:"req-usage-line",children:"in: - / out: - / all: -"}),s.jsx("span",{className:"req-usage-line sub",children:r("缓存读取: - / 命中率: -")})]},o);const f=`in: ${Dx(e.promptTokens)} / out: ${Dx(e.completionTokens)} / all: ${Dx(e.totalTokens)}`,p=r("缓存读取: {cache} / 命中率: {rate}",{cache:Dx(e.cacheReadTokens),rate:WZ(e)}),h=XZ(e),b=h?`${p} · ${h}`:p;return s.jsxs("span",{className:d,title:[f,b].filter(Boolean).join(` -`),children:[s.jsx("span",{className:"req-usage-line",children:f}),s.jsxs("span",{className:"req-usage-line sub",children:[p,h?s.jsxs("span",{title:KZ,children:[" · ",h]}):null]})]},o)}case"error":return Ap(e)?s.jsx("span",{className:d},o):e.error?s.jsx("span",{className:d,title:String(e.error),children:String(e.error)},o):s.jsx("span",{className:`req-none ${c} ta-${a}`,children:"-"},o);case"detail":{const f=e.id?String(e.id):"";return f?s.jsx("span",{className:d,children:s.jsx(ne,{size:"sm",variant:"outline",className:"req-detail-btn",title:r("查看该请求的上游原始报文"),onClick:()=>{As().wbRequestDetail?.open?.(f,e)},children:r("详情")})},o):s.jsx("span",{className:`req-none ${c} ta-${a}`,children:"-"},o)}default:return null}}function oW(e){return s.jsx("div",{className:"req-head",children:e.map(t=>s.jsx("span",{"data-col":t.key,className:`${t.sel.slice(1)}${t.key==="dur"?" req-num":""} ta-${t.align}`,children:t.label},t.key))},"__head")}function iW(){const[e,t]=x.useState(sW),[a,o]=x.useState(()=>TI()),[c,d]=x.useState(()=>sn.range),[f,p]=x.useState(!1),[h,b]=x.useState(0),[y,v]=x.useState(()=>sn.size),[j,w]=x.useState(()=>({ms:Ox,enabled:Mx})),[S,N]=x.useState([]),[k,A]=x.useState([]),[,P]=x.useState(0),R=x.useRef(null),C=x.useRef(null),E=x.useRef(!1),D=x.useCallback(ee=>(sn.filters={...sn.filters,...ee},o({...sn.filters}),sn.filters),[]),I=x.useCallback(ee=>{sn.range=ee,d(ee)},[]),O=x.useCallback(ee=>{sn.offset=ee,b(ee)},[]),_=x.useCallback(ee=>{sn.runningOnly=ee,p(ee)},[]),z=x.useCallback(()=>{eS||!As().wbFilterMemory||(eS=!0,!NI&&D(TI()))},[D]),H=x.useCallback((ee,he)=>{As().wbRequestHover?.beforeListRedraw?.(),he&&(tS=he),E.current=!0,t(ee)},[]),B=x.useCallback(ee=>{As().wbRequestHover?.beforeListRedraw?.(),E.current=!0,t(he=>({...he,error:ee}))},[]),L=x.useCallback(async(ee={})=>{const{silent:he=!1,resetPage:te=!1}=ee;te&&O(0),tW(),z(),he||OI();const ke=++kI;try{const le=As().workbuddyDesktop;if(!le)throw new Error(r("后端桥不可用"));const de=await le.getStatsRequests(GZ());if(ke!==kI)return;const Se={entries:Array.isArray(de?.entries)?de.entries:[],total:Number(de?.total)||0,matched:Number(de?.matched)||0,running:Number(de?.running)||0,loaded:!0,error:""},Te=Math.max(0,(Math.ceil(Se.matched/sn.size)-1)*sn.size);if(sn.offset>Te)return O(Te),L({silent:he});te&&(C.current=0),H(Se,Se.entries)}catch(le){he||(console.warn("读取请求日志失败:",X0(le)),B(r("读取请求日志失败,详见控制台")))}},[O,H,B,z]);x.useEffect(()=>(Ju={load:L,setAuto:ee=>w(ee),setFilterOptions:(ee,he)=>{N(ee),A(he)},repaint:()=>P(ee=>ee+1)},As().wbRequestHover?.bind?.({host:R.current,entryOf:ee=>{const he=ee?.dataset?.reqId||"";return he&&(tS.find(te=>String(te?.id||"")===he)||tS.find(te=>String(te?.ts||"")===he))||null}}),L({silent:!0}),OI(),()=>{Ju=null}),[L]),x.useLayoutEffect(()=>{nW(),P(ee=>ee+1)},[]);const F=e.error||!e.entries.length?"":cS().map(ee=>ee.key).join("|");x.useLayoutEffect(()=>{F&&As().wbTableColumns?.repaint?.("requests")},[F]),x.useEffect(()=>{if(!j.enabled||j.ms<=0)return;const ee=window.setInterval(()=>{document.hidden||As().wbApp?.currentPage!=="requests"||sS||(sS=!0,L({silent:!0}).finally(()=>{sS=!1}))},j.ms);return()=>{window.clearInterval(ee)}},[j,L]),x.useLayoutEffect(()=>{E.current&&(E.current=!1,As().wbRequestHover?.afterListRedraw?.());const ee=C.current;if(ee===null)return;C.current=null;const he=R.current;he&&(he.scrollTop=ee)});function $(ee){const he=Math.max(1,Math.ceil(e.matched/sn.size)),ke=(Math.min(Math.max(1,ee),he)-1)*sn.size;ke!==sn.offset&&(O(ke),C.current=0,L())}function X(ee){const he=bI(ee);if(he===sn.size)return;sn.size=he,v(he),mg("requests",he);const te=sn.offset+1;O(Math.max(0,Math.floor((te-1)/he)*he)),C.current=0,L()}function K(ee){NI=!0,qZ(D(ee)),L({resetPage:!0})}function q(ee){const he=W0.includes(ee)?ee:J0;he!==sn.range&&(I(he),$Z(he),L({resetPage:!0}))}function Q(ee){ee!==sn.runningOnly&&(_(ee),L({resetPage:!0}))}const G=cS(),J=Math.max(1,Math.ceil(e.matched/sn.size)),Y=Math.min(J,Math.floor(h/sn.size)+1),re=e.matched?h+1:0,M=e.matched?h+e.entries.length:0,U=e.error?"—":e.matched===e.total?r("{n} 条",{n:e.total}):r("{matched} / {total} 条",{matched:e.matched,total:e.total}),Z=!f&&e.running>0?r(" · {n} 进行中",{n:e.running}):"",ae=[zZ[c]||r("全部")];f?ae.push(r("只看进行中")):a.status==="ok"?ae.push(r("只看成功")):a.status==="error"&&ae.push(r("只看失败")),a.provider&&ae.push(RI(a.provider)),a.model&&ae.push(a.model);const ce=x.useMemo(()=>!a.provider||S.some(ee=>ee.value===a.provider)?S:[{value:a.provider,label:a.provider},...S],[a.provider,S]),W=x.useMemo(()=>!a.model||k.includes(a.model)?k:[a.model,...k],[a.model,k]),ue=eW(e.entries),ve=e.entries.map((ee,he)=>s.jsx("div",{className:`req-row${Ap(ee)?" running":oS(ee)?"":" failed"}`,children:G.map(te=>lW(ee,te))},ue[he]));return s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("请求日志列表")}),s.jsx(ze,{id:"req-badge",variant:"outline","data-tone":"",children:U+Z}),s.jsx("span",{className:"panel-sub",children:r("按时间倒序,全量保留")}),s.jsx("div",{className:"head-actions",children:s.jsx(ne,{id:"btn-req-clear",variant:"destructive",onClick:()=>{As().wbRequestClearModal?.open?.()},children:r("清理")})})]}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"log-filters",children:[s.jsx(En,{options:HZ,value:c,onValueChange:q,"aria-label":r("请求日志时间范围"),className:"shrink-0"}),s.jsxs(ut,{value:a.status,disabled:f,onValueChange:ee=>K({status:String(ee??"")}),children:[s.jsx(ft,{id:"req-status",className:"min-w-[120px]",title:r(f?"「仅看进行中」开启时,状态固定为进行中":"按请求结果筛选"),"aria-label":r("按请求结果筛选"),children:s.jsx(dt,{children:jI.find(ee=>ee.value===a.status)?.label??UZ})}),s.jsx(pt,{children:jI.map(ee=>s.jsx($e,{value:ee.value,children:ee.label},ee.value))})]}),s.jsxs(ut,{value:a.provider,onValueChange:ee=>K({provider:String(ee??"")}),children:[s.jsx(ft,{id:"req-provider",className:"min-w-[120px]",title:r("按提供商筛选(只列日志里出现过的)"),"aria-label":r("按提供商筛选"),children:s.jsx(dt,{children:a.provider?ce.find(ee=>ee.value===a.provider)?.label||a.provider:wI})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:wI}),ce.map(ee=>s.jsx($e,{value:ee.value,children:ee.label},ee.value))]})]}),s.jsxs(ut,{value:a.model,onValueChange:ee=>K({model:String(ee??"")}),children:[s.jsx(ft,{id:"req-model",className:"min-w-[120px]",title:r("按模型筛选(只列日志里出现过的;精确匹配模型名)"),"aria-label":r("按模型筛选"),children:s.jsx(dt,{children:a.model||SI})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:SI}),W.map(ee=>s.jsx($e,{value:ee,children:ee},ee))]})]}),s.jsx(FF,{id:"btn-req-running",variant:"outline",size:"default",pressed:f,onPressedChange:Q,title:r("只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)"),children:r("仅看进行中")}),s.jsx("div",{className:"spacer"}),s.jsx("span",{className:"panel-sub",id:"req-summary",children:ae.join(" · ")})]}),s.jsx("div",{className:"log-list",id:"req-list",ref:R,children:e.error?s.jsx("div",{className:"log-empty",children:e.error}):e.entries.length?[oW(G),...ve]:s.jsx("div",{className:"log-empty",children:rW(e)})})]}),s.jsx($i,{total:e.matched,range:{start:re,end:M},page:Y,pageCount:J,size:y,sizes:_Z,onSizeChange:X,onPageChange:$,disabled:!e.loaded&&!e.error})]})}async function MI(e={}){await Ju?.load(e)}function cW(){return EI().toString()}function uW(){return rS=0,MI({resetPage:!0})}const DI='.page[data-page="requests"]';let II=!1;function PI(){if(II)return;const e=document.querySelector(DI);e&&(II=!0,e.replaceChildren(),jn.createRoot(e).render(s.jsx(iW,{})))}document.querySelector(DI)?PI():document.addEventListener("DOMContentLoaded",PI,{once:!0}),window.wbRequestsPanel={load:MI,applyAutoRefresh:iS,visibleColumns:cS,clearParams:cW,notifyCleared:uW},window.wbRequestPhase={labelOf:hI,badgeHtml:IZ,elapsedLineHtml:PZ,elapsedText:gI,phaseOf:Q0};function dW(e){if(e.status==="loading")return kt.appLoading;if(e.status==="unavailable")return kt.modelsDevUnavailable;const t=e.source==="builtin"?kt.modelsDevSourceBuiltin:e.source==="cache"?kt.modelsDevSourceCache:e.source==="network"?kt.modelsDevSourceNetwork:e.source,a=e.updatedAt>0?String(tt().wbApp?.formatTime?.(e.updatedAt)??""):"";if(!t&&!a&&!e.count)return kt.modelsDevEmpty;const o=a?r(",{time}",{time:a}):"",c=e.enabled?r("正在联网更新"):r("仅用现有数据、不联网刷新");return r("{count} 条 · 来自{origin}{at} · {mode}",{count:Number(e.count).toLocaleString("zh-CN"),origin:t||r("未知来源"),at:o,mode:c})}function tt(){return window}function Ot(e){return e instanceof Error?e.message:String(e)}function Ve(e,t){tt().wbApp?.toast?.(e,t)}async function fW(e){try{await tt().workbuddyDesktop?.openReleasePage?.(e)}catch(t){Ve(r("打开链接失败:{error}",{error:Ot(t)}),"err")}}const Ix=[{id:"general",label:r("通用"),icon:"sliders"},{id:"assistant",label:r("AI 助手"),icon:"spark"},{id:"display",label:r("显示"),icon:"display"},{id:"gateway",label:r("网关"),icon:"traffic"},{id:"routing",label:r("路由"),icon:"route"},{id:"retry",label:r("重试"),icon:"refresh"},{id:"timeout",label:r("超时"),icon:"timer"},{id:"security",label:r("安全"),icon:"shield"},{id:"data",label:r("数据"),icon:"database"},{id:"feedback",label:r("反馈与需求"),icon:"feedback"},{id:"about",label:r("更新"),icon:"download"}],_I="workbuddy-desktop-settings-cat",pW="workbuddy-desktop-theme",mW="workbuddy-desktop-zoom",LI="wb:theme",zI="wb:zoom",uS=80,BI=130,Px=5,hW=Array.from({length:(BI-uS)/Px+1},(e,t)=>uS+t*Px),gW=[{value:"system",label:r("跟随系统")},{value:"light",label:r("浅色")},{value:"dark",label:r("深色")}];function xW(){try{const e=localStorage.getItem(pW);return e==="light"||e==="dark"?e:"system"}catch{return"system"}}function bW(){try{const e=localStorage.getItem(mW);if(e===null||e.trim()==="")return 100;const t=Number(e);if(!Number.isFinite(t))return 100;const a=Math.round(t/Px)*Px;return Math.min(BI,Math.max(uS,a))}catch{return 100}}const dS=1,fS=99999,pS=[{key:"logRetentionDays",id:"settings-retention-log",label:r("事件日志保留天数"),unit:r("天"),min:dS,max:fS,hint:r("登录 / 账号切换 / 429 切换等系统事件,可填 1–99999 天。")},{key:"requestLogRetentionDays",id:"settings-retention-request",label:r("请求日志保留天数"),unit:r("天"),min:dS,max:fS,hint:r("「请求日志」页的逐条请求记录,默认 99999 天(近似全量保留),可填 1–99999 天。")},{key:"dailyAggregateRetentionDays",id:"settings-retention-daily",label:r("按天聚合保留天数"),unit:r("天"),min:dS,max:fS,hint:r("报表页的热力图与按天趋势,默认 99999 天(近似全量保留),可填 1–99999 天。")}],HI=[{key:"retryCount",id:"settings-retry-count",label:r("同一账号重试次数"),unit:r("次"),min:0,max:10,hint:r("在同一个账号上原地重发几次(不含首发;整条请求共用一份,用完后换来的账号只发一次就继续换)。可填 0–10,0 表示失败立即换号。")},{key:"retryCrossProviderCount",id:"settings-retry-cross-provider-count",label:r("切换账号重试次数"),unit:r("次"),min:0,max:10,hint:r("失败后最多再换几个账号试(填 N = 最多换 N 个,首发那个不算)。换到同一家名下另一个账号、或换到另一家,都各算一次;换满仍失败就返回错误。可填 0–10,0 表示失败立即报错。")},{key:"retryIntervalSeconds",id:"settings-retry-interval",label:r("重试间隔"),unit:r("秒"),min:0,max:300,hint:r("两次重试之间的等待时间,两项共用,可填 0–300 秒。")}],UI=[{key:"connectTimeoutSeconds",id:"settings-timeout-connect",label:r("连接中超时"),unit:r("秒"),min:1,max:3600,hint:r("建立上游 TCP/TLS 连接或代理隧道的最大等待时间(秒),默认 30 秒,范围 1~3600。")},{key:"headersTimeoutSeconds",id:"settings-timeout-headers",label:r("等待响应超时"),unit:r("秒"),min:1,max:3600,hint:r("请求发出后等待上游响应头的最大时间(秒),默认 300 秒,范围 1~3600。")},{key:"streamIdleTimeoutSeconds",id:"settings-timeout-stream-idle",label:r("流式响应空闲超时"),unit:r("秒"),min:1,max:3600,hint:r("流式响应相邻数据之间允许的最大空闲时间(秒),收到新数据后重新计时,默认 300 秒,范围 1~3600。")},{key:"bodyTimeoutSeconds",id:"settings-timeout-body",label:r("非流式响应超时"),unit:r("秒"),min:1,max:3600,hint:r("读取完整非流式响应体允许的最大时间(秒),默认 300 秒,范围 1~3600。")}],VI=[{key:"queueMaxWaits",id:"settings-queue-max-waits",label:r("排队等待次数"),unit:r("次"),min:0,max:10,hint:r("上游模型繁忙时会先排队(回一句「建议 N 秒后再来」,Qoder 免费模型就是这样):网关等一会儿再发同一请求,最多等几次。默认 2 次,可填 0–10;填 0 表示不等待,排队直接返回 503 并说明「不是登录态或额度问题」。")},{key:"queueWaitSeconds",id:"settings-queue-wait-seconds",label:r("排队等待秒数"),unit:r("秒"),min:0,max:120,hint:r("每次等待的时长,默认 0 = 跟随上游建议(实测 9~30 秒,上游没给建议时用 15 秒)。可填 0–120 秒强制一个固定时长。")}],FI="noRetryStatusCodes",mS=100,hS=599,$I=50,$o=[{value:"passthrough",optionLabel:r("透传客户端 system(默认)"),toastLabel:r("透传客户端 system")},{value:"custom",optionLabel:r("替换为网关提示词"),toastLabel:r("替换为网关提示词")},{value:"append",optionLabel:r("追加网关提示词"),toastLabel:r("追加网关提示词")}];function qI(e,t,a,o){const c=String(e??"").trim();if(!c)return{ok:!1,message:r("{noun}不能为空(可填 {min}–{max})",{noun:o,min:t,max:a})};if(!/^\d+$/.test(c))return{ok:!1,message:r("{noun}必须是整数",{noun:o})};const d=Number(c);return da?{ok:!1,message:r("{noun}必须在 {min}–{max} 之间(当前填的是 {text})",{noun:o,min:t,max:a,text:c})}:{ok:!0,value:d}}function vW(e,t){const a={...t};if(e&&typeof e=="object"){const o=e;typeof o.closeToTray=="boolean"&&(a.closeToTray=o.closeToTray),typeof o.autostart=="boolean"&&(a.autostart=o.autostart),typeof o.lanAccess=="boolean"&&(a.lanAccess=o.lanAccess),typeof o.lanPanel=="boolean"&&(a.lanPanel=o.lanPanel),typeof o.lightweightMode=="boolean"&&(a.lightweightMode=o.lightweightMode)}return a}function GI(e,t,a){if(!t||typeof t!="object")return null;const o=t,c={...a||{}};for(const d of e){const f=Number(o[d.key]);Number.isInteger(f)&&f>=d.min&&f<=d.max&&(c[d.key]=f)}return e.some(d=>Number.isInteger(c[d.key]))?c:null}function KI(e){if(!Number.isFinite(e)||e<=0)return"0 B";if(e<1024)return`${e} B`;const t=e/1024;if(t<1024)return`${t.toFixed(1)} KB`;const a=t/1024;return a<1024?`${a.toFixed(1)} MB`:`${(a/1024).toFixed(2)} GB`}function yW(e){const t=Number(e);return!Number.isFinite(t)||t<0?"—":t.toLocaleString("zh-CN")}const Wn={displayTheme:r("控制界面的深浅色,与左侧边栏底部的三个主题按钮是同一个设置(改哪一处,另一处立刻跟上)。「跟随系统」会随操作系统当前的浅色 / 深色自动切换,并在系统主题变化时即时跟上;选「浅色」或「深色」则把界面固定在该模式,不再随系统变化。窗口标题栏的深浅色会一并同步,不会出现深色界面配一条浅色标题栏的情况。"),displayZoom:r("等比放大或缩小整个界面(文字、控件、间距一起变),效果与浏览器按 Ctrl +/- 相同:80%–130%、5% 一档。窗口本身不缩放,变的是页面内容的显示比例,设置立即生效并记住,下次启动直接按这个比例打开。放得越大可视范围越小,窗口较窄或表格较宽时不建议调得太大。"),displayLanguage:r("界面语言。可选简体中文、繁體中文、English、日本語、한국어、Português (Brasil),或「跟随系统」(按浏览器 / 系统的语言列表自动选)。切换后整页立即刷新;该偏好只存在本机(localStorage),不影响账号、密钥与转发。"),tray:r("默认关闭窗口不会退出程序,而是把窗口缩到系统托盘,转发继续在后台运行(OpenAI 客户端不受影响);要彻底退出程序,请在托盘图标上右键选「退出」。关掉这个开关后,点关闭按钮即退出程序、转发随之中断。「开机自动启动」开启后,登录系统时会自动启动本程序(通常直接驻留托盘),不需要手动打开。"),lightweight:r("开启后,关闭窗口不只是隐藏,而是把界面窗口整个销毁:底层的 WebView2 六个常驻进程(约 200MB 内存)随之退出,网关转发、定时签到、余额查询都在后台进程里继续,不受影响;需要界面时点托盘图标重新打开(WebView 冷启动约 1 秒)。注意:界面销毁期间「ZCode 套餐池(Start Plan)」通道的验证码令牌由界面铸造,无法补充 —— 走该通道的请求会以 503 落进请求日志(写明原因),重新打开界面即自动恢复。此开关只在「关闭窗口时最小化到托盘」开启时有意义,最小化窗口不受影响。"),lan:r("默认网关只监听 127.0.0.1,只有本机能访问。开启后网关改听所有网卡,同一局域网内的设备可以把 API 地址指向本机 IP 一起使用。出于安全考虑,开启前必须先注册一个面板管理员:管理接口从此要求管理员会话或网关 Key,模型额度不会对局域网裸奔;一把启用的网关 Key 都没有时,转发接口也会拒绝服务,直到创建第一把。改动需要重启应用生效。「同时开放网页管理面板」把管理界面也出给局域网(其他设备的浏览器打开本机 IP 即可管理),默认关闭 —— 桌面端的面板仍只由本程序自己出。"),units:r("控制报表与请求日志里 Token 读数的写法:开启后按中文量级显示(1.2亿 / 8400万),关闭则用 k / M 缩写(与上游文档、接口字段的写法一致)。这只影响显示口径,不改变任何统计与存储的数值。"),queue:r("只对「排队制」的上游生效(目前是 Qoder 的免费模型):模型繁忙时上游不报错,只回一句「建议 N 秒后再来」(业务码 10605),网关按建议时长等一会儿再发同一请求,等满次数仍排不上才把「排队中」作为错误返回(HTTP 503,文案会说明这不是登录态或额度问题)。等待发生在首个字节之前,吃的是「等待响应超时」那份预算 —— 两项设置一起决定一次请求最多卡多久;排队不会标记账号限额、也不会换账号(换谁都一样在排队)。保存后对下一个请求立即生效,不用重启。"),captchaPool:r("ZCode「套餐池(Start Plan)」通道的每条推理请求都要附一个当次铸造的阿里云无痕验证令牌(一次性、约 2 分钟过期):令牌由界面里的铸造守卫静默铸好、推给网关入池,转发时逐条取用。这里的三个值管的是补货节奏:①「库存目标」= 库存低于它守卫才补铸、补到它为止,默认 3 个;②「轮询间隔」= 守卫多久看一次库存,默认 2500 毫秒,只影响反应速度(库存够用时不会多铸);③「单轮铸造上限」= 发现缺货时一轮最多铸几个,默认 1 个(连铸之间固定隔 500 毫秒)。请求密集、请求日志里连续出现「需要人机验证令牌」的 503 时,把目标与单轮上限适当调大、轮询调快,能让池子跟得上消耗 —— 代价是上游(阿里云)对铸造频率有风控,铸得越多越快越容易触发限流,且用不掉的令牌 2 分钟即过期作废、等于白烧配额,所以建议小步调。保存后对下一轮补货立即生效,不用重启。"),timeouts:r("上游请求四个阶段各自的等待上限(秒,1~3600):①「连接中超时」= 建立 TCP/TLS 连接或代理隧道的最大等待,默认 30 秒;②「等待响应超时」= 请求发出后等上游响应头的最大时间,默认 300 秒 —— 网关请求上游恒带 stream,响应头在 SSE 建立时就到达,与模型思考多久无关;③「流式响应空闲超时」= 流式响应相邻两块数据之间允许的最大空闲,收到新数据即重新计时,默认 300 秒 —— 上游长时间不吐数据即判定连接僵死并断开;④「非流式响应超时」= 读完整份非流式响应体的总预算(一次性计时、不重置),默认 300 秒。四项都是「等待上限」,只要数据还在来,正常的流式回答就一直往下传。保存后对下一个请求立即生效,不用重启。"),retry:r("请求转发到上游失败时(服务器瞬时错误、连接失败,以及 WorkBuddy 的 11-128 敏感词拦截),网关会等一段间隔后再发一次。次数分两档,管的是两件事:①「同一账号重试次数」在同一账号上原地重发几次 —— 一般是提示词被上游拦截、或链路瞬时抖动,等一会儿重发往往就好了;②「切换账号重试次数」这份请求最多再换几个账号试 —— 首发的那个账号不算,每换一个扣一次,不分是不是同一家(换到同一家的下一个账号也算一次),换满还失败就把错误返回给客户端。两项都设为 0 表示失败不重试、直接报错。注意账号级限额(429)不占这里的换号次数 —— 那类失败走「冷却该账号并换下一个账号」的降级,与这里的重试是两条路。另有「指定错误码直接换号」名单(默认 402):命中名单的上游状态码不在同一账号重发,直接按队列换下一个账号继续试(换号也占「切换账号重试次数」),换满仍失败才把错误返回给客户端。"),routing:r("转发一个请求时按什么顺序挑账号:①「路由策略」决定排队规则 —— 顺序优先是排第 1 的接走所有请求、直到它不能答(限流 / 故障 / 额度尽)才轮到下一位;负载均衡是每条请求换下一位,把负载摊到各个账号上。②「会话粘性」让同一会话(相同的模型 + 相同的开头消息)尽量跟住同一个账号 —— 多轮对话的上下文缓存能复用、少换号少浪费;自动档粘 5 分钟、会话内档粘 6 小时、每轮与关闭都不粘。账号的排队位次(账号表的第一列)在所有策略下都是地基,策略在它之上再排。此外路由还有三层自愈保护:转发失败的账号退避休息一会儿(15 分钟起步翻倍、封顶 1 小时)、被 429 的账号垫底排到队尾 30 分钟、余额不足 / Token 周期用量到顶的账号由限制器拦下 —— 前两层可以在下面按需开关,限制器由账号的「限制器」设置管。"),sanitize:r("上游用「逐字精确匹配」的方式审核请求体(不是语义审核):客户端注入的固定模板句、计费头字段名、以及某些裸错误码出现在报文里就会整单拦截,返回 400。开启本项后,网关在每次转发前改写这些指纹 —— 表头键值整段删除,承载语义的模板句只换一个词(如 official CLI for Claude → official CLI tool for Claude),对话内容与语义都不受影响。规则集是内置的,不需要也无法维护词表。关掉本项后客户端模板会原样发往上游,可能重新出现模板句被误拦的报错。"),developerRoleCompat:r("OpenAI 新版 API 用 developer 角色取代 system 承载指令,新版 OpenAI SDK 与部分客户端发的对话里会出现 role 为 developer 的消息;不少 OpenAI 兼容上游(尤其是自定义提供商的透传路径)不认识这个角色,收到就报 400。开启本项后,网关在每次转发前把 developer 角色的消息改写为 system —— 两类角色在 OpenAI 语义上等价(OpenAI 自己的迁移指南也是这么映射的),改写不影响对话内容与语义;对本来就只认 system 的各家内置上游同样无害。默认开启;关掉后 developer 消息会原样发往上游,不认识它的上游会报「角色不存在」一类的错误。"),clineHeaders:r("Cline 上游按请求头判定「调用方是不是 Cline 自家产品」:X-CLIENT-TYPE: cline-sdk 是硬门槛(缺了它免费池模型一律 403),版本号与平台标识让请求更贴近官方客户端的形态。这里列出的是网关默认会发的头:改某一行的值 = 覆盖默认值(也可以新增默认清单之外的自定义头);把值清空保存 = 这个头不发送;在界面删掉被改过的行 = 回落默认值。改完点「保存」,下一个 Cline 请求立即生效。不懂某一行的含义就不要动它 —— 伪装头的价值在「与官方客户端逐字一致」。"),cors:r("网关面(/v1/*)默认不应答浏览器跨源请求:页面直连会被预检拦下,只报「无法连接 API」。注意来源是 * —— 未配「网关 Key」时任何网页都能借本机网关打上游,故默认关闭;只给自己用的话,让页面与网关同源(本地反代 /v1)更稳妥。面板接口(/api/*)不受影响。"),prompt:r("客户端(Claude Code / Codex 等 CLI)会在 system 提示词里注入几十句固定模板,上游按逐字匹配审核,命中就整单拦截(HTTP 400)。指纹脱敏能改写实测命中过的那几句,但换一个客户端版本就可能冒出新的。这里可以把 system 整段换成网关自己那份:①「透传」= 不动客户端 system(默认,行为与之前完全一致);②「替换」= 删掉客户端所有 system / developer 消息,换成网关的提示词(客户端项目规范随之消失);③「追加」= 在开头连续 system 块之后插入一条网关提示词,既有消息逐字不动(客户端规范与网关提示词并用)。那份提示词有两个来源:提示词文件(留空用内置默认),或直接在界面上编辑正文 —— 编辑过就以那份正文为准(优先于文件),清空则回到文件 / 内置默认。另外,透传 / 追加模式下撞了内容拦截(多半是指纹误报)时,网关会自动换一段最小中性提示词重试一次,并把「降级期」开到次日 00:00 —— 期间所有请求直接带中性提示词出门,不再先撞一次 400;这里能看见并提前解除它。上面几项是**默认值**:想给某个提供商单独配,用下面的「按提供商」加一行(带「网关自带」的家默认就列在那里)—— 不同的上游对这些内容的接受程度不一样。"),debug:r("开启后,网关会把每次转发**发给上游的请求**(请求头 + 请求体)与**上游返回的响应**(状态码 + 响应头 + 响应体)完整保存到本地,供请求日志页的「详情」查看。请求头里的 Authorization、Cookie、API Key 等凭据字段一律替换成 [redacted],不会明文落盘;请求体与响应体按原样保存(可能包含你的对话内容)。报文只保留最近 500 条,超出后丢弃最旧的。这是排障用的临时开关,不需要时建议关闭。"),io:r("导出会把全部账号与自定义提供商定义写入一个 JSON 文件,可以拷到另一台机器上导入后继续使用。导入采用合并策略:同提供商下按业务身份(UID / userId / apiKey 等)去重 —— 已存在的账号只更新凭证,保留本机原有的优先级顺序;新账号追加到转发顺序末尾,不会抢占当前正在使用的账号;自定义提供商定义按 id 合并,本机缺失时自动补建。"),backup:r("把整份数据库(账号、网关配置、事件日志、请求统计、调试报文与桌面设置)导出成一个 SQL 文件,恢复时整体替换本机全部数据 —— 与上面「账号导入 / 导出」的逐条合并是两种语义:恢复会连配置、日志与当前启用状态一起换成备份那台机器的状态。导出默认包含日志与调试报文(文件可能很大),不需要时可以取消勾选只备业务数据。恢复前会自动把当前数据备份到配置目录的 backups/ 下,恢复后需要重新登录管理面板。"),retention:r("三档数据各自独立计时,超出保留天数的部分会被删除:事件日志是登录、账号切换、429 切换这类系统事件;请求日志是网关每次转发到上游的逐条记录;按天聚合供报表页的热力图与按天趋势使用。请求日志与按天聚合默认 99999 天(约 274 年,效果上等于全量保留),事件日志默认 30 天。把某一档改小(例如 30 天改成 7 天)保存后会立即删除超出的历史数据,此操作不可恢复;改大或保持不变不会删除任何数据。三档的可填范围均为 1–99999 天。"),storage:r("全部数据(账号、事件日志、请求记录、调试报文、设置)统一保存在配置目录下的 agent2api.db 这一个 SQLite 数据库里。备份时只需拷贝这个文件;更换保存位置请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。"),modelsDev:r("models.dev 是一个开源的模型资料库(社区维护),这里用它补齐「上游没告诉我们」的模型能力:上下文窗口、最大输出、工具调用、图片、视频、是否支持思考,以及这个模型认识哪些思考档位。优先级是:你在「模型能力」里手工改的值 > 上游清单自己声明的值 > models.dev 的值 —— 它只填上游没声明的空位,从不推翻上游已声明的。网关随包内嵌了一份快照,所以离线、首次启动、访问不通时也有数据;开启时会按「模型目录刷新」任务的间隔联网更新(同一轮里一并刷新,也可在这里立即刷新)。关闭只停联网刷新,已经拿到的数据继续用。")},Un={timeouts:r("保存后立即生效,不用重启;超时按上游失败处理(计入请求日志的「错误」列,并按「请求重试」的设置决定是否重试)。"),queue:r("等待期间请求日志的状态列会显示「排队中」阶段,详情弹窗的「内部重试」里能逐次看到等待时长;等待用尽后返回 HTTP 503(不是 401/429:既不会刷新凭证,也不会把账号标成限额)。"),captchaPool:r("保存后对下一轮补货立即生效,不用重启。守卫跑在应用界面里:轻量模式关窗销毁界面期间无法铸造,走套餐池(Start Plan)通道的请求会以 503 落进请求日志(写明原因),重新打开界面即自动恢复。"),retry:r("保存后立即生效,不用重启;次数用尽仍失败时,错误原样返回给客户端。"),routing:r("保存后立即生效,不用重启:策略与粘性作用于**下一次**转发的选路,进行中的请求不受影响。"),retryCodes:r("命中这些上游状态码的失败不在同一账号重发,直接换下一个账号继续试(换号占用「切换账号重试次数」,换满仍失败才把错误原样返回给客户端)。输入 100–599 的状态码后回车添加,点 × 或退格删除;默认 402(积分不足)。清空全部表示任何错误都照常重试。"),sanitize:r("默认开启。命中明细会记进请求日志的「敏」标签(悬停可看命中了哪几条规则)。本项只改发给上游的副本,客户端看到的响应内容不变。"),developerRoleCompat:r("默认开启。只改 role 字段本身,消息内容与顺序都不动;本项只改发给上游的副本,客户端看到的响应内容不变。"),clineHeaders:r("值与默认值相同的行不占覆盖(存的是「改过的键」,不是全量配置);值留空保存表示这个头不发送,删除改过的行表示回落默认值。改完立即生效,不用重启。"),cors:r("默认关闭。改动立即生效,不用重启;关闭期间的跨源请求会被预检拦下。"),debug:r("开启后新发生的请求才会被记录(已经过去的请求补不回来)。报文统一存放在本地数据库里,只保留最近 500 条(超出后丢弃最旧的),条数见左侧「数据 → 数据存储」的「调试报文」。"),promptMode:r("替换 = 客户端 system / developer 消息全部删掉,换成网关的提示词;追加 = 保留客户端内容,只在开头 system 块之后多插一条。两者都只改发给上游的副本,客户端看到的响应不变。"),promptFile:r("UTF-8 文本文件路径,留空用内置默认提示词(通用编码助手提示词,不含任何会被上游拦截的模板句)。只在「替换 / 追加」模式下生效,透传模式不读它。"),promptText:r("在这里直接编辑提示词正文:保存后以这份文本为准(优先于上面的提示词文件),清空则回到文件 / 内置默认。改一个字不必再去编辑器里开文件。"),promptGatewayText:r("上游按「结构」校验身份:三段必须各自成块,所以这里分开编辑三段(不能合成一段)。动态段里的运行值写成占位符,发请求时才替换:{cwd} 工作目录、{platform} 平台、{shell} shell、{os_version} 系统版本、{git} 是否 git 仓库、{provider} 提供方标识、{model} 模型名。清空某一段 = 那一段恢复官方原文。第三段发出时前面会自带一个空行(官方形状)。"),prompt:r("改完立即生效,不用重启(下一个请求就用新模式)。降级是自动的临时状态,解除后本模式自己的提示词立刻恢复。"),promptProviders:r("「用哪份提示词」本来是按上游分别决定的事,所以每家可以有自己的模式、提示词文件与正文:不在这里列出的提供商一律沿用上面那份默认设置,「跟随默认」把这一家取消、回到默认。带「网关自带」那一行的家(目前是 ZCode 两家)默认就列在这里 —— 那一段是网关自己装上去的文本,不来自客户端也不来自提示词文件,默认开启、可以关掉,也可以改它的正文(改过的那一段以你的文本为准);关掉后还能不能通过上游校验,取决于上游当下的口径,行内说明了实测依据。「模式 / 提示词文件 / 正文」管的是客户端自己的 system 怎么处理,与那个开关是两件事。"),modelsDev:r("「模型管理」里每个能力的来源标记(上游 / models.dev / 已覆盖)读的就是这份数据;映射弹窗的思考档位下拉也按这里的数据只列该模型真实认识的档位。"),retention:r("保存后立即生效:改小保留天数会立刻删除超出的历史数据,且不可恢复。"),storageFile:r("配置目录下的 agent2api.db(账号、日志、请求记录、调试报文与设置都在里面)。"),storageSize:r("库主文件的大小(不含运行期间的 WAL 临时文件,退出时已自动并回)。"),storage:r("数据统一保存在配置目录下的 agent2api.db;如需更换位置,请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。"),captcha:r("开启后,登录页在浏览器后台自动完成验证(对真人无感),而脚本每次尝试都要先算一道题 —— 暴力破解与抢注的成本显著上升。仅影响面板的登录 / 注册,与 API 客户端的 API Key 无关。"),panelLogin:r("当前浏览器以管理员身份登录着本面板。「退出登录」会撤销这台设备的登录会话(30 天内的自动续期一并失效),需要重新输入账号密码;其他已登录的设备不受影响。"),exportDanger:r("导出文件内含 accessToken / refreshToken / apiKey 等凭证与自定义提供商定义,可直接用于登录。请妥善保管,不要外传或上传到公共位置。"),backupDanger:r("备份文件内含全部账号凭证、网关 API Key 与面板管理员凭据,可直接用于登录。请妥善保管,不要外传或上传到公共位置。")},kt={appLoading:"—",appUnavailable:r("主进程未返回启动设置,请更新桌面端后重试"),appTrayOn:r("关闭窗口时程序不退出,转发继续在后台运行;退出请用托盘菜单"),appTrayOff:r("关闭窗口即退出程序,后台转发随之中断"),appLightOn:r("轻量模式已开启:关窗即销毁界面进程(释放约 200MB 内存),网关在后台继续;界面销毁期间 ZCode 套餐池(Start Plan)通道不可用,相关请求会在请求日志里注明原因"),appLightOff:r("未开启轻量模式:关窗仅隐藏窗口,界面进程常驻内存,随时打开不等待"),unitsOn:r("当前显示为「1.2亿 / 8400万」这类中文量级。"),unitsOff:r("当前显示为「1.20M / 8.4k」这类英文缩写。"),debugUnavailable:r("未能读取调试模式设置,请稍后重试"),debugOn:r("正在保存上游原始报文:"),debugOff:r("未开启,转发时不保存任何原始报文。"),sanitizeUnavailable:r("未能读取指纹脱敏设置,请稍后重试"),sanitizeOn:r("正在剥离出站请求里的审核指纹:表头键值整段删除,模板句最小改写。"),sanitizeOff:r("未开启,客户端 system 模板会原样发往上游,可能被内容审核误拦(400)。"),roleCompatUnavailable:r("未能读取开发者角色兼容设置,请稍后重试"),roleCompatOn:r("正在归一角色:developer 角色消息转发前改写为 system。"),roleCompatOff:r("未开启,developer 角色消息会原样发往上游,不认识该角色的上游会报错(400)。"),corsUnavailable:r("未能读取网关跨域访问设置,请稍后重试"),corsOn:r("正在应答跨源请求:浏览器跨域请求可直连 /v1/*。"),corsOff:r("未开启,浏览器跨域无法直连网关。"),modelsDevUnavailable:r("未能读取 models.dev 元数据状态,请稍后重试"),modelsDevSourceBuiltin:r("随包内嵌的快照(发版时的那份)"),modelsDevSourceCache:r("上次成功刷新的结果(存在本机数据库里)"),modelsDevSourceNetwork:r("刚刚从 models.dev 拉到的最新数据"),modelsDevEmpty:r("还没有装载任何数据。"),promptUnavailable:r("未能读取系统提示词设置,请稍后重试"),degradeUntilFallback:r("次日 00:00"),themeSystem:r("当前跟随操作系统的深浅色设置,系统切换时界面会自动跟上。"),themeLight:r("当前固定为浅色模式,不随系统变化。"),themeDark:r("当前固定为深色模式,不随系统变化。"),zoomWeb:r("网页端的界面缩放由浏览器自己控制(Ctrl + / Ctrl -,或浏览器菜单里的缩放),此项不可调。"),zoomDefault:r("当前按 100% 显示(默认比例)。"),languageHint:r("界面语言只保存在本机(localStorage)。切换后整页立即刷新,不影响账号、密钥与转发。")},_x=[{value:"order",label:r("顺序优先"),tip:r("排第 1 的接走所有请求,直到它不能答(限流 / 故障 / 额度尽)才轮到下一位 —— 就是「从上往下」的现状行为。")},{value:"rotate",label:r("负载均衡"),tip:r("每条请求轮换到下一个可用账号;新会话交给当前最闲的那位。适合想把手里的号摊开用、不让某一个一直扛。")},{value:"provider",label:r("家内轮询"),tip:r("家之间仍按优先级,只有同一家内部轮流用各账号 —— 想优先用某几家、又不愿把某一家的某个账号焊死时用它。")}],Lx=[{value:"auto",label:r("自动"),tip:r("同一会话在 5 分钟内尽量跟住同一个账号;停了 5 分钟再发就算新会话。多数场景用它 —— 多轮对话少换号,也不至于把账号焊死。")},{value:"session",label:r("会话内"),tip:r("同一会话在 6 小时内跟住同一个账号;适合一坐几个小时的长会话,上游的上下文缓存能吃到更多。")},{value:"turn",label:r("每轮"),tip:r("每条请求都重新按策略选路 —— 行为与「关闭」一致,仅保留会话识别的记录。")},{value:"off",label:r("关闭"),tip:r("不做会话识别,每条请求都重新按策略选路。")}],jW=[{key:"routingFailoverBackoff",label:r("失败退避"),defaultOn:!0,tip:r("转发失败的账号休息一会儿再回来:15 分钟起步,连续失败翻倍,封顶 1 小时;成功一次立即恢复。目的是「让这次失败先凉一会儿」,免得一条条地反复撞同一个坏账号。")},{key:"routingSink",label:r("限流垫底"),defaultOn:!0,tip:r("还有额度时被 429 的账号排到队尾,30 分钟后自动回到队列(成功转发一次也会立即回来)。它标记的是「疑似共享池被别人抢」,不是「这个账号本身有问题」。⚠️ 注意它与路由策略的配合:在「家内轮询」下,队尾意味着这一家整体被往后排 —— 如果某个提供商的账号被批量限流,它们会集体沉到队尾,那个家可能长时间轮不到。这时可以关掉本项,让 429 过的账号只受冷却约束(冷却到点自动回来)。")},{key:"routingBreakerEnabled",label:r("熔断"),defaultOn:!1,tip:r("连续失败到阈值后把这个账号整个打开(默认关)。与失败退避的分工:退避管「单次失败先凉一会儿」,惩罚轻、自动恢复;熔断管「这个账号现在整体不对劲,别再一条条地试」,要连续成功若干次才关。打开期间该账号被跳过,除非全场只剩它 —— 那时仍放行试一次。")}],YI=[{key:"zcodeCaptchaPoolTarget",id:"settings-captcha-pool-target",label:r("库存目标"),unit:r("个"),min:1,max:10,hint:r("ZCode 套餐池(Start Plan)通道每条请求都要消耗一个当次铸造的人机验证令牌(一次性,2 分钟过期),库存低于该值时界面自动补铸到它。默认 3 个。调大能吸收突发并发,但用不掉的令牌 2 分钟就过期作废,烧的是上游风控配额。可填 1–10。")},{key:"zcodeCaptchaPoolPollMs",id:"settings-captcha-pool-poll-ms",label:r("轮询间隔"),unit:r("毫秒"),min:1e3,max:3e4,hint:r("铸造守卫每隔多久看一次库存(毫秒)。只影响发现缺货的反应速度 —— 库存够用时轮询不会铸造任何令牌。默认 2500 毫秒,可填 1000–30000。")},{key:"zcodeCaptchaPoolMaxPerRound",id:"settings-captcha-pool-max-per-round",label:r("单轮铸造上限"),unit:r("个"),min:1,max:5,hint:r("发现缺货时一轮最多铸几个(连铸之间固定隔 500 毫秒)。默认 1 个:SDK 同一实例连铸容易互相干扰。调大补货更快,但单轮连铸越多越容易触发上游风控。可填 1–5。")}];function XI(){return tt().workbuddyDesktop?.platform==="web"}let ht={...{category:Ix[0].id,scrollReset:0,busy:null,app:{status:"loading",closeToTray:!1,autostart:!1,lanAccess:!1,lanPanel:!1,lightweightMode:!1,lanIp:null,port:0,adminRegistered:null},unitsChinese:!0,retention:{status:"loading",values:null},retry:{status:"loading",values:null},retryCodes:null,timeouts:{status:"loading",values:null},queue:{status:"loading",values:null},captchaPool:{status:"loading",values:null},routing:{status:"loading",strategy:"order",sticky:"auto",failoverBackoff:!0,sink:!0,breakerEnabled:!1},debug:{status:"loading",on:!1,count:null,limit:null},sanitize:{status:"loading",on:!1},developerRoleCompat:{status:"loading",on:!1},clineHeaders:{status:"loading",defaults:{},overrides:{},effective:{}},cors:{status:"loading",on:!1},prompt:{status:"loading",mode:"passthrough",file:"",text:"",source:"",lines:0,fileError:"",degradeActive:!1,degradeUntilText:"",providers:[],gateway:{},gatewayText:{},options:[]},storage:{status:"loading",available:!0,file:"",bytes:0,accounts:null,logs:null,requests:null,dailyDays:null,debug:null},captcha:{available:!0,enabled:!1},modelsDev:{status:"loading",enabled:!0,source:"",updatedAt:0,count:0},ioFailure:null,retentionConfirm:null,dbIncludeLogs:!0,dbBackupConfirm:null,dbWarning:null,lanConfirm:null,lanRegister:null,panelLogin:!1},panelLogin:XI()};const gS=new Set;function wW(e){return gS.add(e),()=>{gS.delete(e)}}function SW(){return ht}function NW(){return x.useSyncExternalStore(wW,SW)}function Xe(e){ht={...ht,...e};for(const t of gS)t()}function Ma(e){Xe({app:{...ht.app,...e}})}function Da(){Xe({})}let Sn=null;function is(e){Sn=e,Xe({busy:e})}function Jn(){Sn=null,Xe({busy:null})}function xS(e){const t=Ix.some(a=>a.id===e)?String(e):Ix[0].id;Xe({category:t,scrollReset:ht.scrollReset+1})}function kW(e){xS(e);try{localStorage.setItem(_I,e)}catch{}}function QI(){let e=null;try{e=localStorage.getItem(_I)}catch{e=null}xS(e)}function ZI(){Xe({unitsChinese:tt().wbUnits?.isChinese?.()!==!1})}function CW(e){tt().wbUnits?.setChinese?.(e),ZI(),Ve(r(e?"✅ 已改用本地量级词(万 / 亿式)":"✅ 已改用 k / M 缩写"))}function bS(e){if(e===void 0)return;if(!e||typeof e!="object"){Ma({status:"unavailable"});return}const t=e;Ma({status:"ready",closeToTray:t.closeToTray===!0,autostart:t.autostart===!0,lanAccess:t.lanAccess===!0,lanPanel:t.lanPanel===!0,lightweightMode:t.lightweightMode===!0})}async function WI(){try{bS(await tt().workbuddyDesktop?.getAppSettings())}catch(e){console.warn("读取应用设置失败:",Ot(e)),bS(null)}}async function TW(){const e=tt().workbuddyDesktop;if(e?.localIp){try{Ma({lanIp:await e.localIp()||null})}catch{}try{const t=Number((await e.getBackendStatus?.())?.port)||0;t&&Ma({port:t})}catch{}try{const t=(await e.panelAdminStatus?.())?.registered===!0;Ma({adminRegistered:t})}catch{}}}async function vS(e,t){if(Sn){Da();return}const a=ht.app,o={closeToTray:e==="tray"?t:a.closeToTray,autostart:e==="autostart"?t:a.autostart,lanAccess:a.lanAccess,lanPanel:a.lanPanel,lightweightMode:e==="lightweight"?t:a.lightweightMode};is("app"),Ma(o);const c=r(e==="autostart"?"开机自动启动":e==="lightweight"?"轻量模式":"关闭窗口时最小化到托盘");try{const d=tt().workbuddyDesktop;if(!d)throw new Error(r("主进程桥不可用"));const f=await d.saveAppSettings(o);Ma({status:"ready",...vW(f,o)}),Ve(r("✅ 已更新「{label}」",{label:c}))}catch(d){Ma(a),Ve(r("保存失败:{message}",{message:Ot(d)}),"err")}finally{Jn()}}function EW(e){if(Sn){Da();return}Xe({lanConfirm:{mode:e?"enable":"disable",panel:ht.app.lanPanel}})}function AW(e){if(Sn){Da();return}Xe({lanConfirm:{mode:"panel",panel:e}})}function yS(e){const t=ht.lanConfirm;if(Xe({lanConfirm:null}),!(!t||!e)){if(t.mode==="disable"){zx(!1,!1);return}if(t.mode==="panel"){zx(!0,t.panel);return}RW()}}async function RW(){const e=tt().workbuddyDesktop;if(!e?.panelAdminStatus){Ve(r("当前环境不支持局域网访问设置"),"err");return}is("lan");let t;try{t=(await e.panelAdminStatus())?.registered===!0}catch(a){Jn(),Ve(r("查询管理员状态失败:{message}",{message:Ot(a)}),"err");return}if(Jn(),Ma({adminRegistered:t}),t){await zx(!0,ht.app.lanPanel);return}Xe({lanRegister:{busy:!1,error:""}})}async function OW(e,t){if(Sn)return;const a=tt().workbuddyDesktop;if(!a?.panelRegister)return;const o=e.trim();if(!o||o.length>64){Xe({lanRegister:{busy:!1,error:r("请填写管理员账号(64 字符以内)")}});return}if(t.length<8){Xe({lanRegister:{busy:!1,error:r("密码至少 8 位")}});return}Xe({lanRegister:{busy:!0,error:""}});try{if(await a.panelRegister(o,t),ht.lanRegister===null)return;Xe({lanRegister:null}),Ma({adminRegistered:!0}),await zx(!0,ht.app.lanPanel)}catch(c){Xe({lanRegister:{busy:!1,error:Ot(c)}})}}function JI(){Xe({lanRegister:null})}async function zx(e,t){if(Sn)return;const a=tt().workbuddyDesktop;if(!a?.changeLanAccess){Ve(r("当前环境不支持局域网访问设置"),"err");return}is("lan"),Ma({lanAccess:e,lanPanel:t});try{(await a.changeLanAccess(e,t))?.createdKey&&Ve(r("✅ 已自动创建网关 Key「默认」(可在「网关 Key」页查看)")),Ve(r("✅ 设置已保存,应用正在重启…"))}catch(o){Ve(r("保存失败:{message}",{message:Ot(o)}),"err"),await WI()}finally{Jn()}}async function MW(){if(!Sn){Xe({ioFailure:null}),is("export");try{const e=await tt().workbuddyDesktop?.exportAccounts();if(e?.canceled){Ve(r("已取消导出"));return}const t=Number(e?.count)||0,a=Number(e?.customProviders)||0;if(!t&&!a){Ve(r("没有可导出的账号"),"err");return}const o=a?r("、{n} 个自定义提供商",{n:a}):"",c=e?.file?r(" 到 {file}",{file:e.file}):"";Ve(r("✅ 已导出 {count} 个账号{note}{file}",{count:t,note:o,file:c}))}catch(e){Ve(r("操作失败:{message}",{message:Ot(e)}),"err")}finally{Jn()}}}async function DW(){if(!Sn){Xe({ioFailure:null}),is("import");try{const e=await tt().workbuddyDesktop?.importAccounts();if(e?.canceled){Ve(r("已取消导入"));return}const t=Number(e?.added)||0,a=Number(e?.updated)||0,o=Number(e?.skipped)||0,c=Number(e?.failed)||0,d=Array.isArray(e?.errors)?e.errors:[],f=e?.customProviders??{},p=Number(f.added)||0,h=Number(f.updated)||0,b=[];o&&b.push(r("跳过 {n} 个",{n:o})),c&&b.push(r("失败 {n} 个",{n:c}));const y=b.length?r(",{items}",{items:b.join(r("、"))}):"",v=p||h?r(",自定义提供商新增 {added} 个、更新 {updated} 个",{added:p,updated:h}):"",j=r("新增 {added} 个、更新 {updated} 个{suffix}{note}",{added:t,updated:a,suffix:y,note:v});if(c){Ve(r("导入完成:{summary}",{summary:j}),"err");const w=d.slice(0,3).map(S=>{const N=S?.customProvider?r("自定义提供商 {id}",{id:S?.id||r("(无 id)")}):S?.id??r("未知账号"),k=S?.message??r("未知原因");return r("{label}({message})",{label:N,message:k})}).join(r(";"));Xe({ioFailure:{failed:c,detail:w,more:d.length>3}})}else Ve(r("✅ 导入完成:{summary}",{summary:j}));(t||a||p||h)&&await tt().wbApp?.refresh?.()}catch(e){Ve(r("操作失败:{message}",{message:Ot(e)}),"err")}finally{Jn()}}}function IW(e){Xe({dbIncludeLogs:e})}async function PW(){if(!Sn){is("dbExport");try{const e=tt().workbuddyDesktop;if(!e?.exportDatabase){Ve(r("当前环境不支持整库备份(仅桌面端可用)"),"err");return}const t=await e.exportDatabase(ht.dbIncludeLogs);if(t?.canceled){Ve(r("已取消导出"));return}const a=Number(t?.bytes)||0,o=Array.isArray(t?.tables)?t.tables.length:0;Ve(r("✅ 已备份 {n} 张表({size})到 {file}",{n:o,size:KI(a),file:t?.file||""}))}catch(e){Ve(r("操作失败:{message}",{message:Ot(e)}),"err")}finally{Jn()}}}function _W(){Sn||Xe({dbBackupConfirm:{includeLogs:ht.dbIncludeLogs}})}function jS(e){Xe({dbBackupConfirm:null}),e&&LW()}async function LW(){if(Sn)return;const e=tt().workbuddyDesktop;if(!e?.importDatabase){Ve(r("当前环境不支持整库恢复(仅桌面端可用)"),"err");return}is("dbImport"),Xe({dbWarning:null});try{const t=await e.importDatabase(),a=Array.isArray(t?.tables)?t.tables.length:0,o=t?.safetyBackup?r(",替换前的数据已备份为 {name}",{name:t.safetyBackup}):"";Ve(r("✅ 已恢复 {n} 张表{note}",{n:a,note:o}));const c=typeof t?.warning=="string"?t.warning.trim():"";c&&Xe({dbWarning:c}),await _S(),await tt().wbApp?.refresh?.()}catch(t){Ve(r("恢复失败:{message}",{message:Ot(t)}),"err")}finally{Jn()}}function Bx(e){if(e===void 0)return;const t=GI(pS,e,ht.retention.values);Xe({retention:{status:t===null?"unavailable":"ready",values:t}})}async function Hx(){try{Bx(await tt().workbuddyDesktop?.getRetention())}catch(e){console.warn("读取数据保留设置失败:",Ot(e)),Bx(null)}}let Ux=null;function Vx(e){const t=Ux;Ux=null,Xe({retentionConfirm:null}),t?.(e)}function zW(e){return new Promise(t=>{Ux&&Vx(!1),Ux=t,Xe({retentionConfirm:{head:e}})})}function BW(e,t,a){return t===null?r("没能读到「{label}」的当前值,改为 {days} 天可能会删除超出的历史数据。",{label:e.label,days:a}):r("「{label}」将从 {previous} 天改为 {days} 天。",{label:e.label,previous:t,days:a})}async function HW(e,t){if(Sn||ht.retentionConfirm)return;const a=qI(t,e.min,e.max,r("天数"));if(!a.ok){Ve(a.message,"err");return}const o=ht.retention.values?.[e.key],c=typeof o=="number"&&Number.isInteger(o)?o:null;if(c!==null&&a.value===c)return;const d=c===null||a.valueNumber.isInteger(d?.[f.key]))){const f=d?.[e.key]??t;Ve(a?r("✅ 已保留 {days} 天,超出部分已清理",{days:f}):r("✅ 已保留 {days} 天",{days:f}));return}await Hx(),Ve(a?r("✅ 已保留 {days} 天,超出部分已清理",{days:t}):r("✅ 已保留 {days} 天",{days:t}))}catch(c){Ve(r("保存失败:{message}",{message:Ot(c)}),"err"),await Hx()}finally{Jn()}}function Rp(e,t){if(t===void 0)return;const a=GI(e.fields,t,e.read().values);e.write({status:a===null?"unavailable":"ready",values:a}),e.syncExtras?.(a===null?null:t)}async function ed(e){try{Rp(e,await e.get())}catch(t){console.warn(`${e.consoleLabel}失败:`,Ot(t)),Rp(e,null)}}async function Fx(e,t,a){if(Sn)return;const o=qI(a,t.min,t.max,t.label);if(!o.ok){Ve(o.message,"err");return}const c=e.read().values?.[t.key];if(Number.isInteger(c)&&o.value===c)return;is(e.scope);const d=e.read().values;d&&e.write({status:"ready",values:{...d,[t.key]:o.value}});try{const f=await e.save({[t.key]:o.value});Rp(e,f);const p=e.read().values?.[t.key],h=Number.isInteger(p)?p:o.value;Ve(r("✅ 已保存:{label} {value}",{label:t.label,value:String(h)}))}catch(f){Ve(r("保存失败:{message}",{message:Ot(f)}),"err"),await ed(e)}finally{Jn()}}const eP={scope:"queue",fields:VI,consoleLabel:"读取排队等待设置",get:()=>tt().workbuddyDesktop?.getQueue(),save:e=>tt().workbuddyDesktop?.saveQueue(e),read:()=>ht.queue,write:e=>Xe({queue:e})};async function tP(){await ed(eP)}async function VW(e,t){await Fx(eP,e,t)}const nP={scope:"captchaPool",fields:YI,consoleLabel:"读取验证码令牌池设置",get:()=>tt().workbuddyDesktop?.getCaptchaPool(),save:e=>tt().workbuddyDesktop?.saveCaptchaPool(e),read:()=>ht.captchaPool,write:e=>Xe({captchaPool:e})};async function sP(){await ed(nP)}async function FW(e,t){await Fx(nP,e,t)}const rP="routingStrategy",aP="routingSticky",$W="routingFailoverBackoff",qW="routingSink",GW="routingBreakerEnabled",KW={[rP]:new Set(_x.map(e=>e.value)),[aP]:new Set(Lx.map(e=>e.value))};function lP(e){const t=e&&typeof e=="object"?e:{},a=(c,d)=>{const f=t[c];return typeof f=="string"&&KW[c].has(f)?f:d},o=(c,d)=>{const f=t[c];return typeof f=="boolean"?f:d};return{strategy:a(rP,"order"),sticky:a(aP,"auto"),failoverBackoff:o($W,!0),sink:o(qW,!0),breakerEnabled:o(GW,!1)}}async function $x(){try{const e=await tt().workbuddyDesktop?.getRoutingConfig();if(e===void 0){Xe({routing:{...ht.routing,status:"unavailable"}});return}Xe({routing:{status:"ready",...lP(e)}})}catch(e){console.warn("读取路由设置失败:",Ot(e)),Xe({routing:{...ht.routing,status:"unavailable"}})}}async function wS(e){if(Sn)return;const t=tt().workbuddyDesktop;if(!t?.setRoutingConfig){Ve(r("桌面运行时不可用,暂时无法保存"),"err");return}is("routing");try{const a=await t.setRoutingConfig(e);a!==void 0?Xe({routing:{status:"ready",...lP(a)}}):await $x(),Ve(r("✅ 路由设置已保存"))}catch(a){Ve(r("保存失败:{message}",{message:Ot(a)}),"err"),await $x()}finally{Jn()}}const Op={scope:"retry",fields:HI,consoleLabel:"读取请求重试设置",get:()=>tt().workbuddyDesktop?.getRetry(),save:e=>tt().workbuddyDesktop?.saveRetry(e),read:()=>ht.retry,write:e=>Xe({retry:e}),syncExtras:e=>{if(e===null){Xe({retryCodes:null});return}const t=e[FI];if(!Array.isArray(t))return;Xe({retryCodes:t.filter(o=>typeof o=="number"&&Number.isInteger(o)&&o>=mS&&o<=hS)})}};function YW(e){Rp(Op,e)}async function oP(){await ed(Op)}async function XW(e,t){await Fx(Op,e,t)}async function SS(e){if(!Sn){Xe({retryCodes:e}),is("codes");try{const t=await tt().workbuddyDesktop?.saveRetry({[FI]:e});Rp(Op,t),Ve(r("✅ 已保存:指定错误码直接换号"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await ed(Op)}finally{Jn()}}}async function QW(e){const t=ht.retryCodes;if(t===null)return;const a=String(e??"").trim();if(!a)return;if(!/^\d+$/.test(a)||Number(a)hS){Ve(r("状态码必须是 {min}–{max} 的整数(收到: {text})",{min:mS,max:hS,text:a}),"err");return}const o=Number(a);if(t.includes(o)){Ve(r("状态码 {code} 已在名单里",{code:o}));return}if(t.length>=$I){Ve(r("名单最多 {max} 个状态码",{max:$I}),"err");return}await SS([...t,o])}async function ZW(e){const t=ht.retryCodes;!t||!t.includes(e)||await SS(t.filter(a=>a!==e))}async function WW(){const e=ht.retryCodes;e?.length&&await SS(e.slice(0,-1))}const iP={scope:"timeouts",fields:UI,consoleLabel:"读取请求超时设置",get:()=>tt().workbuddyDesktop?.getTimeouts(),save:e=>tt().workbuddyDesktop?.saveTimeouts(e),read:()=>ht.timeouts,write:e=>Xe({timeouts:e})};async function cP(){await ed(iP)}async function JW(e,t){await Fx(iP,e,t)}function qx(e){if(e===void 0)return;if(!e||typeof e!="object"){Xe({debug:{status:"unavailable",on:!1,count:null,limit:null}});return}const t=e,a=Number(t.count),o=Number(t.limit);Xe({debug:{status:"ready",on:t.debugMode===!0,count:Number.isInteger(a)?a:null,limit:Number.isInteger(o)?o:null}})}async function NS(){try{qx(await tt().workbuddyDesktop?.getDebug())}catch(e){console.warn("读取调试模式设置失败:",Ot(e)),qx(null)}}async function eJ(e){if(Sn){Da();return}is("debug"),Xe({debug:{...ht.debug,on:e}});try{const t=await tt().workbuddyDesktop?.saveDebug(e);qx(t),Ve(r(e?"✅ 调试模式已开启":"✅ 调试模式已关闭"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await NS()}finally{Jn()}}function Gx(e){if(e===void 0)return;if(!e||typeof e!="object"){Xe({sanitize:{status:"unavailable",on:!1}});return}Xe({sanitize:{status:"ready",on:e.sanitizeBlacklistFingerprints===!0}})}async function kS(){try{Gx(await tt().workbuddyDesktop?.getSanitize())}catch(e){console.warn("读取出站指纹脱敏设置失败:",Ot(e)),Gx(null)}}async function tJ(e){if(Sn){Da();return}is("sanitize"),Xe({sanitize:{status:"ready",on:e}});try{const t=await tt().workbuddyDesktop?.saveSanitize(e);Gx(t),Ve(r(e?"✅ 出站指纹脱敏已开启":"已关闭出站指纹脱敏"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await kS()}finally{Jn()}}function CS(e){if(e===void 0)return;if(!e||typeof e!="object"){Xe({developerRoleCompat:{status:"unavailable",on:!1}});return}Xe({developerRoleCompat:{status:"ready",on:e.developerRoleCompat===!0}})}async function TS(){try{CS(await tt().workbuddyDesktop?.getDeveloperRoleCompat())}catch(e){console.warn("读取开发者角色兼容设置失败:",Ot(e)),CS(null)}}async function nJ(e){if(Sn){Da();return}is("developerRoleCompat"),Xe({developerRoleCompat:{status:"ready",on:e}});try{const t=await tt().workbuddyDesktop?.saveDeveloperRoleCompat(e);CS(t),Ve(r(e?"✅ 开发者角色兼容已开启":"已关闭开发者角色兼容"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await TS()}finally{Jn()}}function ES(e){if(!e||typeof e!="object")return{};const t={};for(const[a,o]of Object.entries(e))typeof o=="string"&&(t[a]=o);return t}function AS(e){if(e===void 0)return;if(!e||typeof e!="object"){Xe({clineHeaders:{status:"unavailable",defaults:{},overrides:{},effective:{}}});return}const t=e;Xe({clineHeaders:{status:"ready",defaults:ES(t.defaults),overrides:ES(t.overrides),effective:ES(t.effective)}})}async function RS(){try{AS(await tt().workbuddyDesktop?.getClineHeaders())}catch(e){console.warn("读取 Cline 伪装头失败:",Ot(e)),AS(null)}}async function sJ(){await RS(),Ve(r("Cline 伪装头已刷新"))}async function uP(e){if(Sn){Da();return}is("clineHeaders");try{const t=await tt().workbuddyDesktop?.saveClineHeaders(e);AS(t),Ve(r("✅ Cline 伪装头已保存"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await RS()}finally{Jn()}}function OS(e){if(e===void 0)return;if(!e||typeof e!="object"){Xe({cors:{status:"unavailable",on:!1}});return}Xe({cors:{status:"ready",on:e.corsEnabled===!0}})}async function MS(){try{OS(await tt().workbuddyDesktop?.getCors())}catch(e){console.warn("读取网关跨域访问设置失败:",Ot(e)),OS(null)}}async function rJ(e){if(Sn){Da();return}is("cors"),Xe({cors:{status:"ready",on:e}});try{const t=await tt().workbuddyDesktop?.saveCors(e);OS(t),Ve(r(e?"网关跨域访问已开启":"已关闭网关跨域访问"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await MS()}finally{Jn()}}async function dP(){try{const e=await tt().workbuddyDesktop?.getCaptchaSetting();Xe({captcha:{available:!0,enabled:e?.captchaEnabled===!0}})}catch(e){Xe({captcha:{available:!1,enabled:!1}}),console.warn("读取机器人校验设置失败:",Ot(e))}}async function aJ(e){if(Sn){Da();return}is("captcha"),Xe({captcha:{...ht.captcha,enabled:e}});try{const t=await tt().workbuddyDesktop?.saveCaptchaSetting(e);Xe({captcha:{available:!0,enabled:t?.captchaEnabled===!0}}),Ve(r(e?"✅ 机器人校验已开启":"⚠️ 机器人校验已关闭"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await dP()}finally{Jn()}}function DS(e){return!e||typeof e!="object"?{status:"unavailable",enabled:!0,source:"",updatedAt:0,count:0}:{status:"ready",enabled:e.modelsDevEnabled!==!1,source:typeof e.source=="string"?e.source:"",updatedAt:Number(e.updatedAt)||0,count:Number(e.count)||0}}async function IS(){try{const e=await tt().workbuddyDesktop?.getModelsDev?.();Xe({modelsDev:DS(e)})}catch(e){Xe({modelsDev:{...ht.modelsDev,status:"unavailable"}}),console.warn("读取 models.dev 元数据设置失败:",Ot(e))}}async function lJ(e){if(Sn){Da();return}is("modelsDev"),Xe({modelsDev:{...ht.modelsDev,enabled:e}});try{const t=await tt().workbuddyDesktop?.saveModelsDev?.(e);Xe({modelsDev:DS(t)}),Ve(r(e?"✅ models.dev 模型元数据已开启":"已关闭 models.dev 联网刷新"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await IS()}finally{Jn()}}async function oJ(){if(Sn){Da();return}is("modelsDevRefresh");try{const e=await tt().workbuddyDesktop?.refreshModelsDev?.();Xe({modelsDev:DS(e?.status)});const t=e?.result,a=String(t?.status||"");a==="refreshed"?Ve(r("models.dev 元数据已更新({count} 条)",{count:Number(t?.count)||0})):a==="failed"?Ve(r("刷新失败:{message}",{message:String(t?.message||"")}),"err"):Ve(String(t?.message||r("本次无需刷新")))}catch(e){Ve(r("刷新失败:{message}",{message:Ot(e)}),"err"),await IS()}finally{Jn()}}function td(e){if(e===void 0)return;if(!e||typeof e!="object"){Xe({prompt:{...ht.prompt,status:"unavailable"}});return}const t=e,a=uJ(t.promptGateway);Xe({prompt:{status:"ready",mode:String(t.promptMode||"passthrough"),file:String(t.promptFile||""),text:String(t.promptText||""),source:String(t.promptSource||""),lines:Number(t.promptLines)||0,fileError:String(t.promptFileError||""),degradeActive:t.degradeActive===!0,degradeUntilText:String(t.degradeUntilText||""),providers:iJ(t.promptProviders,a),gateway:a,gatewayText:dJ(t.promptGatewayText),options:cJ(t.promptProviderOptions)}})}function iJ(e,t){return!e||typeof e!="object"?[]:Object.entries(e).filter(([a,o])=>!!a&&!!o&&typeof o=="object").map(([a,o])=>{const c=o;return{id:a,mode:String(c.promptMode||"passthrough"),file:String(c.promptFile||""),text:String(c.promptText||""),source:String(c.promptSource||""),lines:Number(c.promptLines)||0,fileError:String(c.promptFileError||""),gateway:t[a]??!0,configured:!0}}).sort((a,o)=>a.id.localeCompare(o.id))}function cJ(e){return Array.isArray(e)?e.filter(t=>!!t&&typeof t=="object"&&String(t.id||"")).map(t=>{const a=t;return{id:String(a.id),label:String(a.label||a.id),gatewayNote:String(a.gatewayNote||""),gatewayChars:Number(a.gatewayChars)||0,gatewayBlocks:fJ(a.gatewayBlocks)}}):[]}function uJ(e){if(!e||typeof e!="object")return{};const t={};for(const[a,o]of Object.entries(e)){const c=String(a||"").trim();!c||typeof o!="boolean"||(t[c]=o)}return t}function dJ(e){if(!e||typeof e!="object")return{};const t={};for(const[a,o]of Object.entries(e)){const c=String(a||"").trim();if(!c||!o||typeof o!="object")continue;const d=o,f={identity:"",stable:"",dynamic:""};let p=!1;for(const h of["identity","stable","dynamic"]){const b=String(d[h]||"");f[h]=b,b.trim()&&(p=!0)}p&&(t[c]=f)}return t}function fJ(e){if(!e||typeof e!="object")return null;const t=e,a={identity:String(t.identity||""),stable:String(t.stable||""),dynamic:String(t.dynamic||"")};return!a.identity.trim()&&!a.stable.trim()&&!a.dynamic.trim()?null:a}async function dc(){try{td(await tt().workbuddyDesktop?.getPrompt())}catch(e){console.warn("读取系统提示词设置失败:",Ot(e)),td(null)}}async function Mp(e,t){if(Sn)return await dc(),!1;is("prompt");try{const a=await tt().workbuddyDesktop?.savePrompt(t);return td(a),Ve(r("✅ 已保存:{label}",{label:e})),!0}catch(a){return Ve(r("保存失败:{message}",{message:Ot(a)}),"err"),await dc(),!1}finally{Jn()}}async function pJ(e){const t=$o.find(a=>a.value===e);await Mp(r("模式改为「{mode}」",{mode:t?.toastLabel??e}),{promptMode:e})}async function mJ(e){await Mp(e.trim()?r("提示词文件已更新"):r("已改回内置默认提示词"),{promptFile:e})}async function hJ(e){const t=!e.trim();return Mp(r(t?"已清掉界面编辑的提示词(回落文件 / 内置默认)":"提示词正文已更新"),{promptText:t?"":e})}function qo(e,t){return e.options.find(a=>a.id===t)?.label||t}function fc(e){const t=e==null?"":String(e).trim();return t&&ht.prompt.options.some(a=>a.id===t)?t:""}async function Dp(e,t,a){return Mp(e,{promptProviders:{[t]:a}})}function Kx(e,t){const a=!t.configured;return{promptMode:t.mode,promptFile:t.file,promptText:a&&e.source==="inline"?e.text:""}}async function gJ(e){const t=fc(e);if(!t)return;const a=ht.prompt;await Dp(r("已为「{label}」单独配置提示词",{label:qo(a,t)}),t,Kx(a,{mode:a.mode,file:a.file,text:a.text,source:a.source,lines:a.lines,fileError:a.fileError,gateway:a.gateway[t]??!0,configured:!1}))}async function xJ(e,t){const a=fc(e.id);if(!a||!$o.some(c=>c.value===t))return;const o=$o.find(c=>c.value===t);await Dp(r("「{label}」的模式改为「{mode}」",{label:qo(ht.prompt,a),mode:o?.toastLabel??t}),a,{...Kx(ht.prompt,e),promptMode:t})}async function bJ(e,t){const a=fc(e.id);a&&await Dp(t.trim()?r("「{label}」的提示词文件已更新",{label:qo(ht.prompt,a)}):r("「{label}」改用内置默认提示词",{label:qo(ht.prompt,a)}),a,{...Kx(ht.prompt,e),promptFile:t})}async function vJ(e,t){const a=fc(e.id);if(!a)return!1;const o=!t.trim(),c=qo(ht.prompt,a);return Dp(o?r("已清掉「{label}」界面编辑的正文",{label:c}):r("「{label}」的提示词正文已更新",{label:c}),a,{...Kx(ht.prompt,e),promptText:o?"":t})}async function yJ(e){const t=fc(e);t&&await Dp(r("已取消「{label}」的单独配置",{label:qo(ht.prompt,t)}),t,null)}async function jJ(e,t){const a=fc(e);if(!a)return;const o=qo(ht.prompt,a);if(Sn){await dc();return}is("prompt");try{const c=await tt().workbuddyDesktop?.savePrompt({promptGateway:{[a]:t}});td(c),Ve(t?r("✅ 已为「{label}」装上网关自带提示词",{label:o}):r("已关闭「{label}」的网关自带提示词:能否通过上游校验取决于上游当前口径",{label:o}),t?"ok":"err")}catch(c){Ve(r("保存失败:{message}",{message:Ot(c)}),"err"),await dc()}finally{Jn()}}async function wJ(e,t){const a=fc(e);if(!a)return!1;const o=qo(ht.prompt,a),c=!t||!(t.identity.trim()||t.stable.trim()||t.dynamic.trim());return Mp(c?r("「{label}」的网关自带提示词已改回官方原文",{label:o}):r("「{label}」的网关自带提示词正文已更新",{label:o}),{promptGatewayText:{[a]:c?null:t}})}async function SJ(){if(!Sn){is("prompt");try{const e=await tt().workbuddyDesktop?.savePrompt({clearDegrade:!0});td(e),Ve(r("✅ 已解除内容拦截降级"))}catch(e){Ve(r("解除失败:{message}",{message:Ot(e)}),"err"),await dc()}finally{Jn()}}}function Ip(e){const t=Number(e);return Number.isFinite(t)?t:null}function PS(e){if(e===void 0)return;const t=e&&typeof e=="object"?e.database:null;if(!t||typeof t!="object"){Xe({storage:{...ht.storage,status:"unavailable"}});return}const a=t;Xe({storage:{status:"ready",available:a.available!==!1,file:String(a.file||""),bytes:Number(a.bytes)||0,accounts:Ip(a.accounts),logs:Ip(a.logs),requests:Ip(a.requests),dailyDays:Ip(a.dailyDays),debug:Ip(a.debug)}})}async function fP(){try{PS(await tt().workbuddyDesktop?.getStorage())}catch(e){console.warn("读取数据存储概况失败:",Ot(e)),PS(null)}}async function NJ(){try{return await tt().workbuddyDesktop?.panelLogout(),window.location.href="/login",!0}catch(e){return Ve(r("退出失败:{message}",{message:Ot(e)}),"err"),!1}}async function _S(){QI(),ZI(),Xe({panelLogin:XI()}),await Promise.all([WI(),TW(),Hx(),oP(),cP(),tP(),sP(),$x(),NS(),kS(),TS(),RS(),MS(),dc(),fP(),dP(),IS(),tt().wbUpdatePanel?.load?.()])}async function kJ(){await Hx(),Ve(r("保留天数已刷新"))}async function CJ(){await oP(),Ve(r("重试设置已刷新"))}async function TJ(){await cP(),Ve(r("超时设置已刷新"))}async function EJ(){await tP(),Ve(r("排队等待设置已刷新"))}async function AJ(){await sP(),Ve(r("验证码令牌池设置已刷新"))}async function RJ(){await $x(),Ve(r("路由设置已刷新"))}async function OJ(){await NS(),Ve(r("调试模式设置已刷新"))}async function MJ(){await kS(),Ve(r("指纹脱敏设置已刷新"))}async function DJ(){await TS(),Ve(r("开发者角色兼容设置已刷新"))}async function IJ(){await MS(),Ve(r("网关跨域访问设置已刷新"))}async function PJ(){await dc(),Ve(r("系统提示词设置已刷新"))}async function _J(){await fP(),Ve(r("存储概况已刷新"))}const Yx=[{key:"identity",label:r("第一段(身份句)"),short:r("第一段"),hint:r("官方原文只有一句:声明自己是 ZCode。")},{key:"stable",label:r("第二段(稳定段)"),short:r("第二段"),hint:r("工具用法与环境说明那一大段。")},{key:"dynamic",label:r("第三段(动态段)"),short:r("第三段"),hint:r("沟通方式 / 上下文管理 + Environment 段。")}];function LJ(e){return r(e==="inline"?"界面里编辑的正文":e==="file"?"提示词文件":e==="builtin"?"内置默认提示词":"(未生效)")}function pP({title:e,text:t,source:a,file:o,disabled:c,save:d,onClose:f}){const[p,h]=x.useState(t),[b,y]=x.useState(!1),v=b||c,j=p!==t;async function w(S){y(!0);try{await d(S)&&f()}finally{y(!1)}}return s.jsx(Yt,{open:!0,onOpenChange:S=>{S||v||f()},children:s.jsxs(Xt,{className:"w-[min(880px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:e})}),s.jsxs(Lt,{children:[s.jsxs(dn,{children:[s.jsx("h3",{children:r("保存后以这里的文本为准")}),s.jsxs("p",{children:[r("这一份就是「替换 / 追加」模式下网关要发的 system 正文。"),a==="none"?r("当前是透传模式:存下来也不会发出,切成「替换 / 追加」之后才生效。"):r("当前生效的正文来自{source}{file}。保存后这一份会盖过提示词文件与内置默认{note}。",{source:LJ(a),file:o.trim()&&a!=="inline"?r("(文件:{path})",{path:o.trim()}):"",note:a!=="inline"&&o.trim()?r(",文件里后续的改动在清空这里之前不再生效"):""}),r("想回到原来那份,把这里清空再保存即可(不会删掉你配的文件路径)。")]})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"settings-prompt-body",children:r("提示词正文")}),s.jsx(Mi,{id:"settings-prompt-body",rows:18,className:"min-h-[320px]",spellCheck:!1,disabled:v,value:p,onChange:S=>h(S.currentTarget.value)}),s.jsx("div",{className:"hint",children:p.trim()?r("{lines} 行、{chars} 字符(按当前草稿计)",{lines:p.split(` -`).length,chars:p.length}):r("空 = 不要这一份,回落提示词文件 / 内置默认提示词。")})]})]}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"outline",className:"mr-auto",disabled:v||a!=="inline",onClick:()=>{w("")},children:r("清除这里的正文")}),s.jsx(ne,{variant:"outline",disabled:v,onClick:f,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:v||!j,onClick:()=>{w(p)},children:r(b?"保存中…":"保存")})]})]})})}function zJ({prompt:e,locked:t,busy:a}){const[o,c]=x.useState(!1);return s.jsxs(s.Fragment,{children:[s.jsx(ne,{id:"btn-prompt-edit-body",variant:"outline",disabled:t||a,onClick:()=>c(!0),children:r("编辑正文")}),o?s.jsx(pP,{title:r("编辑提示词正文 · 全局"),text:e.text,source:e.source,file:e.file,disabled:a,save:hJ,onClose:()=>c(!1)}):null]})}function BJ({item:e,label:t,locked:a,busy:o}){const[c,d]=x.useState(!1);return s.jsxs(s.Fragment,{children:[s.jsx(ne,{id:`btn-prompt-edit-body-${e.id}`,variant:"outline",disabled:a||o,onClick:()=>d(!0),children:r("编辑正文")}),c?s.jsx(pP,{title:r("编辑提示词正文 · {label}",{label:t}),text:e.text,source:e.source,file:e.file,disabled:o,save:f=>vJ(e,f),onClose:()=>d(!1)}):null]})}function HJ({id:e,title:t,official:a,over:o,enabled:c,disabled:d,onClose:f}){const[p,h]=x.useState(()=>({identity:o?.identity?.trim()?o.identity:a.identity,stable:o?.stable?.trim()?o.stable:a.stable,dynamic:o?.dynamic?.trim()?o.dynamic:a.dynamic})),[b,y]=x.useState(!1),v=b||d,j=Yx.some(N=>p[N.key]!==a[N.key]),w=Yx.filter(N=>o?.[N.key]?.trim()).map(N=>N.label);async function S(N){y(!0);try{await wJ(e,N)&&f()}finally{y(!1)}}return s.jsx(Yt,{open:!0,onOpenChange:N=>{N||v||f()},children:s.jsxs(Xt,{className:"w-[min(880px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:t})}),s.jsxs(Lt,{children:[s.jsxs(dn,{children:[s.jsx("h3",{children:r("三段各自成块,所以分开编辑")}),s.jsxs("p",{children:[r("上游按"),s.jsx("strong",{children:r("结构")}),r("校验身份:这三段必须各自成块排在最前(实测:三段并成一段会被回 405 / 3012)。因此这里按段编辑,不能合成一段。改过的段以你的文本为准,没改的段继续用官方原文;清空某一段 = 那一段恢复官方原文。"),w.length?r("当前已改过:{edited}。",{edited:w.join(r("、"))}):r("当前三段都是官方原文。"),c?"":r("注意:这一家的「网关自带」开关现在是关的,改过的正文要等开关打开才生效。")]}),s.jsx("p",{children:Un.promptGatewayText})]}),Yx.map(N=>s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsxs(Je,{htmlFor:`settings-prompt-block-${e}-${N.key}`,children:[N.label,s.jsx("span",{className:"ml-2 font-normal text-subtle",children:p[N.key]===a[N.key]?r("(官方原文)"):r("(已改)")})]}),s.jsx(Mi,{id:`settings-prompt-block-${e}-${N.key}`,rows:N.key==="identity"?2:10,className:N.key==="identity"?"min-h-[64px]":"min-h-[200px]",spellCheck:!1,disabled:v,value:p[N.key],onChange:k=>{const A=k.currentTarget.value;h(P=>({...P,[N.key]:A}))}}),s.jsx("div",{className:"hint",children:N.hint})]},N.key))]}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"outline",className:"mr-auto",disabled:v||!w.length,onClick:()=>{S(null)},children:r("恢复官方原文")}),s.jsx(ne,{variant:"outline",disabled:v,onClick:f,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:v||!j,onClick:()=>{S(p)},children:r(b?"保存中…":"保存")})]})]})})}function UJ({item:e,option:t,over:a,locked:o,busy:c}){const[d,f]=x.useState(!1),p=t.gatewayBlocks;return p?s.jsxs(s.Fragment,{children:[s.jsx(ne,{id:`btn-prompt-gateway-text-${e.id}`,variant:"outline",disabled:o||c,onClick:()=>f(!0),children:r("编辑正文")}),d?s.jsx(HJ,{id:e.id,title:r("编辑网关自带提示词 · {label}",{label:t.label}),official:p,over:a,enabled:e.gateway,disabled:c,onClose:()=>f(!1)}):null]}):null}function VJ(e){return e?Yx.filter(t=>(e[t.key]||"").trim()).map(t=>t.short).join(r("、")):""}function mP(e,t){return tt().wbIcons?.icon?.(e,t)||""}function Jt({tone:e,children:t}){const a=e==="ok"?"success":e==="bad"?"destructive":"outline";return s.jsx(ze,{variant:a,children:t})}function Gn({title:e,tip:t,badge:a,actions:o}){return s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:e}),t?s.jsx("span",{className:"tip-q","data-tip":t}):null,a,o?s.jsx("div",{className:"head-actions",children:o}):null]})}function FJ({title:e,tip:t,badge:a,actions:o,children:c}){function d(f){f.target.closest(".tip-q, .head-actions")&&f.preventDefault()}return s.jsxs("details",{className:"panel panel-fold",children:[s.jsxs("summary",{className:"panel-head",onClick:d,children:[s.jsx("span",{className:"fold-caret","aria-hidden":!0,dangerouslySetInnerHTML:{__html:mP("caretRight",14)}}),s.jsx("h2",{children:e}),t?s.jsx("span",{className:"tip-q","data-tip":t}):null,a,o?s.jsx("div",{className:"head-actions",children:o}):null]}),c]})}function Ur({id:e,label:t,checked:a,disabled:o,tip:c,onCheckedChange:d}){return s.jsxs("label",{className:"switch",children:[s.jsx(ns,{id:e,checked:a,disabled:o,onCheckedChange:f=>d(f)}),s.jsx("span",{children:t}),c?s.jsx("span",{className:"tip-q","data-tip":c}):null]})}function Pp({field:e,value:t,disabled:a,onCommit:o}){const[c,d]=x.useState(null),f=c!==null?c:t===null?"":String(t);async function p(){const h=c;if(h!==null)try{await o(h)}finally{d(null)}}return s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:e.id,children:e.label}),s.jsxs("span",{className:"retention-input",children:[s.jsx(Fe,{id:e.id,type:"number",min:e.min,max:e.max,step:1,inputMode:"numeric",className:"w-[92px] max-w-[92px] text-center tabular-nums",value:f,disabled:a||t===null,onChange:h=>d(h.target.value),onFocus:()=>d(String(t??"")),onBlur:()=>{p()},onKeyDown:h=>{h.key==="Enter"&&h.currentTarget.blur()}}),s.jsx("span",{className:"unit",children:e.unit})]}),s.jsx("div",{className:"hint",children:e.hint})]})}function _p(e){return e.status==="ready"?s.jsx(Jt,{tone:"ok",children:r("已生效")}):e.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")})}function kr({id:e,onClick:t}){return s.jsx(ne,{id:e,variant:"outline",onClick:t,children:r("刷新")})}function $J(e){const{lanAccess:t,lanPanel:a,lanIp:o,port:c}=e.app;if(!t)return r("未开启:网关只监听 127.0.0.1,仅本机可以访问。");const d=`http://${o||r("<本机IP>")}${c?`:${c}`:""}`,f=a?r(";网页管理面板已一并开放(浏览器打开同一地址)"):"";return r("已开启:其他设备把 API 地址指向 {url}{panel}。",{url:`${d}/v1`,panel:f})}function qJ({snap:e}){const t=e.app,a=t.status==="unavailable"?kt.appUnavailable:t.status==="loading"?kt.appLoading:t.closeToTray?t.lightweightMode?kt.appLightOn:kt.appTrayOn:kt.appTrayOff;return s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("启动与托盘"),tip:Wn.tray,badge:t.status==="ready"?s.jsx(Jt,{tone:"ok",children:r("已应用")}):t.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")})}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"settings-switches",children:[s.jsx(Ur,{id:"settings-close-to-tray",label:r("关闭窗口时最小化到托盘"),checked:t.closeToTray,disabled:e.busy==="app",onCheckedChange:o=>{vS("tray",o)}}),s.jsx(Ur,{id:"settings-lightweight",label:r("轻量模式(关窗释放界面内存)"),tip:Wn.lightweight,checked:t.lightweightMode,disabled:e.busy==="app"||!t.closeToTray,onCheckedChange:o=>{vS("lightweight",o)}}),s.jsx(Ur,{id:"settings-autostart",label:r("开机自动启动"),checked:t.autostart,disabled:e.busy==="app",onCheckedChange:o=>{vS("autostart",o)}})]}),s.jsx("div",{className:"settings-state",children:a})]})]}),!e.panelLogin&&s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("局域网访问"),tip:Wn.lan}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"settings-switches",children:[s.jsx(Ur,{id:"settings-lan-access",label:r("允许局域网内的设备访问网关"),checked:e.app.lanAccess,disabled:e.busy==="lan",onCheckedChange:EW}),s.jsx(Ur,{id:"settings-lan-panel",label:r("同时开放网页管理面板"),checked:e.app.lanPanel,disabled:e.busy==="lan"||!e.app.lanAccess,onCheckedChange:AW})]}),s.jsx("div",{className:"settings-state",children:$J(e)})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("计量单位"),tip:Wn.units}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"settings-switches",children:s.jsx(Ur,{id:"settings-chinese-units",label:r("使用中文单位(亿 / 万)"),checked:e.unitsChinese,onCheckedChange:CW})}),s.jsx("div",{className:"settings-state",children:e.unitsChinese?kt.unitsOn:kt.unitsOff})]})]})]})}function GJ(e){return e==="light"?kt.themeLight:e==="dark"?kt.themeDark:kt.themeSystem}function KJ(){const[e,t]=x.useState(xW),[a,o]=x.useState(bW),c=tt().workbuddyDesktop?.platform==="web";x.useEffect(()=>{const f=h=>t(String(h.detail||"system")),p=h=>o(Number(h.detail)||100);return window.addEventListener(LI,f),window.addEventListener(zI,p),()=>{window.removeEventListener(LI,f),window.removeEventListener(zI,p)}},[]);const d=a===100?r("100%(默认)"):`${a}%`;return s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("显示模式"),tip:Wn.displayTheme}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{children:s.jsx(En,{"aria-label":r("显示模式"),options:gW,value:e,onValueChange:f=>tt().wbApp?.applyTheme?.(f)})}),s.jsx("div",{className:"settings-state",children:GJ(e)})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("界面缩放"),tip:Wn.displayZoom}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"retention-list",children:s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"settings-zoom",children:r("缩放比例")}),s.jsx("span",{className:"prompt-input",children:s.jsxs(ut,{value:String(a),onValueChange:f=>tt().wbApp?.applyZoom?.(Number(f)),children:[s.jsx(ft,{id:"settings-zoom",className:"w-[140px]",disabled:c,"aria-label":r("界面缩放比例"),children:s.jsx(dt,{children:d})}),s.jsx(pt,{children:hW.map(f=>s.jsx($e,{value:String(f),children:f===100?r("100%(默认)"):`${f}%`},f))})]})}),s.jsx("div",{className:"hint",children:r("放大或缩小整个界面(文字与控件一起变),效果与浏览器 Ctrl +/- 相同:共 11 档,立即生效并记住。")})]})}),s.jsx("div",{className:"settings-state",children:c?kt.zoomWeb:a===100?kt.zoomDefault:r("当前按 {n}% 显示。",{n:a})})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("语言设置"),tip:Wn.displayLanguage}),s.jsxs("div",{className:"panel-body",children:[s.jsx(YJ,{}),s.jsx("div",{className:"settings-state",children:kt.languageHint})]})]})]})}function YJ(){const e=window.wbI18n,t=e?.rawLocale?.()??"auto",a=e?.LOCALES??[],o=c=>c==="auto"?r("跟随系统"):a.find(d=>d.code===c)?.label??c;return s.jsx("div",{className:"retention-list",children:s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"settings-language",children:r("界面语言")}),s.jsx("span",{className:"prompt-input",children:s.jsxs(ut,{value:t,onValueChange:c=>{c&&e?.setLocale(c)},children:[s.jsx(ft,{id:"settings-language",className:"w-[200px]","aria-label":r("界面语言"),children:s.jsx(dt,{children:o(t)})}),s.jsxs(pt,{children:[s.jsx($e,{value:"auto",children:r("跟随系统")}),a.map(c=>s.jsx($e,{value:c.code,children:c.label},c.code))]})]})})]})})}function XJ({codes:e,status:t,busy:a}){const[o,c]=x.useState(""),d=x.useRef(null),f=t==="unavailable";return s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"settings-retry-no-codes-input",children:r("指定错误码直接换号")}),s.jsx("div",{className:"retention-input",children:s.jsxs("div",{className:f?"tag-input disabled":"tag-input",onClick:()=>d.current?.focus(),children:[(e||[]).map(p=>s.jsxs("span",{className:"tag-chip",children:[s.jsx("span",{className:"v",children:p}),s.jsx(ne,{type:"button",variant:"ghost",size:"icon-2xs",className:"tag-x text-muted-foreground hover:bg-destructive-soft hover:text-destructive",title:r("删除 {code}",{code:p}),"aria-label":r("删除状态码 {code}",{code:p}),onClick:h=>{h.stopPropagation(),ZW(p)},children:"✕"})]},p)),s.jsx(Fe,{ref:d,id:"settings-retry-no-codes-input",type:"text",className:"tag-input-field h-auto border-0 bg-transparent px-0 shadow-none focus:shadow-none",placeholder:f?"—":r("输入状态码,回车添加"),inputMode:"numeric",autoComplete:"off",value:o,disabled:f||a,onChange:p=>c(p.target.value),onKeyDown:p=>{if(p.key==="Enter"){p.preventDefault(),QW(o),c("");return}p.key==="Backspace"&&!o&&WW()}})]})}),s.jsx("div",{className:"hint",children:Un.retryCodes})]})}function QJ({prompt:e,locked:t,busy:a}){const[o,c]=x.useState(null);async function d(){const f=o;if(f!==null)try{await mJ(f)}finally{c(null)}}return s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"settings-prompt-file",children:r("提示词文件")}),s.jsx("span",{className:"prompt-input",children:s.jsx(Fe,{id:"settings-prompt-file",type:"text",placeholder:r("留空 = 用内置默认提示词"),value:o!==null?o:e.file,disabled:t||a,onChange:f=>c(f.target.value),onFocus:()=>c(e.file),onBlur:()=>{d()},onKeyDown:f=>{f.key==="Enter"&&f.currentTarget.blur()}})}),s.jsx("div",{className:"hint",children:Un.promptFile})]})}function ZJ(e){if(e.status==="loading")return kt.appLoading;if(e.status==="unavailable")return kt.promptUnavailable;const t=hP(e.source),a=[];return e.mode==="passthrough"?a.push(r("客户端 system 原样出站(只靠指纹脱敏改写模板句)。")):a.push(r("{mode}生效:上游收到的 system 来自{source}{lines}。",{mode:e.mode==="custom"?r("替换"):r("追加"),source:t,lines:e.lines?r("({n} 行)",{n:e.lines}):""})),e.fileError&&a.push(`⚠️ ${e.fileError}`),a.join("")}function hP(e){return e==="inline"?r("界面里编辑的正文"):e==="file"?r("提示词文件"):e==="builtin"?r("内置默认提示词"):""}function WJ(e){return r("已自动切换到最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。",{until:e.degradeUntilText||kt.degradeUntilFallback})+r("期间本模式自己的提示词不会发出;把提示词改好后可以立即解除。")}function JJ({item:e,label:t,locked:a,busy:o}){const[c,d]=x.useState(null);async function f(){const p=c;if(p!==null)try{p!==e.file&&await bJ(e,p)}finally{d(null)}}return s.jsx("span",{className:"prompt-input",children:s.jsx(Fe,{id:`settings-prompt-file-${e.id}`,type:"text",placeholder:r("留空 = 用内置默认提示词"),"aria-label":r("{label} 提示词文件",{label:t}),value:c!==null?c:e.file,disabled:a||o,onChange:p=>d(p.target.value),onFocus:()=>d(e.file),onBlur:()=>{f()},onKeyDown:p=>{p.key==="Enter"&&p.currentTarget.blur()}})})}function eee(e,t){if(!t)return r("未单独配置:模式、提示词文件与正文都跟随上面的全局配置(当前「{mode}」)。",{mode:$o.find(o=>o.value===e.mode)?.toastLabel??e.mode});if(e.mode==="passthrough")return r("客户端 system 原样出站。");const a=r("{mode}生效:上游收到的 system 来自{source}{lines}。",{mode:e.mode==="custom"?r("替换"):r("追加"),source:hP(e.source),lines:e.lines?r("({n} 行)",{n:e.lines}):""});return e.fileError?`${a} ⚠️ ${e.fileError}`:a}function tee({item:e,option:t,over:a,locked:o,busy:c}){const d=e.gateway,f=t.gatewayChars,p=f?r("约 {n} 字符",{n:f.toLocaleString("zh-CN")}):r("一段内置装配"),h=VJ(a);return s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{className:"prompt-gateway-label",children:r("网关自带")}),s.jsxs("span",{className:"prompt-input",children:[s.jsx(Ur,{id:`settings-prompt-gateway-${e.id}`,label:r("装上{size}的官方身份提示词",{size:p}),checked:d,disabled:o||c,onCheckedChange:b=>{jJ(e.id,b)}}),s.jsx(UJ,{item:e,option:t,over:a,locked:o,busy:c})]}),s.jsxs("div",{className:d?"hint":"hint prompt-gateway-off",children:[d?t.gatewayNote:r("已关闭。{note}",{note:t.gatewayNote}),h?r(" 正文已改:{edited}(其余段用官方原文)。",{edited:h}):""]})]})}function nee({prompt:e,locked:t,busy:a}){const o=e.options,c=new Map(e.providers.map(w=>[w.id,w])),d=[];for(const w of o)w.gatewayNote&&d.push(c.get(w.id)??{id:w.id,mode:e.mode,file:e.file,text:e.text,source:e.source,lines:e.lines,fileError:e.fileError,gateway:e.gateway[w.id]??!0,configured:!1});for(const w of e.providers)d.some(S=>S.id===w.id)||d.push({...w,gateway:e.gateway[w.id]??!0,configured:!0});const f=new Set(d.map(w=>w.id)),p=o.filter(w=>!w.gatewayNote&&!f.has(w.id)),[h,b]=x.useState(""),y=w=>o.find(S=>S.id===w)?.label||w,v=w=>o.find(S=>S.id===w),j=w=>v(w)?.gatewayNote||"";return s.jsxs(s.Fragment,{children:[d.map(w=>{const S=y(w.id),N=$o.find(P=>P.value===w.mode)?.optionLabel??w.mode,k=j(w.id),A=v(w.id);return s.jsxs(x.Fragment,{children:[s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:`settings-prompt-mode-${w.id}`,children:S}),s.jsxs("span",{className:"prompt-input",children:[s.jsxs(ut,{value:w.mode,onValueChange:P=>{P!=null&&String(P)&&xJ(w,String(P))},children:[s.jsx(ft,{id:`settings-prompt-mode-${w.id}`,className:"w-[240px]",disabled:t||a,"aria-label":r("{label} 的提示词模式",{label:S}),children:s.jsx(dt,{children:N})}),s.jsx(pt,{children:$o.map(P=>s.jsx($e,{value:P.value,children:P.optionLabel},P.value))})]}),s.jsx(JJ,{item:w,label:S,locked:t,busy:a}),s.jsx(BJ,{item:w,label:S,locked:t,busy:a}),w.configured?s.jsx(ne,{variant:"outline",disabled:t||a,onClick:()=>{yJ(w.id)},children:r("跟随默认")}):null]}),s.jsx("div",{className:"hint",children:eee(w,w.configured)})]}),A&&k?s.jsx(tee,{item:w,option:A,over:e.gatewayText[w.id],locked:t,busy:a}):null]},w.id)}),s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"settings-prompt-provider-add",children:r("添加提供商")}),s.jsx("span",{className:"prompt-input",children:s.jsxs(ut,{value:h,onValueChange:w=>{b("");const S=w==null?"":String(w);S&&gJ(S)},children:[s.jsx(ft,{id:"settings-prompt-provider-add",className:"w-[240px]",disabled:t||a||p.length===0,"aria-label":r("添加要单独配置的提供商"),children:s.jsx(dt,{children:h?y(h):p.length?r("添加提供商…"):r("全部已配置")})}),s.jsx(pt,{children:p.map(w=>s.jsx($e,{value:w.id,children:w.label},w.id))})]})}),s.jsx("div",{className:"hint",children:Un.promptProviders})]})]})}function see({snap:e}){const t=e.prompt,a=t.status!=="ready",o=e.busy==="prompt",c=$o.find(d=>d.value===t.mode)?.optionLabel??t.mode;return s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("系统提示词"),tip:Wn.prompt,badge:t.status==="ready"?s.jsx(Jt,{tone:"ok",children:t.mode==="passthrough"?r("透传"):r("已接管")}):t.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-prompt-refresh",onClick:()=>{PJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"retention-list",children:[s.jsxs("div",{className:"prompt-group",children:[r("全局配置"),s.jsx("span",{className:"note",children:r("所有未单独配置的提供商都用这一份")})]}),s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"settings-prompt-mode",children:r("模式")}),s.jsx("span",{className:"prompt-input",children:s.jsxs(ut,{value:t.mode,onValueChange:d=>{d!=null&&String(d)&&pJ(String(d))},children:[s.jsx(ft,{id:"settings-prompt-mode",className:"w-[240px]",disabled:a||o,"aria-label":r("系统提示词模式"),children:s.jsx(dt,{children:c})}),s.jsx(pt,{children:$o.map(d=>s.jsx($e,{value:d.value,children:d.optionLabel},d.value))})]})}),s.jsx("div",{className:"hint",children:Un.promptMode})]}),s.jsx(QJ,{prompt:t,locked:a,busy:o}),s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"btn-prompt-edit-body",children:r("提示词正文")}),s.jsx("span",{className:"prompt-input",children:s.jsx(zJ,{prompt:t,locked:a,busy:o})}),s.jsxs("div",{className:"hint",children:[Un.promptText,t.source==="inline"?r("(当前生效的就是这一份,{n} 行)",{n:t.lines}):t.text.trim()?r("(已存一份正文,{n} 行;切成「替换 / 追加」后生效)",{n:t.lines}):""]})]}),s.jsxs("div",{className:"prompt-group",children:[r("按提供商配置"),s.jsx("span",{className:"note",children:r("只列单独配置过的家,其余沿用上面的全局配置")})]}),s.jsx(nee,{prompt:t,locked:a,busy:o}),t.status==="ready"&&t.degradeActive?s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{children:r("内容拦截降级")}),s.jsx("span",{className:"prompt-input",children:s.jsx(ne,{id:"btn-prompt-clear-degrade",variant:"outline",disabled:o,onClick:()=>{SJ()},children:r("立即解除")})}),s.jsx("div",{className:"hint",children:WJ(t)})]}):null]}),s.jsx("div",{className:"settings-state",children:ZJ(t)}),s.jsx("div",{className:"hint retention-note",children:Un.prompt})]})]})}function ree(e){if(e.status==="loading")return kt.appLoading;if(e.status==="unavailable")return kt.debugUnavailable;const t=e.count!==null&&e.limit!==null?r("已保存 {count} / {limit} 条报文(超出后丢弃最旧的)。",{count:e.count,limit:e.limit}):"";return e.on?`${kt.debugOn}${t}${r("凭据类请求头已脱敏。")}`:kt.debugOff}function aee({snap:e}){return s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("请求超时"),tip:Wn.timeouts,badge:_p(e.timeouts),actions:s.jsx(kr,{id:"btn-timeouts-refresh",onClick:()=>{TJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"retention-list",children:UI.map(t=>s.jsx(Pp,{field:t,value:e.timeouts.values?.[t.key]??null,disabled:e.busy==="timeouts",onCommit:a=>JW(t,a)},t.key))}),s.jsx("div",{className:"hint retention-note",children:Un.timeouts})]})]})}function lee({snap:e}){return s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("请求重试"),tip:Wn.retry,badge:_p(e.retry),actions:s.jsx(kr,{id:"btn-retry-refresh",onClick:()=>{CJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"retention-list",children:[HI.map(t=>s.jsx(Pp,{field:t,value:e.retry.values?.[t.key]??null,disabled:e.busy==="retry",onCommit:a=>XW(t,a)},t.key)),s.jsx(XJ,{codes:e.retryCodes,status:e.retry.status,busy:e.busy==="codes"||e.busy==="retry"})]}),s.jsx("div",{className:"hint retention-note",children:Un.retry})]})]})}const oee={routingFailoverBackoff:"failoverBackoff",routingSink:"sink",routingBreakerEnabled:"breakerEnabled"};function iee({snap:e}){const t=e.routing,a=e.busy==="routing"||t.status!=="ready",o=_x.find(d=>d.value===t.strategy)?.value??"order",c=Lx.find(d=>d.value===t.sticky)?.value??"auto";return s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("账号路由"),tip:Wn.routing,badge:t.status==="ready"?void 0:t.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-routing-refresh",onClick:()=>{RJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"retention-list",children:[s.jsx("div",{className:"prompt-group",children:r("选路规则")}),s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{children:r("路由策略")}),s.jsxs("div",{className:"routing-row",children:[s.jsx("span",{className:"tip-q","data-tip":_x.find(d=>d.value===o)?.tip}),s.jsx(En,{options:_x.map(d=>({value:d.value,label:d.label})),value:o,disabled:a,"aria-label":r("路由策略"),onValueChange:d=>{wS({routingStrategy:String(d)})}})]})]}),s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{children:r("会话粘性")}),s.jsxs("div",{className:"routing-row",children:[s.jsx("span",{className:"tip-q","data-tip":Lx.find(d=>d.value===c)?.tip}),s.jsx(En,{options:Lx.map(d=>({value:d.value,label:d.label})),value:c,disabled:a,"aria-label":r("会话粘性"),onValueChange:d=>{wS({routingSticky:String(d)})}})]})]}),s.jsxs("div",{className:"prompt-group",children:[r("自愈保护"),s.jsx("span",{className:"note",children:r("默认值通常够用,出问题时才需要调")})]}),jW.map(d=>{const f=`route-protection-${d.key}`;return s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:f,children:d.label}),s.jsxs("div",{className:"routing-row",children:[s.jsx("span",{className:"tip-q","data-tip":d.tip}),s.jsx(ns,{id:f,checked:t[oee[d.key]],disabled:a,onCheckedChange:p=>{wS({[d.key]:p})}})]})]},d.key)})]}),s.jsx("div",{className:"hint retention-note",children:Un.routing})]})]})}function cee({snap:e}){return s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("排队等待"),tip:Wn.queue,badge:_p(e.queue),actions:s.jsx(kr,{id:"btn-queue-refresh",onClick:()=>{EJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"retention-list",children:VI.map(t=>s.jsx(Pp,{field:t,value:e.queue.values?.[t.key]??null,disabled:e.busy==="queue",onCommit:a=>VW(t,a)},t.key))}),s.jsx("div",{className:"hint retention-note",children:Un.queue})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("验证码令牌池"),tip:Wn.captchaPool,badge:_p(e.captchaPool),actions:s.jsx(kr,{id:"btn-captcha-pool-refresh",onClick:()=>{AJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"retention-list",children:YI.map(t=>s.jsx(Pp,{field:t,value:e.captchaPool.values?.[t.key]??null,disabled:e.busy==="captchaPool",onCommit:a=>FW(t,a)},t.key))}),s.jsx("div",{className:"hint retention-note",children:Un.captchaPool})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("models.dev 模型元数据"),tip:Wn.modelsDev,badge:e.modelsDev.status==="ready"?s.jsx(Jt,{tone:"ok",children:r("{count} 条",{count:Number(e.modelsDev.count).toLocaleString("zh-CN")})}):e.modelsDev.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-modelsdev-refresh",onClick:()=>{oJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx(Ur,{id:"settings-modelsdev",label:r("用 models.dev 补齐上游没声明的模型能力与思考档位(联网刷新,内嵌快照始终生效)"),checked:e.modelsDev.enabled,disabled:e.modelsDev.status!=="ready"||e.busy==="modelsDev",onCheckedChange:t=>{lJ(t)}}),s.jsx("div",{className:"settings-state",children:dW(e.modelsDev)}),s.jsx("div",{className:"hint retention-note",children:Un.modelsDev})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("指纹脱敏"),tip:Wn.sanitize,badge:e.sanitize.status==="ready"?s.jsx(Jt,{tone:"ok",children:r("已生效")}):e.sanitize.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-sanitize-refresh",onClick:()=>{MJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"settings-switches",children:s.jsx(Ur,{id:"settings-sanitize",label:r("剥离上游审核黑名单指纹(改写请求体里的模板句与表头)"),checked:e.sanitize.on,disabled:e.sanitize.status!=="ready"||e.busy==="sanitize",onCheckedChange:t=>{tJ(t)}})}),s.jsx("div",{className:"settings-state",children:e.sanitize.status==="loading"?kt.appLoading:e.sanitize.status==="unavailable"?kt.sanitizeUnavailable:e.sanitize.on?kt.sanitizeOn:kt.sanitizeOff}),s.jsx("div",{className:"hint retention-note",children:Un.sanitize})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("开发者角色兼容"),tip:Wn.developerRoleCompat,badge:e.developerRoleCompat.status==="ready"?s.jsx(Jt,{tone:"ok",children:r("已生效")}):e.developerRoleCompat.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-role-compat-refresh",onClick:()=>{DJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"settings-switches",children:s.jsx(Ur,{id:"settings-developer-role-compat",label:r("转发前将 developer 角色消息转换为 system,兼容不支持 developer 角色的 OpenAI 兼容上游"),checked:e.developerRoleCompat.on,disabled:e.developerRoleCompat.status!=="ready"||e.busy==="developerRoleCompat",onCheckedChange:t=>{nJ(t)}})}),s.jsx("div",{className:"settings-state",children:e.developerRoleCompat.status==="loading"?kt.appLoading:e.developerRoleCompat.status==="unavailable"?kt.roleCompatUnavailable:e.developerRoleCompat.on?kt.roleCompatOn:kt.roleCompatOff}),s.jsx("div",{className:"hint retention-note",children:Un.developerRoleCompat})]})]}),s.jsx(dee,{snap:e}),s.jsx(see,{snap:e}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("调试模式"),tip:Wn.debug,badge:e.debug.status==="ready"?s.jsx(Jt,{tone:"ok",children:r("已生效")}):e.debug.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-debug-refresh",onClick:()=>{OJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"settings-switches",children:s.jsx(Ur,{id:"settings-debug-mode",label:r("保存上游原始报文(请求头、请求体、响应头、响应体)"),checked:e.debug.on,disabled:e.debug.status!=="ready"||e.busy==="debug",onCheckedChange:t=>{eJ(t)}})}),s.jsx("div",{className:"settings-state",children:ree(e.debug)}),s.jsx("div",{className:"hint retention-note",children:Un.debug})]})]})]})}function gP(e){const t=Object.entries(e.defaults).map(([a,o])=>({key:a,value:a in e.overrides?e.overrides[a]:o,isDefault:!0}));for(const[a,o]of Object.entries(e.overrides))a in e.defaults||t.push({key:a,value:o,isDefault:!1});return t}function uee(e,t){const a=Object.keys(e);return a.length!==Object.keys(t).length?!1:a.every(o=>e[o]===t[o])}function dee({snap:e}){const t=e.clineHeaders,[a,o]=x.useState(()=>gP(t));x.useEffect(()=>{o(gP(t))},[t]);const c=e.busy==="clineHeaders",d=t.status==="ready";function f(b,y){o(v=>v.map((j,w)=>w===b?{...j,...y}:j))}function p(){const b={};for(const y of a){const v=y.key.trim();v&&(y.isDefault?y.value!==(t.defaults[v]??"")&&(b[v]=y.value):y.value!==""&&(b[v]=y.value))}return b}const h=d&&!uee(p(),t.overrides);return s.jsx(FJ,{title:r("Cline 伪装头"),tip:Wn.clineHeaders,badge:t.status==="ready"?s.jsx(Jt,{tone:"ok",children:r("已生效")}):t.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-cline-headers-refresh",onClick:()=>{sJ()}}),children:s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"retention-list",children:[d?a.map((b,y)=>s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(Fe,{className:"w-[190px] shrink-0 font-mono text-[13px]",value:b.key,readOnly:b.isDefault,disabled:c,placeholder:"X-Custom-Header",onChange:v=>f(y,{key:v.target.value})}),s.jsx(Fe,{className:"flex-1 font-mono text-[13px]",value:b.value,disabled:c,placeholder:b.isDefault?r("值(留空 = 不发送)"):r("值"),onChange:v=>f(y,{value:v.target.value})}),s.jsx(ne,{variant:"ghost",disabled:c,onClick:()=>{b.isDefault?f(y,{value:t.defaults[b.key]??""}):o(v=>v.filter((j,w)=>w!==y))},children:b.isDefault?r("还原"):r("删除")})]},String(y))):null,s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(ne,{variant:"outline",disabled:c||!d,onClick:()=>o(b=>[...b,{key:"",value:"",isDefault:!1}]),children:r("添加自定义头")}),s.jsx("div",{className:"flex-1"}),s.jsx(ne,{variant:"ghost",disabled:c||!d||Object.keys(t.overrides).length===0,onClick:()=>{uP({})},children:r("恢复默认")}),s.jsx(ne,{disabled:c||!d||!h,onClick:()=>{uP(p())},children:r("保存")})]})]}),s.jsx("div",{className:"settings-state",children:t.status==="loading"?kt.appLoading:t.status==="unavailable"?r("未能读取 Cline 伪装头设置,请稍后重试"):h?r("有未保存的修改"):r("{n} 个头将随每个 Cline 请求发送",{n:Object.keys(t.effective).length})+(Object.keys(t.overrides).length>0?r("({n} 项被覆盖)",{n:Object.keys(t.overrides).length}):r("(全部为默认值)"))}),s.jsx("div",{className:"hint retention-note",children:Un.clineHeaders})]})})}function fee({snap:e}){const[t,a]=x.useState(!1);async function o(){a(!0),await NJ()||a(!1)}return s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("网关跨域访问(CORS)"),tip:Wn.cors,badge:e.cors.status==="ready"?e.cors.on?s.jsx(Jt,{tone:"bad",children:r("已开启")}):s.jsx(Jt,{tone:"ok",children:r("已关闭")}):e.cors.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-cors-refresh",onClick:()=>{IJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"settings-switches",children:s.jsx(Ur,{id:"settings-cors",label:r("允许浏览器里的页面跨来源调用网关(/v1/*)"),checked:e.cors.on,disabled:e.cors.status!=="ready"||e.busy==="cors",onCheckedChange:c=>{rJ(c)}})}),s.jsx("div",{className:"settings-state",children:e.cors.status==="loading"?kt.appLoading:e.cors.status==="unavailable"?kt.corsUnavailable:e.cors.on?kt.corsOn:kt.corsOff}),s.jsx("div",{className:"hint retention-note",children:Un.cors})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("机器人校验")}),s.jsxs("div",{className:"panel-body",children:[s.jsx(Ur,{id:"settings-captcha",label:r("登录 / 注册需要通过 ALTCHA 人机验证(工作量证明)"),checked:e.captcha.enabled,disabled:!e.captcha.available||e.busy==="captcha",onCheckedChange:c=>{aJ(c)}}),s.jsx("div",{className:"hint",children:Un.captcha})]})]}),e.panelLogin?s.jsxs("section",{className:"panel",id:"panel-login-section",children:[s.jsx(Gn,{title:r("面板登录")}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"hint",children:Un.panelLogin}),s.jsx("div",{className:"mt-2.5",children:s.jsx(ne,{id:"btn-panel-logout",variant:"outline",disabled:t,onClick:()=>{o()},children:r("退出登录")})})]})]}):null]})}function Lp({label:e,value:t}){return s.jsxs("div",{className:"storage-count",children:[s.jsx("span",{className:"k",children:e}),s.jsx("span",{className:"v",children:t})]})}function pee(e){return e.status==="loading"?s.jsx(Jt,{tone:"idle",children:r("检测中…")}):e.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:e.available?"ok":"bad",children:e.available?r("已生效"):r("数据库不可用")})}function mee({snap:e}){const t=e.busy,a=e.storage,o=tt().workbuddyDesktop?.platform==="web",c=a.status==="ready",d=c&&a.available,f=c&&a.file?a.file:"—",p=h=>d&&h!==null?yW(h):"—";return s.jsxs(s.Fragment,{children:[!o&&s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("整库备份 / 恢复"),tip:Wn.backup,actions:s.jsxs(s.Fragment,{children:[s.jsx(ne,{id:"btn-settings-db-export",variant:"outline",disabled:t==="dbExport"||t==="dbImport",onClick:()=>{PW()},children:r(t==="dbExport"?"备份中…":"备份整库")}),s.jsx(ne,{id:"btn-settings-db-restore",variant:"destructive",disabled:t==="dbExport"||t==="dbImport",onClick:()=>_W(),children:r(t==="dbImport"?"恢复中…":"恢复整库")})]})}),s.jsxs("div",{className:"panel-body",children:[e.dbWarning?s.jsxs("div",{className:"danger-zone",id:"settings-db-warning",children:[s.jsx("strong",{children:r("⚠️ 密钥警告")}),s.jsx("br",{}),e.dbWarning]}):null,s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"settings-db-include-logs",children:r("包含日志与调试报文")}),s.jsx(ns,{id:"settings-db-include-logs",checked:e.dbIncludeLogs,disabled:t!==null,onCheckedChange:IW}),s.jsx("div",{className:"hint",children:r("取消勾选后只备份业务数据(账号、配置与余额记录),文件显著变小;事件日志、请求统计与调试报文不会进备份文件。")})]}),s.jsx("div",{className:"danger-zone",children:s.jsx("strong",{children:Un.backupDanger})})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("账号导入 / 导出"),tip:Wn.io,actions:s.jsxs(s.Fragment,{children:[s.jsx(ne,{id:"btn-settings-export",variant:"outline",disabled:t==="export",onClick:()=>{MW()},children:r(t==="export"?"导出中…":"导出账号")}),s.jsx(ne,{id:"btn-settings-import",variant:"default",disabled:t==="import",onClick:()=>{DW()},children:r(t==="import"?"导入中…":"导入账号")})]})}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"danger-zone",children:[s.jsx("strong",{children:r("导出文件内含 accessToken / refreshToken / apiKey 等凭证与自定义提供商定义")}),r(",可直接用于登录。请妥善保管,不要外传或上传到公共位置。")]}),e.ioFailure?s.jsx("div",{className:"io-result",children:s.jsxs("span",{className:"text-destructive",children:[r("失败 {n} 个:{detail}",{n:e.ioFailure.failed,detail:e.ioFailure.detail}),e.ioFailure.more?r(" 等"):""]})}):null]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("数据保留"),tip:Wn.retention,badge:_p(e.retention),actions:s.jsx(kr,{id:"btn-retention-refresh",onClick:()=>{kJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"retention-list",children:pS.map(h=>s.jsx(Pp,{field:h,value:e.retention.values?.[h.key]??null,disabled:e.busy==="retention",onCommit:b=>HW(h,b)},h.key))}),s.jsx("div",{className:"hint retention-note",children:Un.retention})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("数据存储"),tip:Wn.storage,badge:pee(a),actions:s.jsx(kr,{id:"btn-storage-refresh",onClick:()=>{_J()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"retention-list",children:[s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"storage-db-path",children:r("数据库文件")}),s.jsx("span",{className:"storage-line",children:s.jsx("span",{className:"storage-path",id:"storage-db-path",title:f,children:f})}),s.jsx("div",{className:"hint",children:Un.storageFile})]}),s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"storage-db-size",children:r("占用大小")}),s.jsx("span",{className:"storage-line",children:s.jsx("span",{className:"storage-path",id:"storage-db-size",children:d?KI(a.bytes):"—"})}),s.jsx("div",{className:"hint",children:Un.storageSize})]})]}),s.jsxs("div",{className:"retention-list storage-counts",children:[s.jsx(Lp,{label:r("账号"),value:p(a.accounts)}),s.jsx(Lp,{label:r("事件日志"),value:p(a.logs)}),s.jsx(Lp,{label:r("请求记录"),value:p(a.requests)}),s.jsx(Lp,{label:r("报表天数"),value:p(a.dailyDays)}),s.jsx(Lp,{label:r("调试报文"),value:p(a.debug)})]}),s.jsx("div",{className:"hint retention-note",children:Un.storage})]})]})]})}const hee=[{title:r("问题反馈"),desc:r("遇到 Bug、报错或异常行为"),cta:r("去反馈"),url:"https://github.com/aimod-cc/agent2api/issues/new?template=bug_report.yml"},{title:r("功能建议"),desc:r("想要的新功能或改进想法"),cta:r("提建议"),url:"https://github.com/aimod-cc/agent2api/issues/new?template=feature_request.yml"},{title:r("请求提供商 / 模型支持"),desc:r("希望接入新的提供商或模型"),cta:r("去申请"),url:"https://github.com/aimod-cc/agent2api/issues/new?template=provider_request.yml"}];function gee(){return s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("反馈与需求")}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"hint",children:r("点下面的入口会用系统默认浏览器打开 GitHub 的对应表单(需要 GitHub 账号,模板已预设好,填完直接提交即可)。")}),s.jsx("div",{className:"mt-3 flex flex-col gap-2",children:hee.map(e=>s.jsxs("div",{className:"flex items-center justify-between gap-3 rounded-md border border-hairline bg-surface-2 px-3 py-2.5",children:[s.jsxs("div",{className:"min-w-0",children:[s.jsx("div",{className:"text-[12.5px] text-foreground",children:e.title}),s.jsx("div",{className:"mt-0.5 text-[12px] text-subtle",children:e.desc})]}),s.jsx(ne,{variant:"outline",size:"sm",onClick:()=>{fW(e.url)},children:e.cta})]},e.url))})]})]})}function xee({confirm:e}){const t=x.useRef(null);return s.jsx(Yt,{open:e!==null,onOpenChange:a=>{a||Vx(!1)},children:s.jsxs(Xt,{className:"w-[min(440px,calc(100vw-48px))]",initialFocus:t,children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("确认缩短保留天数")})}),s.jsx(Lt,{children:s.jsxs("div",{className:"danger-zone",children:[e?.head,s.jsx("br",{}),s.jsx("strong",{children:r("超出的历史数据会被立即删除,且不可恢复。")}),r("确定继续?")]})}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{ref:t,variant:"outline",onClick:()=>Vx(!1),children:r("取消")}),s.jsx(ne,{variant:"destructive",onClick:()=>Vx(!0),children:r("继续并清理")})]})]})})}const LS={enable:{title:r("开启局域网访问"),body:()=>s.jsxs(s.Fragment,{children:[r("为了安全,开启前需要先注册一个"),s.jsx("b",{children:r("面板管理员账号")}),r("(已注册过会跳过这一步,直接生效)。"),s.jsx("br",{}),r("开启后网关将监听所有网卡,同一局域网内的设备即可把 API 地址指向本机 IP 一起使用;管理接口从此要求管理员会话或网关 Key,转发接口在没有一把启用的 Key 时也会拒绝服务(届时会自动创建一把名为「默认」的 Key)。"),s.jsx("br",{}),s.jsx("strong",{children:r("保存后应用将重启以生效。")}),r("确定继续?")]}),label:r("继续")},disable:{title:r("关闭局域网访问"),body:()=>s.jsxs(s.Fragment,{children:[r("关闭后网关回到只监听 127.0.0.1,局域网内的设备将无法继续访问;已配置的网关 Key 与账号都不受影响。"),s.jsx("br",{}),s.jsx("strong",{children:r("保存后应用将重启以生效。")}),r("确定继续?")]}),label:r("关闭并重启")},panel:{title:r("变更网页管理面板"),body:e=>e?s.jsxs(s.Fragment,{children:[r("开放后,局域网内其他设备的浏览器打开本机 IP 即可进入管理面板(需管理员账号登录)。"),s.jsx("br",{}),s.jsx("strong",{children:r("保存后应用将重启以生效。")}),r("确定继续?")]}):s.jsxs(s.Fragment,{children:[r("关闭后,管理界面不再从局域网提供,只有本机的桌面程序可以管理;已开启的 API 转发不受影响。"),s.jsx("br",{}),s.jsx("strong",{children:r("保存后应用将重启以生效。")}),r("确定继续?")]}),label:r("保存并重启")}};function bee({confirm:e}){const t=x.useRef(null);return s.jsx(Yt,{open:e!==null,onOpenChange:a=>{a||jS(!1)},children:s.jsxs(Xt,{className:"w-[min(440px,calc(100vw-48px))]",initialFocus:t,children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("恢复整库备份")})}),s.jsx(Lt,{children:s.jsxs("div",{className:"danger-zone",children:[r("本机的全部数据(账号、网关配置、日志统计与桌面设置)将被所选备份文件"),s.jsx("strong",{children:r("整体替换")}),r(",包括恢复后需要重新登录管理面板。"),s.jsx("br",{}),s.jsx("strong",{children:r("替换前会自动把当前数据备份到配置目录的 backups/ 文件夹。")}),r("确定继续?")]})}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{ref:t,variant:"outline",onClick:()=>jS(!1),children:r("取消")}),s.jsx(ne,{variant:"destructive",onClick:()=>jS(!0),children:r("选择备份文件并恢复")})]})]})})}function vee({confirm:e,register:t}){const a=x.useRef(null),o=x.useRef(null),c=x.useRef(null),d=e!==null||t!==null,f=()=>{t!==null?JI():yS(!1)},p=()=>{OW(o.current?.value??"",c.current?.value??"")};return s.jsx(Yt,{open:d,onOpenChange:h=>{h||f()},children:s.jsx(Xt,{className:"w-[min(480px,calc(100vw-48px))]",initialFocus:a,children:t!==null?s.jsxs(s.Fragment,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("注册管理员账号")})}),s.jsxs(Lt,{children:[s.jsx("div",{children:r("局域网开放后,管理接口要求登录。请设置管理员账号与密码,注册完成会直接继续开启流程。")}),s.jsxs("form",{onSubmit:h=>{h.preventDefault(),t.busy||p()},children:[s.jsx(Je,{htmlFor:"lan-admin-name",className:"mt-3.5 mb-[5px] block text-[12.5px]",children:r("账号")}),s.jsx(Fe,{ref:o,id:"lan-admin-name",autoComplete:"username",placeholder:r("管理员账号"),disabled:t.busy}),s.jsx(Je,{htmlFor:"lan-admin-password",className:"mt-3.5 mb-[5px] block text-[12.5px]",children:r("密码")}),s.jsx(Fe,{ref:c,id:"lan-admin-password",type:"password",autoComplete:"new-password",placeholder:r("至少 8 位"),disabled:t.busy})]}),s.jsx("div",{className:"mt-3 min-h-5 text-[13px] whitespace-pre-wrap text-destructive",children:t.error})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{ref:a,variant:"outline",onClick:JI,disabled:t.busy,children:r("取消")}),s.jsx(ne,{onClick:p,disabled:t.busy,children:t.busy?r("正在注册…"):r("注册并开启")})]})]}):s.jsxs(s.Fragment,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:e?LS[e.mode].title:""})}),s.jsx(Lt,{children:s.jsx("div",{children:e?LS[e.mode].body(e.panel):null})}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{ref:a,variant:"outline",onClick:()=>yS(!1),children:r("取消")}),s.jsx(ne,{onClick:()=>yS(!0),children:e?LS[e.mode].label:r("继续")})]})]})})})}function yee(){const e=NW(),t=x.useRef(null);x.useEffect(()=>{const o=t.current;o&&(o.scrollTop=0)},[e.scrollReset]);const a=o=>e.category===o?"settings-pane active":"settings-pane";return s.jsxs("div",{className:"settings-layout",children:[s.jsx("nav",{className:"settings-nav",id:"settings-nav",children:Ix.map(o=>s.jsxs("button",{type:"button",className:e.category===o.id?"settings-nav-item active":"settings-nav-item","data-cat":o.id,onClick:()=>kW(o.id),children:[s.jsx("span",{className:"ico",dangerouslySetInnerHTML:{__html:mP(o.icon,17)}}),o.label]},o.id))}),s.jsxs("div",{className:"settings-panes",ref:t,children:[s.jsx("div",{className:a("general"),"data-cat":"general",children:s.jsx(qJ,{snap:e})}),s.jsx("div",{className:a("assistant"),"data-cat":"assistant",children:s.jsx(U7,{})}),s.jsx("div",{className:a("display"),"data-cat":"display",children:s.jsx(KJ,{})}),s.jsx("div",{className:a("gateway"),"data-cat":"gateway",children:s.jsx(cee,{snap:e})}),s.jsx("div",{className:a("routing"),"data-cat":"routing",children:s.jsx(iee,{snap:e})}),s.jsx("div",{className:a("retry"),"data-cat":"retry",children:s.jsx(lee,{snap:e})}),s.jsx("div",{className:a("timeout"),"data-cat":"timeout",children:s.jsx(aee,{snap:e})}),s.jsx("div",{className:a("security"),"data-cat":"security",children:s.jsx(fee,{snap:e})}),s.jsx("div",{className:a("data"),"data-cat":"data",children:s.jsx(mee,{snap:e})}),s.jsx("div",{className:a("feedback"),"data-cat":"feedback",children:s.jsx(gee,{})}),s.jsx("div",{className:a("about"),"data-cat":"about"})]}),s.jsx(xee,{confirm:e.retentionConfirm}),s.jsx(bee,{confirm:e.dbBackupConfirm}),s.jsx(vee,{confirm:e.lanConfirm,register:e.lanRegister})]})}const xP='.page[data-page="settings"]';let zS=null;function bP(){if(zS)return;const e=document.querySelector(xP);e&&(e.replaceChildren(),zS=jn.createRoot(e),Ts.flushSync(()=>{zS?.render(s.jsx(yee,{}))}))}QI(),document.querySelector(xP)?bP():document.addEventListener("DOMContentLoaded",bP,{once:!0}),window.wbSettingsPanel={load:_S,render:bS,renderRetention:Bx,renderRetry:YW,renderDebug:qx,renderSanitize:Gx,renderPrompt:td,renderStorage:PS,showCategory:xS},tt().wbApp?.currentPage==="settings"&&_S();const vP="agent2api-col-config:",BS=[{value:"left",label:r("左")},{value:"center",label:r("中")},{value:"right",label:r("右")}],jee=e=>BS.some(t=>t.value===e),wee=BS.map(e=>`ta-${e.value}`),yP="colset-dragging",jP="open";let HS=null;const wP="grid grid-cols-[18px_minmax(78px,1fr)_44px_minmax(106px,auto)] items-center gap-2";function SP(){return window}const Xx=new Map;function US(e){return e.columns.map(t=>({key:t.key,visible:t.visible!==!1,align:t.align||"left"}))}function See(e,t){const a=Array.isArray(t),o=!a&&t&&typeof t=="object"?t:null,c=a?t:Array.isArray(o?.items)?o.items:[],d=o?.defaults&&typeof o.defaults=="object"?o.defaults:null,f=new Map(e.columns.map(y=>[y.key,y])),p=new Map(US(e).map(y=>[y.key,y])),h=[],b=new Set;for(const y of c){const v=y&&typeof y=="object"?y:{},j=String(v.key||"");if(!f.has(j)||b.has(j))continue;b.add(j);const w=f.get(j),S=w.align||"left",N=d?d[j]??S:w.legacyAlign??S,k=jee(v.align)?v.align:null;h.push({key:j,visible:v.visible!==!1,align:k&&k!==N?k:S})}for(const y of e.columns)b.has(y.key)||Nee(h,e,{...p.get(y.key)});return h}function Nee(e,t,a){const o=t.columns.findIndex(c=>c.key===a.key);for(let c=o-1;c>=0;c-=1){const d=e.findIndex(f=>f.key===t.columns[c].key);if(d>=0){e.splice(d+1,0,a);return}}e.unshift(a)}function kee(e){try{const t=localStorage.getItem(vP+e.id);return See(e,t?JSON.parse(t):[])}catch{return US(e)}}function Cee(e){try{const t={};for(const a of e.columns)t[a.key]=a.align||"left";localStorage.setItem(vP+e.id,JSON.stringify({v:2,defaults:t,items:e.config}))}catch{}}const Tee=e=>Xx.get(e)?.config||null,Eee=(e,t)=>e.columns.find(a=>a.key===t)?.label||t;function NP(e,t){const a=Xx.get(e);if(!a)return t;const o=new Map(t.map(c=>[c.key,c]));return a.config.filter(c=>c.visible&&o.has(c.key)).map(c=>({...o.get(c.key),align:c.align}))}function zp(e,{silent:t=!1}={}){Cee(e),t||e.onChange?.(e.config),_l&&_l.id===e.id&&_l.repaint()}let _l=null;function Qx(){if(!_l)return;const e=_l;_l=null,e.destroy()}function Aee(e,t){Qx();const a=document.createElement("div");document.body.append(a);const o=jn.createRoot(a),c={current:null};_l={id:e.id,spec:e,repaint:()=>c.current?.(),destroy:()=>{o.unmount(),a.remove()}},o.render(s.jsx(Iee,{spec:e,anchor:t,repaintRef:c,onClose:Qx}))}function Ree(e,t){_l&&_l.id===e.id?Qx():Aee(e,t)}function Oee(e,t,a){const o=[...e],[c]=o.splice(t,1);return o.splice(a,0,c),o}function Mee(e,t,a,o){const c=document.elementFromPoint(e,t)?.closest?.("[data-col-key]");if(!c||!a||!a.contains(c))return"";const d=c.getAttribute("data-col-key")||"";return o.some(f=>f.key===d)?d:""}const Dee=s.jsxs("svg",{viewBox:"0 0 24 24",width:13,height:13,fill:"currentColor","aria-hidden":"true",className:"block",children:[s.jsx("circle",{cx:"9",cy:"6",r:"1.6"}),s.jsx("circle",{cx:"15",cy:"6",r:"1.6"}),s.jsx("circle",{cx:"9",cy:"12",r:"1.6"}),s.jsx("circle",{cx:"15",cy:"12",r:"1.6"}),s.jsx("circle",{cx:"9",cy:"18",r:"1.6"}),s.jsx("circle",{cx:"15",cy:"18",r:"1.6"})]});function Iee({spec:e,anchor:t,repaintRef:a,onClose:o}){const c=t,d=x.useRef(null),[f,p]=x.useState(null),h=x.useRef(null),[,b]=x.useState(0),y=x.useCallback(()=>b(N=>N+1),[]);x.useLayoutEffect(()=>(a.current=y,()=>{a.current=null}),[y,a]),x.useEffect(()=>(c.classList.add(jP),c.setAttribute("aria-expanded","true"),()=>{c.classList.remove(jP),c.setAttribute("aria-expanded","false")}),[c]),x.useEffect(()=>{const N=()=>{const k=c.getBoundingClientRect();(!c.isConnected||!k.width||k.bottom<=0||k.top>=window.innerHeight)&&o()};return window.addEventListener("scroll",N,!0),window.addEventListener("resize",N),()=>{window.removeEventListener("scroll",N,!0),window.removeEventListener("resize",N)}},[c,o]),x.useEffect(()=>()=>{const N=h.current;N&&(N.teardown(),zp(e))},[e]);function v(N,k){const A=e.config.find(P=>P.key===N);A&&(!k&&e.config.filter(P=>P.visible).length<=1||(A.visible=k,zp(e)))}function j(N,k){const A=e.config.find(P=>P.key===N);!A||A.align===k||(A.align=k,zp(e))}function w(){e.config=US(e),zp(e)}function S(N,k){k.preventDefault(),h.current?.teardown(),p(N),document.body.classList.add(yP);const A=d.current;function P(){window.removeEventListener("pointermove",R),window.removeEventListener("pointerup",C),document.body.classList.remove(yP),h.current=null,p(null)}function R(E){const D=Mee(E.clientX,E.clientY,A,e.config);if(!D||D===N)return;const I=e.config.findIndex(_=>_.key===N),O=e.config.findIndex(_=>_.key===D);I<0||O<0||(e.config=Oee(e.config,I,O),y())}function C(){P(),zp(e)}h.current={teardown:P},window.addEventListener("pointermove",R),window.addEventListener("pointerup",C)}return s.jsx(wf,{open:!0,modal:!1,onOpenChange:(N,k)=>{N||(k?.reason==="outside-press"&&(HS=k.event??null),o())},children:s.jsxs(Sf,{anchor:c,side:"bottom",align:"end",sideOffset:6,collisionPadding:8,className:"w-[336px]","aria-label":r("{name}的列设置",{name:e.label||e.id}),children:[s.jsxs("div",{className:Ue(wP,"border-b border-hairline px-3 pt-[9px] pb-[7px]","text-[10.5px] font-semibold tracking-[.05em] text-muted-foreground"),children:[s.jsx("span",{}),s.jsx("span",{className:"text-center",children:r("列")}),s.jsx("span",{className:"text-center",children:r("显示")}),s.jsx("span",{className:"text-center",children:r("对齐")})]}),s.jsx("div",{ref:d,className:"min-h-0 flex-1 overflow-y-auto px-3 py-1",children:e.config.map(N=>{const k=Eee(e,N.key),A=f===N.key;return s.jsxs("div",{"data-col-key":N.key,className:Ue(wP,"rounded-sm py-[5px] hover:bg-nav-hover",A&&"opacity-50"),children:[s.jsx("span",{className:"flex cursor-grab items-center justify-center text-muted-foreground opacity-50 hover:opacity-100",title:r("按住拖动调整列顺序"),onPointerDown:P=>S(N.key,P),children:Dee}),s.jsx("span",{className:"min-w-0 truncate text-xs text-foreground",title:k,children:k}),s.jsx(ns,{className:"justify-self-center",checked:N.visible,onCheckedChange:P=>v(N.key,P),"aria-label":r("显示「{label}」列",{label:k}),title:N.visible?r("这一列正在显示"):r("这一列已隐藏")}),s.jsx(En,{className:"w-full justify-center",options:BS,value:N.align,onValueChange:P=>j(N.key,P),"aria-label":r("「{label}」列的对齐",{label:k})})]},N.key)})}),s.jsxs("div",{className:"flex items-center gap-2 border-t border-hairline px-3 pt-2 pb-[9px]",children:[s.jsx("span",{className:"flex-1 text-[11px] text-muted-foreground",children:r("拖动 ⋮⋮ 调整顺序")}),s.jsx(ne,{variant:"outline",size:"sm",onClick:w,children:r("恢复默认")})]})]})})}function Pee(e,t){e&&(e.classList.remove(...wee),e.classList.add(`ta-${t}`))}const kP=new Map;function _ee(e,t){const a=kP.get(e);if(a&&a.table===t&&a.row.isConnected)return a;const o=t?.querySelector("thead tr");if(!o)return null;const c=t?.querySelector("colgroup")??null,d={table:t,row:o,group:c,ths:new Map([...o.querySelectorAll("th[data-col]")].map(f=>[f.getAttribute("data-col")||"",f])),cols:new Map([...c?.querySelectorAll("col[data-col]")||[]].map(f=>[f.getAttribute("data-col")||"",f]))};return kP.set(e,d),d}function Lee(e,t,a){const o=Xx.get(e);if(!o||!t)return;const c=_ee(e,t);if(!c)return;const{row:d,group:f,ths:p,cols:h}=c;for(const b of o.config){const y=p.get(b.key);if(!y)continue;const v=h.get(b.key);if(!b.visible||a?.has(b.key)){y.remove(),v?.remove();continue}Pee(y,b.align),d.appendChild(y),v&&f&&f.appendChild(v)}SP().wbTableColumns?.repaint?.(e)}function zee(e){const t=document.createElement("button");return t.type="button",t.className=`sm colset-btn${e.buttonClass?` ${e.buttonClass}`:""}`,t.id=`btn-colset-${e.id}`,t.title=r("调整「{name}」的列:显示 / 隐藏、顺序、对齐",{name:e.label||e.id}),t.innerHTML=`${SP().wbIcons?.icon?.("settings",14)||""}${r("列设置")}`,t.setAttribute("aria-haspopup","dialog"),t.setAttribute("aria-expanded","false"),t.addEventListener("click",a=>{if(HS===a){HS=null;return}Ree(e,t)}),t}function Bee(e){return(typeof e.mount=="function"?e.mount():e.mount?document.querySelector(e.mount):null)||null}function Hee(e){const t={...e,config:kee(e)};Xx.set(e.id,t);const a=Bee(e),o=zee(t);return a&&(e.buttonPlacement==="last"?a.appendChild(o):a.insertBefore(o,a.firstChild)),{apply:c=>NP(e.id,c),config:()=>t.config}}window.wbColSettings={register:Hee,apply:NP,configOf:Tee,syncStaticHead:Lee,close:Qx};function rl(){return window}const Uee=new Set(["updateCheck"]);function CP(e){return(Array.isArray(e?.tasks)?e.tasks:[]).filter(a=>!Uee.has(a.id))}const Vee=2e4,TP=1,Fee=" · ";function nd(e,t){rl().wbApp?.toast?.(e,t)}function Zx(e){return e instanceof Error?e.message:String(e)}function EP(e){return r(e==="seconds"?"秒":"分钟")}function AP(e){const t=Number(e)||0;return t?new Date(t).toLocaleTimeString("zh-CN",{hour12:!1}):""}function RP(e){const t=Number(e)||0;if(!t)return"";const a=t-Date.now();if(a<=0)return r("即将执行");const o=Math.round(a/1e3);if(o<60)return r("{n} 秒后",{n:o});const c=Math.round(o/60);if(c<60)return r("{n} 分钟后",{n:c});const d=Math.round(c/60);return d<24?r("{n} 小时后",{n:d}):r("{n} 天后",{n:Math.round(d/24)})}function $ee(e){const t=e.runner==="backend",a=[];if(e.lastRunAt){const o=AP(e.lastRunAt);a.push(e.lastResult?r("上次执行 {time}({result})",{time:o,result:e.lastResult}):r("上次执行 {time}",{time:o}))}else a.push(r(t?"还没有执行记录":"由页面按间隔自动刷新"));return t&&e.enabled&&e.nextRunAt&&a.push(r("下次执行 {time}({relative})",{time:AP(e.nextRunAt),relative:RP(e.nextRunAt)})),t&&e.retryAt&&e.retryAt>Date.now()&&a.push(r("冷却中,{relative}重试",{relative:RP(e.retryAt)})),a.join(Fee)}function qee(e,t){const a=String(t??"").trim(),o=EP(e.unit);if(!a)return{ok:!1,message:r("间隔不能为空(可填 {min}–{max} {unit})",{min:e.min,max:e.max,unit:o})};if(!/^\d+$/.test(a))return{ok:!1,message:r("间隔必须是整数")};const c=Number(a);return ce.max?{ok:!1,message:r("间隔必须在 {min}–{max} {unit} 之间(当前填的是 {value})",{min:e.min,max:e.max,unit:o,value:a})}:{ok:!0,value:c}}function Gee(e){const t=rl();e.id==="logsAutoRefresh"?t.wbLogsPanel?.applyAutoRefresh?.(e):e.id==="requestsAutoRefresh"?t.wbRequestsPanel?.applyAutoRefresh?.(e):e.id==="reportAutoRefresh"&&t.wbReport?.applyAutoRefresh?.(e)}let pc=!1,Wx=null,VS=!1;async function OP(){if(!Wx){VS=!0;return}await Wx()}function Kee(){const[e,t]=x.useState([]),[a,o]=x.useState(!1),[c,d]=x.useState(null),[f,p]=x.useState({});function h(D){p(I=>{if(!(D in I))return I;const O={...I};return delete O[D],O})}const b=x.useCallback(async()=>{const D=rl().workbuddyDesktop;try{if(!D)throw new Error(r("后端桥不可用"));const I=await D.getScheduledTasks();t(CP(I)),o(!0)}catch(I){console.warn("读取定时任务失败:",Zx(I)),t([]),o(!0)}},[]),y=x.useCallback(async()=>{if(pc||document.hidden||rl().wbApp?.currentPage!=="tasks")return;const D=rl().workbuddyDesktop;if(D)try{const I=await D.getScheduledTasks();t(CP(I))}catch(I){console.warn("同步定时任务状态失败:",Zx(I))}},[]);x.useEffect(()=>{const D=window.setInterval(()=>{y()},Vee);return()=>{window.clearInterval(D)}},[y]),x.useEffect(()=>{rl().wbApp?.renderTopbarStatus?.()},[e,a]),x.useEffect(()=>(Wx=b,VS&&(VS=!1,b()),()=>{Wx=null}),[b]);async function v(D,I,O){if(pc)return;const _=rl().workbuddyDesktop;if(_){pc=!0;try{const z=await _.saveScheduledTask(D,I);t(H=>H.map(B=>B.id===D?z:B)),Gee(z),nd(r("✅ 已更新「{label}」",{label:O}))}catch(z){nd(r("保存失败:{reason}",{reason:Zx(z)}),"err"),await b()}finally{pc=!1}}}async function j(D,I){const O=qee(D,I);if(!O.ok){nd(O.message,"err"),h(D.id);return}if(O.value===D.interval){h(D.id);return}await v(D.id,{interval:O.value},r("{name}间隔",{name:r(D.label)})),h(D.id)}async function w(D){if(pc)return;const I=rl().workbuddyDesktop;if(I){pc=!0,d(D.id);try{const O=await I.runScheduledTask(D.id);if(O?.task){const _=O.task;t(z=>z.map(H=>H.id===D.id?_:H))}nd(r("✅ {task}:{summary}",{task:r(D.label),summary:O?.summary||r("已执行")})),D.id==="credentialMaintenance"&&await rl().wbApp?.refresh?.()}catch(O){nd(r("执行失败:{reason}",{reason:Zx(O)}),"err"),await b()}finally{pc=!1,d(null)}}}function S(D){const I=EP(D.unit),O=c===D.id;return s.jsxs("div",{className:"task-item","data-task":D.id,children:[s.jsxs("div",{className:"task-main",children:[s.jsxs("div",{className:"task-title",children:[s.jsxs("label",{className:"switch",children:[s.jsx(ns,{checked:D.enabled===!0,onCheckedChange:_=>{v(D.id,{enabled:_},r("{name}开关",{name:r(D.label)}))}}),s.jsx("span",{className:"task-name",children:r(D.label)})]}),s.jsx("span",{className:"tip-q","data-tip":r(D.description)}),s.jsx(ze,{className:"task-badge",variant:D.enabled?"success":"outline",children:D.enabled?r("已开启"):r("已关闭")}),D.running?s.jsx(ze,{className:"task-badge",variant:"warning",children:r("执行中…")}):null]}),s.jsx("div",{className:"task-state",children:$ee(D)})]}),s.jsxs("div",{className:"task-actions",children:[s.jsxs("span",{className:"task-interval",children:[s.jsx("span",{className:"task-interval-label",children:r("每")}),s.jsx(Fe,{type:"number",inputMode:"numeric",min:D.min,max:D.max,step:1,className:"w-[74px] max-w-[74px] text-center tabular-nums",value:f[D.id]??String(D.interval),title:r("可填 {min}–{max} {unit}(默认 {def} {unit})",{min:D.min,max:D.max,unit:I,def:D.defaultInterval}),onChange:_=>{const z=_.currentTarget.value;p(H=>({...H,[D.id]:z}))},onBlur:_=>{j(D,_.currentTarget.value)},onKeyDown:_=>{_.key==="Enter"&&_.currentTarget.blur()}}),s.jsx("span",{className:"task-interval-unit",children:I})]}),D.canRun===!0?s.jsx(ne,{size:"sm",variant:"outline",disabled:O||D.running===!0,onClick:()=>{w(D)},children:r(O?"执行中…":"立即执行")}):null]})]},D.id)}const N=e.length===0,k=e.filter(D=>D.enabled).length,A=N?a?r("不可用"):"—":r("{enabled} / {total} 个已开启",{enabled:k,total:e.length}),P=N?a?"destructive":"outline":k?"success":"outline",R=P==="destructive"?"bad":P==="success"?"ok":"",C=e.map(D=>S(D)),E=C.slice(TP);return s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("任务清单")}),s.jsx(ze,{id:"tasks-badge",variant:P,"data-tone":R,children:A}),s.jsx("span",{className:"panel-sub",children:r("状态每 20 秒自动同步")}),s.jsx("div",{className:"head-actions",children:s.jsx(ne,{variant:"outline",onClick:()=>{b().then(()=>nd(r("定时任务已刷新")))},children:r("刷新")})})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{className:"task-list",children:N?s.jsx("div",{className:"log-empty",children:r(a?"没有可显示的定时任务。后端未返回任务清单,请确认网关正在运行。":"正在加载定时任务…")}):s.jsxs(s.Fragment,{children:[C.slice(0,TP),E.length?s.jsx("div",{className:"task-grid",children:E}):null]})})}),s.jsxs("div",{className:"panel-foot",children:[s.jsxs("span",{children:[r("任务配置保存在 "),s.jsx("code",{children:"~/.agent2api/config.json"}),r(" 的 "),s.jsx("code",{children:"scheduledTasks"}),r(" 字段")]}),s.jsx("span",{children:r("改动立即生效,不需要重启程序")})]})]})}const MP='.page[data-page="tasks"]';let DP=!1;function IP(){if(DP)return;const e=document.querySelector(MP);e&&(DP=!0,e.replaceChildren(),jn.createRoot(e).render(s.jsx(Kee,{})))}document.querySelector(MP)?IP():document.addEventListener("DOMContentLoaded",IP,{once:!0}),window.wbTasksPanel={load:OP},rl().wbApp?.currentPage==="tasks"&&OP();const PP="workbuddy-desktop-update-skip";function Yee(){try{return localStorage.getItem(PP)||""}catch{return""}}function Xee(e){try{localStorage.setItem(PP,e)}catch{}}let _P="";function Jx(e,t){if(!e||e.hasUpdate!==!0||e.pendingRestart===!0)return;const a=String(e.latestVersion||"").trim();a&&nn().wbApp?.currentPage!=="settings"&&(a===Yee()||a===_P||(_P=a,LP(),sd=document.createElement("div"),document.body.append(sd),Bp=jn.createRoot(sd),Bp.render(s.jsx(Qee,{info:e,onGoUpdate:t,onClose:LP}))))}function Qee({info:e,onGoUpdate:t,onClose:a}){const o=String(e.latestVersion||""),c=String(e.notes||"").trim(),d=c?dD(c):"";function f(){Xee(o),a()}function p(){a();const h=nn();h.wbApp?.showPage?.("settings"),h.wbSettingsPanel?.showCategory?.("about"),t(e)}return s.jsx(Yt,{open:!0,onOpenChange:h=>{h||a()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("检测到更新")})}),s.jsxs(Lt,{children:[s.jsxs("p",{className:"text-[12.5px] leading-[1.6] text-subtle",children:[r("新版本 "),s.jsx("strong",{className:"text-foreground",children:o}),r(" 已发布(当前 {current}),更新日志如下:",{current:e.currentVersion||r("未知")})]}),s.jsx("div",{className:"max-h-[46vh] overflow-y-auto overscroll-contain pr-1.5",onClick:Xu,children:d?s.jsx("div",{className:"md-body",dangerouslySetInnerHTML:{__html:d}}):s.jsx("div",{className:"md-body",children:s.jsx("p",{className:"md-body-empty",children:r("这个版本没有填写发布说明。")})})})]}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"outline",size:"sm",onClick:f,children:r("跳过此次更新")}),s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:a,children:r("取消")}),s.jsx(ne,{variant:"default",onClick:p,children:r("去更新")})]})]})})}let Bp=null,sd=null;function LP(){Bp&&(Bp.unmount(),Bp=null),sd&&(sd.remove(),sd=null)}const zP="updateCheck",rd=1,eb=1440,Zee="https://github.com/settings/tokens/new?description="+encodeURIComponent("Agent2API 更新检查");function Wee(e){return e?e.error?{badge:s.jsx(ze,{shape:"tag",variant:"destructive",title:e.error,children:r("无法解密")}),hint:r("重新粘贴保存一次即可自愈")}:e.filled&&e.origin==="stored"?{badge:s.jsx(ze,{shape:"tag",variant:"success",children:r("已填写")}),hint:r("已加密存储在本地,不显示具体值;粘贴新令牌可覆盖")}:e.filled?{badge:s.jsx(ze,{shape:"tag",variant:"secondary",children:r("环境变量")}),hint:r("已配置 GITHUB_TOKEN 环境变量,界面保存的令牌优先于它")}:{badge:s.jsx(ze,{shape:"tag",variant:"outline",children:r("未填写")}),hint:r("填写后检查限额 60 → 5000 次/小时(创建令牌无需勾选任何权限)")}:{badge:s.jsx(ze,{shape:"tag",variant:"outline",children:r("读取中")}),hint:""}}function Jee({open:e,onClose:t}){const[a,o]=x.useState(fD),[c,d]=x.useState(null),[f,p]=x.useState(""),[h,b]=x.useState(!1),[y,v]=x.useState(null),[j,w]=x.useState(""),[S,N]=x.useState(!1),[k,A]=x.useState(null),[P,R]=x.useState(!1),C=pD(a);x.useEffect(()=>{e&&(p(""),wX().then(o),(async()=>{try{d(await nn().workbuddyDesktop?.getUpdateToken()??null)}catch{}})(),(async()=>{try{const F=await nn().workbuddyDesktop?.getUpdateBeta();A({enabled:F?.enabled===!0})}catch{}})(),(async()=>{try{const $=((await nn().workbuddyDesktop?.getScheduledTasks?.())?.tasks??[]).find(q=>q.id===zP),X=$?.enabled===!0,K=Math.max(rd,Math.round(Number($?.interval)||0));v({enabled:X,interval:K}),w(String(K))}catch{}})())},[e]);async function E(F){if(!S){N(!0);try{const $=await nn().workbuddyDesktop?.saveScheduledTask?.(zP,F),X=$?.enabled===!0,K=Math.max(rd,Math.round(Number($?.interval)||0));v({enabled:X,interval:K}),w(String(K)),Es(r("✅ 自动检查更新已保存"))}catch($){Es(r("保存失败:{error}",{error:pr($)}),"err")}finally{N(!1)}}}async function D(){const F=Math.round(Number(j));if(!Number.isFinite(F)||Feb){Es(r("检查间隔必须是 {min} ~ {max} 分钟",{min:rd,max:eb}),"err");return}y&&F===y.interval||await E({interval:F})}async function I(F){if(P)return;R(!0);let $=!1;try{const X=await nn().workbuddyDesktop?.setUpdateBeta({enabled:F});A({enabled:X?.enabled===!0}),$=!0,X?.saved===!1?Es(r("通道已切换,但写入磁盘失败(重启后会回到原设置)"),"err"):Es(r(F?"✅ 已加入 beta 版,之后的检查会包含 beta 版":"✅ 已退出 beta 版,之后的检查只看正式版"))}catch(X){Es(r("保存失败:{error}",{error:pr(X)}),"err")}finally{R(!1)}if($)try{await nn().workbuddyDesktop?.checkUpdate(),await jX()}catch{}}async function O(F){const $=await SX(F);$&&o(X=>({...X,proxyChoice:$.choice}))}async function _(){const F=f.trim();if(!F){Es(r("请先粘贴 GitHub 令牌"),"err");return}b(!0);try{const $=await nn().workbuddyDesktop?.setUpdateToken({token:F});d($??null),p(""),$?.saved===!1?Es(r("令牌已生效,但写入磁盘失败(重启后会丢失)"),"err"):Es(r("✅ GitHub 令牌已保存(加密存储,界面不再显示)"))}catch($){Es(r("保存失败:{error}",{error:pr($)}),"err")}finally{b(!1)}}async function z(){b(!0);try{const F=await nn().workbuddyDesktop?.setUpdateToken({token:null});d(F??null),Es(r("✅ 已清除界面保存的 GitHub 令牌(环境变量若配置过则继续生效)"))}catch(F){Es(r("清除失败:{error}",{error:pr(F)}),"err")}finally{b(!1)}}const H=Wee(c),B=!!c?.error||c?.origin==="stored",L=c?.filled&&c?.origin!=="env"?r("已填写(粘贴新令牌可覆盖)"):r("粘贴 GitHub 令牌(ghp_… / github_pat_…)");return s.jsx(Yt,{open:e,onOpenChange:F=>{F||t()},children:s.jsxs(Xt,{className:"w-[min(460px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("更新设置")})}),s.jsxs(Lt,{className:"gap-3.5",children:[s.jsxs("div",{children:[s.jsxs("div",{className:"flex items-center justify-between gap-2",children:[s.jsx("div",{className:"text-[13px] font-semibold text-foreground",children:r("自动检查更新")}),s.jsx(ns,{checked:y?.enabled===!0,disabled:S||!y,onCheckedChange:F=>{E({enabled:F===!0})},"aria-label":r("自动检查更新")})]}),s.jsxs("div",{className:"mt-1.5 flex items-center gap-2",children:[s.jsx(Fe,{type:"number",min:rd,max:eb,step:1,className:"w-[110px]",disabled:S||!y||y.enabled!==!0,value:j,onChange:F=>w(F.currentTarget.value),onKeyDown:F=>{F.key==="Enter"&&(F.preventDefault(),D())},"aria-label":r("检查间隔(分钟)")}),s.jsx("span",{className:"text-[12px] text-subtle",children:r("分钟({min} ~ {max})",{min:rd,max:eb})}),s.jsx(ne,{variant:"outline",size:"sm",disabled:S||!y||Math.round(Number(j))===y.interval,onClick:()=>{D()},children:r("保存")})]}),s.jsx("div",{className:"mt-1.5 text-[12px] text-subtle",children:r("到点自动向 GitHub 查询新版本,查到就走「检测到更新」弹窗;关闭后「检查更新」按钮仍可手动触发")})]}),s.jsx("div",{className:"h-px bg-hairline"}),s.jsxs("div",{children:[s.jsxs("div",{className:"flex items-center justify-between gap-2",children:[s.jsx("div",{className:"text-[13px] font-semibold text-foreground",children:r("加入 beta 版")}),s.jsx(ns,{checked:k?.enabled===!0,disabled:P||!k,onCheckedChange:F=>{I(F===!0)},"aria-label":r("加入 beta 版")})]}),s.jsx("div",{className:"mt-1.5 text-[12px] text-subtle",children:r("开启后只更新到 beta 版(预发布),取版本最高的一个;beta 版可能不稳定。开启期间不会因为正式版发布而自动切回正式版 —— 想切回请关闭本开关。关闭后只更新到正式版,且不会降级到版本号更低的版本。")})]}),s.jsx("div",{className:"h-px bg-hairline"}),s.jsxs("div",{children:[s.jsx("div",{className:"text-[13px] font-semibold text-foreground",children:r("出网代理")}),s.jsx("div",{className:"mt-1.5",children:s.jsxs(ut,{value:C.current,onValueChange:F=>{O(String(F))},children:[s.jsx(ft,{className:Ue("w-full",C.broken&&"border-destructive-bd"),title:C.title,"aria-label":r("更新出网代理"),children:s.jsx(dt,{className:"min-w-0 truncate",children:C.selected?.label||r("直连")})}),s.jsx(pt,{children:C.items.map(F=>s.jsx($e,{value:F.value,disabled:F.disabled,children:F.label},F.value))})]})})]}),s.jsx("div",{className:"h-px bg-hairline"}),s.jsxs("div",{children:[s.jsxs("div",{className:"flex items-center justify-between gap-2",children:[s.jsx("div",{className:"text-[13px] font-semibold text-foreground",children:r("GitHub 令牌")}),s.jsx(ne,{variant:"outline",size:"xs",title:r("在默认浏览器中打开 GitHub 的令牌创建页(备注已预设,无需勾选任何权限,直接点 Generate token)"),onClick:()=>{uD(Zee)},children:r("打开 GitHub 令牌页面")})]}),s.jsxs("div",{className:"mt-1.5 flex items-center gap-2",children:[H.badge,s.jsx("span",{className:"text-[12px] text-subtle",children:H.hint})]}),s.jsxs("div",{className:"mt-2 flex items-center gap-2",children:[s.jsx(Fe,{type:"password",className:"flex-1",placeholder:L,autoComplete:"new-password",spellCheck:!1,value:f,onChange:F=>p(F.currentTarget.value),onKeyDown:F=>{F.key==="Enter"&&(F.preventDefault(),_())}}),s.jsx(ne,{variant:"default",size:"sm",disabled:h||!f.trim(),onClick:()=>{_()},children:r("保存")}),B?s.jsx(ne,{variant:"outline",size:"sm",disabled:h,onClick:()=>{z()},children:r("清除")}):null]})]})]})]})})}function ete({open:e,onClose:t}){return s.jsx(Yt,{open:e,onOpenChange:a=>{a||t()},children:s.jsxs(Xt,{className:"w-[min(320px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("加入 QQ 群")})}),s.jsxs(Lt,{className:"items-center gap-3 text-center",children:[s.jsx("img",{className:"qq-qr",src:yX,alt:r("QQ 群二维码")}),s.jsxs("div",{className:"qq-no",children:[s.jsx("span",{className:"k",children:r("群号")}),s.jsx("code",{children:lx}),s.jsx(ne,{type:"button",size:"sm",variant:"ghost","data-copy":lx,children:r("复制")})]}),s.jsx("p",{className:"qq-hint",children:r("用手机 QQ 扫上面的二维码,或在 QQ 里搜索群号加入。")})]})]})})}const tte=1e3,BP=8,nte=10,ste=1e3,rte={"":"outline",ok:"success",warn:"warning",bad:"destructive"},ate={info:null,checkedAt:0,repository:"",badgeText:r("未检查"),badgeTone:"",stateText:r("点击「检查更新」查询最新发布版本。"),stateError:!1,percent:0,phase:"idle",readyPath:"",checking:!1,actionBusy:!1,restartSeconds:0,tokenFilled:null};function Ll(e){return String(e).padStart(2,"0")}function lte(e){const t=new Date(String(e??""));return!e||Number.isNaN(t.getTime())?"":`${t.getFullYear()}-${Ll(t.getMonth()+1)}-${Ll(t.getDate())} ${Ll(t.getHours())}:${Ll(t.getMinutes())}`}function HP(e){const t=new Date(e);if(!e||Number.isNaN(t.getTime()))return"";const a=new Date,o=t.getFullYear()===a.getFullYear()&&t.getMonth()===a.getMonth()&&t.getDate()===a.getDate(),c=`${Ll(t.getHours())}:${Ll(t.getMinutes())}:${Ll(t.getSeconds())}`;return o?c:`${Ll(t.getMonth()+1)}-${Ll(t.getDate())} ${c}`}function Hp(e){const t=e?.installerKind;if(t==="dmg"||t==="deb"||t==="appimage"||t==="nsis")return t;const a=navigator.userAgent||"";return/Mac|iPhone|iPad/.test(a)?"dmg":/Linux/.test(a)?"deb":"nsis"}function Ia(e){return e?.selfUpdate===!0}function UP(e){return e?Ia(e)?e.serverAsset??null:e.asset??null:null}function ote(e){if(Ia(e))return r("正在下载服务端更新包…(下载完成后由面板完成替换,重启容器内的服务后生效)");switch(Hp(e)){case"dmg":return r("正在下载安装包…(下载完成后会挂载磁盘映像,把应用拖进「应用程序」即可完成安装)");case"deb":return r("正在下载安装包…(下载完成后会交给系统安装器,按提示输入密码即可完成安装)");case"appimage":return r("正在下载安装包…(下载完成后会启动新的 AppImage;把它替换掉旧文件即完成升级)");default:return r("正在下载安装包…(下载完成后启动安装程序需要管理员权限,会弹出 UAC 确认框)")}}function VP(e,t){if(Ia(e))return r("更新包已就绪:{name}。点「安装并重启」后,面板会校验并替换容器内的程序与界面文件,然后重启服务(页面会自动刷新)。",{name:t});switch(Hp(e)){case"dmg":return r("安装包已就绪:{name}。点击「打开安装包」后会挂载磁盘映像,把应用拖进「应用程序」即可完成安装。",{name:t});case"deb":return r("安装包已就绪:{name}。点击「打开安装包」后会交给系统安装器,按提示输入密码即可完成安装(本程序无需退出)。",{name:t});case"appimage":return r("安装包已就绪:{name}。点击「安装并重启」后会启动新的 AppImage;把它移动到你的应用目录并替换旧文件即完成升级(本程序会先退出以释放文件占用)。",{name:t});default:return r("安装包已就绪:{name}。点击「安装并重启」后需要管理员权限,会弹出 UAC 确认框。",{name:t})}}function ite(e){if(Ia(e))return r("安装并重启");const t=Hp(e);return r(t==="dmg"||t==="deb"?"打开安装包":"安装并重启")}let Vr=ate;const FS=new Set;function $S(){return Vr}function cte(e){return FS.add(e),()=>{FS.delete(e)}}function zn(e){Vr={...Vr,...e};for(const t of FS)t()}async function qS(){try{const e=await nn().workbuddyDesktop?.getUpdateToken();zn({tokenFilled:e?.filled===!0})}catch{}}let Cr=!1,GS=null,tb=null;function ad(){tb!==null&&(window.clearInterval(tb),tb=null)}function Rs(e,t=""){zn({badgeText:e,badgeTone:t})}function Mn(e,t=!1){zn({stateText:e,stateError:t})}function nb(){const{info:e,checkedAt:t}=Vr;if(!e)return;const a=t?r("(检查于 {time})",{time:HP(t)}):"";if(e.pendingRestart===!0){Rs(r("待重启"),"warn"),Mn(r("更新已应用到容器内,重启服务后生效。{at}",{at:a}));return}if(e.hasUpdate===!0){if(Rs(r("有新版本"),"warn"),Ia(e)&&!e.serverAsset){Mn(r("发现新版本 {latest}(当前 {current}),但该版本没有提供容器内更新包;请在宿主机执行 docker compose pull && docker compose up -d 升级。{at}",{latest:String(e.latestVersion||""),current:String(e.currentVersion||""),at:a}));return}Mn(r("发现新版本 {latest}(当前 {current})。{at}",{latest:String(e.latestVersion||""),current:String(e.currentVersion||""),at:a}))}else e.hasUpdate===!1?(Rs(r("已是最新"),"ok"),Mn(r("当前已是最新版本({current})。{at}",{current:String(e.currentVersion||""),at:a}))):(Rs(r("无法比较"),"warn"),Mn(e.latestVersion?r("最新发布版本为 {latest},但当前版本号「{current}」无法解析,未做新旧判断。{at}",{latest:e.latestVersion,current:e.currentVersion||r("未知"),at:a}):r("仓库暂无发布版本。{at}",{at:a})))}function FP(e){!e||e.checked===!1||(zn({info:e,checkedAt:Number(e.checkedAt)||0}),e.repository&&KS(e.repository),Vr.phase==="idle"&&nb())}function KS(e){const t=String(e||"").trim();/^[\w.-]+\/[\w.-]+$/.test(t)&&zn({repository:t})}function YS(e){if(!e)return!1;if(e.active){const t=Math.max(0,Math.min(100,Number(e.percent)||0)),a=(Number(e.received)||0)/1024/1024,o=(Number(e.total)||0)/1024/1024;return zn({phase:"downloading",percent:t}),Rs(r("下载中"),"warn"),Mn(r("正在下载 {name}:{percent}%({received} / {total} MB)",{name:e.filename||r("安装包"),percent:t,received:a.toFixed(1),total:o.toFixed(1)})),!0}if(ad(),zn({percent:0}),e.error)return Rs(r("下载失败"),"bad"),Mn(r("下载失败:{error}",{error:e.error}),!0),zn({phase:"failed"}),!0;if(e.canceled)return Rs(r("已取消"),"warn"),Mn(Ia(Vr.info)?r("下载已取消,可重新点击「下载并更新」。"):r("下载已取消,可重新点击「下载并安装」。")),zn({phase:"idle"}),!0;if(e.done&&e.path){const t=e.path,a=e.filename||t;if(Rs(r("可安装"),"ok"),zn({phase:"ready",readyPath:t}),Ia(Vr.info))return Mn(VP(Vr.info,a)),!0;if(t!==GS){GS=t;const o=Hp(Vr.info);Mn(r(o==="dmg"?"安装包已下载完成,正在挂载磁盘映像…":o==="deb"?"安装包已下载完成,正在打开系统安装器…":o==="appimage"?"安装包已下载完成,正在启动新的 AppImage…":"安装包已下载完成,正在启动安装程序(需要管理员权限,会弹出 UAC 确认框)…")),qP(t)}else Mn(VP(Vr.info,a));return!0}return!1}async function ute(){try{const e=await nn().workbuddyDesktop?.updateProgress();if(!e){ad();return}YS(e)}catch(e){ad(),Mn(r("读取下载进度失败:{error}",{error:pr(e)}),!0)}}function $P(){ad(),tb=window.setInterval(()=>{ute()},tte)}async function qP(e){const t=nn().workbuddyDesktop;if(t)try{const o=(await t.runInstaller(e,!0))?.restart!==!1,c=Hp(Vr.info)==="deb"?r("已打开系统安装器,请在它弹出的窗口里完成安装。安装完成后重新打开本程序即可。"):r("安装包已挂载,请在弹出的窗口里把应用拖进「应用程序」完成安装。安装完成后重新打开本程序即可。");Mn(o?r("安装程序已启动,本程序将退出以便完成覆盖安装。安装程序需要管理员权限,会弹出 UAC 确认框,请选择「是」。"):c)}catch(a){Rs(r("启动失败"),"bad"),Mn(r("启动安装程序失败:{error}",{error:pr(a)}),!0)}}async function dte(){const e=nn().workbuddyDesktop;if(e?.installUpdate)try{Rs(r("安装中"),"warn"),Mn(r("正在校验并替换容器内的程序与界面文件…"));const t=await e.installUpdate(),a=String(t?.version||"").trim();Mn(a?r("已替换为 {version},正在重启服务…",{version:a}):r("替换完成,正在重启服务…")),await e.restartService?.().catch(()=>{}),XS(a)}catch(t){Rs(r("安装失败"),"bad"),Mn(r("安装失败:{error}",{error:pr(t)}),!0)}}async function fte(){const e=nn().workbuddyDesktop;if(!(!e?.rollbackUpdate||Cr)){Cr=!0,zn({actionBusy:!0});try{Rs(r("回滚中"),"warn"),Mn(r("正在回滚到上一版本…")),await e.rollbackUpdate(),Mn(r("已还原上一版本,正在重启服务…")),await e.restartService?.().catch(()=>{}),XS("")}catch(t){Rs(r("回滚失败"),"bad"),Mn(r("回滚失败:{error}",{error:pr(t)}),!0)}finally{Cr=!1,zn({actionBusy:!1})}}}function XS(e){ad(),zn({phase:"restarting",restartSeconds:BP,readyPath:""}),Rs(r("重启中"),"warn");let t=BP;const a=()=>{if(Mn(e?r("{version} 已就位,服务正在重启,页面会在恢复后自动刷新…({seconds}s)",{version:e,seconds:Math.max(0,t)}):r("服务正在重启,页面会在恢复后自动刷新…({seconds}s)",{seconds:Math.max(0,t)})),t<=0){window.clearInterval(o),pte();return}t-=1,zn({restartSeconds:t})},o=window.setInterval(a,1e3);a()}async function pte(){for(let e=0;e{window.setTimeout(t,ste)})}window.location.reload()}async function GP(){if(Cr)return null;Cr=!0,zn({checking:!0}),Rs(r("检查中"),"warn"),Mn(r("正在查询 GitHub 上的最新发布版本…"));let e=null;try{const t=nn().workbuddyDesktop;if(!t)throw new Error(r("后端桥不可用"));e=await t.checkUpdate()??null,zn({info:e,checkedAt:Number(e?.checkedAt)||Date.now()}),e?.repository&&KS(e.repository),nb()}catch(t){e=null,zn({info:null,checkedAt:0}),Rs(r("检查失败"),"bad"),Mn(r("检查更新失败:{error}",{error:pr(t)}),!0)}finally{Cr=!1,zn({checking:!1})}return nn().wbApp?.updateUpdateBadge?.(e),Jx(e,Up),e}async function KP(){let e=null;try{e=await nn().workbuddyDesktop?.getUpdateStatus()??null}catch{}if(e&&e.checked!==!1){FP(e),nn().wbApp?.updateUpdateBadge?.(e),Jx(e,Up);return}Vr.info||(Rs(r("未检查")),Mn(r("点击「检查更新」查询 GitHub 上的最新发布版本。")))}async function QS(){if(Cr)return;const e=$S();if(e.info?.pendingRestart===!0&&e.phase!=="downloading"){Cr=!0,zn({actionBusy:!0});try{await nn().workbuddyDesktop?.restartService?.().catch(()=>{}),XS("")}finally{Cr=!1,zn({actionBusy:!1})}return}if(e.phase!=="downloading"&&e.readyPath){Cr=!0,zn({actionBusy:!0});try{Ia(e.info)?await dte():await qP(e.readyPath)}finally{Cr=!1,zn({actionBusy:!1})}return}if(e.phase==="downloading"){try{await nn().workbuddyDesktop?.cancelUpdate(),Mn(r("正在取消下载…"))}catch(a){Es(r("取消失败:{error}",{error:pr(a)}),"err")}return}const t=UP(e.info);if(!t?.url){Es(Ia(e.info)?r("这一版没有提供容器内更新包"):r("没有可下载的安装包"),"err");return}if(XP(e)){Cr=!0,zn({actionBusy:!0});try{await nn().workbuddyDesktop?.downloadUpdate({url:t.url,name:t.name}),GS=null,zn({phase:"downloading",readyPath:"",percent:0}),Rs(r("下载中"),"warn"),Mn(ote(e.info)),$P()}catch(a){Rs(r("下载失败"),"bad"),Mn(r("下载失败:{error}",{error:pr(a)}),!0)}finally{Cr=!1,zn({actionBusy:!1})}}}async function YP(){nb(),qS(),await KP();try{const e=await nn().workbuddyDesktop?.updateProgress();if(e?.active){YS(e),$P();return}if(e?.done&&e.path){YS(e);return}}catch{}}async function Up(e){if(e?.pendingRestart===!0){await QS();return}e?.hasUpdate===!0&&(zn({info:e,checkedAt:Number(e.checkedAt)||Vr.checkedAt}),e.repository&&KS(e.repository),nb(),nn().wbApp?.updateUpdateBadge?.(e),Jx(e,Up)),await YP();const t=$S();if(!(t.phase==="downloading"||t.readyPath)&&t.info?.hasUpdate)for(let a=0;a<3;a+=1){if(Cr){await new Promise(o=>{window.setTimeout(o,400)});continue}await QS();return}}function XP(e){return e.phase==="restarting"?null:e.info?.pendingRestart===!0&&e.phase==="idle"?{label:r("立即重启"),disabled:e.actionBusy}:!(e.info?.hasUpdate===!0&&!!UP(e.info)?.url)&&e.phase!=="downloading"&&e.phase!=="ready"?null:{label:e.phase==="downloading"?r("取消下载"):e.phase==="failed"?r("重试下载"):e.phase==="ready"?ite(e.info):Ia(e.info)?r("下载并更新"):r("下载并安装"),disabled:e.actionBusy}}function mte(e,t){const a=e.info,o=String(a?.notes||"").trim();if(!o)return s.jsx("div",{className:"update-log-hint",children:r(a?"这个版本没有填写发布说明。":"点击「检查更新」后,这里会显示最新版本的更新说明。")});const c=dD(o),d=cD(a?.pageUrl),f=lte(a?.publishedAt);return s.jsxs("div",{className:"rel-note",children:[s.jsxs("div",{className:"rel-note-head",children:[s.jsx("span",{className:"rel-tag",children:String(a?.latestVersion||r("最新版本"))}),a?.prerelease===!0?s.jsx(ze,{shape:"tag",variant:"warning",children:r("预发布")}):null,f?s.jsx("span",{className:"rel-date",children:f}):null]}),s.jsxs("div",{className:"rel-note-body",children:[c?s.jsx("div",{className:"md-body",dangerouslySetInnerHTML:{__html:c}}):s.jsx("div",{className:"md-body",children:s.jsx("p",{className:"md-body-empty",children:r("这个版本没有填写发布说明。")})}),s.jsxs("div",{className:"rel-foot",children:[d?s.jsx("a",{href:d,"data-external":d,target:"_blank",rel:"noopener",children:r("在 GitHub 查看完整说明")}):null,s.jsx(ne,{type:"button",variant:"link",size:"sm",className:"h-auto border-0 px-0 text-[11.5px] font-semibold text-primary-fg underline-offset-auto",title:r("群号:{group}",{group:lx}),onClick:t,children:r("加入 QQ 群")})]})]})]})}function hte(){const e=x.useSyncExternalStore(cte,$S),t=XP(e),[a,o]=x.useState(!1),[c,d]=x.useState(!1);x.useEffect(()=>()=>{ad()},[]),x.useEffect(()=>{qS()},[]);const f=e.repository?`https://github.com/${e.repository}`:"",p=e.repository?`https://github.com/${e.repository.split("/")[0]}`:"",h=Ia(e.info),b=h&&e.info?.backupAvailable===!0&&e.phase!=="downloading"&&e.phase!=="ready"&&e.phase!=="restarting",y=e.info?.latestVersion?e.info.prerelease?r("{version}(预发布)",{version:e.info.latestVersion}):e.info.latestVersion:"—",v=String(e.info?.latestVersion||"").trim(),j=v?`${v}${e.checkedAt?r(" · 检查于 {time}",{time:HP(e.checkedAt)}):""}`:"";return s.jsxs("section",{className:"panel",onClick:Xu,children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("软件更新")}),s.jsx("span",{className:"tip-q","data-tip":r(h?"容器内更新:更新包从 GitHub 发布页下载,校验 sha256 后由面板替换容器内的程序与界面文件,再重启服务(容器由 restart 策略拉起,页面会自动刷新)。更新的是容器里的文件,compose 里的镜像标签不变 —— 之后执行 docker compose pull && up -d 会回到镜像里的版本;更新失败可一键回滚到上一版本。检查更新与下载的出网线路、GitHub 令牌都在「更新设置」里。":"更新包从 GitHub 发布页下载,下载完成后可直接启动安装程序并自动重启本程序。安装包会先校验来源域名与文件完整性。检查更新与下载安装包的出网线路、GitHub 令牌都在「更新设置」里:线路默认直连(直连失败时自动借 Clash Verge 的混合端口重试一次),选定指定出口后只走该出口;填写 GitHub 令牌可把检查限额从 60 次/小时提高到 5000 次/小时。")}),s.jsx(ze,{id:"update-badge",variant:rte[e.badgeTone],"data-tone":e.badgeTone,children:e.badgeText}),s.jsxs("div",{className:"head-actions",children:[e.tokenFilled===!1?s.jsx("span",{className:"text-[12px] font-semibold text-warning",title:r("填写后检查限额 60 → 5000 次/小时(创建令牌无需勾选任何权限)"),children:r("GitHub Token 未填写")}):null,s.jsx(ne,{id:"btn-update-settings",variant:"outline",title:r("出网线路与 GitHub 令牌设置"),onClick:()=>o(!0),children:r("更新设置")}),s.jsx(ne,{id:"btn-update-check",variant:"outline",disabled:e.checking||e.phase==="restarting",onClick:()=>{GP()},children:e.checking?r("检查中…"):r("检查更新")}),b?s.jsx(ne,{id:"btn-update-rollback",variant:"outline",disabled:e.actionBusy,title:r("把容器内的程序与界面文件还原到更新前的版本,并重启服务"),onClick:()=>{fte()},children:r("回滚到上一版本")}):null,t?s.jsx(ne,{id:"btn-update-download",variant:"default",disabled:t.disabled,onClick:()=>{QS()},children:t.label}):null]})]}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"update-version",children:[s.jsxs("span",{className:"version-pill",children:[s.jsx("span",{className:"k",children:r("当前版本")}),s.jsx("span",{className:"v",id:"update-current",children:e.info?.currentVersion||"—"})]}),s.jsx("span",{className:"version-arrow",children:"→"}),s.jsxs("span",{className:"version-pill new",children:[s.jsx("span",{className:"k",children:r("最新版本")}),s.jsx("span",{className:"v",id:"update-latest",children:y})]})]}),s.jsx("div",{className:"detail",id:"update-state",style:e.stateError?{color:"var(--danger)"}:void 0,children:e.stateText}),e.phase==="downloading"&&e.percent>0&&e.percent<100?s.jsx(cf,{value:e.percent}):null,p&&f?s.jsxs("div",{className:"update-author",id:"update-author",children:[s.jsxs("div",{className:"update-author-text",children:[s.jsx("strong",{children:r("关于作者")}),s.jsxs("span",{children:[r("本工具由")," ",s.jsx("a",{id:"update-author-link",href:p,"data-external":p,target:"_blank",rel:"noopener",children:e.repository.split("/")[0]})," ",r("个人开发并开源维护,免费使用,也不会收集你的账号数据。如果它帮到了你,欢迎到项目仓库点个 Star 支持一下。")]})]}),s.jsx("div",{className:"update-author-actions",children:s.jsx(ne,{id:"btn-update-favorite",type:"button",size:"sm",variant:"outline","data-external":f,title:r("在浏览器中打开项目仓库,点 Star 支持作者"),children:r("收藏项目")})})]}):null,s.jsxs("div",{className:"update-log",id:"update-notes",children:[s.jsxs("div",{className:"update-log-head",children:[s.jsx("span",{className:"update-log-title",children:r("更新日志")}),s.jsx("span",{className:"update-log-meta",id:"update-log-meta",children:j})]}),s.jsx("div",{className:"update-log-body",id:"update-log-body",children:mte(e,()=>d(!0))})]})]}),a?s.jsx(Jee,{open:!0,onClose:()=>{o(!1),qS()}}):null,c?s.jsx(ete,{open:!0,onClose:()=>d(!1)}):null]})}const QP='.settings-pane[data-cat="about"]';let ZS=null;function ZP(){if(ZS)return;const e=document.querySelector(QP);e&&(e.replaceChildren(),ZS=jn.createRoot(e),ZS.render(s.jsx(hte,{})))}document.querySelector(QP)?ZP():document.addEventListener("DOMContentLoaded",ZP,{once:!0}),window.wbUpdatePanel={load:YP,check:GP,syncFromCache:KP,openAndDownload:Up,applyStatus:FP,showUpdateModal:e=>Jx(e,Up)};const gte="zcode.z.ai/billing",xte={active:"success",pending:"warning",expired:"ghost",unknown:"outline"};function bte(e){const t=String(e||"").trim().toLowerCase();return t==="daily"?r("每日"):t==="weekly"?r("每周"):t==="monthly"?r("每月"):""}function vte(e){const t=Number(e);if(!Number.isFinite(t)||t<=0)return"";const a=window.wbUnits;return a?.formatTokens?a.formatTokens(t):String(t)}function mc(e){const t=Number(e);return!Number.isFinite(t)||t<=0?"":Pe().wbApp?.formatTime?.(t*1e3)||new Date(t*1e3).toLocaleString()}function yte(e){const t=vte(e.grantUnits),a=String(e.unitType||"").trim(),o=t?[t,a].filter(Boolean).join(" "):"",c=bte(e.period),d=[String(e.showName||"").trim(),o].filter(Boolean).join(" ");return d?c?r("{body}({period})",{body:d,period:c}):d:""}function WP(e){return(e.entitlements||[]).map(yte).filter(Boolean).join(r("、"))}function jte(e){const t=mc(e.startsAt),a=mc(e.endsAt);return t&&a?`${t} → ${a}`:a?r("到期 {time}",{time:a}):t?r("开始 {time}",{time:t}):""}function wte(e){const t=new Date(e*1e3);if(Number.isNaN(t.getTime()))return"";const a=`${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}`,o=d=>Math.floor(Date.UTC(d.getFullYear(),d.getMonth(),d.getDate())/864e5),c=o(t)-o(new Date);return c===0?r("今天 {time}",{time:a}):c===1?r("明天 {time}",{time:a}):mc(e)}function Ste(e){const t=[],a=Number(e.pendingUntil);if(Number.isFinite(a)&&a>0){const c=wte(a);c&&t.push(r("待生效 {time}",{time:c}))}const o=mc(e.endsAt);if(o&&t.push(r("到期 {time}",{time:o})),t.length===0){const c=mc(e.startsAt);c&&t.push(r("开始 {time}",{time:c}))}return t.join(" · ")}function Nte({options:e,onClose:t}){const a=e.id,[,o]=x.useState(0);x.useEffect(()=>sR(()=>o(I=>I+1)),[]);const[c,d]=x.useState([]),[f,p]=x.useState({status:"loading"}),[h,b]=x.useState(""),y=Aa(a),v=String(y?.name||e.name||a),j=eg(y),w=[...new Set([...j,...c])],S=y?.claimPlans&&typeof y.claimPlans=="object"&&!Array.isArray(y.claimPlans)?y.claimPlans:{},N=y?Wa(y):void 0,k=N&&typeof N=="object"&&!Array.isArray(N)?N:null,A=k&&Array.isArray(k.plans)?k.plans:[],P=w.filter(I=>I&&!A.some(O=>String(O.planId||"")===I)),R=Gs().usageInflight.has(a),C=Pe().wbZcodeClaim,E=x.useCallback(async()=>{if(!C?.preview){p({status:"error",message:r("当前环境不支持领取(桥接方法缺失)")});return}p({status:"loading"});try{const I=await C.preview(a);p({status:"ready",result:I||{}})}catch(I){p({status:"error",message:mt(I)})}},[C,a]);x.useEffect(()=>{E();const I=Aa(a);I&&Wa(I)===void 0&&ig(a)},[a,E]);async function D(I){if(!(!C?.claim||h)){b(I);try{const O=await C.claim(a,I);if(O?.ok||O?.failure==="already_claimed"){const _=O.planId||I;_&&d(z=>z.includes(_)?z:[...z,_]),ig(a),Pe().wbApp?.refresh?.(),E()}}finally{b("")}}}return s.jsx(Yt,{open:!0,onOpenChange:(I,O)=>{if(!I){if(h){O.cancel();return}t()}},children:s.jsxs(Xt,{className:"w-[min(560px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("ZCode 套餐明细 — {name}",{name:v})})}),s.jsxs(Lt,{className:"space-y-4",children:[s.jsxs("section",{children:[s.jsxs("div",{className:"mb-1.5 flex items-center gap-2 text-[12px] font-semibold text-muted-foreground",children:[r("可领取的套餐"),s.jsx(ze,{variant:"outline",shape:"tag",children:r("官方限时活动")})]}),f.status==="loading"?s.jsxs("div",{className:"flex items-center gap-2 py-2 text-[12.5px] text-muted-foreground",children:[s.jsx(of,{className:"size-3"}),r("正在探测可领取的套餐…")]}):f.status==="error"?s.jsxs("div",{className:"py-1 text-[12.5px] text-destructive",children:[r("探测失败:{message}",{message:f.message}),s.jsx(ne,{variant:"outline",size:"xs",className:"ml-2",onClick:()=>{E()},children:r("重试")})]}):f.result.deployed?(f.result.plans||[]).length===0?s.jsx("p",{className:"py-1 text-[12.5px] text-muted-foreground",children:r("当前没有可领取的套餐(活动期内每天发一份,明天可再领)")}):s.jsx("ul",{className:"m-0 list-none p-0",children:(f.result.plans||[]).map(I=>{const O=String(I.planId||""),_=w.includes(O),z=WP(I),H=jte(I);return s.jsxs("li",{className:"flex items-start justify-between gap-3 border-b border-hairline py-2 last:border-b-0",children:[s.jsxs("div",{className:"min-w-0",children:[s.jsxs("div",{className:"flex items-center gap-2 text-[13px] font-medium",children:[s.jsx("span",{className:"truncate",children:I.name||O||r("套餐")}),_?s.jsx(ze,{variant:"success",shape:"tag",children:r("今日已领")}):null]}),H?s.jsx("div",{className:"text-[12px] text-muted-foreground",children:H}):null,z?s.jsx("div",{className:"text-[12px] text-muted-foreground",children:z}):null,I.description?s.jsx("div",{className:"text-[12px] text-muted-foreground",children:I.description}):null]}),s.jsx(ne,{variant:"outline",size:"xs",className:"flex-none",title:_?r("这一份今天已经领过了(重复领取上游会回「已领取过」)"):void 0,disabled:h!=="",onClick:()=>{D(O)},children:r(h===O?"领取中…":_?"再领一次":"领取")})]},O||I.name)})}):s.jsx("p",{className:"py-1 text-[12.5px] text-muted-foreground",children:r("当前没有可领取的套餐(活动尚未开始)")}),s.jsx("p",{className:"mt-1.5 text-[11.5px] text-muted-foreground",children:r("活动期内每天发一份新套餐;同一份当天重复领取会提示「已领取过」,次日可再领。领取要走一次人机验证(滑块)。")}),f.status==="ready"&&f.result.activationError?s.jsx("p",{className:"text-[11.5px] text-muted-foreground",children:r("(激活事件补报失败,可能影响领取资格:{message})",{message:f.result.activationError})}):null]}),s.jsxs("section",{children:[s.jsxs("div",{className:"mb-1.5 flex items-center gap-2 text-[12px] font-semibold text-muted-foreground",children:[r("我的套餐"),Array.isArray(k?.wallets)&&k.wallets.length>0&&k.availableView?s.jsx(ze,{variant:"outline",shape:"tag",children:String(k.walletsFrom||"")==="monitor"?String(k.availableView):r("可用 {value}",{value:String(k.availableView)})}):null,s.jsx("div",{className:"ml-auto",children:s.jsx(ne,{variant:"outline",size:"xs",disabled:R,title:r("重新读取该账号的余额与套餐(走余额查询那条链)"),onClick:()=>{ig(a)},children:r(R?"查询中…":"查询余额")})})]}),A.length>0?s.jsx("ul",{className:"m-0 list-none p-0",children:A.map(I=>{const O=String(I.planId||""),_=String(I.state||"unknown"),z=WP(I),H=Ste(I),B=O&&y?.claimPlans&&Number(y.claimPlans[O])||0;return s.jsxs("li",{className:"flex items-start justify-between gap-3 border-b border-hairline py-2 last:border-b-0",children:[s.jsxs("div",{className:"min-w-0",children:[s.jsxs("div",{className:"flex items-center gap-2 text-[13px] font-medium",children:[s.jsx("span",{className:"truncate",children:I.name||O||r("套餐")}),s.jsx(ze,{variant:xte[_]||"outline",shape:"tag",children:qA(_)})]}),H?s.jsx("div",{className:"text-[12px] text-muted-foreground",children:H}):null,z?s.jsx("div",{className:"text-[12px] text-muted-foreground",children:z}):null]}),B>0?s.jsx("span",{className:"flex-none text-[11.5px] text-muted-foreground",children:r("本地领取于 {time}",{time:mc(B/1e3)})}):null]},O||I.name)})}):s.jsx("p",{className:"py-1 text-[12.5px] text-muted-foreground",children:N===void 0?r("还没有余额读数(已自动查询一次,稍候;也可以点右上角「查询余额」重读)"):k&&k.source&&k.source!==gte?r("这个账号的余额走窗口限额通道(只配了编码套餐 API Key):只有每 N 小时的剩余比例,读不到套餐清单"):r("读数里没有套餐清单(免费账号、或套餐的上游状态还没刷新;可点右上角「查询余额」重读)")}),P.length>0?s.jsx("p",{className:"mt-1.5 text-[11.5px] text-muted-foreground",children:r("本地台账里今天还领过这些,但上面的读数里还没有:{plans}(读数刷新可能有延迟)",{plans:P.map(I=>{const O=Number(S[I])||0,_=O>0?mc(O/1e3):"";return _?r("{plan}({time} 领取)",{plan:I,time:_}):I}).join(r("、"))})}):null]})]}),s.jsxs(tn,{children:[s.jsx("span",{className:"min-w-0 text-[11.5px] text-muted-foreground",children:w.length>0?r("今天(北京时间)已领取 {n} 份:{plans}",{n:w.length,plans:w.join(r("、"))}):r("今天还没有领过:上面「可领取的套餐」里逐份点「领取」即可")}),s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:t,children:r("关闭")})]})]})})}let Vp=null,ld=null;function JP(){Vp&&(Vp.unmount(),Vp=null),ld&&(ld.remove(),ld=null)}function kte(e){e?.id&&(JP(),ld=document.createElement("div"),document.body.append(ld),Vp=jn.createRoot(ld),Vp.render(s.jsx(Nte,{options:e,onClose:JP})))}window.wbZcodePlans={open:kte};const e_="w-auto flex-auto";function t_({onInput:e,domRef:t,icon:a,className:o,inputClassName:c,...d}){const f=h=>{t.current=h},p=h=>{e?.(h.currentTarget.value)};return a?s.jsxs(Di,{className:Ue(e_,o),children:[s.jsx(Pi,{ref:f,className:c,onInput:p,...d}),s.jsx(Ii,{"aria-hidden":"true",children:a})]}):s.jsx(Fe,{ref:f,className:Ue(e_,c),onInput:p,...d})}function Cte(e,t){const a=jn.createRoot(e),o={current:null};return a.render(s.jsx(t_,{domRef:o,defaultValue:t.value,placeholder:t.placeholder,type:t.type??"search",icon:t.icon,"aria-label":t.ariaLabel,onInput:t.onInput})),{setValue(c){const d=o.current;d&&d.value!==c&&(d.value=c)},getValue(){return o.current?.value??""},focus(){o.current?.focus()},unmount(){a.unmount()}}}const Tte=[["id","id"],["type","type"],["name","name"],["title","title"],["placeholder","placeholder"],["min","min"],["max","max"],["step","step"],["autocomplete","autoComplete"]];function Ete(e){const t={defaultValue:e.value||void 0,inputClassName:e.className||void 0};for(const[f,p]of Tte){const h=e.getAttribute(f);h!=null&&(t[p]=h)}const a=e.getAttribute("maxlength");a!=null&&(t.maxLength=Number(a));const o=e.getAttribute("inputmode");o!=null&&(t.inputMode=o),e.hasAttribute("spellcheck")&&(t.spellCheck=e.spellcheck),e.disabled&&(t.disabled=!0),e.readOnly&&(t.readOnly=!0),e.hidden&&(t.hidden=!0);const c=e.getAttribute("aria-label");c!=null&&(t["aria-label"]=c);for(const f of e.getAttributeNames())!f.startsWith("data-")||f.startsWith("data-island-")||(t[f]=e.getAttribute(f)??"");const d=e.getAttribute("data-island-icon");return d!=null&&(t.icon=d),t}function n_(e){if(e.dataset.islandUpgraded)return;e.dataset.islandUpgraded="1";const t=Ete(e),a=e.parentElement?.classList.contains("input-affix")?e.parentElement:null;let o;a?(a.className=a.className.replace(/\binput-affix\b/,"").trim(),a.style.display="flex",a.style.alignItems="center",o=a):(o=document.createElement("span"),o.style.display="contents",e.replaceWith(o));const c={current:null},d=jn.createRoot(o);Ts.flushSync(()=>{d.render(s.jsx(t_,{...t,domRef:c}))})}function s_(e=document){if(e instanceof HTMLInputElement&&e.matches("[data-island-input]")){n_(e);return}e.querySelectorAll("input[data-island-input]").forEach(n_)}function Ate(){s_();const e=new MutationObserver(t=>{for(const a of t)for(const o of a.addedNodes)o instanceof Element&&s_(o)});return e.observe(document.body,{childList:!0,subtree:!0}),e}function Rte(e,t){const a=jn.createRoot(e);let o={options:t.options,value:t.value,ariaLabel:t.ariaLabel,className:t.className};const c=()=>{a.render(s.jsx(En,{options:o.options,value:o.value,onValueChange:t.onChange,"aria-label":o.ariaLabel,className:o.className}))};return c(),{setValue(d){d!==o.value&&(o={...o,value:d},c())},setOptions(d){o={...o,options:d},c()},unmount(){a.unmount()}}}window.wbSegmented={mount:Rte},window.wbInput={mount:Cte},Ate()})(); +`)}}const b=[...f.entries()].sort((y,v)=>y[0]-v[0]).map(([,y])=>y);b.length&&e.onEvent({type:"toolCalls",calls:b}),e.onEvent({type:"done",finishReason:p})}function iw(e){const t=String(e||"").trim();if(!t)return{};try{const a=JSON.parse(t);return a&&typeof a=="object"&&!Array.isArray(a)?a:{}}catch{return{}}}function el(){const e=window.workbuddyDesktop;if(!e)throw new Error(r("桌面桥不可用(本页需要在 Agent2API 客户端里打开)"));return e}function Mu(){window.wbApp?.refresh?.()}function at(e,t=256){return typeof e=="string"?e.trim().slice(0,t):""}function Qi(e){return typeof e=="boolean"?e:void 0}function Kf(e){return typeof e=="number"&&Number.isFinite(e)?e:void 0}function Du(e){return Array.isArray(e)?e.filter(t=>!!t&&typeof t=="object"):[]}function Dl(e){return(e instanceof Error?e.message:String(e)).replace(/^Error:\s*/,"")}async function Dg(){const e=await el().getState?.();return(Array.isArray(e?.accounts)?e.accounts:[]).slice().sort((a,o)=>Number(a.priority??0)-Number(o.priority??0))}function Yf(e){return at(e.name)||at(e.nickname)||at(e.id)||r("未命名账号")}function cw(e,t){const a=t.trim();if(!a)throw new Error(r("没给账号名"));const o=e.find(p=>at(p.id)===a);if(o)return o;const c=e.filter(p=>Yf(p)===a);if(c.length===1)return c[0];const d=e.filter(p=>Yf(p).includes(a));if(d.length===1)return d[0];const f=(c.length?c:d).map(Yf).slice(0,6);throw f.length?new Error(r("「{name}」匹配到多个账号:{list},请用完整账号名重试",{name:a,list:f.join("、")})):new Error(r("没有找到账号「{name}」",{name:a}))}const E7={id:"list_models",label:r("获取模型列表"),kind:"read",desc:"读取各提供商的模型清单(模型 ID、所属提供商、是否启用、已挂的映射名)以及全部映射。回答「有哪些模型」「哪些模型在多家重复」「某个名字挂在哪」这类问题之前先调用它。",params:{type:"object",properties:{provider:{type:"string",description:"只看某一家(提供商 id,如 codearts / workbuddy);不填 = 全部"},keyword:{type:"string",description:"按模型 ID 或映射名做子串过滤;不填 = 不过滤"},enabledOnly:{type:"boolean",description:"true = 只看已启用的模型"}}},run:async e=>{const t=at(e.provider,64).toLowerCase(),a=at(e.keyword,128).toLowerCase(),o=Qi(e.enabledOnly)===!0,c=await el().getModelManage?.(),f=(Array.isArray(c?.models)?c.models:[]).filter(p=>!(t&&at(p.provider).toLowerCase()!==t||o&&p.enabled!==!0||a&&![at(p.id),at(p.name),...Array.isArray(p.aliases)?p.aliases.map(b=>at(b)):[]].join(" ").toLowerCase().includes(a))).map(p=>({provider:at(p.provider),id:at(p.id),enabled:p.enabled===!0,aliases:Array.isArray(p.aliases)?p.aliases.map(h=>at(h)):[]}));return{text:JSON.stringify({total:f.length,models:f.slice(0,400)}),summary:r("{n} 个模型",{n:f.length})}}},A7={id:"list_account_balance",label:r("查询账号余额"),kind:"read",desc:"查询各账号的余额 / 剩余额度(会向各上游发起真实的余额查询)。回答「还剩多少」「哪个账号快用完了」这类问题之前先调用它。",params:{type:"object",properties:{account:{type:"string",description:"只查某个账号(账号名或 id);不填 = 全部账号"}}},run:async e=>{const t=at(e.account,128),a=t?at(cw(await Dg(),t).id):"",o=await el().getAllBalances?.(a||void 0),c=Array.isArray(o?.results)?o.results:o,d=Array.isArray(c)?c:[];return{text:JSON.stringify({accounts:d.slice(0,60)}).slice(0,12e3),summary:r("{n} 个账号",{n:d.length})}}},R7={id:"set_model_enabled",label:r("启用 / 禁用模型"),kind:"write",desc:"开启或关闭某几个模型在网关上的可用性(粒度是「提供商 × 模型」)。禁用后下游请求不再路由到它。一次可以改多个。",params:{type:"object",properties:{items:{type:"array",description:"要改的模型列表",items:{type:"object",properties:{provider:{type:"string",description:"提供商 id"},id:{type:"string",description:"模型 ID"},enabled:{type:"boolean",description:"true = 启用,false = 禁用"}},required:["provider","id","enabled"]}}},required:["items"]},plan:e=>{const t=Du(e.items);return{lead:r("将修改 {n} 个模型的启停状态:",{n:t.length}),table:{head:[r("提供商"),r("模型"),r("改成")],rows:t.map(a=>[at(a.provider),at(a.id),Qi(a.enabled)===!1?r("禁用"):r("启用")])},note:r("改动立即生效;禁用后指向它的请求会落到下一个候选。")}},run:async e=>{const t=el(),a=Du(e.items),o=[];for(const c of a){const d=at(c.provider),f=at(c.id),p=Qi(c.enabled);if(!d||!f||p===void 0)throw new Error(r("参数不完整(需要 provider / id / enabled)"));await t.setModelState?.({provider:d,id:f,enabled:p}),o.push(`${p?"启用":"禁用"} ${d}/${f}`)}return Mu(),{text:JSON.stringify({ok:!0,changed:o}),summary:r("已修改 {n} 个模型",{n:o.length})}}},O7={id:"set_model_mapping",label:r("设置模型映射 ID"),kind:"write",desc:"给模型新增或删除映射名(对外名)。同一对外名可以在多个提供商各挂一条:下游用同一个名字请求,网关按账号优先级主备切换;也可以给不同家的同名模型各加前缀(如 ca/claude-sonnet-4.5)来精确指定某一家。一次可以改多条。删掉的映射名只是不再对外可用,不删任何模型。",params:{type:"object",properties:{items:{type:"array",description:"要改的映射列表",items:{type:"object",properties:{provider:{type:"string",description:"提供商 id"},id:{type:"string",description:"上游模型 ID"},alias:{type:"string",description:"要新增 / 删除的映射名(对外名)"},action:{type:"string",enum:["add","remove"],description:"add = 新增映射,remove = 删除映射"}},required:["provider","id","alias","action"]}}},required:["items"]},plan:e=>{const t=Du(e.items),a=t.filter(d=>at(d.action)!=="remove").length,o=t.length-a,c=[];return a&&c.push(r("新增 {n} 条",{n:a})),o&&c.push(r("删除 {n} 条",{n:o})),{lead:r("将改动 {n} 条映射({parts}):",{n:t.length,parts:c.join("、")}),table:{head:[r("提供商"),r("上游模型"),r("操作"),r("映射名")],rows:t.map(d=>[at(d.provider),at(d.id),at(d.action)==="remove"?r("删除"):r("新增"),at(d.alias)])},note:r("只改映射名,不动上游模型 ID;映射立即生效(新对话即可用)。")}},run:async e=>{const t=el(),a=Du(e.items),o=[];for(const c of a){const d=at(c.provider),f=at(c.id),p=at(c.alias,128);if(!d||!f||!p)throw new Error(r("参数不完整(需要 provider / id / alias)"));at(c.action)==="remove"?(await t.removeModelMapping?.(p,f,d),o.push(`删除 ${p} → ${d}/${f}`)):(await t.addModelMapping?.(p,f,d),o.push(`新增 ${p} → ${d}/${f}`))}return Mu(),{text:JSON.stringify({ok:!0,changed:o}),summary:r("已改动 {n} 条映射",{n:o.length})}}},WO=["maxInputTokens","maxOutputTokens","supportsToolCall","supportsImages","supportsVideo","supportsReasoning"],uw={maxInputTokens:r("上下文窗口"),maxOutputTokens:r("最大输出 Token"),supportsToolCall:r("工具调用"),supportsImages:r("图片识别"),supportsVideo:r("视频识别"),supportsReasoning:r("深度推理")},M7={id:"set_model_capability",label:r("修改模型能力"),kind:"write",desc:"修改某个模型对下游声明的能力位:上下文窗口、最大输出 Token、工具调用 / 图片 / 视频 / 深度推理四个布尔位。只传要改的键;布尔位给 null 表示恢复上游原值。",params:{type:"object",properties:{provider:{type:"string",description:"提供商 id"},id:{type:"string",description:"模型 ID"},capabilities:{type:"object",description:"要改的能力位(只写要改的键)",properties:{maxInputTokens:{type:["number","null"],description:"上下文窗口(token 数)"},maxOutputTokens:{type:["number","null"],description:"最大输出 token 数"},supportsToolCall:{type:["boolean","null"],description:"是否支持工具调用;null = 恢复上游原值"},supportsImages:{type:["boolean","null"],description:"是否支持图片识别;null = 恢复上游原值"},supportsVideo:{type:["boolean","null"],description:"是否支持视频识别;null = 恢复上游原值"},supportsReasoning:{type:["boolean","null"],description:"是否支持深度推理;null = 恢复上游原值"}}}},required:["provider","id","capabilities"]},plan:e=>{const t=e.capabilities&&typeof e.capabilities=="object"?e.capabilities:{},a=WO.filter(o=>o in t);return{lead:r("将修改 {model} 的 {n} 项能力:",{model:`${at(e.provider)} / ${at(e.id)}`,n:a.length}),table:{head:[r("能力"),r("改成")],rows:a.map(o=>{const c=t[o],d=c===null?r("恢复上游原值"):typeof c=="boolean"?r(c?"支持":"不支持"):String(c);return[uw[o]||o,d]})},note:r("能力位决定下游看到的声明(能不能贴图、能塞多少上下文),不改上游的真实行为。")}},run:async e=>{const t=at(e.provider),a=at(e.id),o=e.capabilities&&typeof e.capabilities=="object"?e.capabilities:{},c={};for(const d of WO){if(!(d in o))continue;const f=o[d];if(f===null){c[d]=null;continue}if(d==="maxInputTokens"||d==="maxOutputTokens"){const p=Kf(f);if(p===void 0)throw new Error(r("{key} 需要数字",{key:uw[d]}));c[d]=Math.round(p)}else{const p=Qi(f);if(p===void 0)throw new Error(r("{key} 需要 true / false / null",{key:uw[d]}));c[d]=p}}if(!t||!a||!Object.keys(c).length)throw new Error(r("参数不完整(需要 provider / id / capabilities)"));return await el().setModelCapabilities?.(t,a,c),Mu(),{text:JSON.stringify({ok:!0,provider:t,id:a,capabilities:c}),summary:r("已修改 {n} 项能力",{n:Object.keys(c).length})}}},D7={id:"test_model",label:r("发起模型测试"),kind:"run",desc:"对某个模型发一次真实的对话请求,回报连通性、耗时与首字延迟。会消耗该账号的额度。回答「这个模型通不通 / 快不快」时用它。",params:{type:"object",properties:{provider:{type:"string",description:"提供商 id"},id:{type:"string",description:"模型 ID"},prompt:{type:"string",description:"测试用的用户提示词;不填 = 默认一句问候(最小请求)"}},required:["provider","id"]},plan:e=>({lead:r("将对 {model} 发起一次真实请求:",{model:`${at(e.provider)} / ${at(e.id)}`}),table:{head:[r("项"),r("值")],rows:[[r("提示词"),at(e.prompt)||r("(默认问候)")],[r("代价"),r("消耗该账号额度")]]},note:r("这是真实的上游请求,与生产走同一条链路(会记账,报表排除)。")}),run:async e=>{const t={provider:at(e.provider),model:at(e.id)},a=at(e.prompt,4e3);a&&(t.prompt=a);const o=await el().testModel?.(t),c=String(o?.status??""),d=o?.success===!0,f={ok:d,status:c,error:o?.error??null,reply:String(o?.reply??"").slice(0,1500),duration_ms:o?.duration_ms??null,ttfb_ms:o?.ttfb_ms??null,account:o?.account_name??null};return{text:JSON.stringify(f).slice(0,6e3),summary:d?r("连通 · {ms}ms",{ms:String(f.duration_ms??"")}):r("失败 · {status}",{status:c||"—"})}}},I7={id:"set_provider_order",label:r("调整提供商顺位"),kind:"write",desc:"调整某个提供商在账号全局队列里的**整组顺位**(第几组),决定路由时先试哪一家。这一家的所有账号会作为一整块移动。位置从 1 开始数。",params:{type:"object",properties:{provider:{type:"string",description:"提供商 id"},position:{type:"number",description:"目标组顺位(1 = 最前)"}},required:["provider","position"]},plan:e=>({lead:r("将把「{provider}」整组移到第 {position} 顺位:",{provider:at(e.provider)||r("(未指定)"),position:String(Kf(e.position)??"?")}),note:r("队列是一次性整表提交(后端按新顺序重编优先级),移动会连带调整其它组的相对次序。")}),run:async e=>{const t=at(e.provider).toLowerCase(),a=Kf(e.position);if(!t||a===void 0)throw new Error(r("参数不完整(需要 provider / position)"));const o=await Dg(),c=[];for(const h of o){const b=at(h.id),y=at(h.provider).toLowerCase();if(!b)continue;const v=c[c.length-1];v&&v.provider===y?v.ids.push(b):c.push({provider:y,ids:[b]})}const d=c.findIndex(h=>h.provider===t);if(d<0)throw new Error(r("没有找到提供商「{provider}」的账号",{provider:t}));const f=Math.max(0,Math.min(c.length-1,Math.round(a)-1)),[p]=c.splice(d,1);return c.splice(f,0,p),await el().reorderAccounts?.(c.flatMap(h=>h.ids)),Mu(),{text:JSON.stringify({ok:!0,provider:t,from:d+1,to:f+1,moved:p.ids.length}),summary:r("已移到第 {n} 顺位",{n:f+1})}}},P7={id:"set_account_order",label:r("调整账号优先级"),kind:"write",desc:"把某个账号移动到账号全局队列的第 N 顺位(1 = 最前)。位置决定主备与切换顺序:越靠前越先被使用。",params:{type:"object",properties:{account:{type:"string",description:"账号名或账号 id"},position:{type:"number",description:"目标顺位(1 = 最前)"}},required:["account","position"]},plan:e=>({lead:r("将把账号「{account}」移到第 {position} 顺位:",{account:at(e.account)||r("(未指定)"),position:String(Kf(e.position)??"?")}),note:r("队列是一次性整表提交;位置越靠前,路由时越先被选中。")}),run:async e=>{const t=at(e.account,128),a=Kf(e.position);if(!t||a===void 0)throw new Error(r("参数不完整(需要 account / position)"));const o=await Dg(),c=cw(o,t),d=at(c.id),f=o.map(b=>at(b.id)).filter(Boolean),p=f.indexOf(d);if(p<0)throw new Error(r("账号不在当前队列里"));f.splice(p,1);const h=Math.max(0,Math.min(f.length,Math.round(a)-1));return f.splice(h,0,d),await el().reorderAccounts?.(f),Mu(),{text:JSON.stringify({ok:!0,account:Yf(c),from:p+1,to:h+1}),summary:r("已移到第 {n} 顺位",{n:h+1})}}},_7={id:"set_account_enabled",label:r("启用 / 禁用账号"),kind:"write",desc:"开启或关闭某几个账号的转发资格。禁用只是把它移出队列(余额仍可查),但**如果某条线路只剩它**,禁用会直接断供。一次可以改多个。",params:{type:"object",properties:{items:{type:"array",description:"要改的账号列表",items:{type:"object",properties:{account:{type:"string",description:"账号名或账号 id"},enabled:{type:"boolean",description:"true = 启用,false = 禁用"}},required:["account","enabled"]}}},required:["items"]},plan:e=>{const t=Du(e.items),a=t.filter(o=>Qi(o.enabled)===!1).length;return{lead:r("将修改 {n} 个账号的启用状态(其中禁用 {k} 个):",{n:t.length,k:a}),table:{head:[r("账号"),r("改成")],rows:t.map(o=>[at(o.account),Qi(o.enabled)===!1?r("禁用"):r("启用")])},note:r("禁用会把账号移出转发队列;若这条线路没有别的可用账号,请求会直接失败。")}},run:async e=>{const t=el(),a=Du(e.items),o=await Dg(),c=[];for(const d of a){const f=Qi(d.enabled);if(f===void 0)throw new Error(r("参数不完整(需要 account / enabled)"));const p=cw(o,at(d.account,128));await t.updateAccount?.(at(p.id),{enabled:f}),c.push(`${f?"启用":"禁用"} ${Yf(p)}`)}return Mu(),{text:JSON.stringify({ok:!0,changed:c}),summary:r("已修改 {n} 个账号",{n:c.length})}}},tl=[E7,A7,R7,O7,M7,D7,I7,P7,_7];function dw(e){return tl.find(t=>t.id===e)}function L7(e){return tl.filter(t=>e[t.id]).map(t=>({type:"function",function:{name:t.id,description:t.desc,parameters:t.params}}))}const JO=[{id:"list_models",label:r("获取模型列表"),desc:r("读各提供商的模型清单、映射名与启用状态。")},{id:"list_account_balance",label:r("查询账号余额"),desc:r("读各账号的额度 / 剩余用量。")},{id:"set_model_enabled",label:r("启用 / 禁用模型"),desc:r("开关模型的对外可用性,不改其它属性。")},{id:"set_model_mapping",label:r("设置模型映射 ID"),desc:r("新增 / 删除映射名,支持批量加前缀与同名合并到多家。")},{id:"set_model_capability",label:r("修改模型能力"),desc:r("改上下文窗口 / 最大输出 Token / 工具调用等能力位。")},{id:"test_model",label:r("发起模型测试"),desc:r("对指定模型发一次真实请求,回报连通性与耗时。"),tag:"cost"},{id:"set_provider_order",label:r("调整提供商顺位"),desc:r("改某一家的整组顺位(先试哪一家)。")},{id:"set_account_order",label:r("调整账号优先级"),desc:r("改账号在全局队列里的名次,决定主备与切换顺序。")},{id:"set_account_enabled",label:r("启用 / 禁用账号"),desc:r("禁用会把账号移出转发队列,线路可能因此断供。"),tag:"danger"}],eM=12;function Xf(e,t){return window.wbIcons?.icon?.(e,t)||""}function z7(e){const t=window.wbMarkdown;return t?.render?t.render(e):e.replace(/[&<>]/g,a=>({"&":"&","<":"<",">":">"})[a]||a)}function B7({tone:e}){return e==="plain"?s.jsx("span",{className:"asst-tc-ico",dangerouslySetInnerHTML:{__html:Xf("spark",13)}}):s.jsx("span",{className:Ue("asst-tc-ico",e),children:e==="warn"?"!":"✕"})}function H7({item:e,onConfirm:t}){const a=dw(e.call.name),o=a?.label||e.call.name,c=iw(e.call.arguments),d=Object.keys(c).length?JSON.stringify(c,null,2):"";return s.jsxs("div",{className:Ue("asst-tool-card",e.state==="confirm"&&"confirm",e.state==="confirm"&&a?.kind==="run"&&"danger",e.state==="failed"&&"failed",e.state==="running"&&"running"),children:[s.jsxs("div",{className:"asst-tc-head",children:[e.state==="running"?s.jsx("span",{className:"asst-tc-ico",children:s.jsx("span",{className:"spin"})}):s.jsx(B7,{tone:e.state==="failed"?"bad":e.state==="confirm"?"warn":"plain"}),s.jsx("span",{className:"asst-tc-name",children:o}),e.state==="confirm"&&s.jsx("span",{className:"badge tag warn",children:r("待确认")}),e.state==="done"&&s.jsxs("span",{className:"asst-tc-state",children:["✓ ",e.summary||r("完成")]}),e.state==="failed"&&s.jsx("span",{className:"asst-tc-state bad",children:r("失败")}),e.state==="cancelled"&&s.jsx("span",{className:"asst-tc-state plain",children:r("已取消")})]}),e.state==="confirm"&&e.plan&&s.jsxs("div",{className:"asst-tc-plan",children:[e.plan.lead,e.plan.table&&s.jsxs("table",{className:"asst-mini-table",children:[s.jsx("thead",{children:s.jsx("tr",{children:e.plan.table.head.map((f,p)=>s.jsx("th",{children:f},p))})}),s.jsx("tbody",{children:e.plan.table.rows.slice(0,20).map((f,p)=>s.jsx("tr",{children:f.map((h,b)=>s.jsx("td",{className:b===f.length-1?"mono hl":"",children:h},b))},p))})]}),e.plan.table&&e.plan.table.rows.length>20&&s.jsx("div",{className:"hint",children:r("(仅显示前 20 条,共 {n} 条)",{n:e.plan.table.rows.length})}),e.plan.note&&s.jsx("div",{className:"hint",children:e.plan.note})]}),e.state==="failed"&&e.error&&s.jsx("div",{className:"asst-tc-error",children:e.error}),e.state==="done"&&e.detail&&s.jsxs("details",{children:[s.jsx("summary",{className:"asst-tc-result",style:{cursor:"pointer"},children:r("查看结果")}),s.jsx("pre",{className:"asst-tc-json",children:e.detail})]}),e.state!=="confirm"&&d&&s.jsxs("details",{children:[s.jsx("summary",{className:"asst-tc-result",style:{cursor:"pointer"},children:r("查看参数")}),s.jsx("pre",{className:"asst-tc-json",children:d})]}),e.state==="confirm"&&s.jsxs("div",{className:"asst-tc-foot",children:[s.jsx("span",{className:Ue("hint",a?.kind==="run"&&"danger"),children:a?.kind==="run"?r("这会真实发起上游请求并消耗额度"):r("确认后立即生效")}),s.jsx(ne,{size:"sm",variant:a?.kind==="run"?"destructive":"default",onClick:()=>t(!0),children:r("执行")}),s.jsx(ne,{size:"sm",variant:"outline",onClick:()=>t(!1),children:r("取消")})]})]})}function U7(){const[e,t]=x.useState(null),[a,o]=x.useState([]),[c,d]=x.useState([]),[f,p]=x.useState(!1),[h,b]=x.useState(!1),[y,v]=x.useState(""),[j,w]=x.useState(""),[S,N]=x.useState(!1),k=x.useRef([]),A=x.useRef([]),P=x.useRef(null),R=x.useRef(null),C=x.useRef(null),E=x.useRef(!1),D=x.useRef(0),I=x.useRef(null),O=x.useRef(0),_=x.useCallback(()=>{d(k.current.slice())},[]);x.useEffect(()=>{const M=C.current;if(!M)return;M.scrollHeight-M.scrollTop-M.clientHeight<160&&(M.scrollTop=M.scrollHeight)},[c]);const z=x.useCallback(async()=>{if(!E.current){E.current=!0;try{const M=await QO();t(M),N(!1),D.current=0}catch(M){N(!0),w(r("加载助手设置失败:{message}",{message:Dl(M)})),D.current<3&&(D.current+=1,E.current=!1,setTimeout(()=>{z()},4e3))}try{o(await KO())}catch(M){w(U=>U||r("读取模型清单失败:{message}",{message:Dl(M)}))}}},[]);x.useEffect(()=>{const M=window;return M.wbAssistant={activate:()=>{z()}},z(),()=>{delete M.wbAssistant}},[z]);const H=x.useMemo(()=>{const M={};for(const U of tl)M[U.id]=e?.tools?.[U.id]??!1;return M},[e]),B=x.useMemo(()=>L7(H),[H]),L=Object.values(H).filter(Boolean).length,F=e?.model??null,$=x.useCallback(async(M,U,Z)=>{for(let ae=U;ae{if(O.current>=eM){k.current.push({kind:"notice",text:r("这一轮来回已达 {n} 次上限,我先停下等你指示(可以换个说法,或拆成几步让我做)。",{n:eM}),bad:!0}),_();return}O.current+=1,p(!0),w("");const M=new AbortController;R.current=M;const U=k.current.push({kind:"bot",text:"",streaming:!0})-1;_();let Z=[],ae=!1;try{await T7({messages:A.current,tools:B,signal:M.signal,onEvent:ue=>{if(ue.type==="delta"){const ve=k.current[U];ve&&ve.kind==="bot"&&(k.current[U]={...ve,text:ve.text+ue.text},_())}ue.type==="toolCalls"&&(Z=ue.calls)}})}catch(ue){ae=!0;const ve=Dl(ue),ee=k.current[U],he=ee&&ee.kind==="bot"?ee.text:"";he?k.current[U]={kind:"bot",text:he}:k.current.splice(U,1);const te=M.signal.aborted;k.current.push({kind:"notice",text:te?r("已停止这次回答。"):r("请求失败:{message}",{message:ve}),bad:!te}),_()}finally{R.current=null}if(ae){p(!1);return}const ce=k.current[U];ce&&ce.kind==="bot"&&(k.current[U]={...ce,streaming:!1});const W=ce&&ce.kind==="bot"?ce.text:"";if(A.current.push({role:"assistant",content:W,toolCalls:Z.length?Z:void 0}),_(),!Z.length){p(!1);return}await $(Z,0,[])},[B,$,_]);I.current=X;const K=x.useCallback(async M=>{const U=P.current;if(!U)return;P.current=null,b(!1);const Z=U.calls[U.index],ae=dw(Z.name),ce=iw(Z.arguments);if(!M||!ae)k.current[U.cardIndex]={kind:"tool",call:Z,state:"cancelled"},U.collected.push({toolCallId:Z.id,content:"用户取消了这次操作,没有做任何改动。"}),_();else{k.current[U.cardIndex]={kind:"tool",call:Z,state:"running"},p(!0),_();try{const W=await ae.run(ce);k.current[U.cardIndex]={kind:"tool",call:Z,state:"done",summary:W.summary,detail:W.text},U.collected.push({toolCallId:Z.id,content:W.text})}catch(W){const ue=Dl(W);k.current[U.cardIndex]={kind:"tool",call:Z,state:"failed",error:ue},U.collected.push({toolCallId:Z.id,content:`工具执行失败:${ue}`})}_()}await $(U.calls,U.index+1,U.collected)},[$,_]),q=x.useCallback(async M=>{const U=M.trim();if(!(!U||f||h)){if(O.current=0,k.current.push({kind:"user",text:U}),A.current.push({role:"user",content:U}),v(""),_(),!e){k.current.push({kind:"notice",text:r("助手设置没读出来,现在还不能发消息 —— 看输入框下方的提示。"),bad:!0}),_();return}if(!e.model){k.current.push({kind:"notice",text:r("还没有选择对话模型:先在下方选一个(或去「设置 → AI 助手」里选)。"),bad:!0}),_();return}await X()}},[h,f,e,X,_]),Q=x.useCallback(()=>{R.current?.abort()},[]),G=x.useCallback(()=>{R.current?.abort(),P.current=null,k.current=[],A.current=[],b(!1),p(!1),w(""),_()},[_]),J=x.useCallback(async M=>{if(!M)return;if(!e){w(r("助手设置没读出来,暂时存不了对话模型 —— 看输入框下方的提示。"));return}const U={...e,model:M};t(U);try{await ZO(U)}catch(Z){w(r("保存对话模型失败:{message}",{message:Dl(Z)}))}},[e]),Y=f||h,re=x.useRef(null);return s.jsxs("div",{className:"asst-layout",children:[s.jsxs("div",{className:"asst-top",children:[s.jsxs(ut,{value:F??"",onValueChange:M=>{J(String(M))},children:[s.jsx(ft,{className:"asst-model-pick","aria-label":r("对话模型"),children:s.jsx(dt,{children:F?s.jsxs("span",{className:"flex items-center gap-2",children:[s.jsx("span",{className:Ue("dot","ok")}),s.jsx("span",{className:"mono",children:F})]}):r("选择模型…")})}),s.jsxs(pt,{children:[a.length===0&&s.jsx($e,{value:"none",disabled:!0,children:r("没有可用的模型")}),a.map(M=>s.jsx($e,{value:M,children:M},M))]})]}),s.jsx("span",{className:"asst-top-hint",children:r("助手走本地网关,消耗所选账号的额度")}),s.jsx("span",{className:"spacer"}),s.jsx(ne,{variant:"ghost",size:"sm",onClick:G,disabled:f||h||!c.length,children:r("新对话")})]}),s.jsxs("div",{className:"asst-scroll",ref:C,children:[c.length===0&&s.jsxs("div",{className:"asst-empty",children:[s.jsx("div",{className:"empty-logo",dangerouslySetInnerHTML:{__html:Xf("spark",26)||""}}),s.jsx("h3",{children:r("AI 助手")}),s.jsxs("p",{children:[r("用一句话让我查或改本机的模型转发配置。"),s.jsx("br",{}),r("我会在动手前把改动列给你确认。")]}),s.jsx("div",{className:"asst-suggests",children:[r("看看哪些模型在多家重复了"),r("测一下当前最快的模型通不通"),r("CodeArts 的账号余额还剩多少?"),r("把重复的模型加上家名前缀区分开")].map(M=>s.jsxs("button",{type:"button",onClick:()=>{q(M)},children:["“",M,"”"]},M))}),e!==null&&L===0&&s.jsx("div",{className:"asst-empty-warn",children:r("当前一个工具都没勾选,助手只能纯聊天。去「设置 → AI 助手 → 工具」里勾上需要的工具。")}),S&&s.jsx("div",{className:"asst-empty-warn bad",children:j||r("助手设置读取失败,工具可能已过时。")})]}),c.map((M,U)=>M.kind==="user"?s.jsx("div",{className:"asst-msg user",children:s.jsx("div",{className:"bubble",children:M.text})},U):M.kind==="bot"?!M.text&&M.streaming?s.jsxs("div",{className:"asst-msg bot",children:[s.jsxs("div",{className:"who",children:[s.jsx("span",{className:"ico",dangerouslySetInnerHTML:{__html:Xf("spark",14)||""}})," ",r("助手")]}),s.jsx("div",{className:"body",children:s.jsx("span",{className:"spin"})})]},U):s.jsxs("div",{className:"asst-msg bot",children:[s.jsxs("div",{className:"who",children:[s.jsx("span",{className:"ico",dangerouslySetInnerHTML:{__html:Xf("spark",14)||""}})," ",r("助手")]}),s.jsxs("div",{className:"body",children:[s.jsx("span",{dangerouslySetInnerHTML:{__html:z7(M.text)}}),M.streaming&&s.jsx("span",{className:"asst-caret"})]})]},U):M.kind==="notice"?s.jsx("div",{className:"asst-msg bot",children:s.jsx("div",{className:Ue("asst-empty-warn",M.bad&&"bad"),style:{margin:0},children:M.text})},U):s.jsx(H7,{item:M,onConfirm:Z=>{K(Z)}},U))]}),s.jsxs("div",{className:"asst-composer",children:[s.jsxs("div",{className:"asst-ta-wrap",children:[s.jsx(Mi,{ref:re,rows:1,value:y,placeholder:r(Y?"等这一轮跑完…":"直接说要改什么 / 查什么…(Enter 发送,Shift+Enter 换行)"),disabled:Y,onChange:M=>{v(M.currentTarget.value);const U=M.currentTarget;U.style.height="auto",U.style.height=`${Math.min(U.scrollHeight,132)}px`},onKeyDown:M=>{M.key==="Enter"&&!M.shiftKey&&!M.nativeEvent.isComposing&&(M.preventDefault(),q(y))}}),f?s.jsx("button",{type:"button",className:"asst-send stop",title:r("停止"),onClick:Q,children:"■"}):s.jsx("button",{type:"button",className:"asst-send",title:r("发送"),disabled:Y||!y.trim(),onClick:()=>{q(y)},children:"➤"})]}),s.jsxs("div",{className:"asst-composer-foot",children:[s.jsxs("span",{className:"tools-state",children:[s.jsx("span",{className:"ico",dangerouslySetInnerHTML:{__html:Xf("settings",12)||""}}),r("已注入 {n} / {total} 个工具 · 系统提示词:{state}",{n:L,total:tl.length,state:e?.systemPromptEnabled?r("开"):r("关")})]}),s.jsx("span",{className:"spacer"}),j?s.jsxs("span",{className:"hint",children:[j,S&&s.jsx("button",{type:"button",className:"asst-tc-more",onClick:()=>{z()},children:r("重试")})]}):F?s.jsx("span",{className:"hint",children:r("回复由 {model} 生成 · 走本地网关",{model:F})}):null]})]})]})}const tM='.page[data-page="assistant"]';let fw=null;function nM(){if(fw)return;const e=document.querySelector(tM);e&&(e.replaceChildren(),fw=wn.createRoot(e),fw.render(s.jsx(U7,{})))}document.querySelector(tM)?nM():document.addEventListener("DOMContentLoaded",nM,{once:!0}),GO().catch(()=>{});function V7(e,t){return window.wbIcons?.icon?.(e,t)||""}function F7({id:e,label:t,checked:a,disabled:o,onCheckedChange:c}){return s.jsxs("label",{className:"switch",children:[s.jsx(ns,{id:e,checked:a,disabled:o,onCheckedChange:d=>c(d)}),s.jsx("span",{children:t})]})}function $7(){const[e,t]=x.useState(null),[a,o]=x.useState([]),[c,d]=x.useState(""),[f,p]=x.useState(!1),[h,b]=x.useState(!1),[y,v]=x.useState(!1),[j,w]=x.useState("");x.useEffect(()=>{let C=!0;return(async()=>{try{const E=await QO();if(!C)return;t(E)}catch(E){if(!C)return;p(!0),d(r("加载助手设置失败:{message}",{message:Dl(E)}))}try{const E=await KO();if(!C)return;o(E)}catch(E){if(!C)return;d(D=>D||r("读取模型清单失败:{message}",{message:Dl(E)}))}})(),()=>{C=!1}},[]);const S=x.useCallback(async(C,E)=>{t(C),b(!0);try{const D=await ZO(C);t(D),p(!1),d(E||r("已保存"))}catch(D){p(!0),d(r("保存失败:{message}",{message:Dl(D)}))}finally{b(!1)}},[]),N=e!==null,k=x.useMemo(()=>{const C={};for(const E of tl)C[E.id]=e?.tools?.[E.id]??!1;return C},[e]),A=Object.values(k).filter(Boolean).length,P=!!e?.systemPrompt?.trim(),R=(C,E)=>{if(!e)return;const D=JO.find(O=>O.id===C)?.label||C,I={...e,tools:{...e.tools,[C]:E}};S(I,E?r("已勾选「{label}」",{label:D}):r("已取消「{label}」",{label:D}))};return s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("对话模型")}),s.jsx("span",{className:"panel-sub",children:r("助手自己用哪个模型生成回复")})]}),s.jsx("div",{className:"panel-body",children:s.jsxs("div",{className:"asst-row",children:[s.jsx("label",{children:r("模型")}),s.jsxs(ut,{value:e?.model??"",disabled:!N||h,onValueChange:C=>{if(!e)return;const E=String(C);E&&S({...e,model:E},r("对话模型已切换"))},children:[s.jsx(ft,{className:"asst-model-pick","aria-label":r("对话模型"),children:s.jsx(dt,{children:e?.model??r("选择模型…")})}),s.jsxs(pt,{children:[a.length===0&&s.jsx($e,{value:"none",disabled:!0,children:r("没有可用的模型")}),a.map(C=>s.jsx($e,{value:C,children:C},C))]})]}),s.jsx("span",{className:"hint",children:r("列表 = 模型管理页里所有已启用的模型。助手走本地网关发起对话,消耗所选账号的额度。")})]})})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("系统提示词")}),s.jsx("span",{className:"panel-sub",children:r("作为 system 消息注入给助手")})]}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"asst-row",children:[s.jsx(F7,{id:"settings-assistant-prompt",label:r("注入系统提示词"),checked:e?.systemPromptEnabled!==!1,disabled:!N||h,onCheckedChange:C=>{e&&S({...e,systemPromptEnabled:C},r(C?"系统提示词已开启":"系统提示词已关闭"))}}),s.jsxs(ne,{variant:"outline",size:"sm",disabled:!N,onClick:()=>{w(e?.systemPrompt?.trim()?e.systemPrompt:lw),v(!0)},children:[s.jsx("span",{dangerouslySetInnerHTML:{__html:V7("settings",13)||""}}),r("编辑")]}),s.jsx("span",{className:"hint",children:r("点「编辑」弹窗改内容;开关关掉就整段不发。")})]}),s.jsx("div",{className:"settings-state",children:e?.systemPromptEnabled===!1?r("当前:已关闭(助手不收到任何系统提示词,工具定义照发)"):P?r("当前:已开启 · 自定义内容 {n} 字",{n:e?.systemPrompt.trim().length??0}):r("当前:已开启 · 使用内置默认提示词(还没自定义)")}),s.jsxs("div",{className:"asst-matrix",children:[s.jsx("div",{className:"mt-title",children:r("四种组合的实际效果")}),s.jsxs("table",{className:"asst-mini-table",children:[s.jsx("thead",{children:s.jsxs("tr",{children:[s.jsx("th",{children:r("提示词开关")}),s.jsx("th",{children:r("已勾选工具")}),s.jsx("th",{children:r("实际注入")})]})}),s.jsxs("tbody",{children:[s.jsxs("tr",{children:[s.jsx("td",{children:r("开")}),s.jsx("td",{children:r("有")}),s.jsx("td",{children:r("系统提示词 + 工具定义")})]}),s.jsxs("tr",{children:[s.jsx("td",{children:r("开")}),s.jsx("td",{children:s.jsx("b",{children:r("无")})}),s.jsx("td",{children:s.jsx("b",{children:r("都不注入(助手退化为纯聊天)")})})]}),s.jsxs("tr",{children:[s.jsx("td",{children:r("关")}),s.jsx("td",{children:r("有")}),s.jsx("td",{children:r("只注入工具定义")})]}),s.jsxs("tr",{children:[s.jsx("td",{children:r("关")}),s.jsx("td",{children:r("无")}),s.jsx("td",{children:r("都不注入")})]})]})]}),s.jsx("p",{className:"hint",children:r("「工具全不勾选 → 不注入系统提示词」:想当纯聊天用时,不必再去关上面的开关。")})]})]})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("工具")}),s.jsx("span",{className:"panel-sub",children:r("勾选即注入该工具;不勾选 = 助手看不到它")})]}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"asst-tools-bar",children:[s.jsx("span",{className:"badge brand",children:r("已勾选 {n} / {total}",{n:A,total:tl.length})}),s.jsx("span",{className:"spacer"}),s.jsx(ne,{variant:"ghost",size:"sm",disabled:!N||h,onClick:()=>{if(!e)return;const C={};for(const E of tl)C[E.id]=!1;S({...e,tools:C},r("已全部取消勾选"))},children:r("全不选")}),s.jsx(ne,{variant:"ghost",size:"sm",disabled:!N||h,onClick:()=>{if(!e)return;const C={};for(const E of tl)C[E.id]=E.kind==="read";for(const E of["set_model_enabled","set_model_mapping","set_model_capability","test_model"])C[E]=!0;S({...e,tools:C},r("已恢复推荐勾选"))},children:r("恢复推荐")})]}),["read","write"].map(C=>s.jsxs(x.Fragment,{children:[s.jsx("div",{className:"asst-tool-group",children:C==="read"?s.jsxs(s.Fragment,{children:[r("只读工具")," ",s.jsx("span",{className:"muted",children:r("· 不会改动任何配置")})]}):s.jsxs(s.Fragment,{children:[r("改动类工具")," ",s.jsx("span",{className:"muted",children:r("· 每次执行前列改动清单,等你确认")})]})}),JO.filter(E=>{const I=tl.find(O=>O.id===E.id)?.kind==="read";return C==="read"?I:!I}).map(E=>{const D=tl.find(O=>O.id===E.id),I=k[E.id]===!0;return s.jsxs("label",{className:"asst-tool",children:[s.jsx("input",{type:"checkbox",checked:I,disabled:!N||h,onChange:O=>R(E.id,O.currentTarget.checked)}),s.jsxs("span",{className:"tt",children:[s.jsxs("span",{className:"tt-name",children:[E.label,D?.kind==="read"&&s.jsx("span",{className:"badge tag plain",children:r("只读")}),E.tag==="cost"&&s.jsx("span",{className:"badge tag",children:r("消耗额度")}),E.tag==="danger"&&s.jsx("span",{className:"badge tag bad",children:r("高危")}),D?.kind==="write"&&!E.tag&&s.jsx("span",{className:"badge tag warn",children:r("影响转发")})]}),s.jsx("span",{className:"tt-desc",children:E.desc})]})]},E.id)})]},C)),s.jsx("div",{className:Ue("settings-state",f&&"text-destructive"),children:c})]})]}),s.jsx(Yt,{open:y,onOpenChange:C=>{C||v(!1)},children:s.jsxs(Xt,{className:"w-[min(680px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("编辑系统提示词")})}),s.jsxs(Lt,{children:[s.jsx(dn,{children:s.jsx("p",{children:r("这段文本作为 system 消息排在对话最前面。工具的定义与当前配置概览由网关自动附加,不用写在这里。")})}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"assistant-system-prompt",children:r("提示词正文")}),s.jsx(Mi,{id:"assistant-system-prompt",rows:14,className:"asst-prompt-ta min-h-[300px]",spellCheck:!1,value:j,onChange:C=>w(C.currentTarget.value)}),s.jsx("div",{className:"hint",children:r("清空 = 回到内置默认(与「恢复默认」等价)。")})]})]}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"outline",className:"mr-auto",onClick:()=>w(lw),children:r("恢复默认")}),s.jsx(ne,{variant:"outline",onClick:()=>v(!1),children:r("取消")}),s.jsx(ne,{variant:"default",onClick:()=>{if(!e)return;const C=j.trim()===lw.trim()?"":j;S({...e,systemPrompt:C},r("系统提示词已保存")),v(!1)},children:r("保存")})]})]})})]})}function Lr(){return Pe().workbuddyDesktop??{}}function sM(){return window}const rM="workbuddy-desktop-checkin-tab",q7=["center","schedule","records"];function G7(){try{const e=localStorage.getItem(rM);return q7.includes(e)?e:"center"}catch{return"center"}}function aM(e){if(e!==bs.tab){try{localStorage.setItem(rM,e)}catch{}sr({tab:e})}}async function Ig(e){aM(e),bs.loaded||await Il()}let bs={version:0,tab:G7(),snapshot:null,loaded:!1,loadError:"",expanded:new Set,runningAll:!1,autoSaving:!1,autoTimeDraft:null,keepaliveSaving:!1,keepaliveDraft:null,signing:new Set,onboarding:new Map,growth:new Map};const pw=new Set;function K7(){for(const e of pw)e()}function sr(e){bs={...bs,...e,version:bs.version+1},K7()}function fr(){return bs}function lM(e){return pw.add(e),()=>pw.delete(e)}function Y7(e){const t=typeof e?.key=="string"?e.key:"";return t?{key:t,title:typeof e.title=="string"&&e.title?e.title:t,group:typeof e.group=="string"&&e.group?e.group:"",points:Number(e.points)||0,done:e.done===!0,blocked:e.blocked===!0}:null}function mw(e){return(Array.isArray(e)?e:[]).map(Y7).filter(a=>a!==null)}function ps(e){return Number(e)||0}async function Il(){try{const e=await Lr().getCheckinCenter?.();if(!e)throw new Error(r("后端未返回签到中心数据"));sr({snapshot:e,loaded:!0,loadError:"",autoTimeDraft:null,keepaliveDraft:null}),X7(e.extras?.onboarding??[])}catch(e){sr({loaded:!0,loadError:mt(e)})}}function X7(e){for(const t of e){const a=t.settled;if(!a||typeof a!="object")continue;const o=bs.onboarding.get(t.id);if(o?.status==="loaded"&&o.unclaimed>0)continue;const c=mw(a.tasks),d=ps(a.at);Io(t.id,{status:"loaded",error:void 0,checkedAt:void 0,settled:!0,settledAt:d>0?d:void 0,tasks:c,unclaimed:0,earned:ps(a.earned),total:ps(a.total)})}}function oM(e){const t=new Set(bs.expanded);t.has(e)?t.delete(e):t.add(e),sr({expanded:t})}function hw(e){const t=`onb:${e}`,a=new Set(bs.expanded);a.has(t)?a.delete(t):a.add(t),sr({expanded:a})}function iM(e){return bs.expanded.has(`onb:${e}`)}async function Q7(){if(!bs.runningAll){sr({runningAll:!0});try{const e=await Lr().runAutoCheckinNow?.();if(e){const t=ps(e.succeeded),a=ps(e.active),o=ps(e.total),c=ps(e.failedCount),d=a>0?r(",日活保活 {n} 个",{n:a}):"";c>0?Le(r("签到完成:{succeeded}/{total} 成功{active},{failed} 个失败",{succeeded:t,total:o,active:d,failed:c}),"err"):Le(r("✅ 签到完成:{succeeded}/{total} 个账号成功领取{active}",{succeeded:t,total:o,active:d})),sM().wbAccountsView?.refreshUsageAfterCheckin?.(null)}}catch(e){Le(r("签到失败:{error}",{error:mt(e)}),"err")}finally{sr({runningAll:!1}),await Il(),cM(fr().snapshot?.extras.onboarding??[]),Pe().wbApp?.refresh?.()}}}async function Pg(e,t="checkin"){const a=`${e}:${t}`;if(bs.signing.has(a))return;const o=new Set(bs.signing);o.add(a),sr({signing:o});try{let c;t==="checkin"?c=(await Lr().checkinAllAccounts?.(e))?.results?.[0]:c=await Lr().runCheckinActivity?.(e,t)??void 0;const d=c?.claim,f=c?.activity,p=f?.report?.succeeded===!0,h=f?.webConversation?.succeeded===!0;c?.error?Le(r("签到失败:{error}",{error:String(c.error)}),"err"):t==="keepalive"?f?.pokeSucceeded===!0||h||p?Le(r("✅ {message}",{message:d?.msg||r("活跃保活完成")})):Le(d?.msg||r("活跃保活失败"),"err"):d?.success===!0?Le(r("✅ {message}",{message:d.msg||r("签到成功")})):d?.alreadyCompleted===!0?Le(d?.msg||r("今日已领取"),"err"):p?Le(r("✅ {message}",{message:d?.msg||r("活跃上报完成,但活动未开启")})):f?.pokeSucceeded===!0||h?Le(r("✅ {message}",{message:d?.msg||r("活跃保活完成,但活动未开启")})):Le(d?.msg||r("未领取"),"err"),sM().wbAccountsView?.refreshUsageAfterCheckin?.(e)}catch(c){Le(r("签到失败:{error}",{error:mt(c)}),"err")}finally{const c=new Set(fr().signing);c.delete(a),sr({signing:c}),await Il();const d=(fr().snapshot?.extras.onboarding??[]).filter(f=>f.id===e);cM(d),Pe().wbApp?.refresh?.()}}function _g(e){sr({keepaliveDraft:e.map(t=>t.trim()).filter(Boolean)})}async function Z7(e){const t=e.map(a=>a.trim()).filter(Boolean);if(!fr().keepaliveSaving){sr({keepaliveSaving:!0});try{const a=await Lr().saveCheckinKeepalive?.(t);if(a){const o=fr().snapshot;sr({snapshot:o&&{...o,keepalive:a},keepaliveDraft:null}),Le(r("已保存:保活模型链"))}}catch(a){Le(r("保存失败:{error}",{error:mt(a)}),"err")}finally{sr({keepaliveSaving:!1})}}}async function cM(e){for(const t of e)W7(t.id)||await gw(t.id);for(const t of e){const a=fr().onboarding.get(t.id);a?.status!=="loaded"||a.claiming||a.settled||(t.provider==="officeace"||a.unclaimed>0)&&await uM(t.id)}}function W7(e){return bs.onboarding.get(e)?.settled===!0}function Iu(e){return bs.onboarding.get(e)??{status:"idle",tasks:[],unclaimed:0,earned:0,total:0,claiming:!1}}function Io(e,t){const a=new Map(bs.onboarding);a.set(e,{...Iu(e),...t}),sr({onboarding:a})}async function gw(e,t){const a=Iu(e);if(!(a.status==="loading"||a.claiming)){Io(e,{status:"loading",error:void 0,settled:!1});try{const o=await Lr().getOnboardingTasks?.(e,t?.refresh===!0),c=mw(o?.tasks),d=o?.settled===!0;Io(e,{status:"loaded",checkedAt:Date.now(),tasks:c,unclaimed:c.filter(f=>!f.done&&!f.blocked).length,earned:ps(o?.earned),total:ps(o?.total),settled:d,settledAt:d&&ps(o?.settledAt)||void 0}),t?.expand===!0&&c.length>0&&!iM(e)&&hw(e)}catch(o){Io(e,{status:"error",error:mt(o),settled:!1})}}}async function uM(e){const t=Iu(e);if(!(t.claiming||t.status!=="loaded")){Io(e,{claiming:!0});try{Io(e,{tasks:Iu(e).tasks.map(v=>v.done?v:{...v,claiming:!0,error:void 0})});const a=await Lr().claimOnboardingTasks?.(e),o=new Map,c=Array.isArray(a?.results)?a.results:[];for(const v of c){const j=typeof v?.key=="string"?v.key:"";j&&v?.ok!==!0&&o.set(j,String(v?.error||r("领取失败")))}const d=mw(a?.tasks),f=(d.length?d:Iu(e).tasks).map(v=>({...v,claiming:!1,error:o.get(v.key)})),p=f.filter(v=>v.done&&!o.has(v.key)).length,h=a?.settled===!0;Io(e,{claiming:!1,checkedAt:Date.now(),tasks:f,unclaimed:f.filter(v=>!v.done&&!v.blocked).length,earned:ps(a?.earned),total:ps(a?.total),settled:h,settledAt:h&&ps(a?.settledAt)||void 0});const b=f.filter(v=>v.error&&!v.done).length,y=typeof a?.message=="string"?a.message.trim():"";b?Le(r("新手福利有 {n} 项领取失败,可重试",{n:b}),"err"):y?Le(y,"ok"):p&&Le(r("✅ 新手福利领取完成,累计 {n} 积分",{n:ps(a?.earned)}))}catch(a){const o=mt(a);Io(e,{claiming:!1,tasks:Iu(e).tasks.map(c=>({...c,claiming:!1,error:c.done?void 0:o}))}),Le(r("领取失败:{error}",{error:o}),"err")}finally{await Il()}}}function dM(e){return(Array.isArray(e)?e:[]).map(a=>{const o=a??{},c=typeof o.code=="string"?o.code:"";return c?{code:c,title:typeof o.title=="string"&&o.title?o.title:c,description:typeof o.description=="string"?o.description:"",status:typeof o.status=="string"?o.status:"",current:ps(o.current),target:ps(o.target),locked:o.locked===!0,unlockAt:o.unlockAt,supported:o.supported===!0,unforgeable:o.unforgeable===!0,unforgeableReason:typeof o.unforgeableReason=="string"?o.unforgeableReason:"",kind:typeof o.kind=="string"?o.kind:"",source:typeof o.source=="string"?o.source:""}:null}).filter(a=>a!==null)}function fM(e){return(Array.isArray(e)?e:[]).map(a=>{const o=a??{},c=typeof o.code=="string"?o.code:"";return c?{code:c,outcome:typeof o.outcome=="string"?o.outcome:"failed",message:typeof o.message=="string"?o.message:"",credit:ps(o.credit),energy:ps(o.energy)}:null}).filter(a=>a!==null)}function Zi(e){return bs.growth.get(e)??{status:"idle",energy:null,streak:null,tasks:[],travel:null,school:null,running:!1,runningSchool:!1,runningTravel:!1}}function zr(e,t){const a=new Map(bs.growth);a.set(e,{...Zi(e),...t}),sr({growth:a})}function J7(e){return bs.expanded.has(`growth:${e}`)}function pM(e){const t=`growth:${e}`,a=new Set(bs.expanded);a.has(t)?a.delete(t):a.add(t),sr({expanded:a})}async function Lg(e){const t=Zi(e);if(!(t.status==="loading"||t.running)){zr(e,{status:"loading",error:void 0});try{const a=await Lr().getGrowthStatus?.(e),o=a?.tasks??{},c=a?.travel??null,d=a?.school??null;zr(e,{status:"loaded",checkedAt:Date.now(),energy:a?.energy===null||a?.energy===void 0?null:ps(a.energy),streak:a?.streak===null||a?.streak===void 0?null:ps(a.streak),tasks:dM(o.tasks),travel:c,school:d?{...d,tasks:dM(d.tasks)}:null,error:typeof o.error=="string"?o.error:void 0})}catch(a){zr(e,{status:"error",error:mt(a)})}}}async function mM(e,t){const a=Zi(e);if(a.running)return;const o=(a.tasks??[]).filter(h=>!h.supported||h.unforgeable===!0||h.locked===!0?!1:t==="claim"?h.status==="completed"||h.current>=h.target&&h.status!=="claimed":h.status!=="claimed");if(o.length===0){Le(r("没有需要执行的任务"));return}zr(e,{running:!0,results:[],error:void 0});let c=0,d=0,f=0,p=0;for(const h of o)try{const b=await Lr().runGrowthTask?.(e,h.code,t),y=fM([b])[0];y&&(zr(e,{results:[...Zi(e).results??[],y]}),c+=y.credit??0,d+=y.energy??0,(y.outcome==="done"||y.outcome==="already")&&(f+=1),y.outcome==="failed"&&(p+=1))}catch(b){p+=1,zr(e,{results:[...Zi(e).results??[],{code:h.code,outcome:"failed",message:mt(b),credit:0,energy:0}]})}zr(e,{running:!1}),p>0?Le(r("成长任务完成({done} 项成功、{failed} 项失败),积分 +{credit} 能量 +{energy}",{done:f,failed:p,credit:c,energy:d}),"err"):Le(r("✅ 成长任务执行完成:积分 +{credit} 能量 +{energy}",{credit:c,energy:d})),await Lg(e)}async function e9(e,t){if(!Zi(e).runningSchool){zr(e,{runningSchool:!0,error:void 0});try{const o=await Lr().runGrowthSchool?.(e,t),c=fM(o?.results),d=o?.lottery??null;zr(e,{runningSchool:!1,results:c,schoolText:c.length?c.map(f=>`${f.code}:${f.message}`).join(";"):void 0,lotteryText:d?String(d.message??r("已抽 {n} 次",{n:ps(d.drawn)})):void 0,checkedAt:Date.now()}),Le(d?r("开学季执行完成:{message}",{message:String(d.message??"")}):r("✅ 开学季任务执行完成")),await Lg(e)}catch(o){zr(e,{runningSchool:!1,error:mt(o)}),Le(r("开学季执行失败:{error}",{error:mt(o)}),"err")}}}async function t9(e){if(!Zi(e).runningTravel){zr(e,{runningTravel:!0,error:void 0});try{const a=await Lr().runGrowthTravel?.(e),o=String(a?.message??""),c=String(a?.outcome??"");zr(e,{runningTravel:!1,checkedAt:Date.now()}),c==="failed"?Le(r("猫猫旅行失败:{message}",{message:o}),"err"):Le(r("猫猫旅行:{message}",{message:o})),await Lg(e)}catch(a){zr(e,{runningTravel:!1,error:mt(a)}),Le(r("猫猫旅行失败:{error}",{error:mt(a)}),"err")}}}function n9(e){return e.totalCount?e.doneCount>=e.totalCount?"done":e.doneCount>0?"part":"todo":"none"}function s9(e){const a=(Array.isArray(e?.schedules)?e.schedules:[]).filter(o=>o.enabled===!0).map(o=>Number(o.nextRunAt)).filter(o=>Number.isFinite(o)&&o>0);return a.length?Math.min(...a):null}let Po={version:0,data:null,loaded:!1,loadError:"",saving:!1,runningId:null,editingId:null};const xw=new Set;function r9(){for(const e of xw)e()}function Br(e){Po={...Po,...e,version:Po.version+1},r9()}function hM(){return Po}function a9(e){return xw.add(e),()=>xw.delete(e)}function Pu(){return Pe().workbuddyDesktop??{}}async function gM(){try{const e=await Pu().getCheckinSchedules?.();if(!e)throw new Error(r("后端未返回定时签到数据"));Br({data:e,loaded:!0,loadError:""})}catch(e){Br({loaded:!0,loadError:mt(e)})}}function zg(e){e&&Br({data:e,loadError:""}),Il()}async function l9(e){if(!Po.saving){Br({saving:!0});try{const t=await Pu().saveAutoCheckin?.({enabled:e});zg(t),Le(r(e?"✅ 定时签到已开启":"定时签到已关闭"))}catch(t){Le(r("保存失败:{error}",{error:mt(t)}),"err")}finally{Br({saving:!1})}}}async function xM(e){if(!Po.saving){Br({saving:!0});try{const t=await Pu().saveCheckinSchedule?.(e);zg(t),Br({editingId:null}),Le(r("已保存:定时条目"))}catch(t){Le(r("保存失败:{error}",{error:mt(t)}),"err")}finally{Br({saving:!1})}}}async function o9(e,t){await xM({id:e.id,label:e.label??"",enabled:t,time:e.time??"",providers:Array.isArray(e.providers)?e.providers:[]})}async function i9(e){if(!Po.saving){Br({saving:!0});try{await Pu().deleteCheckinSchedule?.(e),zg(await Pu().getCheckinSchedules?.()),Le(r("已删除定时条目"))}catch(t){Le(r("删除失败:{error}",{error:mt(t)}),"err")}finally{Br({saving:!1})}}}async function c9(e){if(!Po.runningId){Br({runningId:e});try{const t=await Pu().runCheckinSchedule?.(e)??void 0,a=o=>Number(o)||0;if(t){const o=a(t.succeeded),c=a(t.active),d=a(t.total),f=a(t.failedCount),p=c>0?r(",日活保活 {n} 个",{n:c}):"";f>0?Le(r("签到完成:{succeeded}/{total} 成功{active},{failed} 个失败",{succeeded:o,total:d,active:p,failed:f}),"err"):Le(r("✅ 签到完成:{succeeded}/{total} 个账号成功领取{active}",{succeeded:o,total:d,active:p})),zg(t)}}catch(t){Le(r("执行失败:{error}",{error:mt(t)}),"err")}finally{Br({runningId:null}),await Il()}}}function bw(e){Br({editingId:e})}const u9=r("到点后自动签到勾选提供商的可用账号(WorkBuddy 国内版走每日签到接口,国际版走每日活跃任务:探测活动、领日活奖励、再用免费模型保活并在网页通道建会话点亮日活;小浣熊走桌面端每日积分链路;AutoClaw 走官方客户端的每日签到任务;Qoder 中国版走活动领取,没被下发活动的账号会得到中性提示;Qoder 国际版要先带设备风控身份(本机 Qoder 客户端 / qodercli 的 UMID 组件生成)才能看到「每日 100 Credits」活动,缺组件时会得到明确的说明而不是报错;Trae 领 SOLO 的每日签到积分(按自然日 0 点重置),上游没对该账号开活动、或凭据里没有设备号时得到的是「未领取 + 具体原因」而不是报错;Loomy 每天替账号打一次首次登录积分;KukuAI 领「免费领积分」的每日任务;CodeArts 领当日的运营福利(领取 → 确认 → 回读核实,自动执行受每天 6 次 / 间隔 10 分钟的写请求限流);OfficeAce 果办一次领当天全部奖励(每日签到 + 新人礼))。错过时点开机后会自动补签,不会因为当时没开机而漏掉。各家签到接口都是幂等的,重复执行不会重复领取。");function d9(){const[e,t]=x.useState(hM());return x.useEffect(()=>a9(()=>t(hM())),[]),e}function f9(){const[e,t]=x.useState(fr());return x.useEffect(()=>lM(()=>t(fr())),[]),e}function bM(e,t){const a=Array.isArray(e.providers)?e.providers:[];return t.filter(o=>a.includes(o.id)).map(o=>o.label)}function p9(e,t){const a=bM(e,t);return a.length?t.length>0&&a.length>=t.length?r("全部 {n} 家",{n:t.length}):a.join(r("、")):r("未勾选提供商")}function m9(e,t){const a=bM(e,t);return a.length?a.join(r("、")):r("未勾选提供商")}function h9(e){const t=e.lastResult;if(!t)return r("还没执行过");const a=f=>Number(f)||0,o=[r("成功 {n}",{n:a(t.succeeded)})],c=a(t.active);c>0&&o.push(r("保活 {n}",{n:c}));const d=a(t.failedCount);return d>0&&o.push(r("失败 {n}",{n:d})),`${t.at?Ls(t.at):"—"}${t.reason?` · ${t.reason}`:""} —— ${o.join(r(" · "))}`}function vM({entry:e,store:t}){const a=Array.isArray(t.data?.providerOptions)?t.data.providerOptions:[],o=e===null,[c,d]=x.useState(e?.label??""),[f,p]=x.useState(e?.time??"00:01"),[h,b]=x.useState(new Set(o?a.map(S=>S.id):e?.providers??[]));x.useEffect(()=>{o&&a.length>0&&b(new Set(a.map(S=>S.id)))},[]);const y=f.trim()!==""&&h.size>0,v=()=>b(new Set(a.map(S=>S.id))),j=()=>b(new Set),w=async()=>{await xM({id:e?.id,label:c.trim(),enabled:e?.enabled??!0,time:f.trim(),providers:a.map(S=>S.id).filter(S=>h.has(S))})};return s.jsxs("div",{className:"ck-sched-editor",children:[s.jsxs("div",{className:"ck-set-line",children:[s.jsx("span",{className:"ck-set-k",children:r("名称")}),s.jsx("span",{className:"ck-set-v",children:s.jsx(Fe,{type:"text",className:"max-w-[220px]",placeholder:r("可选 · 缺省显示时刻"),value:c,disabled:t.saving,onChange:S=>d(S.currentTarget.value)})})]}),s.jsxs("div",{className:"ck-set-line",children:[s.jsx("span",{className:"ck-set-k",children:r("每天时刻")}),s.jsxs("span",{className:"ck-set-v flex items-center gap-2",children:[s.jsx(Fe,{type:"time",className:"w-[96px] max-w-[96px] font-mono tabular-nums",value:f,disabled:t.saving,onChange:S=>p(S.currentTarget.value)}),s.jsx("span",{className:"text-subtle text-xs",children:r("北京时间 · 错过时点开机后会自动补签")})]})]}),s.jsxs("div",{className:"ck-set-line ck-set-line-top",children:[s.jsx("span",{className:"ck-set-k",children:r("签到范围")}),s.jsxs("span",{className:"ck-set-v",children:[s.jsxs("div",{className:"ck-mini-row",children:[s.jsx("span",{className:"ck-mini-note",children:r("到点时逐个签到,已勾 {n} / {total} 家",{n:h.size,total:a.length})}),s.jsx("button",{type:"button",className:"ck-mini-btn",disabled:t.saving,onClick:v,children:r("全选")}),s.jsx("button",{type:"button",className:"ck-mini-btn",disabled:t.saving||h.size===0,onClick:j,children:r("清空")})]}),s.jsx("div",{className:"ck-range",children:a.map(S=>s.jsxs("label",{className:"ck-range-item",children:[s.jsx(Pr,{checked:h.has(S.id),disabled:t.saving,onCheckedChange:N=>{const k=new Set(h);N===!0?k.add(S.id):k.delete(S.id),b(k)}}),s.jsx("span",{className:"ck-range-name",title:S.label,children:S.label})]},S.id))}),h.size===0?s.jsx("div",{className:"ck-mini-warn",children:r("至少勾一家才能保存(不勾等于这条条目不签到)")}):null]})]}),s.jsxs("div",{className:"ck-sched-actions",children:[s.jsx(ne,{size:"sm",variant:"outline",disabled:t.saving,onClick:()=>bw(null),children:r("取消")}),s.jsx(ne,{size:"sm",variant:"default",disabled:!y||t.saving,onClick:()=>{w()},children:t.saving?r("保存中…"):r("保存")})]})]})}function g9({store:e}){return s.jsxs("div",{className:"ck-sched-row open ck-sched-row-new",children:[s.jsxs("div",{className:"ck-sched-main",children:[s.jsx("span",{className:"ck-new-dot","aria-hidden":!0,children:"+"}),s.jsxs("div",{className:"ck-prov-info",children:[s.jsx("div",{className:"ck-prov-name",children:r("新建定时条目")}),s.jsx("div",{className:"ck-prov-desc",children:r("填好时刻与签到范围后点「保存」;名称可以留空(界面显示时刻)")})]})]}),s.jsx(vM,{entry:null,store:e})]})}function x9({entry:e,store:t}){const a=Array.isArray(t.data?.providerOptions)?t.data.providerOptions:[],o=t.editingId===e.id,c=t.runningId===e.id,d=t.data?.enabled===!0,f=Number(e.nextRunAt),p=async()=>{const h=Pe().wbConfirm?.ask;if(!h)return;const y=(e.displayLabel||e.time||e.id).replace(/&/g,"&").replace(//g,">");await h({title:r("删除定时条目"),html:r("确定删除「{name}」?删除后到点不再执行。",{name:y}),okText:r("删除"),okClass:"danger"})&&await i9(e.id)};return s.jsxs("div",{className:o?"ck-sched-row open":"ck-sched-row",children:[s.jsxs("div",{className:"ck-sched-main",children:[s.jsx(ns,{checked:e.enabled===!0,disabled:t.saving,onCheckedChange:h=>{o9(e,h===!0)}}),s.jsxs("div",{className:"ck-prov-info",children:[s.jsxs("div",{className:"ck-prov-name",children:[e.displayLabel||e.time||e.id,e.enabled===!0?null:s.jsx(ze,{variant:"outline",shape:"tag",children:r("已停用")})]}),s.jsx("div",{className:"ck-prov-desc ck-sched-scope",title:m9(e,a),children:r("每天 {time} · {scope}",{time:e.time??"—",scope:p9(e,a)})}),s.jsx("div",{className:"ck-prov-desc",children:r("上次执行:{line}",{line:h9(e)})})]}),s.jsxs("div",{className:"ck-prov-right",children:[s.jsx("span",{className:"ck-sched-next",children:d&&e.enabled===!0&&Number.isFinite(f)&&f>0?r("下次 {date}",{date:Ls(f)}):"—"}),s.jsx(ne,{size:"sm",variant:"ghost",disabled:c||t.runningId!==null,onClick:()=>{c9(e.id)},children:r(c?"执行中…":"立即执行")}),s.jsx(ne,{size:"sm",variant:"ghost",onClick:()=>bw(o?null:e.id),children:r(o?"收起":"编辑")}),s.jsx(ne,{size:"sm",variant:"ghost",className:"text-destructive",disabled:t.saving,onClick:()=>{p()},children:r("删除")})]})]}),o?s.jsx(vM,{entry:e,store:t}):null]})}function b9(e,t){return e.length===t.length&&e.every((a,o)=>a===t[o])}let Qf=null;function v9(e,t){for(let a=0;ah!==t.row),d=v9(c,e.clientY);if(d===t.from)return;const f=[...t.list],[p]=f.splice(t.from,1);f.splice(d,0,p),t.list=f,t.from=d,t.onChange(f)}function Zf(){const e=Qf;Qf=null,window.removeEventListener("pointermove",yM),window.removeEventListener("pointerup",Zf),window.removeEventListener("pointercancel",Zf),document.body.classList.remove("ck-chain-dragging"),e?.row.classList.remove("dragging")}function y9(e,t,a,o){if(e.button!==0||Qf||a.length<2)return;const c=e.currentTarget.closest(".ck-chain-row");c&&(e.preventDefault(),Qf={list:[...a],from:t,row:c,onChange:o},c.classList.add("dragging"),document.body.classList.add("ck-chain-dragging"),window.addEventListener("pointermove",yM),window.addEventListener("pointerup",Zf),window.addEventListener("pointercancel",Zf))}function j9({models:e,defaultModels:t,dirty:a,saving:o}){const[c,d]=x.useState(""),f=()=>{const p=c.trim();if(p){if(e.includes(p)){Le(r("清单里已经有「{name}」",{name:p}),"err");return}_g([...e,p]),d("")}};return s.jsxs("section",{className:"panel ck-chain-panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("保活模型链")}),s.jsx("span",{className:"panel-sub",children:r("WorkBuddy 国际版日活任务的免费模型 · 全局一份,与条目无关")}),s.jsx(ne,{size:"sm",variant:"default",className:"ml-auto",disabled:!a||o,onClick:()=>{Z7(e)},children:r(o?"保存中…":"保存")})]}),s.jsxs("div",{className:"p-4",children:[s.jsxs("div",{className:"ck-set-line ck-set-line-top",children:[s.jsx("span",{className:"ck-set-k",children:r("模型清单")}),s.jsxs("span",{className:"ck-set-v",children:[s.jsxs("div",{className:"ck-mini-row",children:[s.jsx("span",{className:"ck-mini-note",children:r("按序逐个尝试,共 {n} 个",{n:e.length})}),s.jsx("button",{type:"button",className:"ck-mini-btn",disabled:o||t.length===0,title:r("把缺省链填进清单,再点「保存」生效"),onClick:()=>_g([...t]),children:r("填入缺省链")})]}),s.jsxs("div",{className:"ck-chain",children:[e.map((p,h)=>s.jsxs("div",{className:"ck-chain-row",children:[s.jsx("span",{className:"ck-chain-grip",role:"button",tabIndex:-1,"aria-label":r("按住拖动调整顺序"),title:r("按住拖动,调整这个模型在链里的顺序"),onPointerDown:b=>y9(b,h,e,_g),dangerouslySetInnerHTML:{__html:Pe().wbIcons?.icon?.("gripVertical",14)||""}}),s.jsx("span",{className:"ck-chain-name",title:p,children:p}),s.jsxs("span",{className:"ck-chain-index",children:["#",h+1]}),s.jsx("button",{type:"button",className:"ck-chain-del",disabled:o,"aria-label":r("移除 {name}",{name:p}),title:r("从清单里移除"),onClick:()=>_g(e.filter((b,y)=>y!==h)),children:"×"})]},p)),e.length===0?s.jsx("div",{className:"ck-chain-empty",children:r("清单为空 —— 保存后恢复缺省链")}):null]}),s.jsxs("div",{className:"ck-chain-add",children:[s.jsx(Fe,{type:"text",className:"max-w-[260px] font-mono text-xs",placeholder:r("WorkBuddy 国际版保活模型,按顺序回退"),value:c,disabled:o,onChange:p=>d(p.currentTarget.value),onKeyDown:p=>{p.key==="Enter"&&(p.preventDefault(),f())}}),s.jsx(ne,{size:"sm",variant:"outline",disabled:o||c.trim()==="",onClick:f,children:r("添加模型")}),a?s.jsx("span",{className:"ck-chain-dirty",children:r("有未保存的改动")}):null]})]})]}),s.jsx("div",{className:"ck-note",children:r("「保活模型链」是 WorkBuddy 国际版领日活时用来保活的免费模型,按顺序逐个尝试、 第一个成功即止;清空清单并保存 = 恢复缺省链(当前缺省:{defaults})。 当天去重 + 幂等领取:重复执行只会拿到「已领取」,不会重复加分。",{defaults:t.join(r("、"))})})]})]})}function w9(){const e=d9(),t=f9();if(x.useEffect(()=>{gM()},[]),!e.loaded)return s.jsxs("div",{className:"ck-loading",children:[s.jsx(of,{className:"size-5"}),s.jsx("span",{children:r("正在读取定时签到数据…")})]});const a=e.data,o=a?.enabled===!0,c=Array.isArray(a?.schedules)?a.schedules:[],d=t.snapshot?.keepalive,f=Array.isArray(d?.models)?d.models:[],p=Array.isArray(d?.defaultModels)?d.defaultModels:[],h=t.keepaliveDraft,b=h??f,y=h!==null&&!b9(h,f);return s.jsxs("div",{children:[e.loadError?s.jsxs("div",{className:"ck-page-error",children:[s.jsx("span",{children:r("读取定时签到数据失败:{error}",{error:e.loadError})}),s.jsx(ne,{size:"sm",variant:"outline",onClick:()=>{gM()},children:r("重试")})]}):null,s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("定时签到")}),s.jsx("span",{className:"tip-q","data-tip":u9}),s.jsx("span",{className:"panel-sub",children:r("每条条目各有时刻与范围 · 各自当天去重 · 错过时点开机自动补签")}),s.jsx(ne,{size:"sm",variant:"outline",className:"ml-auto flex-none",disabled:e.saving,onClick:()=>bw(e.editingId==="new"?null:"new"),children:e.editingId==="new"?r("收起新建"):r("添加条目")})]}),s.jsx("div",{className:"p-4",children:s.jsxs("div",{className:"ck-set-line",children:[s.jsx("span",{className:"ck-set-k",children:r("总开关")}),s.jsxs("span",{className:"ck-set-v flex items-center gap-2",children:[s.jsx(ns,{checked:o,disabled:e.saving,onCheckedChange:v=>{l9(v===!0)}}),s.jsx("span",{className:"text-subtle text-xs",children:r(o?"开着:启用的条目按各自时刻执行":"关着:所有条目都不执行")})]})]})}),s.jsxs("div",{children:[e.editingId==="new"?s.jsx(g9,{store:e}):null,c.map(v=>s.jsx(x9,{entry:v,store:e},v.id)),c.length===0?s.jsx("div",{className:"ck-empty",children:r("还没有定时条目 —— 点「添加条目」建一条,或到「签到中心」手动签到。")}):null]})]}),s.jsx(j9,{models:b,defaultModels:p,dirty:y,saving:t.keepaliveSaving})]})}const Bg="checkin-records",jM=["today","7","30","month","all"],_u="all",S9=[{value:"today",label:r("今天")},{value:"7",label:r("7 天")},{value:"30",label:r("30 天")},{value:"month",label:r("本月")},{value:"all",label:r("全部")}],wM="workbuddy-desktop-checkin-records-range";function Hg(e){return new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime()}function SM(e){const t=new Date;switch(e){case"today":return Hg(t);case"7":return Hg(new Date(t.getFullYear(),t.getMonth(),t.getDate()-6));case"30":return Hg(new Date(t.getFullYear(),t.getMonth(),t.getDate()-29));case"month":return Hg(new Date(t.getFullYear(),t.getMonth(),1));default:return null}}function N9(){try{const e=localStorage.getItem(wM);return e!==null&&jM.includes(e)?e:_u}catch{return _u}}function NM(e){try{localStorage.setItem(wM,e)}catch{}}function kM(e){return typeof e=="number"&&e>0?e:xu}const vw={success:{label:"成功领取",variant:"success"},already:{label:"今日已领",variant:"brand"},pending:{label:"等待窗口",variant:"warning"},failed:{label:"失败",variant:"destructive"}},k9={schedule:"定时",startup:"补签",manual:"手动",single:"单签",activity:"活动"},yw=[{key:"time",label:r("时间")},{key:"account",label:r("账号")},{key:"provider",label:r("提供商")},{key:"source",label:r("来源")},{key:"outcome",label:r("结果")},{key:"detail",label:r("说明")}];let jw=null;function CM(){return jw?jw.apply([...yw]):yw.map(e=>({...e,align:e.align??"left"}))}function TM(){window.wbTableColumns?.repaint?.(Bg)}function EM(e){return(fr().snapshot?.auto?.providerOptions??[]).find(a=>a.id===e)?.label??e}function C9(e,t){const a=`ck-col-${t.key} ta-${t.align}`;switch(t.key){case"time":return s.jsxs("td",{className:a,children:[e.ts?Ls(e.ts):"—",s.jsx("span",{className:"ck-record-date",children:e.date||""})]},t.key);case"account":return s.jsx("td",{className:a,children:e.accountName||e.accountId||"—"},t.key);case"provider":return s.jsx("td",{className:a,children:e.provider?EM(e.provider):"—"},t.key);case"source":return s.jsxs("td",{className:a,children:[s.jsx("span",{className:"ck-record-source",children:k9[e.source??""]??e.source??"—"}),e.reason?s.jsx("span",{className:"ck-record-reason",children:e.reason}):null]},t.key);case"outcome":{const o=vw[e.outcome??""]??{label:e.outcome||"—",variant:"outline"};return s.jsxs("td",{className:a,children:[s.jsx(ze,{variant:o.variant,shape:"tag",children:o.label}),e.keptActive?s.jsx(ze,{variant:"outline",shape:"tag",className:"ml-1",children:r("保活")}):null]},t.key)}case"detail":return s.jsx("td",{className:e.error?`${a} ck-record-detail-fail`:a,children:e.error||e.message||"—"},t.key);default:return null}}function T9(){const[e,t]=x.useState([]),[a,o]=x.useState(0),[c,d]=x.useState(1),[f,p]=x.useState(()=>N9()),[h,b]=x.useState(()=>kM(pg(Bg,dg))),[y,v]=x.useState({outcome:"",provider:"",keyword:""}),[j,w]=x.useState(""),[S,N]=x.useState(!1),[k,A]=x.useState(""),[P,R]=x.useState(0),C=x.useRef(null),E=x.useRef(0),[,D]=x.useReducer(Q=>Q+1,0),I=fr().snapshot?.auto?.providerOptions??[],O=CM(),_=Math.max(1,Math.ceil(a/h)),z=Math.min(c,_),H=y.outcome!==""||y.provider!==""||y.keyword!==""||f!==_u,B=a?{start:(z-1)*h+1,end:Math.min(a,z*h)}:null,L=x.useCallback(async Q=>{const G=E.current+1;E.current=G,N(!0),A("");try{const Y=await Pe().workbuddyDesktop?.getCheckinRecords?.({limit:Q.size,offset:(Q.page-1)*Q.size,outcome:Q.filters.outcome,provider:Q.filters.provider,keyword:Q.filters.keyword,start:SM(Q.range)});if(G!==E.current)return;t(Array.isArray(Y?.records)?Y.records:[]),o(Number(Y?.total)||0)}catch(J){if(G!==E.current)return;A(J instanceof Error?J.message:String(J))}finally{G===E.current&&N(!1)}},[]);x.useEffect(()=>{L({page:c,size:h,range:f,filters:y})},[L,c,h,f,y]),x.useEffect(()=>{c>_&&d(_)},[c,_]),x.useLayoutEffect(()=>{const Q=Pe().wbColSettings;if(!Q?.register){const G=window.setTimeout(()=>R(J=>J+1),50);return()=>window.clearTimeout(G)}jw=Q.register({id:Bg,label:r("签到记录表"),columns:yw.map(G=>({key:G.key,label:G.label,align:G.align??"left"})),mount:()=>C.current,onChange:()=>{Ts.flushSync(()=>D()),TM()}}),D()},[P]),x.useLayoutEffect(()=>{TM()}),x.useEffect(()=>()=>{Pe().wbColSettings?.close()},[]);const F=Q=>{v(Q),d(1)},$=Q=>{const G=jM.includes(Q)?Q:_u;G!==f&&(NM(G),p(G),d(1))},X=Q=>{b(kM(Q)),mg(Bg,Q),d(1)},K=()=>{w(""),NM(_u),p(_u),F({outcome:"",provider:"",keyword:""})},q=async()=>{const Q=Pe().wbConfirm?.ask;if(!Q)return;const G=r(H?"确定清空当前筛选出的签到记录?清空后无法恢复。":"确定清空全部签到记录?清空后无法恢复。");if(await Q({title:r("清空签到记录"),html:G,okText:r("清空"),okClass:"danger"}))try{await Pe().workbuddyDesktop?.clearCheckinRecords?.(H?{outcome:y.outcome,provider:y.provider,keyword:y.keyword,start:SM(f)}:{all:"1"}),AM("签到记录已清空"),await L({page:1,size:h,range:f,filters:y}),d(1)}catch(J){AM(r("清空失败:{error}",{error:J instanceof Error?J.message:String(J)}),"err")}};return s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("签到记录")}),s.jsx("span",{className:"panel-sub",children:r("每执行一次签到 · 每个账号一条明细 · 结果与收益以后端落账为准")}),s.jsxs("div",{ref:C,className:"ml-auto flex items-center gap-2",children:[s.jsx(ne,{size:"sm",variant:"outline",disabled:S,onClick:()=>{L({page:c,size:h,range:f,filters:y})},children:r(S?"刷新中…":"刷新")}),s.jsx(ne,{size:"sm",variant:"ghost",className:"text-destructive",onClick:()=>{q()},children:r("清空")})]})]}),s.jsxs("div",{className:"ck-records-filters",children:[s.jsx(En,{options:S9,value:f,onValueChange:$,"aria-label":r("签到记录时间范围"),className:"shrink-0"}),s.jsxs(ut,{value:y.outcome,onValueChange:Q=>F({...y,outcome:Q??""}),children:[s.jsx(ft,{className:"w-[128px] shrink-0","aria-label":r("按结果筛选"),children:s.jsx(dt,{children:y.outcome?vw[y.outcome]?.label??y.outcome:r("全部结果")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:r("全部结果")}),Object.entries(vw).map(([Q,G])=>s.jsx($e,{value:Q,children:G.label},Q))]})]}),s.jsxs(ut,{value:y.provider,onValueChange:Q=>F({...y,provider:Q??""}),children:[s.jsx(ft,{className:"w-[148px] shrink-0","aria-label":r("按提供商筛选"),children:s.jsx(dt,{children:y.provider?EM(y.provider):r("全部提供商")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:r("全部提供商")}),I.map(Q=>s.jsx($e,{value:Q.id,children:Q.label},Q.id))]})]}),s.jsx("div",{className:"spacer"}),s.jsx(Fe,{type:"search",className:"ck-records-search w-[200px] basis-[200px] min-w-[150px] grow-0 shrink",placeholder:r("搜账号 / 收益 / 错误 / 触发原因"),value:j,onChange:Q=>w(Q.currentTarget.value),onBlur:()=>{j!==y.keyword&&F({...y,keyword:j.trim()})},onKeyDown:Q=>{Q.key==="Enter"&&Q.currentTarget.blur(),Q.key==="Escape"&&(w(y.keyword),Q.currentTarget.blur())}}),H?s.jsx(ne,{size:"sm",variant:"ghost",onClick:K,children:r("清除筛选")}):null,s.jsx("span",{className:"panel-sub",children:S?r("读取中…"):r("{n} 条",{n:a})})]}),k?s.jsxs("div",{className:"ck-page-error",children:[s.jsx("span",{children:r("读取签到记录失败:{error}",{error:k})}),s.jsx(ne,{size:"sm",variant:"outline",onClick:()=>{L({page:c,size:h,range:f,filters:y})},children:r("重试")})]}):null,e.length===0&&!S?s.jsx("div",{className:"ck-empty",children:r(H?"没有符合筛选条件的签到记录":"还没有签到记录 —— 手动签到或等定时条目执行后,明细会出现在这里。")}):s.jsx("div",{className:"ck-records-scroll",children:s.jsxs("table",{className:"ck-table ck-records-table",children:[s.jsx("colgroup",{children:O.map(Q=>s.jsx("col",{"data-col":Q.key,className:`ck-col-${Q.key}`},Q.key))}),s.jsx("thead",{children:s.jsx("tr",{children:O.map(Q=>s.jsx("th",{"data-col":Q.key,className:`ck-col-${Q.key} ta-${Q.align}`,children:Q.label},Q.key))})}),s.jsx("tbody",{children:e.map(Q=>s.jsx("tr",{children:O.map(G=>C9(Q,G))},Q.rowId))})]})}),s.jsx($i,{total:a,range:B,page:z,pageCount:_,size:h,sizes:dg,onSizeChange:X,onPageChange:d,disabled:S})]})}function AM(e,t){Pe().wbApp?.toast?.(e,t)}window.wbCheckinRecords={visibleColumns:()=>CM().map(e=>({key:e.key}))};const RM={workbuddy:r("腾讯每日签到接口 · 仅国内版"),"workbuddy-intl":r("每日活跃任务 · 网页会话保活点亮日活,领取才加积分 · 两者可分开执行"),raccoon:r("桌面端每日积分链路"),autoclaw:r("官方客户端的每日签到任务"),"autoclaw-intl":r("与国内版同一套任务接口 · 站点不同"),qoder:r("活动(campaign)领取 · 每天 10:00 刷新"),"qoder-intl":r("带设备风控身份领取「每日 100 Credits」 · 需要 UMID 组件"),trae:r("SOLO 的 checkin_credits 领取 · 按自然日 0 点刷新"),loomy:r("无独立签到接口 · 每天替账号打一次首次登录积分"),kuku:r("「免费领积分」的每日任务 · 逐个领取"),officeace:r("一次领当天全部奖励(每日签到 + 新人礼) · 幂等由上游保证"),codearts:r("每日福利 · 领取 → 确认 → 回读核实 · 按北京时间自然日重置"),chatglm:r("每日登录积分 · 顺带领取会员活动 · 访客账号无积分体系(跳过)"),atomcode:r("领取免费 CodingPlan(按需:套餐有效时只回读额度)"),lobsterai:r("每日活动签到(+100 积分,已领则中性提示)")},E9=[{value:"center",label:r("签到中心")},{value:"schedule",label:r("定时签到")},{value:"records",label:r("签到记录")}];function A9(e){return r(e==="raccoon"?"小浣熊":e==="loomy"?"Loomy":e==="codearts"?"CodeArts":e==="officeace"?"OfficeAce 果办":e)}function R9(){const[e,t]=x.useState(fr());return x.useEffect(()=>lM(()=>t(fr())),[]),e}function O9({group:e}){const t=n9(e);return t==="none"?s.jsx(ze,{variant:"outline",shape:"tag",children:r("无账号")}):t==="done"?s.jsx(ze,{variant:"success",shape:"tag",children:r("已签")}):t==="part"?s.jsx(ze,{variant:"warning",shape:"tag",children:r("部分")}):s.jsx(ze,{variant:"brand",shape:"tag",children:r("待签")})}function Wf({id:e,label:t}){const a=kg[e==="workbuddy-intl"?"workbuddy":e];return s.jsx("span",{className:"ck-prov-logo",children:a?s.jsx("img",{src:a,alt:""}):t.slice(0,1)||"·"})}function M9({group:e}){const t=fr().signing,a=(o,c)=>t.has(`${o}:${c}`);return s.jsxs("table",{className:"ck-table",children:[s.jsx("thead",{children:s.jsxs("tr",{children:[s.jsx("th",{children:r("账号")}),s.jsx("th",{children:r("今日签到")}),s.jsx("th",{children:r("上次签到时间")}),s.jsx("th",{"aria-label":r("操作")})]})}),s.jsx("tbody",{children:e.accounts.map(o=>{const c=e.id==="workbuddy-intl";return s.jsxs("tr",{children:[s.jsx("td",{children:o.name||o.id}),s.jsx("td",{children:o.checkedInToday?s.jsx(ze,{variant:"success",shape:"tag",children:r("已签")}):s.jsx(ze,{variant:"brand",shape:"tag",children:r("待签")})}),s.jsx("td",{className:"text-subtle",children:o.checkinAt?Ls(o.checkinAt):"—"}),s.jsx("td",{className:"text-right",children:c?s.jsxs("span",{className:"inline-flex items-center gap-1",children:[s.jsx(ne,{size:"sm",variant:"ghost",disabled:a(o.id,"keepalive"),title:r("用免费模型与网页会话维持账号活跃 · 不领取奖励"),onClick:()=>{Pg(o.id,"keepalive")},children:a(o.id,"keepalive")?r("保活中…"):r("保活")}),s.jsx(ne,{size:"sm",variant:"ghost",disabled:a(o.id,"claim"),title:r("探测活动并领取日活奖励 · 不做保活"),onClick:()=>{Pg(o.id,"claim")},children:a(o.id,"claim")?r("领取中…"):r("领取")}),s.jsx(ne,{size:"sm",variant:"ghost",disabled:a(o.id,"full"),title:r("探测 + 领取 + 保活 · 与定时签到同一套组合"),onClick:()=>{Pg(o.id,"full")},children:a(o.id,"full")?r("执行中…"):r("保活+领取")})]}):s.jsx(ne,{size:"sm",variant:"ghost",disabled:a(o.id,"checkin"),onClick:()=>{Pg(o.id)},children:a(o.id,"checkin")?r("签到中…"):o.checkedInToday?r("重签"):r("签到")})})]},o.id)})})]})}function D9({group:e,expanded:t}){const a=e.totalCount>0;return s.jsxs("div",{className:t?"ck-prov-row open":"ck-prov-row",children:[s.jsxs("div",{className:"ck-prov-main",onClick:()=>a&&oM(e.id),onKeyDown:o=>{a&&(o.key==="Enter"||o.key===" ")&&(o.preventDefault(),oM(e.id))},role:a?"button":void 0,tabIndex:a?0:-1,"aria-expanded":a?t:void 0,children:[s.jsx(Wf,{id:e.id,label:e.label}),s.jsxs("div",{className:"ck-prov-info",children:[s.jsx("div",{className:"ck-prov-name",children:e.label}),s.jsx("div",{className:"ck-prov-desc",children:RM[e.id]??""})]}),s.jsxs("div",{className:"ck-prov-right",children:[s.jsx("span",{className:"ck-prov-count",children:a?r("{total} 账号 · {done} 已签",{total:e.totalCount,done:e.doneCount}):"—"}),s.jsx(O9,{group:e}),a?s.jsx("span",{className:"ck-chev","aria-hidden":!0,children:"›"}):null]})]}),e.id==="qoder-intl"?s.jsx(OM,{}):null,t?s.jsx("div",{className:"ck-prov-detail",children:s.jsx(M9,{group:e})}):null]})}function OM(){const[e,t]=x.useState(null),a=x.useCallback(async()=>{try{t(await Lr().getQoderUmid?.()??null)}catch{}},[]);x.useEffect(()=>{a()},[a]);const o=e?.installing===!0&&e.available!==!0;if(x.useEffect(()=>{if(!o)return;const d=window.setInterval(()=>{a()},3e3);return()=>{window.clearInterval(d)}},[o,a]),!e||e.available)return null;const c=async()=>{try{t(d=>d&&{...d,installing:!0}),await Lr().installQoderUmid?.(),Pe().wbApp?.toast?.(r("✅ UMID 组件已安装,下轮签到即可领取国际版积分"))}catch(d){Pe().wbApp?.toast?.(r("UMID 组件安装失败:{error}",{error:d instanceof Error?d.message:String(d)}),"err")}finally{a()}};return s.jsxs("div",{className:"flex flex-wrap items-center gap-2 pb-2.5 pl-[58px] pr-4 text-[12px] text-muted-foreground",children:[s.jsx("span",{children:r("国际版签到需要 UMID 组件生成设备风控身份(本机未检测到)。")}),e.installSupported?s.jsx(ne,{variant:"outline",size:"sm",disabled:e.installing===!0,onClick:()=>{c()},children:e.installing?r("安装中…"):r("一键安装组件")}):s.jsx("span",{children:r("安装 Qoder 客户端或 qodercli 后即可(无需重启网关)。")})]})}function I9({row:e}){const t=e.provider??"loomy",a=fr().onboarding.get(e.id),o=a?.tasks??[],c=a?.unclaimed??0,d=a?.status==="loaded"&&o.length>0,f=d&&iM(e.id);return s.jsxs("div",{className:f?"ck-prov-row open":"ck-prov-row",children:[s.jsxs("div",{className:"ck-prov-main",onClick:d?()=>hw(e.id):void 0,onKeyDown:p=>{d&&(p.key==="Enter"||p.key===" ")&&(p.preventDefault(),hw(e.id))},role:d?"button":void 0,tabIndex:d?0:-1,"aria-expanded":d?f:void 0,children:[s.jsx(Wf,{id:t,label:A9(t)}),s.jsxs("div",{className:"ck-prov-info",children:[s.jsx("div",{className:"ck-prov-name",children:e.name||e.id}),s.jsxs("div",{className:"ck-prov-desc",children:[a?.status==="loaded"?r("已领 {claimed}/{total} · 累计 {earned} 积分",{claimed:o.length-c,total:o.length,earned:a.total?`${a.earned} / ${a.total}`:a.earned}):r("尚未查询任务状态"),a?.settled&&a.settledAt?` · ${r("结算于 {time}",{time:Ls(a.settledAt)})}`:a?.checkedAt?` · ${r("查询于 {time}",{time:Ls(a.checkedAt)})}`:""]})]}),s.jsxs("div",{className:"ck-prov-right",onClick:p=>p.stopPropagation(),children:[a?.status==="loaded"&&c>0?s.jsx(ze,{variant:"warning",shape:"tag",children:r("{n} 项待领",{n:c})}):a?.status==="loaded"?s.jsx(ze,{variant:"success",shape:"tag",children:r("全部领取")}):a?.status==="error"?s.jsx(ze,{variant:"destructive",shape:"tag",children:r("查询失败")}):null,s.jsx(ne,{size:"sm",variant:"outline",disabled:a?.status==="loading"||a?.claiming===!0,onClick:()=>{gw(e.id,{expand:!0,refresh:!0})},children:a?.status==="loading"?r("查询中…"):r("查询任务")}),s.jsx(ne,{size:"sm",variant:"outline",disabled:a?.status!=="loaded"||t!=="officeace"&&c===0||a?.claiming===!0,onClick:()=>{uM(e.id)},children:a?.claiming?r("领取中…"):c>0?r("一键领取({n})",{n:c}):r("领取")}),d?s.jsx("span",{className:"ck-chev","aria-hidden":!0,children:"›"}):null]})]}),a?.status==="error"?s.jsx("div",{className:"ck-fold-note",children:r("查询失败:{error}",{error:a.error??""})}):null,f?s.jsx("div",{className:"ck-prov-detail",children:o.map(p=>s.jsxs("div",{className:"ck-task-line",children:[s.jsx("span",{className:p.done?"ck-task-tick done":"ck-task-tick",children:p.done?"✓":"·"}),s.jsx("span",{children:p.title}),s.jsxs("span",{className:"ck-task-pts",children:["+",p.points]}),s.jsx("span",{className:"ck-task-st",children:p.claiming?s.jsx(ze,{variant:"warning",shape:"tag",children:r("领取中")}):p.done?s.jsx(ze,{variant:"success",shape:"tag",children:r("已领取")}):p.blocked?s.jsx(ze,{variant:"outline",shape:"tag",children:r("暂不可领")}):p.error?s.jsx(ze,{variant:"destructive",shape:"tag",children:p.error}):s.jsx(ze,{variant:"brand",shape:"tag",children:r("可领取")})})]},p.key))}):null]})}function P9({row:e}){const t=eg({claimPlans:e.claimPlans}),a=t.length>0;return s.jsxs("div",{className:"ck-welfare-row",children:[s.jsx(Wf,{id:"zcode",label:"ZCode"}),s.jsxs("div",{className:"ck-prov-info",children:[s.jsxs("div",{className:"ck-prov-name",children:[e.name||e.id,a?s.jsx(ze,{variant:"success",shape:"tag",title:r("本地台账里今天(北京时间)领过的套餐:{plans}",{plans:t.join(r("、"))}),children:r("今天已领 {n} 份",{n:t.length})}):s.jsx(ze,{variant:"outline",shape:"tag",title:r("本地台账里今天还没领过;活动期内每天发一份新套餐"),children:r("今天未领")})]}),s.jsx("div",{className:"ck-prov-desc",children:a?r("点「套餐明细」看领到了哪几份、什么时候生效;活动期内每天可再领"):e.claimAt?r("上次领取 {time} · 领取需通过滑块验证码",{time:Ls(e.claimAt)}):r("限时体验套餐(start-plan),活动期内每天一份 · 领取需滑块验证码")})]}),s.jsx("div",{className:"ck-prov-right",children:s.jsx(ne,{size:"sm",variant:"outline",onClick:()=>RR(e.id,e.name),children:r(a?"套餐明细":"去领取")})})]})}function _9({row:e}){const t=fr().growth.get(e.id),a=t?.tasks??[],o=a.filter(v=>v.supported&&!v.unforgeable),c=o.filter(v=>v.status!=="claimed"&&v.current0,f=d&&J7(e.id),p=t?.travel,h=t?.school,b=t?.results??[],y=v=>b.find(j=>j.code===v);return s.jsxs("div",{className:f?"ck-prov-row open":"ck-prov-row",children:[s.jsxs("div",{className:"ck-prov-main",onClick:d?()=>pM(e.id):void 0,onKeyDown:v=>{d&&(v.key==="Enter"||v.key===" ")&&(v.preventDefault(),pM(e.id))},role:d?"button":void 0,tabIndex:d?0:-1,"aria-expanded":d?f:void 0,children:[s.jsx(Wf,{id:"workbuddy",label:"WorkBuddy"}),s.jsxs("div",{className:"ck-prov-info",children:[s.jsx("div",{className:"ck-prov-name",children:e.name||e.id}),s.jsxs("div",{className:"ck-prov-desc",children:[t?.status==="loaded"?r("{total} 项任务 · {pending} 项可完成",{total:o.length,pending:c.length}):r("查询任务树并一键点亮 / 领奖(含开学季与猫猫旅行)"),t?.checkedAt?` · ${r("查询于 {time}",{time:Ls(t.checkedAt)})}`:""]})]}),s.jsxs("div",{className:"ck-prov-right",onClick:v=>v.stopPropagation(),children:[t?.status==="loaded"?c.length>0?s.jsx(ze,{variant:"warning",shape:"tag",children:r("{n} 项可完成",{n:c.length})}):s.jsx(ze,{variant:"success",shape:"tag",children:r("全部完成")}):t?.status==="error"?s.jsx(ze,{variant:"destructive",shape:"tag",children:r("查询失败")}):null,s.jsx(ne,{size:"sm",variant:"outline",disabled:t?.status==="loading"||t?.running===!0,onClick:()=>{Lg(e.id)},children:t?.status==="loading"?r("查询中…"):r("查询任务")}),s.jsx(ne,{size:"sm",variant:"outline",disabled:t?.status!=="loaded"||t?.running===!0,title:r("接单 + 点亮 + 领奖(只处理已实测沉淀的任务)"),onClick:()=>{mM(e.id,"full")},children:t?.running?r("执行中…"):r("一键完成")}),s.jsx(ne,{size:"sm",variant:"ghost",disabled:t?.status!=="loaded"||t?.running===!0,title:r("只领取已完成任务的奖励(幂等,重复点不会重复入账)"),onClick:()=>{mM(e.id,"claim")},children:r("领取")}),d?s.jsx("span",{className:"ck-chev","aria-hidden":!0,children:"›"}):null]})]}),t?.status==="error"?s.jsx("div",{className:"ck-fold-note",children:r("查询失败:{error}",{error:t.error??""})}):null,f?s.jsxs("div",{className:"ck-prov-detail",children:[s.jsxs("div",{className:"ck-fold-note",children:[t?.energy!==null&&t?.energy!==void 0?r("能量 {n}",{n:t.energy}):"",t?.streak!==null&&t?.streak!==void 0?` · ${r("连登 {n} 天",{n:t.streak})}`:"",t?.energy===null&&t?.streak===null?r("(能量 / 连登读数本次未取到)"):""]}),a.map(v=>{const j=y(v.code);return s.jsxs("div",{className:"ck-task-line",children:[s.jsx("span",{className:v.status==="claimed"?"ck-task-tick done":"ck-task-tick",children:v.status==="claimed"?"✓":"·"}),s.jsxs("span",{title:v.description||v.source||v.code,children:[v.title||v.code,v.target>1?` (${v.current}/${v.target})`:""]}),j?s.jsx("span",{className:"ck-task-pts",children:j.message}):null,s.jsx("span",{className:"ck-task-st",children:v.unforgeable?s.jsx(ze,{variant:"outline",shape:"tag",children:r("需真实操作")}):v.supported?v.status==="claimed"?s.jsx(ze,{variant:"success",shape:"tag",children:r("已领取")}):v.status==="completed"?s.jsx(ze,{variant:"brand",shape:"tag",children:r("可领取")}):v.locked?s.jsx(ze,{variant:"outline",shape:"tag",children:r("未解锁")}):s.jsx(ze,{variant:"warning",shape:"tag",children:r("可完成")}):s.jsx(ze,{variant:"outline",shape:"tag",children:r("暂不支持")})})]},v.code)}),s.jsxs("div",{className:"ck-task-line",children:[s.jsx("span",{className:"ck-task-tick",children:"🐾"}),s.jsxs("span",{children:[r("猫猫旅行"),p&&p.hasBuddy===!0?`:${p.buddyName||r("已领养")} · ${p.state==="traveling"?r("在途"):p.state==="arrived"?r("可领奖"):p.state==="idle"?p.dailyLimitReached?r("今日已派出"):r("可派出"):p.state??""}`:p&&p.hasBuddy===!1?`:${r("未领养(领养需先完成当日对话量)")}`:":"+(p?.error?String(p.error):r("尚未查询"))]}),s.jsx("span",{className:"ck-task-st",children:s.jsx(ne,{size:"sm",variant:"ghost",disabled:t?.runningTravel===!0||t?.status!=="loaded",onClick:()=>{t9(e.id)},children:t?.runningTravel?r("执行中…"):r("巡检一次")})})]}),s.jsxs("div",{className:"ck-task-line",children:[s.jsx("span",{className:"ck-task-tick",children:"🎓"}),s.jsxs("span",{children:[r("开学季活动"),h&&h.inPeriod===!0?`:${r("进行中")}${h.lottery?.balance!==void 0&&h.lottery?.balance!==null?` · ${r("抽奖机会 {n} 次",{n:h.lottery.balance})}`:""}`:h&&h.inPeriod===!1?`:${r("非活动期")}`:":"+(h?.error?String(h.error):r("尚未查询"))]}),s.jsx("span",{className:"ck-task-st",children:s.jsx(ne,{size:"sm",variant:"ghost",disabled:t?.runningSchool===!0||t?.status!=="loaded",title:r("执行开学季任务并抽空抽奖机会(分享 / 对话 / 专家事件)"),onClick:()=>{e9(e.id,!0)},children:t?.runningSchool?r("执行中…"):r("执行并抽奖")})})]}),t?.schoolText?s.jsx("div",{className:"ck-fold-note",children:t.schoolText}):null,t?.lotteryText?s.jsx("div",{className:"ck-fold-note",children:t.lotteryText}):null]}):null]})}function L9({group:e}){return s.jsxs("div",{className:"ck-prov-row",children:[s.jsxs("div",{className:"ck-prov-main",style:{cursor:"default"},children:[s.jsx(Wf,{id:e.id,label:e.label}),s.jsxs("div",{className:"ck-prov-info",children:[s.jsx("div",{className:"ck-prov-name",children:e.label}),s.jsx("div",{className:"ck-prov-desc",children:RM[e.id]??""})]}),s.jsx("div",{className:"ck-prov-right",children:s.jsx(ze,{variant:"outline",shape:"tag",children:r("无账号")})})]}),e.id==="qoder-intl"?s.jsx(OM,{}):null]})}function z9({store:e}){const t=e.snapshot,[a,o]=x.useState(null);x.useEffect(()=>{let N=!0;return(async()=>{try{const A=await Pe().workbuddyDesktop?.getCheckinRecords?.({limit:1});N&&o(Number(A?.total)||0)}catch{}})(),()=>{N=!1}},[e.version]);const c=t?.daily,d=t?.auto,f=(c?.outOfScope??[]).reduce((N,k)=>N+(Number(k.count)||0),0),p=t?.extras.onboarding??[],h=p.filter(N=>e.onboarding.get(N.id)?.status==="loaded"),b=p.reduce((N,k)=>N+(e.onboarding.get(k.id)?.unclaimed??0),0),y=t?.extras.plans??[],v=t?.extras.growth??[],j=Array.isArray(d?.schedules)?d.schedules:[],w=j.filter(N=>N.enabled===!0).length,S=s9(d);return s.jsxs("div",{children:[s.jsxs("div",{className:"ck-stats",children:[s.jsxs("div",{className:"ck-stat",children:[s.jsx("div",{className:"ck-stat-label",children:r("今日签到进度")}),s.jsx("div",{className:"ck-stat-value",children:c?s.jsxs(s.Fragment,{children:[" ",c.todayDone,s.jsxs("small",{children:[" / ",c.todayEligible]})]}):s.jsx("span",{className:"ck-pending",children:"—"})}),s.jsx(cf,{className:"ck-stat-bar",value:c&&c.todayEligible>0?c.todayDone/c.todayEligible*100:0}),s.jsx("div",{className:"ck-stat-foot",children:f>0?r("另有 {n} 个账号不参与每日签到",{n:f}):r("全部账号均可签到")})]}),s.jsxs("div",{className:"ck-stat ck-stat-link",onClick:()=>{Ig("schedule")},role:"button",tabIndex:0,onKeyDown:N=>{N.key==="Enter"&&Ig("schedule")},children:[s.jsx("div",{className:"ck-stat-label",children:r("定时签到")}),s.jsx("div",{className:"ck-stat-value",children:d?s.jsxs(s.Fragment,{children:[w,s.jsxs("small",{children:[" / ",j.length," ",r("条启用")]})]}):s.jsx("span",{className:"ck-pending",children:"—"})}),s.jsx("div",{className:"ck-stat-foot",children:d?.enabled!==!0?r("总开关已关闭 · 在「定时签到」页打开"):S?r("下次 {date} · 点此管理条目",{date:Ls(S)}):r("暂无启用的条目 · 点此添加")})]}),s.jsxs("div",{className:"ck-stat",children:[s.jsx("div",{className:"ck-stat-label",children:r("待领新手福利")}),s.jsx("div",{className:"ck-stat-value",children:p.length?s.jsxs(s.Fragment,{children:[b,s.jsxs("small",{children:[" ",r("项")]})]}):s.jsx("span",{className:"ck-pending",children:r("无")})}),s.jsx("div",{className:"ck-stat-foot",children:p.length?r("已有结果 {checked} / 共 {total} 个账号",{checked:h.length,total:p.length}):r("没有支持新手福利的账号")})]}),s.jsxs("div",{className:"ck-stat ck-stat-link",onClick:()=>{Ig("records")},role:"button",tabIndex:0,onKeyDown:N=>{N.key==="Enter"&&Ig("records")},children:[s.jsx("div",{className:"ck-stat-label",children:r("累计签到记录")}),s.jsx("div",{className:"ck-stat-value",children:a===null?s.jsx("span",{className:"ck-pending",children:"—"}):a}),s.jsx("div",{className:"ck-stat-foot",children:r("每账号一条明细 · 点此查看「签到记录」")})]})]}),s.jsxs("div",{className:"ck-main",children:[s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("每日签到")}),s.jsx("span",{className:"panel-sub",children:r("点击行展开账号明细 · 单账号签到不受范围限制")}),s.jsx(ne,{size:"sm",variant:"default",className:"ml-auto",disabled:e.runningAll||d?.running===!0,onClick:()=>{Q7()},children:e.runningAll||d?.running===!0?r("签到中…"):r("立即全部签到")})]}),s.jsxs("div",{children:[(c?.providers??[]).map(N=>N.totalCount>0?s.jsx(D9,{group:N,expanded:e.expanded.has(N.id)},N.id):s.jsx(L9,{group:N},N.id)),(c?.outOfScope??[]).length>0?s.jsx("div",{className:"ck-fold-note",children:r("不参与每日签到:{list}。CodeArts 的一次性奖励在下方「新手福利」,它的每日福利就在上方「每日签到」里;ZCode 的套餐领取见「活动福利」。",{list:c.outOfScope.map(N=>r("{label} ×{count}({reason})",{label:N.label,count:N.count,reason:N.reason})).join(r(";"))})}):null]})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("新手福利")}),s.jsx("span",{className:"panel-sub",children:r("一次性福利 · 签到后自动查询并领取,领完记在本机不再查上游 · 点击行展开任务清单")}),p.length>0?s.jsx(ne,{size:"sm",variant:"outline",className:"ml-auto",onClick:()=>{for(const N of p)N.settled&&typeof N.settled=="object"||gw(N.id)},children:r("全部查询")}):null]}),p.length?s.jsx("div",{children:p.map(N=>s.jsx(I9,{row:N},N.id))}):s.jsx("div",{className:"ck-empty",children:r("没有支持新手福利的账号 —— 目前有 Loomy、小浣熊、CodeArts 三家。")})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("活动福利")}),s.jsx("span",{className:"panel-sub",children:r("限时体验套餐 · 领取需滑块验证码 · 逐份查看与领取")})]}),y.length>0?s.jsx("div",{children:y.map(N=>s.jsx(P9,{row:N},N.id))}):s.jsx("div",{className:"ck-empty",children:r("没有可领套餐的账号(ZCode 未添加)。")})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("成长任务")}),s.jsx("span",{className:"panel-sub",children:r("WorkBuddy 国内版 · 查询任务树后一键点亮 / 领奖 · 含开学季与猫猫旅行 · 点击行展开明细")})]}),v.length>0?s.jsx("div",{children:v.map(N=>s.jsx(_9,{row:N},N.id))}):s.jsx("div",{className:"ck-empty",children:r("没有支持成长任务的账号(需要 WorkBuddy 国内版)。")})]})]})]})}function B9(){const e=R9(),t=e.snapshot;if(x.useEffect(()=>{Il()},[]),x.useEffect(()=>{window.wbApp?.renderTopbarStatus?.()},[e.version]),!e.loaded)return s.jsxs("div",{className:"ck-loading",children:[s.jsx(of,{className:"size-5"}),s.jsx("span",{children:r("正在读取签到数据…")})]});const a=t?.auto,o=a?.enabled===!0;return s.jsxs("div",{children:[e.loadError?s.jsxs("div",{className:"ck-page-error",children:[s.jsx("span",{children:r("读取签到数据失败:{error}",{error:e.loadError})}),s.jsx(ne,{size:"sm",variant:"outline",onClick:()=>{Il()},children:r("重试")})]}):null,s.jsxs("div",{className:"ck-tabs",children:[s.jsx(En,{options:E9,value:e.tab,onValueChange:c=>aM(c),"aria-label":r("签到中心分区")}),s.jsx(ze,{id:"checkin-badge",className:"ml-auto",variant:a?o?"success":"outline":"destructive","data-tone":a?o?"ok":"":"bad",children:a?o?a?.lastFiredToday?r("今日已执行"):r("已开启"):r("已关闭"):r("不可用")})]}),e.tab==="center"?s.jsx(z9,{store:e}):null,e.tab==="schedule"?s.jsx(w9,{}):null,e.tab==="records"?s.jsx(T9,{}):null]})}const MM='.page[data-page="checkin"]';let DM=!1;function IM(){if(DM)return;const e=document.querySelector(MM);e&&(DM=!0,e.replaceChildren(),wn.createRoot(e).render(s.jsx(B9,{})))}const PM=()=>Il();window.wbCheckinPanel={load:PM},document.querySelector(MM)?IM():document.addEventListener("DOMContentLoaded",IM,{once:!0}),Pe().wbApp?.currentPage==="checkin"&&PM();function H9(e){return String(e??"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}function U9({options:e,onSettle:t}){const{title:a="确认操作",html:o="",text:c="",okText:d="确定",okClass:f="primary",bodyClass:p="danger-zone",cancelText:h="取消"}=e,b=x.useRef(null);return x.useEffect(()=>{function y(v){v.key==="Escape"&&t(!1)}return document.addEventListener("keydown",y),()=>document.removeEventListener("keydown",y)},[t]),x.useEffect(()=>{function y(v){v.target?.dataset.slot==="alert-dialog-overlay"&&t(!1)}return document.addEventListener("click",y),()=>document.removeEventListener("click",y)},[t]),s.jsx(_V,{open:!0,onOpenChange:y=>{y||t(!1)},children:s.jsxs(HV,{initialFocus:b,showCloseButton:!0,children:[s.jsx(UV,{className:"pr-10",children:s.jsx(zV,{children:a})}),s.jsx(VV,{children:s.jsx($V,{tone:p==="danger-zone"?"danger":"neutral",dangerouslySetInnerHTML:{__html:o||H9(c).replace(/\n/g,"
    ")}})}),s.jsxs(FV,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{ref:b,variant:"outline",onClick:()=>t(!1),children:h}),s.jsx(ne,{variant:f==="danger"?"destructive":"default",onClick:()=>t(!0),children:d})]})]})})}let Jf=null,Lu=null,Ug=null;function V9(){Jf&&(Jf.unmount(),Jf=null),Lu&&(Lu.remove(),Lu=null)}function _M(e){const t=Ug;Ug=null,V9(),t?.(e)}function F9(e={}){return new Promise(t=>{Ug&&_M(!1),Ug=t,Lu=document.createElement("div"),document.body.append(Lu),Jf=wn.createRoot(Lu),Jf.render(s.jsx(U9,{options:e,onSettle:_M}))})}window.wbConfirm={ask:F9};const $9=r("Base URL 填进客户端的「API 地址 / Base URL」栏;对话协议按客户端支持的类型三选一(Chat Completions / Responses / Anthropic Messages),三者共用同一套模型与账号池,可随时切换;鉴权用「网关 Key」页里任一启用 Key。");function ep({id:e,name:t,note:a}){return s.jsxs("div",{className:"ep-row",children:[s.jsxs("div",{className:"ep-head",children:[s.jsx("span",{className:"ep-name",children:t}),s.jsx("span",{className:"ep-note",children:a})]}),s.jsxs("div",{className:"ep-line",children:[s.jsx("code",{className:"ep-value",id:e,children:"—"}),s.jsx(ne,{variant:"ghost",size:"icon-xs",className:"copy-btn size-[21px] rounded-[var(--r-xs)]","data-copy-from":e,title:r("复制地址"),children:"⧉"})]})]})}function q9(){return s.jsxs("section",{className:"panel ep-panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("接口地址")}),s.jsx("span",{className:"tip-q","data-tip":$9})]}),s.jsxs("div",{className:"ep-list",children:[s.jsx(ep,{id:"api-base",name:"Base URL",note:r("填进客户端的「API 地址 / Base URL」")}),s.jsx("div",{className:"ep-group",children:r("对话协议(按客户端支持的类型选一行)")}),s.jsx(ep,{id:"api-chat",name:"OpenAI Chat Completions",note:r("多数 OpenAI 兼容客户端的默认协议")}),s.jsx(ep,{id:"api-responses",name:"OpenAI Responses",note:r("OpenAI 新协议(Codex、新版 SDK)")}),s.jsx(ep,{id:"api-messages",name:"Anthropic Messages",note:r("Claude Code / Anthropic SDK(token 计数:/v1/messages/count_tokens)")}),s.jsx("div",{className:"ep-group",children:r("模型清单")}),s.jsx(ep,{id:"api-models",name:r("模型列表"),note:r("客户端自动拉取可用模型;不受鉴权限制,配 Key 之前就能拉")})]}),s.jsx("div",{className:"panel-foot",children:s.jsxs("span",{children:[r("鉴权用「网关 Key」页里任一"),s.jsx("b",{children:r("启用")}),r("的 Key:请求带 "),s.jsx("code",{children:"Authorization: Bearer "}),r(" 或 "),s.jsx("code",{children:"x-api-key: "}),r("。一把启用的 Key 都没有时不鉴权(默认网关只监听 127.0.0.1;在设置页开启「局域网访问」后监听所有网卡,鉴权与安全闸门随之启用)。")]})})]})}const LM='.page[data-page="docs"]';let zM=!1;function BM(){if(zM)return;const e=document.querySelector(LM);e&&(zM=!0,e.replaceChildren(),wn.createRoot(e).render(s.jsx(q9,{})))}document.querySelector(LM)?BM():document.addEventListener("DOMContentLoaded",BM,{once:!0});function da(){return window}function ww(e){return e instanceof Error?e.message:String(e)}function tp(e,t){da().wbApp?.toast?.(e,t)}function G9(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t]??t)}const Sw=[{key:"name",label:r("名称")},{key:"key",label:"Key"},{key:"time",label:r("创建时间")},{key:"state",label:r("启用")},{key:"act",label:r("操作"),align:"right"}],K9={name:"cell-name",key:"cell-key",time:"cell-time",state:"cell-state state",act:"cell-act r"},HM='.page[data-page="keys"]';let Vg=null,Nw=null;function UM(){return Vg?Vg.apply([...Sw]):Sw.map(e=>({...e,align:e.align??"left"}))}function VM(){da().wbColSettings?.syncStaticHead?.("keys",document.querySelector("table.keys-table"))}function Y9(){if(Vg)return;const e=da().wbColSettings?.register({id:"keys",label:r("网关 Key 表"),columns:Sw,mount:()=>document.querySelector('.page[data-page="keys"] .panel-head .head-actions'),onChange:()=>{VM(),Nw?.()}});e&&(Vg=e,VM())}function X9(e){return Array.isArray(e?.keys)?e.keys:[]}function Q9(e){return Array.isArray(e?.providers)?e.providers:[]}function Z9(e){const t=e?.modelsByProvider;return t&&typeof t=="object"?t:{}}function kw(e){return r(da().wbProviders?.labelOf?.(e)||e)}function W9(e,t,a){const o=new Map,c=f=>{const p=String(f??"").trim();if(!p)return;const h=p.toLowerCase();o.has(h)||o.set(h,p)};t.forEach(f=>{const p=e[f]??e[String(f).toLowerCase()];Array.isArray(p)&&p.forEach(c)}),a.forEach(c);const d=[...o.values()].map(f=>({value:f,label:r(f)}));return d.sort((f,p)=>f.value.toLowerCase()String(t)):[]}function $M(e,t){const a=new Map(t.map(c=>[c.value.toLowerCase(),c.value])),o=new Set;return e.forEach(c=>{const d=a.get(String(c).toLowerCase());d&&o.add(d)}),t.filter(c=>o.has(c.value)).map(c=>c.value)}function nK({target:e,providers:t,modelsByProvider:a,onClose:o,onSaved:c}){const d=e?.id??null,f=!!d,[p,h]=x.useState(e?.name??""),[b,y]=x.useState(""),[v,j]=x.useState(""),[w,S]=x.useState(!1),N=x.useRef(!1),[k,A]=x.useState(()=>FM(e?.allowedProviders)),[P,R]=x.useState(()=>FM(e?.allowedModels)),C=x.useMemo(()=>t.map(H=>({value:String(H.id),label:r(String(H.label??H.id))})),[t]),E=x.useMemo(()=>$M(k,C),[k,C]),D=x.useMemo(()=>W9(a,E,P),[a,E,P]),I=x.useMemo(()=>$M(P,D),[P,D]),O=D.length?r("没有匹配的选项"):E.length?r("这几家当前没有可用模型(账号未登录或清单为空)"):r("请先在上面选择可用提供商");function _(){N.current=!1,S(!1)}async function z(){if(!N.current){N.current=!0,S(!0),j(r("保存中…"));try{const H=da().workbuddyDesktop;if(!H)throw new Error(r("后端桥不可用"));if(d){const F=await H.updateKey(d,{allowedProviders:E,allowedModels:I});_(),c(F),o(),tp(r("✅ 可用范围已保存"));return}const B=b.trim();if(B&&B.length<8){j(r("Key 至少需要 8 个字符"));return}const L=await H.createKey({name:p.trim(),key:B||void 0,allowedProviders:E,allowedModels:I});_(),c(L,L?.created?.id),o(),tp(r("✅ Key 已创建,记得复制给客户端"))}catch(H){j(r("保存失败:{reason}",{reason:ww(H)}))}finally{_()}}}return s.jsx(Yt,{open:!0,onOpenChange:(H,B)=>{if(!H){if(N.current){B.cancel();return}o()}},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:f?r("可用范围 · {name}",{name:e?.name||r("未命名")}):r("新建 API Key")})}),s.jsxs(Lt,{children:[s.jsxs(dn,{children:[s.jsxs("div",{className:"flex flex-wrap items-center gap-2.5",children:[s.jsx(Je,{htmlFor:"key-name",className:"text-[12.5px] whitespace-nowrap text-subtle",children:r("名称")}),s.jsx(Fe,{id:"key-name",type:"text",maxLength:60,placeholder:r("例如 Cursor / 公司电脑"),autoComplete:"off",value:p,disabled:f,onChange:H=>h(H.currentTarget.value)})]}),!f&&s.jsxs("div",{className:"flex flex-wrap items-center gap-2.5",children:[s.jsx(Je,{htmlFor:"key-value",className:"text-[12.5px] whitespace-nowrap text-subtle",children:"Key"}),s.jsx(Fe,{id:"key-value",type:"text",placeholder:r("留空自动生成;手填至少 8 个字符"),autoComplete:"off",spellCheck:!1,value:b,onChange:H=>y(H.currentTarget.value),onKeyDown:H=>{H.key==="Enter"&&z()}})]}),s.jsxs("div",{className:"flex flex-wrap items-center gap-2.5",children:[s.jsx(Je,{className:"text-[12.5px] whitespace-nowrap text-subtle",children:r("可用提供商")}),s.jsx(Gh,{value:E,onValueChange:A,options:C,placeholder:r("留空 = 不限制"),searchPlaceholder:r("搜索提供商…"),"aria-label":r("可用提供商"),className:"flex-auto min-w-[180px] max-w-[260px]"})]}),s.jsxs("div",{className:"flex flex-wrap items-center gap-2.5",children:[s.jsx(Je,{className:"text-[12.5px] whitespace-nowrap text-subtle",children:r("可用模型")}),s.jsx(Gh,{value:I,onValueChange:R,options:D,placeholder:r("留空 = 不限制"),emptyHint:O,searchPlaceholder:r("搜索模型…"),"aria-label":r("可用模型"),className:"flex-auto min-w-[180px] max-w-[260px]"})]}),s.jsx("p",{children:r("留空表示不限制;同时设置时请求需同时满足两个条件(模型在白名单内且路由到允许的提供商)。 「可用模型」的候选跟着上面勾选的提供商走:没勾提供商时它是空的(还没有约束范围), 勾了几家就列出这几家能收的全部对外名。")}),s.jsx("p",{id:"key-restrict-summary",children:tK(E,I)})]}),s.jsx("div",{className:"min-h-[18px] text-[11.5px] text-muted-foreground",children:v})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:o,disabled:w,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:w,onClick:()=>{z()},children:r(f?"保存范围":"创建")})]})]})})}let Fg=null;async function sK(){await Fg?.load()}function rK(){Fg?.render()}function aK(){const[e,t]=x.useState(null),[a,o]=x.useState(()=>new Set),[c,d]=x.useState(()=>new Set),[f,p]=x.useState(null),[,h]=x.useState(0),b=x.useRef(new Set),y=x.useRef(!1),v=x.useCallback(z=>{t(z)},[]),j=x.useCallback(z=>{z&&Array.isArray(z.keys)&&v(z)},[v]),w=x.useCallback(async()=>{if(!y.current){y.current=!0;try{const z=da().workbuddyDesktop;if(!z)throw new Error(r("后端桥不可用"));v(await z.getKeys()??null)}catch(z){tp(r("读取 Key 列表失败:{reason}",{reason:ww(z)}),"err")}finally{y.current=!1}}},[v]);async function S(z,H,B){if(!b.current.has(z)){b.current.add(z),d(new Set(b.current));try{j(await H()),B&&tp(B)}catch(L){tp(r("操作失败:{reason}",{reason:ww(L)}),"err")}finally{b.current.delete(z),d(new Set(b.current))}}}function N(z){o(H=>{const B=new Set(H);return B.has(z)?B.delete(z):B.add(z),B})}async function k(z){const H=da().workbuddyDesktop,B=da().wbConfirm?.ask;if(!H||!B)return;const L=G9(z.name||z.masked||z.id);await B({title:r("删除网关 Key"),html:r("确定删除 Key「{name}」?使用它的客户端会立刻无法访问。",{name:L}),okText:r("删除"),okClass:"danger"})&&S(z.id,()=>H.deleteKey(z.id),r("Key 已删除"))}function A(z,H){const B=da().workbuddyDesktop;B&&S(z.id,()=>B.updateKey(z.id,{enabled:H}),r(H?"Key 已启用":"Key 已停用"))}x.useLayoutEffect(()=>(Fg={load:w,render:()=>h(z=>z+1)},Nw=()=>h(z=>z+1),Y9(),h(z=>z+1),()=>{Fg=null,Nw=null}),[w]),x.useEffect(()=>{da().wbApp?.currentPage==="keys"&&w()},[w]),x.useEffect(()=>{da().wbApp?.renderTopbarStatus?.()},[e]);const P=X9(e),R=hg(P.length,"keys"),C=R.paged?R.slice(P):P,E=UM(),D=e?.authRequired===!0,I=C.filter(z=>z.enabled).length,O=D?r("已启用鉴权 · {n} 把 Key 生效",{n:I}):r("未启用鉴权");function _(z,H,B){switch(z){case"name":return s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mid",children:s.jsx("span",{className:"t",children:H.name||r("未命名")})}),s.jsx("div",{className:"mname",title:eK(H),children:J9(H)})]});case"key":{const L=a.has(H.id);return s.jsxs("div",{className:"keycell",children:[s.jsx("code",{className:"kv",children:L?H.key:H.masked}),s.jsx(ne,{size:"sm",variant:"ghost",onClick:()=>N(H.id),children:r(L?"隐藏":"显示")}),s.jsx(ne,{size:"sm",variant:"ghost","data-copy":H.key,title:r("复制 Key"),children:r("复制")})]})}case"time":return s.jsx("span",{className:"muted",children:H.createdAt?da().wbApp?.formatTime?.(H.createdAt):"—"});case"state":return s.jsx(ns,{checked:H.enabled===!0,disabled:B,"aria-label":r("启用「{name}」",{name:H.name||r("未命名")}),onCheckedChange:L=>A(H,L)});case"act":return s.jsxs("div",{className:"row-actions",children:[s.jsx(ne,{size:"sm",variant:"ghost",disabled:B,onClick:()=>p({key:H}),children:r("可用范围")}),s.jsx(ne,{size:"sm",variant:"destructive",disabled:B,onClick:()=>{k(H)},children:r("删除")})]});default:return null}}return s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("API Key 列表")}),s.jsx(ze,{id:"keys-status",variant:D?"success":"warning","data-tone":D?"ok":"warn",children:O}),s.jsx("div",{className:"head-actions",children:s.jsx(ne,{variant:"default",onClick:()=>p({key:null}),children:r("+ 新建 Key")})})]}),s.jsx("div",{className:"models-table-wrap",children:s.jsxs("table",{className:"models-table keys-table",children:[s.jsxs("colgroup",{children:[s.jsx("col",{className:"k-name","data-col":"name"}),s.jsx("col",{className:"k-key","data-col":"key"}),s.jsx("col",{className:"k-time","data-col":"time"}),s.jsx("col",{className:"k-state","data-col":"state"}),s.jsx("col",{className:"k-act","data-col":"act"})]}),s.jsx("thead",{children:s.jsxs("tr",{children:[s.jsx("th",{"data-col":"name",children:r("名称")}),s.jsx("th",{"data-col":"key",children:"Key"}),s.jsx("th",{"data-col":"time",children:r("创建时间")}),s.jsx("th",{"data-col":"state",children:r("启用")}),s.jsx("th",{className:"r","data-col":"act",children:r("操作")})]})}),s.jsx("tbody",{children:C.length?C.map(z=>{const H=c.has(z.id);return s.jsx("tr",{className:z.enabled?"":"off","data-id":z.id,children:E.map(B=>s.jsx("td",{className:`${K9[B.key]??""} ta-${B.align}`,children:_(B.key,z,H)},B.key))},z.id)}):s.jsx("tr",{children:s.jsx("td",{colSpan:E.length,className:"empty",children:r(e?"还没有 Key,当前不鉴权":"加载中…")})})})]})}),s.jsx($i,{leading:s.jsxs("span",{children:[r("客户端请求需带 "),s.jsx("code",{children:"Authorization: Bearer "}),r(" 或 "),s.jsx("code",{children:"x-api-key: "}),r("; 修改后立即生效,本程序自身会自动使用第一把启用的 Key。每把 Key 可单独限制"),s.jsx("b",{children:r("可用提供商")}),r("与"),s.jsx("b",{children:r("可用模型")}),r("(行内「可用范围」):留空 = 不限制,两个都设时 按交集生效 —— 被限制的模型对这把 Key 表现为「不存在」(拉 /v1/models 也看不到它), 提供它的家不在可用列表里时请求同样被拒。")]}),total:P.length,range:R.paged?{start:R.rangeStart,end:R.rangeEnd}:null,page:R.page,pageCount:R.pageCount,size:R.size,onSizeChange:R.setSize,onPageChange:R.goto}),f?s.jsx(nK,{target:f.key,providers:Q9(e),modelsByProvider:Z9(e),onClose:()=>p(null),onSaved:(z,H)=>{H&&o(B=>new Set(B).add(H)),j(z)}}):null]})}let qM=!1;function GM(){if(qM)return;const e=document.querySelector(HM);e&&(qM=!0,e.replaceChildren(),wn.createRoot(e).render(s.jsx(aK,{})))}document.querySelector(HM)?GM():document.addEventListener("DOMContentLoaded",GM,{once:!0}),window.wbKeysPanel={load:sK,render:rK,visibleColumns:UM};const lK="login-app",oK=x.createElement("panel-captcha",{id:"captcha"});function iK(){return s.jsxs("div",{className:"w-[min(400px,100%)] rounded-lg border border-border bg-card p-[36px_34px_30px] shadow-3",children:[s.jsxs("div",{className:"mb-[26px] flex flex-col items-center gap-3 text-center",children:[s.jsxs("svg",{viewBox:"0 0 24 24",role:"img","aria-label":"Agent2API",className:"size-[52px] rounded-[12px] shadow-[0_6px_18px_rgba(0,122,255,0.35)]",children:[s.jsx("rect",{width:"24",height:"24",rx:"5.4",fill:"#007AFF"}),s.jsxs("g",{fill:"none",stroke:"#fff",strokeWidth:"2.2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("path",{d:"M5.23 9.24h10.71"}),s.jsx("path",{d:"M15.94 6.72 18.77 9.24 15.94 11.76"}),s.jsx("path",{d:"M18.77 14.76H8.06"}),s.jsx("path",{d:"M8.06 12.24 5.23 14.76 8.06 17.28"})]})]}),s.jsxs("div",{children:[s.jsx("h1",{className:"m-0 text-[21px] font-bold tracking-[0.2px]",children:"Agent2API"}),s.jsx("div",{className:"text-[12.5px] text-muted-foreground",children:r("OpenAI 兼容网关 · 管理面板")})]})]}),s.jsx("div",{id:"mode-tip",className:"mb-[18px] text-center text-[13px] text-muted-foreground"}),s.jsxs("form",{id:"form",autoComplete:"on",children:[s.jsx(Je,{htmlFor:"username",className:"mt-3.5 mb-[5px] text-[12.5px]",children:r("账号")}),s.jsx(Fe,{id:"username",autoComplete:"username",placeholder:r("管理员账号")}),s.jsx(Je,{htmlFor:"password",className:"mt-3.5 mb-[5px] text-[12.5px]",children:r("密码")}),s.jsx(Fe,{id:"password",type:"password",autoComplete:"new-password",placeholder:r("至少 8 位")}),oK,s.jsx(ne,{id:"submit",type:"submit",size:"lg",className:"w-full",children:r("继续")}),s.jsx("div",{id:"error",className:"mt-3 min-h-5 text-center text-[13px] whitespace-pre-wrap text-destructive"})]}),s.jsx("div",{className:"mt-[22px] text-center text-[12px] text-muted-foreground",children:r("登录后可在「网关 Key」页为 API 客户端创建密钥")})]})}function cK(){const e=document.getElementById(lK);e&&Ts.flushSync(()=>{wn.createRoot(e).render(s.jsx(iK,{}))})}cK();function Hr(){return window}function Cw(e){return e instanceof Error?e.message:String(e)}function $g(e,t){Hr().wbApp?.toast?.(e,t)}const KM=1e3,uK=fg,dK=500,YM="workbuddy-desktop-logs-range",Tw="workbuddy-desktop-logs-filters",fK=300,pK="~/.agent2api/logs.jsonl",Ew=["today","7","30","month","all"],Aw="all",mK={today:r("今天"),7:r("7 天"),30:r("30 天"),month:r("本月"),all:r("全部")},hK=Ew.map(e=>({value:e,label:mK[e]})),Rw=[{value:"",label:r("全部级别")},{value:"debug",label:r("debug 及以上")},{value:"info",label:r("info 及以上")},{value:"warn",label:r("warn 及以上")},{value:"error",label:r("仅 error")}],gK=r("全部级别"),XM=r("全部分类"),xK={debug:r("调试"),info:r("信息"),warn:r("警告"),error:r("错误")};let qg=KM,Gg=!0,Ow=!1,Mw=!1,Dw=!1,Iw=null,_o=null,Pw=null,Kg=null,_w=!1,Lw=!1;const zw={level:"",category:"",keyword:""};function bK(){const e=Hr().wbFilterMemory;if(!e)return{...zw};_w=!0;const t=e.load(Tw,{...zw});return{level:Rw.some(a=>a.value===t.level)?t.level:"",category:t.category,keyword:t.keyword}}function Yg(e){Hr().wbFilterMemory?.save(Tw,{level:e.level||"",category:e.category||"",keyword:e.keyword.trim()||""})}function vK(){try{const e=localStorage.getItem(YM);return e!==null&&Ew.includes(e)?e:Aw}catch{return Aw}}function yK(e){try{localStorage.setItem(YM,e)}catch{}}function Xg(e){return new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime()}function jK(e){const t=new Date;switch(e){case"today":return Xg(t);case"7":return Xg(new Date(t.getFullYear(),t.getMonth(),t.getDate()-6));case"30":return Xg(new Date(t.getFullYear(),t.getMonth(),t.getDate()-29));case"month":return Xg(new Date(t.getFullYear(),t.getMonth(),1));default:return null}}function wK(e){if(!e)return"—";const t=new Date(e),a=d=>String(d).padStart(2,"0"),o=`${a(t.getHours())}:${a(t.getMinutes())}:${a(t.getSeconds())}`,c=`${a(t.getMonth()+1)}-${a(t.getDate())}`;return t.getFullYear()===new Date().getFullYear()?`${c} ${o}`:`${t.getFullYear()}-${c} ${o}`}function SK(e){const t=e.data;if(!t)return"";const a=[];return(t.from||t.to)&&a.push([t.from,t.to].filter(Boolean).join(" → ")),t.model&&a.push(r("模型 {model}",{model:t.model})),t.resetAtText?a.push(r("{time} 恢复",{time:t.resetAtText})):t.status&&!t.from&&!t.to&&a.push(`HTTP ${t.status}`),a.length?a.join(" · "):""}function Bw(e){return Array.isArray(e?.entries)?e.entries:[]}function Qg(e,t,a){const o=Math.max(1,a==="all"?1:Math.ceil(t.length/a));return Math.min(Math.max(1,e),o)}function Hw(e,t){return Object.hasOwn(e,t)}function NK(e){const t={};if(e&&typeof e=="object")for(const[a,o]of Object.entries(e))typeof o=="string"&&(t[a]=o);return t}function QM(e,t){const a=new URLSearchParams,o=e.keyword.trim(),c=jK(t);return e.level&&a.set("level",e.level),e.category&&a.set("category",e.category),o&&a.set("keyword",o),c!==null&&a.set("start",String(c)),a}function kK(e,t){const a=QM(e,t);return a.set("limit",String(dK)),a.toString()}function CK(e,t){return QM(e,t).toString()}function TK(e){Iw=e||null,Hr().wbApp?.updateLogsBadge?.(Iw)}async function EK(){if(!Ow)try{const e=await Hr().workbuddyDesktop?.getScheduledTasks(),t=(e?.tasks||[]).find(a=>a.id==="logsAutoRefresh");Uw(t||null),Ow=Array.isArray(e?.tasks)}catch(e){console.warn("读取日志自动刷新间隔失败,按默认 10 秒:",Cw(e)),Uw(null)}}function Uw(e){Ow=!0;const t=Number(e?.interval);!!e&&typeof e=="object"&&Number.isFinite(t)&&t>0&&(e.unit==="seconds"||e.unit==="minutes")?(Gg=e.enabled!==!1,qg=e.unit==="minutes"?t*6e4:t*1e3):(Gg=!0,qg=KM),_o?.applyAutoConfig(qg,Gg)}async function AK(e={}){await _o?.load(e)}function RK(e){if(!_o){Kg={data:e??null};return}_o.render(e)}function OK(){return Iw}async function MK(e){if(!_o){Pw=e;return}await _o.showCategory(e)}const DK={entries:[],total:0,matched:0,file:pK,categories:{},loaded:!1};function Vw(e,t){return{entries:Bw(e),total:Number(e?.total)||0,matched:Number(e?.matched)||0,file:e?.file||t.file,categories:Object.keys(t.categories).length?t.categories:NK(e?.categories),loaded:!0}}function IK(e){return e.loaded?e.total?r("没有符合筛选条件的日志"):r("暂无日志"):r("正在加载日志…")}function PK(){const[e,t]=x.useState(DK),[a,o]=x.useState(bK),[c,d]=x.useState(vK),[f,p]=x.useState(1),[h,b]=x.useState(()=>pg("logs")),[y,v]=x.useState(null),[j,w]=x.useState(()=>({ms:qg,enabled:Gg})),S=x.useRef(null),N=x.useRef(null),k=x.useRef(null),A=x.useRef(null),P=x.useRef(a),R=x.useRef(c),C=x.useRef(f),E=x.useRef(h),D=x.useRef(e),I=x.useCallback(le=>{const de={...P.current,...le};return P.current=de,o(de),de},[]),O=x.useCallback(le=>{R.current=le,d(le)},[]),_=x.useCallback(le=>{C.current=le,p(le)},[]),z=x.useCallback(le=>{E.current=le,b(le),mg("logs",le)},[]),H=x.useCallback(le=>{D.current=le,t(le)},[]),B=x.useCallback(()=>{if(_w)return;const le=Hr().wbFilterMemory;if(!le||(_w=!0,Lw))return;const de=le.load(Tw,{...zw});I({level:de.level,category:de.category,keyword:de.keyword})},[I]),L=x.useCallback(async(le={})=>{const{silent:de=!1,resetPage:Se=!1}=le;Se&&_(1),EK(),B();const Te=kK(P.current,R.current),_e=C.current;try{const Ae=Hr().workbuddyDesktop;if(!Ae)throw new Error(r("后端桥不可用"));const[oe,me]=await Promise.all([Ae.getLogs(Te),Ae.getLogStats()]),pe=Se?0:S.current?.scrollTop??0,Ce=Vw(oe,D.current);H(Ce),_(Qg(C.current,Bw(oe),E.current)),N.current=C.current===_e?pe:0,TK(me),Object.keys(Ce.categories).length&&P.current.category&&!Hw(Ce.categories,P.current.category)&&(Yg(I({category:""})),L({resetPage:!0}))}catch(Ae){de||(console.warn("读取运行日志失败:",Cw(Ae)),H(Vw(null,D.current)),_(Qg(C.current,[],E.current)))}},[H,_,B]),F=x.useCallback(le=>{le!==void 0&&(H(Vw(le,D.current)),_(Qg(C.current,Bw(le),E.current)))},[H,_]),$=x.useCallback(async le=>{if(Hw(D.current.categories,le)||await L({silent:!0}),!Hw(D.current.categories,le))return;const de=I({category:le});Yg(de),_(1),await L({resetPage:!0}),Hr().wbApp?.showPage?.("logs",{persist:!0})},[I,_,L]);x.useEffect(()=>{if(_o={load:L,render:F,showCategory:$,applyAutoConfig:(de,Se)=>w({ms:de,enabled:Se})},Kg){const de=Kg;Kg=null,F(de.data)}const le=Pw;return le!==null?(Pw=null,$(le)):L({silent:!0}),()=>{_o=null,k.current!==null&&window.clearTimeout(k.current),k.current=null}},[L,F,$]),x.useEffect(()=>{if(!j.enabled||j.ms<=0)return;const le=window.setInterval(()=>{document.hidden||Hr().wbApp?.currentPage!=="logs"||Mw||(Mw=!0,L({silent:!0}).finally(()=>{Mw=!1}))},j.ms);return()=>{window.clearInterval(le)}},[j,L]),x.useLayoutEffect(()=>{const le=N.current;if(le===null)return;N.current=null;const de=S.current;de&&(de.scrollTop=le)});async function X(le,de,Se){if(!Dw){Dw=!0,v({which:le,label:de});try{await Se()}catch(Te){$g(r("操作失败:{error}",{error:Cw(Te)}),"err")}finally{Dw=!1,v(null)}}}async function K(){const le=CK(P.current,R.current),de=le.length>0,Se=de?r("确定清空当前筛选出的 {count} 条日志?清空后无法恢复。",{count:Number(D.current.matched)||0}):r("确定清空全部运行日志?清空后无法恢复。"),Te=Hr().wbConfirm?.ask;Te&&await Te({title:r("清空运行日志"),html:Se,okText:r("清空"),okClass:"danger"})&&await X("clear",r("清空中…"),async()=>{const _e=Hr().workbuddyDesktop;if(!_e)throw new Error(r("后端桥不可用"));await _e.clearLogs(de?le:"all=1"),await L({resetPage:!0}),$g(r("运行日志已清空"))})}async function q(){await X("export",r("导出中…"),async()=>{const le=Hr().workbuddyDesktop;if(!le)throw new Error(r("后端桥不可用"));const de=await le.exportLogs();if(!(!de||de.canceled)){if(de.count===0){$g(r("暂无日志可导出"),"err");return}$g(r("✅ 已导出 {count} 条日志到 {file}",{count:String(de.count),file:String(de.file)}))}})}function Q(le){const de=Qg(le,D.current.entries,E.current);de!==C.current&&(_(de),N.current=0)}function G(le){if(le===E.current)return;const de=D.current.entries.length,Se=de?(C.current-1)*(E.current==="all"?de:E.current):0;z(le),_(le==="all"?1:Math.floor(Se/le)+1),N.current=0}function J(le){Lw=!0,Yg(I(le)),L({resetPage:!0})}function Y(le){Lw=!0,I({keyword:le}),k.current!==null&&window.clearTimeout(k.current),k.current=window.setTimeout(()=>{k.current=null,Yg(P.current),L({resetPage:!0})},fK)}function re(le){const de=Ew.includes(le)?le:Aw;de!==R.current&&(O(de),yK(de),L({resetPage:!0}))}const M=e.entries,U=h!=="all",Z=Math.max(1,U?Math.ceil(M.length/h):1),ae=Math.min(Math.max(1,f),Z),ce=U?M.slice((ae-1)*h,ae*h):M,W=M.length?U?(ae-1)*h+1:1:0,ue=M.length?U?W+ce.length-1:M.length:0,ve=e.categories,ee=e.matched===e.total?r("{n} 条",{n:e.total}):r("{matched} / {total} 条",{matched:e.matched,total:e.total});x.useEffect(()=>{const le=e.loaded?ee:"—";A.current!==le&&(A.current=le,Hr().wbApp?.renderTopbarStatus?.())},[e.loaded,ee]);function he(le,de){const Se=le.level||"",Te=SK(le);return s.jsxs("div",{className:`log-row ${Se}`,children:[s.jsx("span",{className:"log-rail",children:s.jsx("span",{className:`log-dot ${Se}`})}),s.jsxs("div",{className:"log-main",children:[s.jsxs("div",{className:"log-line",children:[s.jsx("span",{className:"log-cat",children:ve[le.category||""]||le.category}),s.jsx("span",{className:`log-lvl ${Se}`,children:xK[Se]||le.level}),s.jsx("span",{className:"msg",children:le.message})]}),Te?s.jsx("div",{className:"log-extra-wrap",children:s.jsx("span",{className:"extra",children:Te})}):null]}),s.jsx("span",{className:"time",children:wK(le.ts)})]},le.id??de)}const te=y?.which==="export",ke=y?.which==="clear";return s.jsxs("section",{className:"panel",id:"logs-panel-events",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("系统事件")}),s.jsx(ze,{id:"logs-badge",variant:"outline","data-tone":"",children:e.loaded?ee:"—"}),s.jsxs("div",{className:"head-actions",children:[s.jsx(ne,{id:"btn-logs-export",variant:"outline",disabled:y!==null,onClick:()=>{q()},children:te?y?.label:r("导出")}),s.jsx(ne,{id:"btn-logs-clear",variant:"destructive",disabled:y!==null,onClick:()=>{K()},children:ke?y?.label:r("清空")})]})]}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"log-filters",children:[s.jsx(En,{options:hK,value:c,onValueChange:re,"aria-label":r("事件日志时间范围"),className:"shrink-0"}),s.jsxs(ut,{value:a.level,onValueChange:le=>J({level:String(le??"")}),children:[s.jsx(ft,{id:"logs-level",className:"min-w-[120px]",title:r("按最低级别筛选"),"aria-label":r("按最低级别筛选"),children:s.jsx(dt,{children:Rw.find(le=>le.value===a.level)?.label??gK})}),s.jsx(pt,{children:Rw.map(le=>s.jsx($e,{value:le.value,children:le.label},le.value))})]}),s.jsxs(ut,{value:a.category,onValueChange:le=>J({category:String(le??"")}),children:[s.jsx(ft,{id:"logs-category",className:"min-w-[120px]",title:r("按分类筛选"),"aria-label":r("按分类筛选"),children:s.jsx(dt,{children:a.category?ve[a.category]||a.category:XM})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:XM}),Object.entries(ve).map(([le,de])=>s.jsx($e,{value:le,children:de},le))]})]}),s.jsxs(Di,{className:"w-auto flex-auto",children:[s.jsx(Pi,{id:"logs-keyword",type:"search",placeholder:r("搜索消息关键词…"),autoComplete:"off","aria-label":r("搜索消息关键词"),value:a.keyword,onChange:le=>Y(le.currentTarget.value)}),s.jsx(Ii,{"aria-hidden":"true",children:"⌕"})]})]}),s.jsx("div",{className:"log-list",id:"log-list",ref:S,children:ce.length?ce.map((le,de)=>he(le,de)):s.jsx("div",{className:"log-empty",children:IK(e)})})]}),s.jsx($i,{total:M.length,range:U?{start:W,end:ue}:null,page:ae,pageCount:Z,size:h,sizes:uK,onSizeChange:G,onPageChange:Q})]})}const ZM='.page[data-page="logs"]';let WM=!1;function JM(){if(WM)return;const e=document.querySelector(ZM);e&&(WM=!0,e.replaceChildren(),wn.createRoot(e).render(s.jsx(PK,{})))}document.querySelector(ZM)?JM():document.addEventListener("DOMContentLoaded",JM,{once:!0}),window.wbLogsPanel={load:AK,render:RK,lastStats:OK,applyAutoRefresh:Uw,showCategory:MK};const zu=["maxInputTokens","maxOutputTokens","supportsToolCall","supportsImages","supportsVideo","supportsReasoning"],np=["maxInputTokens","maxOutputTokens"],e2=["supportsToolCall","supportsImages","supportsVideo","supportsReasoning"],Bu={maxInputTokens:r("上下文窗口"),maxOutputTokens:r("最大输出 Token"),supportsToolCall:r("工具调用"),supportsImages:r("图片识别"),supportsVideo:r("视频识别"),supportsReasoning:r("支持思考")};r("工具"),r("图片"),r("视频"),r("思考");const _K=1e8;function Fw(e){return np.includes(e)}function $w(e,t){if(Fw(e)){const a=typeof t=="number"?t:Number(t);return!Number.isFinite(a)||a<1||a>_K?null:Math.trunc(a)}return typeof t=="boolean"?t:null}function t2(e){const t={};if(!e||typeof e!="object")return t;const a=e;for(const o of zu){const c=$w(o,a[o]);c!==null&&(t[o]=c)}return t}function qw(e){return Array.isArray(e)?zu.filter(t=>e.some(a=>a===t)):[]}function Gw(e){return t2(e?.capabilities)}function n2(e){const t=Number(e);if(!Number.isFinite(t)||t<=0)return"—";const a=o=>o.toFixed(1).replace(/\.0$/,"");return t>=1e6?`${a(t/1e6)}M`:t>=1e3?`${a(t/1e3)}K`:String(Math.round(t))}function sp(e){const t=Number(e);return!Number.isFinite(t)||t<=0?r("未声明"):Math.round(t).toLocaleString("en-US")}function Kw(e){return e===!0?"on":e===!1?"off":"unset"}function LK(e){const t=e?.reasoningLevels;if(!Array.isArray(t))return null;const a=t.filter(c=>typeof c=="string"&&c.trim()!=="");if(!a.length)return null;const o=e?.reasoningDefaultLevel;return{levels:a,defaultLevel:typeof o=="string"?o.trim():""}}function zK(e,t,a,o=""){const c=Bu[e],d=a?r("(已被手动覆盖,弹窗里可恢复继承)"):"",f=BK(o);if(Fw(e))return t==null?r("{label}:上游未声明{suffix}",{label:c,suffix:d}):r("{label}:{value}(对下游声明的精确值){origin}{suffix}",{label:c,value:sp(t),origin:f,suffix:d});const p=Kw(t);return p==="on"?r("{label}:支持{origin}{suffix}",{label:c,origin:f,suffix:d}):p==="off"?r("{label}:不支持{origin}{suffix}",{label:c,origin:f,suffix:d}):r("{label}:上游未声明(下游会按不支持处理){suffix}",{label:c,suffix:d})}function Hu(e,t){const a=e?.capSources?.[t];return a==="upstream"||a==="modelsdev"||a==="user"?a:""}function BK(e){return e==="modelsdev"?r("(由 models.dev 补齐)"):e==="user"?r("(手动设定)"):""}function HK(e){const t={};for(const a of zu){const o=e[a];Fw(a)?t[a]=$w(a,o.trim()):t[a]=o==="on"?!0:o==="off"?!1:null}return t}function rp(){return window}const s2=e=>String(e??"").trim().toLowerCase(),Ks=(e,t)=>s2(e)===s2(t);function as(e){return typeof e=="string"&&e.startsWith("custom-")}function Wi(){return(rp().wbProviders?.customList?.()||[]).filter(e=>as(e?.id))}function r2(){return rp().wbProviders?.customList?.()||[]}function Lo(e){return Wi().find(t=>t.id===e)||null}function UK(e){const t=t2(e),a={};for(const o of zu){const c=t[o];(typeof c=="number"||typeof c=="boolean")&&(a[o]=c)}return Object.keys(a).length?a:void 0}function a2(e){const t=(Array.isArray(e?.models)?e.models:[]).map(c=>{const d={id:String(c?.id??"").trim(),enabled:c?.enabled!==!1,reasoning:typeof c?.reasoning=="string"?c.reasoning:""};return d.capabilities=UK(c?.capabilities),d}).filter(c=>c.id),a=(Array.isArray(e?.mappings)?e.mappings:[]).map(c=>({alias:String(c?.alias??"").trim(),target:String(c?.target??"").trim(),enabled:c?.enabled!==!1,reasoning:typeof c?.reasoning=="string"?c.reasoning:""})).filter(c=>c.alias&&c.target);for(const c of t){const d=a.find(f=>Ks(f.alias,c.id)&&Ks(f.target,c.id));d&&(c.enabled=c.enabled&&d.enabled,c.reasoning=d.reasoning||c.reasoning)}const o=a.filter(c=>!(Ks(c.alias,c.target)&&t.some(d=>Ks(d.id,c.target))));return{models:t,mappings:o}}function l2(e){const t=Lo(e);if(!t)return null;const a=a2(t),o=t.name||e,c=[],d=[];for(const f of a.models)c.push({id:f.id,name:"",provider:e,providerLabel:o,source:"",enabled:f.enabled,aliases:[],capabilities:f.capabilities||{},capOverrides:[]}),d.push({alias:f.id,target:f.id,provider:e,enabled:f.enabled,reasoning:f.reasoning,isDefault:!0,dangling:!1,carried:!0});for(const f of a.mappings){const p=c.find(h=>Ks(h.id,f.target));d.push({alias:f.alias,target:f.target,provider:e,enabled:f.enabled,reasoning:f.reasoning,isDefault:!1,dangling:!p,carried:!!p}),p&&p.aliases.push(f.alias)}return{models:c,mappings:d}}async function Ji(e,t){const a=rp().wbProviders;if(!a?.customRequest)throw new Error(r("目录模块未就绪"));const o=Lo(e);if(!o)throw new Error(r("该自定义提供商已不存在(可能已被删除),请刷新后重试"));const c=a2(o),d=t(c);return await a.customRequest("POST","/api/custom-providers/models",{providerId:e,models:c.models,mappings:c.mappings}),await a.refreshCustom?.(),d}async function o2(e,t,a,o={}){const{enabled:c,reasoning:d}=o;await Ji(e,f=>{if(Ks(t,a)){const h=f.models.find(b=>Ks(b.id,a));if(!h)throw new Error(r("该提供商的清单里没有模型「{name}」",{name:a}));return c!==void 0&&(h.enabled=!!c),d!==void 0&&(h.reasoning=String(d??"")),null}const p=f.mappings.find(h=>Ks(h.alias,t)&&Ks(h.target,a));if(p)return c!==void 0&&(p.enabled=!!c),d!==void 0&&(p.reasoning=String(d??"")),null;if(c===!1)throw new Error(r("映射「{alias} → {target}」不存在",{alias:t,target:a}));return f.mappings.push({alias:t,target:a,enabled:c??!0,reasoning:String(d??"")}),null})}async function i2(e,t,a){await Ji(e,o=>{const c=o.mappings.length;if(o.mappings=o.mappings.filter(d=>!(Ks(d.alias,t)&&Ks(d.target,a))),o.mappings.length===c)throw new Error(r("映射「{alias} → {target}」不存在",{alias:t,target:a}));return null})}async function c2(e,t){const a=String(t??"").trim();if(!a)throw new Error(r("请填写模型 ID"));await Ji(e,o=>{if(o.models.some(c=>Ks(c.id,a)))throw new Error(r("模型「{name}」已存在(忽略大小写判重)",{name:a}));return o.models.push({id:a,enabled:!0,reasoning:""}),null})}async function VK(e,t){const a=(Array.isArray(t)?t:[]).map(o=>String(o??"").trim()).filter(Boolean);if(!a.length)throw new Error(r("没有选中任何模型"));return Ji(e,o=>{let c=0;for(const d of a)o.models.some(f=>Ks(f.id,d))||(o.models.push({id:d,enabled:!0,reasoning:""}),c++);return c})}async function u2(e,t,a){await Ji(e,o=>{const c=o.models.find(f=>Ks(f.id,t));if(!c)throw new Error(r("该提供商的清单里没有模型「{name}」",{name:t}));const d={...c.capabilities||{}};for(const[f,p]of Object.entries(a))p==null?delete d[f]:d[f]=p;return c.capabilities=Object.keys(d).length?d:void 0,null})}async function d2(e,t){return Ji(e,a=>{const o=a.mappings.length;a.mappings=a.mappings.filter(d=>!Ks(d.target,t));const c=o-a.mappings.length;return a.models=a.models.filter(d=>!Ks(d.id,t)),{removedMappings:c}})}async function FK(e){const t=rp().wbProviders;if(!t?.customRequest)throw new Error(r("目录模块未就绪"));const a=await t.customRequest("POST","/api/custom-providers/fetch-models",{providerId:e}),o=(Array.isArray(a?.models)?a.models:[]).map(d=>String(d??"").trim()).filter(Boolean),c=await Ji(e,d=>{let f=0;for(const p of o)d.models.some(h=>Ks(h.id,p))||(d.models.push({id:p,enabled:!0,reasoning:""}),f++);return f});return{total:o.length,added:Number(c)||0}}rp().wbModelsCustom={isCustom:as,list:Wi,listSource:r2,record:Lo,buildView:l2,setBinding:o2,removeMapping:i2,addModel:c2,addModels:VK,removeModel:d2,setCapabilities:u2,fetchModels:FK};const fa="__custom__",$K=["off","none","minimal","low","medium","high","xhigh","max"];function ap(e,t){const a=f2(t);if(a.length)return a;const o=e?.reasoningLevels;return Array.isArray(o)&&o.length?o.filter(c=>typeof c=="string"):[...$K]}function f2(e){const t=e?.reasoningLevels;return Array.isArray(t)?t.filter(a=>typeof a=="string"&&a.trim()!==""):[]}function qK(e,t){const a=t.map(f2).filter(f=>f.length>0);if(!a.length||a.length!==t.length)return ap(e);const[o,...c]=a,d=o.filter(f=>c.every(p=>p.includes(f)));return d.length?d:ap(e)}function p2(e,t,a){return`${String(e??"").trim().toLowerCase()}${String(t??"").trim().toLowerCase()}${String(a??"").trim().toLowerCase()}`}function GK(e){const t=new Map;for(const a of Array.isArray(e)?e:[]){const o=typeof a?.reasoning=="string"?a.reasoning.trim():"";o&&t.set(p2(a.alias,a.target,a.provider),o)}return(a,o,c)=>t.get(p2(a,o,c))||""}function Wt(){return window}function zo(e){return e instanceof Error?e.message:String(e)}function Nr(e,t){Wt().wbApp?.toast?.(e,t)}function Yw(e){const t=Wt().wbApp?.esc;return t?t(e):String(e??"").replace(/[&<>"']/g,a=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[a]||a)}function KK(e){const t=Wt().formatTime??Wt().wbApp?.formatTime;return String(t?.(e)??"")}const m2=e=>String(e??"").trim().toLowerCase(),Bo=(e,t)=>m2(e)===m2(t),h2=8,g2="workbuddy-desktop-models-filters",Xw=["all","enabled","disabled","mapped"],YK={all:r("全部"),enabled:r("已启用"),disabled:r("已禁用"),mapped:r("有映射")},XK=Xw.map(e=>({value:e,label:YK[e]})),Qw=["default","rate-asc","rate-desc"],QK={default:r("默认"),"rate-asc":r("倍率升序"),"rate-desc":r("倍率降序")},ZK=Qw.map(e=>({value:e,label:QK[e]})),Zw=[{key:"check",label:r("选择"),align:"center"},{key:"model",label:r("上游模型")},{key:"rate",label:r("倍率")},{key:"source",label:r("来源")},{key:"budget",label:r("上下文 / 输出")},{key:"caps",label:r("能力")},{key:"alias",label:r("模型映射")},{key:"act",label:r("操作"),align:"right"}],x2=new Set(["rate","source"]),lp="custom-all",Ww={models:[],mappings:[]},b2={provider:"all",state:"all",search:"",sort:"default"};function v2(){const e=Wt().wbFilterMemory;if(!e)return{...b2};const t=e.load(g2,{...b2});return{provider:String(t.provider||"all"),state:Xw.includes(t.state)?t.state:"all",search:String(t.search||""),sort:Qw.includes(t.sort)?t.sort:"default"}}const Zg=v2();let y2=!!Wt().wbFilterMemory,Wg=!1,ls={provider:Zg.provider,stateFilter:Zg.state,search:Zg.search,sort:Zg.sort,data:null,expanded:new Set,pending:new Set,mapping:null,customModel:null,capability:null};const Jw=new Set;function op(){return ls}function WK(e){return Jw.add(e),()=>{Jw.delete(e)}}function zs(e){const t={...ls,...e},a=ex(t),o=ex(ls)!==a;ls=a===t.provider?t:{...t,provider:a},eY(),o&&tx();for(const c of Jw)c()}function Jg(e){Wt().wbFilterMemory?.save(g2,e)}function j2(){if(y2||Wg||!Wt().wbFilterMemory)return;y2=!0;const e=v2();(e.provider!==ls.provider||e.state!==ls.stateFilter||e.search!==ls.search||e.sort!==ls.sort)&&zs(e)}function ex(e){const t=e.provider;return t==="all"||t===lp||Wi().some(a=>a.id===t)||Array.isArray(e.data?.models)&&e.data.models.some(a=>(a.provider||"")===t)||!e.data||!e0()?t:"all"}function Uu(){return ex(ls)}function e0(){return!!Wt().wbProviders}function Vu(){return Uu()===lp}function w2(){return Vu()||as(Uu())}let ip=null;function t0(e){const t=Lo(e);return(!ip||ip.id!==e||ip.record!==t)&&(ip={id:e,record:t,view:l2(e)||Ww}),ip.view}let cp=null;function JK(){const e=r2();if(cp&&cp.directory===e)return cp.view;const t=[],a=[];for(const o of Wi()){const c=t0(String(o.id||""));t.push(...c.models||[]),a.push(...c.mappings||[])}return cp={directory:e,view:t.length||a.length?{models:t,mappings:a}:Ww},cp.view}function n0(){const e=Uu();return as(e)?t0(e):e===lp?JK():ls.data||Ww}function up(){const e=n0();return Array.isArray(e.models)?e.models:[]}function S2(){const e=n0();return Array.isArray(e.mappings)?e.mappings:[]}let N2=(e,t,a)=>"";function eY(){N2=GK(S2())}function s0(){const e=new Map,t=Array.isArray(ls.data?.models)?ls.data.models:[];for(const a of t){const o=a.provider||"";if(!o)continue;const c=e.get(o)||{label:a.providerLabel||o,n:0};c.n++,e.set(o,c)}return e}function tY(){const e=new Map;for(const t of Wi()){const a=String(t.id||"");if(!a)continue;const o=t0(a);e.set(a,{label:String(t.name||a),n:(o.models||[]).length})}return e}function nY(){return[...s0().keys()]}function sY(e){const a=(Array.isArray(ls.data?.models)?ls.data.models:[]).find(o=>o.provider===e);return Number(a?.refreshedAt)||0}function r0(){const e=new Map;for(const t of up()){const a=t.provider||"";a&&!e.has(a)&&e.set(a,r(t.providerLabel||a))}return[...e].map(([t,a])=>({id:t,label:a}))}function k2(e){return e?up().filter(t=>(t.provider||"")===e).sort((t,a)=>+(t.enabled===!1)-+(a.enabled===!1)).map(t=>({id:t.id,label:t.name&&t.name!==t.id?r("{id}({name})",{id:t.id,name:t.name}):t.id,off:t.enabled===!1})):[]}function rY(){const e=[],t=Wt().wbProviders?.all?.();if(Array.isArray(t))for(const a of t){const o=String(a?.id||"");o&&e.push({id:o,label:r(String(a?.label||o))})}for(const a of Wi()){const o=String(a.id||"");o&&e.push({id:o,label:String(a.name||o)})}return e.length?e:r0()}let dp=null,a0=null;function C2(){const e=dp?dp.apply(Zw):Zw;return w2()?e.filter(t=>!x2.has(t.key)):e}function tx(){Wt().wbColSettings?.syncStaticHead("models",a0,w2()?x2:null)}function aY(){if(dp)return;dp=Wt().wbColSettings?.register({id:"models",label:r("模型管理表"),columns:Zw,mount:()=>document.querySelector('.page[data-page="gateway"] .panel-head .head-actions'),buttonPlacement:"last",onChange:()=>{tx(),ec()}})||null,dp&&ec()}let nx=0,l0=!1;async function sx({force:e=!1}={}){if(l0&&!e)return;const t=++nx;l0=!0,j2();try{const a=await Wt().workbuddyDesktop?.getModelManage();if(t!==nx)return;zs({data:a&&Array.isArray(a.models)?a:null})}catch(a){if(t!==nx)return;ec(),Nr(r("读取模型清单失败:{message}",{message:zo(a)}),"err")}finally{t===nx&&(l0=!1)}}async function T2(){try{await Wt().wbProviders?.refreshCustom?.()}catch{}await sx({force:!0})}function ec(){zs({})}function tc(e){const t=e;if(t&&Array.isArray(t.models)){zs({data:t});return}ec()}async function rx(e,t,a,o){if(as(e))return o2(e,t,a,o);const c=Wt().workbuddyDesktop;if(!c)throw new Error(r("后端桥不可用"));return c.addModelMapping(t,a,e,o.reasoning,o.enabled)}async function lY(e,t,a){if(as(e))return i2(e,t,a);const o=Wt().workbuddyDesktop;if(!o)throw new Error(r("后端桥不可用"));return o.removeModelMapping(t,a,e)}async function oY(e,t){if(as(e))return c2(e,t);const a=Wt().workbuddyDesktop;if(!a)throw new Error(r("后端桥不可用"));return a.addCustomModel(e,t)}async function E2(e,t){if(as(e))return d2(e,t);const a=Wt().workbuddyDesktop;if(!a)throw new Error(r("后端桥不可用"));return a.removeCustomModel(e,t)}async function iY(e,t,a){if(as(e))return u2(e,t,a);const o=Wt().workbuddyDesktop;if(!o)throw new Error(r("后端桥不可用"));return o.setModelCapabilities(e,t,a)}async function o0(e,t,a){if(!ls.pending.has(e)){zs({pending:new Set(ls.pending).add(e)});try{tc(await t()),a&&Nr(a)}catch(o){Nr(r("操作失败:{message}",{message:zo(o)}),"err")}finally{const o=new Set(ls.pending);o.delete(e),zs({pending:o})}}}function fp(e){return Fu(e).some(t=>t.enabled!==!1)}function Fu(e){const t=e.provider||"",a=S2().filter(p=>Bo(p.target,e.id)&&(!p.provider||Bo(p.provider,t))),o=new Map;for(const p of a){const h=String(p.alias).toLowerCase();(!o.has(h)||p.provider)&&o.set(h,p)}const c=String(e.id).toLowerCase(),d=o.get(c);o.delete(c);const f=[{alias:e.id,target:e.id,provider:t,enabled:e.enabled!==!1&&d?.enabled!==!1,isDefault:!0}];return f.push(...o.values()),f}const nc=e=>`${e.provider||""}:${e.id}`,i0=(e,t,a)=>`${e}:${t}:${a||""}`;function cY(e){zs({expanded:new Set(ls.expanded).add(e)})}function uY(e){const t=new Set(ls.expanded);t.delete(e),zs({expanded:t})}function dY(e){const t=Xw.includes(e)?e:"all";t!==ls.stateFilter&&(Wg=!0,zs({stateFilter:t}),Jg({state:t}))}function fY(e){const t=Qw.includes(e)?e:"default";t!==ls.sort&&(Wg=!0,zs({sort:t}),Jg({sort:t}))}function pY(e){Wg=!0,zs({search:e}),Jg({search:e})}function pp(e){zs({provider:String(e??"").trim()||"all"}),Jg({provider:ls.provider}),tx()}async function mY(e){const t=Wt().wbCustomProvidersUi;if(!t?.remove){Nr(r("删除入口未就绪,请重试或重启应用"),"err");return}await t.remove(e)&&await sx({force:!0})}function hY(){Wt().wbAccountAddForms?.openNewCustomForm?.()}function A2(e){zs({mapping:e})}function gY(){zs({mapping:null})}function xY(){const e=Uu(),t=as(e);zs({customModel:{provider:t||e!=="all"&&e!==lp?e:"",locked:t}})}function bY(){zs({customModel:null})}function R2(e,t){zs({capability:{provider:e,id:t}})}function vY(){zs({capability:null})}function O2(e,t){return up().find(a=>(a.provider||"")===e&&Bo(a.id,t))||null}function c0(e){if(as(e)){const t=Lo(e)?.name;return typeof t=="string"&&t||e}return r(Wt().wbProviders?.labelOf?.(e)||e)}function M2(){const e=Uu(),t=as(e),a=t?Lo(e)?.name||e:r("内置提供商");Wt().wbModelsFetchModal?.open({providerId:e===lp?"":e,custom:t,name:a,onDone:()=>ec(),onRefreshed:()=>{sx({force:!0})}})}window.wbModelsPanel={render:ec,load:sx,refreshAll:T2,refreshModels:M2,visibleColumns:C2,selectProvider:pp,builtinProviders:nY,providerRefreshedAt:sY};function u0(e,t,a){return N2(e,t,a)}function yY(e){a0=e}function jY(){const e=a0;if(!e)return;const t=o=>Object.keys(o).some(c=>c.startsWith("__reactFiber$")||c.startsWith("__reactInternalInstance$"));for(const o of[...e.children])o.tagName==="TBODY"?o.id!=="models"&&o.remove():o.tagName==="TR"&&!t(o)&&o.remove();const a=e.querySelector("tbody#models");if(a)for(const o of[...a.children])t(o)||o.remove()}const wY=e=>[{value:"inherit",label:e},{value:"on",label:r("支持")},{value:"off",label:r("不支持")}];function SY(e){const t=Kw(e);return r(t==="on"?"支持":t==="off"?"不支持":"未声明")}function D2(e,t,a,o,c=""){const d=t!=null,f=np.includes(e)?sp(t):SY(t);return a?o?r("已填写:对下游声明 {value}",{value:f}):r("已覆盖:对下游声明 {value}",{value:f}):o?r("未填写(下游按未声明处理)"):d?c==="modelsdev"?r("models.dev 补齐:对下游声明 {value}",{value:f}):r("继承上游:{value}",{value:f}):r("上游未声明(下游会按不支持处理)")}function NY(e,t,a){return a?zu.filter(o=>{const c=e[o];return c!=null}):t}function kY(e,t){const a={};for(const o of zu){const c=t.includes(o),d=e[o];np.includes(o)?a[o]=c&&typeof d=="number"?String(d):"":a[o]=c?d===!0?"on":"off":"inherit"}return a}function CY({context:e,onClose:t}){const a=as(e.provider),o=O2(e.provider,e.id),c=LK(o),d=Gw(o),f=NY(d,qw(o?.capOverrides),a),[p,h]=x.useState(()=>kY(d,f)),[b,y]=x.useState(""),[v,j]=x.useState(!1);if(!o)return s.jsx(Yt,{open:!0,onOpenChange:E=>{E||t()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("模型能力")})}),s.jsx(Lt,{children:s.jsx("p",{className:"text-sm leading-[1.7] text-subtle",children:r("这一行已不在当前清单里(模型被移除、或目录刷新后上游不再提供它)。关闭后刷新列表再试。")})}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:t,children:r("关闭")})]})]})});const w=o.id,S=o.providerLabel||c0(e.provider),N=r(a?"未声明":"继承上游"),k=r(a?"清除":"恢复继承");function A(E){const D=d[E];return P(E)?"":a?r("未填写"):typeof D=="number"?r("继承上游 {value}",{value:sp(D)}):r("上游未声明")}function P(E){return f.includes(E)}function R(E,D){h(I=>({...I,[E]:D}))}async function C(){if(!v){for(const E of np){const D=p[E].trim();if(D&&$w(E,D)===null){y(r("{label}要填 1 ~ 1 亿之间的整数(留空 = {inherit})",{label:Bu[E],inherit:N}));return}}j(!0),y(r("保存中…"));try{tc(await iY(e.provider,e.id,HK(p))),t(),Nr(r("✅ 已保存 {id} 的能力位({provider})",{id:w,provider:S}))}catch(E){y(r("保存失败:{message}",{message:zo(E)})),j(!1)}}}return s.jsx(Yt,{open:!0,onOpenChange:(E,D)=>{if(!E){if(v){D.cancel();return}t()}},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("模型能力")})}),s.jsxs(Lt,{children:[s.jsxs("div",{className:"rounded-md border border-border bg-surface-inset px-3 py-2.5 font-mono text-[12px] text-subtle",children:[s.jsx("b",{className:"text-primary-fg",children:o.id}),r("({provider})",{provider:S})]}),np.map(E=>s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsxs("div",{className:"flex items-center gap-[7px]",children:[s.jsx(Je,{htmlFor:`cap-${E}`,children:Bu[E]}),P(E)?s.jsx(ne,{variant:"ghost",size:"2xs",disabled:v,title:r(a?"清除这一项(回到未声明)":"清除覆盖,回到上游声明值"),onClick:()=>R(E,""),children:k}):null]}),s.jsx(Fe,{id:`cap-${E}`,inputMode:"numeric",autoComplete:"off",spellCheck:!1,placeholder:A(E),disabled:v,value:p[E],onChange:D=>R(E,D.currentTarget.value),onKeyDown:D=>{D.key==="Enter"&&(D.preventDefault(),C())}}),s.jsx("p",{className:"text-xs leading-[1.6] text-subtle",children:D2(E,d[E]??null,P(E),a,Hu(o,E))})]},E)),e2.map(E=>s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{children:Bu[E]}),s.jsx(En,{options:wY(N),value:p[E],"aria-label":Bu[E],className:"self-start",onValueChange:D=>R(E,String(D))}),s.jsx("p",{className:"text-xs leading-[1.6] text-subtle",children:D2(E,d[E]??null,P(E),a,Hu(o,E))})]},E)),c?s.jsxs("div",{className:"rounded-md border border-border bg-surface-inset px-3 py-2.5 text-xs leading-[1.7] text-subtle",children:[s.jsxs("div",{className:"flex flex-wrap items-center gap-x-2 gap-y-1",children:[s.jsx("span",{children:r("可配思考档位")}),s.jsx("span",{className:"font-mono text-[12px] text-primary-fg",children:c.levels.join(" / ")}),c.defaultLevel?s.jsxs("span",{children:[r("(默认 "),s.jsx("span",{className:"font-mono text-[12px] text-primary-fg",children:c.defaultLevel}),r(")")]}):null]}),s.jsx("div",{className:"mt-1",children:r("这是模型自己能配的档位(来自上游目录),只读;要改某一档请到映射弹窗里选。")})]}):null,s.jsxs("p",{className:"text-xs leading-[1.65] text-subtle",children:[r("这几项是给"),s.jsx("b",{children:r("下游客户端")}),r("看的能力声明("),s.jsx("code",{children:"/v1/models"}),r(" 里的 "),s.jsx("code",{children:"max_input_tokens"})," / ",s.jsx("code",{children:"supports_*"}),r(" 与 "),s.jsx("code",{children:"input_modalities"}),r("),下游按它们决定发不发图片、按多大的窗口堆历史。"),s.jsx("b",{children:r("不影响网关的转发与路由")}),r("。上游清单给的值不准时,在这里改;"),r(a?"这家的清单是你自己登记的,不填的项下游按未声明处理。":"「恢复继承」会把这一项交还给上游清单的值。")]}),s.jsx("div",{className:"min-h-[18px] text-xs text-subtle",children:b})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",disabled:v,onClick:t,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:v,onClick:()=>{C()},children:r("保存")})]})]})})}function mp(){return window}const TY="你好",I2=4e3,EY=[{value:"chat_completions",label:"OpenAI - Chat Completions"},{value:"responses",label:"OpenAI - Responses"},{value:"anthropic",label:"Anthropic - Messages"}],AY={chat_completions:"Chat Completions",responses:"Responses",anthropic:"Anthropic Messages"},RY="direct",OY={remote:r("远程目录"),builtin:r("内置清单"),manual:r("手动登记")},MY=[{test:e=>e===429,hint:r("该账号对这个模型正在限额冷却,等一会儿自己会好;也可以先测别的账号。")},{test:e=>e===401||e===403,hint:r("上游拒绝了这条登录态:重新登录一次,或把这条账号删掉。")},{test:e=>e===404,hint:r("上游不认识这个模型名:先点「获取模型」刷新清单,或核对默认绑定指向的上游模型。")},{test:e=>e===504,hint:r("上游没在预算内给完回答。可以只勾一个账号再测一次,看是普遍慢还是某一个账号慢。")},{test:e=>e>=500,hint:r("多半是上游自己的问题,过一会儿再试。同一个错误出现在所有账号上时,先查模型目录与映射。")}];function P2(){return mp().wbApp?.getState?.()?.accounts?.accounts||[]}function _2(e){return P2().filter(t=>(t.provider||pu)===e&&t.available!==!1&&t.enabled!==!1).sort(Wh)}function L2(e){const t=String(e.email||"").trim();return t&&cr(e.provider).emailAsName?t:hu(e)||t||r("未命名账号")}function DY(){const e=globalThis.crypto?.randomUUID?.();if(e)return e;const t=a=>Array.from({length:a},()=>Math.floor(Math.random()*16).toString(16)).join("");return`${t(8)}-${t(4)}-4${t(3)}-a${t(3)}-${t(12)}`}function d0(e){const t=Number(e);return!Number.isFinite(t)||t<=0?"—":t<1e3?`${Math.round(t)} ms`:`${(t/1e3).toFixed(t<1e4?2:1)} s`}function IY(e,t){return _2(e).length?"":r("该提供商没有可用账号(要在账号页启用一个、且凭证完整),一行都发不出去")}function z2({children:e,plain:t=!1}){return s.jsxs("p",{className:t?"flex items-center gap-2 text-xs leading-[1.6] text-subtle":"flex gap-2 rounded-md border border-border bg-surface-2 px-3 py-2.5 text-xs leading-[1.65] text-subtle",children:[s.jsx("span",{"aria-hidden":"true",className:t?"size-4 flex-none rounded-full border border-border-strong text-center text-[10px] leading-[14px]":"mt-px size-4 flex-none rounded-full border border-border-strong text-center text-[10px] leading-[14px]",children:"i"}),s.jsx("span",{children:e})]})}function PY({open:e,label:t,text:a,onToggle:o}){return s.jsxs("div",{className:"overflow-hidden rounded-sm border border-border",children:[s.jsxs(ne,{variant:"ghost",size:"xs",className:"w-full","aria-expanded":e,onClick:o,children:[s.jsx("span",{className:"truncate",children:t}),s.jsx("span",{"aria-hidden":"true",className:"ml-auto text-[10px]",children:e?"▴":"▾"})]}),e?s.jsx("div",{className:"border-t border-hairline bg-surface px-2.5 py-2",children:s.jsx("pre",{className:"whitespace-pre-wrap font-mono text-[11.5px] leading-[1.65] text-subtle",children:a})}):null]})}function _Y(e){const[t,a]=x.useState(()=>Date.now());return x.useEffect(()=>{if(!e)return;a(Date.now());const o=window.setInterval(()=>a(Date.now()),400);return()=>window.clearInterval(o)},[e]),t}function B2(e){if(e.transport||!e.result||e.result.error)return!1;const t=Number(e.result.status)||0;return t>=200&&t<300}function LY({target:e,onClose:t}){const a=e.provider,o=O2(a,e.id),c=_2(a),d=vj(P2()),[f,p]=x.useState(""),[h,b]=x.useState(TY),[y,v]=x.useState("chat_completions"),[j,w]=x.useState(""),[S,N]=x.useState(null),[k,A]=x.useState(""),[P,R]=x.useState(""),[C,E]=x.useState(!0),[D,I]=x.useState(()=>{const te=c.find(ke=>!Cf(ke,e.id))||c[0];return te?[te.id]:[]}),[O,_]=x.useState([]),[z,H]=x.useState(()=>new Set),B=x.useRef(0),L=x.useRef([]);L.current=O;const F=O.some(te=>te.state==="running"),$=_Y(F);x.useEffect(()=>{let te=!0;return(async()=>{try{const ke=mp().workbuddyDesktop?.getProxyPool;if(typeof ke!="function")throw new Error(r("当前环境不支持代理池"));const le=await ke();if(!te)return;if(!le||typeof le!="object"||!Array.isArray(le.items))throw new Error(r("代理池响应异常"));N(le.items),A("")}catch(ke){if(!te)return;N([]),A(zo(ke))}})(),()=>{te=!1}},[]);const X=x.useCallback((te=!1)=>{const ke=L.current.filter(le=>le.state==="running");B.current+=1;for(const le of ke)mp().workbuddyDesktop?.terminateStatsRequest?.(le.testId)?.catch(()=>{});_([]),ke.length&&!te&&Nr(r("已中止在途测试({n} 个账号)",{n:ke.length}))},[]);x.useEffect(()=>()=>{X(!0)},[X]);async function K(te,ke){const le={provider:a,model:e.id,account_id:te.accountId,prompt:h.trim(),system_prompt:f.trim(),reasoning:P,stream:C,protocol:y,...j?{proxy:j}:{},test_id:te.testId},de=Se=>{B.current===ke&&_(Te=>Te.map(_e=>_e.testId===te.testId?{..._e,...Se}:_e))};try{const Se=await mp().workbuddyDesktop?.testModel(le);if(!Se){de({state:"done",transport:r("桥接调用返回空(本机网关没有响应这次测试)")});return}de({state:"done",result:Se})}catch(Se){de({state:"done",transport:zo(Se)})}}function q(){const te=c.filter(Se=>D.includes(Se.id));if(!te.length){Nr(r("至少选一个测试账号"),"err");return}const ke=B.current+1;B.current=ke;const le=Date.now(),de=te.map(Se=>({accountId:Se.id,label:L2(Se),position:d.get(Se.id)?.position||0,testId:DY(),startedAt:le,state:"running"}));H(new Set),_(de);for(const Se of de)K(Se,ke)}function Q(){F?X():_([])}const G=s.jsx("span",{className:"ml-2 font-mono text-[12px] font-normal text-subtle",children:e.id});if(!o)return s.jsx(Yt,{open:!0,onOpenChange:te=>{te||t()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("测试模型")})}),s.jsx(Lt,{children:s.jsx("p",{className:"text-sm leading-[1.7] text-subtle",children:r("这一行已不在当前清单里(模型被移除、或目录刷新后上游不再提供它)。关闭后刷新列表再试。")})}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:t,children:r("关闭")})]})]})});const J=Fu(o).find(te=>te.isDefault)?.enabled===!1,Y=u0(o.id,o.id,a),re=Fu(o).filter(te=>!te.isDefault).map(te=>te.alias),M=ap(op().data,o).filter(te=>te!=="off"&&te!=="none"),U=J?r("跟随映射(该行未启用,本次不注入)"):Y?r("跟随映射(当前 {level})",{level:Y}):r("跟随映射(未绑定等级)"),Z=[r("以模型本名直发"),re.length?r("另有 {n} 条别名映射({aliases}),别名不参与本次",{n:re.length,aliases:re.join(r("、"))}):"",J?r("该行当前未启用:测试照常按本名直发(「先测通、再决定要不要启用」正是这颗按钮的用法),但本行的思考等级绑定不参与本次,生产路由也要等绑定打开后才会放行"):Y?r("映射上绑定的思考等级是 {level},「跟随映射」按它注入",{level:Y}):r("映射上未绑定思考等级,「跟随映射」等于这次不注入")].filter(Boolean).join(r(";"))+r("。"),ae=mp().wbProviders?.PROTOCOL_OPTIONS||EY,ce=ae.find(te=>te.value===y)?.label||y,W=[{value:"",label:r("跟随账号配置")},{value:RY,label:r("直连(不使用代理)")},...(S??[]).map(te=>({value:`${Qa}${te.id}`,label:du(te)}))];S===null?W.push({value:"__hint_pool__",label:r("正在读取代理列表…"),disabled:!0}):S.length||W.push({value:"__hint_pool__",label:r(k?"代理列表读取失败":"还没有代理(去「网络代理」页添加)"),disabled:!0});const ue=W.find(te=>te.value===j)?.label||r("跟随账号配置"),ve=c.map(te=>({value:te.id,label:`#${d.get(te.id)?.position||0} ${L2(te)}`+(Cf(te,e.id)?r(" · 限额中"):"")}));function ee(te){const ke=(Ae,oe)=>s.jsxs("div",{className:"flex items-center gap-2.5 border-b border-hairline px-3 py-2",children:[Ae,s.jsx("b",{className:"min-w-0 truncate text-[12.5px] text-foreground",title:te.label,children:te.label}),te.position?s.jsxs("span",{className:"flex-none text-xs text-subtle",children:["#",te.position]}):null,s.jsx("span",{className:"ml-auto flex-none font-mono text-[11.5px] tabular-nums text-subtle",children:oe})]},"head");if(te.state==="running"){const Ae=($-te.startedAt)/1e3;return s.jsxs("div",{className:"flex items-center gap-2.5 rounded-md border border-border bg-surface-2 px-3 py-2.5 text-xs text-subtle",children:[s.jsx(of,{className:"flex-none"}),s.jsxs("span",{className:"min-w-0 truncate",children:[r("账号 "),s.jsx("b",{className:"text-foreground",children:te.label}),r(" 已发出,等待上游首帧…")]}),s.jsx("span",{className:"ml-auto flex-none font-mono tabular-nums",children:r("已等待 {seconds}s",{seconds:Ae.toFixed(1)})})]},te.testId)}const le=te.result;if(!B2(te)){const Ae=Number(le?.status)||0,oe=MY.find(me=>me.test(Ae))?.hint??(te.transport?r("确认桌面端还在运行,再重试一次。"):"");return s.jsxs("div",{className:"rounded-md border border-border bg-surface-2",children:[ke(s.jsx(ze,{shape:"tag",variant:"destructive",className:"flex-none",children:Ae||r("失败")}),r("用时 {time}",{time:d0(le?.duration_ms)})),s.jsxs("div",{className:"flex flex-col gap-1.5 px-3 py-2.5",children:[s.jsxs("div",{className:"text-[12.5px] leading-[1.7]",children:[te.transport?s.jsxs(s.Fragment,{children:[s.jsx("b",{className:"text-destructive",children:r("本机网关没有返回结论")}),r(":"),te.transport]}):s.jsxs(s.Fragment,{children:[s.jsx("b",{className:"text-destructive",children:r("测试未通过")}),r(":"),le?.error||r("上游没有给出可读的错误说明")]}),Number(le?.attempts)>1?r("(中间共尝试 {n} 次)",{n:Number(le?.attempts)}):""]}),oe?s.jsx("p",{className:"text-xs leading-[1.65] text-subtle",children:oe}):null]})]},te.testId)}const de=String(le?.reply??""),Se=String(le?.reasoning??""),Te=`think-${te.testId}`,_e=[le?.upstream_model&&le.upstream_model!==e.id?r("上游 {model}",{model:le.upstream_model}):"",le?.upstream_reasoning?r("思考等级 {level}",{level:le.upstream_reasoning}):"",le?.protocol&&le.protocol!=="chat_completions"?AY[le.protocol]||le.protocol:"",le?.proxy?r("节点 {proxy}",{proxy:le.proxy}):"",Number(le?.attempts)>1?r("尝试 {n} 次",{n:Number(le?.attempts)}):""].filter(Boolean).join(" · ");return s.jsxs("div",{className:"rounded-md border border-border bg-surface-2",children:[ke(s.jsx(ze,{shape:"tag",variant:"success",className:"flex-none",children:Number(le?.status)||200}),r("用时 {time} · 首字 {ttfb}",{time:d0(le?.duration_ms),ttfb:d0(le?.ttfb_ms)})),s.jsxs("div",{className:"flex flex-col gap-2 px-3 py-2.5",children:[s.jsxs("div",{className:"flex items-center gap-2 text-[11.5px] text-subtle",children:[s.jsx("span",{className:"flex-none",children:r("回复")}),_e?s.jsx("span",{className:"min-w-0 truncate",title:_e,children:_e}):null,s.jsx(ne,{variant:"ghost",size:"2xs",className:"ml-auto flex-none","data-copy":de,children:r("复制")})]}),s.jsx("div",{className:"max-h-[220px] overflow-y-auto whitespace-pre-wrap rounded-sm border border-border bg-surface px-3 py-2 text-[12.5px] leading-[1.7]",children:de||s.jsx("span",{className:"text-subtle",children:r("(上游这一次返回了空正文)")})}),Se?s.jsx(PY,{open:z.has(Te),label:r("思考过程({n} 字)",{n:Se.length}),text:Se,onToggle:()=>H(Ae=>{const oe=new Set(Ae);return oe.has(Te)?oe.delete(Te):oe.add(Te),oe})}):null]})]},te.testId)}function he(){if(F){const ke=O.length-O.filter(le=>le.state==="running").length;return s.jsxs(s.Fragment,{children:[O.length>1?s.jsxs("p",{className:"text-xs text-subtle",children:[r("正在测 "),s.jsx("b",{className:"text-foreground",children:O.length}),r(" 个账号"),ke?s.jsxs(s.Fragment,{children:[r(",已完成 "),s.jsx("b",{className:"text-foreground",children:ke})]}):null,r("。")]}):null,O.map(le=>ee(le))]})}const te=O.filter(B2).length;return s.jsxs(s.Fragment,{children:[O.length>1?s.jsxs("p",{className:"text-xs text-subtle",children:[s.jsx("b",{className:te?"text-success":"text-foreground",children:te}),r(" 成功 ·"),s.jsxs("b",{className:O.length-te?"text-destructive":"text-foreground",children:[" ",O.length-te]}),r(" 失败")]}):null,O.map(ke=>ee(ke)),te===0?s.jsxs(z2,{children:[r("两边原因不一样时各自处理。如果"),s.jsx("b",{className:"text-foreground",children:r("同一条错误出现在所有账号上")}),r(",通常就不是账号问题了:先确认这个模型还在上游目录里(「获取模型」刷一次),再看默认绑定指向的上游模型对不对。")]}):null]})}return s.jsx(Yt,{open:!0,onOpenChange:te=>{te||t()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsxs(Ut,{children:[r("测试模型"),G]})}),s.jsxs(Lt,{children:[s.jsxs("div",{className:"flex items-center gap-2 rounded-md border border-border bg-surface-inset px-3 py-2.5",children:[s.jsx("span",{className:"min-w-0 flex-1 truncate font-mono text-[12.5px] font-semibold text-primary-fg",title:o.id,children:o.id}),s.jsx(ze,{shape:"tag",variant:"brand",children:c0(a)}),s.jsx(ze,{shape:"tag",variant:"outline",children:as(a)?r("自定义家"):OY[o.source]||r("来源未知")}),s.jsxs(Ui,{children:[s.jsx(Vi,{render:s.jsx("span",{className:"tip-q",tabIndex:0,"aria-label":r("本次测试的发送口径")}),children:"?"}),s.jsx(Fi,{children:Z})]})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"model-test-system",children:r("系统提示词")}),s.jsx(Mi,{id:"model-test-system",rows:2,maxLength:I2,placeholder:r("留空则不携带;设置页的提示词模式照常生效"),value:f,onChange:te=>p(te.currentTarget.value)})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"model-test-prompt",children:r("用户提示词")}),s.jsx(Mi,{id:"model-test-prompt",rows:2,maxLength:I2,placeholder:r("留空用默认问候「你好」"),value:h,onChange:te=>b(te.currentTarget.value)})]}),s.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsxs("div",{className:"flex items-center gap-1.5",children:[s.jsx(Je,{htmlFor:"model-test-protocol",children:r("对话协议")}),s.jsxs(Ui,{children:[s.jsx(Vi,{render:s.jsx("span",{className:"tip-q",tabIndex:0,"aria-label":r("对话协议的说明")}),children:"?"}),s.jsx(Fi,{children:r("按所选网关对话协议组装请求并校验响应转换(Chat Completions / Responses / Anthropic Messages)。")})]})]}),s.jsxs(ut,{value:y,onValueChange:te=>v(String(te)),children:[s.jsx(ft,{id:"model-test-protocol",className:"w-full",children:s.jsx(dt,{className:"min-w-0 truncate",children:ce})}),s.jsx(pt,{children:ae.map(te=>s.jsx($e,{value:te.value,children:te.label},te.value))})]})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsxs("div",{className:"flex items-center gap-1.5",children:[s.jsx(Je,{htmlFor:"model-test-proxy",children:r("测试节点")}),s.jsxs(Ui,{children:[s.jsx(Vi,{render:s.jsx("span",{className:"tip-q",tabIndex:0,"aria-label":r("测试节点的说明")}),children:"?"}),s.jsx(Fi,{children:r("仅本次测试临时生效,不修改账号保存的代理配置。可选「网络代理」页里的节点或强制直连。")})]})]}),s.jsxs(ut,{value:j,onValueChange:te=>w(String(te)),children:[s.jsx(ft,{id:"model-test-proxy",className:"w-full",children:s.jsx(dt,{className:"min-w-0 truncate",children:ue})}),s.jsx(pt,{children:W.map(te=>s.jsx($e,{value:te.value,disabled:te.disabled,children:te.label},te.value))})]})]})]}),s.jsxs("div",{className:"grid grid-cols-2 gap-4",children:[s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"model-test-reasoning",children:r("思考等级")}),s.jsxs(ut,{value:P,onValueChange:te=>R(String(te)),children:[s.jsx(ft,{id:"model-test-reasoning",className:"w-full",children:s.jsx(dt,{children:P?r("{level}(本次指定)",{level:P}):U})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:U}),M.map(te=>s.jsx($e,{value:te,children:r("{level}(本次指定)",{level:te})},te))]})]})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsxs("div",{className:"flex items-center gap-1.5",children:[s.jsx(Je,{htmlFor:"model-test-stream",children:r("流式请求")}),s.jsxs(Ui,{children:[s.jsx(Vi,{render:s.jsx("span",{className:"tip-q",tabIndex:0,"aria-label":r("流式请求的说明")}),children:"?"}),s.jsx(Fi,{children:r("默认开,与真实请求一致:可同时验证 SSE 链路与首字延迟。")})]})]}),s.jsx(ns,{id:"model-test-stream",checked:C,onCheckedChange:te=>E(!!te)})]})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(Je,{htmlFor:"model-test-accounts",children:r("测试账号")}),s.jsxs(Ui,{children:[s.jsx(Vi,{render:s.jsx("span",{className:"tip-q",tabIndex:0,"aria-label":r("测试账号的说明")}),children:"?"}),s.jsx(Fi,{children:r("勾几个就并行测几次;默认选队列里最靠前的一个。")})]}),s.jsx(ne,{variant:"ghost",size:"xs",className:"ml-auto",disabled:!c.length||D.length===c.length,onClick:()=>I(c.map(te=>te.id)),children:r("全选({n})",{n:c.length})})]}),s.jsx(Gh,{id:"model-test-accounts",value:D,onValueChange:I,options:ve,placeholder:r("选一个账号(至少一个)"),searchPlaceholder:r("搜索账号…"),emptyHint:r("这家没有可用账号(要在账号页启用一个、且凭证完整)")})]}),s.jsx(z2,{plain:!0,children:r("真实链路 · 消耗少量额度 · 日志带「测试」标记,不计报表")})]}),s.jsxs(tn,{className:"justify-end",children:[s.jsx(ne,{variant:"outline",onClick:t,children:r("关闭")}),s.jsx(ne,{variant:"default",disabled:!D.length||!c.length,onClick:q,children:r("开始测试")})]}),s.jsx(Yt,{open:O.length>0,onOpenChange:te=>{te||Q()},children:s.jsxs(Xt,{overlayForceRender:!0,className:"w-[min(680px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsxs(Ut,{children:[r(F?"测试中…":"测试结果"),G]})}),s.jsx(Lt,{children:he()}),s.jsx(tn,{className:"justify-end",children:F?s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"mr-auto text-xs text-subtle",children:r("关闭即中止")}),s.jsx(ne,{variant:"outline",onClick:()=>X(),children:r("中止测试")}),s.jsx(ne,{variant:"default",disabled:!0,children:r("测试中…")})]}):s.jsxs(s.Fragment,{children:[s.jsx(ne,{variant:"outline",onClick:Q,children:r("关闭")}),s.jsx(ne,{variant:"default",onClick:q,children:r("再测一次")})]})})]})})]})})}const H2=[{value:"enable",label:r("启用")},{value:"disable",label:r("禁用")},{value:"reasoning",label:r("设置思考等级")},{value:"delete",label:r("删除")}];function U2(e){return e.source==="manual"||as(e.provider)}function zY(e){return(e.name&&e.name!==e.id?r("{name}({id})",{name:e.name,id:e.id}):e.id)||e.id}function BY({models:e,onClose:t,onDone:a}){const[o,c]=x.useState("enable"),d=qK(op().data,e),[f,p]=x.useState(()=>({select:d[0]||"",custom:""})),[h,b]=x.useState(!1),[y,v]=x.useState(""),j=f.select===fa?f.custom.trim():f.select,w=e.filter(U2).length,S=H2.find(A=>A.value===o)?.label||r("操作");async function N(A){return Promise.resolve(Wt().wbConfirm?.ask?.({title:r("删除选中的模型"),html:r("确定删除选中的 {count} 个模型登记?删除后 /v1/models 不再广告它们、请求也会被拒。",{count:A})+(e.length>A?"
    "+r("另有 {n} 个非手动登记的模型将被跳过。",{n:e.length-A}):""),okText:r("删除"),okClass:"danger"})??!1)}async function k(){if(h)return;if(!e.length){Nr(r("没有选中的模型"),"err");return}if(o==="reasoning"&&f.select===fa&&!j){v(s.jsx("span",{className:"text-destructive",children:r("请填写自定义思考等级,或改选候选档 / 「清除」")}));return}if(o==="delete"){if(w===0){v(s.jsx("span",{className:"text-destructive",children:r("选中的模型都不是手动登记,无法删除(开关映射才是它们的手段)")}));return}if(!await N(w))return}b(!0),v("");let A=0,P=0,R=0,C="";for(const E of e){const D=E.provider||"";try{if(o==="delete"){if(!U2(E)){P+=1;continue}tc(await E2(D,E.id))}else if(o==="enable"||o==="disable"){const I=o==="enable";for(const O of Fu(E))tc(await rx(D,O.alias,E.id,{enabled:I}))}else for(const I of Fu(E))tc(await rx(D,I.alias,E.id,{reasoning:j}));A+=1}catch(I){R+=1,C||(C=zo(I)),v(s.jsx("span",{className:"text-destructive",children:r("失败 {n} 个 · 首个失败:{error}",{n:R,error:C})}))}}R===0?(Nr(P?r("✅ 批量{action}完成:共 {n} 个模型(跳过不可删除的 {skipped} 个)",{action:S,n:A,skipped:P}):r("✅ 批量{action}完成:共 {n} 个模型",{action:S,n:A})),a()):(Nr(r("批量{action}完成:成功 {ok} 个,失败 {failed} 个",{action:S,ok:A,failed:R}),"err"),v(s.jsxs("span",{className:"text-destructive",children:[r("成功 {ok} 个,失败 {failed} 个",{ok:A,failed:R}),P?r(",跳过 {n} 个",{n:P}):"",C?r(";首个失败:{error}",{error:C}):""]}))),b(!1)}return s.jsx(Yt,{open:!0,onOpenChange:A=>{A||h||t()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("批量操作 · 已选 {n} 个模型",{n:e.length})})}),s.jsxs(Lt,{children:[s.jsxs(dn,{children:[s.jsx("h3",{children:r("将作用于以下模型")}),s.jsx("p",{style:{maxHeight:84,overflowY:"auto"},children:e.map(zY).join(r("、"))})]}),s.jsxs(dn,{children:[s.jsx("h3",{children:r("操作")}),s.jsx(Ph,{value:o,onValueChange:A=>c(A),className:"flex-row flex-wrap items-center gap-5","aria-label":r("批量动作"),children:H2.map(A=>s.jsxs(Je,{className:"inline-flex cursor-pointer items-center gap-2 font-normal",children:[s.jsx(jo,{value:A.value}),A.label]},A.value))}),o==="reasoning"?s.jsxs("div",{className:"mt-2.5",children:[s.jsxs("div",{className:"field-row",children:[s.jsx("label",{htmlFor:"batch-model-reasoning",children:r("思考等级")}),s.jsxs(ut,{value:f.select,onValueChange:A=>p(P=>({...P,select:String(A)})),children:[s.jsx(ft,{id:"batch-model-reasoning",className:"min-w-[220px]",children:s.jsx(dt,{children:f.select===fa?r("自定义等级…"):f.select||r("清除(不覆盖)")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:r("清除(不覆盖)")}),d.map(A=>s.jsx($e,{value:A,children:A},A)),s.jsx($e,{value:fa,children:r("自定义等级…")})]})]})]}),f.select===fa?s.jsxs("div",{className:"field-row mt-2",children:[s.jsx("label",{htmlFor:"batch-model-reasoning-custom",children:r("自定义等级")}),s.jsx(Fe,{id:"batch-model-reasoning-custom",maxLength:32,value:f.custom,onChange:A=>p(P=>({...P,custom:A.currentTarget.value}))})]}):null,s.jsxs("p",{className:"detail mt-1.5",children:[r("设置 / 清除会应用到选中模型的"),s.jsx("b",{children:r("全部映射")}),r("(默认绑定与别名 chips);「清除」= 不给这些映射指定等级(选「不覆盖」的口径与映射弹窗一致)")]})]}):null,o==="delete"?s.jsx("p",{className:"detail mt-2",children:w===0?r("选中的都不是手动登记的模型,删除不可用(上游目录带回来的行由清单决定存在性)"):r("可删除 {n} 个(手动登记 / 自定义家);",{n:w})+(e.length>w?r("其余 {n} 个会被跳过。",{n:e.length-w}):"")}):null]}),s.jsx("div",{className:"detail",style:{minHeight:18},children:y})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",disabled:h,onClick:t,children:r("关闭")}),s.jsx(ne,{variant:o==="delete"?"destructive":"default",disabled:h,onClick:()=>{k()},children:h?r("执行中…"):r("执行{action}",{action:S})})]})]})})}function rr(){return window}const V2={intl:"fm-table-intl",custom:"fm-table-custom"},F2={intl:"fetch-models",custom:"fetch-models-custom"},f0=["provider","source","state","count","note","updated","op"],p0=["pick","model","state"],HY={provider:"w-[13%]",source:"w-[21%]",state:"w-[9%]",count:"w-[6%]",note:"w-[20%]",updated:"w-[16%]",op:"w-[15%]"},UY={pick:"w-[8%]",model:"w-[74%]",state:"w-[18%]"},VY={pick:"",provider:r("提供商"),source:r("模型来源"),state:r("状态"),count:r("模型数"),note:r("说明"),updated:r("更新日期"),model:r("上游模型"),op:r("操作")},FY={refreshed:{variant:"brand",label:r("已刷新")},failed:{variant:"destructive",label:r("失败")},skipped:{variant:"outline",label:r("跳过")}},$2=new Set(["cline-free","cline-pass"]),q2="workbuddy-desktop-fetch-model-source";function hp(e){return String(e??"").trim().toLowerCase()}function m0(e,t){rr().wbApp?.toast?.(e,t)}function h0(e){return e instanceof Error?e.message:String(e)}function G2(e){const t=rr().formatTime??rr().wbApp?.formatTime;return String(t?.(e)??"")}function K2(){return rr().wbApp?.getState?.()?.accounts?.accounts||[]}function $Y(e){const a=(rr().wbProviders?.customList?.()||[]).find(o=>o.id===e)?.models;return new Set((Array.isArray(a)?a:[]).map(o=>hp(o?.id)).filter(Boolean))}function Y2(e){if(e?.nameCustom===!0&&e?.name)return e.name;const t=String(e?.email||"").trim();return rr().wbAccountsModel?.providerFeatures?.(e?.provider)?.emailAsName&&t?t:String(e?.nickname||e?.name||t||e?.id||"").trim()||r("未命名账号")}function qY(e){if($2.has(e))return[];const t=rr().wbAccountsModel?.byPriorityOrder,a=K2().filter(o=>(o?.provider||"workbuddy")===e&&o?.available!==!1&&o?.enabled!==!1);return t?a.sort(t):a}function X2(){const e=new Set(rr().wbModelsPanel?.builtinProviders?.()||[]);for(const t of K2())t?.available===!1||t?.enabled===!1||e.add(t?.provider||"workbuddy");return[...e]}function GY(){const e=new Set(X2());return(rr().wbProviders?.all?.()||[]).filter(t=>e.has(t?.id??""))}function KY(e){return G2(Number(e.refreshedAt)||0)||"—"}function YY(e){return FY[String(e.status)]??{variant:"outline",label:String(e.status||r("未知"))}}function XY(){try{const e=JSON.parse(localStorage.getItem(q2)||"{}");if(!e||typeof e!="object")return{};const t={};for(const[a,o]of Object.entries(e))typeof o=="string"&&o&&(t[a]=o);return t}catch{return{}}}function QY(e){rr().wbFilterMemory?.save?.(q2,e)}function ZY({options:e,onClose:t}){const a=e.providerId,o=!!e.custom,c=e.name||a,{onDone:d,onRefreshed:f}=e,[p,h]=x.useState("idle"),[b,y]=x.useState(""),[v,j]=x.useState(""),[w,S]=x.useState(null),[N,k]=x.useState(""),A=w!==null,[P,R]=x.useState(!1),[C,E]=x.useState([]),[D,I]=x.useState(()=>new Set),[O,_]=x.useState(()=>new Set),[z,H]=x.useState([]),[B,L]=x.useState(""),[F,$]=x.useState(XY);x.useEffect(()=>{rr().wbTableColumns?.repaint?.(o?F2.custom:F2.intl)},[o]);const X=o?[]:GY(),K=o?[]:J(),q=B.trim().toLowerCase(),Q=C.filter(oe=>!q||oe.toLowerCase().includes(q)),G=o?P?r("已选 {picked} 个 · 上游共 {upstream} 个 · 清单已有 {managed} 个",{picked:O.size,upstream:C.length,managed:D.size}):r("尚未获取"):Y(K);function J(){const oe=new Map(z.map(Ce=>[String(Ce.provider||""),Ce])),me=X.map(Ce=>{const Ne=String(Ce.id||"");return{provider:Ne,label:String(Ce.label||Ne),item:oe.get(Ne)}}),pe=new Set(me.map(Ce=>Ce.provider));for(const Ce of z){const Ne=String(Ce.provider||"");pe.has(Ne)||me.push({provider:Ne,label:String(Ce.providerLabel||Ne),item:Ce})}return me}function Y(oe){if(p==="loading")return r("正在刷新…");if(p==="error")return"";const me=oe.map(Ne=>Ne.item).filter(Ne=>!!Ne);if(!me.length)return P?r("刷新完成,但没有得到任何结果"):oe.length?r("共 {n} 家 · 点「获取模型」开始",{n:oe.length}):r("0 家");const pe=me.filter(Ne=>Ne.status==="refreshed").length,Ce=me.filter(Ne=>Ne.status==="failed").length;return Ce?r("{total} 家 · 成功 {done} · 失败 {failed}",{total:oe.length,done:pe,failed:Ce}):r("{total} 家 · 成功 {done}",{total:oe.length,done:pe})}function re(oe,me){const pe=qY(oe);if(!pe.length)return{list:pe,selected:""};const Ce=String(F[oe]||""),Ne=String(me||""),be=De=>pe.some(Ge=>String(Ge.id)===De);return{list:pe,selected:be(Ce)?Ce:be(Ne)?Ne:String(pe[0].id)}}function M(oe){const me={};for(const[pe,Ce]of Object.entries(F))Ce&&(me[pe]=Ce);for(const pe of oe){const{selected:Ce}=re(pe.provider,pe.reported);Ce&&(me[pe.provider]=Ce)}return me}const U=K.map(oe=>({provider:oe.provider,reported:oe.item?.accountId}));function Z(oe,me){$(pe=>({...pe,[oe]:me})),QY({[oe]:me})}async function ae(){if(!A){S("load"),j(""),y(""),h("loading");try{if(o){const me=await rr().wbProviders?.customRequest?.("POST","/api/custom-providers/fetch-models",{providerId:a}),pe=new Set,Ce=(Array.isArray(me?.models)?me.models:[]).map(be=>String(be??"").trim()).filter(be=>{const De=hp(be);return!be||pe.has(De)?!1:(pe.add(De),!0)}),Ne=$Y(a);E(Ce),I(Ne),_(new Set(Ce.filter(be=>!Ne.has(hp(be))))),R(!0),h("ready");return}const oe=await rr().workbuddyDesktop?.refreshModels({accounts:M(U),providers:X2()});H(Array.isArray(oe?.results)?oe.results:[]),R(!0),h("ready"),Number(oe?.refreshed)>0&&f?.()}catch(oe){const me=h0(oe);y(me),h("error"),j(me)}finally{S(null)}}}async function ce(oe){if(!(A||!oe.provider)){S("row"),k(oe.provider),j("");try{const me=await rr().workbuddyDesktop?.refreshModels({accounts:M([{provider:oe.provider,reported:oe.item?.accountId}]),providers:[oe.provider]});H(pe=>W(pe,Array.isArray(me?.results)?me.results:[])),R(!0),h("ready"),Number(me?.refreshed)>0&&f?.()}catch(me){const pe=h0(me);j(pe),m0(r("获取失败:{message}",{message:pe}),"err")}finally{S(null),k("")}}}function W(oe,me){const pe=new Map(me.map(be=>[String(be.provider||""),be])),Ce=oe.map(be=>pe.get(String(be.provider||""))??be),Ne=new Set(Ce.map(be=>String(be.provider||"")));for(const be of me)Ne.has(String(be.provider||""))||Ce.push(be);return Ce}async function ue(){if(!o||A||!O.size)return;const oe=[...O];S("import"),j("");try{const me=Number(await rr().wbModelsCustom?.addModels?.(a,oe))||0;m0(r("✅ 已导入 {added} 个模型({skipped} 个已存在,跳过)",{added:me,skipped:oe.length-me})),S(null),t(),d?.()}catch(me){const pe=h0(me);m0(r("导入失败:{message}",{message:pe}),"err"),j(pe),S(null)}}function ve(){A||t()}function ee(){const oe=new Set(O);for(const me of C)D.has(hp(me))||oe.add(me);_(oe)}function he(oe){return s.jsx(Cl,{children:s.jsx(jn,{colSpan:o?p0.length:f0.length,className:"py-[26px] text-center text-muted-foreground",children:oe})})}function te(oe){return s.jsx("span",{className:"font-mono text-[11.5px] tabular-nums text-muted-foreground",children:oe})}function ke(oe){return s.jsx("div",{className:"truncate font-mono text-[12px] font-semibold",children:oe})}function le(oe,me){const{list:pe,selected:Ce}=re(oe,me);if(!pe.length){const be=$2.has(oe)?r("这家的模型清单是全局的,不跟账号走"):r("这家还没有可用账号,刷新会使用默认登录态");return s.jsx("span",{className:"text-[11.5px] text-muted-foreground",title:be,children:"—"})}const Ne=pe.find(be=>String(be.id)===Ce);return s.jsxs(ut,{value:Ce,onValueChange:be=>Z(oe,String(be)),children:[s.jsx(ft,{className:"w-full",title:r("用哪个账号去拉这家的模型清单"),"aria-label":r("模型来源"),children:s.jsx(dt,{children:Ne?Y2(Ne):"—"})}),s.jsx(pt,{children:pe.map(be=>s.jsx($e,{value:String(be.id),children:Y2(be)},String(be.id)))})]})}function de(oe,me){_(pe=>{const Ce=new Set(pe);return me?Ce.add(oe):Ce.delete(oe),Ce})}function Se(oe,me,pe){if(pe||oe.target?.closest('[data-slot="checkbox"], input[type="checkbox"]'))return;const Ne=window.getSelection();Ne&&!Ne.isCollapsed&&oe.currentTarget.contains(Ne.anchorNode)||de(me,!O.has(me))}function Te(){return p==="idle"?he(r("点右侧「获取模型」从上游拉取这家的模型清单")):p==="loading"?he(r("正在获取…")):p==="error"?he(r("获取失败:{message}",{message:b})):Q.length?Q.map(oe=>{const me=D.has(hp(oe));return s.jsxs(Cl,{className:me?"opacity-50":"cursor-pointer",onClick:pe=>Se(pe,oe,me),children:[s.jsx(jn,{children:s.jsx(Pr,{checked:O.has(oe),disabled:me,"aria-label":oe,onCheckedChange:pe=>de(oe,!!pe)})}),s.jsx(jn,{children:ke(oe)}),s.jsx(jn,{children:me?s.jsx(ze,{variant:"brand",title:r("这个模型已经在这家的清单里,不重复导入"),children:r("已添加")}):s.jsx(ze,{variant:"outline",title:r("勾上它,导入后即进入这家的清单"),children:r("未添加")})})]},oe)}):he(C.length?r("没有匹配「{keyword}」的模型",{keyword:q}):r("上游没有返回任何模型"))}function _e(oe){const me=w==="row"&&N===oe.provider;return s.jsx(ne,{variant:"outline",size:"sm",className:"w-full",disabled:A,title:r("只刷新这一家的模型清单"),onClick:()=>{ce(oe)},children:me?r("获取中…"):oe.item?r("重新获取"):r("获取")})}function Ae(){return p==="loading"?he(r("正在获取…")):p==="error"?he(r("获取失败:{message}",{message:b})):K.length?K.map(oe=>{const me=oe.item;if(!me){const be=G2(Number(rr().wbModelsPanel?.providerRefreshedAt?.(oe.provider))||0);return s.jsxs(Cl,{children:[s.jsx(jn,{children:ke(oe.label)}),s.jsx(jn,{children:le(oe.provider)}),s.jsx(jn,{children:s.jsx(ze,{variant:"outline",children:r("待获取")})}),s.jsx(jn,{children:te("—")}),s.jsx(jn,{children:te("—")}),s.jsx(jn,{children:te(be||"—")}),s.jsx(jn,{children:_e(oe)})]},oe.provider)}const pe=YY(me),Ce=Number(me.count),Ne=me.status==="refreshed"?r("远程目录已更新,「来源」列会显示为「远程」"):me.message||(me.fixed?r("这家用固定模型清单,无可刷新"):r("本次没有取到新内容"));return s.jsxs(Cl,{children:[s.jsx(jn,{children:ke(oe.label)}),s.jsx(jn,{children:le(oe.provider,me.accountId)}),s.jsx(jn,{children:s.jsx(ze,{variant:pe.variant,children:pe.label})}),s.jsx(jn,{children:te(Ce?r("{n} 个",{n:Ce}):"—")}),s.jsx(jn,{children:s.jsx("span",{className:"text-[11.5px] leading-[1.5] text-muted-foreground",children:Ne})}),s.jsx(jn,{children:te(KY(me))}),s.jsx(jn,{children:_e(oe)})]},oe.provider)}):he(r("还没有可刷新的提供商:先在账号页添加一个账号"))}return s.jsx(Yt,{open:!0,onOpenChange:(oe,me)=>{if(!oe){if(A){me.cancel();return}t()}},children:s.jsxs(Xt,{className:o?"w-[min(607px,calc(100vw-48px))]":"w-[min(1012px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("获取模型 — {name}",{name:c})})}),s.jsxs(Lt,{children:[s.jsxs("div",{className:"flex flex-wrap items-center gap-2.5",children:[s.jsx("span",{className:"text-[11.5px] text-muted-foreground",children:G}),o&&s.jsxs(Di,{className:"w-[240px] flex-none",children:[s.jsx(Pi,{type:"search",placeholder:r("搜索模型 ID…"),autoComplete:"off",value:B,onChange:oe=>L(oe.currentTarget.value)}),s.jsx(Ii,{"aria-hidden":"true",children:"⌕"})]}),s.jsx("div",{className:"mr-auto"}),o&&s.jsxs(s.Fragment,{children:[s.jsx(ne,{variant:"ghost",size:"sm",onClick:ee,children:r("全选未添加")}),s.jsx(ne,{variant:"ghost",size:"sm",onClick:()=>_(new Set),children:r("清空")})]}),s.jsx(ne,{variant:"outline",size:"sm",title:r("刷新全部家(逐家打上游)"),disabled:A,onClick:()=>{ae()},children:r(w==="load"?"获取中…":P?"重新获取":"获取模型")})]}),s.jsxs(Zy,{id:o?V2.custom:V2.intl,className:"table-fixed",containerClassName:"max-h-[min(48vh,460px)]",children:[s.jsx("colgroup",{children:(o?p0:f0).map(oe=>s.jsx("col",{"data-col":oe,className:o?UY[oe]:HY[oe]},oe))}),s.jsx(AE,{children:s.jsx(Cl,{children:(o?p0:f0).map(oe=>s.jsx(RE,{"data-col":oe,children:VY[oe]},oe))})}),s.jsx(Wy,{className:"[&_tr:last-child>td]:border-b-0",children:o?Te():Ae()})]})]}),s.jsxs(tn,{children:[s.jsx("span",{className:"min-w-0 text-[11.5px] text-muted-foreground",children:v}),s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:ve,children:r(o?"取消":"完成")}),o&&s.jsx(ne,{variant:"default",disabled:!O.size||A,onClick:()=>{ue()},children:w==="import"?r("导入中…"):O.size?r("导入选中的 {n} 个模型",{n:O.size}):r("导入")})]})]})})}let gp=null,$u=null;function Q2(){gp&&(gp.unmount(),gp=null),$u&&($u.remove(),$u=null)}function WY(e){e?.providerId&&(Q2(),$u=document.createElement("div"),document.body.append($u),gp=wn.createRoot($u),gp.render(s.jsx(ZY,{options:e,onClose:Q2})))}window.wbModelsFetchModal={open:WY};const Ho=(e,t)=>t?`${e} ta-${t}`:e;function Z2(e,t){const a=(t||"").trim();if(a)return up().find(o=>Bo(o.provider,e)&&Bo(o.id,a))}const JY="[.pv-row:hover_&_[data-slot=nav-item-count]]:invisible";function eX(e){const t=/x\s*([\d.]+)/i.exec(String(e||""));return t?`${t[1]}x`:String(e||"")}function W2(e){const t=/x\s*([\d.]+)/i.exec(String(e||""));if(!t)return null;const a=Number(t[1]);return Number.isFinite(a)?a:null}const tX={workbuddy:250,"workbuddy-intl":295,raccoon:60,catpaw:85,autoclaw:250,"autoclaw-intl":295,qoder:330,"qoder-intl":350,"cline-free":200,"cline-pass":200,codearts:210,loomy:155,kuku:15,accio:320,"accio-intl":335,trae:170,zcode:285};function nX(e){const t=tX[e];if(t!==void 0)return t;let a=0;for(let o=0;o>>0;return a%360}function sX(e){const t=String(e||"").trim();if(!t)return"?";const a=t.match(/[A-Za-z]/);return(a?a[0]:t[t.length-1]).toUpperCase()}const rX=e=>({"--av-h":String(nX(e))}),g0=s.jsx("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:s.jsxs("g",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("rect",{x:"3.5",y:"3.5",width:"7",height:"7",rx:"1.5"}),s.jsx("rect",{x:"13.5",y:"3.5",width:"7",height:"7",rx:"1.5"}),s.jsx("rect",{x:"13.5",y:"13.5",width:"7",height:"7",rx:"1.5"}),s.jsx("rect",{x:"3.5",y:"13.5",width:"7",height:"7",rx:"1.5"})]})});function aX(e){if(!e)return"";const t=Wt().wbPresetProviders?.list;if(!Array.isArray(t))return"";const a=t.find(o=>o.name===e);return a&&Wt().wbPresetProviders?.iconOf?.(String(a.key||""))||""}function lX(e,t){return as(e)?aX(t):kg[e==="workbuddy-intl"?"workbuddy":e]||""}function qu(e,t,a){const o=a?"":lX(e,t),c=e==="all"||e==="custom-all";return s.jsx("span",{className:Ue("pv-ico",c&&"mute",o&&"img"),style:o?void 0:rX(e),"aria-hidden":"true",children:o?s.jsx("img",{src:o,alt:""}):a??sX(t)})}const oX={supportsToolCall:s.jsx("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:s.jsx("path",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",d:"M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"})}),supportsImages:s.jsx("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:s.jsxs("g",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("rect",{x:"3",y:"3",width:"18",height:"18",rx:"2.5"}),s.jsx("circle",{cx:"8.5",cy:"8.5",r:"1.5"}),s.jsx("path",{d:"M21 15l-5-5L5 21"})]})}),supportsVideo:s.jsx("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:s.jsxs("g",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("circle",{cx:"12",cy:"12",r:"9"}),s.jsx("path",{d:"M10 8.5l6 3.5-6 3.5z"})]})}),supportsReasoning:s.jsx("svg",{viewBox:"0 0 24 24","aria-hidden":"true",children:s.jsxs("g",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[s.jsx("path",{d:"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"}),s.jsx("path",{d:"M9 18h6M10 22h4"})]})})};function iX(e,t,a,o){return o==="enabled"&&!fp(e)||o==="disabled"&&fp(e)||o==="mapped"&&!(e.aliases||[]).length||a!=="all"&&(e.provider||"")!==a?!1:t?[e.id,e.name,...e.aliases||[]].join(" ").toLowerCase().includes(t):!0}function cX(){const e=x.useSyncExternalStore(WK,op),t=ex(e),a=as(t)||t==="custom-all",[o,c]=x.useState(null),[d,f]=x.useState(!1),[p,h]=x.useState(new Set),b=a&&!Vu()&&e0()&&!Lo(t);x.useEffect(()=>{aY(),tx(),j2(),Wt().wbApp?.currentPage==="gateway"&&T2().then(()=>Wt().wbProviders?.refreshCustom?.()).then(()=>{ec()})},[]),x.useEffect(()=>{jY()});const y=n0(),v=Array.isArray(y.models)?y.models:[],j=e.search.trim().toLowerCase(),w=v.filter(J=>iX(J,j,t,e.stateFilter)),S=e.sort,N=C2(),k=N.length,A=t==="all"?r("全部"):t==="custom-all"?r("自定义 · 全部"):a?String(Lo(t)?.name||t):s0().get(t)?.label||c0(t),P=v.filter(fp).length,R=v.filter(J=>(J.aliases||[]).length>0).length,C=hg(w.length,"models"),E=v.filter(J=>p.has(nc(J))),D=(J,Y)=>{const re=new Set(p);Y?re.add(J):re.delete(J),h(re)},I=w.map(nc),O=I.length>0&&I.every(J=>p.has(J)),_=I.some(J=>p.has(J)),z=s.jsx(Pr,{checked:O,indeterminate:!O&&_,disabled:!I.length,"aria-label":r("全选当前筛选结果"),title:r("全选当前筛选出的模型(跨分页);再点取消"),onCheckedChange:J=>{const Y=new Set(p);for(const re of I)J===!0?Y.add(re):Y.delete(re);h(Y)}});function H(){const J=s0(),Y=Wi(),re=tY(),M=[...J.values()].reduce((ce,W)=>ce+W.n,0),U=r("全部({n} 家)",{n:J.size}),Z=[...re.values()].reduce((ce,W)=>ce+W.n,0),ae=r("全部({n} 家)",{n:[...re.values()].filter(ce=>ce.n>0).length});return s.jsxs("aside",{className:"prov-rail",id:"prov-rail","aria-label":r("按提供商选择"),children:[s.jsxs("div",{className:"rail-scroll",children:[s.jsx("div",{className:"rail-label",children:r("内置提供商")}),s.jsx(uu,{active:t==="all","data-provider":"all",title:U,count:M,icon:qu("all","",g0),className:"shadow-none",onClick:()=>pp("all"),children:U}),[...J].map(([ce,W])=>s.jsx(uu,{active:t===ce,"data-provider":ce,title:W.label,count:W.n,icon:qu(ce,W.label),className:"shadow-none",onClick:()=>pp(ce),children:W.label},ce)),s.jsx("div",{className:"rail-label",children:r("自定义提供商")}),Y.length?[...Z>0?[s.jsx(uu,{active:Vu(),"data-provider":"custom-all",title:ae,count:Z,icon:qu("custom-all","",g0),className:"shadow-none",onClick:()=>pp("custom-all"),children:ae},"custom-all")]:[],...Y.map(ce=>{const W=String(ce.id||""),ue=String(ce.name||W);return s.jsxs("div",{className:"pv-row",children:[s.jsx(uu,{active:t===W,"data-provider":W,title:ue,count:re.get(W)?.n??0,icon:qu(W,ue),className:Ue("shadow-none",JY),onClick:()=>pp(W),children:ue}),s.jsx("button",{type:"button",className:"pv-del","aria-label":r("删除自定义提供商"),title:r("删除这个自定义提供商(连同名下账号)"),onClick:()=>{mY(W)},children:"×"})]},W)})]:s.jsx("div",{className:"rail-empty",children:r("还没有自定义提供商")})]}),s.jsx("div",{className:"rail-foot",children:s.jsx(uu,{variant:"add",id:"rail-add-custom",title:r("新建一个自定义提供商(同时创建它的第一个账号)"),onClick:()=>hY(),children:r("+ 新建自定义提供商")})})]})}function B(J,Y,re,M){const U=u0(J,Y,re);return s.jsx(ne,{variant:U?"secondary":"dashed",size:"2xs",disabled:M,className:"font-mono font-bold shadow-none",title:U?r("思考等级 {level}(点击修改)",{level:U}):r("设置思考等级(当前未绑定)"),onClick:()=>A2({alias:J,target:Y,provider:re}),children:U||r("+等级")})}function L(J){const Y=J.provider||"",re=Fu(J),[M,...U]=re;async function Z(ce){await Wt().wbConfirm?.ask?.({title:r("删除映射"),html:r("确定删除映射「{alias} → {target}」?",{alias:Yw(ce),target:Yw(J.id)}),okText:r("删除"),okClass:"danger"})&&o0(i0(ce,J.id,Y),()=>lY(Y,ce,J.id),r("映射已删除"))}const ae=ce=>{const W=ce.alias,ue=ce.enabled!==!1,ve=e.pending.has(i0(W,J.id,Y));return s.jsxs("span",{className:Ue("alias",!ue&&"map-off"),children:[s.jsx(ns,{size:"sm",checked:ue,disabled:ve,title:r(ue?"映射已启用,点击关闭":"映射已关闭,点击启用"),"aria-label":r("{action}映射 {alias}",{action:r(ue?"关闭":"启用"),alias:W}),onCheckedChange:ee=>{o0(i0(W,J.id,Y),()=>rx(Y,W,J.id,{enabled:ee}),r(ee?"映射已启用":"映射已关闭"))}}),s.jsx("span",{className:"t","data-copy":W,title:r("点击复制:{name}",{name:W}),children:W}),ce.isDefault?s.jsx("span",{className:"binding-default",children:r("默认")}):null,B(W,J.id,Y,ve),ce.isDefault?null:s.jsx(ne,{variant:"ghost",size:"icon-2xs",className:"text-[12px] leading-none",disabled:ve,title:r("删除映射 {alias}",{alias:W}),onClick:()=>{Z(W)},children:"×"})]},`${W}${ce.isDefault?"d":"m"}`)};return s.jsxs("div",{className:"aliases",children:[s.jsxs("div",{className:"alias-row",children:[M?ae(M):null,s.jsx(ne,{variant:"dashed",size:"xs",onClick:()=>A2({target:J.id,provider:Y}),children:r("+ 映射")})]}),U.map(ae)]})}function F(J){if(J.source==="manual")return s.jsxs("span",{className:"src manual",title:r("手动登记的上游模型;移除它会直接删掉这条登记"),children:[s.jsx("i",{"aria-hidden":"true"}),r("手动")]});if(!J.source&&as(J.provider))return s.jsxs("span",{className:"src manual",title:r("用户登记的自定义模型;整份清单只属于这一家"),children:[s.jsx("i",{"aria-hidden":"true"}),r("自定义")]});if(J.source!=="remote"&&J.source!=="builtin")return s.jsx("span",{className:"rate",children:"—"});const Y=J.source==="remote",re=KK(Number(J.refreshedAt)||0),M=Y?re?r("来自上游目录接口,清单拉取于 {at};刷新失败时保留上一份成功结果",{at:re}):r("来自上游目录接口;刷新失败时保留上一份成功结果"):r("上游目录尚未拉到,用的是内置静态清单;点「刷新模型清单」可重试");return s.jsxs("span",{className:Ue("src",Y?"remote":"builtin"),title:M,children:[s.jsx("i",{"aria-hidden":"true"}),r(Y?"远程":"内置")]})}function $(J){const Y=Gw(J),re=qw(J.capOverrides),M=typeof Y.maxInputTokens=="number"?Y.maxInputTokens:null,U=typeof Y.maxOutputTokens=="number"?Y.maxOutputTokens:null,Z=r("上下文窗口 {input} / 最大输出 Token {output} —— 点击修改模型能力",{input:sp(M),output:sp(U)})+X(J);return s.jsxs("button",{type:"button",className:"caps-open",title:Z,onClick:()=>R2(J.provider||"",J.id),children:[s.jsxs("span",{className:Ue("cap-num",M===null&&"unset",Hu(J,"maxInputTokens")==="modelsdev"&&"filled"),children:[n2(M),re.includes("maxInputTokens")?s.jsx("i",{className:"cap-mark",title:r("已覆盖上游值")}):null]}),s.jsx("span",{className:"cap-sep",children:"/"}),s.jsxs("span",{className:Ue("cap-num",U===null&&"unset",Hu(J,"maxOutputTokens")==="modelsdev"&&"filled"),children:[n2(U),re.includes("maxOutputTokens")?s.jsx("i",{className:"cap-mark",title:r("已覆盖上游值")}):null]})]})}function X(J){const Y=["maxInputTokens","maxOutputTokens"].filter(re=>Hu(J,re)==="modelsdev").map(re=>Bu[re]);return Y.length?r(";其中 {keys} 由 models.dev 补齐",{keys:Y.join(r("、"))}):""}function K(J){const Y=Gw(J),re=qw(J.capOverrides);return s.jsx("button",{type:"button",className:"caps-open caps-badges",title:r("对下游声明的能力(工具调用 / 图片识别 / 视频识别 / 支持思考)—— 点击修改"),onClick:()=>R2(J.provider||"",J.id),children:e2.map(M=>{const U=Y[M]??null,Z=Kw(U),ae=re.includes(M),ce=Hu(J,M);return s.jsxs("span",{className:Ue("cap-dot",Z,ce==="modelsdev"&&"filled",ae&&"marked"),title:zK(M,U,ae,ce),children:[oX[M],ae?s.jsx("i",{className:"cap-mark"}):null]},M)})})}function q(J,Y,re){async function M(){await Wt().wbConfirm?.ask?.({title:r("移除自定义模型"),html:r("确定移除自定义模型「{id}」?这条登记会被直接移除,之后 /v1/models 不再广告它、请求它也会被拒。",{id:Yw(Y.id)}),okText:r("移除"),okClass:"danger"})&&o0(nc(Y),()=>E2(Y.provider||"",Y.id),as(Y.provider||"")?r("模型已移除"):r("自定义模型已移除"))}switch(J.key){case"check":return s.jsx("td",{className:Ho("cell-check",J.align),children:s.jsx(Pr,{checked:p.has(nc(Y)),title:r("勾选后可批量操作"),"aria-label":r("勾选后可批量操作"),onCheckedChange:U=>D(nc(Y),U===!0)})});case"model":return s.jsxs("td",{className:Ho("cell-model",J.align),children:[s.jsx("div",{className:"mid",children:s.jsx("span",{className:"t","data-copy":Y.id,title:r("点击复制:{name}",{name:Y.id}),children:Y.id})}),Y.name&&Y.name!==Y.id?s.jsx("div",{className:"mname",children:Y.name}):null]});case"rate":return s.jsx("td",{className:Ho("cell-rate",J.align),children:s.jsx("span",{className:"rate",children:Y.credits?eX(Y.credits):"—"})});case"source":return s.jsx("td",{className:Ho("cell-source",J.align),children:F(Y)});case"budget":return s.jsx("td",{className:Ho("cell-budget",J.align),children:$(Y)});case"caps":return s.jsx("td",{className:Ho("cell-caps",J.align),children:K(Y)});case"alias":return s.jsx("td",{className:Ho("cell-alias",J.align),children:L(Y)});case"act":{const U=IY(Y.provider||""),Z=Y.source==="manual"||as(Y.provider||"");return s.jsx("td",{className:Ho("cell-act r",J.align),children:s.jsxs("div",{className:"row-actions",children:[s.jsx(ne,{variant:"ghost",size:"sm",disabled:!!U,title:U||r("以这个上游模型发一次最小请求,走真实转发链路"),onClick:()=>c({provider:Y.provider||"",id:Y.id}),children:r("测试")}),Z?s.jsx(ne,{variant:"ghost",size:"sm",className:"text-destructive",disabled:re,onClick:()=>{M()},children:r("移除")}):null]})})}default:return null}}function Q(J){const Y=e.pending.has(nc(J)),re=p.has(nc(J));return s.jsx("tr",{className:Ue(re&&"sel"),children:N.map(M=>s.jsx(x.Fragment,{children:q(M,J,Y)},M.key))},`${J.provider||""}${J.id}`)}function G(){if(!v.length){const Z=a?e0()?Vu()?r("自定义提供商还没有模型:先在左栏选一家,点「添加模型」登记,或「获取上游模型」从上游拉取"):r(b?"该提供商已不存在(可能已被删除),请刷新列表":"这家还没有模型:点「添加模型」登记,或「获取上游模型」从上游拉取"):r("加载中…"):e.data?r("暂无模型(请先添加账号)"):r("加载中…");return s.jsx("tr",{children:s.jsx("td",{colSpan:k,className:"empty",children:Z})})}if(!w.length)return s.jsx("tr",{children:s.jsx("td",{colSpan:k,className:"empty",children:j?r("没有匹配「{keyword}」的模型",{keyword:j}):r("没有匹配当前筛选的模型")})});const J=t==="all"||Vu(),Y=J&&!j&&e.stateFilter==="all"&&!C.paged,re=new Map;for(const Z of w){const ae=Z.provider||"";re.has(ae)||re.set(ae,{label:Z.providerLabel||ae||r("未知"),items:[]}),re.get(ae)?.items.push(Z)}const M=[];let U=0;for(const[Z,ae]of re){ae.items.sort((te,ke)=>{const le=+!fp(te)-+!fp(ke);if(le!==0||S==="default")return le;const de=W2(te.credits),Se=W2(ke.credits);return de===null||Se===null?de===Se?0:de===null?1:-1:S==="rate-asc"?de-Se:Se-de});const ce=e.expanded.has(Z)||!Y,W=ce?ae.items:ae.items.slice(0,h2),ue=ae.items.length-W.length,ve=C.paged?Math.max(0,C.rangeStart-1-U):0,ee=C.paged?Math.min(W.length,C.rangeEnd-U):W.length,he=ve0?r("(续)"):"",r("{n} 个模型",{n:ae.items.length})]})},`group:${Z}`));for(const te of he)M.push(Q(te));U+=W.length,ue>0?M.push(s.jsx("tr",{className:"tr-more",children:s.jsx("td",{colSpan:k,children:s.jsx(ne,{variant:"ghost",size:"sm",onClick:()=>cY(Z),children:r("展开其余 {n} 个模型 ▾",{n:ue})})})},`more:${Z}`)):ce&&Y&&ae.items.length>h2&&M.push(s.jsx("tr",{className:"tr-more",children:s.jsx("td",{colSpan:k,children:s.jsx(ne,{variant:"ghost",size:"sm",onClick:()=>uY(Z),children:r("收起 ▴")})})},`collapse:${Z}`))}return M}return s.jsxs(s.Fragment,{children:[s.jsx("section",{className:"panel",children:s.jsxs("div",{className:"mm",children:[H(),s.jsxs("div",{className:"mm-main",children:[s.jsxs("div",{className:"panel-head",children:[s.jsxs("div",{className:"mm-titlebar",children:[s.jsxs("div",{className:"mm-title",children:[t==="all"||t==="custom-all"?qu(t,"",g0):qu(t,A),s.jsx("h2",{children:A}),s.jsxs("span",{className:"mm-meta",children:[s.jsx("b",{children:v.length}),r(" 个模型 · 已启用 "),s.jsx("b",{children:P}),r(" · 有映射 "),s.jsx("b",{children:R})]})]}),s.jsxs("div",{className:"head-actions",children:[s.jsx(ne,{id:"btn-refresh-models",title:as(t)?r("从这一家的上游拉一份模型清单,勾选要哪些再导入(已添加的不会重复导入)"):r("刷新模型管理页里各提供商的远程模型目录,逐家结果列在弹窗里"),onClick:()=>M2(),children:r("获取模型")}),s.jsx(ne,{id:"btn-add-custom-model",variant:"outline",size:"sm",onClick:()=>xY(),children:r("+ 添加模型")})]})]}),s.jsxs("div",{className:"mm-filters",children:[s.jsx(En,{options:XK,value:e.stateFilter,onValueChange:dY,"aria-label":r("按状态筛选"),className:"shrink-0"}),s.jsx(En,{options:ZK,value:e.sort,onValueChange:fY,"aria-label":r("按倍率排序"),className:"shrink-0"}),s.jsxs(Di,{className:"w-full min-[768px]:w-[240px] flex-none min-[768px]:flex-none",id:"models-search",children:[s.jsx(Pi,{type:"search",placeholder:r("搜索模型 ID / 名称 / 映射名…"),"aria-label":r("搜索模型"),autoComplete:"off",value:e.search,onChange:J=>pY(J.currentTarget.value)}),s.jsx(Ii,{"aria-hidden":"true",children:"⌕"})]}),E.length>0?s.jsxs("span",{className:"batch-chip",children:[r("已选 "),s.jsx("b",{children:E.length}),r(" 个"),s.jsx(ne,{id:"btn-batch-models",variant:"outline",size:"xs",title:r("对选中的 {n} 个模型执行批量操作",{n:E.length}),onClick:()=>f(!0),children:r("批量操作")})]}):null]})]}),s.jsx("div",{className:"models-table-wrap",children:s.jsxs("table",{className:"models-table",ref:J=>yY(J),children:[s.jsxs("colgroup",{children:[s.jsx("col",{className:"c-check","data-col":"check"}),s.jsx("col",{className:"c-model","data-col":"model"}),s.jsx("col",{className:"c-rate","data-col":"rate"}),s.jsx("col",{className:"c-source","data-col":"source"}),s.jsx("col",{className:"c-budget","data-col":"budget"}),s.jsx("col",{className:"c-caps","data-col":"caps"}),s.jsx("col",{className:"c-alias","data-col":"alias"}),s.jsx("col",{className:"c-act","data-col":"act"})]}),s.jsx("thead",{children:s.jsxs("tr",{children:[s.jsx("th",{"data-col":"check",children:z}),s.jsx("th",{"data-col":"model",children:r("上游模型")}),s.jsx("th",{"data-col":"rate",children:r("倍率")}),s.jsx("th",{"data-col":"source",children:r("来源")}),s.jsxs("th",{"data-col":"budget",children:[r("上下文 / 输出"),s.jsx("span",{className:"th-help","aria-hidden":"true",title:r("数字带虚线底 = 由 models.dev 补齐;右上角小点 = 你手动覆盖过;— = 未声明。点任意一格可改。"),children:s.jsx("svg",{viewBox:"0 0 24 24",children:s.jsxs("g",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",children:[s.jsx("circle",{cx:"12",cy:"12",r:"9"}),s.jsx("path",{d:"M12 16v-4"}),s.jsx("path",{d:"M12 8h.01"})]})})})]}),s.jsxs("th",{"data-col":"caps",children:[r("能力"),s.jsx("span",{className:"th-help","aria-hidden":"true",title:r("圆点带内侧描边 = 由 models.dev 补齐;右上角小点 = 你手动覆盖过;虚线圈 = 未声明。悬停某一枚可看逐项出处。"),children:s.jsx("svg",{viewBox:"0 0 24 24",children:s.jsxs("g",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",children:[s.jsx("circle",{cx:"12",cy:"12",r:"9"}),s.jsx("path",{d:"M12 16v-4"}),s.jsx("path",{d:"M12 8h.01"})]})})})]}),s.jsxs("th",{"data-col":"alias",children:[r("模型映射"),s.jsx("span",{className:"th-help","aria-hidden":"true",title:r("每条映射 = 对外名 → 上游模型;原始 ID 与别名有独立开关,默认绑定永远存在(没有映射时是合成的那条)。"),children:s.jsx("svg",{viewBox:"0 0 24 24",children:s.jsxs("g",{fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",children:[s.jsx("circle",{cx:"12",cy:"12",r:"9"}),s.jsx("path",{d:"M12 16v-4"}),s.jsx("path",{d:"M12 8h.01"})]})})})]}),s.jsx("th",{className:"r","data-col":"act",children:r("操作")})]})}),s.jsx("tbody",{id:"models",children:G()})]})}),s.jsx($i,{className:"models-panel-foot",leading:Vu()?s.jsxs(s.Fragment,{children:[r("这里是"),s.jsx("b",{children:r("全部自定义提供商")}),r("的模型合集:每家的清单互相独立,别名也只在自己那家内生效;要改名称 / 协议 / Base URL,到账号页该家账号的「设置」→ 提供商一栏。")]}):a?s.jsxs(s.Fragment,{children:[r("自定义提供商的清单"),s.jsx("b",{children:r("只属于这一家")}),r(":这里的模型不会出现在其他家,别名也只在这一家内生效。改名称 / 协议 / Base URL 在账号页该家账号的「设置」→ 提供商一栏;整家不要了,鼠标移到左栏这家上点 × 删除(连同名下账号)。")]}):void 0,total:w.length,range:C.paged?{start:C.rangeStart,end:C.rangeEnd}:null,page:C.page,pageCount:C.pageCount,size:C.size,onSizeChange:C.setSize,onPageChange:C.goto})]})]})}),e.mapping?s.jsx(dX,{context:e.mapping,onClose:gY}):null,e.customModel?s.jsx(fX,{initial:e.customModel,onClose:bY}):null,e.capability?s.jsx(CY,{context:e.capability,onClose:vY}):null,o?s.jsx(LY,{target:o,onClose:()=>c(null)}):null,d?s.jsx(BY,{models:E,onClose:()=>f(!1),onDone:()=>{h(new Set),f(!1)}}):null]})}const uX=r("绑定在「对外名 → 上游模型」这一条映射上的思考等级(列表照抄 OmniProxy 的手动绑定),选「不覆盖」表示不给这条映射指定等级。它会跟着这条映射注入转发,由承载的那家翻译成自己的档位字段(CatPaw 归并成 low/high/max,Qoder 按模型声明的档位归一)。以下几种情况故意不注入:① off / none(关闭思考)—— 本项目没有安全的表达方式;② 表外的自定义等级(各家能力范围不同,无法判断上游收不收);③ 客户端请求体里已经自己指定了档位(那是更明确的意图,绑定不覆盖);④ 这家上游不认识档位字段(如 WorkBuddy / 小浣熊 / AutoClaw / Cline)。注入与跳过都会写进详细日志。");function J2(e,t,a){const o=k2(e),c=(t||"").trim(),d=o.find(f=>f.id.toLowerCase()===c.toLowerCase());return d?d.id:c||o[0]?.id||""}function dX({context:e,onClose:t}){const a=!!e.alias,[o,c]=x.useState(()=>e.provider||r0()[0]?.id||""),[d,f]=x.useState(e.alias||""),[p,h]=x.useState(()=>J2(o,e.target)),[b,y]=x.useState(""),[v,j]=x.useState(!1),[w,S]=x.useState(()=>{const _=ap(op().data,Z2(o,p)),z=a?u0(e.alias,e.target,e.provider):"";return z&&_.includes(z)?{select:z,custom:""}:{select:z?fa:"",custom:z}}),N=ap(op().data,Z2(o,p)),k=(()=>{const _=r0();for(const z of[e.provider,o])z&&!_.some(H=>H.id===z)&&_.push({id:z,label:r(Wt().wbProviders?.labelOf?.(z)||z)});return _})(),A=(()=>{const _=k2(o),z=(p||"").trim();return z&&!_.some(H=>H.id.toLowerCase()===z.toLowerCase())&&_.unshift({id:z,label:r("{name}(不在该家当前清单里)",{name:z}),off:!0}),_})(),P=k.find(_=>_.id===o)?.label||o||r("(全局)"),R=w.select&&w.select!==fa&&!N.includes(w.select)?{select:fa,custom:w.custom||w.select}:w,C=R.select===fa?R.custom.trim():R.select,E=R.select===fa;function D(_){c(_),h(J2(_,p))}async function I(){if(v)return;const _=a?e.alias||"":d.trim(),z=p;if(!_){y(r("请填写对外映射名"));return}if(!z){y(r("该提供商当前没有可选的上游模型"));return}if(!o){y(r("请选择提供商"));return}if(!a&&Bo(_,z)&&up().some(H=>Bo(H.id,z)&&Bo(H.provider,o))){y(r("原始 ID 已作为默认绑定,请直接使用该绑定的开关或等级按钮"));return}j(!0),y(r("保存中…"));try{tc(await rx(o,_,z,{reasoning:C})),j(!1),t();const H=C?r(" · 思考等级 {level}",{level:C}):"",B=r(Wt().wbProviders?.labelOf?.(o)||o);Nr(a?r("✅ 已更新 {name} 的思考等级",{name:_}):r("✅ 已添加映射 {name} → {target}({label}){suffix}",{name:_,target:z,label:B,suffix:H}))}catch(H){y(r("保存失败:{message}",{message:zo(H)})),j(!1)}}const O=_=>{_.key==="Enter"&&(_.preventDefault(),I())};return s.jsx(Yt,{open:!0,onOpenChange:(_,z)=>{if(!_){if(v){z.cancel();return}t()}},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r(a?"设置思考等级":"添加模型映射")})}),s.jsxs(Lt,{children:[s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"mapping-provider",children:r("提供商")}),s.jsxs(ut,{value:o,disabled:!0,onValueChange:_=>D(String(_)),children:[s.jsx(ft,{id:"mapping-provider",className:"w-full",children:s.jsx(dt,{children:k.find(_=>_.id===o)?.label||o})}),s.jsx(pt,{children:k.map(_=>s.jsx($e,{value:_.id,children:_.label},_.id))})]})]}),s.jsxs("div",{className:"grid grid-cols-[1fr_24px_1fr] items-end gap-2.5",children:[s.jsxs("div",{className:"flex min-w-0 flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"mapping-alias",children:r("对外映射名(下游请求时用)")}),s.jsx(Fe,{id:"mapping-alias",maxLength:128,placeholder:r("例如 gpt-4o"),autoComplete:"off",autoFocus:!a,value:d,disabled:a,onChange:_=>f(_.currentTarget.value),onKeyDown:O})]}),s.jsx("div",{className:"pb-2 text-center text-muted-foreground",children:"→"}),s.jsxs("div",{className:"flex min-w-0 flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"mapping-upstream",children:r("转发到上游模型")}),s.jsxs(ut,{value:p,disabled:!0,onValueChange:_=>h(String(_)),children:[s.jsx(ft,{id:"mapping-upstream",className:"w-full",children:s.jsx(dt,{children:A.find(_=>_.id===p)?.label||p})}),s.jsx(pt,{children:A.map(_=>s.jsxs($e,{value:_.id,children:[_.label,_.off?r("(已禁用)"):""]},_.id))})]})]})]}),s.jsxs("div",{className:"rounded-md border border-border bg-surface-inset px-3 py-2.5 font-mono text-[12px] text-subtle",children:[r("下游请求 "),s.jsx("b",{className:"text-primary-fg",children:d.trim()||r("<对外名>")}),r(" → 转发")," ",s.jsx("b",{className:"text-primary-fg",children:p||r("<上游模型>")}),r("({label})",{label:P}),C?s.jsxs(s.Fragment,{children:[r(" · 思考等级 "),s.jsx("b",{className:"text-primary-fg",children:C})]}):null]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsxs("div",{className:"flex items-center gap-[7px]",children:[s.jsx(Je,{htmlFor:"mapping-reasoning",children:r("思考等级")}),s.jsxs(Ui,{children:[s.jsx(Vi,{render:s.jsx("span",{className:"tip-q",tabIndex:0,"aria-label":r("思考等级的注入规则")}),children:"?"}),s.jsx(Fi,{children:uX})]})]}),s.jsxs(ut,{value:R.select,onValueChange:_=>{const z=String(_);S(H=>({...H,select:z}))},children:[s.jsx(ft,{id:"mapping-reasoning",className:"w-full",autoFocus:a,children:s.jsx(dt,{children:R.select===fa?r("自定义等级…"):R.select||r("不覆盖")})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:r("不覆盖")}),N.map(_=>s.jsx($e,{value:_,children:_},_)),s.jsx($e,{value:fa,children:r("自定义等级…")})]})]}),E?s.jsx(Fe,{id:"mapping-reasoning-custom",maxLength:32,autoFocus:!0,placeholder:r("自定义等级(例如 custom-high)"),autoComplete:"off",disabled:v,value:R.custom,onChange:_=>S(z=>({...z,custom:_.currentTarget.value})),onKeyDown:O}):null]}),s.jsxs("p",{className:"text-xs leading-[1.65] text-subtle",children:[r("对外名可自由命名,允许与上游模型 ID 同名(同名时该上游的原生路由优先,映射作兜底); 支持字母、数字与 "),s.jsx("code",{children:"- _ . / :"}),r("。同一对外名可在多个提供商各添加一条: 下游用同一个名字请求,网关按账号优先级主备切换,失败自动落到下一个提供商。")]}),s.jsx("div",{className:"min-h-[18px] text-xs text-subtle",children:b})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:t,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:v,onClick:()=>{I()},children:r(a?"保存等级":"保存映射")})]})]})})}function fX({initial:e,onClose:t}){const[a,o]=x.useState(e.provider),[c,d]=x.useState(""),[f,p]=x.useState(""),[h,b]=x.useState(!1),y=(()=>{const N=rY();return a&&!N.some(k=>k.id===a)&&N.push({id:a,label:r(Wt().wbProviders?.labelOf?.(a)||a)}),N})(),v=y.some(N=>N.id===a)?a:y[0]?.id||"",j=y.find(N=>N.id===v)?.label||v||r("(未选)");async function w(){if(h)return;const N=c.trim();if(!v){p(r("请选择提供商"));return}if(!N){p(r("请填写上游模型 ID"));return}b(!0),p(r("保存中…"));try{const k=await oY(v,N);tc(k),b(!1),t();const A=y.find(P=>P.id===v)?.label||v;if(as(Uu()))Nr(r("✅ 已登记模型 {name}({label})",{name:N,label:A}));else{const P=k;Array.isArray(P?.models)&&P.models.some(C=>C.id===N&&(C.provider||"")===v)?Nr(r("✅ 已登记自定义模型 {name}({label})",{name:N,label:A})):Nr(r("✅ 已登记 {name}({label}),但该提供商还没有可用账号,这一行要加上账号后才会显示",{name:N,label:A}),"err")}}catch(k){p(r("保存失败:{message}",{message:zo(k)})),b(!1)}}const S=N=>{N.key==="Enter"&&(N.preventDefault(),w())};return s.jsx(Yt,{open:!0,onOpenChange:(N,k)=>{if(!N){if(h){k.cancel();return}t()}},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("添加自定义模型")})}),s.jsxs(Lt,{children:[s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"custom-model-provider",children:r("提供商")}),s.jsxs(ut,{value:v,disabled:e.locked,onValueChange:N=>o(String(N)),children:[s.jsx(ft,{id:"custom-model-provider",className:"w-full",children:s.jsx(dt,{children:j})}),s.jsx(pt,{children:y.map(N=>s.jsx($e,{value:N.id,children:N.label},N.id))})]})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"custom-model-id",children:r("上游模型 ID")}),s.jsx(Fe,{id:"custom-model-id",maxLength:128,placeholder:r("例如 gpt-5.5-preview"),autoComplete:"off",spellCheck:!1,autoFocus:!0,value:c,onChange:N=>d(N.currentTarget.value),onKeyDown:S})]}),s.jsxs("div",{className:"rounded-md border border-border bg-surface-inset px-3 py-2.5 font-mono text-[12px] text-subtle",children:[r("在 "),s.jsx("b",{className:"text-primary-fg",children:j}),r(" 上登记上游模型")," ",s.jsx("b",{className:"text-primary-fg",children:c.trim()||r("<上游模型 ID>")}),r("(登记后即可用这个名字请求)")]}),s.jsxs("p",{className:"text-xs leading-[1.65] text-subtle",children:[r("填上游真正认识的那个模型 ID(不是给下游用的名字)。登记后它会进入该家的模型清单: 表格里出现这一行、"),s.jsx("code",{children:"/v1/models"}),r(" 会广告它、请求它也会被转发到这家。 支持字母、数字与 "),s.jsx("code",{children:"- _ . / :"}),r("。")]}),s.jsxs("p",{className:"text-xs leading-[1.65] text-subtle",children:[r("「来源」列会标成"),s.jsx("b",{children:r("手动")}),r(",与远程目录 / 内置清单区分开。删除自定义模型是"),s.jsx("b",{children:r("直接移除")}),r("这条登记(不像内置模型那样只是隐藏),因为它的存在完全由这次登记决定。")]}),s.jsx("div",{className:"min-h-[18px] text-xs text-subtle",children:f})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:t,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:h,onClick:()=>{w()},children:r("保存")})]})]})})}const eD='.page[data-page="gateway"]';let x0=null;function tD(){if(x0)return;const e=document.querySelector(eD);e&&(e.replaceChildren(),x0=wn.createRoot(e),x0.render(s.jsx(cX,{})))}document.querySelector(eD)?tD():document.addEventListener("DOMContentLoaded",tD,{once:!0});const nD=3065;function pX(){if(nl().workbuddyDesktop?.platform!=="web")return null;const e=typeof window>"u"?"":window.location?.origin;return e&&e!=="null"?e:null}function sD(e,t){return pX()??(e?`http://127.0.0.1:${e}`:t)}function nl(){return window}function ax(e){return e instanceof Error?e.message:String(e)}function Gu(e,t){nl().wbApp?.toast?.(e,t)}let xp={backend:null,gatewayBase:sD(0,`http://127.0.0.1:${nD}`)};const b0=new Set;function v0(){return xp}function mX(e){return b0.add(e),()=>{b0.delete(e)}}function hX(e){xp=e;for(const t of b0)t()}function rD(){return xp.gatewayBase.replace(/^https?:\/\//,"")}function gX(){return xp.backend?.ready===!0}function aD(e){const t=(a,o)=>{const c=document.getElementById(a);c&&(c.textContent=o)};t("api-base",`${e}/v1`),t("api-chat",`POST ${e}/v1/chat/completions`),t("api-responses",`POST ${e}/v1/responses`),t("api-messages",`POST ${e}/v1/messages`),t("api-models",`GET ${e}/v1/models`)}let y0=!1;async function Ku(){if(!y0){y0=!0;try{const e=nl().workbuddyDesktop;if(!e)throw new Error("后端桥不可用");const t=await e.getBackendStatus(),a=Number(t?.port)||0,o=v0(),c={backend:t??null,gatewayBase:sD(a,o.gatewayBase)};c.gatewayBase!==o.gatewayBase&&aD(c.gatewayBase),hX(c),nl().wbApp?.renderTopbarStatus?.()}catch(e){console.warn("读取后端状态失败:",ax(e))}finally{y0=!1}}}function xX(){aD(xp.gatewayBase),Ku()}async function lD(e){Gu(e||r("正在重启…"));try{await nl().workbuddyDesktop?.restartApp()}catch(t){Gu(r("重启失败:{reason}",{reason:ax(t)}),"err")}}const bX={idle:r("结束占用进程"),querying:r("查询中…"),ending:r("结束中…")};function vX({container:e}){const{backend:t,gatewayBase:a}=x.useSyncExternalStore(mX,v0),[o,c]=x.useState("idle"),d=t?.failure||null,f=rD(),p=t?.ready;let h="live off",b=r("正在检查… · {port}",{port:f});p===!0?(h="live pulse",b=r("网关运行中 · {port}",{port:f})):d?(h="live bad",b=r("{label} · {port}",{label:d.label||r("网关启动失败"),port:f})):p===!1&&(b=r("网关启动中… · {port}",{port:f}));const y=d?d.message??"":p===!0?r("本地网关正在监听 {url},可直接调用 OpenAI 兼容接口",{url:a}):r("正在确认网关是否已就绪");x.useEffect(()=>{Ku()},[]),x.useEffect(()=>{const j=nl().workbuddyDesktop?.onBackendError?.(()=>{Ku()});return typeof j=="function"?j:void 0},[]),x.useEffect(()=>{e.title=y},[e,y]);async function v(){if(o!=="idle")return;const j=nl().workbuddyDesktop;if(j){c("querying");try{const w=await j.getPortOccupant(),S=w?.occupant;if(!S){Gu(r("端口 {port} 上没有查到监听进程,请改用「更换端口」",{port:w?.port??""}),"err");return}const N=[r("即将结束以下进程:"),"",r("进程名:{name}",{name:String(S.name)}),r("PID:{pid}",{pid:String(S.pid)}),r("路径:{path}",{path:S.path||r("(未知)")}),"",r("结束它会强制退出该程序未保存的数据。确认继续?")],k=nl().wbConfirm?.ask;if(!k||!await k({title:r("结束占用端口的进程"),text:N.join(` +`),okText:r("结束进程"),okClass:"danger"}))return;c("ending"),(await j.endPortOccupant())?.released?(Gu(r("已结束进程 {name}(PID {pid}),端口已释放",{name:String(S.name),pid:String(S.pid)})),await k({title:r("重启程序"),text:r("端口已释放。现在重启程序让网关用这个端口启动?"),okText:r("重启")})?await lD(r("重启中…")):await Ku()):(Gu(r("进程已结束,但端口仍被占用,建议改用「更换端口」"),"err"),await Ku())}catch(w){Gu(r("结束失败:{reason}",{reason:ax(w)}),"err")}finally{c("idle")}}}return s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"status-line",children:[s.jsx("span",{className:h}),s.jsx("span",{className:"txt",children:b})]}),d?.conflict?s.jsxs("div",{className:"status-actions",children:[d.canEndOccupant===!0?s.jsx(ne,{size:"sm",disabled:o!=="idle",onClick:()=>{v()},children:bX[o]}):null,s.jsx(ne,{size:"sm",onClick:()=>{wX()},children:r("更换端口")})]}):null]})}let j0=null;function oD(){if(j0)return;const e=document.getElementById("sidebar-status");e&&(e.replaceChildren(),j0=wn.createRoot(e),j0.render(s.jsx(vX,{container:e})))}const yX={idle:r("保存并重启"),checking:r("校验中…"),saving:r("保存中…")};function jX({status:e,onClose:t}){const a=Number(e?.port)||nD,[o,c]=x.useState(()=>a<65535?String(a+1):String(a-1)),[d,f]=x.useState("idle"),[p,h]=x.useState(""),[b,y]=x.useState(""),v=x.useRef(!0),j=Number(o)||0,w=e?.failure?.message||r("当前端口 {port},网关运行正常。",{port:a});async function S(){if(d!=="idle")return;const N=nl().workbuddyDesktop;if(!N)return;const k=Number(o)||0;if(!k||k<1024||k>65535){y(r("端口需在 1024-65535 之间"));return}f("checking"),y(""),h("");try{const A=await N.checkPort(k);if(!A?.ok){h(A?.message||r("该端口不可用"));return}if(A.same){h(A.message??"");return}if(f("saving"),(await N.changePort(k))?.changed===!1){h(r("与当前端口相同,无需重启"));return}t(),await lD(r("端口已改为 {port},正在重启…",{port:k}))}catch(A){y(ax(A))}finally{f("idle")}}return s.jsx(Yt,{open:!0,onOpenChange:N=>{N||t()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("更换网关端口")})}),s.jsxs(Lt,{children:[s.jsxs(dn,{children:[s.jsx("h3",{children:r("当前状态")}),s.jsx("p",{children:w})]}),s.jsxs(dn,{children:[s.jsx("h3",{children:r("新端口")}),s.jsxs("div",{className:"flex flex-wrap items-center gap-2.5",children:[s.jsx(Je,{htmlFor:"port-input",className:"text-[12.5px] whitespace-nowrap text-subtle",children:r("端口号")}),s.jsx(Fe,{id:"port-input",type:"number",min:1024,max:65535,step:1,placeholder:r("例如 3066"),autoComplete:"off",className:"max-w-[130px]",value:o,onChange:N=>{c(N.currentTarget.value)},onKeyDown:N=>{N.key==="Enter"&&S()},autoFocus:!0,onFocus:N=>{v.current&&(v.current=!1,N.currentTarget.select())}})]}),p?s.jsx("p",{children:p}):null,s.jsx("p",{children:r("端口需在 1024-65535 之间。1023 及以下是系统保留范围,普通程序无法监听。")})]}),s.jsxs(dn,{children:[s.jsx("h3",{children:r("换端口后要改的地方")}),s.jsxs("p",{children:[r("网关地址会变成 "),s.jsx("code",{children:`http://127.0.0.1:${j||"—"}`}),r("。已经把这个地址填进其它工具(Claude Code、Cherry Studio 等)的,需要一并改成新地址,否则那些工具会连不上。")]})]}),s.jsx("p",{className:"min-h-[18px] text-[11.5px] text-muted-foreground",children:b})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:t,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:d!=="idle",onClick:()=>{S()},children:yX[d]})]})]})})}let bp=null,Yu=null;function iD(){bp&&(bp.unmount(),bp=null),Yu&&(Yu.remove(),Yu=null)}async function wX(){let e=v0().backend;e||(e=await nl().workbuddyDesktop?.getBackendStatus().catch(()=>null)??null),iD(),Yu=document.createElement("div"),document.body.append(Yu),bp=wn.createRoot(Yu),bp.render(s.jsx(jX,{status:e,onClose:iD}))}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",oD,{once:!0}):oD(),window.wbPortPanel={render:xX,sync:Ku,portLabel:rD,isReady:gX};function nn(){return window}function Es(e,t){nn().wbApp?.toast?.(e,t)}function pr(e){return e instanceof Error?e.message:String(e)}function cD(e){const t=String(e||"").trim();return/^https?:\/\//i.test(t)?t:""}async function uD(e){const t=cD(e);if(!t){Es(r("链接地址不受支持"),"err");return}try{await nn().workbuddyDesktop?.openReleasePage(t)}catch(a){Es(r("打开链接失败:{error}",{error:pr(a)}),"err")}}const lx="1127656296",SX=`assets/qq-group/${lx}.jpg`;function dD(e){return nn().wbMarkdown?.render?.(e)||""}function Xu(e){const a=e.target?.closest?.("[data-external]");a&&(e.preventDefault(),uD(a.dataset.external||""))}async function NX(){try{const e=await nn().workbuddyDesktop?.getUpdateStatus?.();e&&e.checked!==!1&&nn().wbApp?.updateUpdateBadge?.(e)}catch(e){console.warn("读取版本检查结果失败:",pr(e))}}const vp="__proxy_other__",fD={proxyChoice:null,pool:null,poolError:""};async function kX(){const e=nn().workbuddyDesktop,t={...fD};if(!e?.getUpdateProxy)return t;try{t.proxyChoice=(await e.getUpdateProxy())?.proxy??null}catch{}try{if(typeof e.getProxyPool!="function")throw new Error("桥未提供代理池方法");const a=(await e.getProxyPool())?.items;if(!Array.isArray(a))throw new Error("代理池响应异常");t.pool=a}catch(a){t.pool=[],t.poolError=pr(a)}return t}async function CX(e){if(e===vp)return null;const t=nn().workbuddyDesktop;if(!t?.setUpdateProxy)return null;const a=e===""?null:{source:"pool",proxyId:e.slice(Qa.length)};try{const o=await t.setUpdateProxy({proxy:a}),c=o?.proxy??null,d=o?.saved!==!1;return d?Es(c?r("✅ 更新出网已切换为 {label}",{label:c.label||r("指定代理")}):r("✅ 更新出网已切换为直连")):Es(r("出网线路已切换,但写入磁盘失败(重启后会恢复原线路)"),"err"),{choice:c,saved:d}}catch(o){return Es(r("保存失败:{error}",{error:pr(o)}),"err"),null}}function pD(e){const t=e.proxyChoice,a=t?.config?.source||t?.source,o=t?.label||r(a==="custom"?"自定义代理":"已设置"),c=!!t?.error,d=Array.isArray(e.pool)?e.pool:[];let f="";a==="pool"&&t?.config?.proxyId?f=`${Qa}${t.config.proxyId}`:t&&(f=vp);const p=[{value:"",label:r("直连")}];for(const h of d)p.push({value:`${Qa}${h.id}`,label:du(h)});if(e.pool===null?p.push({value:"__hint_pool__",label:r("正在读取代理列表…"),disabled:!0}):d.length||p.push({value:"__hint_pool__",label:e.poolError?r("代理列表读取失败"):r("还没有代理(去「网络代理」页添加)"),disabled:!0}),f===vp){const h=a==="clash"?r("Clash 出口:"):a==="custom"?r("自定义:"):"";p.push({value:vp,label:`${h}${o}${c?r("(不可用)"):""}`})}else a==="pool"&&!d.some(h=>`${Qa}${h.id}`===f)&&p.push({value:f,label:`${o}${c?r("(不可用)"):""}`});return{current:f,items:p,selected:p.find(h=>h.value===f),broken:c,title:c?r("当前线路不可用:{error};请重新选择代理或切回直连",{error:String(t?.error||"")}):r("检查更新与下载安装包走哪条线路(当前:{current});选择即保存。选项来自「网络代理」页;直连失败时会自动借 Clash 混合端口重试一次,选定指定代理后只走它",{current:t?o:r("直连")})}}const Uo="https://proxylane.dev/zh-cn/isp-proxies?utm_source=agent2api&utm_medium=partnership&utm_campaign=agent2api_202610&utm_content=client",TX="assets/proxylane-logo-for-light-bg.png",EX="assets/proxylane-logo-for-dark-bg.png";function mD({children:e}){return s.jsx("a",{className:"font-semibold text-primary-fg no-underline hover:underline",href:Uo,"data-external":Uo,onClick:Xu,children:e})}function AX({item:e,onClose:t}){const a=e!==null,[o,c]=x.useState(e?.name||""),[d,f]=x.useState(e?.protocol==="socks5"?"socks5":"http"),[p,h]=x.useState(e?.host||""),[b,y]=x.useState(e?.port?String(e.port):""),[v,j]=x.useState(e?.username||""),[w,S]=x.useState(e?.password||""),[N,k]=x.useState(e?e.enabled:!0),[A,P]=x.useState(!1),[R,C]=x.useState(!1),[E,D]=x.useState(null);function I(){const z=o.trim();if(!z)return{error:r("请填写代理名称")};const H=p.trim();if(!H)return{error:r("请填写代理主机地址")};const B=Number(b);return!Number.isInteger(B)||B<1||B>65535?{error:r("代理端口必须是 1-65535 的整数")}:{payload:{name:z,source:"manual",protocol:d,host:H,port:B,username:v.trim(),password:w,enabled:N}}}async function O(){if(R)return;const z=I();if("error"in z){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",z.error]}));return}const H=z.payload,B={source:"custom",protocol:H.protocol,host:H.host,port:H.port,username:H.username,password:H.password};C(!0),D(r("正在连接上游…"));try{const L=await tr().workbuddyDesktop?.testProxy?.({proxy:B});if(L?.success){const F=Uj(L.durationMs);D(s.jsxs("span",{className:"text-success",children:[r("✅ 出口可用"),L.ip?r(" 出口 IP {ip}",{ip:L.ip}):"",F?` ${F}`:""]}))}else D(s.jsxs("span",{className:"text-destructive",children:["❌ ",L?.error||r("连接失败")]}))}catch(L){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",Ra(L)]}))}finally{C(!1)}}async function _(){if(A)return;const z=I();if("error"in z){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",z.error]}));return}P(!0),D(null);try{await Pq(e,z.payload),qn(a?r("已保存「{name}」",{name:o.trim()}):r("已新增「{name}」",{name:o.trim()})),t()}catch(H){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",Ra(H)]})),P(!1)}}return s.jsx(Yt,{open:!0,onOpenChange:z=>{!z&&!A&&t()},children:s.jsxs(Xt,{className:"max-w-[560px]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r(a?"编辑代理":"新增代理")})}),s.jsxs(Lt,{className:"flex flex-col gap-3.5",children:[s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-name",children:r("名称")}),s.jsx(Fe,{id:"proxy-name",maxLength:60,autoComplete:"off",spellCheck:!1,placeholder:r("例如:香港 HTTP 出口"),value:o,onChange:z=>c(z.currentTarget.value)})]}),s.jsxs("div",{className:"flex flex-row items-end gap-2.5",children:[s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-protocol",children:r("协议")}),s.jsxs(ut,{value:d,onValueChange:z=>f(z),children:[s.jsx(ft,{id:"proxy-protocol",className:"min-w-[110px]",children:s.jsx(dt,{children:d==="socks5"?"SOCKS5":"HTTP"})}),s.jsxs(pt,{children:[s.jsx($e,{value:"http",children:"HTTP"}),s.jsx($e,{value:"socks5",children:"SOCKS5"})]})]})]}),s.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-host",children:r("主机")}),s.jsx(Fe,{id:"proxy-host",placeholder:"127.0.0.1",autoComplete:"off",spellCheck:!1,value:p,onChange:z=>h(z.currentTarget.value)})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-port",children:r("端口")}),s.jsx(Fe,{id:"proxy-port",type:"number",min:1,max:65535,className:"max-w-[110px]",placeholder:"7890",value:b,onChange:z=>y(z.currentTarget.value)})]})]}),s.jsxs("div",{className:"flex flex-row items-end gap-2.5",children:[s.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-user",children:r("用户名")}),s.jsx(Fe,{id:"proxy-user",placeholder:r("可选"),autoComplete:"off",value:v,onChange:z=>j(z.currentTarget.value)})]}),s.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-pass",children:r("密码")}),s.jsx(Fe,{id:"proxy-pass",placeholder:r("可选"),autoComplete:"off",value:w,onChange:z=>S(z.currentTarget.value)})]})]}),s.jsxs("div",{className:"flex flex-row items-center gap-2.5",children:[s.jsx(ns,{checked:N,onCheckedChange:k,"aria-label":r("启用这条代理")}),s.jsx("span",{className:"text-[12.5px]",children:r("启用(禁用的条目不会被账号使用,被引用时账号回退直连;仍可测试)")})]}),s.jsxs("p",{className:"text-xs leading-[1.65] text-subtle",children:[r("需要干净的出口?"),s.jsx(mD,{children:"ProxyLane"}),r(" AI 账号专用静态住宅 IP:Scamalytics 低欺诈评分,不限流量,首单 75 折(AGENT2API25)")," ",s.jsx(mD,{children:r("去获取")})]}),s.jsx("div",{className:"min-h-[18px] text-xs text-subtle",children:E})]}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"outline",disabled:R||A,onClick:()=>{O()},children:r(R?"测试中…":"测试出口")}),s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",disabled:A,onClick:t,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:A,onClick:()=>{_()},children:r(A?"保存中…":"保存")})]})]})})}function RX({onClose:e}){const[t,a]=x.useState(!0),[o,c]=x.useState(!1),[d,f]=x.useState(""),[p,h]=x.useState(""),[b,y]=x.useState(""),[v,j]=x.useState({}),[w,S]=x.useState(""),[N,k]=x.useState(!1);x.useEffect(()=>{let C=!0;return(async()=>{try{const E=await fO();if(!C||E===null)return;f(String(E.listenerPortBase??"")),h(String(E.listenerPortEnd??"")),y(String(E.testUrl??"")),j(E.defaults??{}),a(!1)}catch(E){if(!C)return;S(Ra(E)),a(!1)}})(),()=>{C=!1}},[]);function A(){const C=Number(d),E=Number(p);if(!Number.isInteger(C)||C<1||C>65535||!Number.isInteger(E)||E<1||E>65535)return S(r("端口范围必须是 1-65535 的整数")),null;if(C>E)return S(r("起始端口不能大于结束端口")),null;const D=b.trim();return D&&!D.startsWith("http://")&&!D.startsWith("https://")?(S(r("测试网址要以 http:// 或 https:// 开头")),null):(S(""),{listenerPortBase:C,listenerPortEnd:E,testUrl:D})}async function P(){if(o||t)return;const C=A();if(C){c(!0);try{await _q(C),k(!0)}catch(E){S(Ra(E))}finally{c(!1)}}}function R(){f(String(v.listenerPortBase??"")),h(String(v.listenerPortEnd??"")),y(String(v.testUrl??"")),k(!1),S("")}return s.jsx(Yt,{open:!0,onOpenChange:C=>{!C&&!o&&e()},children:s.jsxs(Xt,{className:"max-w-[560px]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("代理设置")})}),s.jsx(Lt,{className:"flex flex-col gap-3.5",children:t?s.jsx("p",{className:"text-xs text-subtle",children:r("加载中…")}):s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"flex flex-row items-end gap-2.5",children:[s.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-settings-base",children:r("节点端口范围(起始)")}),s.jsx(Fe,{id:"proxy-settings-base",type:"number",min:1,max:65535,placeholder:String(v.listenerPortBase??""),autoComplete:"off",value:d,onChange:C=>{f(C.currentTarget.value),k(!1)}})]}),s.jsx("span",{"aria-hidden":"true",className:"flex-none pb-[7px] text-subtle",children:"—"}),s.jsxs("div",{className:"flex min-w-0 flex-1 flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-settings-end",children:r("节点端口范围(结束)")}),s.jsx(Fe,{id:"proxy-settings-end",type:"number",min:1,max:65535,placeholder:String(v.listenerPortEnd??""),autoComplete:"off",value:p,onChange:C=>{h(C.currentTarget.value),k(!1)}})]})]}),s.jsx("p",{className:"text-xs leading-[1.65] text-subtle",children:r("订阅节点会在本机各占一个监听端口,从该范围里挑空闲的分配;节点多的大订阅把默认范围({base}-{end})用尽时会报「没有可用的节点端口」,这时把范围调大再重启内核。已分配出去的端口不会因改范围而失效。",{base:v.listenerPortBase??"",end:v.listenerPortEnd??""})}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"proxy-settings-url",children:r("出口测试网址")}),s.jsx(Fe,{id:"proxy-settings-url",placeholder:v.testUrl||"",autoComplete:"off",spellCheck:!1,className:"font-mono text-[12px]",value:b,onChange:C=>{y(C.currentTarget.value),k(!1)}})]}),s.jsx("p",{className:"text-xs leading-[1.65] text-subtle",children:r("「测试」与「批量测试」访问的目标;留空用默认(打 WorkBuddy 上游)。判据是能否拿到 HTTP 响应,不用考虑该网址返回什么内容。")}),s.jsxs("div",{className:"min-h-[18px] text-xs",children:[w?s.jsxs("span",{className:"text-destructive",children:["❌ ",w]}):null,!w&&N?s.jsxs("span",{className:"text-success",children:["✅ ",r("已保存。重启内核后,新分配的节点端口才会按新范围来。")]}):null]})]})}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"ghost",disabled:t||o,onClick:R,children:r("恢复默认")}),s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",disabled:o,onClick:e,children:r("关闭")}),s.jsx(ne,{variant:"default",disabled:t||o,onClick:()=>{P()},children:r(o?"保存中…":"保存")})]})]})})}function hD(e){return(Math.max(0,Number(e)||0)/1024/1024).toFixed(1)}function OX(e){return/^[A-Za-z]:[\\/]/.test(e)||e.startsWith("\\\\")||e.startsWith("/")}function MX({onClose:e}){const t=x.useSyncExternalStore(yg,jg),a=Su(),o=a?.download,c=o?.state==="downloading",d=c&&o&&o.total>0?Math.round(o.received/o.total*100):null,[f,p]=x.useState(null),[h,b]=x.useState(""),[y,v]=x.useState(null),[j,w]=x.useState(a?.kernelDir||""),[S,N]=x.useState(!1),[k,A]=x.useState(!1),[P,R]=x.useState("");x.useEffect(()=>{let H=!0;return(async()=>{try{const B=(await nr().workbuddyDesktop?.getProxyPool?.())?.items;if(!Array.isArray(B))throw new Error(r("代理池响应异常"));H&&p(B)}catch(B){if(!H)return;p([]),b(wr(B))}})(),()=>{H=!1}},[]),x.useEffect(()=>{a?.installed&&e()},[a?.installed]);const C=pD({proxyChoice:a?.downloadProxy??null,pool:f,poolError:h}),E=y??C.current,D=C.items.find(H=>H.value===E)??C.selected,I=a?.kernelDirDefault||"";async function O(){if(S)return;const H=j.trim();if(H&&!OX(H)){R(r("存放位置要填绝对路径,例如 D:\\proxy-kernel"));return}R(""),N(!0);try{const B={dir:H};E!==vp&&(B.proxy=E===""?null:{source:"pool",proxyId:E.slice(Qa.length)}),Fq(B)}finally{N(!1)}}async function _(){if(!k){A(!0);try{const H=await $q();H==="done"?an(r("下载已经完成,停止没赶上(内核已安装)")):H==="none"?an(r("当前没有正在进行的下载"),"err"):H==="stalled"&&an(r("停止请求已发出,但下载还没收手,稍后再看一次进度"),"err")}finally{A(!1)}}}async function z(){const H=nr().workbuddyDesktop;if(typeof H?.pickFolder!="function"){an(r("当前环境不支持选择文件夹,直接填写绝对路径即可"),"err");return}try{const B=await H.pickFolder(j.trim()||I||null);if(B?.unsupported){an(r("当前环境不支持选择文件夹,直接填写绝对路径即可"),"err");return}if(B?.canceled||!B?.path)return;w(B.path)}catch(B){an(r("打开文件夹选择框失败:{reason}",{reason:wr(B)}),"err")}}return s.jsx(Yt,{open:!0,onOpenChange:H=>{H||e()},children:s.jsxs(Xt,{className:"w-[min(520px,calc(100vw-48px))]",children:[s.jsxs(Vt,{children:[s.jsx(Ut,{children:r(c?"正在下载内核":"下载内核")}),s.jsx("span",{className:"text-[11.5px] text-subtle",children:r("内核 {version}({asset})",{version:a?.version||"—",asset:a?.asset||"—"})})]}),c?s.jsxs(Lt,{className:"flex flex-col gap-3",children:[s.jsx(cf,{value:d??null}),s.jsxs("div",{className:"flex items-center justify-between text-[12px]",children:[s.jsx("span",{className:"text-text-2",children:d!==null?r("{percent}%({received} / {total} MB)",{percent:d,received:hD(o?.received),total:hD(o?.total)}):r("正在连接 GitHub…")}),s.jsx("span",{className:"text-subtle",children:r("存放位置 {dir}",{dir:a?.kernelDir||"—"})})]}),s.jsx("p",{className:"text-xs leading-[1.65] text-subtle",children:r("关掉这个窗口不会中断下载(它在后台继续跑)—— 要停下就点下面的「停止下载」,未下载完的临时文件会被删除。")})]}):s.jsxs(Lt,{className:"flex flex-col gap-3.5",children:[s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"kernel-proxy",children:r("下载线路")}),s.jsxs(ut,{value:E,onValueChange:H=>v(String(H)),children:[s.jsx(ft,{id:"kernel-proxy",className:Ue("w-full",C.broken&&"border-destructive-bd"),children:s.jsx(dt,{className:"min-w-0 truncate",children:D?.label||r("直连")})}),s.jsx(pt,{children:C.items.map(H=>s.jsx($e,{value:H.value,disabled:H.disabled,children:H.label},H.value))})]}),s.jsx("div",{className:"text-[12px] text-subtle",children:C.broken?r("当前线路不可用:{error};重新选一条或切回直连",{error:String(a?.downloadProxy?.error||"")}):r("选项来自「网络代理」页(Clash Verge 同步的出口在这里可用;订阅节点要等内核起来才有端口)。选「直连」时连不上会自动借 Clash 混合端口重试一次;选定某条线路后只走它。")})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"kernel-dir",children:r("存放位置")}),s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(Fe,{id:"kernel-dir",className:"min-w-0 flex-1 font-mono text-[12px]",autoComplete:"off",spellCheck:!1,value:j,placeholder:I,onChange:H=>w(H.currentTarget.value)}),s.jsx(ne,{variant:"outline",size:"sm",className:"flex-none",onClick:()=>{z()},children:r("选择目录…")}),I&&j.trim()!==I?s.jsx(ne,{variant:"ghost",size:"sm",className:"flex-none",title:I,onClick:()=>w(I),children:r("恢复默认")}):null]}),s.jsx("div",{className:"text-[12px] text-subtle",children:r("内核二进制 / 配置 / 端口映射都会放在这里,默认 {dir};目录不存在会在下载开始时自动创建。",{dir:I||"—"})})]}),o?.state==="error"&&o.error?s.jsx("span",{className:"text-[12px] text-destructive",children:r("上次下载失败:{reason}",{reason:o.error})}):null,o?.state==="canceled"?s.jsx("span",{className:"text-[12px] text-subtle",children:r("上次下载已停止,未下载完的临时文件已删除")}):null,P?s.jsx("span",{className:"text-[12px] text-destructive",children:P}):null]}),s.jsx(tn,{children:c?s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:e,children:r("后台继续")}),s.jsx(ne,{variant:"destructive",disabled:k,onClick:()=>{_()},children:r(k?"正在停止…":"停止下载")})]}):s.jsxs(s.Fragment,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:e,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:S||t.pending,onClick:()=>{O()},children:r(S?"正在开始…":"下载")})]})})]})})}function gD({showPortRange:e=!1}){const t=x.useSyncExternalStore(yg,jg),a=Su(),[o,c]=x.useState(null),[d,f]=x.useState(!1);x.useEffect(()=>{let b=!0;return(async()=>{try{const y=await fO();if(!b||!y)return;const v=Number(y.listenerPortBase),j=Number(y.listenerPortEnd);Number.isFinite(v)&&Number.isFinite(j)&&v>0&&j>0&&c({base:v,end:j})}catch{}})(),()=>{b=!1}},[]);const p=a?.download.state==="downloading",h=p&&a.download.total>0?Math.round(a.download.received/a.download.total*100):null;return s.jsxs("div",{className:"flex flex-col gap-2 rounded-md border border-border bg-surface-2 px-3 py-2",children:[s.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[s.jsx(ze,{variant:a?.running?"default":"secondary",children:a?.running?r("内核运行中"):a?.installed?a.halted?r("内核已手动关闭"):r("内核已停止"):r("内核未安装")}),s.jsxs("span",{className:"text-[12.5px] text-text-2",children:[a?.installed?r("内核 {version}",{version:a.version})+" · ":"",a?.running?r("控制端口 {port} · {n} 个节点监听器",{port:a?.controllerPort??"—",n:a?.listenerCount??0}):a?.installed?a.halted?r("内核已手动关闭(点「启动内核」拉起)"):r("内核已停止,点「启动内核」拉起"):r("按需下载(约 20-40MB)")]}),e&&o?s.jsx("span",{className:"text-[11.5px] text-subtle",children:r("节点端口范围 {base}–{end}",{base:o.base,end:o.end})}):null,s.jsx("div",{className:"ml-auto flex items-center gap-2",children:a?.installed?s.jsxs(s.Fragment,{children:[s.jsx(ne,{size:"sm",variant:"outline",disabled:t.pending||p,onClick:()=>{qq()},children:a?.running?r("重启内核"):r("启动内核")}),a?.running?s.jsx(ne,{size:"sm",variant:"outline",disabled:t.pending||p,onClick:()=>{Gq()},children:r("关闭内核")}):null,s.jsx(ne,{size:"sm",variant:"destructive",disabled:t.pending||p,onClick:()=>{Kq()},children:r("删除内核")})]}):s.jsx(ne,{size:"sm",variant:"outline",disabled:t.pending&&!p,onClick:()=>f(!0),children:r(p?"下载中…":"下载内核")})})]}),p?s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsx(cf,{className:"min-w-[200px] flex-1",value:h??null}),s.jsx("span",{className:"text-[11px] text-subtle",children:h!==null?r("{percent}%({received} / {total} MB)",{percent:h,received:xD(a.download.received),total:xD(a.download.total)}):r("下载中…")})]}):null,a?.lastError?s.jsx("span",{className:"text-[12px] text-destructive",children:a.lastError}):null,d?s.jsx(MX,{onClose:()=>f(!1)}):null]})}function DX({onClose:e}){const t=x.useSyncExternalStore(yg,jg),a=x.useSyncExternalStore(cO,uO),o=Lq(),c=t.data!==null,[d,f]=x.useState(null);return x.useEffect(()=>{Ml()},[]),s.jsxs(Yt,{open:!0,onOpenChange:p=>{p||e()},children:[s.jsxs(Xt,{className:"w-[min(880px,calc(100vw-48px))]",children:[s.jsxs(Vt,{children:[s.jsx(Ut,{children:r("订阅管理")}),s.jsxs("span",{className:"text-[11.5px] text-subtle",children:[c?r("{n} 条订阅",{n:o.length}):r("加载中…")," · ",r("节点由内核起成本地监听器,镜像进代理池")]})]}),s.jsxs(Lt,{className:"flex flex-col gap-3",children:[s.jsx(gD,{}),s.jsx("div",{className:"models-table-wrap",children:s.jsxs("table",{className:"models-table",children:[s.jsxs("colgroup",{children:[s.jsx("col",{className:"w-[18%]"}),s.jsx("col",{className:"w-[31%]"}),s.jsx("col",{className:"w-[9%]"}),s.jsx("col",{className:"w-[14%]"}),s.jsx("col",{className:"w-[8%]"}),s.jsx("col",{className:"w-[20%]"})]}),s.jsx("thead",{children:s.jsxs("tr",{children:[s.jsx("th",{children:r("名称")}),s.jsx("th",{children:r("订阅来源")}),s.jsx("th",{className:"ta-center",children:r("节点")}),s.jsx("th",{children:r("上次同步")}),s.jsx("th",{className:"ta-center",children:r("启用")}),s.jsx("th",{className:"ta-right",children:r("操作")})]})}),s.jsx("tbody",{children:o.length?o.map(p=>{const h=Su()?.providerSync?.[p.id];return s.jsxs("tr",{className:p.enabled?"":"off",children:[s.jsx("td",{children:s.jsx("b",{children:p.name||r("未命名")})}),s.jsx("td",{className:"group",children:s.jsx("div",{className:"flex min-w-0 items-center gap-1",children:p.kind==="local"?s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"min-w-0 truncate font-mono text-[12px] text-text-2",title:p.fileName,children:p.fileName||r("本地 YAML 文件")}),s.jsx(ze,{variant:"secondary",children:r("本地文件")})]}):s.jsxs(s.Fragment,{children:[s.jsx("span",{className:"min-w-0 truncate font-mono text-[12px] text-text-2",title:p.url,children:IX(p.url)}),s.jsx(ne,{variant:"ghost",size:"icon-xs",className:"copy-btn size-[21px] rounded-[var(--r-xs)] opacity-0 transition-opacity group-hover:opacity-100 focus-visible:opacity-100","data-copy":p.url,title:r("复制订阅链接"),children:"⧉"})]})})}),s.jsx("td",{className:"ta-center",children:bD(h,p)>0?s.jsx(ze,{variant:"secondary",children:r("{n} 个",{n:bD(h,p)})}):s.jsx("span",{className:"text-subtle",children:r("—")})}),s.jsx("td",{children:h?.at?s.jsx("span",{className:"text-[12px] text-text-2",children:sO(h.at)}):s.jsx("span",{className:"text-subtle",children:r("未同步")})}),s.jsx("td",{className:"ta-center",children:s.jsx(ns,{checked:p.enabled,disabled:t.pending,"aria-label":r("启用订阅「{name}」",{name:p.name||r("未命名")}),onCheckedChange:b=>{Hq(p,b)}})}),s.jsx("td",{children:s.jsxs("div",{className:"row-actions",children:[s.jsx(ne,{size:"sm",variant:"ghost",disabled:t.pending||t.refreshing!==null||p.kind==="local",title:p.kind==="local"?r("本地文件订阅无需更新(要换内容请编辑后重新选择文件)"):void 0,onClick:()=>{Uq(p)},children:t.refreshing===p.id?r("更新中…"):r("立即更新")}),s.jsx(ne,{size:"sm",variant:"ghost",disabled:t.pending,onClick:()=>f({item:p}),children:r("编辑")}),s.jsx(ne,{size:"sm",variant:"destructive",disabled:t.pending,onClick:()=>{Vq(p)},children:r("删除")})]})})]},p.id)}):s.jsx("tr",{children:s.jsx("td",{colSpan:6,className:"empty",children:c?s.jsxs("div",{className:"flex flex-col items-center gap-2 py-3",children:[s.jsx("span",{children:r("还没有订阅:粘贴订阅链接、导入本地 YAML 文件,或从图片识别二维码。")}),s.jsx(ne,{size:"sm",variant:"outline",disabled:t.pending,onClick:()=>f({item:null}),children:r("+ 新增订阅")}),s.jsx("span",{className:"text-[11px] text-subtle",children:r("订阅里的节点会自动同步进代理池,账号即可引用。")})]}):r("加载中…")})})})]})}),s.jsx("p",{className:"text-xs leading-[1.65] text-subtle",children:r("订阅里的节点会被内核起成本地监听器,镜像到代理池(账号按 id 引用,与本页其它出口同一用法)。内核只在用到订阅时按需下载(约 20-40MB),不随安装包分发。")}),t.error?s.jsx("span",{className:"text-destructive",children:r("读取失败:{reason}",{reason:t.error})}):null]}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"default",disabled:t.pending,onClick:()=>f({item:null}),children:r("+ 新增订阅")}),s.jsx(ne,{variant:"outline",disabled:a.syncing||a.batchBusy,onClick:()=>{Aq()},children:a.syncing?r("同步中…"):r("同步 Clash Verge")}),s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:e,children:r("完成")})]})]}),d?s.jsx(_X,{item:d.item,onClose:()=>f(null)}):null]})}function xD(e){return!Number.isFinite(e)||e<=0?"—":(e/1e3/1e3).toFixed(1)}function IX(e){try{const t=new URL(e);return t.host+(t.pathname!=="/"?"/…":"")}catch{return e.slice(0,60)}}function bD(e,t){return e?.count??t.nodeCount}function PX(){return s.jsxs("div",{className:"flex flex-col gap-2.5 rounded-lg border border-border bg-surface-2 px-3.5 py-3",children:[s.jsxs("a",{href:Uo,"data-external":Uo,onClick:Xu,className:"inline-flex w-fit items-center no-underline",children:[s.jsx("img",{src:TX,alt:"ProxyLane",className:"h-[24px] w-auto dark:hidden"}),s.jsx("img",{src:EX,alt:"ProxyLane",className:"hidden h-[24px] w-auto dark:block"})]}),s.jsxs("p",{className:"text-xs leading-[1.7] text-text-2",children:[s.jsx("a",{className:"font-semibold text-primary-fg no-underline hover:underline",href:Uo,"data-external":Uo,onClick:Xu,children:"ProxyLane"}),r(" 提供 AI 账号专用的原生静态住宅 IP(Static Residential ISP):IP 来自真实家庭宽带运营商,Scamalytics 低欺诈评分,每个 IP 独享,不限流量,$3.90 起。")]}),s.jsx("p",{className:"text-xs leading-[1.7] text-text-2",children:r("首次购买使用优惠码 AGENT2API25 享 75 折。")}),s.jsx("div",{children:s.jsx(ne,{size:"sm",render:s.jsx("a",{href:Uo,"data-external":Uo,onClick:Xu}),children:r("去获取")})})]})}function _X({item:e,onClose:t}){const a=e!==null,[o,c]=x.useState(e?.kind==="local"?"local":"url"),[d,f]=x.useState(e?.name||""),[p,h]=x.useState(e?.url||""),[b,y]=x.useState(e?.fileName||""),[v,j]=x.useState(""),[w,S]=x.useState(e?e.enabled:!0),[N,k]=x.useState(!1),[A,P]=x.useState(!1),[R,C]=x.useState(!1),[E,D]=x.useState(null),I=x.useRef(null);async function O(){if(N)return;const L=d.trim();if(!L){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",r("请填写订阅名称")]}));return}const F=p.trim();if(o==="url"&&!(F.startsWith("http://")||F.startsWith("https://"))){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",r("订阅链接必须是 http:// 或 https:// 开头")]}));return}if(!a&&o==="local"&&!v){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",r("请选择要导入的 YAML 文件")]}));return}k(!0);try{a&&o==="local"?await mO(e,{name:L,enabled:w,kind:"local",fileName:b||void 0,...v?{contentB64:v}:{}}):a?await mO(e,{name:L,url:F,enabled:w,kind:"url"}):o==="local"?await pO({name:L,kind:"local",fileName:b||void 0,contentB64:v}):await pO({name:L,kind:"url",url:F}),t()}catch($){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",wr($)]})),k(!1)}}async function _(L){if(!A){P(!0),D(r("正在识别二维码…"));try{const F=await Yq(L);h(F),D(s.jsxs("span",{className:"text-success",children:["✅ ",r("已识别订阅链接")]}))}catch(F){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",wr(F)]}))}finally{P(!1)}}}async function z(L){if(!R){C(!0),D(null);try{const F=await hO(L);j(F),y(L.name),D(s.jsxs("span",{className:"text-success",children:["✅ ",r("已选择「{name}」,保存后导入",{name:L.name})]}))}catch(F){D(s.jsxs("span",{className:"text-destructive",children:["❌ ",wr(F)]}))}finally{C(!1)}}}function H(L){if(o!=="url")return;const $=Array.from(L.clipboardData?.files||[]).find(X=>X.type.startsWith("image/"));$&&(L.preventDefault(),_($))}function B(L){L!==o&&(c(L),D(null))}return s.jsx(Yt,{open:!0,onOpenChange:L=>{!L&&!N&&t()},children:s.jsxs(Xt,{className:"max-w-[560px]",overlayForceRender:!0,onPaste:H,children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r(a?"编辑订阅":"新增订阅")})}),s.jsxs(Lt,{className:"flex flex-col gap-3.5",children:[o==="residential"?null:s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"sub-name",children:r("名称")}),s.jsx(Fe,{id:"sub-name",maxLength:60,autoComplete:"off",spellCheck:!1,placeholder:r("例如:我的机场"),value:d,onChange:L=>f(L.currentTarget.value)})]}),a?null:s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{children:r("订阅来源")}),s.jsx(En,{options:[{value:"url",label:r("订阅链接")},{value:"local",label:r("本地 YAML 文件")},{value:"residential",label:r("家庭宽带")}],value:o,onValueChange:B,"aria-label":r("订阅来源")})]}),o==="residential"?s.jsx(PX,{}):o==="url"?s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"sub-url",children:r("订阅链接")}),s.jsx(Fe,{id:"sub-url",autoComplete:"off",spellCheck:!1,placeholder:"https://…",value:p,onChange:L=>h(L.currentTarget.value)}),s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(ne,{size:"sm",variant:"outline",disabled:A,onClick:()=>I.current?.click(),children:r(A?"识别中…":"从图片识别二维码")}),s.jsx("span",{className:"text-[11px] text-subtle",children:r("也可以把二维码截图直接粘贴到弹窗里(Ctrl+V)")}),s.jsx("input",{ref:I,type:"file",accept:"image/*",className:"hidden",onChange:L=>{const F=L.currentTarget.files?.[0];F&&_(F),L.currentTarget.value=""}})]})]}):s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"sub-file",children:r("YAML 文件")}),s.jsxs("div",{className:"flex min-w-0 items-center gap-2",children:[s.jsx(ne,{size:"sm",variant:"outline",disabled:R,className:"shrink-0",onClick:()=>I.current?.click(),children:r(R?"读取中…":a?"重新选择文件…":"选择 YAML 文件…")}),s.jsx("span",{className:"min-w-0 truncate font-mono text-[12px] text-text-2",title:b||void 0,children:b||r(a?"保持原文件":"未选择文件")})]}),a?s.jsx("span",{className:"text-[11px] text-subtle",children:r("本地文件不会自动更新:要更新节点,重新选择文件后保存;不重选则内容保持不变。")}):null,s.jsx("input",{ref:I,type:"file",accept:".yaml,.yml,text/yaml,application/yaml,text/plain",className:"hidden",onChange:L=>{const F=L.currentTarget.files?.[0];F&&z(F),L.currentTarget.value=""}})]}),o==="residential"?null:s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"flex flex-row items-center gap-2.5",children:[s.jsx(ns,{checked:w,onCheckedChange:S,"aria-label":r("启用这条订阅")}),s.jsx("span",{className:"text-[12.5px]",children:r("启用(禁用的订阅不参与内核同步,它的节点会从代理池移除)")})]}),s.jsx("p",{className:"text-xs leading-[1.65] text-subtle",children:r(o==="url"?"订阅内容由内核定时刷新(每 24 小时);节点数与同步状态在保存后由轮询刷新,稍等几秒。":"本地文件订阅的内容在导入时固定:节点会由内核起成本地监听器并镜像进代理池,节点数稍等几秒由轮询刷新。")}),s.jsx("div",{className:"min-h-[18px] text-xs text-subtle",children:E})]})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",disabled:N,onClick:t,children:r("取消")}),o==="residential"?null:s.jsx(ne,{variant:"default",disabled:N||A||R,onClick:()=>{O()},children:r(N?"保存中…":"保存")})]})]})})}const w0=[{key:"pick",label:r("选择"),align:"center"},{key:"name",label:r("节点")},{key:"addr",label:r("地址")},{key:"state",label:r("启用"),align:"center"},{key:"test",label:r("连通性")},{key:"act",label:r("操作"),align:"right"}],LX={pick:"p-pick",name:"",addr:"p-addr",state:"p-state",test:"p-test",act:"p-act"},vD="proxies";function S0(){return window}let ox=null,yD=null;function jD(){return ox?ox.apply([...w0]):w0.map(e=>({...e,align:e.align??"left"}))}function zX(){S0().wbTableColumns?.repaint?.(vD)}function BX(e){if(yD=e,ox)return!0;const t=S0().wbColSettings;return t?.register?(ox=t.register({id:vD,label:r("代理池表"),columns:w0.map(a=>({key:a.key,label:a.label,align:a.align??"left"})),mount:()=>document.querySelector('.page[data-page="proxies"] .panel-head .head-actions'),onChange:()=>yD?.()}),!0):!1}function HX(){S0().wbColSettings?.close?.()}function UX(){const e=x.useSyncExternalStore(cO,uO),t=x.useSyncExternalStore(yg,jg).data?.items??[],[a,o]=x.useState(null),[c,d]=x.useState(!1),[f,p]=x.useState(!1),[h,b]=x.useState(""),[y,v]=x.useState("all"),[j,w]=x.useState("all"),[S,N]=x.useState("all"),[k,A]=x.useState(0),[,P]=x.useReducer(W=>W+1,0),R=vg(),C=e.data!==null,E=e.selected.size,D=R.filter(W=>e.selected.has(W.id)&&xg(W)).length,I=e.batchProgress;x.useEffect(()=>{Ml()},[]),x.useLayoutEffect(()=>{if(!BX(()=>P())){const W=window.setTimeout(()=>A(ue=>ue+1),50);return()=>window.clearTimeout(W)}P()},[k]),x.useLayoutEffect(()=>{zX()}),x.useEffect(()=>()=>{HX()},[]);const O=h.trim().toLowerCase(),_=$X(R,t);function z(W){return j==="all"?!0:j==="manual"||j==="clash"||j==="subscription"?sl(W)===j:j===ND?!!W.orphan:W.source==="subscription"&&!W.orphan&&W.providerId===j}function H(W){return!(y!=="all"&&sl(W)!==y||!z(W)||S!=="all"&&SD(W)!==S||O&&![W.name,Yh(W),W.host,W.protocol,String(W.port??"")].join(" ").toLowerCase().includes(O))}function B(W){const ue=W??"all";w(ue),v(ue==="all"?"all":ue==="manual"||ue==="clash"?ue:"subscription")}function L(W){v(W),w("all")}const F=R.filter(H),$=F.map(W=>W.id),X=$.length>0&&$.every(W=>e.selected.has(W)),K=$.some(W=>e.selected.has(W))&&!X,q=jD(),Q=[];for(const W of wD())Q.push(...F.filter(ue=>sl(ue)===W.key));const G=hg(F.length,"proxies"),J=G.paged?G.slice(Q):Q,Y=new Map;for(const W of F)Y.set(sl(W),(Y.get(sl(W))??0)+1);x.useEffect(()=>{G.goto(1)},[O,y,j,S]);function re(W){const ue=W.lastTest;if(!ue||!ue.at)return s.jsx("span",{className:"text-subtle",children:r("未测试")});const ve=Uj(ue.durationMs);return s.jsxs("div",{className:"flex flex-col gap-0.5 leading-[1.5] min-w-0",children:[s.jsxs("span",{className:ue.success?"text-success":"text-destructive",children:[ue.success?r("✅ {ip}",{ip:ue.ip||r("连接成功")}):r("❌ 连接失败"),ve?` ${ve}`:"",s.jsxs("span",{className:"text-[11px] text-subtle",children:[" ",sO(ue.at)]})]}),ue.success?null:s.jsx("span",{className:"overflow-hidden text-ellipsis whitespace-nowrap text-[11px] text-subtle",title:String(ue.error||r("连接失败")),children:String(ue.error||r("连接失败"))})]})}function M(W){const ue=W.usedBy||[],ve=[];return W.protocol&&ve.push(W.protocol),ue.length&&ve.push(s.jsx("span",{className:"text-warn",title:ue.map(ee=>String(ee.name||ee.id||"")).join(r("、")),children:r("{n} 个账号在用",{n:ue.length})})),ve.length?s.jsx("span",{className:"text-[11px] text-subtle",children:ve.map((ee,he)=>s.jsxs(x.Fragment,{children:[he?" · ":"",ee]},he))}):null}function U(W){if(xq(W)&&(W.port===null||W.port===void 0))return s.jsx("span",{className:"text-subtle",children:r("已禁用(未开监听端口)")});if(W.resolveError){const ve=W.source==="subscription"&&(W.port===null||W.port===void 0);return s.jsx("span",{className:ve?"text-subtle":"text-destructive",title:W.resolveError,children:r(ve?"未分配端口":"解析失败")})}if(hq(W)){const ve=Su()?.installed===!1;return s.jsx("span",{className:"text-subtle",title:r(ve?"代理内核未安装(在「订阅管理」里下载内核后,节点会自动分配监听端口)":"还没有监听端口(还没有账号引用它 —— 在账号里选它并保存后,内核会自动开端口)"),children:Yh(W)||r("未分配端口")})}const ue=Yh(W);return s.jsxs("div",{className:"flex min-w-0 items-center gap-1",children:[s.jsx("span",{className:"min-w-0 flex-1 font-mono text-[12px] leading-[1.45] text-text-2 break-all",title:ue||void 0,children:ue||"—"}),ue?s.jsx(ne,{variant:"ghost",size:"icon-xs",className:"copy-btn flex-none","data-copy":ue,title:r("复制地址"),children:"⧉"}):null]})}function Z(W,ue,ve,ee){const he=ve||e.batchBusy;switch(W){case"pick":return s.jsx(Pr,{checked:ee,title:r("勾选后可批量操作"),"aria-label":r("勾选「{name}」",{name:ue.name||r("未命名")}),onCheckedChange:te=>Cq(ue.id,te)});case"name":return s.jsxs("div",{className:"flex flex-col gap-0.5 leading-[1.5] min-w-0",children:[s.jsx("b",{children:ue.name||r("未命名")}),M(ue)]});case"addr":return U(ue);case"state":{const te=aO(ue);return s.jsx(ns,{checked:ue.enabled!==!1,disabled:he||te,"aria-label":r("启用「{name}」",{name:ue.name||r("未命名")}),title:Nq(ue),onCheckedChange:ke=>{Oq(ue,ke)}})}case"test":return re(ue);case"act":{const te=bg(ue);xg(ue);const ke=iO(ue),le=e.testing===ue.id;return s.jsxs("div",{className:"row-actions",children:[s.jsx(ne,{size:"sm",variant:"ghost",disabled:he||e.testing!==null,onClick:()=>{Rq(ue)},children:r(le?"测试中…":"测试")}),ue.source!=="clash"&&ue.source!=="subscription"?s.jsxs(s.Fragment,{children:[s.jsx(ne,{size:"sm",variant:"ghost",disabled:he,onClick:()=>o({item:ue}),children:r("编辑")}),s.jsx(ne,{size:"sm",variant:"destructive",disabled:he,onClick:()=>{dO(ue)},children:r("删除")})]}):null,te?s.jsx(ne,{size:"sm",variant:"destructive",disabled:he,title:ke,onClick:()=>{dO(ue)},children:r("删除")}):null]})}default:return null}}function ae(W){const ue=e.pending.has(W.id),ve=kq(W.id);return s.jsx("tr",{className:Ue(W.enabled?"":"off",ve&&"bg-primary-soft"),"data-id":W.id,children:q.map(ee=>s.jsx("td",{className:`ta-${ee.align}`,children:Z(ee.key,W,ue,ve)},ee.key))},W.id)}function ce(){if(!R.length)return s.jsx("tr",{children:s.jsx("td",{colSpan:q.length,className:"empty",children:r(C?"还没有代理。点右上角「+ 新增代理」手填一个,或在「订阅管理」里导入订阅节点、同步 Clash Verge 的出口。":"加载中…")})});if(!F.length)return s.jsx("tr",{children:s.jsx("td",{colSpan:q.length,className:"empty",children:O?r("没有匹配「{kw}」的出口",{kw:h.trim()}):r("没有匹配当前筛选的出口")})});const W=[];let ue="";for(const ve of J){const ee=sl(ve);if(ee!==ue){ue=ee;const he=wD().find(te=>te.key===ee);W.push(s.jsx("tr",{className:"tr-group",children:s.jsxs("td",{colSpan:q.length,children:[s.jsx("span",{className:"prov-tag",children:he?.label??ee}),r("{n} 个",{n:Y.get(ee)??0}),he?.note?s.jsxs("span",{className:"font-normal",children:[" · ",he.note]}):null]})},`group:${ee}:${W.length}`))}W.push(ae(ve))}return s.jsx(s.Fragment,{children:W})}return s.jsx(s.Fragment,{children:s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("代理池")}),s.jsx("span",{className:"panel-sub",children:C?r("{n} 个出口",{n:R.length}):r("加载中…")}),s.jsx("span",{className:"text-[11px] text-subtle",children:r("订阅与 Clash 的出口自动镜像进来")}),s.jsxs("div",{className:"head-actions",children:[s.jsx(ne,{variant:"outline",disabled:e.batchBusy,onClick:()=>d(!0),children:r("设置")}),s.jsx(ne,{variant:"outline",disabled:e.batchBusy,onClick:()=>p(!0),children:t.length?r("订阅管理({n})",{n:t.length}):r("订阅管理")}),s.jsx(ne,{variant:"outline",disabled:e.loading||e.batchBusy,onClick:()=>{Lf(),Ml()},children:e.loading?r("刷新中…"):r("刷新")}),s.jsx(ne,{variant:"default",disabled:e.batchBusy,onClick:()=>o({item:null}),children:r("+ 新增代理")})]})]}),s.jsx("div",{className:"px-3 pt-3",children:s.jsx(gD,{showPortRange:!0})}),E>0?s.jsxs("div",{className:"batch-bar active",children:[s.jsxs("span",{className:"batch-count",children:[r("已选")," ",s.jsx("b",{children:E})," ",r("个 · 共")," ",s.jsx("b",{children:F.length})," ",r("个")]}),D?s.jsx("span",{className:"batch-hidden",children:r("其中 {n} 条是同步来的只读条目(Clash / 订阅):可参与批量测试,删除会跳过",{n:D})}):null,s.jsxs("div",{className:"batch-actions",children:[s.jsx(ne,{variant:"outline",disabled:e.batchBusy,onClick:()=>{Dq()},children:I?r("测试中 {done}/{total}",{done:I.done,total:I.total}):r("批量测试")}),s.jsx(ne,{variant:"destructive",disabled:e.batchBusy,onClick:()=>{Iq()},children:r("批量删除")}),s.jsx(ne,{variant:"outline",disabled:e.batchBusy,onClick:()=>Eq(),children:r("取消选择")})]})]}):s.jsxs("div",{className:"flex flex-wrap items-center gap-2.5 border-b border-border px-4 py-[10px]",children:[s.jsxs(Di,{className:"w-[clamp(150px,22vw,220px)]",children:[s.jsx(Pi,{type:"search",value:h,"aria-label":r("搜索节点"),placeholder:r("搜索名称 / 地址 / 端口"),onChange:W=>b(W.currentTarget.value)}),s.jsx(Ii,{align:"inline-start","aria-hidden":"true",children:s.jsx(qX,{})})]}),s.jsxs(ut,{value:j,onValueChange:B,children:[s.jsx(ft,{className:"w-[clamp(150px,18vw,210px)]","aria-label":r("按提供方筛选"),children:s.jsx(dt,{children:_.find(W=>W.value===j)?.label??r("全部提供方")})}),s.jsx(pt,{children:_.map(W=>s.jsx($e,{value:W.value,children:s.jsxs("span",{className:"flex w-full items-center gap-2",children:[s.jsx("span",{className:"min-w-0 flex-1 truncate"+(W.count?"":" text-subtle"),children:W.label}),s.jsx("span",{className:"flex-none text-[11px] text-subtle",children:W.count})]})},W.value))})]}),s.jsx(En,{options:VX(R),value:y,onValueChange:L,"aria-label":r("按来源筛选")}),s.jsx(En,{options:FX(R),value:S,onValueChange:N,"aria-label":r("按状态筛选")}),s.jsx("span",{className:"ml-auto text-[11px] text-subtle",children:r("显示 {n} / 共 {total} 个出口",{n:F.length,total:R.length})})]}),s.jsx("div",{className:"models-table-wrap",children:s.jsxs("table",{className:"models-table proxies-table",children:[s.jsx("colgroup",{children:q.map(W=>s.jsx("col",{"data-col":W.key,className:LX[W.key]||void 0},W.key))}),s.jsx("thead",{children:s.jsx("tr",{children:q.map(W=>s.jsx("th",{"data-col":W.key,className:`ta-${W.align}`,children:W.key==="pick"?s.jsx(Pr,{checked:X,indeterminate:K,disabled:!$.length,title:r("全选当前筛选出的出口"),"aria-label":r("全选当前筛选出的出口"),onCheckedChange:ue=>Tq($,ue)}):W.label},W.key))})}),s.jsx("tbody",{children:ce()})]})}),s.jsx($i,{leading:s.jsxs(s.Fragment,{children:[e.data?.clash&&e.data.clash.available===!1?s.jsx("span",{className:"text-subtle",children:e.data.clash.error?r("未检测到 Clash Verge:{reason}",{reason:e.data.clash.error}):r("未检测到 Clash Verge")}):null,e.error?s.jsx("span",{className:"text-destructive",children:r("读取失败:{reason}",{reason:e.error})}):null]}),total:F.length,page:G.page,pageCount:G.pageCount,size:G.size,onSizeChange:G.setSize,onPageChange:G.goto,range:G.paged?{start:G.rangeStart,end:G.rangeEnd}:null}),a?s.jsx(AX,{item:a.item,onClose:()=>o(null)}):null,c?s.jsx(RX,{onClose:()=>d(!1)}):null,f?s.jsx(DX,{onClose:()=>p(!1)}):null]})})}function sl(e){return e.source==="clash"?"clash":e.source==="subscription"?"subscription":"manual"}function wD(){return[{key:"manual",label:r("手动"),note:r("本页可编辑、可删除")},{key:"clash",label:"Clash Verge",note:r("名字与端口在 Clash Verge 里改;启用可在本页关闭")},{key:"subscription",label:r("订阅"),note:r("名字与端口跟随订阅与内核;启用可在本页关闭")}]}function VX(e){const t=a=>e.filter(o=>sl(o)===a).length;return[{value:"all",label:r("全部"),count:e.length},{value:"manual",label:r("手动"),count:t("manual")},{value:"clash",label:"Clash",count:t("clash")},{value:"subscription",label:r("订阅"),count:t("subscription")}]}function SD(e){return e.enabled===!1?"off":e.port===null||e.port===void 0?"wait":e.lastTest?.at?e.lastTest.success?"ok":"fail":"none"}function FX(e){const t=a=>e.filter(o=>SD(o)===a).length;return[{value:"all",label:r("全部状态"),count:e.length},{value:"ok",label:r("可用"),count:t("ok")},{value:"fail",label:r("失败"),count:t("fail")},{value:"wait",label:r("未就绪"),count:t("wait")},{value:"off",label:r("已禁用"),count:t("off")}]}const ND="__orphan";function $X(e,t){const a=e.filter(d=>sl(d)==="subscription"),o=t.map(d=>({value:d.id,label:d.name||d.id,count:a.filter(f=>!f.orphan&&f.providerId===d.id).length})).sort((d,f)=>f.count-d.count||d.label.localeCompare(f.label)),c=a.filter(d=>d.orphan).length;return[{value:"all",label:r("全部提供方"),count:e.length},{value:"manual",label:r("手动"),count:e.filter(d=>sl(d)==="manual").length},{value:"clash",label:"Clash Verge",count:e.filter(d=>sl(d)==="clash").length},{value:"subscription",label:r("全部订阅"),count:a.length},...o,...c?[{value:ND,label:r("已移除的订阅"),count:c}]:[]]}function qX(){return s.jsxs("svg",{width:"14",height:"14",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:[s.jsx("circle",{cx:"11",cy:"11",r:"8"}),s.jsx("path",{d:"m21 21-4.3-4.3"})]})}window.wbProxiesPanel={load:async()=>{await Lf(),await Ml()},visibleColumns:()=>jD().map(e=>({key:e.key}))};const kD='.page[data-page="proxies"]';function CD(){const e=document.querySelector(kD);!e||e.dataset.mounted==="1"||(e.dataset.mounted="1",e.replaceChildren(),wn.createRoot(e).render(s.jsx(UX,{})),tr().wbApp?.currentPage==="proxies"&&(Lf(),Ml()))}document.querySelector(kD)?CD():document.addEventListener("DOMContentLoaded",CD,{once:!0});const ix=["today","7","30","month","all","custom"],N0="7",k0="custom",TD="workbuddy-desktop-report-range",ED="workbuddy-desktop-report-custom-range",AD={today:r("今天"),7:r("近 7 天"),30:r("近 30 天"),month:r("本月"),all:r("全部"),custom:r("自定义区间")},GX={today:r("今天"),7:r("7 天"),30:r("30 天"),month:r("本月"),all:r("全部"),custom:r("自定义")};function KX(){try{const e=localStorage.getItem(TD);return e!==null&&ix.includes(e)?e:N0}catch{return N0}}function C0(e){const t=a=>String(a).padStart(2,"0");return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())}`}function YX(){const e=new Date;return{start:C0(new Date(e.getFullYear(),e.getMonth(),e.getDate()-6)),end:C0(e)}}const cx=/^\d{4}-\d{2}-\d{2}$/;function XX(){try{const e=localStorage.getItem(ED);if(e){const t=JSON.parse(e),a=typeof t?.start=="string"?t.start:"",o=typeof t?.end=="string"?t.end:"";if(cx.test(a)&&cx.test(o)&&a<=o)return{start:a,end:o}}}catch{}return YX()}function QX(e){try{localStorage.setItem(ED,JSON.stringify(e))}catch{}}const ZX=[[0,r("一")],[2,r("三")],[4,r("五")]],RD=400;function sc(e){const t=T0();return t.formatInt?t.formatInt(e):(Number(e)||0).toLocaleString("zh-CN")}function T0(){return window.wbUnits||{}}function os(e){const t=T0();return t.formatTokens?t.formatTokens(e):String(Number(e)||0)}function OD(e,t){const a=T0();return a.formatAxis?a.formatAxis(e,t):String(e)}function rc(e){return`${((Number(e)||0)*100).toFixed(1)}%`}function zt(e){return Math.round(e*10)/10}function ux(e){return[...e].reduce((t,a)=>t+(/[\u2e80-\u9fff]/.test(a)?9.7:5.4),0)}function dx(e){const[t,a,o]=String(e||"").split("-").map(Number);return new Date(t||1970,(a||1)-1,o||1)}const WX=[r("日"),r("一"),r("二"),r("三"),r("四"),r("五"),r("六")];function fx(e){const t=dx(e);return r("{m}月{d}日 周{wd}",{m:t.getMonth()+1,d:t.getDate(),wd:WX[t.getDay()]})}function MD(e){const t=String(e||"").slice(11,13);return t?`${t}:00`:"—"}const JX=8;function DD(e,t){const a=(Array.isArray(e)?e:[]).map(d=>({name:String(d?.label??d?.model??"").trim()||r("未知模型"),tokens:Number(d?.tokens)||0})).filter(d=>d.tokens>0);if(!a.length)return[];const o=a.slice(0,JX),c=o.map(d=>`${t?`[${t(d.name)}]`:""}${r("{model}:{tokens} tokens",{model:d.name,tokens:os(d.tokens)})}`);return a.length>o.length&&c.push(r("…还有 {n} 个模型",{n:a.length-o.length})),c}function ID(e,t){return[fx(e.date),r("总 Token:{tokens}({n} 次请求)",{tokens:os(e.tokens),n:sc(e.requests)}),...DD(e.models,t)].join(` +`)}function eQ(e,t){return[`${fx(String(e.hour).slice(0,10))} ${MD(e.hour)}`,r("命中率 {rate}(命中 {hit} / 输入 {input})",{rate:rc(e.rate),hit:os(e.hitTokens),input:os(e.inputTokens)}),r("总 Token:{tokens}",{tokens:os(e.totalTokens)}),...DD(e.models,t)].join(` +`)}const tQ=[1,1.5,2,2.5,3,4,5,6,8,10];function nQ(e){if(!(e>0))return 1;const t=10**Math.floor(Math.log10(e)),a=e/t;return(tQ.find(c=>c>=a-1e-9)??10)*t}function E0(e){return nQ(e/4)*4}function sQ(e,t,a){const o=e||{},c=o.topModel,d=Number(o.requests)||0,f=Number(o.successful)||0,p=d?`${(f/d*100).toFixed(1)}%`:"—",h=[{key:"requests",label:r("总请求数"),value:sc(d),sub:AD[t]||"",mono:!1},{key:"successful",label:r("成功请求数"),value:sc(f),sub:r("成功率 {rate}",{rate:p}),mono:!1},{key:"tokens",label:r("总 Token"),value:os(o.tokens),sub:r("输入 + 输出"),mono:!1},{key:"activeDays",label:r("活跃天数"),value:sc(o.activeDays),sub:r("区间共 {n} 天",{n:a}),mono:!1},{key:"streak",label:r("当前连续天数"),value:sc(o.streak),sub:r("含今天在内往前数"),mono:!1}];return h.push(c?{key:"topModel",label:r("Top 模型"),value:String(c.model??""),sub:r("{tokens} tokens · 占 {pct}",{tokens:os(c.tokens),pct:rc(c.percentage)}),mono:!0}:{key:"topModel",label:r("Top 模型"),value:"—",sub:r("所选范围内还没有模型用量"),mono:!1}),h}function PD(e){if(!Array.isArray(e))return null;const t=e.map(c=>({id:String(c?.id??"").trim(),label:String(c?.label??"").trim(),tokens:Number(c?.totalTokens??c?.tokens)||0})).filter(c=>c.tokens>0);if(!t.length)return[];const a=t.reduce((c,d)=>c+d.tokens,0),o=c=>a?c/a*100:0;return[...t].sort((c,d)=>d.tokens-c.tokens).map((c,d)=>{const f=o(c.tokens),p=os(c.tokens),h=c.label||c.id||r("未知"),b=c.id?r("{name}({id})",{name:h,id:c.id}):h;return{key:`${d}-${c.id||h}`,id:c.id,name:h,full:b,tip:r("{name}:{tokens} tokens · 占 {pct}%",{name:b,tokens:p,pct:f.toFixed(1)}),tokensText:p,percentText:`${f.toFixed(1)}%`,barWidth:`${f.toFixed(1)}%`}})}function _D(e){return`${os(e.reduce((t,a)=>t+(Number(a?.totalTokens??a?.tokens)||0),0))} tokens`}const px=["#f73b00","#f7bb07","#4aaa4d","#1872cb","#3444a3","#13c2c2","#722ed1","#eb2f96","#a0d911","#34d399","#d946ef","#ff85c0","#b37feb","#9d174d","#ffa39e","#69b1ff"],rQ={size:220,inner:.62,outer:.88,padAngle:2};function aQ(e,t,a,o,c,d){const f=P=>(P-90)*Math.PI/180,p=(P,R)=>[zt(e+P*Math.cos(f(R))),zt(t+P*Math.sin(f(R)))],h=d-c;if(h>=359.999){const[P,R]=p(a,0),[C,E]=p(a,180),[D,I]=p(o,0),[O,_]=p(o,180);return`M ${P} ${R} A ${a} ${a} 0 1 1 ${C} ${E} A ${a} ${a} 0 1 1 ${P} ${R} Z M ${D} ${I} A ${o} ${o} 0 1 0 ${O} ${_} A ${o} ${o} 0 1 0 ${D} ${I} Z`}const[b,y]=p(a,c),[v,j]=p(a,d),[w,S]=p(o,d),[N,k]=p(o,c),A=h>180?1:0;return`M ${b} ${y} A ${a} ${a} 0 ${A} 1 ${v} ${j} L ${w} ${S} A ${o} ${o} 0 ${A} 0 ${N} ${k} Z`}const lQ="#9aa0a6";function LD(e){const t=new Map;for(const a of Array.isArray(e)?e:[]){const o=String(a?.label??a?.model??a?.id??"").trim();!o||t.has(o)||(Number(a?.totalTokens??a?.tokens)||0)>0&&t.set(o,px[t.size%px.length])}return t}function zD(e,t){const a=(Array.isArray(e)?e:[]).map(k=>({label:String(k?.label??k?.model??k?.id??"").trim()||t,tokens:Number(k?.totalTokens??k?.tokens)||0,requests:Number(k?.requests)||0})).filter(k=>k.tokens>0);if(!a.length)return null;const o=a.reduce((k,A)=>k+A.tokens,0);if(!o)return null;const c=LD(e),{size:d,inner:f,outer:p,padAngle:h}=rQ,b=d/2,y=d/2,v=d/2*p,j=d/2*f;let w=0;const S=[],N=[];return a.forEach((k,A)=>{const P=k.tokens/o,R=P*360,C=a.length>1?Math.min(h/2,R/4):0,E=w+C,D=w+R-C;w+=R;const I=c.get(k.label)??px[A%px.length];let O=null;if(P>=.03){const _=(E+D)/2,z=j+(v-j)*.6,H=(_-90)*Math.PI/180;O={x:zt(b+z*Math.cos(H)),y:zt(y+z*Math.sin(H)),text:`${Math.round(P*100)}%`}}D>E&&S.push({key:`${A}-${k.label}`,d:aQ(b,y,v,j,E,D),fill:I,evenOdd:R>=359.999,label:O,tip:`${k.label} · ${os(k.tokens)} tokens · ${rc(P)}`}),N.push({key:`${A}-${k.label}`,fill:I,name:k.label,tokensText:os(k.tokens),percentText:rc(P),tip:r("{name} · {tokens} tokens · {n} 次请求 · {pct}",{name:k.label,tokens:os(k.tokens),n:sc(k.requests),pct:rc(P)})})}),{size:d,slices:S,legend:N,totalText:os(o)}}const oQ=[1,1.25,1.5,2,2.5,3,4,5,6,8,10];function iQ(e){if(!(e>0))return 1;const t=10**Math.floor(Math.log10(e)),a=e/t;let o=1;for(const c of oQ)c<=a+1e-9&&(o=c);return o*t}function cQ(e){const t=Number(e)||0;if(t<=0)return[0,1,2,3,1/0];const a=Math.max(1,iQ(t/4));return[0,a,a*2,a*3,1/0]}function uQ(e,t){if(e===0)return"0";const a=t[e-1],o=t[e];return Number.isFinite(o)?a===0?`≤${os(o)}`:`${os(a)}–${os(o)}`:`>${os(a)}`}function BD(e){const t=Array.isArray(e)?e:[];return cQ(t.reduce((a,o)=>Math.max(a,Number(o?.tokens)||0),0))}function dQ(e,t){const a=Array.isArray(e)?e:[];if(!a.length)return null;const o=(dx(a[0].date).getDay()+6)%7,c=new Array(o).fill(null).concat(a),d=Math.ceil(c.length/7),f=3,p=20,h=t-p,b=Math.floor((h-f*(d-1))/d),y=Math.max(7,Math.min(26,b)),v=y+3,j=y+f,w=p+d*j-f,S=Math.max(0,Math.round((t-w)/2)),N=v+7*j-f+2,k=Math.max(w,Math.round(t)),A=BD(a),P=O=>A.findIndex(_=>Number(O)<=_),R=[];for(let O=0;O({key:_,x:S+p-6,y:zt(v+O*j+y/2),text:_})),E=[];let D=-1,I=-9;for(let O=0;O({level:t,text:uQ(t,e)}))}function pQ(e){const t=e||{};return[["last10m",r("近 10 分钟")],["last1h",r("近 1 小时")],["last24h",r("近 24 小时")],["last7d",r("近 7 天")]].map(([o,c])=>{const d=t[o]||{},f=Number(d.inputTokens)||0;return{key:o,label:c,value:f?rc(d.rate):"—",sub:r("命中 {hit} / 输入 {input}",{hit:os(d.hitTokens),input:os(f)})}})}function mQ(e,t,a){const o=Array.isArray(e)?e:[];if(!o.length)return null;const c=230,d={left:44,right:56,top:26,bottom:24},f=Math.max(40,t-d.left-d.right),p=c-d.top-d.bottom,h=o.map(K=>Number(K.rate)||0),b=o.map(K=>Number(K.totalTokens)||0),y=Math.max(1,E0(Math.max(...h))),v=Math.max(...b),j=E0(v),w=v>0,S=K=>d.left+(o.length>1?f*K/(o.length-1):f/2),N=K=>d.top+p-p*K/y,k=K=>d.top+p-p*K/j,A=[],P=[],R=[];for(let K=0;K<=4;K+=1){const q=y*K/4,Q=zt(N(q)),G=q*100;A.push({key:`g${K}`,y:Q}),P.push({key:`r${K}`,x:d.left-8,y:Q,anchor:"end",text:`${Number.isInteger(G)?G:G.toFixed(1)}%`}),w&&R.push({key:`t${K}`,x:zt(d.left+f+8),y:Q,anchor:"start",text:OD(j*K/4,j)})}const C=o.length===1?4.5:2.5,E=(K,q)=>({points:K.map((Q,G)=>`${zt(S(G))},${zt(q(Q))}`).join(" "),dots:K.map((Q,G)=>({key:`d${G}`,cx:zt(S(G)),cy:zt(q(Q))}))}),D=[],I=(K,q,Q,G)=>{const J=ux(Q),Y=G==="middle"?K-J/2:G==="end"?K-J:K,re={left:Y,right:Y+J,top:q-8.5,bottom:q+2.5};return D.some(M=>re.leftM.left-2&&re.topM.top-1)?null:(D.push(re),{key:`${Q}-${K}-${q}`,x:zt(K),y:zt(q),text:Q,anchor:G})},O=(K,q)=>{const Q=ux(q)/2;return K-Qd.left+f+6?"end":"middle"},_=[];w&&o.forEach((K,q)=>{if(!(Number(K.totalTokens)>0))return;const Q=k(b[q]),G=Q-12>=d.top-10,J=os(b[q]),Y=I(S(q),G?Q-12:Q+13,J,O(S(q),J));Y&&_.push({...Y,key:`tl${q}`})});const z=[];o.forEach((K,q)=>{if(!((Number(K.inputTokens)||0)+(Number(K.hitTokens)||0)>0))return;const G=N(h[q]),J=G+15<=d.top+p+8,Y=rc(h[q]),re=I(S(q),J?G+13:G-7,Y,O(S(q),Y));re&&z.push({...re,key:`rl${q}`})});const H=o.length>1?f/(o.length-1):f,B=d.left,L=d.left+f,F=o.length<=RD?o.map((K,q)=>{const Q=Math.max(B,S(q)-H/2),G=Math.min(L,S(q)+H/2);return{key:`h${q}`,x:zt(Q),width:zt(G-Q),tip:eQ(K,a)}}):null,$=Math.max(1,Math.ceil(o.length/8)),X=[];for(let K=0;K=o.length;X.push({key:`x${K}`,x:zt(S(K)),y:c-8,anchor:K===0?"start":q?"end":"middle",text:MD(o[K].hour)})}return{width:t,height:c,plotLeft:B,plotW:f,plotH:p,plotTop:d.top,grid:A,rateTicks:P,tokenTicks:R,rateLine:E(h,N),tokenLine:w?E(b,k):null,dotR:C,rateLabels:z,tokenLabels:_,hourTicks:X,hits:F}}const hQ=13;function gQ(e,t,a){const o=Array.isArray(e)?e:[];if(!o.length)return null;const c=240,d={left:50,right:14,top:26,bottom:26},f=Math.max(40,t-d.left-d.right),p=f/o.length,h=o.map($=>Number($.tokens)>0?os($.tokens):""),b=Math.max(0,...h.map(ux)),y=h.every($=>!$)||b+4<=p,v=!y&&p>=hQ;v&&(d.top=10+Math.ceil(b*1.25));const j=c-d.top-d.bottom,w=o.map($=>Number($.tokens)||0),S=Math.max(...w),N=E0(S),k=Math.max(1,Math.min(28,p*.68)),A=d.top+j,P=$=>d.top+j-j*$/N,R=o.length<=RD,C=S<=0,E=[],D=[];if(!C)for(let $=0;$<=4;$+=1){const X=N*$/4,K=zt(P(X));E.push({key:`g${$}`,x1:d.left,y:K,x2:zt(d.left+f)}),D.push({key:`v${$}`,x:d.left-8,y:K,anchor:"end",text:OD(X,N)})}const I=[];o.forEach(($,X)=>{const K=Number($.tokens)||0;if(K<=0)return;const q=P(K);I.push({key:String($.date??X),x:zt(d.left+p*X+(p-k)/2),y:zt(q),width:zt(k),height:zt(Math.max(1,A-q)),rx:k>4?2:1,tip:ID($),nativeTip:!R})});const O=[],_=[],z=($,X)=>{const K=h[$],q=P(Number(o[$].tokens)||0),Q=zt(d.left+p*$+p/2);if(v)return{key:`b${$}`,x:Q,y:zt(q-6),anchor:"start",text:K,rotate:!0};const G=q-6,J=G-9M.leftU.left-2&&M.topU.top-1))return null;_.push(M)}return{key:`b${$}`,x:Q,y:Y,anchor:"middle",text:K}};if(y||v)h.forEach(($,X)=>{if(!$)return;const K=z(X,!1);K&&O.push(K)});else{const $=o.map((X,K)=>({index:K,value:Number(X.tokens)||0})).filter(X=>X.value>0).sort((X,K)=>K.value-X.value);for(const X of $){if(O.length>=8)break;const K=z(X.index,!0);K&&O.push(K)}}const H=R?o.map(($,X)=>({key:`h${X}`,x:zt(d.left+p*X),width:zt(p),tip:ID($,a)})):null,B=Math.max(1,Math.ceil(o.length/6)),L=[];for(let $=0;${const X=o.length===1,K=!X&&$===o.length-1,q=dx(o[$].date);return{key:`d${$}`,x:X?d.left+f/2:K?d.left+f:zt(d.left+p*$+p/2),y:c-8,anchor:X?"middle":K?"end":$===0?"start":"middle",text:`${q.getMonth()+1}/${q.getDate()}`}});return{width:t,height:c,plotLeft:d.left,plotW:f,plotH:j,plotTop:d.top,grid:E,valueTicks:D,bars:I,barLabels:O,dateTicks:F,hits:H,blank:C}}function ac(){return window}const HD=1e3,xQ=ix.map(e=>({value:e,label:GX[e]})),bQ=r("所选范围内每个账号各消耗了多少 Token,按 Token 用量从多到少排。账号是比提供商更细的一维:同一家可以挂多个账号,转发时按优先级在候选链上依次尝试,所以这里反映的是「具体哪个登录态在出力」。百分比按本区间的总 Token 数算(这一块只看用量,请求次数与成功率在概览卡片里)。「未知账号」是指那批请求没有账号身份:走默认登录态转发(未配置账号列表)时会这样;这一维上线前写出的旧聚合行也没有账号明细,但它们会在升级导入旧数据时按明细自动补算回来 —— 只有明细已被手动清理掉的那几天补不回来,仍留在「未知账号」里,那是确实无从恢复的部分。"),vQ=r("所选范围内每个提供商各消耗了多少 Token,按 Token 用量从多到少排。同一个模型名可能有多家都能提供,网关按设置页「转发路由」的优先级依次尝试,所以占比也反映了实际落到谁身上。百分比按本区间的总 Token 数算(这一块只看用量,请求次数与成功率在概览卡片里)。「未知」是指那批请求没记下承载它的提供商 —— 主要是记录提供商这个功能上线之前的旧明细,其次是请求在选定上游之前就失败了(请求体非法、模型不存在、没有可用账号)。这一维与账号那一维不同:旧明细里根本没有「哪家承载」的信息(早期版本只有单一上游,但那是配置事实而非逐条记录),所以无法回算,旧数据会一直留在「未知」里;此后新产生的请求都会记上具体的提供商。"),yQ=r("所选范围内每个模型各消耗了多少 Token,按用量从多到少排,扇区角度即占比。中心是这一维的合计(等于概览里的总 Token),右侧每行给出该模型的 Token 用量与百分比。扇区内的百分比只在 ≥3% 时标出,更小的会挤成一团 —— 完整读数在右侧列表与悬停气泡里。颜色按用量排名分配:最大的那段是红色,依次往后;同一份报表里换个维度看,配色口径不变。"),jQ=r("所选范围内每个提供商各消耗了多少 Token,按用量从多到少排,扇区角度即占比。同一个模型名可能有多家都能提供,网关按设置页「转发路由」的优先级依次尝试,所以占比也反映了实际落到谁身上。中心是这一维的合计(等于概览里的总 Token),右侧每行给出该提供商的 Token 用量与百分比。「未知」是指那批请求没记下承载它的提供商 —— 主要是记录提供商这个功能上线之前的旧明细,其次是请求在选定上游之前就失败了。"),wQ=r("命中率 = 缓存读取 Token / 输入 Token(promptTokens)。窗口越短越贴近「此刻」:上游把缓存读取与输入分开上报时,该比值合法地可能超过 100%,此处不夹取值、如实展示。"),SQ=r("每天的总 Token 用量(输入 + 输出)。柱顶标出当天的读数;区间很长、柱子挤到放不下时只标最高的那几天,其余悬停柱子即可读到。");let mx=HD,hx=!0,A0=!1,R0=!1,O0=0,Vo=null,lc=null,gx=null;function oc(e,t="empty"){return s.jsx("div",{className:t,style:{padding:"14px 0"},children:e})}function xx({text:e}){return s.jsx("span",{className:"tip-q","data-tip":e})}function NQ({accountId:e}){const t=(ac().wbApp?.getState?.()?.accounts?.accounts||[]).find(f=>f?.id===e),a=typeof t?.provider=="string"?t.provider:"";if(!a)return null;const o=ac().wbProviders?.labelOf?.(a)||a,c=t&&ac().wbAccountsModel?.editionSuffix?.(t)||"",d=c?`${o} ${c}`:o;return s.jsx("span",{className:`pbadge p-${a}`,title:r("提供商:{text}",{text:d}),children:d})}function kQ({summary:e,range:t}){const a=Array.isArray(e.dailyTrend)?e.dailyTrend:[];return s.jsx(s.Fragment,{children:sQ(e.overview,t,a.length).map(o=>s.jsxs("div",{className:"field",children:[s.jsx("div",{className:"label",children:o.label}),s.jsxs("div",{className:o.mono?"value mono":"value",children:[o.value,o.sub?s.jsx("div",{className:"sub",children:o.sub}):null]})]},o.key))})}function UD({panelId:e,listId:t,labelId:a,title:o,tip:c,rows:d,totalText:f,withBadge:p,emptyText:h}){return s.jsxs("section",{className:"panel",id:e,children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:o}),s.jsx(xx,{text:c}),s.jsx("span",{className:"panel-sub",id:a,children:f})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{className:"rank-shares",id:t,children:d.length?d.map(b=>s.jsxs("div",{className:"rank-row","data-tip":b.tip,children:[s.jsxs("span",{className:"name",title:b.full,children:[p?s.jsx(NQ,{accountId:b.id}):null,b.name]}),s.jsx("span",{className:"track",children:s.jsx("span",{className:"bar",style:{width:b.barWidth}})}),s.jsx("span",{className:"num",title:`${b.tokensText} tokens`,children:b.tokensText}),s.jsx("span",{className:"pct",children:b.percentText})]},b.key)):oc(h)})})]})}function VD({panelId:e,listId:t,title:a,tip:o,ariaLabel:c,view:d,emptyText:f}){return s.jsxs("section",{className:"panel",id:e,children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:a}),s.jsx(xx,{text:o})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{id:t,children:d?s.jsxs("div",{className:"donut-layout",children:[s.jsxs("div",{className:"donut-wrap",children:[s.jsx("svg",{className:"donut-svg",width:d.size,height:d.size,viewBox:`0 0 ${d.size} ${d.size}`,role:"img","aria-label":c,children:d.slices.map(p=>s.jsxs(x.Fragment,{children:[s.jsx("path",{className:"donut-slice",d:p.d,fill:p.fill,fillRule:p.evenOdd?"evenodd":void 0,tabIndex:-1,"data-tip":p.tip}),p.label?s.jsx("text",{className:"donut-label",x:p.label.x,y:p.label.y,textAnchor:"middle",dominantBaseline:"central",children:p.label.text}):null]},p.key))}),s.jsxs("div",{className:"donut-center",children:[s.jsx("div",{className:"donut-center-value",children:d.totalText}),s.jsx("div",{className:"donut-center-label",children:r("总 Token")})]})]}),s.jsx("div",{className:"donut-legend",children:d.legend.map(p=>s.jsxs("div",{className:"donut-legend-row","data-tip":p.tip,children:[s.jsx("span",{className:"donut-dot",style:{background:p.fill}}),s.jsx("span",{className:"donut-legend-name",title:p.name,children:p.name}),s.jsx("span",{className:"donut-legend-tokens",children:p.tokensText}),s.jsx("span",{className:"donut-legend-percent",children:p.percentText})]},p.key))})]}):oc(f)})})]})}function CQ({view:e}){return s.jsxs("svg",{className:"report-svg",width:e.width,height:e.height,viewBox:`0 0 ${e.width} ${e.height}`,role:"img","aria-label":r("近 365 天活跃热力图"),children:[e.months.map(t=>s.jsx("text",{className:"hm-axis",x:t.x,y:10,children:t.text},t.key)),e.weekdays.map(t=>s.jsx("text",{className:"hm-axis",x:t.x,y:t.y,textAnchor:"end",dominantBaseline:"middle",children:t.text},t.key)),e.cells.map(t=>s.jsx("rect",{className:`hm-cell l${t.level}`,x:t.x,y:t.y,width:t.size,height:t.size,rx:2,tabIndex:-1,"data-tip":t.tip},t.key))]})}function FD({line:e,cls:t,radius:a}){return s.jsxs(s.Fragment,{children:[s.jsx("polyline",{className:`chart-line ${t}`,points:e.points}),e.dots.map(o=>s.jsx("circle",{className:`chart-dot ${t}`,cx:o.cx,cy:o.cy,r:a},o.key))]})}function M0({items:e,cls:t}){return s.jsx(s.Fragment,{children:e.map(a=>s.jsx("text",{className:`chart-label ${t}`,x:a.x,y:a.y,textAnchor:a.anchor,transform:a.rotate?`rotate(-90 ${a.x} ${a.y})`:void 0,dominantBaseline:a.rotate?"central":void 0,children:a.text},a.key))})}function TQ({view:e}){const{width:t,height:a,plotLeft:o,plotW:c,plotTop:d,plotH:f}=e;return s.jsxs(s.Fragment,{children:[s.jsxs("svg",{className:"report-svg",width:t,height:a,viewBox:`0 0 ${t} ${a}`,role:"img","aria-label":r("近 24 小时缓存命中率与总 Token 趋势"),children:[e.grid.map(p=>s.jsx("line",{className:"chart-grid",x1:o,y1:p.y,x2:zt(o+c),y2:p.y},p.key)),e.rateTicks.map(p=>s.jsx("text",{className:"chart-axis",x:p.x,y:p.y,textAnchor:p.anchor,dominantBaseline:"middle",children:p.text},p.key)),e.tokenTicks.map(p=>s.jsx("text",{className:"chart-axis",x:p.x,y:p.y,textAnchor:p.anchor,dominantBaseline:"middle",children:p.text},p.key)),e.tokenLine?s.jsx(FD,{line:e.tokenLine,cls:"tokens",radius:e.dotR}):null,s.jsx(FD,{line:e.rateLine,cls:"rate",radius:e.dotR}),s.jsx(M0,{items:e.tokenLabels,cls:"tokens"}),s.jsx(M0,{items:e.rateLabels,cls:"rate"}),e.hourTicks.map(p=>s.jsx("text",{className:"chart-axis",x:p.x,y:p.y,textAnchor:p.anchor,children:p.text},p.key))]}),e.hits?s.jsx("svg",{className:"report-svg chart-hit-layer",width:t,height:a,viewBox:`0 0 ${t} ${a}`,"aria-hidden":"true",children:e.hits.map(p=>s.jsx("rect",{className:"chart-hit",x:p.x,y:d,width:p.width,height:zt(f),tabIndex:-1,"data-tip":p.tip,"data-tip-variant":"plain"},p.key))}):null]})}function EQ({view:e}){const{width:t,height:a,plotLeft:o,plotW:c,plotTop:d,plotH:f}=e;return s.jsxs(s.Fragment,{children:[s.jsxs("svg",{className:"report-svg",width:t,height:a,viewBox:`0 0 ${t} ${a}`,role:"img","aria-label":r("按天 Token 趋势"),children:[e.grid.map(p=>s.jsx("line",{className:"chart-grid",x1:p.x1,y1:p.y,x2:p.x2,y2:p.y},p.key)),e.valueTicks.map(p=>s.jsx("text",{className:"chart-axis",x:p.x,y:p.y,textAnchor:p.anchor,dominantBaseline:"middle",children:p.text},p.key)),e.bars.map(p=>p.nativeTip?s.jsx("rect",{className:"bar",x:p.x,y:p.y,width:p.width,height:p.height,rx:p.rx,children:s.jsx("title",{children:p.tip})},p.key):s.jsx("rect",{className:"bar",x:p.x,y:p.y,width:p.width,height:p.height,rx:p.rx},p.key)),s.jsx(M0,{items:e.barLabels,cls:"tokens"}),e.dateTicks.map(p=>s.jsx("text",{className:"chart-axis",x:p.x,y:p.y,textAnchor:p.anchor,children:p.text},p.key)),e.blank?s.jsx("text",{className:"chart-empty",x:zt(o+c/2),y:zt(d+f/2),textAnchor:"middle",children:r("所选范围内暂无请求")}):null]}),e.hits?s.jsx("svg",{className:"report-svg chart-hit-layer",width:t,height:a,viewBox:`0 0 ${t} ${a}`,"aria-hidden":"true",children:e.hits.map(p=>s.jsx("rect",{className:"chart-hit",x:p.x,y:d,width:p.width,height:zt(f),tabIndex:-1,"data-tip":p.tip,"data-tip-variant":"plain"},p.key))}):null]})}const AQ=680;function D0(){const e=x.useRef(null),[t,a]=x.useState(0);return x.useLayoutEffect(()=>{const o=e.current;if(!o)return;let c=o.clientWidth;if(c&&a(c),typeof ResizeObserver!="function")return;const d=new ResizeObserver(()=>{const f=o.clientWidth;!f||f===c||(c=f,a(f))});return d.observe(o),()=>d.disconnect()},[]),[e,t||AQ]}async function $D(){if(!A0)try{const e=await ac().workbuddyDesktop?.getScheduledTasks(),t=(e?.tasks||[]).find(a=>a.id==="reportAutoRefresh");I0(t||null),A0=Array.isArray(e?.tasks)}catch(e){console.warn("读取报表自动刷新间隔失败,按默认 1 秒:",e instanceof Error?e.message:e),I0(null)}}function I0(e){A0=!0;const t=Number(e?.interval);!!e&&typeof e=="object"&&Number.isFinite(t)&&t>0&&(e.unit==="seconds"||e.unit==="minutes")?(hx=e.enabled!==!1,mx=e.unit==="minutes"?t*6e4:t*1e3):(hx=!0,mx=HD),lc?.applyAutoConfig(mx,hx)}const RQ={summary:null,error:null};function OQ(){const[e,t]=x.useState(RQ),[a,o]=x.useState(KX),[c,d]=x.useState(XX),[f,p]=x.useState(()=>({ms:mx,enabled:hx})),[,h]=x.useState(0),b=x.useRef(a),y=x.useRef(c),v=x.useRef(""),j=x.useCallback((Y,re)=>{v.current=re,t(Y)},[]),w=x.useCallback(async(Y={})=>{$D();const re=++O0,M=b.current,U=M===k0?y.current:void 0;try{const Z=ac().workbuddyDesktop;if(!Z)throw new Error("后端桥不可用");const ae=await Z.getStatsSummary(M,U);if(re!==O0)return Vo;if(!ae||typeof ae!="object")throw new Error("后端未返回报表数据");Vo=ae;const ce=JSON.stringify(ae);return ce!==v.current&&j({summary:ae,error:null},ce),Vo}catch(Z){if(re!==O0)return Vo;const ae=Z instanceof Error?Z.message:"";return Vo=null,Y.silent||console.warn("读取报表数据失败:",ae),j({summary:null,error:ae||r("未知错误")},""),null}},[j]),S=x.useCallback(Y=>{Y!==void 0&&(Vo=Y||null,Y&&j({summary:Y,error:null},JSON.stringify(Y)))},[j]),N=x.useCallback(Y=>{const re=ix.includes(Y)?Y:N0;if(re!==b.current){b.current=re,o(re);try{localStorage.setItem(TD,re)}catch{}w()}},[w]),k=x.useCallback(Y=>{const re={...y.current,...Y};if(!cx.test(re.start)||!cx.test(re.end))return;const M=re.start<=re.end?re:Y.start?{start:re.start,end:re.start}:{start:re.end,end:re.end};y.current=M,d(M),QX(M),b.current===k0&&w()},[w]);x.useEffect(()=>{if(lc={load:w,render:S,applyAutoConfig:(Y,re)=>p({ms:Y,enabled:re})},gx){const Y=gx;gx=null,S(Y.data)}return ac().wbApp?.currentPage==="overview"&&w({silent:!0}),()=>{lc=null}},[w,S]),x.useEffect(()=>{if(!f.enabled||f.ms<=0)return;const Y=window.setInterval(()=>{document.hidden||ac().wbApp?.currentPage!=="overview"||R0||(R0=!0,w({silent:!0}).finally(()=>{R0=!1}))},f.ms);return()=>{window.clearInterval(Y)}},[f,w]),x.useEffect(()=>{const Y=()=>h(re=>re+1);return window.addEventListener("wb-units-changed",Y),()=>window.removeEventListener("wb-units-changed",Y)},[]);const A=D0(),P=D0(),R=D0(),{summary:C,error:E}=e,D=C&&ix.includes(C.range??"")?C.range:a,I=C&&Array.isArray(C.heatmap)?C.heatmap:[],O=C&&Array.isArray(C.cacheTrend24h)?C.cacheTrend24h:[],_=C&&Array.isArray(C.dailyTrend)?C.dailyTrend:[],z=C&&Array.isArray(C.providers)?C.providers:null,H=C&&Array.isArray(C.accounts)?C.accounts:null,B=C&&Array.isArray(C.models)?C.models:null,L=E?oc(r("读取报表失败:{error}",{error:E}),"empty report-error"):null,F=oc(r("正在加载…")),$=L??F,X=x.useMemo(()=>LD(B||[]),[B]),K=x.useCallback(Y=>X.get(Y)||lQ,[X]),q=C?dQ(I,A[1]):null,Q=C?mQ(O,P[1],K):null,G=C?gQ(_,R[1],K):null,J=C0(new Date);return s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("统计概览")}),s.jsx("span",{className:"panel-sub",id:"report-range-label",children:AD[D]||""}),s.jsxs("div",{className:"head-actions",children:[s.jsx(En,{options:xQ,value:a,onValueChange:N,"aria-label":r("报表时间范围")}),a===k0?s.jsxs("span",{className:"report-date-range",children:[s.jsx(Fe,{type:"date",className:"report-date-input w-[132px]",value:c.start,max:c.end||J,"aria-label":r("开始日期"),title:r("起始日期(含当天)"),onChange:Y=>k({start:Y.target.value})}),s.jsx("span",{className:"report-date-sep","aria-hidden":"true",children:"~"}),s.jsx(Fe,{type:"date",className:"report-date-input w-[132px]",value:c.end,min:c.start||void 0,max:J,"aria-label":r("结束日期"),title:r("结束日期(含当天)"),onChange:Y=>k({end:Y.target.value})})]}):null]})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{className:"field-grid",id:"report-overview",children:C?s.jsx(kQ,{summary:C,range:D}):$})})]}),s.jsxs("div",{className:"report-rank-grid",children:[H?s.jsx(UD,{panelId:"report-accounts-panel",listId:"report-accounts",labelId:"report-accounts-label",title:r("Top 账号"),tip:bQ,rows:PD(H)||[],totalText:_D(H),withBadge:!0,emptyText:r("所选范围内还没有账号用量")}):null,z?s.jsx(UD,{panelId:"report-providers-panel",listId:"report-providers",labelId:"report-providers-label",title:r("Top 提供商"),tip:vQ,rows:PD(z)||[],totalText:_D(z),withBadge:!1,emptyText:r("所选范围内还没有请求记录")}):null]}),s.jsxs("div",{className:"report-donut-grid",children:[B?s.jsx(VD,{panelId:"report-models-panel",listId:"report-models-donut",title:r("模型用量"),tip:yQ,ariaLabel:r("模型用量占比"),view:zD(B,r("未知模型")),emptyText:r("所选范围内还没有模型用量")}):null,z?s.jsx(VD,{panelId:"report-providers-pie-panel",listId:"report-providers-donut",title:r("提供商用"),tip:jQ,ariaLabel:r("提供商用占比"),view:zD(z,r("未知")),emptyText:r("所选范围内还没有提供商用量")}):null]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("活跃热力图")}),s.jsx("span",{className:"panel-sub",children:r("近 365 天,按天 Token 用量着色(档位按本窗口峰值自动定)")})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{className:"heat-wrap",id:"report-heatmap",ref:A[0],children:C?q?s.jsx(CQ,{view:q}):oc(r("暂无热力图数据")):$})}),s.jsxs("div",{className:"panel-foot",children:[s.jsx("span",{className:"heat-scale-label",children:r("Token")}),s.jsx("span",{className:"heat-legend",id:"report-heat-legend",children:C?fQ(BD(I)).map(Y=>s.jsxs("span",{className:"heat-legend-item",children:[s.jsx("span",{className:`l${Y.level}`}),Y.text]},Y.level)):null}),s.jsx("div",{className:"spacer"}),s.jsx("span",{children:r("方格为本地自然日,无请求显示为空槽")})]})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("缓存命中率")}),s.jsx(xx,{text:wQ})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{className:"field-grid",id:"report-cache-rates",children:C?pQ(C.cacheRates).map(Y=>s.jsxs("div",{className:"field rate",children:[s.jsx("div",{className:"label",children:Y.label}),s.jsxs("div",{className:"value big",children:[Y.value,s.jsx("div",{className:"sub",children:Y.sub})]})]},Y.key)):$})})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("近 24 小时缓存命中率趋势")}),s.jsx("span",{className:"panel-sub",children:r("按本地整点,无请求的整点不标注")})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{className:"chart-wrap",id:"report-cache-trend",ref:P[0],children:C?Q?s.jsx(TQ,{view:Q}):oc(r("暂无缓存趋势数据")):$})}),s.jsxs("div",{className:"panel-foot",children:[s.jsxs("span",{className:"chart-legend",children:[s.jsx("span",{className:"swatch rate"}),r("缓存命中率"),s.jsx("span",{className:"axis-hint",children:r("左轴")})]}),s.jsxs("span",{className:"chart-legend",children:[s.jsx("span",{className:"swatch tokens"}),r("总 Token"),s.jsx("span",{className:"axis-hint",children:r("右轴")})]}),s.jsx("div",{className:"spacer"}),s.jsx("span",{children:r("无请求的整点按 0% 计,但不写读数")})]})]}),s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("按天 Token 趋势")}),s.jsx(xx,{text:SQ}),s.jsx("span",{className:"panel-sub",id:"report-trend-label",children:C?`${C.startDate||"—"} ~ ${C.endDate||"—"}`:"—"})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{className:"chart-wrap tall",id:"report-daily-trend",ref:R[0],children:C?G?s.jsx(EQ,{view:G}):oc(r("暂无趋势数据")):$})})]})]})}async function MQ(e={}){return lc?lc.load(e):Vo}function DQ(e){if(e!==void 0){if(!lc){gx={data:e??null};return}lc.render(e)}}function IQ(){return Vo}window.wbReport={load:MQ,render:DQ,lastSummary:IQ,applyAutoRefresh:I0};const qD='.page[data-page="overview"]';let GD=!1;function KD(){if(GD)return;const e=document.querySelector(qD);e&&(GD=!0,e.replaceChildren(),wn.createRoot(e).render(s.jsx(OQ,{})))}document.querySelector(qD)?KD():document.addEventListener("DOMContentLoaded",KD,{once:!0}),$D();const P0={all:{title:r("全部删除"),desc:r("删除日志记录本身。当前有筛选时只删命中的条目,按天聚合报表不受影响;无筛选时清空全部并重置报表。此方式无法恢复。")},raw:{title:r("仅清空报文原文"),desc:r("保留统计行与报表,只抹掉请求 / 响应正文。清理后详情弹窗的「预览对话」将无原文可看,统计数字分毫不动。")}},PQ=["all","raw"],_Q=3e3;function Pl(){return window}let yp=!1;function jp(e,t){Pl().wbApp?.toast?.(e,t)}function LQ(e){const t=Math.max(0,Number(e)||0);if(t<1024)return`${Math.round(t)} B`;const a=t/1024;if(a<1024)return`${a.toFixed(1)} KB`;const o=a/1024;return o<1024?`${o.toFixed(1)} MB`:`${(o/1024).toFixed(2)} GB`}function _0(){const e=Pl().wbRequestsPanel?.clearParams?.();return typeof e=="string"?e:""}function YD(e){return{all:Number(e?.all)||0,raw:Number(e?.raw)||0,dbBytes:Number(e?.dbBytes)||0,vacuumRunning:e?.vacuumRunning===!0}}function L0(e){return e instanceof Error?e.message:String(e)}function XD(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t]??t)}function zQ({onClose:e}){const[t,a]=x.useState("all"),[o,c]=x.useState({status:"loading"}),[d,f]=x.useState(""),[p,h]=x.useState(!1),[b,y]=x.useState(!1),v=x.useRef(!0),j=x.useRef(null),w=x.useCallback(D=>{yp=D,y(D)},[]),S=x.useCallback(()=>{j.current!==null&&(window.clearInterval(j.current),j.current=null)},[]),N=x.useCallback(D=>{c({status:"ready",all:D.all,raw:D.raw,dbBytes:D.dbBytes}),h(D.vacuumRunning)},[]),k=x.useCallback(async()=>{const D=Pl().workbuddyDesktop;if(!D)return;let I;try{I=await D.getStatsClearPreview(_0())}catch{return}if(!v.current)return;const O=YD(I);N(O),!O.vacuumRunning&&(S(),f(""),jp(r("✅ 压缩完成")),Pl().wbRequestsPanel?.load?.({silent:!0}))},[N,S]),A=x.useCallback(()=>{j.current===null&&(h(!0),j.current=window.setInterval(()=>{k()},_Q))},[k]);async function P(){c({status:"loading"});try{const D=Pl().workbuddyDesktop;if(!D)throw new Error(r("后端桥不可用"));const I=await D.getStatsClearPreview(_0());if(!v.current)return;const O=YD(I);N(O),O.vacuumRunning&&A()}catch(D){if(!v.current)return;c({status:"failed"}),f(r("预览读取失败:{error}",{error:L0(D)}))}}x.useEffect(()=>(v.current=!0,P(),()=>{v.current=!1,S(),yp=!1}),[]);async function R(){const D=Pl().workbuddyDesktop;if(D){h(!0);try{await D.compactStatsDb(),f(r("压缩已在后台开始,完成后会自动提示")),A()}catch(I){const O=L0(I);if(/409|正在进行/.test(O)){jp(r("压缩已在进行中")),f(r("压缩已在进行中,接上它的进度等待完成")),A();return}h(!1),f(r("压缩失败:{message}",{message:O})),jp(r("压缩失败:{message}",{message:O}),"err")}}}async function C(){if(yp)return;const D=Pl().workbuddyDesktop,I=Pl().wbConfirm?.ask;if(!D||!I)return;const O=P0[t].title,_=_0(),z=_.length>0,H=o.status==="ready"?t==="raw"?o.raw:o.all:0;if(await I({title:r("清理请求日志"),html:z?r("确定按当前筛选执行「{label}」?将处理 {count} 条,此操作无法恢复。",{label:XD(O),count:H}):r("确定对全部请求日志执行「{label}」?将处理 {count} 条,此操作无法恢复。",{label:XD(O),count:H}),okText:r(t==="raw"?"抹掉原文":"删除"),okClass:"danger"})){w(!0);try{const L=z?`mode=${t}&${_}`:`mode=${t}&all=1`,F=await D.clearStatsRequests(L),$=Number(F?.deleted)||0;jp(t==="raw"?r("已抹掉 {count} 条报文原文",{count:$}):r("已删除 {count} 条",{count:$})),w(!1),e(),Pl().wbRequestsPanel?.notifyCleared?.()}catch(L){const F=L0(L);f(r("清理失败:{message}",{message:F})),jp(r("清理失败:{message}",{message:F}),"err"),w(!1)}}}const E=o.status==="ready"?r("将删除 {count} 条日志 · 其中 {raw} 条仍带报文原文 · 数据库占用 {db}",{count:o.all,raw:o.raw,db:LQ(o.dbBytes)}):o.status==="failed"?r("预览读取失败"):r("正在统计…");return s.jsx(Yt,{open:!0,onOpenChange:(D,I)=>{if(!D){if(yp){I.cancel();return}e()}},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("清理请求日志")})}),s.jsxs(Lt,{children:[s.jsx(Ph,{"aria-label":r("清理方式"),value:t,onValueChange:D=>a(D==="raw"?"raw":"all"),children:PQ.map(D=>{const I=t===D;return s.jsxs("label",{"data-checked":I?"":void 0,className:"group/card grid cursor-pointer grid-cols-[auto_minmax(0,1fr)] items-center gap-x-2.5 gap-y-0.5 rounded-md border border-border px-3 py-2.5 transition-colors duration-150 ease-out hover:border-border-strong data-checked:border-primary data-checked:bg-primary-soft",children:[s.jsx(jo,{value:D,className:"row-span-2 self-center"}),s.jsx("span",{className:"text-[13px] font-semibold text-foreground group-data-checked/card:text-primary-fg",children:P0[D].title}),s.jsx("span",{className:"text-[11.5px] leading-[1.55] text-subtle",children:P0[D].desc})]},D)})}),s.jsx("p",{className:"text-[11.5px] text-muted-foreground",children:E})]}),s.jsxs(tn,{children:[s.jsx("span",{className:"min-w-0 text-[11.5px] text-muted-foreground",children:d}),s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",size:"sm",onClick:()=>{R()},disabled:p,title:r(p?"压缩正在后台执行,完成后自动恢复":"回收已删除数据占用的磁盘空间(checkpoint + VACUUM,后台执行)"),children:r(p?"压缩中…":"压缩数据库")}),s.jsx(ne,{variant:"outline",onClick:e,disabled:b,children:r("取消")}),s.jsx(ne,{variant:"destructive",onClick:()=>{C()},disabled:b,children:r(b?"清理中…":"执行清理")})]})]})})}let wp=null,Qu=null;function QD(){wp&&(wp.unmount(),wp=null),Qu&&(Qu.remove(),Qu=null)}function BQ(){yp||QD()}function HQ(){QD(),Qu=document.createElement("div"),document.body.append(Qu),wp=wn.createRoot(Qu),wp.render(s.jsx(zQ,{onClose:BQ}))}window.wbRequestClearModal={open:HQ};const ZD=[{value:"detail",label:r("请求详情")},{value:"preview",label:r("预览对话")},{value:"raw",label:r("原始报文")}],UQ="detail",WD="border-b border-hairline px-2.5 py-[7px] align-top",_l="border border-hairline px-2 py-1 align-top",VQ="border border-hairline bg-surface-2 px-2 py-1 text-[11.5px] tracking-normal",bx="hover:[&>td]:bg-transparent";function Zu(){return window}function z0(){const e=Zu().workbuddyDesktop;if(!e)throw new Error(r("后端桥不可用"));return e}let B0=0;function FQ(e){if(!e)return"—";const t=new Date(e),a=o=>String(o).padStart(2,"0");return`${t.getFullYear()}-${a(t.getMonth()+1)}-${a(t.getDate())} ${a(t.getHours())}:${a(t.getMinutes())}:${a(t.getSeconds())}`}function JD(e){const t=Math.round(Number(e)||0);if(t<1e3)return`${t}ms`;const a=Math.floor(t/1e3);if(a>=60)return r("{m}分{s}秒",{m:Math.floor(a/60),s:a%60});const o=t%1e3;return o>0?r("{s}秒{ms}ms",{s:a,ms:o}):r("{n}秒",{n:a})}function $Q(e){const t=Number(e);return!Number.isFinite(t)||t<=0?"-":JD(t)}const qQ=r("生成速度:输出 token ÷ 流式段用时(总用时 − 首响);流式段不足总用时一半时按总用时计");function GQ(e){const t=Number(e.completionTokens)||0,a=Number(e.durationMs)||0;if(t<=0||a<=0)return"";const o=Number(e.firstResponseMs),c=Number.isFinite(o)&&o>0&&o=500&&c*2>=a,f=t/((d?c:a)/1e3);return`${f>=100?Math.round(f):f.toFixed(1)} tok/s`}function vx(e){return Zu().wbUnits?.formatTokens?.(e)??(Number(e)||0).toLocaleString("zh-CN")}function KQ(e){const t=Math.max(0,Date.now()-(Number(e)||0));return r("{n}秒",{n:Math.max(1,Math.floor(t/1e3))})}function YQ(e){const t=Number(e.status)||0;return t>=200&&t<300&&!e.error}function yx(e){return(Number(e?.status)||0)===0&&!e?.error}function eI(e){const t=String(e.provider??"").trim();return String(e.providerLabel??"").trim()||(t?Zu().wbProviders?.labelOf?.(t)||t:"")}function Sp(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function H0(e){if(e==null)return"";if(typeof e=="string")return e;try{return JSON.stringify(e,null,2)}catch{return""}}function U0(e){return e instanceof Error?e.message:String(e)}const tI=[{label:r("请求头"),pick:e=>H0(e.requestHeaders)},{label:r("请求体"),pick:e=>H0(e.requestBody)},{label:r("响应头"),pick:e=>H0(e.responseHeaders)},{label:r("响应体"),pick:e=>e.responseBody?String(e.responseBody):""}];function jx({text:e,loading:t=!1}){return s.jsxs("div",{className:"flex items-center justify-center gap-2 py-6 text-center text-[12.5px] text-muted-foreground",children:[t?s.jsx(of,{className:"size-3.5"}):null,e]})}function pa({label:e,children:t}){return s.jsxs(Cl,{className:bx,children:[s.jsx("th",{scope:"row",className:`w-[76px] whitespace-nowrap text-left text-[12.5px] font-semibold text-muted-foreground ${WD}`,children:e}),s.jsx(jn,{className:WD,children:t})]})}function XQ({row:e}){if(yx(e)){const c=Zu().wbRequestPhase;if(!c?.badgeHtml)return s.jsxs(ze,{variant:"info",shape:"tag",title:r("请求正在转发中,用时列显示的是已用时"),children:[s.jsx(my,{className:"req-live-dot"}),r("进行中")]});const d=c.elapsedLineHtml?.(e)??"";return s.jsxs("span",{className:"inline-flex flex-wrap items-center gap-1.5",children:[s.jsx("span",{dangerouslySetInnerHTML:{__html:c.badgeHtml(e)}}),d?s.jsx("span",{className:"text-[11.5px] text-muted-foreground",dangerouslySetInnerHTML:{__html:d}}):null]})}const t=Number(e.status)||0,a=YQ(e),o=!a&&t>=200&&t<300?r("HTTP {status},但响应体阶段出错",{status:t}):"";return s.jsx(ze,{variant:a?"success":"destructive",shape:"tag",title:o||void 0,children:t||r("失败")})}function QQ({row:e}){const t=String(e.clientModel??"").trim(),a=String(e.upstreamModel??"").trim(),o=String(e.model??"").trim(),c=String(e.clientReasoning??"").trim(),d=String(e.upstreamReasoning??"").trim(),f=(b,y)=>y?`${b}(${y})`:b;if(!t||!a||a.toLowerCase()===t.toLowerCase())return s.jsx(s.Fragment,{children:f(o,d||c)||"—"});const p=f(a,d),h=f(t,c);return s.jsxs("span",{className:"flex flex-col gap-0.5",children:[s.jsxs("span",{title:r("转发到上游的模型:{model}",{model:p}),children:["⬆️ ",p]}),s.jsxs("span",{className:"text-muted-foreground",title:r("下游请求的模型:{model}",{model:h}),children:["⬇️ ",h]})]})}function ZQ(e){const t=Number(e?.status),a=e?.status!==null&&e?.status!==void 0&&Number.isFinite(t),o=e?.error?String(e.error):"";if(o){const c=a?r("失败({status}):{error}",{status:String(t),error:o}):r("失败:{error}",{error:o});return s.jsx("span",{className:"break-words text-destructive",children:c})}return a?s.jsx("span",{className:"text-success",children:r("成功({status})",{status:String(t)})}):s.jsx("span",{className:"text-muted-foreground",children:r("无结果记录")})}function nI(e){const t=Number(e);return!Number.isFinite(t)||t<=0?null:t>=1024*1024?`${(t/1024/1024).toFixed(1)} MB`:t>=1024?`${Math.round(t/1024)} KB`:`${t} B`}function WQ({row:e}){const t=Array.isArray(e.attemptDetails)?e.attemptDetails:[];if(!t.length)return s.jsx(s.Fragment,{children:"-"});const a=Number(e.attempts)||1;return s.jsxs(Zy,{className:"text-[11.5px]",children:[s.jsx(AE,{children:s.jsx(Cl,{className:bx,children:[r("轮次"),r("提供商"),r("账号"),r("结果"),r("体积"),r("已下发"),r("内部重试"),r("提示")].map(o=>s.jsx(RE,{className:VQ,children:o},o))})}),s.jsxs(Wy,{children:[t.map((o,c)=>{const d=Array.isArray(o?.retries)?o.retries:[],f=d.map(N=>{const k=Number(N?.delayMs),A=Number.isFinite(k)&&k>0?k>=1e3?r(",{n}秒后重试",{n:Math.round(k/1e3)}):r(",{n}毫秒后重试",{n:Math.round(k)}):"";return`${String(N?.reason||r("未知原因"))}${A}`}).join(` +`),p=String(o?.notice??"").trim(),h=Number(o?.bodyBytes),b=nI(o?.bodyBytes)??"—",y=Number.isFinite(h)&&h>0?r("实际发给上游 {bytes} 字节",{bytes:h.toLocaleString()}):void 0,v=o?.forwardedBytes===void 0||o?.forwardedBytes===null,j=Number(o?.forwardedBytes),w=nI(o?.forwardedBytes)??(v?"—":"0 B"),S=v?void 0:r("实际下发给客户端 {bytes} 字节",{bytes:(Number.isFinite(j)?j:0).toLocaleString()});return s.jsxs(Cl,{className:bx,children:[s.jsx(jn,{className:_l,children:c+1}),s.jsx(jn,{className:_l,children:eI(o)||(o?.provider?String(o.provider):r("未知"))}),s.jsx(jn,{className:_l,children:o?.account?String(o.account):"—"}),s.jsx(jn,{className:_l,children:ZQ(o)}),s.jsx(jn,{className:`${_l} whitespace-nowrap`,title:y,children:b}),s.jsx(jn,{className:`${_l} whitespace-nowrap`,title:S,children:w}),s.jsx(jn,{className:`${_l} whitespace-nowrap`,title:f||void 0,children:d.length?r("↻ {n} 次",{n:d.length}):"-"}),s.jsx(jn,{className:_l,children:p||"—"})]},c)}),t.lengths.jsxs("li",{className:"py-0.5",children:[s.jsxs("b",{children:[y+1,". ",String(b.name||b.id||"—")]}),s.jsxs("span",{className:"text-muted-foreground",children:[" ",String(b.reason||"")]})]},String(b.id??y)))}):null,p.length?s.jsxs("div",{className:"mt-1 text-muted-foreground",children:[r("被跳过:"),p.map(b=>String(b.name||b.id||"")).join("、")]}):null,h?s.jsx("div",{className:"mt-1",children:r("最终承载:{picked}",{picked:h})}):null]})}function eZ({row:e}){if(!e)return s.jsx(jx,{text:r("列表数据已刷新,请重试(关闭后重新打开这条详情)")});const t=String(e.accountName??"").trim(),a=GQ(e);return s.jsx(Zy,{className:"text-[12.5px] text-subtle [&_tr:last-child>td]:border-b-0 [&_tr:last-child>th]:border-b-0",children:s.jsxs(Wy,{children:[s.jsx(pa,{label:r("时间"),children:FQ(e.ts)}),s.jsx(pa,{label:r("状态"),children:s.jsx(XQ,{row:e})}),s.jsx(pa,{label:r("耗时"),children:yx(e)?s.jsxs(s.Fragment,{children:[KQ(e.ts),s.jsx("span",{className:"text-[11.5px] text-muted-foreground",children:r("(请求仍在转发中)")})]}):r("{duration} / 首帧 {first}",{duration:JD(e.durationMs),first:$Q(e.firstResponseMs)})}),s.jsx(pa,{label:r("重试次数"),children:String(Number(e.attempts)||1)}),s.jsx(pa,{label:r("模型"),children:s.jsx(QQ,{row:e})}),s.jsxs(pa,{label:r("提供商"),children:[eI(e)||"—",t?s.jsx("span",{className:"text-[11.5px] text-muted-foreground",children:r("(账号:{account})",{account:t})}):null]}),s.jsxs(pa,{label:r("令牌"),children:[r("输入 {input} · 输出 {output} · 总计 {total} · 缓存读 {cache}",{input:vx(e.promptTokens),output:vx(e.completionTokens),total:vx(e.totalTokens),cache:vx(e.cacheReadTokens)}),a?s.jsxs("span",{title:qQ,children:[" · ",a]}):null]}),e.isTest?s.jsx(pa,{label:r("来源"),children:s.jsx(ze,{variant:"brand",shape:"tag",title:r("模型管理页操作列那颗「测试」发起的请求:与真实请求同一条转发链路,但不计入报表统计"),children:r("模型测试")})}):null,s.jsx(pa,{label:r("错误"),children:e.error?s.jsx("span",{className:"text-destructive",children:String(e.error)}):"—"}),s.jsx(pa,{label:r("尝试明细"),children:s.jsx(WQ,{row:e})}),e.route?s.jsx(pa,{label:r("路由回放"),children:s.jsx(JQ,{row:e})}):null,s.jsx(pa,{label:r("敏感词"),children:Array.isArray(e.sensitiveHits)&&e.sensitiveHits.length?s.jsx("span",{className:"inline-flex flex-wrap gap-1.5",children:e.sensitiveHits.map((o,c)=>s.jsxs(ze,{variant:"sensitive",shape:"tag",children:[String(o?.word??"")," × ",String(Number(o?.count)||0)]},c))}):"—"})]})})}function tZ({state:e}){if(!e)return s.jsx(jx,{text:r("正在读取…"),loading:!0});const t=e==="gone"?null:e.body,a=t&&Zu().wbConversationPreview?.render?.(t.requestBody,t.responseBody)||"";return a?s.jsxs("div",{children:[e!=="gone"&&e.truncated?s.jsx("div",{className:"mb-2 text-[11.5px] text-warning",children:r("正文超过单条上限,可能已被截断")}):null,s.jsx("div",{dangerouslySetInnerHTML:{__html:a}})]}):s.jsx(jx,{text:r("无报文原文(未开启「调试模式」,或已被清理)")})}function nZ({data:e}){const t=e.status===null||e.status===void 0?"-":String(e.status),a=[e.url?`URL: ${e.url}`:"",e.provider?r("提供商: {provider}",{provider:String(e.provider)}):"",r("上游状态码: {status}",{status:t})].filter(Boolean);return s.jsxs("div",{className:"flex flex-col gap-4",children:[a.length?s.jsx("div",{className:"border-b border-hairline pb-2.5 text-xs leading-[1.7] break-all text-subtle",children:a.map((o,c)=>s.jsx("div",{children:o},c))}):null,tI.map(o=>{const c=o.pick(e)||"";return s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx("h3",{className:"text-[11.5px] font-semibold tracking-[.02em] text-muted-foreground",children:o.label}),c?s.jsx("pre",{className:"max-h-80 overflow-auto rounded-md border border-border bg-surface-2 p-[10px_12px] font-mono text-[11.5px] leading-[1.6] break-words whitespace-pre-wrap text-subtle",children:c}):s.jsx(jx,{text:r("这条请求没有保存这一段报文")})]},o.label)})]})}function sZ({id:e,row:t,token:a,onClose:o}){const[c,d]=x.useState(UQ),[f,p]=x.useState(null),[h,b]=x.useState(null),[y,v]=x.useState("—"),[j,w]=x.useState("idle"),S=x.useRef(!0),N=x.useRef(null),k=x.useRef({detail:0,preview:0,raw:0}),A=x.useCallback(()=>!S.current||a!==B0,[a]);async function P(){try{const O=await z0().getStatsRequestRaw(e);if(A())return;const _=Sp(O)?O:null,z=String(_?.requestBody??"").trim()!==""||String(_?.responseBody??"").trim()!=="";p(_&&z?{body:_,truncated:!!_.truncated}:"gone")}catch(O){if(A())return;p("gone"),console.warn("读取原始正文失败(预览对话显示空态):",U0(O))}}async function R(){try{const O=await z0().getDebugTraffic(e);if(A())return;const _=Sp(O)?O:{};if(!tI.some(z=>(z.pick(_)||"").trim())){b("gone");return}b({data:_}),v(_.truncated?r("报文超过单条上限,请求体 / 响应体已按上限截断"):r("内容按原样保存,请求头中的凭据字段已替换为 [redacted]"))}catch(O){if(A())return;b("gone"),v(r("在设置 → 通用里开启「调试模式」后,新发生的请求才会保存报文")),console.warn("读取调试报文失败(原始报文标签隐藏):",U0(O))}}x.useEffect(()=>(S.current=!0,P(),R(),()=>{S.current=!1}),[]),x.useLayoutEffect(()=>{const O=N.current;O&&(O.scrollTop=k.current[c])},[c]);function C(O){if(O==="raw"&&!Sp(h)||O===c)return;const _=N.current;_&&(k.current[c]=_.scrollTop),d(O)}async function E(){if(!e||!yx(t))return;const O=Zu().wbConfirm?.ask;if(!(!O||!await O({title:r("终止请求"),html:r("确定终止这条正在转发的请求?
    上游连接会立即断开,明细将记为「请求已被手动终止」。"),okText:r("终止"),okClass:"danger"}))){w("pending");try{if(await z0().terminateStatsRequest(e),!S.current)return;w("accepted"),v(r("已受理终止:上游连接已断开,列表稍后刷新为「请求已被手动终止」"))}catch(z){if(!S.current)return;w("idle"),v(r("终止失败:{error}",{error:U0(z)}))}}}const D=Sp(h),I=x.useMemo(()=>D?ZD:ZD.filter(O=>O.value!=="raw"),[D]);return s.jsx(Yt,{open:!0,onOpenChange:O=>{O||o()},children:s.jsxs(Xt,{className:"w-[min(920px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("请求详情")})}),s.jsxs(Lt,{ref:N,className:"gap-3",children:[s.jsx(En,{options:I,value:c,onValueChange:C,"aria-label":r("详情内容"),className:"self-start"}),s.jsx("section",{hidden:c!=="detail",children:s.jsx(eZ,{row:t})}),s.jsx("section",{hidden:c!=="preview",children:s.jsx(tZ,{state:f})}),D?s.jsx("section",{hidden:c!=="raw",children:s.jsx(nZ,{data:h.data})}):null]}),s.jsxs(tn,{children:[s.jsx("span",{className:"min-w-0 text-[11.5px] text-muted-foreground",children:y}),s.jsx("div",{className:"mr-auto"}),yx(t)?s.jsx(ne,{variant:"destructive",disabled:j!=="idle",onClick:()=>{E()},title:r("断开上游连接,明细记为「请求已被手动终止」"),children:r("终止请求")}):null,s.jsx(ne,{variant:"outline",onClick:o,children:r("关闭")})]})]})})}let Np=null,Wu=null;function sI(){Np&&(Np.unmount(),Np=null),Wu&&(Wu.remove(),Wu=null)}function rI(){B0+=1,sI()}function rZ(e,t){const a=String(e??"").trim();if(!a)return;sI();const o=++B0;Wu=document.createElement("div"),document.body.append(Wu),Np=wn.createRoot(Wu),Np.render(s.jsx(sZ,{id:a,row:Sp(t)?t:null,token:o,onClose:rI}))}window.wbRequestDetail={open:rZ,close:rI};const aZ=150,lZ=80,ic=8,wx=8,oZ=520,iZ=220,cZ=1.1,uZ=900,V0=12,F0="req-hover-panel",dZ=["fixed z-[35] pointer-events-none","max-h-[min(360px,60vh)] overflow-x-hidden overflow-y-auto","rounded-md p-[10px_12px] text-[12px] leading-[1.6] break-normal","bg-surface-2 text-foreground shadow-3","border-0 dark:border dark:border-border-strong dark:bg-surface-3"].join(" "),kp="rh-row rh-dim min-w-0 break-words text-muted-foreground",fZ="rh-row min-w-0 break-words";function pZ(){return window}function $0(e){return typeof e=="object"&&e!==null?e:null}const aI=e=>Array.isArray(e?.attemptDetails)?e.attemptDetails:[],lI=e=>Array.isArray(e?.retries)?e.retries:[],mZ=e=>Array.isArray(e?.sensitiveHits)?e.sensitiveHits:[];function q0(e){const t=String(e??"").trim();return t?pZ().wbProviders?.labelOf?.(t)||t:""}function hZ(e){const t=$0(e);return(Number(t?.attempts)||1)>1?!0:aI(t).some(a=>lI(a).length>0||!!a?.notice)}function oI(e){return e.dataset.reqHover==="sensitive"?"sensitive":"chain"}function gZ({details:e}){if(e.length<2)return null;const t=e.map(a=>q0(a.provider)||r("未知"));return s.jsxs("div",{className:"rh-chain mb-1.5 border-b border-border pb-1.5 break-words",children:[s.jsx("span",{className:"rh-chain-k font-semibold text-muted-foreground",children:r("切换路径:")}),s.jsx("span",{className:"rh-chain-v font-semibold text-foreground",children:t.join(" → ")})]})}function xZ({item:e}){const t=lI(e);return t.length?s.jsxs("div",{className:"rh-retry mt-0.5 mb-1 ml-2.5 border-l-2 border-border-strong pl-2",children:[s.jsx("div",{className:"rh-retry-head text-[11.5px] font-semibold text-warning",children:r("↻ 重试 {n} 次",{n:t.length})}),t.map((a,o)=>{const c=String(a?.reason??"").trim(),d=Number(a?.status),f=a?.status!==null&&a?.status!==void 0&&Number.isFinite(d),p=Number(a?.delayMs),h=Number.isFinite(p)&&p>0?p>=1e3?r(",{n}秒后重试",{n:Math.round(p/1e3)}):r(",{n}毫秒后重试",{n:Math.round(p)}):"";return s.jsxs("div",{className:"rh-retry-row min-w-0 break-words",children:[s.jsx("span",{className:"rh-retry-why mr-1 text-subtle [overflow-wrap:anywhere]",children:c||r("未知原因")}),f?s.jsx("span",{className:"rh-retry-status mr-1 text-muted-foreground tabular-nums whitespace-nowrap",children:`HTTP ${String(d)}`}):null,h?s.jsx("span",{className:"rh-dim text-muted-foreground",children:h}):null]},o)})]}):null}function bZ({item:e,index:t,inFlight:a}){const o=q0(e.provider),c=String(e.account??"").trim(),d=Number(e.status),f=e.status!==null&&e.status!==void 0&&Number.isFinite(d),p=e.error?String(e.error):"",h=String(e.notice??"").trim();return s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:fZ,children:[s.jsx("span",{className:"rh-no mr-1 text-muted-foreground",children:r("尝试 {n} ·",{n:t+1})}),s.jsx("span",{className:"rh-who mr-1 font-semibold text-foreground",children:o||r("未知")}),c?s.jsx("span",{className:"rh-account mr-1 text-muted-foreground [overflow-wrap:anywhere]",children:c}):null,s.jsx("span",{className:"rh-arrow mr-1 text-muted-foreground",children:"→"}),p?s.jsx("span",{className:"rh-bad font-medium text-destructive [overflow-wrap:anywhere]",children:f?r("失败({status}):{error}",{status:String(d),error:p}):r("失败:{error}",{error:p})}):f?s.jsx("span",{className:"rh-ok font-medium text-success",children:r("成功({status})",{status:String(d)})}):a?s.jsx("span",{className:"rh-dim text-muted-foreground",children:r("进行中…")}):s.jsx("span",{className:"rh-dim text-muted-foreground",children:r("无结果记录")})]}),h?s.jsx("div",{className:"rh-notice mt-0.5 mb-1 ml-2.5 border-l-2 border-border-strong pl-2 text-warning [overflow-wrap:anywhere]",children:`⚠️ ${h}`}):null,s.jsx(xZ,{item:e})]})}function vZ({entry:e}){const t=Number(e.attempts)||1,a=aI(e);if(!a.length){const c=q0(e.provider),d=c?r("共 {n} 次尝试,最终由 {provider} 承载",{n:t,provider:c}):r("共 {n} 次尝试",{n:t});return s.jsxs(s.Fragment,{children:[s.jsx("div",{className:kp,children:d}),s.jsx("div",{className:kp,children:r("这条记录的尝试明细未采集(该字段上线前的旧数据,或请求在转发前就失败)")})]})}const o=(Number(e.status)||0)===0&&!e.error;return s.jsxs(s.Fragment,{children:[s.jsx(gZ,{details:a}),a.map((c,d)=>s.jsx(bZ,{item:c,index:d,inFlight:o&&d===a.length-1},d)),a.lengths.jsxs("div",{className:"rh-row rh-hit flex items-baseline justify-between gap-2.5",children:[s.jsx("span",{className:"rh-hit-w break-words text-foreground",children:String(a?.word??"")}),s.jsx("span",{className:"rh-hit-c text-muted-foreground tabular-nums whitespace-nowrap",children:`× ${String(Number(a?.count)||0)}`})]},o)),t.length>V0?s.jsx("div",{className:kp,children:r("另有 {n} 个词命中",{n:t.length-V0})}):null]}):s.jsx("div",{className:kp,children:r("这条记录命中了敏感词,但没有留下命中明细")})}function jZ({kind:e,entry:t,panelRef:a}){return s.jsx("div",{ref:a,id:F0,role:"tooltip","data-kind":e,className:dZ,children:e==="sensitive"?s.jsx(yZ,{entry:t}):s.jsx(vZ,{entry:t})})}let On=null,Fo=0,Cp=0,Tp=null,Sx=null,G0=null,K0=null,cc=null,uc=null,Ep=null,iI="chain";function Y0(){window.clearTimeout(Fo),window.clearTimeout(Cp),Fo=0,Cp=0,Tp=null}function wZ(){const e=Ep?.current,t=On;if(!e||!t)return;const a=t.getBoundingClientRect(),o=window.innerWidth-ic*2;e.style.maxWidth="none",e.style.width="max-content";const c=e.offsetWidth,d=iI==="sensitive",p=Math.max(d?iZ:oZ,c)*(d?cZ:1),h=Math.min(p,o,uZ);e.style.maxWidth=`${h}px`,e.style.width=d?`${h}px`:"auto";const b=e.offsetWidth,y=e.offsetHeight,v=window.innerHeight-a.bottom-wx-ic,j=a.top-wx-ic,w=vv,S=a.left+a.width/2,N=Math.max(ic,Math.min(S-b/2,window.innerWidth-ic-b)),k=w?a.top-wx-y:a.bottom+wx;e.style.left=`${Math.round(N)}px`,e.style.top=`${Math.round(Math.max(ic,Math.min(k,window.innerHeight-ic-y)))}px`}function cI(e){if(!On)return;const t=e.target;t instanceof Node&&(t===On||On.contains(t)||Oa())}function uI(){Oa()}function dI(){Oa()}let Nx=!1;function SZ(){Nx||(Nx=!0,document.addEventListener("click",cI,!0),window.addEventListener("scroll",uI,!0),window.addEventListener("resize",dI))}function NZ(){Nx&&(Nx=!1,document.removeEventListener("click",cI,!0),window.removeEventListener("scroll",uI,!0),window.removeEventListener("resize",dI))}function kZ(){NZ(),uc&&(uc.unmount(),uc=null),cc&&(cc.remove(),cc=null),Ep=null}function fI(e,t){iI=e,(!cc||!uc||!Ep)&&(cc=document.createElement("div"),document.body.append(cc),Ep=x.createRef(),uc=wn.createRoot(cc));const a=uc,o=Ep;Ts.flushSync(()=>{a.render(s.jsx(jZ,{kind:e,entry:t,panelRef:o}))}),SZ(),wZ()}function pI(e){if(!e.isConnected)return;const t=$0(G0?.(e));t&&(Y0(),On&&On!==e&&Oa(),On=e,fI(oI(e),t),e.classList.add("active"),e.setAttribute("aria-describedby",F0))}function Oa(){Y0(),On&&(On.classList.remove("active"),On.removeAttribute("aria-describedby"),On=null),kZ()}function CZ(){if(!On)return;const e=String(On.dataset.reqHover||""),t=String(On.dataset.reqId||"");K0=!!(e&&t)&&On.matches(":hover")&&!Cp?{action:"move",kind:e,key:t}:{action:"close"}}function TZ(){const e=K0;if(K0=null,!e||!On)return;if(e.action==="close"){Oa();return}const t=Sx?EZ(Sx,e.kind,e.key):null,a=t?$0(G0?.(t)):null;if(!t||!a){Oa();return}t!==On&&(On.classList.remove("active"),On.removeAttribute("aria-describedby"),On=t,fI(oI(t),a),t.classList.add("active"),t.setAttribute("aria-describedby",F0))}function EZ(e,t,a){for(const o of e.querySelectorAll("[data-req-hover]"))if(String(o.dataset.reqHover||"")===t&&String(o.dataset.reqId||"")===a)return o;return null}function kx(e){const t=e.target;return t instanceof Element?t.closest("[data-req-hover]"):null}function AZ(e){const t=kx(e);if(!t||!Sx?.contains(t)||(Y0(),On===t&&uc))return;const a=On?0:aZ;On&&Oa(),Tp=t,Fo=window.setTimeout(()=>{Fo=0,Tp=null,pI(t)},a)}function RZ(e){const t=kx(e);if(t){if(Tp===t){window.clearTimeout(Fo),Fo=0,Tp=null;return}On===t&&(window.clearTimeout(Fo),Fo=0,Cp=window.setTimeout(()=>{Cp=0,On===t&&Oa()},lZ))}}function OZ(e){const t=kx(e);t&&pI(t)}function MZ(e){const t=kx(e);t&&On===t&&Oa()}function DZ(e){e.key!=="Escape"||!On||Oa()}function IZ(e){const t=e?.host;t&&(Sx=t,G0=e?.entryOf??null,t.addEventListener("pointerover",AZ),t.addEventListener("pointerout",RZ),t.addEventListener("focusin",OZ),t.addEventListener("focusout",MZ),t.addEventListener("keydown",DZ))}window.wbRequestHover={bind:IZ,close:Oa,hasProcessFacts:hZ,beforeListRedraw:CZ,afterListRedraw:TZ};function As(){return window}function X0(e){return e instanceof Error?e.message:String(e)}function Cx(e){return e.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t]??t)}const Tx={connecting:{label:r("连接中"),cls:"phase-connecting"},waiting:{label:r("等待响应"),cls:"phase-waiting"},streaming:{label:r("响应中"),cls:"phase-streaming"},retrying:{label:r("重试中"),cls:"phase-retrying"},queued:{label:r("排队中"),cls:"phase-queued"}},mI=r("进行中"),PZ="running",hI={connecting:r("请求已受理,正在选路、取凭证、建立上游连接"),waiting:r("上游请求已发出,正在等第一个字节到达(模型的思考时间也在这段)"),streaming:r("首帧已到,上游内容正在下发"),retrying:r("本轮尝试失败,正在退避等待或切换到下一个账号"),queued:r("上游模型繁忙,请求已排进上游队列;网关正按上游建议的时长等待后重发(不是登录态或额度问题)"),"":r("请求正在转发中,用时列显示的是已用时")};function Q0(e){const t=String(e?.phase??"").trim().toLowerCase();return Object.prototype.hasOwnProperty.call(Tx,t)?t:""}function gI(e){const t=Q0(e);return t?Tx[t].label:mI}function xI(e){if(e==null||e==="")return"";const t=Number(e);if(!Number.isFinite(t)||t<0)return"";const a=Math.max(1,Math.floor(t/1e3));return a<60?r("{n}秒",{n:a}):r("{m}分{s}秒",{m:Math.floor(a/60),s:a%60})}function _Z(e){const t=e?.phaseElapsedMs;if(t!=null&&t!==""){const c=Number(t);if(Number.isFinite(c)&&c>=0)return c}const a=Number(e?.phaseStartedAt);if(!Number.isFinite(a)||a<=0)return null;const o=Date.now()-a;return Number.isFinite(o)?Math.max(0,o):null}function LZ(e){const t=Q0(e),a=t?Tx[t].label:mI,o=t?Tx[t].cls:PZ,c=hI[t]||hI[""];return`${Cx(a)}`}function zZ(e){const t=xI(_Z(e));if(!t)return"";const a=gI(e),o=r("进入「{phase}」阶段已持续 {time}",{phase:a,time:t});return`${Cx(t)}`}const Z0=[{key:"time",label:r("时间"),sel:".req-time",track:"92px"},{key:"target",label:r("提供商 / 账号"),sel:".req-target",track:"minmax(0, 1.1fr)"},{key:"retry",label:r("重试"),sel:".req-retry",track:"52px"},{key:"status",label:r("状态"),sel:".req-status",track:"96px"},{key:"model",label:r("模型"),sel:".req-model",track:"minmax(0, 1.3fr)"},{key:"client",label:r("客户端"),sel:".req-client",track:"110px"},{key:"dur",label:r("用时"),sel:".req-dur",track:"96px",align:"right"},{key:"usage",label:r("用量"),sel:".req-usage",track:"minmax(0, 1.6fr)"},{key:"error",label:r("错误"),sel:".req-error-cell",track:"minmax(0, 1.2fr)"},{key:"detail",label:r("详情"),sel:".req-detail",track:"60px",align:"right"}],bI=1e3,BZ=dg;function vI(e){return typeof e=="number"&&e>0?e:xu}const yI="workbuddy-desktop-logs-requests-range",jI="workbuddy-desktop-requests-filters",HZ=3e4,W0=["today","7","30","month","all"],J0="all",UZ={today:r("今天"),7:r("近 7 天"),30:r("近 30 天"),month:r("本月"),all:r("全部")},VZ={today:r("今天"),7:r("7 天"),30:r("30 天"),month:r("本月"),all:r("全部")},FZ=W0.map(e=>({value:e,label:VZ[e]})),wI=[{value:"",label:r("全部状态")},{value:"ok",label:r("成功")},{value:"error",label:r("失败")}],$Z=r("全部状态"),SI=r("全部提供商"),NI=r("全部模型"),Ex={status:"",provider:"",model:""};function Ax(e){return new Date(e.getFullYear(),e.getMonth(),e.getDate()).getTime()}function qZ(e){const t=new Date;switch(e){case"today":return Ax(t);case"7":return Ax(new Date(t.getFullYear(),t.getMonth(),t.getDate()-6));case"30":return Ax(new Date(t.getFullYear(),t.getMonth(),t.getDate()-29));case"month":return Ax(new Date(t.getFullYear(),t.getMonth(),1));default:return null}}const sn={filters:{...Ex},range:GZ(),runningOnly:!1,offset:0,size:vI(pg("requests",dg))};let Rx={...Ex},eS=!1,kI=!1,tS=[],Ox=bI,Mx=!0,nS=!1,sS=!1,CI=0,rS=0,aS=null,TI=!1,Ju=null;function GZ(){try{const e=localStorage.getItem(yI);return e!==null&&W0.includes(e)?e:J0}catch{return J0}}function KZ(e){try{localStorage.setItem(yI,e)}catch{}}function EI(){const e=As().wbFilterMemory;return e?(eS=!0,Rx=e.load(jI,{...Ex}),sn.filters={...Rx},{...Rx}):{...Ex}}function YZ(e){Rx={...e},As().wbFilterMemory?.save(jI,{status:e.status||"",provider:e.provider||"",model:e.model||""})}function AI(){const e=new URLSearchParams,t=qZ(sn.range);t!==null&&e.set("start",String(t));const a=sn.runningOnly?"running":sn.filters.status;return a&&e.set("status",a),sn.filters.provider&&e.set("provider",sn.filters.provider),sn.filters.model&&e.set("model",sn.filters.model),e}function XZ(){const e=AI();return e.set("offset",String(sn.offset)),e.set("limit",String(sn.size)),e.toString()}function lS(e){const t=Math.round(Number(e)||0);if(t<1e3)return`${t}ms`;const a=Math.floor(t/1e3);if(a>=60)return r("{m}分{s}秒",{m:Math.floor(a/60),s:a%60});const o=t%1e3;return o>0?r("{s}秒{ms}ms",{s:a,ms:o}):r("{n}秒",{n:a})}const QZ=r("生成速度:输出 token ÷ 流式段用时(总用时 − 首响);流式段不足总用时一半时按总用时计"),ZZ=500;function WZ(e){const t=Number(e.completionTokens)||0,a=Number(e.durationMs)||0;if(t<=0||a<=0)return"";const o=Number(e.firstResponseMs),c=Number.isFinite(o)&&o>0&&o=ZZ&&c*2>=a,f=t/((d?c:a)/1e3);return`${f>=100?Math.round(f):f.toFixed(1)} tok/s`}function JZ(e){const t=Number(e);return!Number.isFinite(t)||t<=0?"-":lS(t)}function Dx(e){return As().wbUnits?.formatTokens?.(e)??(Number(e)||0).toLocaleString("zh-CN")}function eW(e){const t=Math.max(0,Date.now()-(Number(e)||0));return r("{n}秒",{n:Math.max(1,Math.floor(t/1e3))})}function tW(e){const t=Number(e.promptTokens)||0;return t<=0?"-":`${((Number(e.cacheReadTokens)||0)/t*100).toFixed(2)}%`}function oS(e){const t=Number(e.status)||0;return t>=200&&t<300&&!e.error}function Ap(e){return(Number(e?.status)||0)===0&&!e?.error}const RI=e=>String(e?.id||e?.ts||"");function nW(e){return(Array.isArray(e?.attemptDetails)?e.attemptDetails:[]).reduce((a,o)=>{const c=o?.retries;return a+(Array.isArray(c)?c.length:0)},0)}function OI(e){return r(As().wbProviders?.labelOf?.(e)||e)}function sW(e){const t=new Set;return e.map((a,o)=>{const c=RI(a)||`i${o}`;let d=c,f=1;for(;t.has(d);)d=`${c}#${f++}`;return t.add(d),d})}async function MI(){if(!(Date.now()-rS({value:String(o?.id||""),label:r(String(o?.label||o?.id||""))})).filter(o=>o.value),a=(Array.isArray(e?.models)?e.models:[]).map(o=>String(o||"")).filter(Boolean);Ju?.setFilterOptions(t,a)}catch(e){console.warn("读取请求日志筛选清单失败,筛选项退化为「全部」:",X0(e))}}}async function rW(){if(!nS)try{const e=await As().workbuddyDesktop?.getScheduledTasks(),t=(e?.tasks||[]).find(a=>a.id==="requestsAutoRefresh");iS(t||null),nS=Array.isArray(e?.tasks)}catch(e){console.warn("读取请求日志自动刷新间隔失败,按默认 1 秒:",X0(e)),iS(null)}}function iS(e){nS=!0;const t=Number(e?.interval),a=e?.unit;!!e&&typeof e=="object"&&Number.isFinite(t)&&t>0&&(a==="seconds"||a==="minutes")?(Mx=e.enabled!==!1,Ox=a==="minutes"?t*6e4:t*1e3):(Mx=!0,Ox=bI),Ju?.setAuto({ms:Ox,enabled:Mx})}function aW(){if(TI)return;const e=As().wbColSettings;e&&(TI=!0,aS=e.register({id:"requests",label:r("请求日志表"),columns:Z0.map(({key:t,label:a,align:o})=>({key:t,label:a,align:o})),mount:()=>document.querySelector('.page[data-page="requests"] .panel-head .head-actions'),onChange:()=>{Ts.flushSync(()=>Ju?.repaint()),As().wbTableColumns?.repaint?.("requests")}}))}function cS(){return(aS?aS.apply(Z0):Z0).map(t=>({...t,align:t.align||"left"}))}const lW={entries:[],total:0,matched:0,running:0,loaded:!1,error:""};function oW(e){return e.loaded?e.total?r("没有符合筛选条件的请求"):r("暂无请求日志,网关还没有转发过请求"):r("正在加载请求日志…")}function iW(e){const t=Number(e.firstResponseMs);return!Number.isFinite(t)||t<=0?null:s.jsx("span",{className:"req-dur-line sub",title:r("首响:上游首帧到达的耗时(请求仍在转发中)"),children:r("首响 {time}",{time:lS(t)})})}function cW(e,t){const{align:a,key:o}=t,c=t.sel.slice(1)+(o==="dur"?" req-num":""),d=`${c} ta-${a}`;switch(o){case"time":{if(!e.ts)return s.jsx("span",{className:d,children:"—"},o);const f=new Date(e.ts),p=h=>String(h).padStart(2,"0");return s.jsxs("span",{className:d,children:[s.jsx("span",{children:`${f.getFullYear()}-${p(f.getMonth()+1)}-${p(f.getDate())}`}),s.jsx("span",{children:`${p(f.getHours())}:${p(f.getMinutes())}:${p(f.getSeconds())}`})]},o)}case"target":{const f=String(e.provider??"").trim(),p=String(e.providerLabel??"").trim(),h=p?r(p):f?OI(f):"";return s.jsxs("span",{className:d,children:[h?s.jsx("span",{className:"req-provider",title:f&&f!==h?r("{name}({id})",{name:h,id:f}):h,children:h}):s.jsx("span",{className:"req-provider is-empty",title:r("这条明细没有记录提供商(旧数据或请求未走到转发)"),children:"—"}),e.accountName?s.jsx("span",{className:"req-acct",title:e.accountId||e.accountName,children:e.accountName}):s.jsx("span",{className:"req-acct is-empty",title:r("请求在任何账号接手之前就失败了"),children:"—"})]},o)}case"retry":{const f=Number(e.attempts)||1,p=RI(e),h=[];if(e.isTest&&h.push(s.jsx(ze,{variant:"brand",shape:"tag",title:r("这条明细是一次模型测试(人工发起):走的是真实转发链路,但不计入报表统计"),children:r("测试")},"test")),As().wbRequestHover?.hasProcessFacts?.(e)??f>1){const y=f>1?f:nW(e);h.push(s.jsx(ze,{variant:"warning",shape:"tag",render:s.jsx("button",{type:"button"}),className:"req-hover-tag","data-req-hover":"chain","data-req-id":p,children:y>1?r("重试 {n}",{n:y}):r("重试")},"chain"))}return Array.isArray(e.sensitiveHits)&&e.sensitiveHits.length&&h.push(s.jsx(ze,{variant:"sensitive",shape:"tag",render:s.jsx("button",{type:"button"}),className:"req-hover-tag","data-req-hover":"sensitive","data-req-id":p,"aria-label":r("命中了敏感词"),children:r("敏")},"sensitive")),h.length?s.jsx("span",{className:d,children:h},o):s.jsx("span",{className:`req-none ta-${a}`,children:"-"},o)}case"status":{if(Ap(e)){const b=window.wbRequestPhase;if(!b?.badgeHtml)return s.jsx("span",{className:d,children:s.jsx("span",{className:"req-status-badge",children:s.jsxs(ze,{shape:"tag",variant:"info",title:r("请求正在转发中,用时列显示的是已用时"),children:[s.jsx(my,{className:"req-live-dot","aria-hidden":"true"}),r("进行中")]})})},o);const y=b.elapsedLineHtml?.(e)??"";return s.jsxs("span",{className:d,children:[s.jsx("span",{className:"req-status-badge",dangerouslySetInnerHTML:{__html:b.badgeHtml(e)}}),y?s.jsx("span",{className:"req-status-line",dangerouslySetInnerHTML:{__html:y}}):null]},o)}const f=Number(e.status)||0,p=oS(e),h=!p&&f>=200&&f<300?r("HTTP {status},但响应体阶段出错:见错误列",{status:f}):void 0;return s.jsx("span",{className:d,children:s.jsx(ze,{shape:"tag",variant:p?"success":"destructive",title:h,children:f||r("失败")})},o)}case"model":{const f=String(e.clientModel??"").trim(),p=String(e.upstreamModel??"").trim(),h=String(e.model??"").trim(),b=String(e.clientReasoning??"").trim(),y=String(e.upstreamReasoning??"").trim(),v=(S,N)=>N?`${S}(${N})`:S;if(!f||!p||p.toLowerCase()===f.toLowerCase()){const S=v(h,y||b);return s.jsx("span",{className:d,title:S,children:S||"—"},o)}const j=v(p,y),w=v(f,b);return s.jsxs("span",{className:`${d} req-model-split`,children:[s.jsxs("span",{className:"req-model-line",title:r("转发到上游的模型:{model}",{model:j}),children:["⬆️ ",j]}),s.jsxs("span",{className:"req-model-line sub",title:r("下游请求的模型:{model}",{model:w}),children:["⬇️ ",w]})]},o)}case"client":{const f=String(e.client??"").trim();if(!f)return s.jsx("span",{className:`${d} is-empty`,title:r("这条明细没有记录来源客户端(旧数据、模型测试,或请求未带 User-Agent)"),children:"—"},o);const p=f==="other"?r("其它"):f;return s.jsx("span",{className:d,title:f==="other"?r("识别到 User-Agent,但不属于已知的客户端类型:{ua}",{ua:f}):r("来源客户端:{client}",{client:p}),children:p},o)}case"dur":return Ap(e)?s.jsxs("span",{className:d,children:[s.jsx("span",{className:"req-dur-line",title:r("已用时(请求仍在转发中)"),children:eW(e.ts)}),iW(e)]},o):s.jsxs("span",{className:d,children:[s.jsx("span",{className:"req-dur-line",children:lS(e.durationMs)}),s.jsx("span",{className:"req-dur-line sub",title:r("首响:上游首帧到达的耗时"),children:r("首响 {time}",{time:JZ(e.firstResponseMs)})})]},o);case"usage":{if(Ap(e))return s.jsx("span",{className:d},o);if(!oS(e))return s.jsxs("span",{className:d,title:r("失败请求不记录用量"),children:[s.jsx("span",{className:"req-usage-line",children:"in: - / out: - / all: -"}),s.jsx("span",{className:"req-usage-line sub",children:r("缓存读取: - / 命中率: -")})]},o);const f=`in: ${Dx(e.promptTokens)} / out: ${Dx(e.completionTokens)} / all: ${Dx(e.totalTokens)}`,p=r("缓存读取: {cache} / 命中率: {rate}",{cache:Dx(e.cacheReadTokens),rate:tW(e)}),h=WZ(e),b=h?`${p} · ${h}`:p;return s.jsxs("span",{className:d,title:[f,b].filter(Boolean).join(` +`),children:[s.jsx("span",{className:"req-usage-line",children:f}),s.jsxs("span",{className:"req-usage-line sub",children:[p,h?s.jsxs("span",{title:QZ,children:[" · ",h]}):null]})]},o)}case"error":return Ap(e)?s.jsx("span",{className:d},o):e.error?s.jsx("span",{className:d,title:String(e.error),children:String(e.error)},o):s.jsx("span",{className:`req-none ${c} ta-${a}`,children:"-"},o);case"detail":{const f=e.id?String(e.id):"";return f?s.jsx("span",{className:d,children:s.jsx(ne,{size:"sm",variant:"outline",className:"req-detail-btn",title:r("查看该请求的上游原始报文"),onClick:()=>{As().wbRequestDetail?.open?.(f,e)},children:r("详情")})},o):s.jsx("span",{className:`req-none ${c} ta-${a}`,children:"-"},o)}default:return null}}function uW(e){return s.jsx("div",{className:"req-head",children:e.map(t=>s.jsx("span",{"data-col":t.key,className:`${t.sel.slice(1)}${t.key==="dur"?" req-num":""} ta-${t.align}`,children:t.label},t.key))},"__head")}function dW(){const[e,t]=x.useState(lW),[a,o]=x.useState(()=>EI()),[c,d]=x.useState(()=>sn.range),[f,p]=x.useState(!1),[h,b]=x.useState(0),[y,v]=x.useState(()=>sn.size),[j,w]=x.useState(()=>({ms:Ox,enabled:Mx})),[S,N]=x.useState([]),[k,A]=x.useState([]),[,P]=x.useState(0),R=x.useRef(null),C=x.useRef(null),E=x.useRef(!1),D=x.useCallback(ee=>(sn.filters={...sn.filters,...ee},o({...sn.filters}),sn.filters),[]),I=x.useCallback(ee=>{sn.range=ee,d(ee)},[]),O=x.useCallback(ee=>{sn.offset=ee,b(ee)},[]),_=x.useCallback(ee=>{sn.runningOnly=ee,p(ee)},[]),z=x.useCallback(()=>{eS||!As().wbFilterMemory||(eS=!0,!kI&&D(EI()))},[D]),H=x.useCallback((ee,he)=>{As().wbRequestHover?.beforeListRedraw?.(),he&&(tS=he),E.current=!0,t(ee)},[]),B=x.useCallback(ee=>{As().wbRequestHover?.beforeListRedraw?.(),E.current=!0,t(he=>({...he,error:ee}))},[]),L=x.useCallback(async(ee={})=>{const{silent:he=!1,resetPage:te=!1}=ee;te&&O(0),rW(),z(),he||MI();const ke=++CI;try{const le=As().workbuddyDesktop;if(!le)throw new Error(r("后端桥不可用"));const de=await le.getStatsRequests(XZ());if(ke!==CI)return;const Se={entries:Array.isArray(de?.entries)?de.entries:[],total:Number(de?.total)||0,matched:Number(de?.matched)||0,running:Number(de?.running)||0,loaded:!0,error:""},Te=Math.max(0,(Math.ceil(Se.matched/sn.size)-1)*sn.size);if(sn.offset>Te)return O(Te),L({silent:he});te&&(C.current=0),H(Se,Se.entries)}catch(le){he||(console.warn("读取请求日志失败:",X0(le)),B(r("读取请求日志失败,详见控制台")))}},[O,H,B,z]);x.useEffect(()=>(Ju={load:L,setAuto:ee=>w(ee),setFilterOptions:(ee,he)=>{N(ee),A(he)},repaint:()=>P(ee=>ee+1)},As().wbRequestHover?.bind?.({host:R.current,entryOf:ee=>{const he=ee?.dataset?.reqId||"";return he&&(tS.find(te=>String(te?.id||"")===he)||tS.find(te=>String(te?.ts||"")===he))||null}}),L({silent:!0}),MI(),()=>{Ju=null}),[L]),x.useLayoutEffect(()=>{aW(),P(ee=>ee+1)},[]);const F=e.error||!e.entries.length?"":cS().map(ee=>ee.key).join("|");x.useLayoutEffect(()=>{F&&As().wbTableColumns?.repaint?.("requests")},[F]),x.useEffect(()=>{if(!j.enabled||j.ms<=0)return;const ee=window.setInterval(()=>{document.hidden||As().wbApp?.currentPage!=="requests"||sS||(sS=!0,L({silent:!0}).finally(()=>{sS=!1}))},j.ms);return()=>{window.clearInterval(ee)}},[j,L]),x.useLayoutEffect(()=>{E.current&&(E.current=!1,As().wbRequestHover?.afterListRedraw?.());const ee=C.current;if(ee===null)return;C.current=null;const he=R.current;he&&(he.scrollTop=ee)});function $(ee){const he=Math.max(1,Math.ceil(e.matched/sn.size)),ke=(Math.min(Math.max(1,ee),he)-1)*sn.size;ke!==sn.offset&&(O(ke),C.current=0,L())}function X(ee){const he=vI(ee);if(he===sn.size)return;sn.size=he,v(he),mg("requests",he);const te=sn.offset+1;O(Math.max(0,Math.floor((te-1)/he)*he)),C.current=0,L()}function K(ee){kI=!0,YZ(D(ee)),L({resetPage:!0})}function q(ee){const he=W0.includes(ee)?ee:J0;he!==sn.range&&(I(he),KZ(he),L({resetPage:!0}))}function Q(ee){ee!==sn.runningOnly&&(_(ee),L({resetPage:!0}))}const G=cS(),J=Math.max(1,Math.ceil(e.matched/sn.size)),Y=Math.min(J,Math.floor(h/sn.size)+1),re=e.matched?h+1:0,M=e.matched?h+e.entries.length:0,U=e.error?"—":e.matched===e.total?r("{n} 条",{n:e.total}):r("{matched} / {total} 条",{matched:e.matched,total:e.total}),Z=!f&&e.running>0?r(" · {n} 进行中",{n:e.running}):"",ae=[UZ[c]||r("全部")];f?ae.push(r("只看进行中")):a.status==="ok"?ae.push(r("只看成功")):a.status==="error"&&ae.push(r("只看失败")),a.provider&&ae.push(OI(a.provider)),a.model&&ae.push(a.model);const ce=x.useMemo(()=>!a.provider||S.some(ee=>ee.value===a.provider)?S:[{value:a.provider,label:a.provider},...S],[a.provider,S]),W=x.useMemo(()=>!a.model||k.includes(a.model)?k:[a.model,...k],[a.model,k]),ue=sW(e.entries),ve=e.entries.map((ee,he)=>s.jsx("div",{className:`req-row${Ap(ee)?" running":oS(ee)?"":" failed"}`,children:G.map(te=>cW(ee,te))},ue[he]));return s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("请求日志列表")}),s.jsx(ze,{id:"req-badge",variant:"outline","data-tone":"",children:U+Z}),s.jsx("span",{className:"panel-sub",children:r("按时间倒序,全量保留")}),s.jsx("div",{className:"head-actions",children:s.jsx(ne,{id:"btn-req-clear",variant:"destructive",onClick:()=>{As().wbRequestClearModal?.open?.()},children:r("清理")})})]}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"log-filters",children:[s.jsx(En,{options:FZ,value:c,onValueChange:q,"aria-label":r("请求日志时间范围"),className:"shrink-0"}),s.jsxs(ut,{value:a.status,disabled:f,onValueChange:ee=>K({status:String(ee??"")}),children:[s.jsx(ft,{id:"req-status",className:"min-w-[120px]",title:r(f?"「仅看进行中」开启时,状态固定为进行中":"按请求结果筛选"),"aria-label":r("按请求结果筛选"),children:s.jsx(dt,{children:wI.find(ee=>ee.value===a.status)?.label??$Z})}),s.jsx(pt,{children:wI.map(ee=>s.jsx($e,{value:ee.value,children:ee.label},ee.value))})]}),s.jsxs(ut,{value:a.provider,onValueChange:ee=>K({provider:String(ee??"")}),children:[s.jsx(ft,{id:"req-provider",className:"min-w-[120px]",title:r("按提供商筛选(只列日志里出现过的)"),"aria-label":r("按提供商筛选"),children:s.jsx(dt,{children:a.provider?ce.find(ee=>ee.value===a.provider)?.label||a.provider:SI})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:SI}),ce.map(ee=>s.jsx($e,{value:ee.value,children:ee.label},ee.value))]})]}),s.jsxs(ut,{value:a.model,onValueChange:ee=>K({model:String(ee??"")}),children:[s.jsx(ft,{id:"req-model",className:"min-w-[120px]",title:r("按模型筛选(只列日志里出现过的;精确匹配模型名)"),"aria-label":r("按模型筛选"),children:s.jsx(dt,{children:a.model||NI})}),s.jsxs(pt,{children:[s.jsx($e,{value:"",children:NI}),W.map(ee=>s.jsx($e,{value:ee,children:ee},ee))]})]}),s.jsx($F,{id:"btn-req-running",variant:"outline",size:"default",pressed:f,onPressedChange:Q,title:r("只显示正在转发中的请求(状态列按阶段显示:连接中 / 等待响应 / 响应中 / 重试中,用时列显示已用时)"),children:r("仅看进行中")}),s.jsx("div",{className:"spacer"}),s.jsx("span",{className:"panel-sub",id:"req-summary",children:ae.join(" · ")})]}),s.jsx("div",{className:"log-list",id:"req-list",ref:R,children:e.error?s.jsx("div",{className:"log-empty",children:e.error}):e.entries.length?[uW(G),...ve]:s.jsx("div",{className:"log-empty",children:oW(e)})})]}),s.jsx($i,{total:e.matched,range:{start:re,end:M},page:Y,pageCount:J,size:y,sizes:BZ,onSizeChange:X,onPageChange:$,disabled:!e.loaded&&!e.error})]})}async function DI(e={}){await Ju?.load(e)}function fW(){return AI().toString()}function pW(){return rS=0,DI({resetPage:!0})}const II='.page[data-page="requests"]';let PI=!1;function _I(){if(PI)return;const e=document.querySelector(II);e&&(PI=!0,e.replaceChildren(),wn.createRoot(e).render(s.jsx(dW,{})))}document.querySelector(II)?_I():document.addEventListener("DOMContentLoaded",_I,{once:!0}),window.wbRequestsPanel={load:DI,applyAutoRefresh:iS,visibleColumns:cS,clearParams:fW,notifyCleared:pW},window.wbRequestPhase={labelOf:gI,badgeHtml:LZ,elapsedLineHtml:zZ,elapsedText:xI,phaseOf:Q0};function mW(e){if(e.status==="loading")return kt.appLoading;if(e.status==="unavailable")return kt.modelsDevUnavailable;const t=e.source==="builtin"?kt.modelsDevSourceBuiltin:e.source==="cache"?kt.modelsDevSourceCache:e.source==="network"?kt.modelsDevSourceNetwork:e.source,a=e.updatedAt>0?String(tt().wbApp?.formatTime?.(e.updatedAt)??""):"";if(!t&&!a&&!e.count)return kt.modelsDevEmpty;const o=a?r(",{time}",{time:a}):"",c=e.enabled?r("正在联网更新"):r("仅用现有数据、不联网刷新");return r("{count} 条 · 来自{origin}{at} · {mode}",{count:Number(e.count).toLocaleString("zh-CN"),origin:t||r("未知来源"),at:o,mode:c})}function tt(){return window}function Ot(e){return e instanceof Error?e.message:String(e)}function Ve(e,t){tt().wbApp?.toast?.(e,t)}async function hW(e){try{await tt().workbuddyDesktop?.openReleasePage?.(e)}catch(t){Ve(r("打开链接失败:{error}",{error:Ot(t)}),"err")}}const Ix=[{id:"general",label:r("通用"),icon:"sliders"},{id:"assistant",label:r("AI 助手"),icon:"spark"},{id:"display",label:r("显示"),icon:"display"},{id:"gateway",label:r("网关"),icon:"traffic"},{id:"routing",label:r("路由"),icon:"route"},{id:"retry",label:r("重试"),icon:"refresh"},{id:"timeout",label:r("超时"),icon:"timer"},{id:"security",label:r("安全"),icon:"shield"},{id:"data",label:r("数据"),icon:"database"},{id:"feedback",label:r("反馈与需求"),icon:"feedback"},{id:"about",label:r("更新"),icon:"download"}],LI="workbuddy-desktop-settings-cat",gW="workbuddy-desktop-theme",xW="workbuddy-desktop-zoom",zI="wb:theme",BI="wb:zoom",uS=80,HI=130,Px=5,bW=Array.from({length:(HI-uS)/Px+1},(e,t)=>uS+t*Px),vW=[{value:"system",label:r("跟随系统")},{value:"light",label:r("浅色")},{value:"dark",label:r("深色")}];function yW(){try{const e=localStorage.getItem(gW);return e==="light"||e==="dark"?e:"system"}catch{return"system"}}function jW(){try{const e=localStorage.getItem(xW);if(e===null||e.trim()==="")return 100;const t=Number(e);if(!Number.isFinite(t))return 100;const a=Math.round(t/Px)*Px;return Math.min(HI,Math.max(uS,a))}catch{return 100}}const dS=1,fS=99999,pS=[{key:"logRetentionDays",id:"settings-retention-log",label:r("事件日志保留天数"),unit:r("天"),min:dS,max:fS,hint:r("登录 / 账号切换 / 429 切换等系统事件,可填 1–99999 天。")},{key:"requestLogRetentionDays",id:"settings-retention-request",label:r("请求日志保留天数"),unit:r("天"),min:dS,max:fS,hint:r("「请求日志」页的逐条请求记录,默认 99999 天(近似全量保留),可填 1–99999 天。")},{key:"dailyAggregateRetentionDays",id:"settings-retention-daily",label:r("按天聚合保留天数"),unit:r("天"),min:dS,max:fS,hint:r("报表页的热力图与按天趋势,默认 99999 天(近似全量保留),可填 1–99999 天。")}],UI=[{key:"retryCount",id:"settings-retry-count",label:r("同一账号重试次数"),unit:r("次"),min:0,max:10,hint:r("在同一个账号上原地重发几次(不含首发;整条请求共用一份,用完后换来的账号只发一次就继续换)。可填 0–10,0 表示失败立即换号。")},{key:"retryCrossProviderCount",id:"settings-retry-cross-provider-count",label:r("切换账号重试次数"),unit:r("次"),min:0,max:10,hint:r("失败后最多再换几个账号试(填 N = 最多换 N 个,首发那个不算)。换到同一家名下另一个账号、或换到另一家,都各算一次;换满仍失败就返回错误。可填 0–10,0 表示失败立即报错。")},{key:"retryIntervalSeconds",id:"settings-retry-interval",label:r("重试间隔"),unit:r("秒"),min:0,max:300,hint:r("两次重试之间的等待时间,两项共用,可填 0–300 秒。")}],VI=[{key:"connectTimeoutSeconds",id:"settings-timeout-connect",label:r("连接中超时"),unit:r("秒"),min:1,max:3600,hint:r("建立上游 TCP/TLS 连接或代理隧道的最大等待时间(秒),默认 30 秒,范围 1~3600。")},{key:"headersTimeoutSeconds",id:"settings-timeout-headers",label:r("等待响应超时"),unit:r("秒"),min:1,max:3600,hint:r("请求发出后等待上游响应头的最大时间(秒),默认 300 秒,范围 1~3600。")},{key:"streamIdleTimeoutSeconds",id:"settings-timeout-stream-idle",label:r("流式响应空闲超时"),unit:r("秒"),min:1,max:3600,hint:r("流式响应相邻数据之间允许的最大空闲时间(秒),收到新数据后重新计时,默认 300 秒,范围 1~3600。")},{key:"bodyTimeoutSeconds",id:"settings-timeout-body",label:r("非流式响应超时"),unit:r("秒"),min:1,max:3600,hint:r("读取完整非流式响应体允许的最大时间(秒),默认 300 秒,范围 1~3600。")}],FI=[{key:"queueMaxWaits",id:"settings-queue-max-waits",label:r("排队等待次数"),unit:r("次"),min:0,max:10,hint:r("上游模型繁忙时会先排队(回一句「建议 N 秒后再来」,Qoder 免费模型就是这样):网关等一会儿再发同一请求,最多等几次。默认 2 次,可填 0–10;填 0 表示不等待,排队直接返回 503 并说明「不是登录态或额度问题」。")},{key:"queueWaitSeconds",id:"settings-queue-wait-seconds",label:r("排队等待秒数"),unit:r("秒"),min:0,max:120,hint:r("每次等待的时长,默认 0 = 跟随上游建议(实测 9~30 秒,上游没给建议时用 15 秒)。可填 0–120 秒强制一个固定时长。")}],$I="noRetryStatusCodes",mS=100,hS=599,qI=50,$o=[{value:"passthrough",optionLabel:r("透传客户端 system(默认)"),toastLabel:r("透传客户端 system")},{value:"custom",optionLabel:r("替换为网关提示词"),toastLabel:r("替换为网关提示词")},{value:"append",optionLabel:r("追加网关提示词"),toastLabel:r("追加网关提示词")}];function GI(e,t,a,o){const c=String(e??"").trim();if(!c)return{ok:!1,message:r("{noun}不能为空(可填 {min}–{max})",{noun:o,min:t,max:a})};if(!/^\d+$/.test(c))return{ok:!1,message:r("{noun}必须是整数",{noun:o})};const d=Number(c);return da?{ok:!1,message:r("{noun}必须在 {min}–{max} 之间(当前填的是 {text})",{noun:o,min:t,max:a,text:c})}:{ok:!0,value:d}}function wW(e,t){const a={...t};if(e&&typeof e=="object"){const o=e;typeof o.closeToTray=="boolean"&&(a.closeToTray=o.closeToTray),typeof o.autostart=="boolean"&&(a.autostart=o.autostart),typeof o.lanAccess=="boolean"&&(a.lanAccess=o.lanAccess),typeof o.lanPanel=="boolean"&&(a.lanPanel=o.lanPanel),typeof o.lightweightMode=="boolean"&&(a.lightweightMode=o.lightweightMode)}return a}function KI(e,t,a){if(!t||typeof t!="object")return null;const o=t,c={...a||{}};for(const d of e){const f=Number(o[d.key]);Number.isInteger(f)&&f>=d.min&&f<=d.max&&(c[d.key]=f)}return e.some(d=>Number.isInteger(c[d.key]))?c:null}function YI(e){if(!Number.isFinite(e)||e<=0)return"0 B";if(e<1024)return`${e} B`;const t=e/1024;if(t<1024)return`${t.toFixed(1)} KB`;const a=t/1024;return a<1024?`${a.toFixed(1)} MB`:`${(a/1024).toFixed(2)} GB`}function SW(e){const t=Number(e);return!Number.isFinite(t)||t<0?"—":t.toLocaleString("zh-CN")}const Wn={displayTheme:r("控制界面的深浅色,与左侧边栏底部的三个主题按钮是同一个设置(改哪一处,另一处立刻跟上)。「跟随系统」会随操作系统当前的浅色 / 深色自动切换,并在系统主题变化时即时跟上;选「浅色」或「深色」则把界面固定在该模式,不再随系统变化。窗口标题栏的深浅色会一并同步,不会出现深色界面配一条浅色标题栏的情况。"),displayZoom:r("等比放大或缩小整个界面(文字、控件、间距一起变),效果与浏览器按 Ctrl +/- 相同:80%–130%、5% 一档。窗口本身不缩放,变的是页面内容的显示比例,设置立即生效并记住,下次启动直接按这个比例打开。放得越大可视范围越小,窗口较窄或表格较宽时不建议调得太大。"),displayLanguage:r("界面语言。可选简体中文、繁體中文、English、日本語、한국어、Português (Brasil),或「跟随系统」(按浏览器 / 系统的语言列表自动选)。切换后整页立即刷新;该偏好只存在本机(localStorage),不影响账号、密钥与转发。"),tray:r("默认关闭窗口不会退出程序,而是把窗口缩到系统托盘,转发继续在后台运行(OpenAI 客户端不受影响);要彻底退出程序,请在托盘图标上右键选「退出」。关掉这个开关后,点关闭按钮即退出程序、转发随之中断。「开机自动启动」开启后,登录系统时会自动启动本程序(通常直接驻留托盘),不需要手动打开。"),lightweight:r("开启后,关闭窗口不只是隐藏,而是把界面窗口整个销毁:底层的 WebView2 六个常驻进程(约 200MB 内存)随之退出,网关转发、定时签到、余额查询都在后台进程里继续,不受影响;需要界面时点托盘图标重新打开(WebView 冷启动约 1 秒)。注意:界面销毁期间「ZCode 套餐池(Start Plan)」通道的验证码令牌由界面铸造,无法补充 —— 走该通道的请求会以 503 落进请求日志(写明原因),重新打开界面即自动恢复。此开关只在「关闭窗口时最小化到托盘」开启时有意义,最小化窗口不受影响。"),lan:r("默认网关只监听 127.0.0.1,只有本机能访问。开启后网关改听所有网卡,同一局域网内的设备可以把 API 地址指向本机 IP 一起使用。出于安全考虑,开启前必须先注册一个面板管理员:管理接口从此要求管理员会话或网关 Key,模型额度不会对局域网裸奔;一把启用的网关 Key 都没有时,转发接口也会拒绝服务,直到创建第一把。改动需要重启应用生效。「同时开放网页管理面板」把管理界面也出给局域网(其他设备的浏览器打开本机 IP 即可管理),默认关闭 —— 桌面端的面板仍只由本程序自己出。"),units:r("控制报表与请求日志里 Token 读数的写法:开启后按中文量级显示(1.2亿 / 8400万),关闭则用 k / M 缩写(与上游文档、接口字段的写法一致)。这只影响显示口径,不改变任何统计与存储的数值。"),queue:r("只对「排队制」的上游生效(目前是 Qoder 的免费模型):模型繁忙时上游不报错,只回一句「建议 N 秒后再来」(业务码 10605),网关按建议时长等一会儿再发同一请求,等满次数仍排不上才把「排队中」作为错误返回(HTTP 503,文案会说明这不是登录态或额度问题)。等待发生在首个字节之前,吃的是「等待响应超时」那份预算 —— 两项设置一起决定一次请求最多卡多久;排队不会标记账号限额、也不会换账号(换谁都一样在排队)。保存后对下一个请求立即生效,不用重启。"),captchaPool:r("ZCode「套餐池(Start Plan)」通道的每条推理请求都要附一个当次铸造的阿里云无痕验证令牌(一次性、约 2 分钟过期):令牌由界面里的铸造守卫静默铸好、推给网关入池,转发时逐条取用。这里的三个值管的是补货节奏:①「库存目标」= 库存低于它守卫才补铸、补到它为止,默认 3 个;②「轮询间隔」= 守卫多久看一次库存,默认 2500 毫秒,只影响反应速度(库存够用时不会多铸);③「单轮铸造上限」= 发现缺货时一轮最多铸几个,默认 1 个(连铸之间固定隔 500 毫秒)。请求密集、请求日志里连续出现「需要人机验证令牌」的 503 时,把目标与单轮上限适当调大、轮询调快,能让池子跟得上消耗 —— 代价是上游(阿里云)对铸造频率有风控,铸得越多越快越容易触发限流,且用不掉的令牌 2 分钟即过期作废、等于白烧配额,所以建议小步调。保存后对下一轮补货立即生效,不用重启。"),timeouts:r("上游请求四个阶段各自的等待上限(秒,1~3600):①「连接中超时」= 建立 TCP/TLS 连接或代理隧道的最大等待,默认 30 秒;②「等待响应超时」= 请求发出后等上游响应头的最大时间,默认 300 秒 —— 网关请求上游恒带 stream,响应头在 SSE 建立时就到达,与模型思考多久无关;③「流式响应空闲超时」= 流式响应相邻两块数据之间允许的最大空闲,收到新数据即重新计时,默认 300 秒 —— 上游长时间不吐数据即判定连接僵死并断开;④「非流式响应超时」= 读完整份非流式响应体的总预算(一次性计时、不重置),默认 300 秒。四项都是「等待上限」,只要数据还在来,正常的流式回答就一直往下传。保存后对下一个请求立即生效,不用重启。"),retry:r("请求转发到上游失败时(服务器瞬时错误、连接失败,以及 WorkBuddy 的 11-128 敏感词拦截),网关会等一段间隔后再发一次。次数分两档,管的是两件事:①「同一账号重试次数」在同一账号上原地重发几次 —— 一般是提示词被上游拦截、或链路瞬时抖动,等一会儿重发往往就好了;②「切换账号重试次数」这份请求最多再换几个账号试 —— 首发的那个账号不算,每换一个扣一次,不分是不是同一家(换到同一家的下一个账号也算一次),换满还失败就把错误返回给客户端。两项都设为 0 表示失败不重试、直接报错。注意账号级限额(429)不占这里的换号次数 —— 那类失败走「冷却该账号并换下一个账号」的降级,与这里的重试是两条路。另有「指定错误码直接换号」名单(默认 402):命中名单的上游状态码不在同一账号重发,直接按队列换下一个账号继续试(换号也占「切换账号重试次数」),换满仍失败才把错误返回给客户端。"),routing:r("转发一个请求时按什么顺序挑账号:①「路由策略」决定排队规则 —— 顺序优先是排第 1 的接走所有请求、直到它不能答(限流 / 故障 / 额度尽)才轮到下一位;负载均衡是每条请求换下一位,把负载摊到各个账号上。②「会话粘性」让同一会话(相同的模型 + 相同的开头消息)尽量跟住同一个账号 —— 多轮对话的上下文缓存能复用、少换号少浪费;自动档粘 5 分钟、会话内档粘 6 小时、每轮与关闭都不粘。账号的排队位次(账号表的第一列)在所有策略下都是地基,策略在它之上再排。此外路由还有三层自愈保护:转发失败的账号退避休息一会儿(15 分钟起步翻倍、封顶 1 小时)、被 429 的账号垫底排到队尾 30 分钟、余额不足 / Token 周期用量到顶的账号由限制器拦下 —— 前两层可以在下面按需开关,限制器由账号的「限制器」设置管。"),sanitize:r("上游用「逐字精确匹配」的方式审核请求体(不是语义审核):客户端注入的固定模板句、计费头字段名、以及某些裸错误码出现在报文里就会整单拦截,返回 400。开启本项后,网关在每次转发前改写这些指纹 —— 表头键值整段删除,承载语义的模板句只换一个词(如 official CLI for Claude → official CLI tool for Claude),对话内容与语义都不受影响。规则集是内置的,不需要也无法维护词表。关掉本项后客户端模板会原样发往上游,可能重新出现模板句被误拦的报错。"),developerRoleCompat:r("OpenAI 新版 API 用 developer 角色取代 system 承载指令,新版 OpenAI SDK 与部分客户端发的对话里会出现 role 为 developer 的消息;不少 OpenAI 兼容上游(尤其是自定义提供商的透传路径)不认识这个角色,收到就报 400。开启本项后,网关在每次转发前把 developer 角色的消息改写为 system —— 两类角色在 OpenAI 语义上等价(OpenAI 自己的迁移指南也是这么映射的),改写不影响对话内容与语义;对本来就只认 system 的各家内置上游同样无害。默认开启;关掉后 developer 消息会原样发往上游,不认识它的上游会报「角色不存在」一类的错误。"),clineHeaders:r("Cline 上游按请求头判定「调用方是不是 Cline 自家产品」:X-CLIENT-TYPE: cline-sdk 是硬门槛(缺了它免费池模型一律 403),版本号与平台标识让请求更贴近官方客户端的形态。这里列出的是网关默认会发的头:改某一行的值 = 覆盖默认值(也可以新增默认清单之外的自定义头);把值清空保存 = 这个头不发送;在界面删掉被改过的行 = 回落默认值。改完点「保存」,下一个 Cline 请求立即生效。不懂某一行的含义就不要动它 —— 伪装头的价值在「与官方客户端逐字一致」。"),cors:r("网关面(/v1/*)默认不应答浏览器跨源请求:页面直连会被预检拦下,只报「无法连接 API」。注意来源是 * —— 未配「网关 Key」时任何网页都能借本机网关打上游,故默认关闭;只给自己用的话,让页面与网关同源(本地反代 /v1)更稳妥。面板接口(/api/*)不受影响。"),prompt:r("客户端(Claude Code / Codex 等 CLI)会在 system 提示词里注入几十句固定模板,上游按逐字匹配审核,命中就整单拦截(HTTP 400)。指纹脱敏能改写实测命中过的那几句,但换一个客户端版本就可能冒出新的。这里可以把 system 整段换成网关自己那份:①「透传」= 不动客户端 system(默认,行为与之前完全一致);②「替换」= 删掉客户端所有 system / developer 消息,换成网关的提示词(客户端项目规范随之消失);③「追加」= 在开头连续 system 块之后插入一条网关提示词,既有消息逐字不动(客户端规范与网关提示词并用)。那份提示词有两个来源:提示词文件(留空用内置默认),或直接在界面上编辑正文 —— 编辑过就以那份正文为准(优先于文件),清空则回到文件 / 内置默认。另外,透传 / 追加模式下撞了内容拦截(多半是指纹误报)时,网关会自动换一段最小中性提示词重试一次,并把「降级期」开到次日 00:00 —— 期间所有请求直接带中性提示词出门,不再先撞一次 400;这里能看见并提前解除它。上面几项是**默认值**:想给某个提供商单独配,用下面的「按提供商」加一行(带「网关自带」的家默认就列在那里)—— 不同的上游对这些内容的接受程度不一样。"),debug:r("开启后,网关会把每次转发**发给上游的请求**(请求头 + 请求体)与**上游返回的响应**(状态码 + 响应头 + 响应体)完整保存到本地,供请求日志页的「详情」查看。请求头里的 Authorization、Cookie、API Key 等凭据字段一律替换成 [redacted],不会明文落盘;请求体与响应体按原样保存(可能包含你的对话内容)。报文只保留最近 500 条,超出后丢弃最旧的。这是排障用的临时开关,不需要时建议关闭。"),io:r("导出会把全部账号与自定义提供商定义写入一个 JSON 文件,可以拷到另一台机器上导入后继续使用。导入采用合并策略:同提供商下按业务身份(UID / userId / apiKey 等)去重 —— 已存在的账号只更新凭证,保留本机原有的优先级顺序;新账号追加到转发顺序末尾,不会抢占当前正在使用的账号;自定义提供商定义按 id 合并,本机缺失时自动补建。"),backup:r("把整份数据库(账号、网关配置、事件日志、请求统计、调试报文与桌面设置)导出成一个 SQL 文件,恢复时整体替换本机全部数据 —— 与上面「账号导入 / 导出」的逐条合并是两种语义:恢复会连配置、日志与当前启用状态一起换成备份那台机器的状态。导出默认包含日志与调试报文(文件可能很大),不需要时可以取消勾选只备业务数据。恢复前会自动把当前数据备份到配置目录的 backups/ 下,恢复后需要重新登录管理面板。"),retention:r("三档数据各自独立计时,超出保留天数的部分会被删除:事件日志是登录、账号切换、429 切换这类系统事件;请求日志是网关每次转发到上游的逐条记录;按天聚合供报表页的热力图与按天趋势使用。请求日志与按天聚合默认 99999 天(约 274 年,效果上等于全量保留),事件日志默认 30 天。把某一档改小(例如 30 天改成 7 天)保存后会立即删除超出的历史数据,此操作不可恢复;改大或保持不变不会删除任何数据。三档的可填范围均为 1–99999 天。"),storage:r("全部数据(账号、事件日志、请求记录、调试报文、设置)统一保存在配置目录下的 agent2api.db 这一个 SQLite 数据库里。备份时只需拷贝这个文件;更换保存位置请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。"),modelsDev:r("models.dev 是一个开源的模型资料库(社区维护),这里用它补齐「上游没告诉我们」的模型能力:上下文窗口、最大输出、工具调用、图片、视频、是否支持思考,以及这个模型认识哪些思考档位。优先级是:你在「模型能力」里手工改的值 > 上游清单自己声明的值 > models.dev 的值 —— 它只填上游没声明的空位,从不推翻上游已声明的。网关随包内嵌了一份快照,所以离线、首次启动、访问不通时也有数据;开启时会按「模型目录刷新」任务的间隔联网更新(同一轮里一并刷新,也可在这里立即刷新)。关闭只停联网刷新,已经拿到的数据继续用。")},Un={timeouts:r("保存后立即生效,不用重启;超时按上游失败处理(计入请求日志的「错误」列,并按「请求重试」的设置决定是否重试)。"),queue:r("等待期间请求日志的状态列会显示「排队中」阶段,详情弹窗的「内部重试」里能逐次看到等待时长;等待用尽后返回 HTTP 503(不是 401/429:既不会刷新凭证,也不会把账号标成限额)。"),captchaPool:r("保存后对下一轮补货立即生效,不用重启。守卫跑在应用界面里:轻量模式关窗销毁界面期间无法铸造,走套餐池(Start Plan)通道的请求会以 503 落进请求日志(写明原因),重新打开界面即自动恢复。"),retry:r("保存后立即生效,不用重启;次数用尽仍失败时,错误原样返回给客户端。"),routing:r("保存后立即生效,不用重启:策略与粘性作用于**下一次**转发的选路,进行中的请求不受影响。"),retryCodes:r("命中这些上游状态码的失败不在同一账号重发,直接换下一个账号继续试(换号占用「切换账号重试次数」,换满仍失败才把错误原样返回给客户端)。输入 100–599 的状态码后回车添加,点 × 或退格删除;默认 402(积分不足)。清空全部表示任何错误都照常重试。"),sanitize:r("默认开启。命中明细会记进请求日志的「敏」标签(悬停可看命中了哪几条规则)。本项只改发给上游的副本,客户端看到的响应内容不变。"),developerRoleCompat:r("默认开启。只改 role 字段本身,消息内容与顺序都不动;本项只改发给上游的副本,客户端看到的响应内容不变。"),clineHeaders:r("值与默认值相同的行不占覆盖(存的是「改过的键」,不是全量配置);值留空保存表示这个头不发送,删除改过的行表示回落默认值。改完立即生效,不用重启。"),cors:r("默认关闭。改动立即生效,不用重启;关闭期间的跨源请求会被预检拦下。"),debug:r("开启后新发生的请求才会被记录(已经过去的请求补不回来)。报文统一存放在本地数据库里,只保留最近 500 条(超出后丢弃最旧的),条数见左侧「数据 → 数据存储」的「调试报文」。"),promptMode:r("替换 = 客户端 system / developer 消息全部删掉,换成网关的提示词;追加 = 保留客户端内容,只在开头 system 块之后多插一条。两者都只改发给上游的副本,客户端看到的响应不变。"),promptFile:r("UTF-8 文本文件路径,留空用内置默认提示词(通用编码助手提示词,不含任何会被上游拦截的模板句)。只在「替换 / 追加」模式下生效,透传模式不读它。"),promptText:r("在这里直接编辑提示词正文:保存后以这份文本为准(优先于上面的提示词文件),清空则回到文件 / 内置默认。改一个字不必再去编辑器里开文件。"),promptGatewayText:r("上游按「结构」校验身份:三段必须各自成块,所以这里分开编辑三段(不能合成一段)。动态段里的运行值写成占位符,发请求时才替换:{cwd} 工作目录、{platform} 平台、{shell} shell、{os_version} 系统版本、{git} 是否 git 仓库、{provider} 提供方标识、{model} 模型名。清空某一段 = 那一段恢复官方原文。第三段发出时前面会自带一个空行(官方形状)。"),prompt:r("改完立即生效,不用重启(下一个请求就用新模式)。降级是自动的临时状态,解除后本模式自己的提示词立刻恢复。"),promptProviders:r("「用哪份提示词」本来是按上游分别决定的事,所以每家可以有自己的模式、提示词文件与正文:不在这里列出的提供商一律沿用上面那份默认设置,「跟随默认」把这一家取消、回到默认。带「网关自带」那一行的家(目前是 ZCode 两家)默认就列在这里 —— 那一段是网关自己装上去的文本,不来自客户端也不来自提示词文件,默认开启、可以关掉,也可以改它的正文(改过的那一段以你的文本为准);关掉后还能不能通过上游校验,取决于上游当下的口径,行内说明了实测依据。「模式 / 提示词文件 / 正文」管的是客户端自己的 system 怎么处理,与那个开关是两件事。"),modelsDev:r("「模型管理」里每个能力的来源标记(上游 / models.dev / 已覆盖)读的就是这份数据;映射弹窗的思考档位下拉也按这里的数据只列该模型真实认识的档位。"),retention:r("保存后立即生效:改小保留天数会立刻删除超出的历史数据,且不可恢复。"),storageFile:r("配置目录下的 agent2api.db(账号、日志、请求记录、调试报文与设置都在里面)。"),storageSize:r("库主文件的大小(不含运行期间的 WAL 临时文件,退出时已自动并回)。"),storage:r("数据统一保存在配置目录下的 agent2api.db;如需更换位置,请设置环境变量 AGENT2API_PROXY_HOME 后重启程序。"),captcha:r("开启后,登录页在浏览器后台自动完成验证(对真人无感),而脚本每次尝试都要先算一道题 —— 暴力破解与抢注的成本显著上升。仅影响面板的登录 / 注册,与 API 客户端的 API Key 无关。"),panelLogin:r("当前浏览器以管理员身份登录着本面板。「退出登录」会撤销这台设备的登录会话(30 天内的自动续期一并失效),需要重新输入账号密码;其他已登录的设备不受影响。"),exportDanger:r("导出文件内含 accessToken / refreshToken / apiKey 等凭证与自定义提供商定义,可直接用于登录。请妥善保管,不要外传或上传到公共位置。"),backupDanger:r("备份文件内含全部账号凭证、网关 API Key 与面板管理员凭据,可直接用于登录。请妥善保管,不要外传或上传到公共位置。")},kt={appLoading:"—",appUnavailable:r("主进程未返回启动设置,请更新桌面端后重试"),appTrayOn:r("关闭窗口时程序不退出,转发继续在后台运行;退出请用托盘菜单"),appTrayOff:r("关闭窗口即退出程序,后台转发随之中断"),appLightOn:r("轻量模式已开启:关窗即销毁界面进程(释放约 200MB 内存),网关在后台继续;界面销毁期间 ZCode 套餐池(Start Plan)通道不可用,相关请求会在请求日志里注明原因"),appLightOff:r("未开启轻量模式:关窗仅隐藏窗口,界面进程常驻内存,随时打开不等待"),unitsOn:r("当前显示为「1.2亿 / 8400万」这类中文量级。"),unitsOff:r("当前显示为「1.20M / 8.4k」这类英文缩写。"),debugUnavailable:r("未能读取调试模式设置,请稍后重试"),debugOn:r("正在保存上游原始报文:"),debugOff:r("未开启,转发时不保存任何原始报文。"),sanitizeUnavailable:r("未能读取指纹脱敏设置,请稍后重试"),sanitizeOn:r("正在剥离出站请求里的审核指纹:表头键值整段删除,模板句最小改写。"),sanitizeOff:r("未开启,客户端 system 模板会原样发往上游,可能被内容审核误拦(400)。"),roleCompatUnavailable:r("未能读取开发者角色兼容设置,请稍后重试"),roleCompatOn:r("正在归一角色:developer 角色消息转发前改写为 system。"),roleCompatOff:r("未开启,developer 角色消息会原样发往上游,不认识该角色的上游会报错(400)。"),corsUnavailable:r("未能读取网关跨域访问设置,请稍后重试"),corsOn:r("正在应答跨源请求:浏览器跨域请求可直连 /v1/*。"),corsOff:r("未开启,浏览器跨域无法直连网关。"),modelsDevUnavailable:r("未能读取 models.dev 元数据状态,请稍后重试"),modelsDevSourceBuiltin:r("随包内嵌的快照(发版时的那份)"),modelsDevSourceCache:r("上次成功刷新的结果(存在本机数据库里)"),modelsDevSourceNetwork:r("刚刚从 models.dev 拉到的最新数据"),modelsDevEmpty:r("还没有装载任何数据。"),promptUnavailable:r("未能读取系统提示词设置,请稍后重试"),degradeUntilFallback:r("次日 00:00"),themeSystem:r("当前跟随操作系统的深浅色设置,系统切换时界面会自动跟上。"),themeLight:r("当前固定为浅色模式,不随系统变化。"),themeDark:r("当前固定为深色模式,不随系统变化。"),zoomWeb:r("网页端的界面缩放由浏览器自己控制(Ctrl + / Ctrl -,或浏览器菜单里的缩放),此项不可调。"),zoomDefault:r("当前按 100% 显示(默认比例)。"),languageHint:r("界面语言只保存在本机(localStorage)。切换后整页立即刷新,不影响账号、密钥与转发。")},_x=[{value:"order",label:r("顺序优先"),tip:r("排第 1 的接走所有请求,直到它不能答(限流 / 故障 / 额度尽)才轮到下一位 —— 就是「从上往下」的现状行为。")},{value:"rotate",label:r("负载均衡"),tip:r("每条请求轮换到下一个可用账号;新会话交给当前最闲的那位。适合想把手里的号摊开用、不让某一个一直扛。")},{value:"provider",label:r("家内轮询"),tip:r("家之间仍按优先级,只有同一家内部轮流用各账号 —— 想优先用某几家、又不愿把某一家的某个账号焊死时用它。")}],Lx=[{value:"auto",label:r("自动"),tip:r("同一会话在 5 分钟内尽量跟住同一个账号;停了 5 分钟再发就算新会话。多数场景用它 —— 多轮对话少换号,也不至于把账号焊死。")},{value:"session",label:r("会话内"),tip:r("同一会话在 6 小时内跟住同一个账号;适合一坐几个小时的长会话,上游的上下文缓存能吃到更多。")},{value:"turn",label:r("每轮"),tip:r("每条请求都重新按策略选路 —— 行为与「关闭」一致,仅保留会话识别的记录。")},{value:"off",label:r("关闭"),tip:r("不做会话识别,每条请求都重新按策略选路。")}],NW=[{key:"routingFailoverBackoff",label:r("失败退避"),defaultOn:!0,tip:r("转发失败的账号休息一会儿再回来:15 分钟起步,连续失败翻倍,封顶 1 小时;成功一次立即恢复。目的是「让这次失败先凉一会儿」,免得一条条地反复撞同一个坏账号。")},{key:"routingSink",label:r("限流垫底"),defaultOn:!0,tip:r("还有额度时被 429 的账号排到队尾,30 分钟后自动回到队列(成功转发一次也会立即回来)。它标记的是「疑似共享池被别人抢」,不是「这个账号本身有问题」。⚠️ 注意它与路由策略的配合:在「家内轮询」下,队尾意味着这一家整体被往后排 —— 如果某个提供商的账号被批量限流,它们会集体沉到队尾,那个家可能长时间轮不到。这时可以关掉本项,让 429 过的账号只受冷却约束(冷却到点自动回来)。")},{key:"routingBreakerEnabled",label:r("熔断"),defaultOn:!1,tip:r("连续失败到阈值后把这个账号整个打开(默认关)。与失败退避的分工:退避管「单次失败先凉一会儿」,惩罚轻、自动恢复;熔断管「这个账号现在整体不对劲,别再一条条地试」,要连续成功若干次才关。打开期间该账号被跳过,除非全场只剩它 —— 那时仍放行试一次。")}],XI=[{key:"zcodeCaptchaPoolTarget",id:"settings-captcha-pool-target",label:r("库存目标"),unit:r("个"),min:1,max:10,hint:r("ZCode 套餐池(Start Plan)通道每条请求都要消耗一个当次铸造的人机验证令牌(一次性,2 分钟过期),库存低于该值时界面自动补铸到它。默认 3 个。调大能吸收突发并发,但用不掉的令牌 2 分钟就过期作废,烧的是上游风控配额。可填 1–10。")},{key:"zcodeCaptchaPoolPollMs",id:"settings-captcha-pool-poll-ms",label:r("轮询间隔"),unit:r("毫秒"),min:1e3,max:3e4,hint:r("铸造守卫每隔多久看一次库存(毫秒)。只影响发现缺货的反应速度 —— 库存够用时轮询不会铸造任何令牌。默认 2500 毫秒,可填 1000–30000。")},{key:"zcodeCaptchaPoolMaxPerRound",id:"settings-captcha-pool-max-per-round",label:r("单轮铸造上限"),unit:r("个"),min:1,max:5,hint:r("发现缺货时一轮最多铸几个(连铸之间固定隔 500 毫秒)。默认 1 个:SDK 同一实例连铸容易互相干扰。调大补货更快,但单轮连铸越多越容易触发上游风控。可填 1–5。")}];function QI(){return tt().workbuddyDesktop?.platform==="web"}let ht={...{category:Ix[0].id,scrollReset:0,busy:null,app:{status:"loading",closeToTray:!1,autostart:!1,lanAccess:!1,lanPanel:!1,lightweightMode:!1,lanIp:null,port:0,adminRegistered:null},unitsChinese:!0,retention:{status:"loading",values:null},retry:{status:"loading",values:null},retryCodes:null,timeouts:{status:"loading",values:null},queue:{status:"loading",values:null},captchaPool:{status:"loading",values:null},routing:{status:"loading",strategy:"order",sticky:"auto",failoverBackoff:!0,sink:!0,breakerEnabled:!1},debug:{status:"loading",on:!1,count:null,limit:null},sanitize:{status:"loading",on:!1},developerRoleCompat:{status:"loading",on:!1},clineHeaders:{status:"loading",defaults:{},overrides:{},effective:{}},cors:{status:"loading",on:!1},prompt:{status:"loading",mode:"passthrough",file:"",text:"",source:"",lines:0,fileError:"",degradeActive:!1,degradeUntilText:"",providers:[],gateway:{},gatewayText:{},options:[]},storage:{status:"loading",available:!0,file:"",bytes:0,accounts:null,logs:null,requests:null,dailyDays:null,debug:null},captcha:{available:!0,enabled:!1},modelsDev:{status:"loading",enabled:!0,source:"",updatedAt:0,count:0},ioFailure:null,retentionConfirm:null,dbIncludeLogs:!0,dbBackupConfirm:null,dbWarning:null,lanConfirm:null,lanRegister:null,panelLogin:!1},panelLogin:QI()};const gS=new Set;function kW(e){return gS.add(e),()=>{gS.delete(e)}}function CW(){return ht}function TW(){return x.useSyncExternalStore(kW,CW)}function Xe(e){ht={...ht,...e};for(const t of gS)t()}function Ma(e){Xe({app:{...ht.app,...e}})}function Da(){Xe({})}let Nn=null;function is(e){Nn=e,Xe({busy:e})}function Jn(){Nn=null,Xe({busy:null})}function xS(e){const t=Ix.some(a=>a.id===e)?String(e):Ix[0].id;Xe({category:t,scrollReset:ht.scrollReset+1})}function EW(e){xS(e);try{localStorage.setItem(LI,e)}catch{}}function ZI(){let e=null;try{e=localStorage.getItem(LI)}catch{e=null}xS(e)}function WI(){Xe({unitsChinese:tt().wbUnits?.isChinese?.()!==!1})}function AW(e){tt().wbUnits?.setChinese?.(e),WI(),Ve(r(e?"✅ 已改用本地量级词(万 / 亿式)":"✅ 已改用 k / M 缩写"))}function bS(e){if(e===void 0)return;if(!e||typeof e!="object"){Ma({status:"unavailable"});return}const t=e;Ma({status:"ready",closeToTray:t.closeToTray===!0,autostart:t.autostart===!0,lanAccess:t.lanAccess===!0,lanPanel:t.lanPanel===!0,lightweightMode:t.lightweightMode===!0})}async function JI(){try{bS(await tt().workbuddyDesktop?.getAppSettings())}catch(e){console.warn("读取应用设置失败:",Ot(e)),bS(null)}}async function RW(){const e=tt().workbuddyDesktop;if(e?.localIp){try{Ma({lanIp:await e.localIp()||null})}catch{}try{const t=Number((await e.getBackendStatus?.())?.port)||0;t&&Ma({port:t})}catch{}try{const t=(await e.panelAdminStatus?.())?.registered===!0;Ma({adminRegistered:t})}catch{}}}async function vS(e,t){if(Nn){Da();return}const a=ht.app,o={closeToTray:e==="tray"?t:a.closeToTray,autostart:e==="autostart"?t:a.autostart,lanAccess:a.lanAccess,lanPanel:a.lanPanel,lightweightMode:e==="lightweight"?t:a.lightweightMode};is("app"),Ma(o);const c=r(e==="autostart"?"开机自动启动":e==="lightweight"?"轻量模式":"关闭窗口时最小化到托盘");try{const d=tt().workbuddyDesktop;if(!d)throw new Error(r("主进程桥不可用"));const f=await d.saveAppSettings(o);Ma({status:"ready",...wW(f,o)}),Ve(r("✅ 已更新「{label}」",{label:c}))}catch(d){Ma(a),Ve(r("保存失败:{message}",{message:Ot(d)}),"err")}finally{Jn()}}function OW(e){if(Nn){Da();return}Xe({lanConfirm:{mode:e?"enable":"disable",panel:ht.app.lanPanel}})}function MW(e){if(Nn){Da();return}Xe({lanConfirm:{mode:"panel",panel:e}})}function yS(e){const t=ht.lanConfirm;if(Xe({lanConfirm:null}),!(!t||!e)){if(t.mode==="disable"){zx(!1,!1);return}if(t.mode==="panel"){zx(!0,t.panel);return}DW()}}async function DW(){const e=tt().workbuddyDesktop;if(!e?.panelAdminStatus){Ve(r("当前环境不支持局域网访问设置"),"err");return}is("lan");let t;try{t=(await e.panelAdminStatus())?.registered===!0}catch(a){Jn(),Ve(r("查询管理员状态失败:{message}",{message:Ot(a)}),"err");return}if(Jn(),Ma({adminRegistered:t}),t){await zx(!0,ht.app.lanPanel);return}Xe({lanRegister:{busy:!1,error:""}})}async function IW(e,t){if(Nn)return;const a=tt().workbuddyDesktop;if(!a?.panelRegister)return;const o=e.trim();if(!o||o.length>64){Xe({lanRegister:{busy:!1,error:r("请填写管理员账号(64 字符以内)")}});return}if(t.length<8){Xe({lanRegister:{busy:!1,error:r("密码至少 8 位")}});return}Xe({lanRegister:{busy:!0,error:""}});try{if(await a.panelRegister(o,t),ht.lanRegister===null)return;Xe({lanRegister:null}),Ma({adminRegistered:!0}),await zx(!0,ht.app.lanPanel)}catch(c){Xe({lanRegister:{busy:!1,error:Ot(c)}})}}function eP(){Xe({lanRegister:null})}async function zx(e,t){if(Nn)return;const a=tt().workbuddyDesktop;if(!a?.changeLanAccess){Ve(r("当前环境不支持局域网访问设置"),"err");return}is("lan"),Ma({lanAccess:e,lanPanel:t});try{(await a.changeLanAccess(e,t))?.createdKey&&Ve(r("✅ 已自动创建网关 Key「默认」(可在「网关 Key」页查看)")),Ve(r("✅ 设置已保存,应用正在重启…"))}catch(o){Ve(r("保存失败:{message}",{message:Ot(o)}),"err"),await JI()}finally{Jn()}}async function PW(){if(!Nn){Xe({ioFailure:null}),is("export");try{const e=await tt().workbuddyDesktop?.exportAccounts();if(e?.canceled){Ve(r("已取消导出"));return}const t=Number(e?.count)||0,a=Number(e?.customProviders)||0;if(!t&&!a){Ve(r("没有可导出的账号"),"err");return}const o=a?r("、{n} 个自定义提供商",{n:a}):"",c=e?.file?r(" 到 {file}",{file:e.file}):"";Ve(r("✅ 已导出 {count} 个账号{note}{file}",{count:t,note:o,file:c}))}catch(e){Ve(r("操作失败:{message}",{message:Ot(e)}),"err")}finally{Jn()}}}async function _W(){if(!Nn){Xe({ioFailure:null}),is("import");try{const e=await tt().workbuddyDesktop?.importAccounts();if(e?.canceled){Ve(r("已取消导入"));return}const t=Number(e?.added)||0,a=Number(e?.updated)||0,o=Number(e?.skipped)||0,c=Number(e?.failed)||0,d=Array.isArray(e?.errors)?e.errors:[],f=e?.customProviders??{},p=Number(f.added)||0,h=Number(f.updated)||0,b=[];o&&b.push(r("跳过 {n} 个",{n:o})),c&&b.push(r("失败 {n} 个",{n:c}));const y=b.length?r(",{items}",{items:b.join(r("、"))}):"",v=p||h?r(",自定义提供商新增 {added} 个、更新 {updated} 个",{added:p,updated:h}):"",j=r("新增 {added} 个、更新 {updated} 个{suffix}{note}",{added:t,updated:a,suffix:y,note:v});if(c){Ve(r("导入完成:{summary}",{summary:j}),"err");const w=d.slice(0,3).map(S=>{const N=S?.customProvider?r("自定义提供商 {id}",{id:S?.id||r("(无 id)")}):S?.id??r("未知账号"),k=S?.message??r("未知原因");return r("{label}({message})",{label:N,message:k})}).join(r(";"));Xe({ioFailure:{failed:c,detail:w,more:d.length>3}})}else Ve(r("✅ 导入完成:{summary}",{summary:j}));(t||a||p||h)&&await tt().wbApp?.refresh?.()}catch(e){Ve(r("操作失败:{message}",{message:Ot(e)}),"err")}finally{Jn()}}}function LW(e){Xe({dbIncludeLogs:e})}async function zW(){if(!Nn){is("dbExport");try{const e=tt().workbuddyDesktop;if(!e?.exportDatabase){Ve(r("当前环境不支持整库备份(仅桌面端可用)"),"err");return}const t=await e.exportDatabase(ht.dbIncludeLogs);if(t?.canceled){Ve(r("已取消导出"));return}const a=Number(t?.bytes)||0,o=Array.isArray(t?.tables)?t.tables.length:0;Ve(r("✅ 已备份 {n} 张表({size})到 {file}",{n:o,size:YI(a),file:t?.file||""}))}catch(e){Ve(r("操作失败:{message}",{message:Ot(e)}),"err")}finally{Jn()}}}function BW(){Nn||Xe({dbBackupConfirm:{includeLogs:ht.dbIncludeLogs}})}function jS(e){Xe({dbBackupConfirm:null}),e&&HW()}async function HW(){if(Nn)return;const e=tt().workbuddyDesktop;if(!e?.importDatabase){Ve(r("当前环境不支持整库恢复(仅桌面端可用)"),"err");return}is("dbImport"),Xe({dbWarning:null});try{const t=await e.importDatabase(),a=Array.isArray(t?.tables)?t.tables.length:0,o=t?.safetyBackup?r(",替换前的数据已备份为 {name}",{name:t.safetyBackup}):"";Ve(r("✅ 已恢复 {n} 张表{note}",{n:a,note:o}));const c=typeof t?.warning=="string"?t.warning.trim():"";c&&Xe({dbWarning:c}),await _S(),await tt().wbApp?.refresh?.()}catch(t){Ve(r("恢复失败:{message}",{message:Ot(t)}),"err")}finally{Jn()}}function Bx(e){if(e===void 0)return;const t=KI(pS,e,ht.retention.values);Xe({retention:{status:t===null?"unavailable":"ready",values:t}})}async function Hx(){try{Bx(await tt().workbuddyDesktop?.getRetention())}catch(e){console.warn("读取数据保留设置失败:",Ot(e)),Bx(null)}}let Ux=null;function Vx(e){const t=Ux;Ux=null,Xe({retentionConfirm:null}),t?.(e)}function UW(e){return new Promise(t=>{Ux&&Vx(!1),Ux=t,Xe({retentionConfirm:{head:e}})})}function VW(e,t,a){return t===null?r("没能读到「{label}」的当前值,改为 {days} 天可能会删除超出的历史数据。",{label:e.label,days:a}):r("「{label}」将从 {previous} 天改为 {days} 天。",{label:e.label,previous:t,days:a})}async function FW(e,t){if(Nn||ht.retentionConfirm)return;const a=GI(t,e.min,e.max,r("天数"));if(!a.ok){Ve(a.message,"err");return}const o=ht.retention.values?.[e.key],c=typeof o=="number"&&Number.isInteger(o)?o:null;if(c!==null&&a.value===c)return;const d=c===null||a.valueNumber.isInteger(d?.[f.key]))){const f=d?.[e.key]??t;Ve(a?r("✅ 已保留 {days} 天,超出部分已清理",{days:f}):r("✅ 已保留 {days} 天",{days:f}));return}await Hx(),Ve(a?r("✅ 已保留 {days} 天,超出部分已清理",{days:t}):r("✅ 已保留 {days} 天",{days:t}))}catch(c){Ve(r("保存失败:{message}",{message:Ot(c)}),"err"),await Hx()}finally{Jn()}}function Rp(e,t){if(t===void 0)return;const a=KI(e.fields,t,e.read().values);e.write({status:a===null?"unavailable":"ready",values:a}),e.syncExtras?.(a===null?null:t)}async function ed(e){try{Rp(e,await e.get())}catch(t){console.warn(`${e.consoleLabel}失败:`,Ot(t)),Rp(e,null)}}async function Fx(e,t,a){if(Nn)return;const o=GI(a,t.min,t.max,t.label);if(!o.ok){Ve(o.message,"err");return}const c=e.read().values?.[t.key];if(Number.isInteger(c)&&o.value===c)return;is(e.scope);const d=e.read().values;d&&e.write({status:"ready",values:{...d,[t.key]:o.value}});try{const f=await e.save({[t.key]:o.value});Rp(e,f);const p=e.read().values?.[t.key],h=Number.isInteger(p)?p:o.value;Ve(r("✅ 已保存:{label} {value}",{label:t.label,value:String(h)}))}catch(f){Ve(r("保存失败:{message}",{message:Ot(f)}),"err"),await ed(e)}finally{Jn()}}const tP={scope:"queue",fields:FI,consoleLabel:"读取排队等待设置",get:()=>tt().workbuddyDesktop?.getQueue(),save:e=>tt().workbuddyDesktop?.saveQueue(e),read:()=>ht.queue,write:e=>Xe({queue:e})};async function nP(){await ed(tP)}async function qW(e,t){await Fx(tP,e,t)}const sP={scope:"captchaPool",fields:XI,consoleLabel:"读取验证码令牌池设置",get:()=>tt().workbuddyDesktop?.getCaptchaPool(),save:e=>tt().workbuddyDesktop?.saveCaptchaPool(e),read:()=>ht.captchaPool,write:e=>Xe({captchaPool:e})};async function rP(){await ed(sP)}async function GW(e,t){await Fx(sP,e,t)}const aP="routingStrategy",lP="routingSticky",KW="routingFailoverBackoff",YW="routingSink",XW="routingBreakerEnabled",QW={[aP]:new Set(_x.map(e=>e.value)),[lP]:new Set(Lx.map(e=>e.value))};function oP(e){const t=e&&typeof e=="object"?e:{},a=(c,d)=>{const f=t[c];return typeof f=="string"&&QW[c].has(f)?f:d},o=(c,d)=>{const f=t[c];return typeof f=="boolean"?f:d};return{strategy:a(aP,"order"),sticky:a(lP,"auto"),failoverBackoff:o(KW,!0),sink:o(YW,!0),breakerEnabled:o(XW,!1)}}async function $x(){try{const e=await tt().workbuddyDesktop?.getRoutingConfig();if(e===void 0){Xe({routing:{...ht.routing,status:"unavailable"}});return}Xe({routing:{status:"ready",...oP(e)}})}catch(e){console.warn("读取路由设置失败:",Ot(e)),Xe({routing:{...ht.routing,status:"unavailable"}})}}async function wS(e){if(Nn)return;const t=tt().workbuddyDesktop;if(!t?.setRoutingConfig){Ve(r("桌面运行时不可用,暂时无法保存"),"err");return}is("routing");try{const a=await t.setRoutingConfig(e);a!==void 0?Xe({routing:{status:"ready",...oP(a)}}):await $x(),Ve(r("✅ 路由设置已保存"))}catch(a){Ve(r("保存失败:{message}",{message:Ot(a)}),"err"),await $x()}finally{Jn()}}const Op={scope:"retry",fields:UI,consoleLabel:"读取请求重试设置",get:()=>tt().workbuddyDesktop?.getRetry(),save:e=>tt().workbuddyDesktop?.saveRetry(e),read:()=>ht.retry,write:e=>Xe({retry:e}),syncExtras:e=>{if(e===null){Xe({retryCodes:null});return}const t=e[$I];if(!Array.isArray(t))return;Xe({retryCodes:t.filter(o=>typeof o=="number"&&Number.isInteger(o)&&o>=mS&&o<=hS)})}};function ZW(e){Rp(Op,e)}async function iP(){await ed(Op)}async function WW(e,t){await Fx(Op,e,t)}async function SS(e){if(!Nn){Xe({retryCodes:e}),is("codes");try{const t=await tt().workbuddyDesktop?.saveRetry({[$I]:e});Rp(Op,t),Ve(r("✅ 已保存:指定错误码直接换号"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await ed(Op)}finally{Jn()}}}async function JW(e){const t=ht.retryCodes;if(t===null)return;const a=String(e??"").trim();if(!a)return;if(!/^\d+$/.test(a)||Number(a)hS){Ve(r("状态码必须是 {min}–{max} 的整数(收到: {text})",{min:mS,max:hS,text:a}),"err");return}const o=Number(a);if(t.includes(o)){Ve(r("状态码 {code} 已在名单里",{code:o}));return}if(t.length>=qI){Ve(r("名单最多 {max} 个状态码",{max:qI}),"err");return}await SS([...t,o])}async function eJ(e){const t=ht.retryCodes;!t||!t.includes(e)||await SS(t.filter(a=>a!==e))}async function tJ(){const e=ht.retryCodes;e?.length&&await SS(e.slice(0,-1))}const cP={scope:"timeouts",fields:VI,consoleLabel:"读取请求超时设置",get:()=>tt().workbuddyDesktop?.getTimeouts(),save:e=>tt().workbuddyDesktop?.saveTimeouts(e),read:()=>ht.timeouts,write:e=>Xe({timeouts:e})};async function uP(){await ed(cP)}async function nJ(e,t){await Fx(cP,e,t)}function qx(e){if(e===void 0)return;if(!e||typeof e!="object"){Xe({debug:{status:"unavailable",on:!1,count:null,limit:null}});return}const t=e,a=Number(t.count),o=Number(t.limit);Xe({debug:{status:"ready",on:t.debugMode===!0,count:Number.isInteger(a)?a:null,limit:Number.isInteger(o)?o:null}})}async function NS(){try{qx(await tt().workbuddyDesktop?.getDebug())}catch(e){console.warn("读取调试模式设置失败:",Ot(e)),qx(null)}}async function sJ(e){if(Nn){Da();return}is("debug"),Xe({debug:{...ht.debug,on:e}});try{const t=await tt().workbuddyDesktop?.saveDebug(e);qx(t),Ve(r(e?"✅ 调试模式已开启":"✅ 调试模式已关闭"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await NS()}finally{Jn()}}function Gx(e){if(e===void 0)return;if(!e||typeof e!="object"){Xe({sanitize:{status:"unavailable",on:!1}});return}Xe({sanitize:{status:"ready",on:e.sanitizeBlacklistFingerprints===!0}})}async function kS(){try{Gx(await tt().workbuddyDesktop?.getSanitize())}catch(e){console.warn("读取出站指纹脱敏设置失败:",Ot(e)),Gx(null)}}async function rJ(e){if(Nn){Da();return}is("sanitize"),Xe({sanitize:{status:"ready",on:e}});try{const t=await tt().workbuddyDesktop?.saveSanitize(e);Gx(t),Ve(r(e?"✅ 出站指纹脱敏已开启":"已关闭出站指纹脱敏"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await kS()}finally{Jn()}}function CS(e){if(e===void 0)return;if(!e||typeof e!="object"){Xe({developerRoleCompat:{status:"unavailable",on:!1}});return}Xe({developerRoleCompat:{status:"ready",on:e.developerRoleCompat===!0}})}async function TS(){try{CS(await tt().workbuddyDesktop?.getDeveloperRoleCompat())}catch(e){console.warn("读取开发者角色兼容设置失败:",Ot(e)),CS(null)}}async function aJ(e){if(Nn){Da();return}is("developerRoleCompat"),Xe({developerRoleCompat:{status:"ready",on:e}});try{const t=await tt().workbuddyDesktop?.saveDeveloperRoleCompat(e);CS(t),Ve(r(e?"✅ 开发者角色兼容已开启":"已关闭开发者角色兼容"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await TS()}finally{Jn()}}function ES(e){if(!e||typeof e!="object")return{};const t={};for(const[a,o]of Object.entries(e))typeof o=="string"&&(t[a]=o);return t}function AS(e){if(e===void 0)return;if(!e||typeof e!="object"){Xe({clineHeaders:{status:"unavailable",defaults:{},overrides:{},effective:{}}});return}const t=e;Xe({clineHeaders:{status:"ready",defaults:ES(t.defaults),overrides:ES(t.overrides),effective:ES(t.effective)}})}async function RS(){try{AS(await tt().workbuddyDesktop?.getClineHeaders())}catch(e){console.warn("读取 Cline 伪装头失败:",Ot(e)),AS(null)}}async function lJ(){await RS(),Ve(r("Cline 伪装头已刷新"))}async function dP(e){if(Nn){Da();return}is("clineHeaders");try{const t=await tt().workbuddyDesktop?.saveClineHeaders(e);AS(t),Ve(r("✅ Cline 伪装头已保存"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await RS()}finally{Jn()}}function OS(e){if(e===void 0)return;if(!e||typeof e!="object"){Xe({cors:{status:"unavailable",on:!1}});return}Xe({cors:{status:"ready",on:e.corsEnabled===!0}})}async function MS(){try{OS(await tt().workbuddyDesktop?.getCors())}catch(e){console.warn("读取网关跨域访问设置失败:",Ot(e)),OS(null)}}async function oJ(e){if(Nn){Da();return}is("cors"),Xe({cors:{status:"ready",on:e}});try{const t=await tt().workbuddyDesktop?.saveCors(e);OS(t),Ve(r(e?"网关跨域访问已开启":"已关闭网关跨域访问"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await MS()}finally{Jn()}}async function fP(){try{const e=await tt().workbuddyDesktop?.getCaptchaSetting();Xe({captcha:{available:!0,enabled:e?.captchaEnabled===!0}})}catch(e){Xe({captcha:{available:!1,enabled:!1}}),console.warn("读取机器人校验设置失败:",Ot(e))}}async function iJ(e){if(Nn){Da();return}is("captcha"),Xe({captcha:{...ht.captcha,enabled:e}});try{const t=await tt().workbuddyDesktop?.saveCaptchaSetting(e);Xe({captcha:{available:!0,enabled:t?.captchaEnabled===!0}}),Ve(r(e?"✅ 机器人校验已开启":"⚠️ 机器人校验已关闭"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await fP()}finally{Jn()}}function DS(e){return!e||typeof e!="object"?{status:"unavailable",enabled:!0,source:"",updatedAt:0,count:0}:{status:"ready",enabled:e.modelsDevEnabled!==!1,source:typeof e.source=="string"?e.source:"",updatedAt:Number(e.updatedAt)||0,count:Number(e.count)||0}}async function IS(){try{const e=await tt().workbuddyDesktop?.getModelsDev?.();Xe({modelsDev:DS(e)})}catch(e){Xe({modelsDev:{...ht.modelsDev,status:"unavailable"}}),console.warn("读取 models.dev 元数据设置失败:",Ot(e))}}async function cJ(e){if(Nn){Da();return}is("modelsDev"),Xe({modelsDev:{...ht.modelsDev,enabled:e}});try{const t=await tt().workbuddyDesktop?.saveModelsDev?.(e);Xe({modelsDev:DS(t)}),Ve(r(e?"✅ models.dev 模型元数据已开启":"已关闭 models.dev 联网刷新"))}catch(t){Ve(r("保存失败:{message}",{message:Ot(t)}),"err"),await IS()}finally{Jn()}}async function uJ(){if(Nn){Da();return}is("modelsDevRefresh");try{const e=await tt().workbuddyDesktop?.refreshModelsDev?.();Xe({modelsDev:DS(e?.status)});const t=e?.result,a=String(t?.status||"");a==="refreshed"?Ve(r("models.dev 元数据已更新({count} 条)",{count:Number(t?.count)||0})):a==="failed"?Ve(r("刷新失败:{message}",{message:String(t?.message||"")}),"err"):Ve(String(t?.message||r("本次无需刷新")))}catch(e){Ve(r("刷新失败:{message}",{message:Ot(e)}),"err"),await IS()}finally{Jn()}}function td(e){if(e===void 0)return;if(!e||typeof e!="object"){Xe({prompt:{...ht.prompt,status:"unavailable"}});return}const t=e,a=pJ(t.promptGateway);Xe({prompt:{status:"ready",mode:String(t.promptMode||"passthrough"),file:String(t.promptFile||""),text:String(t.promptText||""),source:String(t.promptSource||""),lines:Number(t.promptLines)||0,fileError:String(t.promptFileError||""),degradeActive:t.degradeActive===!0,degradeUntilText:String(t.degradeUntilText||""),providers:dJ(t.promptProviders,a),gateway:a,gatewayText:mJ(t.promptGatewayText),options:fJ(t.promptProviderOptions)}})}function dJ(e,t){return!e||typeof e!="object"?[]:Object.entries(e).filter(([a,o])=>!!a&&!!o&&typeof o=="object").map(([a,o])=>{const c=o;return{id:a,mode:String(c.promptMode||"passthrough"),file:String(c.promptFile||""),text:String(c.promptText||""),source:String(c.promptSource||""),lines:Number(c.promptLines)||0,fileError:String(c.promptFileError||""),gateway:t[a]??!0,configured:!0}}).sort((a,o)=>a.id.localeCompare(o.id))}function fJ(e){return Array.isArray(e)?e.filter(t=>!!t&&typeof t=="object"&&String(t.id||"")).map(t=>{const a=t;return{id:String(a.id),label:String(a.label||a.id),gatewayNote:String(a.gatewayNote||""),gatewayChars:Number(a.gatewayChars)||0,gatewayBlocks:hJ(a.gatewayBlocks)}}):[]}function pJ(e){if(!e||typeof e!="object")return{};const t={};for(const[a,o]of Object.entries(e)){const c=String(a||"").trim();!c||typeof o!="boolean"||(t[c]=o)}return t}function mJ(e){if(!e||typeof e!="object")return{};const t={};for(const[a,o]of Object.entries(e)){const c=String(a||"").trim();if(!c||!o||typeof o!="object")continue;const d=o,f={identity:"",stable:"",dynamic:""};let p=!1;for(const h of["identity","stable","dynamic"]){const b=String(d[h]||"");f[h]=b,b.trim()&&(p=!0)}p&&(t[c]=f)}return t}function hJ(e){if(!e||typeof e!="object")return null;const t=e,a={identity:String(t.identity||""),stable:String(t.stable||""),dynamic:String(t.dynamic||"")};return!a.identity.trim()&&!a.stable.trim()&&!a.dynamic.trim()?null:a}async function dc(){try{td(await tt().workbuddyDesktop?.getPrompt())}catch(e){console.warn("读取系统提示词设置失败:",Ot(e)),td(null)}}async function Mp(e,t){if(Nn)return await dc(),!1;is("prompt");try{const a=await tt().workbuddyDesktop?.savePrompt(t);return td(a),Ve(r("✅ 已保存:{label}",{label:e})),!0}catch(a){return Ve(r("保存失败:{message}",{message:Ot(a)}),"err"),await dc(),!1}finally{Jn()}}async function gJ(e){const t=$o.find(a=>a.value===e);await Mp(r("模式改为「{mode}」",{mode:t?.toastLabel??e}),{promptMode:e})}async function xJ(e){await Mp(e.trim()?r("提示词文件已更新"):r("已改回内置默认提示词"),{promptFile:e})}async function bJ(e){const t=!e.trim();return Mp(r(t?"已清掉界面编辑的提示词(回落文件 / 内置默认)":"提示词正文已更新"),{promptText:t?"":e})}function qo(e,t){return e.options.find(a=>a.id===t)?.label||t}function fc(e){const t=e==null?"":String(e).trim();return t&&ht.prompt.options.some(a=>a.id===t)?t:""}async function Dp(e,t,a){return Mp(e,{promptProviders:{[t]:a}})}function Kx(e,t){const a=!t.configured;return{promptMode:t.mode,promptFile:t.file,promptText:a&&e.source==="inline"?e.text:""}}async function vJ(e){const t=fc(e);if(!t)return;const a=ht.prompt;await Dp(r("已为「{label}」单独配置提示词",{label:qo(a,t)}),t,Kx(a,{mode:a.mode,file:a.file,text:a.text,source:a.source,lines:a.lines,fileError:a.fileError,gateway:a.gateway[t]??!0,configured:!1}))}async function yJ(e,t){const a=fc(e.id);if(!a||!$o.some(c=>c.value===t))return;const o=$o.find(c=>c.value===t);await Dp(r("「{label}」的模式改为「{mode}」",{label:qo(ht.prompt,a),mode:o?.toastLabel??t}),a,{...Kx(ht.prompt,e),promptMode:t})}async function jJ(e,t){const a=fc(e.id);a&&await Dp(t.trim()?r("「{label}」的提示词文件已更新",{label:qo(ht.prompt,a)}):r("「{label}」改用内置默认提示词",{label:qo(ht.prompt,a)}),a,{...Kx(ht.prompt,e),promptFile:t})}async function wJ(e,t){const a=fc(e.id);if(!a)return!1;const o=!t.trim(),c=qo(ht.prompt,a);return Dp(o?r("已清掉「{label}」界面编辑的正文",{label:c}):r("「{label}」的提示词正文已更新",{label:c}),a,{...Kx(ht.prompt,e),promptText:o?"":t})}async function SJ(e){const t=fc(e);t&&await Dp(r("已取消「{label}」的单独配置",{label:qo(ht.prompt,t)}),t,null)}async function NJ(e,t){const a=fc(e);if(!a)return;const o=qo(ht.prompt,a);if(Nn){await dc();return}is("prompt");try{const c=await tt().workbuddyDesktop?.savePrompt({promptGateway:{[a]:t}});td(c),Ve(t?r("✅ 已为「{label}」装上网关自带提示词",{label:o}):r("已关闭「{label}」的网关自带提示词:能否通过上游校验取决于上游当前口径",{label:o}),t?"ok":"err")}catch(c){Ve(r("保存失败:{message}",{message:Ot(c)}),"err"),await dc()}finally{Jn()}}async function kJ(e,t){const a=fc(e);if(!a)return!1;const o=qo(ht.prompt,a),c=!t||!(t.identity.trim()||t.stable.trim()||t.dynamic.trim());return Mp(c?r("「{label}」的网关自带提示词已改回官方原文",{label:o}):r("「{label}」的网关自带提示词正文已更新",{label:o}),{promptGatewayText:{[a]:c?null:t}})}async function CJ(){if(!Nn){is("prompt");try{const e=await tt().workbuddyDesktop?.savePrompt({clearDegrade:!0});td(e),Ve(r("✅ 已解除内容拦截降级"))}catch(e){Ve(r("解除失败:{message}",{message:Ot(e)}),"err"),await dc()}finally{Jn()}}}function Ip(e){const t=Number(e);return Number.isFinite(t)?t:null}function PS(e){if(e===void 0)return;const t=e&&typeof e=="object"?e.database:null;if(!t||typeof t!="object"){Xe({storage:{...ht.storage,status:"unavailable"}});return}const a=t;Xe({storage:{status:"ready",available:a.available!==!1,file:String(a.file||""),bytes:Number(a.bytes)||0,accounts:Ip(a.accounts),logs:Ip(a.logs),requests:Ip(a.requests),dailyDays:Ip(a.dailyDays),debug:Ip(a.debug)}})}async function pP(){try{PS(await tt().workbuddyDesktop?.getStorage())}catch(e){console.warn("读取数据存储概况失败:",Ot(e)),PS(null)}}async function TJ(){try{return await tt().workbuddyDesktop?.panelLogout(),window.location.href="/login",!0}catch(e){return Ve(r("退出失败:{message}",{message:Ot(e)}),"err"),!1}}async function _S(){ZI(),WI(),Xe({panelLogin:QI()}),await Promise.all([JI(),RW(),Hx(),iP(),uP(),nP(),rP(),$x(),NS(),kS(),TS(),RS(),MS(),dc(),pP(),fP(),IS(),tt().wbUpdatePanel?.load?.()])}async function EJ(){await Hx(),Ve(r("保留天数已刷新"))}async function AJ(){await iP(),Ve(r("重试设置已刷新"))}async function RJ(){await uP(),Ve(r("超时设置已刷新"))}async function OJ(){await nP(),Ve(r("排队等待设置已刷新"))}async function MJ(){await rP(),Ve(r("验证码令牌池设置已刷新"))}async function DJ(){await $x(),Ve(r("路由设置已刷新"))}async function IJ(){await NS(),Ve(r("调试模式设置已刷新"))}async function PJ(){await kS(),Ve(r("指纹脱敏设置已刷新"))}async function _J(){await TS(),Ve(r("开发者角色兼容设置已刷新"))}async function LJ(){await MS(),Ve(r("网关跨域访问设置已刷新"))}async function zJ(){await dc(),Ve(r("系统提示词设置已刷新"))}async function BJ(){await pP(),Ve(r("存储概况已刷新"))}const Yx=[{key:"identity",label:r("第一段(身份句)"),short:r("第一段"),hint:r("官方原文只有一句:声明自己是 ZCode。")},{key:"stable",label:r("第二段(稳定段)"),short:r("第二段"),hint:r("工具用法与环境说明那一大段。")},{key:"dynamic",label:r("第三段(动态段)"),short:r("第三段"),hint:r("沟通方式 / 上下文管理 + Environment 段。")}];function HJ(e){return r(e==="inline"?"界面里编辑的正文":e==="file"?"提示词文件":e==="builtin"?"内置默认提示词":"(未生效)")}function mP({title:e,text:t,source:a,file:o,disabled:c,save:d,onClose:f}){const[p,h]=x.useState(t),[b,y]=x.useState(!1),v=b||c,j=p!==t;async function w(S){y(!0);try{await d(S)&&f()}finally{y(!1)}}return s.jsx(Yt,{open:!0,onOpenChange:S=>{S||v||f()},children:s.jsxs(Xt,{className:"w-[min(880px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:e})}),s.jsxs(Lt,{children:[s.jsxs(dn,{children:[s.jsx("h3",{children:r("保存后以这里的文本为准")}),s.jsxs("p",{children:[r("这一份就是「替换 / 追加」模式下网关要发的 system 正文。"),a==="none"?r("当前是透传模式:存下来也不会发出,切成「替换 / 追加」之后才生效。"):r("当前生效的正文来自{source}{file}。保存后这一份会盖过提示词文件与内置默认{note}。",{source:HJ(a),file:o.trim()&&a!=="inline"?r("(文件:{path})",{path:o.trim()}):"",note:a!=="inline"&&o.trim()?r(",文件里后续的改动在清空这里之前不再生效"):""}),r("想回到原来那份,把这里清空再保存即可(不会删掉你配的文件路径)。")]})]}),s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsx(Je,{htmlFor:"settings-prompt-body",children:r("提示词正文")}),s.jsx(Mi,{id:"settings-prompt-body",rows:18,className:"min-h-[320px]",spellCheck:!1,disabled:v,value:p,onChange:S=>h(S.currentTarget.value)}),s.jsx("div",{className:"hint",children:p.trim()?r("{lines} 行、{chars} 字符(按当前草稿计)",{lines:p.split(` +`).length,chars:p.length}):r("空 = 不要这一份,回落提示词文件 / 内置默认提示词。")})]})]}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"outline",className:"mr-auto",disabled:v||a!=="inline",onClick:()=>{w("")},children:r("清除这里的正文")}),s.jsx(ne,{variant:"outline",disabled:v,onClick:f,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:v||!j,onClick:()=>{w(p)},children:r(b?"保存中…":"保存")})]})]})})}function UJ({prompt:e,locked:t,busy:a}){const[o,c]=x.useState(!1);return s.jsxs(s.Fragment,{children:[s.jsx(ne,{id:"btn-prompt-edit-body",variant:"outline",disabled:t||a,onClick:()=>c(!0),children:r("编辑正文")}),o?s.jsx(mP,{title:r("编辑提示词正文 · 全局"),text:e.text,source:e.source,file:e.file,disabled:a,save:bJ,onClose:()=>c(!1)}):null]})}function VJ({item:e,label:t,locked:a,busy:o}){const[c,d]=x.useState(!1);return s.jsxs(s.Fragment,{children:[s.jsx(ne,{id:`btn-prompt-edit-body-${e.id}`,variant:"outline",disabled:a||o,onClick:()=>d(!0),children:r("编辑正文")}),c?s.jsx(mP,{title:r("编辑提示词正文 · {label}",{label:t}),text:e.text,source:e.source,file:e.file,disabled:o,save:f=>wJ(e,f),onClose:()=>d(!1)}):null]})}function FJ({id:e,title:t,official:a,over:o,enabled:c,disabled:d,onClose:f}){const[p,h]=x.useState(()=>({identity:o?.identity?.trim()?o.identity:a.identity,stable:o?.stable?.trim()?o.stable:a.stable,dynamic:o?.dynamic?.trim()?o.dynamic:a.dynamic})),[b,y]=x.useState(!1),v=b||d,j=Yx.some(N=>p[N.key]!==a[N.key]),w=Yx.filter(N=>o?.[N.key]?.trim()).map(N=>N.label);async function S(N){y(!0);try{await kJ(e,N)&&f()}finally{y(!1)}}return s.jsx(Yt,{open:!0,onOpenChange:N=>{N||v||f()},children:s.jsxs(Xt,{className:"w-[min(880px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:t})}),s.jsxs(Lt,{children:[s.jsxs(dn,{children:[s.jsx("h3",{children:r("三段各自成块,所以分开编辑")}),s.jsxs("p",{children:[r("上游按"),s.jsx("strong",{children:r("结构")}),r("校验身份:这三段必须各自成块排在最前(实测:三段并成一段会被回 405 / 3012)。因此这里按段编辑,不能合成一段。改过的段以你的文本为准,没改的段继续用官方原文;清空某一段 = 那一段恢复官方原文。"),w.length?r("当前已改过:{edited}。",{edited:w.join(r("、"))}):r("当前三段都是官方原文。"),c?"":r("注意:这一家的「网关自带」开关现在是关的,改过的正文要等开关打开才生效。")]}),s.jsx("p",{children:Un.promptGatewayText})]}),Yx.map(N=>s.jsxs("div",{className:"flex flex-col gap-1.5",children:[s.jsxs(Je,{htmlFor:`settings-prompt-block-${e}-${N.key}`,children:[N.label,s.jsx("span",{className:"ml-2 font-normal text-subtle",children:p[N.key]===a[N.key]?r("(官方原文)"):r("(已改)")})]}),s.jsx(Mi,{id:`settings-prompt-block-${e}-${N.key}`,rows:N.key==="identity"?2:10,className:N.key==="identity"?"min-h-[64px]":"min-h-[200px]",spellCheck:!1,disabled:v,value:p[N.key],onChange:k=>{const A=k.currentTarget.value;h(P=>({...P,[N.key]:A}))}}),s.jsx("div",{className:"hint",children:N.hint})]},N.key))]}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"outline",className:"mr-auto",disabled:v||!w.length,onClick:()=>{S(null)},children:r("恢复官方原文")}),s.jsx(ne,{variant:"outline",disabled:v,onClick:f,children:r("取消")}),s.jsx(ne,{variant:"default",disabled:v||!j,onClick:()=>{S(p)},children:r(b?"保存中…":"保存")})]})]})})}function $J({item:e,option:t,over:a,locked:o,busy:c}){const[d,f]=x.useState(!1),p=t.gatewayBlocks;return p?s.jsxs(s.Fragment,{children:[s.jsx(ne,{id:`btn-prompt-gateway-text-${e.id}`,variant:"outline",disabled:o||c,onClick:()=>f(!0),children:r("编辑正文")}),d?s.jsx(FJ,{id:e.id,title:r("编辑网关自带提示词 · {label}",{label:t.label}),official:p,over:a,enabled:e.gateway,disabled:c,onClose:()=>f(!1)}):null]}):null}function qJ(e){return e?Yx.filter(t=>(e[t.key]||"").trim()).map(t=>t.short).join(r("、")):""}function hP(e,t){return tt().wbIcons?.icon?.(e,t)||""}function Jt({tone:e,children:t}){const a=e==="ok"?"success":e==="bad"?"destructive":"outline";return s.jsx(ze,{variant:a,children:t})}function Gn({title:e,tip:t,badge:a,actions:o}){return s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:e}),t?s.jsx("span",{className:"tip-q","data-tip":t}):null,a,o?s.jsx("div",{className:"head-actions",children:o}):null]})}function GJ({title:e,tip:t,badge:a,actions:o,children:c}){function d(f){f.target.closest(".tip-q, .head-actions")&&f.preventDefault()}return s.jsxs("details",{className:"panel panel-fold",children:[s.jsxs("summary",{className:"panel-head",onClick:d,children:[s.jsx("span",{className:"fold-caret","aria-hidden":!0,dangerouslySetInnerHTML:{__html:hP("caretRight",14)}}),s.jsx("h2",{children:e}),t?s.jsx("span",{className:"tip-q","data-tip":t}):null,a,o?s.jsx("div",{className:"head-actions",children:o}):null]}),c]})}function Ur({id:e,label:t,checked:a,disabled:o,tip:c,onCheckedChange:d}){return s.jsxs("label",{className:"switch",children:[s.jsx(ns,{id:e,checked:a,disabled:o,onCheckedChange:f=>d(f)}),s.jsx("span",{children:t}),c?s.jsx("span",{className:"tip-q","data-tip":c}):null]})}function Pp({field:e,value:t,disabled:a,onCommit:o}){const[c,d]=x.useState(null),f=c!==null?c:t===null?"":String(t);async function p(){const h=c;if(h!==null)try{await o(h)}finally{d(null)}}return s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:e.id,children:e.label}),s.jsxs("span",{className:"retention-input",children:[s.jsx(Fe,{id:e.id,type:"number",min:e.min,max:e.max,step:1,inputMode:"numeric",className:"w-[92px] max-w-[92px] text-center tabular-nums",value:f,disabled:a||t===null,onChange:h=>d(h.target.value),onFocus:()=>d(String(t??"")),onBlur:()=>{p()},onKeyDown:h=>{h.key==="Enter"&&h.currentTarget.blur()}}),s.jsx("span",{className:"unit",children:e.unit})]}),s.jsx("div",{className:"hint",children:e.hint})]})}function _p(e){return e.status==="ready"?s.jsx(Jt,{tone:"ok",children:r("已生效")}):e.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")})}function kr({id:e,onClick:t}){return s.jsx(ne,{id:e,variant:"outline",onClick:t,children:r("刷新")})}function KJ(e){const{lanAccess:t,lanPanel:a,lanIp:o,port:c}=e.app;if(!t)return r("未开启:网关只监听 127.0.0.1,仅本机可以访问。");const d=`http://${o||r("<本机IP>")}${c?`:${c}`:""}`,f=a?r(";网页管理面板已一并开放(浏览器打开同一地址)"):"";return r("已开启:其他设备把 API 地址指向 {url}{panel}。",{url:`${d}/v1`,panel:f})}function YJ({snap:e}){const t=e.app,a=t.status==="unavailable"?kt.appUnavailable:t.status==="loading"?kt.appLoading:t.closeToTray?t.lightweightMode?kt.appLightOn:kt.appTrayOn:kt.appTrayOff;return s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("启动与托盘"),tip:Wn.tray,badge:t.status==="ready"?s.jsx(Jt,{tone:"ok",children:r("已应用")}):t.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")})}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"settings-switches",children:[s.jsx(Ur,{id:"settings-close-to-tray",label:r("关闭窗口时最小化到托盘"),checked:t.closeToTray,disabled:e.busy==="app",onCheckedChange:o=>{vS("tray",o)}}),s.jsx(Ur,{id:"settings-lightweight",label:r("轻量模式(关窗释放界面内存)"),tip:Wn.lightweight,checked:t.lightweightMode,disabled:e.busy==="app"||!t.closeToTray,onCheckedChange:o=>{vS("lightweight",o)}}),s.jsx(Ur,{id:"settings-autostart",label:r("开机自动启动"),checked:t.autostart,disabled:e.busy==="app",onCheckedChange:o=>{vS("autostart",o)}})]}),s.jsx("div",{className:"settings-state",children:a})]})]}),!e.panelLogin&&s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("局域网访问"),tip:Wn.lan}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"settings-switches",children:[s.jsx(Ur,{id:"settings-lan-access",label:r("允许局域网内的设备访问网关"),checked:e.app.lanAccess,disabled:e.busy==="lan",onCheckedChange:OW}),s.jsx(Ur,{id:"settings-lan-panel",label:r("同时开放网页管理面板"),checked:e.app.lanPanel,disabled:e.busy==="lan"||!e.app.lanAccess,onCheckedChange:MW})]}),s.jsx("div",{className:"settings-state",children:KJ(e)})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("计量单位"),tip:Wn.units}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"settings-switches",children:s.jsx(Ur,{id:"settings-chinese-units",label:r("使用中文单位(亿 / 万)"),checked:e.unitsChinese,onCheckedChange:AW})}),s.jsx("div",{className:"settings-state",children:e.unitsChinese?kt.unitsOn:kt.unitsOff})]})]})]})}function XJ(e){return e==="light"?kt.themeLight:e==="dark"?kt.themeDark:kt.themeSystem}function QJ(){const[e,t]=x.useState(yW),[a,o]=x.useState(jW),c=tt().workbuddyDesktop?.platform==="web";x.useEffect(()=>{const f=h=>t(String(h.detail||"system")),p=h=>o(Number(h.detail)||100);return window.addEventListener(zI,f),window.addEventListener(BI,p),()=>{window.removeEventListener(zI,f),window.removeEventListener(BI,p)}},[]);const d=a===100?r("100%(默认)"):`${a}%`;return s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("显示模式"),tip:Wn.displayTheme}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{children:s.jsx(En,{"aria-label":r("显示模式"),options:vW,value:e,onValueChange:f=>tt().wbApp?.applyTheme?.(f)})}),s.jsx("div",{className:"settings-state",children:XJ(e)})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("界面缩放"),tip:Wn.displayZoom}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"retention-list",children:s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"settings-zoom",children:r("缩放比例")}),s.jsx("span",{className:"prompt-input",children:s.jsxs(ut,{value:String(a),onValueChange:f=>tt().wbApp?.applyZoom?.(Number(f)),children:[s.jsx(ft,{id:"settings-zoom",className:"w-[140px]",disabled:c,"aria-label":r("界面缩放比例"),children:s.jsx(dt,{children:d})}),s.jsx(pt,{children:bW.map(f=>s.jsx($e,{value:String(f),children:f===100?r("100%(默认)"):`${f}%`},f))})]})}),s.jsx("div",{className:"hint",children:r("放大或缩小整个界面(文字与控件一起变),效果与浏览器 Ctrl +/- 相同:共 11 档,立即生效并记住。")})]})}),s.jsx("div",{className:"settings-state",children:c?kt.zoomWeb:a===100?kt.zoomDefault:r("当前按 {n}% 显示。",{n:a})})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("语言设置"),tip:Wn.displayLanguage}),s.jsxs("div",{className:"panel-body",children:[s.jsx(ZJ,{}),s.jsx("div",{className:"settings-state",children:kt.languageHint})]})]})]})}function ZJ(){const e=window.wbI18n,t=e?.rawLocale?.()??"auto",a=e?.LOCALES??[],o=c=>c==="auto"?r("跟随系统"):a.find(d=>d.code===c)?.label??c;return s.jsx("div",{className:"retention-list",children:s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"settings-language",children:r("界面语言")}),s.jsx("span",{className:"prompt-input",children:s.jsxs(ut,{value:t,onValueChange:c=>{c&&e?.setLocale(c)},children:[s.jsx(ft,{id:"settings-language",className:"w-[200px]","aria-label":r("界面语言"),children:s.jsx(dt,{children:o(t)})}),s.jsxs(pt,{children:[s.jsx($e,{value:"auto",children:r("跟随系统")}),a.map(c=>s.jsx($e,{value:c.code,children:c.label},c.code))]})]})})]})})}function WJ({codes:e,status:t,busy:a}){const[o,c]=x.useState(""),d=x.useRef(null),f=t==="unavailable";return s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"settings-retry-no-codes-input",children:r("指定错误码直接换号")}),s.jsx("div",{className:"retention-input",children:s.jsxs("div",{className:f?"tag-input disabled":"tag-input",onClick:()=>d.current?.focus(),children:[(e||[]).map(p=>s.jsxs("span",{className:"tag-chip",children:[s.jsx("span",{className:"v",children:p}),s.jsx(ne,{type:"button",variant:"ghost",size:"icon-2xs",className:"tag-x text-muted-foreground hover:bg-destructive-soft hover:text-destructive",title:r("删除 {code}",{code:p}),"aria-label":r("删除状态码 {code}",{code:p}),onClick:h=>{h.stopPropagation(),eJ(p)},children:"✕"})]},p)),s.jsx(Fe,{ref:d,id:"settings-retry-no-codes-input",type:"text",className:"tag-input-field h-auto border-0 bg-transparent px-0 shadow-none focus:shadow-none",placeholder:f?"—":r("输入状态码,回车添加"),inputMode:"numeric",autoComplete:"off",value:o,disabled:f||a,onChange:p=>c(p.target.value),onKeyDown:p=>{if(p.key==="Enter"){p.preventDefault(),JW(o),c("");return}p.key==="Backspace"&&!o&&tJ()}})]})}),s.jsx("div",{className:"hint",children:Un.retryCodes})]})}function JJ({prompt:e,locked:t,busy:a}){const[o,c]=x.useState(null);async function d(){const f=o;if(f!==null)try{await xJ(f)}finally{c(null)}}return s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"settings-prompt-file",children:r("提示词文件")}),s.jsx("span",{className:"prompt-input",children:s.jsx(Fe,{id:"settings-prompt-file",type:"text",placeholder:r("留空 = 用内置默认提示词"),value:o!==null?o:e.file,disabled:t||a,onChange:f=>c(f.target.value),onFocus:()=>c(e.file),onBlur:()=>{d()},onKeyDown:f=>{f.key==="Enter"&&f.currentTarget.blur()}})}),s.jsx("div",{className:"hint",children:Un.promptFile})]})}function eee(e){if(e.status==="loading")return kt.appLoading;if(e.status==="unavailable")return kt.promptUnavailable;const t=gP(e.source),a=[];return e.mode==="passthrough"?a.push(r("客户端 system 原样出站(只靠指纹脱敏改写模板句)。")):a.push(r("{mode}生效:上游收到的 system 来自{source}{lines}。",{mode:e.mode==="custom"?r("替换"):r("追加"),source:t,lines:e.lines?r("({n} 行)",{n:e.lines}):""})),e.fileError&&a.push(`⚠️ ${e.fileError}`),a.join("")}function gP(e){return e==="inline"?r("界面里编辑的正文"):e==="file"?r("提示词文件"):e==="builtin"?r("内置默认提示词"):""}function tee(e){return r("已自动切换到最小中性提示词(撞了上游内容拦截,多半是 system 指纹误报),到 {until} 自动解除。",{until:e.degradeUntilText||kt.degradeUntilFallback})+r("期间本模式自己的提示词不会发出;把提示词改好后可以立即解除。")}function nee({item:e,label:t,locked:a,busy:o}){const[c,d]=x.useState(null);async function f(){const p=c;if(p!==null)try{p!==e.file&&await jJ(e,p)}finally{d(null)}}return s.jsx("span",{className:"prompt-input",children:s.jsx(Fe,{id:`settings-prompt-file-${e.id}`,type:"text",placeholder:r("留空 = 用内置默认提示词"),"aria-label":r("{label} 提示词文件",{label:t}),value:c!==null?c:e.file,disabled:a||o,onChange:p=>d(p.target.value),onFocus:()=>d(e.file),onBlur:()=>{f()},onKeyDown:p=>{p.key==="Enter"&&p.currentTarget.blur()}})})}function see(e,t){if(!t)return r("未单独配置:模式、提示词文件与正文都跟随上面的全局配置(当前「{mode}」)。",{mode:$o.find(o=>o.value===e.mode)?.toastLabel??e.mode});if(e.mode==="passthrough")return r("客户端 system 原样出站。");const a=r("{mode}生效:上游收到的 system 来自{source}{lines}。",{mode:e.mode==="custom"?r("替换"):r("追加"),source:gP(e.source),lines:e.lines?r("({n} 行)",{n:e.lines}):""});return e.fileError?`${a} ⚠️ ${e.fileError}`:a}function ree({item:e,option:t,over:a,locked:o,busy:c}){const d=e.gateway,f=t.gatewayChars,p=f?r("约 {n} 字符",{n:f.toLocaleString("zh-CN")}):r("一段内置装配"),h=qJ(a);return s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{className:"prompt-gateway-label",children:r("网关自带")}),s.jsxs("span",{className:"prompt-input",children:[s.jsx(Ur,{id:`settings-prompt-gateway-${e.id}`,label:r("装上{size}的官方身份提示词",{size:p}),checked:d,disabled:o||c,onCheckedChange:b=>{NJ(e.id,b)}}),s.jsx($J,{item:e,option:t,over:a,locked:o,busy:c})]}),s.jsxs("div",{className:d?"hint":"hint prompt-gateway-off",children:[d?t.gatewayNote:r("已关闭。{note}",{note:t.gatewayNote}),h?r(" 正文已改:{edited}(其余段用官方原文)。",{edited:h}):""]})]})}function aee({prompt:e,locked:t,busy:a}){const o=e.options,c=new Map(e.providers.map(w=>[w.id,w])),d=[];for(const w of o)w.gatewayNote&&d.push(c.get(w.id)??{id:w.id,mode:e.mode,file:e.file,text:e.text,source:e.source,lines:e.lines,fileError:e.fileError,gateway:e.gateway[w.id]??!0,configured:!1});for(const w of e.providers)d.some(S=>S.id===w.id)||d.push({...w,gateway:e.gateway[w.id]??!0,configured:!0});const f=new Set(d.map(w=>w.id)),p=o.filter(w=>!w.gatewayNote&&!f.has(w.id)),[h,b]=x.useState(""),y=w=>o.find(S=>S.id===w)?.label||w,v=w=>o.find(S=>S.id===w),j=w=>v(w)?.gatewayNote||"";return s.jsxs(s.Fragment,{children:[d.map(w=>{const S=y(w.id),N=$o.find(P=>P.value===w.mode)?.optionLabel??w.mode,k=j(w.id),A=v(w.id);return s.jsxs(x.Fragment,{children:[s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:`settings-prompt-mode-${w.id}`,children:S}),s.jsxs("span",{className:"prompt-input",children:[s.jsxs(ut,{value:w.mode,onValueChange:P=>{P!=null&&String(P)&&yJ(w,String(P))},children:[s.jsx(ft,{id:`settings-prompt-mode-${w.id}`,className:"w-[240px]",disabled:t||a,"aria-label":r("{label} 的提示词模式",{label:S}),children:s.jsx(dt,{children:N})}),s.jsx(pt,{children:$o.map(P=>s.jsx($e,{value:P.value,children:P.optionLabel},P.value))})]}),s.jsx(nee,{item:w,label:S,locked:t,busy:a}),s.jsx(VJ,{item:w,label:S,locked:t,busy:a}),w.configured?s.jsx(ne,{variant:"outline",disabled:t||a,onClick:()=>{SJ(w.id)},children:r("跟随默认")}):null]}),s.jsx("div",{className:"hint",children:see(w,w.configured)})]}),A&&k?s.jsx(ree,{item:w,option:A,over:e.gatewayText[w.id],locked:t,busy:a}):null]},w.id)}),s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"settings-prompt-provider-add",children:r("添加提供商")}),s.jsx("span",{className:"prompt-input",children:s.jsxs(ut,{value:h,onValueChange:w=>{b("");const S=w==null?"":String(w);S&&vJ(S)},children:[s.jsx(ft,{id:"settings-prompt-provider-add",className:"w-[240px]",disabled:t||a||p.length===0,"aria-label":r("添加要单独配置的提供商"),children:s.jsx(dt,{children:h?y(h):p.length?r("添加提供商…"):r("全部已配置")})}),s.jsx(pt,{children:p.map(w=>s.jsx($e,{value:w.id,children:w.label},w.id))})]})}),s.jsx("div",{className:"hint",children:Un.promptProviders})]})]})}function lee({snap:e}){const t=e.prompt,a=t.status!=="ready",o=e.busy==="prompt",c=$o.find(d=>d.value===t.mode)?.optionLabel??t.mode;return s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("系统提示词"),tip:Wn.prompt,badge:t.status==="ready"?s.jsx(Jt,{tone:"ok",children:t.mode==="passthrough"?r("透传"):r("已接管")}):t.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-prompt-refresh",onClick:()=>{zJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"retention-list",children:[s.jsxs("div",{className:"prompt-group",children:[r("全局配置"),s.jsx("span",{className:"note",children:r("所有未单独配置的提供商都用这一份")})]}),s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"settings-prompt-mode",children:r("模式")}),s.jsx("span",{className:"prompt-input",children:s.jsxs(ut,{value:t.mode,onValueChange:d=>{d!=null&&String(d)&&gJ(String(d))},children:[s.jsx(ft,{id:"settings-prompt-mode",className:"w-[240px]",disabled:a||o,"aria-label":r("系统提示词模式"),children:s.jsx(dt,{children:c})}),s.jsx(pt,{children:$o.map(d=>s.jsx($e,{value:d.value,children:d.optionLabel},d.value))})]})}),s.jsx("div",{className:"hint",children:Un.promptMode})]}),s.jsx(JJ,{prompt:t,locked:a,busy:o}),s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"btn-prompt-edit-body",children:r("提示词正文")}),s.jsx("span",{className:"prompt-input",children:s.jsx(UJ,{prompt:t,locked:a,busy:o})}),s.jsxs("div",{className:"hint",children:[Un.promptText,t.source==="inline"?r("(当前生效的就是这一份,{n} 行)",{n:t.lines}):t.text.trim()?r("(已存一份正文,{n} 行;切成「替换 / 追加」后生效)",{n:t.lines}):""]})]}),s.jsxs("div",{className:"prompt-group",children:[r("按提供商配置"),s.jsx("span",{className:"note",children:r("只列单独配置过的家,其余沿用上面的全局配置")})]}),s.jsx(aee,{prompt:t,locked:a,busy:o}),t.status==="ready"&&t.degradeActive?s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{children:r("内容拦截降级")}),s.jsx("span",{className:"prompt-input",children:s.jsx(ne,{id:"btn-prompt-clear-degrade",variant:"outline",disabled:o,onClick:()=>{CJ()},children:r("立即解除")})}),s.jsx("div",{className:"hint",children:tee(t)})]}):null]}),s.jsx("div",{className:"settings-state",children:eee(t)}),s.jsx("div",{className:"hint retention-note",children:Un.prompt})]})]})}function oee(e){if(e.status==="loading")return kt.appLoading;if(e.status==="unavailable")return kt.debugUnavailable;const t=e.count!==null&&e.limit!==null?r("已保存 {count} / {limit} 条报文(超出后丢弃最旧的)。",{count:e.count,limit:e.limit}):"";return e.on?`${kt.debugOn}${t}${r("凭据类请求头已脱敏。")}`:kt.debugOff}function iee({snap:e}){return s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("请求超时"),tip:Wn.timeouts,badge:_p(e.timeouts),actions:s.jsx(kr,{id:"btn-timeouts-refresh",onClick:()=>{RJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"retention-list",children:VI.map(t=>s.jsx(Pp,{field:t,value:e.timeouts.values?.[t.key]??null,disabled:e.busy==="timeouts",onCommit:a=>nJ(t,a)},t.key))}),s.jsx("div",{className:"hint retention-note",children:Un.timeouts})]})]})}function cee({snap:e}){return s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("请求重试"),tip:Wn.retry,badge:_p(e.retry),actions:s.jsx(kr,{id:"btn-retry-refresh",onClick:()=>{AJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"retention-list",children:[UI.map(t=>s.jsx(Pp,{field:t,value:e.retry.values?.[t.key]??null,disabled:e.busy==="retry",onCommit:a=>WW(t,a)},t.key)),s.jsx(WJ,{codes:e.retryCodes,status:e.retry.status,busy:e.busy==="codes"||e.busy==="retry"})]}),s.jsx("div",{className:"hint retention-note",children:Un.retry})]})]})}const uee={routingFailoverBackoff:"failoverBackoff",routingSink:"sink",routingBreakerEnabled:"breakerEnabled"};function dee({snap:e}){const t=e.routing,a=e.busy==="routing"||t.status!=="ready",o=_x.find(d=>d.value===t.strategy)?.value??"order",c=Lx.find(d=>d.value===t.sticky)?.value??"auto";return s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("账号路由"),tip:Wn.routing,badge:t.status==="ready"?void 0:t.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-routing-refresh",onClick:()=>{DJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"retention-list",children:[s.jsx("div",{className:"prompt-group",children:r("选路规则")}),s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{children:r("路由策略")}),s.jsxs("div",{className:"routing-row",children:[s.jsx("span",{className:"tip-q","data-tip":_x.find(d=>d.value===o)?.tip}),s.jsx(En,{options:_x.map(d=>({value:d.value,label:d.label})),value:o,disabled:a,"aria-label":r("路由策略"),onValueChange:d=>{wS({routingStrategy:String(d)})}})]})]}),s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{children:r("会话粘性")}),s.jsxs("div",{className:"routing-row",children:[s.jsx("span",{className:"tip-q","data-tip":Lx.find(d=>d.value===c)?.tip}),s.jsx(En,{options:Lx.map(d=>({value:d.value,label:d.label})),value:c,disabled:a,"aria-label":r("会话粘性"),onValueChange:d=>{wS({routingSticky:String(d)})}})]})]}),s.jsxs("div",{className:"prompt-group",children:[r("自愈保护"),s.jsx("span",{className:"note",children:r("默认值通常够用,出问题时才需要调")})]}),NW.map(d=>{const f=`route-protection-${d.key}`;return s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:f,children:d.label}),s.jsxs("div",{className:"routing-row",children:[s.jsx("span",{className:"tip-q","data-tip":d.tip}),s.jsx(ns,{id:f,checked:t[uee[d.key]],disabled:a,onCheckedChange:p=>{wS({[d.key]:p})}})]})]},d.key)})]}),s.jsx("div",{className:"hint retention-note",children:Un.routing})]})]})}function fee({snap:e}){return s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("排队等待"),tip:Wn.queue,badge:_p(e.queue),actions:s.jsx(kr,{id:"btn-queue-refresh",onClick:()=>{OJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"retention-list",children:FI.map(t=>s.jsx(Pp,{field:t,value:e.queue.values?.[t.key]??null,disabled:e.busy==="queue",onCommit:a=>qW(t,a)},t.key))}),s.jsx("div",{className:"hint retention-note",children:Un.queue})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("验证码令牌池"),tip:Wn.captchaPool,badge:_p(e.captchaPool),actions:s.jsx(kr,{id:"btn-captcha-pool-refresh",onClick:()=>{MJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"retention-list",children:XI.map(t=>s.jsx(Pp,{field:t,value:e.captchaPool.values?.[t.key]??null,disabled:e.busy==="captchaPool",onCommit:a=>GW(t,a)},t.key))}),s.jsx("div",{className:"hint retention-note",children:Un.captchaPool})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("models.dev 模型元数据"),tip:Wn.modelsDev,badge:e.modelsDev.status==="ready"?s.jsx(Jt,{tone:"ok",children:r("{count} 条",{count:Number(e.modelsDev.count).toLocaleString("zh-CN")})}):e.modelsDev.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-modelsdev-refresh",onClick:()=>{uJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx(Ur,{id:"settings-modelsdev",label:r("用 models.dev 补齐上游没声明的模型能力与思考档位(联网刷新,内嵌快照始终生效)"),checked:e.modelsDev.enabled,disabled:e.modelsDev.status!=="ready"||e.busy==="modelsDev",onCheckedChange:t=>{cJ(t)}}),s.jsx("div",{className:"settings-state",children:mW(e.modelsDev)}),s.jsx("div",{className:"hint retention-note",children:Un.modelsDev})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("指纹脱敏"),tip:Wn.sanitize,badge:e.sanitize.status==="ready"?s.jsx(Jt,{tone:"ok",children:r("已生效")}):e.sanitize.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-sanitize-refresh",onClick:()=>{PJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"settings-switches",children:s.jsx(Ur,{id:"settings-sanitize",label:r("剥离上游审核黑名单指纹(改写请求体里的模板句与表头)"),checked:e.sanitize.on,disabled:e.sanitize.status!=="ready"||e.busy==="sanitize",onCheckedChange:t=>{rJ(t)}})}),s.jsx("div",{className:"settings-state",children:e.sanitize.status==="loading"?kt.appLoading:e.sanitize.status==="unavailable"?kt.sanitizeUnavailable:e.sanitize.on?kt.sanitizeOn:kt.sanitizeOff}),s.jsx("div",{className:"hint retention-note",children:Un.sanitize})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("开发者角色兼容"),tip:Wn.developerRoleCompat,badge:e.developerRoleCompat.status==="ready"?s.jsx(Jt,{tone:"ok",children:r("已生效")}):e.developerRoleCompat.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-role-compat-refresh",onClick:()=>{_J()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"settings-switches",children:s.jsx(Ur,{id:"settings-developer-role-compat",label:r("转发前将 developer 角色消息转换为 system,兼容不支持 developer 角色的 OpenAI 兼容上游"),checked:e.developerRoleCompat.on,disabled:e.developerRoleCompat.status!=="ready"||e.busy==="developerRoleCompat",onCheckedChange:t=>{aJ(t)}})}),s.jsx("div",{className:"settings-state",children:e.developerRoleCompat.status==="loading"?kt.appLoading:e.developerRoleCompat.status==="unavailable"?kt.roleCompatUnavailable:e.developerRoleCompat.on?kt.roleCompatOn:kt.roleCompatOff}),s.jsx("div",{className:"hint retention-note",children:Un.developerRoleCompat})]})]}),s.jsx(mee,{snap:e}),s.jsx(lee,{snap:e}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("调试模式"),tip:Wn.debug,badge:e.debug.status==="ready"?s.jsx(Jt,{tone:"ok",children:r("已生效")}):e.debug.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-debug-refresh",onClick:()=>{IJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"settings-switches",children:s.jsx(Ur,{id:"settings-debug-mode",label:r("保存上游原始报文(请求头、请求体、响应头、响应体)"),checked:e.debug.on,disabled:e.debug.status!=="ready"||e.busy==="debug",onCheckedChange:t=>{sJ(t)}})}),s.jsx("div",{className:"settings-state",children:oee(e.debug)}),s.jsx("div",{className:"hint retention-note",children:Un.debug})]})]})]})}function xP(e){const t=Object.entries(e.defaults).map(([a,o])=>({key:a,value:a in e.overrides?e.overrides[a]:o,isDefault:!0}));for(const[a,o]of Object.entries(e.overrides))a in e.defaults||t.push({key:a,value:o,isDefault:!1});return t}function pee(e,t){const a=Object.keys(e);return a.length!==Object.keys(t).length?!1:a.every(o=>e[o]===t[o])}function mee({snap:e}){const t=e.clineHeaders,[a,o]=x.useState(()=>xP(t));x.useEffect(()=>{o(xP(t))},[t]);const c=e.busy==="clineHeaders",d=t.status==="ready";function f(b,y){o(v=>v.map((j,w)=>w===b?{...j,...y}:j))}function p(){const b={};for(const y of a){const v=y.key.trim();v&&(y.isDefault?y.value!==(t.defaults[v]??"")&&(b[v]=y.value):y.value!==""&&(b[v]=y.value))}return b}const h=d&&!pee(p(),t.overrides);return s.jsx(GJ,{title:r("Cline 伪装头"),tip:Wn.clineHeaders,badge:t.status==="ready"?s.jsx(Jt,{tone:"ok",children:r("已生效")}):t.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-cline-headers-refresh",onClick:()=>{lJ()}}),children:s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"retention-list",children:[d?a.map((b,y)=>s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(Fe,{className:"w-[190px] shrink-0 font-mono text-[13px]",value:b.key,readOnly:b.isDefault,disabled:c,placeholder:"X-Custom-Header",onChange:v=>f(y,{key:v.target.value})}),s.jsx(Fe,{className:"flex-1 font-mono text-[13px]",value:b.value,disabled:c,placeholder:b.isDefault?r("值(留空 = 不发送)"):r("值"),onChange:v=>f(y,{value:v.target.value})}),s.jsx(ne,{variant:"ghost",disabled:c,onClick:()=>{b.isDefault?f(y,{value:t.defaults[b.key]??""}):o(v=>v.filter((j,w)=>w!==y))},children:b.isDefault?r("还原"):r("删除")})]},String(y))):null,s.jsxs("div",{className:"flex items-center gap-2",children:[s.jsx(ne,{variant:"outline",disabled:c||!d,onClick:()=>o(b=>[...b,{key:"",value:"",isDefault:!1}]),children:r("添加自定义头")}),s.jsx("div",{className:"flex-1"}),s.jsx(ne,{variant:"ghost",disabled:c||!d||Object.keys(t.overrides).length===0,onClick:()=>{dP({})},children:r("恢复默认")}),s.jsx(ne,{disabled:c||!d||!h,onClick:()=>{dP(p())},children:r("保存")})]})]}),s.jsx("div",{className:"settings-state",children:t.status==="loading"?kt.appLoading:t.status==="unavailable"?r("未能读取 Cline 伪装头设置,请稍后重试"):h?r("有未保存的修改"):r("{n} 个头将随每个 Cline 请求发送",{n:Object.keys(t.effective).length})+(Object.keys(t.overrides).length>0?r("({n} 项被覆盖)",{n:Object.keys(t.overrides).length}):r("(全部为默认值)"))}),s.jsx("div",{className:"hint retention-note",children:Un.clineHeaders})]})})}function hee({snap:e}){const[t,a]=x.useState(!1);async function o(){a(!0),await TJ()||a(!1)}return s.jsxs(s.Fragment,{children:[s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("网关跨域访问(CORS)"),tip:Wn.cors,badge:e.cors.status==="ready"?e.cors.on?s.jsx(Jt,{tone:"bad",children:r("已开启")}):s.jsx(Jt,{tone:"ok",children:r("已关闭")}):e.cors.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:"idle",children:r("检测中…")}),actions:s.jsx(kr,{id:"btn-cors-refresh",onClick:()=>{LJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"settings-switches",children:s.jsx(Ur,{id:"settings-cors",label:r("允许浏览器里的页面跨来源调用网关(/v1/*)"),checked:e.cors.on,disabled:e.cors.status!=="ready"||e.busy==="cors",onCheckedChange:c=>{oJ(c)}})}),s.jsx("div",{className:"settings-state",children:e.cors.status==="loading"?kt.appLoading:e.cors.status==="unavailable"?kt.corsUnavailable:e.cors.on?kt.corsOn:kt.corsOff}),s.jsx("div",{className:"hint retention-note",children:Un.cors})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("机器人校验")}),s.jsxs("div",{className:"panel-body",children:[s.jsx(Ur,{id:"settings-captcha",label:r("登录 / 注册需要通过 ALTCHA 人机验证(工作量证明)"),checked:e.captcha.enabled,disabled:!e.captcha.available||e.busy==="captcha",onCheckedChange:c=>{iJ(c)}}),s.jsx("div",{className:"hint",children:Un.captcha})]})]}),e.panelLogin?s.jsxs("section",{className:"panel",id:"panel-login-section",children:[s.jsx(Gn,{title:r("面板登录")}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"hint",children:Un.panelLogin}),s.jsx("div",{className:"mt-2.5",children:s.jsx(ne,{id:"btn-panel-logout",variant:"outline",disabled:t,onClick:()=>{o()},children:r("退出登录")})})]})]}):null]})}function Lp({label:e,value:t}){return s.jsxs("div",{className:"storage-count",children:[s.jsx("span",{className:"k",children:e}),s.jsx("span",{className:"v",children:t})]})}function gee(e){return e.status==="loading"?s.jsx(Jt,{tone:"idle",children:r("检测中…")}):e.status==="unavailable"?s.jsx(Jt,{tone:"bad",children:r("不可用")}):s.jsx(Jt,{tone:e.available?"ok":"bad",children:e.available?r("已生效"):r("数据库不可用")})}function xee({snap:e}){const t=e.busy,a=e.storage,o=tt().workbuddyDesktop?.platform==="web",c=a.status==="ready",d=c&&a.available,f=c&&a.file?a.file:"—",p=h=>d&&h!==null?SW(h):"—";return s.jsxs(s.Fragment,{children:[!o&&s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("整库备份 / 恢复"),tip:Wn.backup,actions:s.jsxs(s.Fragment,{children:[s.jsx(ne,{id:"btn-settings-db-export",variant:"outline",disabled:t==="dbExport"||t==="dbImport",onClick:()=>{zW()},children:r(t==="dbExport"?"备份中…":"备份整库")}),s.jsx(ne,{id:"btn-settings-db-restore",variant:"destructive",disabled:t==="dbExport"||t==="dbImport",onClick:()=>BW(),children:r(t==="dbImport"?"恢复中…":"恢复整库")})]})}),s.jsxs("div",{className:"panel-body",children:[e.dbWarning?s.jsxs("div",{className:"danger-zone",id:"settings-db-warning",children:[s.jsx("strong",{children:r("⚠️ 密钥警告")}),s.jsx("br",{}),e.dbWarning]}):null,s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"settings-db-include-logs",children:r("包含日志与调试报文")}),s.jsx(ns,{id:"settings-db-include-logs",checked:e.dbIncludeLogs,disabled:t!==null,onCheckedChange:LW}),s.jsx("div",{className:"hint",children:r("取消勾选后只备份业务数据(账号、配置与余额记录),文件显著变小;事件日志、请求统计与调试报文不会进备份文件。")})]}),s.jsx("div",{className:"danger-zone",children:s.jsx("strong",{children:Un.backupDanger})})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("账号导入 / 导出"),tip:Wn.io,actions:s.jsxs(s.Fragment,{children:[s.jsx(ne,{id:"btn-settings-export",variant:"outline",disabled:t==="export",onClick:()=>{PW()},children:r(t==="export"?"导出中…":"导出账号")}),s.jsx(ne,{id:"btn-settings-import",variant:"default",disabled:t==="import",onClick:()=>{_W()},children:r(t==="import"?"导入中…":"导入账号")})]})}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"danger-zone",children:[s.jsx("strong",{children:r("导出文件内含 accessToken / refreshToken / apiKey 等凭证与自定义提供商定义")}),r(",可直接用于登录。请妥善保管,不要外传或上传到公共位置。")]}),e.ioFailure?s.jsx("div",{className:"io-result",children:s.jsxs("span",{className:"text-destructive",children:[r("失败 {n} 个:{detail}",{n:e.ioFailure.failed,detail:e.ioFailure.detail}),e.ioFailure.more?r(" 等"):""]})}):null]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("数据保留"),tip:Wn.retention,badge:_p(e.retention),actions:s.jsx(kr,{id:"btn-retention-refresh",onClick:()=>{EJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"retention-list",children:pS.map(h=>s.jsx(Pp,{field:h,value:e.retention.values?.[h.key]??null,disabled:e.busy==="retention",onCommit:b=>FW(h,b)},h.key))}),s.jsx("div",{className:"hint retention-note",children:Un.retention})]})]}),s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("数据存储"),tip:Wn.storage,badge:gee(a),actions:s.jsx(kr,{id:"btn-storage-refresh",onClick:()=>{BJ()}})}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"retention-list",children:[s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"storage-db-path",children:r("数据库文件")}),s.jsx("span",{className:"storage-line",children:s.jsx("span",{className:"storage-path",id:"storage-db-path",title:f,children:f})}),s.jsx("div",{className:"hint",children:Un.storageFile})]}),s.jsxs("div",{className:"retention-row",children:[s.jsx("label",{htmlFor:"storage-db-size",children:r("占用大小")}),s.jsx("span",{className:"storage-line",children:s.jsx("span",{className:"storage-path",id:"storage-db-size",children:d?YI(a.bytes):"—"})}),s.jsx("div",{className:"hint",children:Un.storageSize})]})]}),s.jsxs("div",{className:"retention-list storage-counts",children:[s.jsx(Lp,{label:r("账号"),value:p(a.accounts)}),s.jsx(Lp,{label:r("事件日志"),value:p(a.logs)}),s.jsx(Lp,{label:r("请求记录"),value:p(a.requests)}),s.jsx(Lp,{label:r("报表天数"),value:p(a.dailyDays)}),s.jsx(Lp,{label:r("调试报文"),value:p(a.debug)})]}),s.jsx("div",{className:"hint retention-note",children:Un.storage})]})]})]})}const bee=[{title:r("问题反馈"),desc:r("遇到 Bug、报错或异常行为"),cta:r("去反馈"),url:"https://github.com/aimod-cc/agent2api/issues/new?template=bug_report.yml"},{title:r("功能建议"),desc:r("想要的新功能或改进想法"),cta:r("提建议"),url:"https://github.com/aimod-cc/agent2api/issues/new?template=feature_request.yml"},{title:r("请求提供商 / 模型支持"),desc:r("希望接入新的提供商或模型"),cta:r("去申请"),url:"https://github.com/aimod-cc/agent2api/issues/new?template=provider_request.yml"}];function vee(){return s.jsxs("section",{className:"panel",children:[s.jsx(Gn,{title:r("反馈与需求")}),s.jsxs("div",{className:"panel-body",children:[s.jsx("div",{className:"hint",children:r("点下面的入口会用系统默认浏览器打开 GitHub 的对应表单(需要 GitHub 账号,模板已预设好,填完直接提交即可)。")}),s.jsx("div",{className:"mt-3 flex flex-col gap-2",children:bee.map(e=>s.jsxs("div",{className:"flex items-center justify-between gap-3 rounded-md border border-hairline bg-surface-2 px-3 py-2.5",children:[s.jsxs("div",{className:"min-w-0",children:[s.jsx("div",{className:"text-[12.5px] text-foreground",children:e.title}),s.jsx("div",{className:"mt-0.5 text-[12px] text-subtle",children:e.desc})]}),s.jsx(ne,{variant:"outline",size:"sm",onClick:()=>{hW(e.url)},children:e.cta})]},e.url))})]})]})}function yee({confirm:e}){const t=x.useRef(null);return s.jsx(Yt,{open:e!==null,onOpenChange:a=>{a||Vx(!1)},children:s.jsxs(Xt,{className:"w-[min(440px,calc(100vw-48px))]",initialFocus:t,children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("确认缩短保留天数")})}),s.jsx(Lt,{children:s.jsxs("div",{className:"danger-zone",children:[e?.head,s.jsx("br",{}),s.jsx("strong",{children:r("超出的历史数据会被立即删除,且不可恢复。")}),r("确定继续?")]})}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{ref:t,variant:"outline",onClick:()=>Vx(!1),children:r("取消")}),s.jsx(ne,{variant:"destructive",onClick:()=>Vx(!0),children:r("继续并清理")})]})]})})}const LS={enable:{title:r("开启局域网访问"),body:()=>s.jsxs(s.Fragment,{children:[r("为了安全,开启前需要先注册一个"),s.jsx("b",{children:r("面板管理员账号")}),r("(已注册过会跳过这一步,直接生效)。"),s.jsx("br",{}),r("开启后网关将监听所有网卡,同一局域网内的设备即可把 API 地址指向本机 IP 一起使用;管理接口从此要求管理员会话或网关 Key,转发接口在没有一把启用的 Key 时也会拒绝服务(届时会自动创建一把名为「默认」的 Key)。"),s.jsx("br",{}),s.jsx("strong",{children:r("保存后应用将重启以生效。")}),r("确定继续?")]}),label:r("继续")},disable:{title:r("关闭局域网访问"),body:()=>s.jsxs(s.Fragment,{children:[r("关闭后网关回到只监听 127.0.0.1,局域网内的设备将无法继续访问;已配置的网关 Key 与账号都不受影响。"),s.jsx("br",{}),s.jsx("strong",{children:r("保存后应用将重启以生效。")}),r("确定继续?")]}),label:r("关闭并重启")},panel:{title:r("变更网页管理面板"),body:e=>e?s.jsxs(s.Fragment,{children:[r("开放后,局域网内其他设备的浏览器打开本机 IP 即可进入管理面板(需管理员账号登录)。"),s.jsx("br",{}),s.jsx("strong",{children:r("保存后应用将重启以生效。")}),r("确定继续?")]}):s.jsxs(s.Fragment,{children:[r("关闭后,管理界面不再从局域网提供,只有本机的桌面程序可以管理;已开启的 API 转发不受影响。"),s.jsx("br",{}),s.jsx("strong",{children:r("保存后应用将重启以生效。")}),r("确定继续?")]}),label:r("保存并重启")}};function jee({confirm:e}){const t=x.useRef(null);return s.jsx(Yt,{open:e!==null,onOpenChange:a=>{a||jS(!1)},children:s.jsxs(Xt,{className:"w-[min(440px,calc(100vw-48px))]",initialFocus:t,children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("恢复整库备份")})}),s.jsx(Lt,{children:s.jsxs("div",{className:"danger-zone",children:[r("本机的全部数据(账号、网关配置、日志统计与桌面设置)将被所选备份文件"),s.jsx("strong",{children:r("整体替换")}),r(",包括恢复后需要重新登录管理面板。"),s.jsx("br",{}),s.jsx("strong",{children:r("替换前会自动把当前数据备份到配置目录的 backups/ 文件夹。")}),r("确定继续?")]})}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{ref:t,variant:"outline",onClick:()=>jS(!1),children:r("取消")}),s.jsx(ne,{variant:"destructive",onClick:()=>jS(!0),children:r("选择备份文件并恢复")})]})]})})}function wee({confirm:e,register:t}){const a=x.useRef(null),o=x.useRef(null),c=x.useRef(null),d=e!==null||t!==null,f=()=>{t!==null?eP():yS(!1)},p=()=>{IW(o.current?.value??"",c.current?.value??"")};return s.jsx(Yt,{open:d,onOpenChange:h=>{h||f()},children:s.jsx(Xt,{className:"w-[min(480px,calc(100vw-48px))]",initialFocus:a,children:t!==null?s.jsxs(s.Fragment,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("注册管理员账号")})}),s.jsxs(Lt,{children:[s.jsx("div",{children:r("局域网开放后,管理接口要求登录。请设置管理员账号与密码,注册完成会直接继续开启流程。")}),s.jsxs("form",{onSubmit:h=>{h.preventDefault(),t.busy||p()},children:[s.jsx(Je,{htmlFor:"lan-admin-name",className:"mt-3.5 mb-[5px] block text-[12.5px]",children:r("账号")}),s.jsx(Fe,{ref:o,id:"lan-admin-name",autoComplete:"username",placeholder:r("管理员账号"),disabled:t.busy}),s.jsx(Je,{htmlFor:"lan-admin-password",className:"mt-3.5 mb-[5px] block text-[12.5px]",children:r("密码")}),s.jsx(Fe,{ref:c,id:"lan-admin-password",type:"password",autoComplete:"new-password",placeholder:r("至少 8 位"),disabled:t.busy})]}),s.jsx("div",{className:"mt-3 min-h-5 text-[13px] whitespace-pre-wrap text-destructive",children:t.error})]}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{ref:a,variant:"outline",onClick:eP,disabled:t.busy,children:r("取消")}),s.jsx(ne,{onClick:p,disabled:t.busy,children:t.busy?r("正在注册…"):r("注册并开启")})]})]}):s.jsxs(s.Fragment,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:e?LS[e.mode].title:""})}),s.jsx(Lt,{children:s.jsx("div",{children:e?LS[e.mode].body(e.panel):null})}),s.jsxs(tn,{children:[s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{ref:a,variant:"outline",onClick:()=>yS(!1),children:r("取消")}),s.jsx(ne,{onClick:()=>yS(!0),children:e?LS[e.mode].label:r("继续")})]})]})})})}function See(){const e=TW(),t=x.useRef(null);x.useEffect(()=>{const o=t.current;o&&(o.scrollTop=0)},[e.scrollReset]);const a=o=>e.category===o?"settings-pane active":"settings-pane";return s.jsxs("div",{className:"settings-layout",children:[s.jsx("nav",{className:"settings-nav",id:"settings-nav",children:Ix.map(o=>s.jsxs("button",{type:"button",className:e.category===o.id?"settings-nav-item active":"settings-nav-item","data-cat":o.id,onClick:()=>EW(o.id),children:[s.jsx("span",{className:"ico",dangerouslySetInnerHTML:{__html:hP(o.icon,17)}}),o.label]},o.id))}),s.jsxs("div",{className:"settings-panes",ref:t,children:[s.jsx("div",{className:a("general"),"data-cat":"general",children:s.jsx(YJ,{snap:e})}),s.jsx("div",{className:a("assistant"),"data-cat":"assistant",children:s.jsx($7,{})}),s.jsx("div",{className:a("display"),"data-cat":"display",children:s.jsx(QJ,{})}),s.jsx("div",{className:a("gateway"),"data-cat":"gateway",children:s.jsx(fee,{snap:e})}),s.jsx("div",{className:a("routing"),"data-cat":"routing",children:s.jsx(dee,{snap:e})}),s.jsx("div",{className:a("retry"),"data-cat":"retry",children:s.jsx(cee,{snap:e})}),s.jsx("div",{className:a("timeout"),"data-cat":"timeout",children:s.jsx(iee,{snap:e})}),s.jsx("div",{className:a("security"),"data-cat":"security",children:s.jsx(hee,{snap:e})}),s.jsx("div",{className:a("data"),"data-cat":"data",children:s.jsx(xee,{snap:e})}),s.jsx("div",{className:a("feedback"),"data-cat":"feedback",children:s.jsx(vee,{})}),s.jsx("div",{className:a("about"),"data-cat":"about"})]}),s.jsx(yee,{confirm:e.retentionConfirm}),s.jsx(jee,{confirm:e.dbBackupConfirm}),s.jsx(wee,{confirm:e.lanConfirm,register:e.lanRegister})]})}const bP='.page[data-page="settings"]';let zS=null;function vP(){if(zS)return;const e=document.querySelector(bP);e&&(e.replaceChildren(),zS=wn.createRoot(e),Ts.flushSync(()=>{zS?.render(s.jsx(See,{}))}))}ZI(),document.querySelector(bP)?vP():document.addEventListener("DOMContentLoaded",vP,{once:!0}),window.wbSettingsPanel={load:_S,render:bS,renderRetention:Bx,renderRetry:ZW,renderDebug:qx,renderSanitize:Gx,renderPrompt:td,renderStorage:PS,showCategory:xS},tt().wbApp?.currentPage==="settings"&&_S();const yP="agent2api-col-config:",BS=[{value:"left",label:r("左")},{value:"center",label:r("中")},{value:"right",label:r("右")}],Nee=e=>BS.some(t=>t.value===e),kee=BS.map(e=>`ta-${e.value}`),jP="colset-dragging",wP="open";let HS=null;const SP="grid grid-cols-[18px_minmax(78px,1fr)_44px_minmax(106px,auto)] items-center gap-2";function NP(){return window}const Xx=new Map;function US(e){return e.columns.map(t=>({key:t.key,visible:t.visible!==!1,align:t.align||"left"}))}function Cee(e,t){const a=Array.isArray(t),o=!a&&t&&typeof t=="object"?t:null,c=a?t:Array.isArray(o?.items)?o.items:[],d=o?.defaults&&typeof o.defaults=="object"?o.defaults:null,f=new Map(e.columns.map(y=>[y.key,y])),p=new Map(US(e).map(y=>[y.key,y])),h=[],b=new Set;for(const y of c){const v=y&&typeof y=="object"?y:{},j=String(v.key||"");if(!f.has(j)||b.has(j))continue;b.add(j);const w=f.get(j),S=w.align||"left",N=d?d[j]??S:w.legacyAlign??S,k=Nee(v.align)?v.align:null;h.push({key:j,visible:v.visible!==!1,align:k&&k!==N?k:S})}for(const y of e.columns)b.has(y.key)||Tee(h,e,{...p.get(y.key)});return h}function Tee(e,t,a){const o=t.columns.findIndex(c=>c.key===a.key);for(let c=o-1;c>=0;c-=1){const d=e.findIndex(f=>f.key===t.columns[c].key);if(d>=0){e.splice(d+1,0,a);return}}e.unshift(a)}function Eee(e){try{const t=localStorage.getItem(yP+e.id);return Cee(e,t?JSON.parse(t):[])}catch{return US(e)}}function Aee(e){try{const t={};for(const a of e.columns)t[a.key]=a.align||"left";localStorage.setItem(yP+e.id,JSON.stringify({v:2,defaults:t,items:e.config}))}catch{}}const Ree=e=>Xx.get(e)?.config||null,Oee=(e,t)=>e.columns.find(a=>a.key===t)?.label||t;function kP(e,t){const a=Xx.get(e);if(!a)return t;const o=new Map(t.map(c=>[c.key,c]));return a.config.filter(c=>c.visible&&o.has(c.key)).map(c=>({...o.get(c.key),align:c.align}))}function zp(e,{silent:t=!1}={}){Aee(e),t||e.onChange?.(e.config),Ll&&Ll.id===e.id&&Ll.repaint()}let Ll=null;function Qx(){if(!Ll)return;const e=Ll;Ll=null,e.destroy()}function Mee(e,t){Qx();const a=document.createElement("div");document.body.append(a);const o=wn.createRoot(a),c={current:null};Ll={id:e.id,spec:e,repaint:()=>c.current?.(),destroy:()=>{o.unmount(),a.remove()}},o.render(s.jsx(Lee,{spec:e,anchor:t,repaintRef:c,onClose:Qx}))}function Dee(e,t){Ll&&Ll.id===e.id?Qx():Mee(e,t)}function Iee(e,t,a){const o=[...e],[c]=o.splice(t,1);return o.splice(a,0,c),o}function Pee(e,t,a,o){const c=document.elementFromPoint(e,t)?.closest?.("[data-col-key]");if(!c||!a||!a.contains(c))return"";const d=c.getAttribute("data-col-key")||"";return o.some(f=>f.key===d)?d:""}const _ee=s.jsxs("svg",{viewBox:"0 0 24 24",width:13,height:13,fill:"currentColor","aria-hidden":"true",className:"block",children:[s.jsx("circle",{cx:"9",cy:"6",r:"1.6"}),s.jsx("circle",{cx:"15",cy:"6",r:"1.6"}),s.jsx("circle",{cx:"9",cy:"12",r:"1.6"}),s.jsx("circle",{cx:"15",cy:"12",r:"1.6"}),s.jsx("circle",{cx:"9",cy:"18",r:"1.6"}),s.jsx("circle",{cx:"15",cy:"18",r:"1.6"})]});function Lee({spec:e,anchor:t,repaintRef:a,onClose:o}){const c=t,d=x.useRef(null),[f,p]=x.useState(null),h=x.useRef(null),[,b]=x.useState(0),y=x.useCallback(()=>b(N=>N+1),[]);x.useLayoutEffect(()=>(a.current=y,()=>{a.current=null}),[y,a]),x.useEffect(()=>(c.classList.add(wP),c.setAttribute("aria-expanded","true"),()=>{c.classList.remove(wP),c.setAttribute("aria-expanded","false")}),[c]),x.useEffect(()=>{const N=()=>{const k=c.getBoundingClientRect();(!c.isConnected||!k.width||k.bottom<=0||k.top>=window.innerHeight)&&o()};return window.addEventListener("scroll",N,!0),window.addEventListener("resize",N),()=>{window.removeEventListener("scroll",N,!0),window.removeEventListener("resize",N)}},[c,o]),x.useEffect(()=>()=>{const N=h.current;N&&(N.teardown(),zp(e))},[e]);function v(N,k){const A=e.config.find(P=>P.key===N);A&&(!k&&e.config.filter(P=>P.visible).length<=1||(A.visible=k,zp(e)))}function j(N,k){const A=e.config.find(P=>P.key===N);!A||A.align===k||(A.align=k,zp(e))}function w(){e.config=US(e),zp(e)}function S(N,k){k.preventDefault(),h.current?.teardown(),p(N),document.body.classList.add(jP);const A=d.current;function P(){window.removeEventListener("pointermove",R),window.removeEventListener("pointerup",C),document.body.classList.remove(jP),h.current=null,p(null)}function R(E){const D=Pee(E.clientX,E.clientY,A,e.config);if(!D||D===N)return;const I=e.config.findIndex(_=>_.key===N),O=e.config.findIndex(_=>_.key===D);I<0||O<0||(e.config=Iee(e.config,I,O),y())}function C(){P(),zp(e)}h.current={teardown:P},window.addEventListener("pointermove",R),window.addEventListener("pointerup",C)}return s.jsx(wf,{open:!0,modal:!1,onOpenChange:(N,k)=>{N||(k?.reason==="outside-press"&&(HS=k.event??null),o())},children:s.jsxs(Sf,{anchor:c,side:"bottom",align:"end",sideOffset:6,collisionPadding:8,className:"w-[336px]","aria-label":r("{name}的列设置",{name:e.label||e.id}),children:[s.jsxs("div",{className:Ue(SP,"border-b border-hairline px-3 pt-[9px] pb-[7px]","text-[10.5px] font-semibold tracking-[.05em] text-muted-foreground"),children:[s.jsx("span",{}),s.jsx("span",{className:"text-center",children:r("列")}),s.jsx("span",{className:"text-center",children:r("显示")}),s.jsx("span",{className:"text-center",children:r("对齐")})]}),s.jsx("div",{ref:d,className:"min-h-0 flex-1 overflow-y-auto px-3 py-1",children:e.config.map(N=>{const k=Oee(e,N.key),A=f===N.key;return s.jsxs("div",{"data-col-key":N.key,className:Ue(SP,"rounded-sm py-[5px] hover:bg-nav-hover",A&&"opacity-50"),children:[s.jsx("span",{className:"flex cursor-grab items-center justify-center text-muted-foreground opacity-50 hover:opacity-100",title:r("按住拖动调整列顺序"),onPointerDown:P=>S(N.key,P),children:_ee}),s.jsx("span",{className:"min-w-0 truncate text-xs text-foreground",title:k,children:k}),s.jsx(ns,{className:"justify-self-center",checked:N.visible,onCheckedChange:P=>v(N.key,P),"aria-label":r("显示「{label}」列",{label:k}),title:N.visible?r("这一列正在显示"):r("这一列已隐藏")}),s.jsx(En,{className:"w-full justify-center",options:BS,value:N.align,onValueChange:P=>j(N.key,P),"aria-label":r("「{label}」列的对齐",{label:k})})]},N.key)})}),s.jsxs("div",{className:"flex items-center gap-2 border-t border-hairline px-3 pt-2 pb-[9px]",children:[s.jsx("span",{className:"flex-1 text-[11px] text-muted-foreground",children:r("拖动 ⋮⋮ 调整顺序")}),s.jsx(ne,{variant:"outline",size:"sm",onClick:w,children:r("恢复默认")})]})]})})}function zee(e,t){e&&(e.classList.remove(...kee),e.classList.add(`ta-${t}`))}const CP=new Map;function Bee(e,t){const a=CP.get(e);if(a&&a.table===t&&a.row.isConnected)return a;const o=t?.querySelector("thead tr");if(!o)return null;const c=t?.querySelector("colgroup")??null,d={table:t,row:o,group:c,ths:new Map([...o.querySelectorAll("th[data-col]")].map(f=>[f.getAttribute("data-col")||"",f])),cols:new Map([...c?.querySelectorAll("col[data-col]")||[]].map(f=>[f.getAttribute("data-col")||"",f]))};return CP.set(e,d),d}function Hee(e,t,a){const o=Xx.get(e);if(!o||!t)return;const c=Bee(e,t);if(!c)return;const{row:d,group:f,ths:p,cols:h}=c;for(const b of o.config){const y=p.get(b.key);if(!y)continue;const v=h.get(b.key);if(!b.visible||a?.has(b.key)){y.remove(),v?.remove();continue}zee(y,b.align),d.appendChild(y),v&&f&&f.appendChild(v)}NP().wbTableColumns?.repaint?.(e)}function Uee(e){const t=document.createElement("button");return t.type="button",t.className=`sm colset-btn${e.buttonClass?` ${e.buttonClass}`:""}`,t.id=`btn-colset-${e.id}`,t.title=r("调整「{name}」的列:显示 / 隐藏、顺序、对齐",{name:e.label||e.id}),t.innerHTML=`${NP().wbIcons?.icon?.("settings",14)||""}${r("列设置")}`,t.setAttribute("aria-haspopup","dialog"),t.setAttribute("aria-expanded","false"),t.addEventListener("click",a=>{if(HS===a){HS=null;return}Dee(e,t)}),t}function Vee(e){return(typeof e.mount=="function"?e.mount():e.mount?document.querySelector(e.mount):null)||null}function Fee(e){const t={...e,config:Eee(e)};Xx.set(e.id,t);const a=Vee(e),o=Uee(t);return a&&(e.buttonPlacement==="last"?a.appendChild(o):a.insertBefore(o,a.firstChild)),{apply:c=>kP(e.id,c),config:()=>t.config}}window.wbColSettings={register:Fee,apply:kP,configOf:Ree,syncStaticHead:Hee,close:Qx};function rl(){return window}const $ee=new Set(["updateCheck"]);function TP(e){return(Array.isArray(e?.tasks)?e.tasks:[]).filter(a=>!$ee.has(a.id))}const qee=2e4,EP=1,Gee=" · ";function nd(e,t){rl().wbApp?.toast?.(e,t)}function Zx(e){return e instanceof Error?e.message:String(e)}function AP(e){return r(e==="seconds"?"秒":"分钟")}function RP(e){const t=Number(e)||0;return t?new Date(t).toLocaleTimeString("zh-CN",{hour12:!1}):""}function OP(e){const t=Number(e)||0;if(!t)return"";const a=t-Date.now();if(a<=0)return r("即将执行");const o=Math.round(a/1e3);if(o<60)return r("{n} 秒后",{n:o});const c=Math.round(o/60);if(c<60)return r("{n} 分钟后",{n:c});const d=Math.round(c/60);return d<24?r("{n} 小时后",{n:d}):r("{n} 天后",{n:Math.round(d/24)})}function Kee(e){const t=e.runner==="backend",a=[];if(e.lastRunAt){const o=RP(e.lastRunAt);a.push(e.lastResult?r("上次执行 {time}({result})",{time:o,result:e.lastResult}):r("上次执行 {time}",{time:o}))}else a.push(r(t?"还没有执行记录":"由页面按间隔自动刷新"));return t&&e.enabled&&e.nextRunAt&&a.push(r("下次执行 {time}({relative})",{time:RP(e.nextRunAt),relative:OP(e.nextRunAt)})),t&&e.retryAt&&e.retryAt>Date.now()&&a.push(r("冷却中,{relative}重试",{relative:OP(e.retryAt)})),a.join(Gee)}function Yee(e,t){const a=String(t??"").trim(),o=AP(e.unit);if(!a)return{ok:!1,message:r("间隔不能为空(可填 {min}–{max} {unit})",{min:e.min,max:e.max,unit:o})};if(!/^\d+$/.test(a))return{ok:!1,message:r("间隔必须是整数")};const c=Number(a);return ce.max?{ok:!1,message:r("间隔必须在 {min}–{max} {unit} 之间(当前填的是 {value})",{min:e.min,max:e.max,unit:o,value:a})}:{ok:!0,value:c}}function Xee(e){const t=rl();e.id==="logsAutoRefresh"?t.wbLogsPanel?.applyAutoRefresh?.(e):e.id==="requestsAutoRefresh"?t.wbRequestsPanel?.applyAutoRefresh?.(e):e.id==="reportAutoRefresh"&&t.wbReport?.applyAutoRefresh?.(e)}let pc=!1,Wx=null,VS=!1;async function MP(){if(!Wx){VS=!0;return}await Wx()}function Qee(){const[e,t]=x.useState([]),[a,o]=x.useState(!1),[c,d]=x.useState(null),[f,p]=x.useState({});function h(D){p(I=>{if(!(D in I))return I;const O={...I};return delete O[D],O})}const b=x.useCallback(async()=>{const D=rl().workbuddyDesktop;try{if(!D)throw new Error(r("后端桥不可用"));const I=await D.getScheduledTasks();t(TP(I)),o(!0)}catch(I){console.warn("读取定时任务失败:",Zx(I)),t([]),o(!0)}},[]),y=x.useCallback(async()=>{if(pc||document.hidden||rl().wbApp?.currentPage!=="tasks")return;const D=rl().workbuddyDesktop;if(D)try{const I=await D.getScheduledTasks();t(TP(I))}catch(I){console.warn("同步定时任务状态失败:",Zx(I))}},[]);x.useEffect(()=>{const D=window.setInterval(()=>{y()},qee);return()=>{window.clearInterval(D)}},[y]),x.useEffect(()=>{rl().wbApp?.renderTopbarStatus?.()},[e,a]),x.useEffect(()=>(Wx=b,VS&&(VS=!1,b()),()=>{Wx=null}),[b]);async function v(D,I,O){if(pc)return;const _=rl().workbuddyDesktop;if(_){pc=!0;try{const z=await _.saveScheduledTask(D,I);t(H=>H.map(B=>B.id===D?z:B)),Xee(z),nd(r("✅ 已更新「{label}」",{label:O}))}catch(z){nd(r("保存失败:{reason}",{reason:Zx(z)}),"err"),await b()}finally{pc=!1}}}async function j(D,I){const O=Yee(D,I);if(!O.ok){nd(O.message,"err"),h(D.id);return}if(O.value===D.interval){h(D.id);return}await v(D.id,{interval:O.value},r("{name}间隔",{name:r(D.label)})),h(D.id)}async function w(D){if(pc)return;const I=rl().workbuddyDesktop;if(I){pc=!0,d(D.id);try{const O=await I.runScheduledTask(D.id);if(O?.task){const _=O.task;t(z=>z.map(H=>H.id===D.id?_:H))}nd(r("✅ {task}:{summary}",{task:r(D.label),summary:O?.summary||r("已执行")})),D.id==="credentialMaintenance"&&await rl().wbApp?.refresh?.()}catch(O){nd(r("执行失败:{reason}",{reason:Zx(O)}),"err"),await b()}finally{pc=!1,d(null)}}}function S(D){const I=AP(D.unit),O=c===D.id;return s.jsxs("div",{className:"task-item","data-task":D.id,children:[s.jsxs("div",{className:"task-main",children:[s.jsxs("div",{className:"task-title",children:[s.jsxs("label",{className:"switch",children:[s.jsx(ns,{checked:D.enabled===!0,onCheckedChange:_=>{v(D.id,{enabled:_},r("{name}开关",{name:r(D.label)}))}}),s.jsx("span",{className:"task-name",children:r(D.label)})]}),s.jsx("span",{className:"tip-q","data-tip":r(D.description)}),s.jsx(ze,{className:"task-badge",variant:D.enabled?"success":"outline",children:D.enabled?r("已开启"):r("已关闭")}),D.running?s.jsx(ze,{className:"task-badge",variant:"warning",children:r("执行中…")}):null]}),s.jsx("div",{className:"task-state",children:Kee(D)})]}),s.jsxs("div",{className:"task-actions",children:[s.jsxs("span",{className:"task-interval",children:[s.jsx("span",{className:"task-interval-label",children:r("每")}),s.jsx(Fe,{type:"number",inputMode:"numeric",min:D.min,max:D.max,step:1,className:"w-[74px] max-w-[74px] text-center tabular-nums",value:f[D.id]??String(D.interval),title:r("可填 {min}–{max} {unit}(默认 {def} {unit})",{min:D.min,max:D.max,unit:I,def:D.defaultInterval}),onChange:_=>{const z=_.currentTarget.value;p(H=>({...H,[D.id]:z}))},onBlur:_=>{j(D,_.currentTarget.value)},onKeyDown:_=>{_.key==="Enter"&&_.currentTarget.blur()}}),s.jsx("span",{className:"task-interval-unit",children:I})]}),D.canRun===!0?s.jsx(ne,{size:"sm",variant:"outline",disabled:O||D.running===!0,onClick:()=>{w(D)},children:r(O?"执行中…":"立即执行")}):null]})]},D.id)}const N=e.length===0,k=e.filter(D=>D.enabled).length,A=N?a?r("不可用"):"—":r("{enabled} / {total} 个已开启",{enabled:k,total:e.length}),P=N?a?"destructive":"outline":k?"success":"outline",R=P==="destructive"?"bad":P==="success"?"ok":"",C=e.map(D=>S(D)),E=C.slice(EP);return s.jsxs("section",{className:"panel",children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("任务清单")}),s.jsx(ze,{id:"tasks-badge",variant:P,"data-tone":R,children:A}),s.jsx("span",{className:"panel-sub",children:r("状态每 20 秒自动同步")}),s.jsx("div",{className:"head-actions",children:s.jsx(ne,{variant:"outline",onClick:()=>{b().then(()=>nd(r("定时任务已刷新")))},children:r("刷新")})})]}),s.jsx("div",{className:"panel-body",children:s.jsx("div",{className:"task-list",children:N?s.jsx("div",{className:"log-empty",children:r(a?"没有可显示的定时任务。后端未返回任务清单,请确认网关正在运行。":"正在加载定时任务…")}):s.jsxs(s.Fragment,{children:[C.slice(0,EP),E.length?s.jsx("div",{className:"task-grid",children:E}):null]})})}),s.jsxs("div",{className:"panel-foot",children:[s.jsxs("span",{children:[r("任务配置保存在 "),s.jsx("code",{children:"~/.agent2api/config.json"}),r(" 的 "),s.jsx("code",{children:"scheduledTasks"}),r(" 字段")]}),s.jsx("span",{children:r("改动立即生效,不需要重启程序")})]})]})}const DP='.page[data-page="tasks"]';let IP=!1;function PP(){if(IP)return;const e=document.querySelector(DP);e&&(IP=!0,e.replaceChildren(),wn.createRoot(e).render(s.jsx(Qee,{})))}document.querySelector(DP)?PP():document.addEventListener("DOMContentLoaded",PP,{once:!0}),window.wbTasksPanel={load:MP},rl().wbApp?.currentPage==="tasks"&&MP();const _P="workbuddy-desktop-update-skip";function Zee(){try{return localStorage.getItem(_P)||""}catch{return""}}function Wee(e){try{localStorage.setItem(_P,e)}catch{}}let LP="";function Jx(e,t){if(!e||e.hasUpdate!==!0||e.pendingRestart===!0)return;const a=String(e.latestVersion||"").trim();a&&nn().wbApp?.currentPage!=="settings"&&(a===Zee()||a===LP||(LP=a,zP(),sd=document.createElement("div"),document.body.append(sd),Bp=wn.createRoot(sd),Bp.render(s.jsx(Jee,{info:e,onGoUpdate:t,onClose:zP}))))}function Jee({info:e,onGoUpdate:t,onClose:a}){const o=String(e.latestVersion||""),c=String(e.notes||"").trim(),d=c?dD(c):"";function f(){Wee(o),a()}function p(){a();const h=nn();h.wbApp?.showPage?.("settings"),h.wbSettingsPanel?.showCategory?.("about"),t(e)}return s.jsx(Yt,{open:!0,onOpenChange:h=>{h||a()},children:s.jsxs(Xt,{children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("检测到更新")})}),s.jsxs(Lt,{children:[s.jsxs("p",{className:"text-[12.5px] leading-[1.6] text-subtle",children:[r("新版本 "),s.jsx("strong",{className:"text-foreground",children:o}),r(" 已发布(当前 {current}),更新日志如下:",{current:e.currentVersion||r("未知")})]}),s.jsx("div",{className:"max-h-[46vh] overflow-y-auto overscroll-contain pr-1.5",onClick:Xu,children:d?s.jsx("div",{className:"md-body",dangerouslySetInnerHTML:{__html:d}}):s.jsx("div",{className:"md-body",children:s.jsx("p",{className:"md-body-empty",children:r("这个版本没有填写发布说明。")})})})]}),s.jsxs(tn,{children:[s.jsx(ne,{variant:"outline",size:"sm",onClick:f,children:r("跳过此次更新")}),s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:a,children:r("取消")}),s.jsx(ne,{variant:"default",onClick:p,children:r("去更新")})]})]})})}let Bp=null,sd=null;function zP(){Bp&&(Bp.unmount(),Bp=null),sd&&(sd.remove(),sd=null)}const BP="updateCheck",rd=1,eb=1440,ete="https://github.com/settings/tokens/new?description="+encodeURIComponent("Agent2API 更新检查");function tte(e){return e?e.error?{badge:s.jsx(ze,{shape:"tag",variant:"destructive",title:e.error,children:r("无法解密")}),hint:r("重新粘贴保存一次即可自愈")}:e.filled&&e.origin==="stored"?{badge:s.jsx(ze,{shape:"tag",variant:"success",children:r("已填写")}),hint:r("已加密存储在本地,不显示具体值;粘贴新令牌可覆盖")}:e.filled?{badge:s.jsx(ze,{shape:"tag",variant:"secondary",children:r("环境变量")}),hint:r("已配置 GITHUB_TOKEN 环境变量,界面保存的令牌优先于它")}:{badge:s.jsx(ze,{shape:"tag",variant:"outline",children:r("未填写")}),hint:r("填写后检查限额 60 → 5000 次/小时(创建令牌无需勾选任何权限)")}:{badge:s.jsx(ze,{shape:"tag",variant:"outline",children:r("读取中")}),hint:""}}function nte({open:e,onClose:t}){const[a,o]=x.useState(fD),[c,d]=x.useState(null),[f,p]=x.useState(""),[h,b]=x.useState(!1),[y,v]=x.useState(null),[j,w]=x.useState(""),[S,N]=x.useState(!1),[k,A]=x.useState(null),[P,R]=x.useState(!1),C=pD(a);x.useEffect(()=>{e&&(p(""),kX().then(o),(async()=>{try{d(await nn().workbuddyDesktop?.getUpdateToken()??null)}catch{}})(),(async()=>{try{const F=await nn().workbuddyDesktop?.getUpdateBeta();A({enabled:F?.enabled===!0})}catch{}})(),(async()=>{try{const $=((await nn().workbuddyDesktop?.getScheduledTasks?.())?.tasks??[]).find(q=>q.id===BP),X=$?.enabled===!0,K=Math.max(rd,Math.round(Number($?.interval)||0));v({enabled:X,interval:K}),w(String(K))}catch{}})())},[e]);async function E(F){if(!S){N(!0);try{const $=await nn().workbuddyDesktop?.saveScheduledTask?.(BP,F),X=$?.enabled===!0,K=Math.max(rd,Math.round(Number($?.interval)||0));v({enabled:X,interval:K}),w(String(K)),Es(r("✅ 自动检查更新已保存"))}catch($){Es(r("保存失败:{error}",{error:pr($)}),"err")}finally{N(!1)}}}async function D(){const F=Math.round(Number(j));if(!Number.isFinite(F)||Feb){Es(r("检查间隔必须是 {min} ~ {max} 分钟",{min:rd,max:eb}),"err");return}y&&F===y.interval||await E({interval:F})}async function I(F){if(P)return;R(!0);let $=!1;try{const X=await nn().workbuddyDesktop?.setUpdateBeta({enabled:F});A({enabled:X?.enabled===!0}),$=!0,X?.saved===!1?Es(r("通道已切换,但写入磁盘失败(重启后会回到原设置)"),"err"):Es(r(F?"✅ 已加入 beta 版,之后的检查会包含 beta 版":"✅ 已退出 beta 版,之后的检查只看正式版"))}catch(X){Es(r("保存失败:{error}",{error:pr(X)}),"err")}finally{R(!1)}if($)try{await nn().workbuddyDesktop?.checkUpdate(),await NX()}catch{}}async function O(F){const $=await CX(F);$&&o(X=>({...X,proxyChoice:$.choice}))}async function _(){const F=f.trim();if(!F){Es(r("请先粘贴 GitHub 令牌"),"err");return}b(!0);try{const $=await nn().workbuddyDesktop?.setUpdateToken({token:F});d($??null),p(""),$?.saved===!1?Es(r("令牌已生效,但写入磁盘失败(重启后会丢失)"),"err"):Es(r("✅ GitHub 令牌已保存(加密存储,界面不再显示)"))}catch($){Es(r("保存失败:{error}",{error:pr($)}),"err")}finally{b(!1)}}async function z(){b(!0);try{const F=await nn().workbuddyDesktop?.setUpdateToken({token:null});d(F??null),Es(r("✅ 已清除界面保存的 GitHub 令牌(环境变量若配置过则继续生效)"))}catch(F){Es(r("清除失败:{error}",{error:pr(F)}),"err")}finally{b(!1)}}const H=tte(c),B=!!c?.error||c?.origin==="stored",L=c?.filled&&c?.origin!=="env"?r("已填写(粘贴新令牌可覆盖)"):r("粘贴 GitHub 令牌(ghp_… / github_pat_…)");return s.jsx(Yt,{open:e,onOpenChange:F=>{F||t()},children:s.jsxs(Xt,{className:"w-[min(460px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("更新设置")})}),s.jsxs(Lt,{className:"gap-3.5",children:[s.jsxs("div",{children:[s.jsxs("div",{className:"flex items-center justify-between gap-2",children:[s.jsx("div",{className:"text-[13px] font-semibold text-foreground",children:r("自动检查更新")}),s.jsx(ns,{checked:y?.enabled===!0,disabled:S||!y,onCheckedChange:F=>{E({enabled:F===!0})},"aria-label":r("自动检查更新")})]}),s.jsxs("div",{className:"mt-1.5 flex items-center gap-2",children:[s.jsx(Fe,{type:"number",min:rd,max:eb,step:1,className:"w-[110px]",disabled:S||!y||y.enabled!==!0,value:j,onChange:F=>w(F.currentTarget.value),onKeyDown:F=>{F.key==="Enter"&&(F.preventDefault(),D())},"aria-label":r("检查间隔(分钟)")}),s.jsx("span",{className:"text-[12px] text-subtle",children:r("分钟({min} ~ {max})",{min:rd,max:eb})}),s.jsx(ne,{variant:"outline",size:"sm",disabled:S||!y||Math.round(Number(j))===y.interval,onClick:()=>{D()},children:r("保存")})]}),s.jsx("div",{className:"mt-1.5 text-[12px] text-subtle",children:r("到点自动向 GitHub 查询新版本,查到就走「检测到更新」弹窗;关闭后「检查更新」按钮仍可手动触发")})]}),s.jsx("div",{className:"h-px bg-hairline"}),s.jsxs("div",{children:[s.jsxs("div",{className:"flex items-center justify-between gap-2",children:[s.jsx("div",{className:"text-[13px] font-semibold text-foreground",children:r("加入 beta 版")}),s.jsx(ns,{checked:k?.enabled===!0,disabled:P||!k,onCheckedChange:F=>{I(F===!0)},"aria-label":r("加入 beta 版")})]}),s.jsx("div",{className:"mt-1.5 text-[12px] text-subtle",children:r("开启后只更新到 beta 版(预发布),取版本最高的一个;beta 版可能不稳定。开启期间不会因为正式版发布而自动切回正式版 —— 想切回请关闭本开关。关闭后只更新到正式版,且不会降级到版本号更低的版本。")})]}),s.jsx("div",{className:"h-px bg-hairline"}),s.jsxs("div",{children:[s.jsx("div",{className:"text-[13px] font-semibold text-foreground",children:r("出网代理")}),s.jsx("div",{className:"mt-1.5",children:s.jsxs(ut,{value:C.current,onValueChange:F=>{O(String(F))},children:[s.jsx(ft,{className:Ue("w-full",C.broken&&"border-destructive-bd"),title:C.title,"aria-label":r("更新出网代理"),children:s.jsx(dt,{className:"min-w-0 truncate",children:C.selected?.label||r("直连")})}),s.jsx(pt,{children:C.items.map(F=>s.jsx($e,{value:F.value,disabled:F.disabled,children:F.label},F.value))})]})})]}),s.jsx("div",{className:"h-px bg-hairline"}),s.jsxs("div",{children:[s.jsxs("div",{className:"flex items-center justify-between gap-2",children:[s.jsx("div",{className:"text-[13px] font-semibold text-foreground",children:r("GitHub 令牌")}),s.jsx(ne,{variant:"outline",size:"xs",title:r("在默认浏览器中打开 GitHub 的令牌创建页(备注已预设,无需勾选任何权限,直接点 Generate token)"),onClick:()=>{uD(ete)},children:r("打开 GitHub 令牌页面")})]}),s.jsxs("div",{className:"mt-1.5 flex items-center gap-2",children:[H.badge,s.jsx("span",{className:"text-[12px] text-subtle",children:H.hint})]}),s.jsxs("div",{className:"mt-2 flex items-center gap-2",children:[s.jsx(Fe,{type:"password",className:"flex-1",placeholder:L,autoComplete:"new-password",spellCheck:!1,value:f,onChange:F=>p(F.currentTarget.value),onKeyDown:F=>{F.key==="Enter"&&(F.preventDefault(),_())}}),s.jsx(ne,{variant:"default",size:"sm",disabled:h||!f.trim(),onClick:()=>{_()},children:r("保存")}),B?s.jsx(ne,{variant:"outline",size:"sm",disabled:h,onClick:()=>{z()},children:r("清除")}):null]})]})]})]})})}function ste({open:e,onClose:t}){return s.jsx(Yt,{open:e,onOpenChange:a=>{a||t()},children:s.jsxs(Xt,{className:"w-[min(320px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("加入 QQ 群")})}),s.jsxs(Lt,{className:"items-center gap-3 text-center",children:[s.jsx("img",{className:"qq-qr",src:SX,alt:r("QQ 群二维码")}),s.jsxs("div",{className:"qq-no",children:[s.jsx("span",{className:"k",children:r("群号")}),s.jsx("code",{children:lx}),s.jsx(ne,{type:"button",size:"sm",variant:"ghost","data-copy":lx,children:r("复制")})]}),s.jsx("p",{className:"qq-hint",children:r("用手机 QQ 扫上面的二维码,或在 QQ 里搜索群号加入。")})]})]})})}const rte=1e3,HP=8,ate=10,lte=1e3,ote={"":"outline",ok:"success",warn:"warning",bad:"destructive"},ite={info:null,checkedAt:0,repository:"",badgeText:r("未检查"),badgeTone:"",stateText:r("点击「检查更新」查询最新发布版本。"),stateError:!1,percent:0,phase:"idle",readyPath:"",checking:!1,actionBusy:!1,restartSeconds:0,tokenFilled:null};function zl(e){return String(e).padStart(2,"0")}function cte(e){const t=new Date(String(e??""));return!e||Number.isNaN(t.getTime())?"":`${t.getFullYear()}-${zl(t.getMonth()+1)}-${zl(t.getDate())} ${zl(t.getHours())}:${zl(t.getMinutes())}`}function UP(e){const t=new Date(e);if(!e||Number.isNaN(t.getTime()))return"";const a=new Date,o=t.getFullYear()===a.getFullYear()&&t.getMonth()===a.getMonth()&&t.getDate()===a.getDate(),c=`${zl(t.getHours())}:${zl(t.getMinutes())}:${zl(t.getSeconds())}`;return o?c:`${zl(t.getMonth()+1)}-${zl(t.getDate())} ${c}`}function Hp(e){const t=e?.installerKind;if(t==="dmg"||t==="deb"||t==="appimage"||t==="nsis")return t;const a=navigator.userAgent||"";return/Mac|iPhone|iPad/.test(a)?"dmg":/Linux/.test(a)?"deb":"nsis"}function Ia(e){return e?.selfUpdate===!0}function VP(e){return e?Ia(e)?e.serverAsset??null:e.asset??null:null}function ute(e){if(Ia(e))return r("正在下载服务端更新包…(下载完成后由面板完成替换,重启容器内的服务后生效)");switch(Hp(e)){case"dmg":return r("正在下载安装包…(下载完成后会挂载磁盘映像,把应用拖进「应用程序」即可完成安装)");case"deb":return r("正在下载安装包…(下载完成后会交给系统安装器,按提示输入密码即可完成安装)");case"appimage":return r("正在下载安装包…(下载完成后会启动新的 AppImage;把它替换掉旧文件即完成升级)");default:return r("正在下载安装包…(下载完成后启动安装程序需要管理员权限,会弹出 UAC 确认框)")}}function FP(e,t){if(Ia(e))return r("更新包已就绪:{name}。点「安装并重启」后,面板会校验并替换容器内的程序与界面文件,然后重启服务(页面会自动刷新)。",{name:t});switch(Hp(e)){case"dmg":return r("安装包已就绪:{name}。点击「打开安装包」后会挂载磁盘映像,把应用拖进「应用程序」即可完成安装。",{name:t});case"deb":return r("安装包已就绪:{name}。点击「打开安装包」后会交给系统安装器,按提示输入密码即可完成安装(本程序无需退出)。",{name:t});case"appimage":return r("安装包已就绪:{name}。点击「安装并重启」后会启动新的 AppImage;把它移动到你的应用目录并替换旧文件即完成升级(本程序会先退出以释放文件占用)。",{name:t});default:return r("安装包已就绪:{name}。点击「安装并重启」后需要管理员权限,会弹出 UAC 确认框。",{name:t})}}function dte(e){if(Ia(e))return r("安装并重启");const t=Hp(e);return r(t==="dmg"||t==="deb"?"打开安装包":"安装并重启")}let Vr=ite;const FS=new Set;function $S(){return Vr}function fte(e){return FS.add(e),()=>{FS.delete(e)}}function zn(e){Vr={...Vr,...e};for(const t of FS)t()}async function qS(){try{const e=await nn().workbuddyDesktop?.getUpdateToken();zn({tokenFilled:e?.filled===!0})}catch{}}let Cr=!1,GS=null,tb=null;function ad(){tb!==null&&(window.clearInterval(tb),tb=null)}function Rs(e,t=""){zn({badgeText:e,badgeTone:t})}function Mn(e,t=!1){zn({stateText:e,stateError:t})}function nb(){const{info:e,checkedAt:t}=Vr;if(!e)return;const a=t?r("(检查于 {time})",{time:UP(t)}):"";if(e.pendingRestart===!0){Rs(r("待重启"),"warn"),Mn(r("更新已应用到容器内,重启服务后生效。{at}",{at:a}));return}if(e.hasUpdate===!0){if(Rs(r("有新版本"),"warn"),Ia(e)&&!e.serverAsset){Mn(r("发现新版本 {latest}(当前 {current}),但该版本没有提供容器内更新包;请在宿主机执行 docker compose pull && docker compose up -d 升级。{at}",{latest:String(e.latestVersion||""),current:String(e.currentVersion||""),at:a}));return}Mn(r("发现新版本 {latest}(当前 {current})。{at}",{latest:String(e.latestVersion||""),current:String(e.currentVersion||""),at:a}))}else e.hasUpdate===!1?(Rs(r("已是最新"),"ok"),Mn(r("当前已是最新版本({current})。{at}",{current:String(e.currentVersion||""),at:a}))):(Rs(r("无法比较"),"warn"),Mn(e.latestVersion?r("最新发布版本为 {latest},但当前版本号「{current}」无法解析,未做新旧判断。{at}",{latest:e.latestVersion,current:e.currentVersion||r("未知"),at:a}):r("仓库暂无发布版本。{at}",{at:a})))}function $P(e){!e||e.checked===!1||(zn({info:e,checkedAt:Number(e.checkedAt)||0}),e.repository&&KS(e.repository),Vr.phase==="idle"&&nb())}function KS(e){const t=String(e||"").trim();/^[\w.-]+\/[\w.-]+$/.test(t)&&zn({repository:t})}function YS(e){if(!e)return!1;if(e.active){const t=Math.max(0,Math.min(100,Number(e.percent)||0)),a=(Number(e.received)||0)/1024/1024,o=(Number(e.total)||0)/1024/1024;return zn({phase:"downloading",percent:t}),Rs(r("下载中"),"warn"),Mn(r("正在下载 {name}:{percent}%({received} / {total} MB)",{name:e.filename||r("安装包"),percent:t,received:a.toFixed(1),total:o.toFixed(1)})),!0}if(ad(),zn({percent:0}),e.error)return Rs(r("下载失败"),"bad"),Mn(r("下载失败:{error}",{error:e.error}),!0),zn({phase:"failed"}),!0;if(e.canceled)return Rs(r("已取消"),"warn"),Mn(Ia(Vr.info)?r("下载已取消,可重新点击「下载并更新」。"):r("下载已取消,可重新点击「下载并安装」。")),zn({phase:"idle"}),!0;if(e.done&&e.path){const t=e.path,a=e.filename||t;if(Rs(r("可安装"),"ok"),zn({phase:"ready",readyPath:t}),Ia(Vr.info))return Mn(FP(Vr.info,a)),!0;if(t!==GS){GS=t;const o=Hp(Vr.info);Mn(r(o==="dmg"?"安装包已下载完成,正在挂载磁盘映像…":o==="deb"?"安装包已下载完成,正在打开系统安装器…":o==="appimage"?"安装包已下载完成,正在启动新的 AppImage…":"安装包已下载完成,正在启动安装程序(需要管理员权限,会弹出 UAC 确认框)…")),GP(t)}else Mn(FP(Vr.info,a));return!0}return!1}async function pte(){try{const e=await nn().workbuddyDesktop?.updateProgress();if(!e){ad();return}YS(e)}catch(e){ad(),Mn(r("读取下载进度失败:{error}",{error:pr(e)}),!0)}}function qP(){ad(),tb=window.setInterval(()=>{pte()},rte)}async function GP(e){const t=nn().workbuddyDesktop;if(t)try{const o=(await t.runInstaller(e,!0))?.restart!==!1,c=Hp(Vr.info)==="deb"?r("已打开系统安装器,请在它弹出的窗口里完成安装。安装完成后重新打开本程序即可。"):r("安装包已挂载,请在弹出的窗口里把应用拖进「应用程序」完成安装。安装完成后重新打开本程序即可。");Mn(o?r("安装程序已启动,本程序将退出以便完成覆盖安装。安装程序需要管理员权限,会弹出 UAC 确认框,请选择「是」。"):c)}catch(a){Rs(r("启动失败"),"bad"),Mn(r("启动安装程序失败:{error}",{error:pr(a)}),!0)}}async function mte(){const e=nn().workbuddyDesktop;if(e?.installUpdate)try{Rs(r("安装中"),"warn"),Mn(r("正在校验并替换容器内的程序与界面文件…"));const t=await e.installUpdate(),a=String(t?.version||"").trim();Mn(a?r("已替换为 {version},正在重启服务…",{version:a}):r("替换完成,正在重启服务…")),await e.restartService?.().catch(()=>{}),XS(a)}catch(t){Rs(r("安装失败"),"bad"),Mn(r("安装失败:{error}",{error:pr(t)}),!0)}}async function hte(){const e=nn().workbuddyDesktop;if(!(!e?.rollbackUpdate||Cr)){Cr=!0,zn({actionBusy:!0});try{Rs(r("回滚中"),"warn"),Mn(r("正在回滚到上一版本…")),await e.rollbackUpdate(),Mn(r("已还原上一版本,正在重启服务…")),await e.restartService?.().catch(()=>{}),XS("")}catch(t){Rs(r("回滚失败"),"bad"),Mn(r("回滚失败:{error}",{error:pr(t)}),!0)}finally{Cr=!1,zn({actionBusy:!1})}}}function XS(e){ad(),zn({phase:"restarting",restartSeconds:HP,readyPath:""}),Rs(r("重启中"),"warn");let t=HP;const a=()=>{if(Mn(e?r("{version} 已就位,服务正在重启,页面会在恢复后自动刷新…({seconds}s)",{version:e,seconds:Math.max(0,t)}):r("服务正在重启,页面会在恢复后自动刷新…({seconds}s)",{seconds:Math.max(0,t)})),t<=0){window.clearInterval(o),gte();return}t-=1,zn({restartSeconds:t})},o=window.setInterval(a,1e3);a()}async function gte(){for(let e=0;e{window.setTimeout(t,lte)})}window.location.reload()}async function KP(){if(Cr)return null;Cr=!0,zn({checking:!0}),Rs(r("检查中"),"warn"),Mn(r("正在查询 GitHub 上的最新发布版本…"));let e=null;try{const t=nn().workbuddyDesktop;if(!t)throw new Error(r("后端桥不可用"));e=await t.checkUpdate()??null,zn({info:e,checkedAt:Number(e?.checkedAt)||Date.now()}),e?.repository&&KS(e.repository),nb()}catch(t){e=null,zn({info:null,checkedAt:0}),Rs(r("检查失败"),"bad"),Mn(r("检查更新失败:{error}",{error:pr(t)}),!0)}finally{Cr=!1,zn({checking:!1})}return nn().wbApp?.updateUpdateBadge?.(e),Jx(e,Up),e}async function YP(){let e=null;try{e=await nn().workbuddyDesktop?.getUpdateStatus()??null}catch{}if(e&&e.checked!==!1){$P(e),nn().wbApp?.updateUpdateBadge?.(e),Jx(e,Up);return}Vr.info||(Rs(r("未检查")),Mn(r("点击「检查更新」查询 GitHub 上的最新发布版本。")))}async function QS(){if(Cr)return;const e=$S();if(e.info?.pendingRestart===!0&&e.phase!=="downloading"){Cr=!0,zn({actionBusy:!0});try{await nn().workbuddyDesktop?.restartService?.().catch(()=>{}),XS("")}finally{Cr=!1,zn({actionBusy:!1})}return}if(e.phase!=="downloading"&&e.readyPath){Cr=!0,zn({actionBusy:!0});try{Ia(e.info)?await mte():await GP(e.readyPath)}finally{Cr=!1,zn({actionBusy:!1})}return}if(e.phase==="downloading"){try{await nn().workbuddyDesktop?.cancelUpdate(),Mn(r("正在取消下载…"))}catch(a){Es(r("取消失败:{error}",{error:pr(a)}),"err")}return}const t=VP(e.info);if(!t?.url){Es(Ia(e.info)?r("这一版没有提供容器内更新包"):r("没有可下载的安装包"),"err");return}if(QP(e)){Cr=!0,zn({actionBusy:!0});try{await nn().workbuddyDesktop?.downloadUpdate({url:t.url,name:t.name}),GS=null,zn({phase:"downloading",readyPath:"",percent:0}),Rs(r("下载中"),"warn"),Mn(ute(e.info)),qP()}catch(a){Rs(r("下载失败"),"bad"),Mn(r("下载失败:{error}",{error:pr(a)}),!0)}finally{Cr=!1,zn({actionBusy:!1})}}}async function XP(){nb(),qS(),await YP();try{const e=await nn().workbuddyDesktop?.updateProgress();if(e?.active){YS(e),qP();return}if(e?.done&&e.path){YS(e);return}}catch{}}async function Up(e){if(e?.pendingRestart===!0){await QS();return}e?.hasUpdate===!0&&(zn({info:e,checkedAt:Number(e.checkedAt)||Vr.checkedAt}),e.repository&&KS(e.repository),nb(),nn().wbApp?.updateUpdateBadge?.(e),Jx(e,Up)),await XP();const t=$S();if(!(t.phase==="downloading"||t.readyPath)&&t.info?.hasUpdate)for(let a=0;a<3;a+=1){if(Cr){await new Promise(o=>{window.setTimeout(o,400)});continue}await QS();return}}function QP(e){return e.phase==="restarting"?null:e.info?.pendingRestart===!0&&e.phase==="idle"?{label:r("立即重启"),disabled:e.actionBusy}:!(e.info?.hasUpdate===!0&&!!VP(e.info)?.url)&&e.phase!=="downloading"&&e.phase!=="ready"?null:{label:e.phase==="downloading"?r("取消下载"):e.phase==="failed"?r("重试下载"):e.phase==="ready"?dte(e.info):Ia(e.info)?r("下载并更新"):r("下载并安装"),disabled:e.actionBusy}}function xte(e,t){const a=e.info,o=String(a?.notes||"").trim();if(!o)return s.jsx("div",{className:"update-log-hint",children:r(a?"这个版本没有填写发布说明。":"点击「检查更新」后,这里会显示最新版本的更新说明。")});const c=dD(o),d=cD(a?.pageUrl),f=cte(a?.publishedAt);return s.jsxs("div",{className:"rel-note",children:[s.jsxs("div",{className:"rel-note-head",children:[s.jsx("span",{className:"rel-tag",children:String(a?.latestVersion||r("最新版本"))}),a?.prerelease===!0?s.jsx(ze,{shape:"tag",variant:"warning",children:r("预发布")}):null,f?s.jsx("span",{className:"rel-date",children:f}):null]}),s.jsxs("div",{className:"rel-note-body",children:[c?s.jsx("div",{className:"md-body",dangerouslySetInnerHTML:{__html:c}}):s.jsx("div",{className:"md-body",children:s.jsx("p",{className:"md-body-empty",children:r("这个版本没有填写发布说明。")})}),s.jsxs("div",{className:"rel-foot",children:[d?s.jsx("a",{href:d,"data-external":d,target:"_blank",rel:"noopener",children:r("在 GitHub 查看完整说明")}):null,s.jsx(ne,{type:"button",variant:"link",size:"sm",className:"h-auto border-0 px-0 text-[11.5px] font-semibold text-primary-fg underline-offset-auto",title:r("群号:{group}",{group:lx}),onClick:t,children:r("加入 QQ 群")})]})]})]})}function bte(){const e=x.useSyncExternalStore(fte,$S),t=QP(e),[a,o]=x.useState(!1),[c,d]=x.useState(!1);x.useEffect(()=>()=>{ad()},[]),x.useEffect(()=>{qS()},[]);const f=e.repository?`https://github.com/${e.repository}`:"",p=e.repository?`https://github.com/${e.repository.split("/")[0]}`:"",h=Ia(e.info),b=h&&e.info?.backupAvailable===!0&&e.phase!=="downloading"&&e.phase!=="ready"&&e.phase!=="restarting",y=e.info?.latestVersion?e.info.prerelease?r("{version}(预发布)",{version:e.info.latestVersion}):e.info.latestVersion:"—",v=String(e.info?.latestVersion||"").trim(),j=v?`${v}${e.checkedAt?r(" · 检查于 {time}",{time:UP(e.checkedAt)}):""}`:"";return s.jsxs("section",{className:"panel",onClick:Xu,children:[s.jsxs("div",{className:"panel-head",children:[s.jsx("h2",{children:r("软件更新")}),s.jsx("span",{className:"tip-q","data-tip":r(h?"容器内更新:更新包从 GitHub 发布页下载,校验 sha256 后由面板替换容器内的程序与界面文件,再重启服务(容器由 restart 策略拉起,页面会自动刷新)。更新的是容器里的文件,compose 里的镜像标签不变 —— 之后执行 docker compose pull && up -d 会回到镜像里的版本;更新失败可一键回滚到上一版本。检查更新与下载的出网线路、GitHub 令牌都在「更新设置」里。":"更新包从 GitHub 发布页下载,下载完成后可直接启动安装程序并自动重启本程序。安装包会先校验来源域名与文件完整性。检查更新与下载安装包的出网线路、GitHub 令牌都在「更新设置」里:线路默认直连(直连失败时自动借 Clash Verge 的混合端口重试一次),选定指定出口后只走该出口;填写 GitHub 令牌可把检查限额从 60 次/小时提高到 5000 次/小时。")}),s.jsx(ze,{id:"update-badge",variant:ote[e.badgeTone],"data-tone":e.badgeTone,children:e.badgeText}),s.jsxs("div",{className:"head-actions",children:[e.tokenFilled===!1?s.jsx("span",{className:"text-[12px] font-semibold text-warning",title:r("填写后检查限额 60 → 5000 次/小时(创建令牌无需勾选任何权限)"),children:r("GitHub Token 未填写")}):null,s.jsx(ne,{id:"btn-update-settings",variant:"outline",title:r("出网线路与 GitHub 令牌设置"),onClick:()=>o(!0),children:r("更新设置")}),s.jsx(ne,{id:"btn-update-check",variant:"outline",disabled:e.checking||e.phase==="restarting",onClick:()=>{KP()},children:e.checking?r("检查中…"):r("检查更新")}),b?s.jsx(ne,{id:"btn-update-rollback",variant:"outline",disabled:e.actionBusy,title:r("把容器内的程序与界面文件还原到更新前的版本,并重启服务"),onClick:()=>{hte()},children:r("回滚到上一版本")}):null,t?s.jsx(ne,{id:"btn-update-download",variant:"default",disabled:t.disabled,onClick:()=>{QS()},children:t.label}):null]})]}),s.jsxs("div",{className:"panel-body",children:[s.jsxs("div",{className:"update-version",children:[s.jsxs("span",{className:"version-pill",children:[s.jsx("span",{className:"k",children:r("当前版本")}),s.jsx("span",{className:"v",id:"update-current",children:e.info?.currentVersion||"—"})]}),s.jsx("span",{className:"version-arrow",children:"→"}),s.jsxs("span",{className:"version-pill new",children:[s.jsx("span",{className:"k",children:r("最新版本")}),s.jsx("span",{className:"v",id:"update-latest",children:y})]})]}),s.jsx("div",{className:"detail",id:"update-state",style:e.stateError?{color:"var(--danger)"}:void 0,children:e.stateText}),e.phase==="downloading"&&e.percent>0&&e.percent<100?s.jsx(cf,{value:e.percent}):null,p&&f?s.jsxs("div",{className:"update-author",id:"update-author",children:[s.jsxs("div",{className:"update-author-text",children:[s.jsx("strong",{children:r("关于作者")}),s.jsxs("span",{children:[r("本工具由")," ",s.jsx("a",{id:"update-author-link",href:p,"data-external":p,target:"_blank",rel:"noopener",children:e.repository.split("/")[0]})," ",r("个人开发并开源维护,免费使用,也不会收集你的账号数据。如果它帮到了你,欢迎到项目仓库点个 Star 支持一下。")]})]}),s.jsx("div",{className:"update-author-actions",children:s.jsx(ne,{id:"btn-update-favorite",type:"button",size:"sm",variant:"outline","data-external":f,title:r("在浏览器中打开项目仓库,点 Star 支持作者"),children:r("收藏项目")})})]}):null,s.jsxs("div",{className:"update-log",id:"update-notes",children:[s.jsxs("div",{className:"update-log-head",children:[s.jsx("span",{className:"update-log-title",children:r("更新日志")}),s.jsx("span",{className:"update-log-meta",id:"update-log-meta",children:j})]}),s.jsx("div",{className:"update-log-body",id:"update-log-body",children:xte(e,()=>d(!0))})]})]}),a?s.jsx(nte,{open:!0,onClose:()=>{o(!1),qS()}}):null,c?s.jsx(ste,{open:!0,onClose:()=>d(!1)}):null]})}const ZP='.settings-pane[data-cat="about"]';let ZS=null;function WP(){if(ZS)return;const e=document.querySelector(ZP);e&&(e.replaceChildren(),ZS=wn.createRoot(e),ZS.render(s.jsx(bte,{})))}document.querySelector(ZP)?WP():document.addEventListener("DOMContentLoaded",WP,{once:!0}),window.wbUpdatePanel={load:XP,check:KP,syncFromCache:YP,openAndDownload:Up,applyStatus:$P,showUpdateModal:e=>Jx(e,Up)};const vte="zcode.z.ai/billing",yte={active:"success",pending:"warning",expired:"ghost",unknown:"outline"};function jte(e){const t=String(e||"").trim().toLowerCase();return t==="daily"?r("每日"):t==="weekly"?r("每周"):t==="monthly"?r("每月"):""}function wte(e){const t=Number(e);if(!Number.isFinite(t)||t<=0)return"";const a=window.wbUnits;return a?.formatTokens?a.formatTokens(t):String(t)}function mc(e){const t=Number(e);return!Number.isFinite(t)||t<=0?"":Pe().wbApp?.formatTime?.(t*1e3)||new Date(t*1e3).toLocaleString()}function Ste(e){const t=wte(e.grantUnits),a=String(e.unitType||"").trim(),o=t?[t,a].filter(Boolean).join(" "):"",c=jte(e.period),d=[String(e.showName||"").trim(),o].filter(Boolean).join(" ");return d?c?r("{body}({period})",{body:d,period:c}):d:""}function JP(e){return(e.entitlements||[]).map(Ste).filter(Boolean).join(r("、"))}function Nte(e){const t=mc(e.startsAt),a=mc(e.endsAt);return t&&a?`${t} → ${a}`:a?r("到期 {time}",{time:a}):t?r("开始 {time}",{time:t}):""}function kte(e){const t=new Date(e*1e3);if(Number.isNaN(t.getTime()))return"";const a=`${String(t.getHours()).padStart(2,"0")}:${String(t.getMinutes()).padStart(2,"0")}`,o=d=>Math.floor(Date.UTC(d.getFullYear(),d.getMonth(),d.getDate())/864e5),c=o(t)-o(new Date);return c===0?r("今天 {time}",{time:a}):c===1?r("明天 {time}",{time:a}):mc(e)}function Cte(e){const t=[],a=Number(e.pendingUntil);if(Number.isFinite(a)&&a>0){const c=kte(a);c&&t.push(r("待生效 {time}",{time:c}))}const o=mc(e.endsAt);if(o&&t.push(r("到期 {time}",{time:o})),t.length===0){const c=mc(e.startsAt);c&&t.push(r("开始 {time}",{time:c}))}return t.join(" · ")}function Tte({options:e,onClose:t}){const a=e.id,[,o]=x.useState(0);x.useEffect(()=>sR(()=>o(I=>I+1)),[]);const[c,d]=x.useState([]),[f,p]=x.useState({status:"loading"}),[h,b]=x.useState(""),y=Aa(a),v=String(y?.name||e.name||a),j=eg(y),w=[...new Set([...j,...c])],S=y?.claimPlans&&typeof y.claimPlans=="object"&&!Array.isArray(y.claimPlans)?y.claimPlans:{},N=y?Wa(y):void 0,k=N&&typeof N=="object"&&!Array.isArray(N)?N:null,A=k&&Array.isArray(k.plans)?k.plans:[],P=w.filter(I=>I&&!A.some(O=>String(O.planId||"")===I)),R=Gs().usageInflight.has(a),C=Pe().wbZcodeClaim,E=x.useCallback(async()=>{if(!C?.preview){p({status:"error",message:r("当前环境不支持领取(桥接方法缺失)")});return}p({status:"loading"});try{const I=await C.preview(a);p({status:"ready",result:I||{}})}catch(I){p({status:"error",message:mt(I)})}},[C,a]);x.useEffect(()=>{E();const I=Aa(a);I&&Wa(I)===void 0&&ig(a)},[a,E]);async function D(I){if(!(!C?.claim||h)){b(I);try{const O=await C.claim(a,I);if(O?.ok||O?.failure==="already_claimed"){const _=O.planId||I;_&&d(z=>z.includes(_)?z:[...z,_]),ig(a),Pe().wbApp?.refresh?.(),E()}}finally{b("")}}}return s.jsx(Yt,{open:!0,onOpenChange:(I,O)=>{if(!I){if(h){O.cancel();return}t()}},children:s.jsxs(Xt,{className:"w-[min(560px,calc(100vw-48px))]",children:[s.jsx(Vt,{children:s.jsx(Ut,{children:r("ZCode 套餐明细 — {name}",{name:v})})}),s.jsxs(Lt,{className:"space-y-4",children:[s.jsxs("section",{children:[s.jsxs("div",{className:"mb-1.5 flex items-center gap-2 text-[12px] font-semibold text-muted-foreground",children:[r("可领取的套餐"),s.jsx(ze,{variant:"outline",shape:"tag",children:r("官方限时活动")})]}),f.status==="loading"?s.jsxs("div",{className:"flex items-center gap-2 py-2 text-[12.5px] text-muted-foreground",children:[s.jsx(of,{className:"size-3"}),r("正在探测可领取的套餐…")]}):f.status==="error"?s.jsxs("div",{className:"py-1 text-[12.5px] text-destructive",children:[r("探测失败:{message}",{message:f.message}),s.jsx(ne,{variant:"outline",size:"xs",className:"ml-2",onClick:()=>{E()},children:r("重试")})]}):f.result.deployed?(f.result.plans||[]).length===0?s.jsx("p",{className:"py-1 text-[12.5px] text-muted-foreground",children:r("当前没有可领取的套餐(活动期内每天发一份,明天可再领)")}):s.jsx("ul",{className:"m-0 list-none p-0",children:(f.result.plans||[]).map(I=>{const O=String(I.planId||""),_=w.includes(O),z=JP(I),H=Nte(I);return s.jsxs("li",{className:"flex items-start justify-between gap-3 border-b border-hairline py-2 last:border-b-0",children:[s.jsxs("div",{className:"min-w-0",children:[s.jsxs("div",{className:"flex items-center gap-2 text-[13px] font-medium",children:[s.jsx("span",{className:"truncate",children:I.name||O||r("套餐")}),_?s.jsx(ze,{variant:"success",shape:"tag",children:r("今日已领")}):null]}),H?s.jsx("div",{className:"text-[12px] text-muted-foreground",children:H}):null,z?s.jsx("div",{className:"text-[12px] text-muted-foreground",children:z}):null,I.description?s.jsx("div",{className:"text-[12px] text-muted-foreground",children:I.description}):null]}),s.jsx(ne,{variant:"outline",size:"xs",className:"flex-none",title:_?r("这一份今天已经领过了(重复领取上游会回「已领取过」)"):void 0,disabled:h!=="",onClick:()=>{D(O)},children:r(h===O?"领取中…":_?"再领一次":"领取")})]},O||I.name)})}):s.jsx("p",{className:"py-1 text-[12.5px] text-muted-foreground",children:r("当前没有可领取的套餐(活动尚未开始)")}),s.jsx("p",{className:"mt-1.5 text-[11.5px] text-muted-foreground",children:r("活动期内每天发一份新套餐;同一份当天重复领取会提示「已领取过」,次日可再领。领取要走一次人机验证(滑块)。")}),f.status==="ready"&&f.result.activationError?s.jsx("p",{className:"text-[11.5px] text-muted-foreground",children:r("(激活事件补报失败,可能影响领取资格:{message})",{message:f.result.activationError})}):null]}),s.jsxs("section",{children:[s.jsxs("div",{className:"mb-1.5 flex items-center gap-2 text-[12px] font-semibold text-muted-foreground",children:[r("我的套餐"),Array.isArray(k?.wallets)&&k.wallets.length>0&&k.availableView?s.jsx(ze,{variant:"outline",shape:"tag",children:String(k.walletsFrom||"")==="monitor"?String(k.availableView):r("可用 {value}",{value:String(k.availableView)})}):null,s.jsx("div",{className:"ml-auto",children:s.jsx(ne,{variant:"outline",size:"xs",disabled:R,title:r("重新读取该账号的余额与套餐(走余额查询那条链)"),onClick:()=>{ig(a)},children:r(R?"查询中…":"查询余额")})})]}),A.length>0?s.jsx("ul",{className:"m-0 list-none p-0",children:A.map(I=>{const O=String(I.planId||""),_=String(I.state||"unknown"),z=JP(I),H=Cte(I),B=O&&y?.claimPlans&&Number(y.claimPlans[O])||0;return s.jsxs("li",{className:"flex items-start justify-between gap-3 border-b border-hairline py-2 last:border-b-0",children:[s.jsxs("div",{className:"min-w-0",children:[s.jsxs("div",{className:"flex items-center gap-2 text-[13px] font-medium",children:[s.jsx("span",{className:"truncate",children:I.name||O||r("套餐")}),s.jsx(ze,{variant:yte[_]||"outline",shape:"tag",children:qA(_)})]}),H?s.jsx("div",{className:"text-[12px] text-muted-foreground",children:H}):null,z?s.jsx("div",{className:"text-[12px] text-muted-foreground",children:z}):null]}),B>0?s.jsx("span",{className:"flex-none text-[11.5px] text-muted-foreground",children:r("本地领取于 {time}",{time:mc(B/1e3)})}):null]},O||I.name)})}):s.jsx("p",{className:"py-1 text-[12.5px] text-muted-foreground",children:N===void 0?r("还没有余额读数(已自动查询一次,稍候;也可以点右上角「查询余额」重读)"):k&&k.source&&k.source!==vte?r("这个账号的余额走窗口限额通道(只配了编码套餐 API Key):只有每 N 小时的剩余比例,读不到套餐清单"):r("读数里没有套餐清单(免费账号、或套餐的上游状态还没刷新;可点右上角「查询余额」重读)")}),P.length>0?s.jsx("p",{className:"mt-1.5 text-[11.5px] text-muted-foreground",children:r("本地台账里今天还领过这些,但上面的读数里还没有:{plans}(读数刷新可能有延迟)",{plans:P.map(I=>{const O=Number(S[I])||0,_=O>0?mc(O/1e3):"";return _?r("{plan}({time} 领取)",{plan:I,time:_}):I}).join(r("、"))})}):null]})]}),s.jsxs(tn,{children:[s.jsx("span",{className:"min-w-0 text-[11.5px] text-muted-foreground",children:w.length>0?r("今天(北京时间)已领取 {n} 份:{plans}",{n:w.length,plans:w.join(r("、"))}):r("今天还没有领过:上面「可领取的套餐」里逐份点「领取」即可")}),s.jsx("div",{className:"mr-auto"}),s.jsx(ne,{variant:"outline",onClick:t,children:r("关闭")})]})]})})}let Vp=null,ld=null;function e_(){Vp&&(Vp.unmount(),Vp=null),ld&&(ld.remove(),ld=null)}function Ete(e){e?.id&&(e_(),ld=document.createElement("div"),document.body.append(ld),Vp=wn.createRoot(ld),Vp.render(s.jsx(Tte,{options:e,onClose:e_})))}window.wbZcodePlans={open:Ete};const t_="w-auto flex-auto";function n_({onInput:e,domRef:t,icon:a,className:o,inputClassName:c,...d}){const f=h=>{t.current=h},p=h=>{e?.(h.currentTarget.value)};return a?s.jsxs(Di,{className:Ue(t_,o),children:[s.jsx(Pi,{ref:f,className:c,onInput:p,...d}),s.jsx(Ii,{"aria-hidden":"true",children:a})]}):s.jsx(Fe,{ref:f,className:Ue(t_,c),onInput:p,...d})}function Ate(e,t){const a=wn.createRoot(e),o={current:null};return a.render(s.jsx(n_,{domRef:o,defaultValue:t.value,placeholder:t.placeholder,type:t.type??"search",icon:t.icon,"aria-label":t.ariaLabel,onInput:t.onInput})),{setValue(c){const d=o.current;d&&d.value!==c&&(d.value=c)},getValue(){return o.current?.value??""},focus(){o.current?.focus()},unmount(){a.unmount()}}}const Rte=[["id","id"],["type","type"],["name","name"],["title","title"],["placeholder","placeholder"],["min","min"],["max","max"],["step","step"],["autocomplete","autoComplete"]];function Ote(e){const t={defaultValue:e.value||void 0,inputClassName:e.className||void 0};for(const[f,p]of Rte){const h=e.getAttribute(f);h!=null&&(t[p]=h)}const a=e.getAttribute("maxlength");a!=null&&(t.maxLength=Number(a));const o=e.getAttribute("inputmode");o!=null&&(t.inputMode=o),e.hasAttribute("spellcheck")&&(t.spellCheck=e.spellcheck),e.disabled&&(t.disabled=!0),e.readOnly&&(t.readOnly=!0),e.hidden&&(t.hidden=!0);const c=e.getAttribute("aria-label");c!=null&&(t["aria-label"]=c);for(const f of e.getAttributeNames())!f.startsWith("data-")||f.startsWith("data-island-")||(t[f]=e.getAttribute(f)??"");const d=e.getAttribute("data-island-icon");return d!=null&&(t.icon=d),t}function s_(e){if(e.dataset.islandUpgraded)return;e.dataset.islandUpgraded="1";const t=Ote(e),a=e.parentElement?.classList.contains("input-affix")?e.parentElement:null;let o;a?(a.className=a.className.replace(/\binput-affix\b/,"").trim(),a.style.display="flex",a.style.alignItems="center",o=a):(o=document.createElement("span"),o.style.display="contents",e.replaceWith(o));const c={current:null},d=wn.createRoot(o);Ts.flushSync(()=>{d.render(s.jsx(n_,{...t,domRef:c}))})}function r_(e=document){if(e instanceof HTMLInputElement&&e.matches("[data-island-input]")){s_(e);return}e.querySelectorAll("input[data-island-input]").forEach(s_)}function Mte(){r_();const e=new MutationObserver(t=>{for(const a of t)for(const o of a.addedNodes)o instanceof Element&&r_(o)});return e.observe(document.body,{childList:!0,subtree:!0}),e}function Dte(e,t){const a=wn.createRoot(e);let o={options:t.options,value:t.value,ariaLabel:t.ariaLabel,className:t.className};const c=()=>{a.render(s.jsx(En,{options:o.options,value:o.value,onValueChange:t.onChange,"aria-label":o.ariaLabel,className:o.className}))};return c(),{setValue(d){d!==o.value&&(o={...o,value:d},c())},setOptions(d){o={...o,options:d},c()},unmount(){a.unmount()}}}window.wbSegmented={mount:Dte},window.wbInput={mount:Ate},Mte()})();