ai-zero-token 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +13 -5
  3. package/README.zh-CN.md +13 -5
  4. package/admin-ui/dist/assets/InfoRow-0ULI9iI3.js +1 -0
  5. package/admin-ui/dist/assets/accounts-DymL4WIa.js +2 -0
  6. package/admin-ui/dist/assets/activity-D21-Xrc4.js +1 -0
  7. package/admin-ui/dist/assets/app-mark-nsRs4vo7.svg +8 -0
  8. package/admin-ui/dist/assets/circle-check-ZYtn9GqY.js +1 -0
  9. package/admin-ui/dist/assets/clock-3-BzDANsVk.js +1 -0
  10. package/admin-ui/dist/assets/docs-BRNWAMPw.css +1 -0
  11. package/admin-ui/dist/assets/docs-DtO-AOWU.js +1735 -0
  12. package/admin-ui/dist/assets/earth-DFdZaQIi.js +1 -0
  13. package/admin-ui/dist/assets/image-bed-yIVQ4dKs.js +1 -0
  14. package/admin-ui/dist/assets/index-By4r-wy3.css +1 -0
  15. package/admin-ui/dist/assets/index-DRe-tByu.js +10 -0
  16. package/admin-ui/dist/assets/jsx-runtime-DqpGtLhh.js +1 -0
  17. package/admin-ui/dist/assets/launch-CQXYrl-h.js +1 -0
  18. package/admin-ui/dist/assets/logs-awABDg1C.js +1 -0
  19. package/admin-ui/dist/assets/network-detect-sSrnwZqf.js +1 -0
  20. package/admin-ui/dist/assets/overview-BbSON0Jl.js +1 -0
  21. package/admin-ui/dist/assets/profiles-DMOjJORP.js +1 -0
  22. package/admin-ui/dist/assets/refresh-cw-CAAH2rqe.js +1 -0
  23. package/admin-ui/dist/assets/search-B2hz41D3.js +1 -0
  24. package/admin-ui/dist/assets/server-BrjJPb9D.js +1 -0
  25. package/admin-ui/dist/assets/settings-DvRiHS7i.js +1 -0
  26. package/admin-ui/dist/assets/tester-CftPgRE9.js +3 -0
  27. package/admin-ui/dist/assets/upload-CwXb7Q1b.js +1 -0
  28. package/admin-ui/dist/assets/zap-B4_oDbCp.js +1 -0
  29. package/admin-ui/dist/index.html +5 -3
  30. package/build/icon.icns +0 -0
  31. package/build/icon.ico +0 -0
  32. package/build/icon.png +0 -0
  33. package/build/icon.svg +8 -0
  34. package/build/mac-install-guide.txt +25 -0
  35. package/dist/core/context.js +3 -0
  36. package/dist/core/providers/http-client.js +11 -4
  37. package/dist/core/services/auth-service.js +88 -12
  38. package/dist/core/services/config-service.js +87 -23
  39. package/dist/core/services/github-image-bed-service.js +264 -0
  40. package/dist/core/services/network-detect-service.js +189 -74
  41. package/dist/core/services/version-service.js +1 -1
  42. package/dist/core/store/github-image-bed-history-store.js +68 -0
  43. package/dist/core/store/github-image-bed-store.js +52 -0
  44. package/dist/core/store/profile-store.js +73 -32
  45. package/dist/core/store/settings-store.js +14 -0
  46. package/dist/desktop/main.js +158 -6
  47. package/dist/server/app.js +168 -26
  48. package/dist/server/index.js +41 -15
  49. package/docs/DESKTOP_RELEASE.md +35 -3
  50. package/docs/PRODUCT_UPDATE_DESKTOP_TOOLBOX.md +2 -2
  51. package/package.json +34 -2
  52. package/admin-ui/dist/assets/app-mark-Gd2QnHMO.svg +0 -9
  53. package/admin-ui/dist/assets/index-DNzR8XR7.css +0 -1
  54. package/admin-ui/dist/assets/index-DZMegNPs.js +0 -1745
  55. package/dist/server/admin-page.js +0 -4586
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.3 - 2026-05-08
4
+
5
+ - Added ZIP batch account import with preflight validation for bundled account JSON files.
6
+ - Added account export audit metadata, including export count, latest export time, and export type.
7
+ - Added account-card export status badges so exported accounts are visible in the account list.
8
+ - Added selected-account batch deletion from the account management page.
9
+ - Improved global quota refresh performance with configurable concurrency.
10
+ - Added a runtime setting for quota refresh concurrency, configurable from 1 to 32.
11
+ - Restored the prominent global update banner with separate desktop and npm update paths.
12
+ - Fixed account export UI state so export status updates immediately after exporting.
13
+
14
+ ## 2.0.2 - 2026-05-07
15
+
16
+ - Added a GitHub image-bed workflow to the React desktop UI, including upload history, configuration storage, and gateway service support.
17
+ - Reworked the network diagnostics page around overseas app reachability for reverse-proxy scenarios, with clearer access verdicts, exit/proxy signals, DNS/WebRTC context, and a more productized matrix layout.
18
+ - Removed the legacy server-rendered embedded admin page; production now serves the React admin UI build only.
19
+ - Improved desktop and tester UI polish, routing, shared workspace state, icons, and request helpers.
20
+ - Hardened network detection with more resilient IPv4/IPv6 probing, DNS collection, and partial-result handling.
21
+ - Updated macOS desktop packaging so `dist:mac` builds a universal app, with explicit `dist:mac:arm64` and `dist:mac:x64` scripts for Apple Silicon and Intel-only installers.
22
+
3
23
  ## 2.0.1 - 2026-05-04
4
24
 
5
25
  - Improved desktop sidebar navigation responsiveness by switching routes immediately and de-duplicating hash-change updates.
package/README.md CHANGED
@@ -18,10 +18,10 @@ AI Zero Token provides a local CLI, web console, and HTTP gateway that expose sa
18
18
  - `POST /v1/images/edits`
19
19
  - ChatGPT/Codex OAuth login with local token refresh.
20
20
  - Multi-account management in the web console.
21
- - Account JSON import/export, including selected batch export.
21
+ - Account JSON import/export, including ZIP batch import, selected batch export, and export audit indicators.
22
22
  - Apply a saved account to local Codex by backing up and updating `~/.codex/auth.json`.
23
23
  - `gpt-image-2` image generation and JSON image editing through the ChatGPT internal Responses path.
24
- - Optional quota-exhaustion auto switch to the next saved API account with available quota.
24
+ - Optional quota-exhaustion auto switch to the next saved API account with available quota, plus configurable quota refresh concurrency.
25
25
  - Optional upstream proxy configuration for OAuth, model refresh, and gateway forwarding.
26
26
  - Local model discovery from the Codex model cache with manual refresh support.
27
27
 
@@ -84,13 +84,16 @@ http://127.0.0.1:8787/v1
84
84
  The web console is the recommended entry point:
85
85
 
86
86
  - Log in with OpenAI Codex OAuth.
87
- - Import one or more account JSON files.
87
+ - Import one or more account JSON files, or validate and import a ZIP archive that contains multiple account JSON files.
88
88
  - Switch the active account.
89
- - Export one account or selected accounts.
89
+ - Export one account or selected accounts, with export status shown on account cards.
90
+ - Delete selected accounts in batches.
90
91
  - Apply a saved account to local Codex.
91
92
  - Configure the default text model and upstream proxy.
92
93
  - Enable automatic account switching when the active API account has exhausted its recorded quota.
94
+ - Tune global quota refresh concurrency for larger account pools.
93
95
  - Test `models`, `responses`, `chat.completions`, `images.generations`, and `images.edits`.
96
+ - See a global update banner when a newer version is available, with separate GitHub desktop release and npm update paths.
94
97
 
95
98
  ![AI Zero Token admin dashboard](docs/images/admin-dashboard.jpg)
96
99
 
@@ -179,9 +182,12 @@ The web console supports:
179
182
 
180
183
  - OAuth login.
181
184
  - JSON import from a single profile, an array, or a `profiles` bundle.
185
+ - ZIP batch import for archives containing multiple account JSON files, with validation before import.
182
186
  - Single-account export.
183
187
  - Selected batch export using checkboxes.
184
- - Account deletion and active-account switching.
188
+ - Export audit status, including export count, latest export time, and export type.
189
+ - Single-account and selected batch deletion.
190
+ - Active-account switching.
185
191
 
186
192
  Exported account JSON includes authentication tokens and should be treated as a secret.
187
193
 
@@ -229,6 +235,8 @@ AI_ZERO_TOKEN_HOME=/path/to/home azt start
229
235
 
230
236
  The web console settings are persisted in the same local state directory. The quota auto-switch option is stored as `autoSwitch.enabled`; when enabled, the gateway uses the latest saved quota snapshot and moves API traffic away from the active account once that snapshot shows a quota window is exhausted.
231
237
 
238
+ The global quota refresh concurrency can be configured in the web console settings. The default is `16`; lower it if upstream rate limits increase, or raise it for larger local account pools.
239
+
232
240
  The default request body limit is `32 MiB`, which is intended to make JSON base64 image references practical for local image editing. You can override it with:
233
241
 
234
242
  ```bash
package/README.zh-CN.md CHANGED
@@ -18,10 +18,10 @@ AI Zero Token 是一个本地优先的 OpenAI 兼容网关,用于把 ChatGPT/C
18
18
  - `POST /v1/images/edits`
19
19
  - 支持 ChatGPT/Codex OAuth 登录和本地 token 自动刷新。
20
20
  - 管理页支持多账号保存、切换和删除。
21
- - 支持账号 JSON 导入/导出,以及复选框选择后的批量导出。
21
+ - 支持账号 JSON 导入/导出、ZIP 批量导入、复选框批量导出和导出记录提示。
22
22
  - 支持把已保存账号应用到本机 Codex,并自动备份 `~/.codex/auth.json`。
23
23
  - 支持 `gpt-image-2` 文生图和 JSON 图生图。
24
- - 支持 API 账号额度耗尽后自动切换到下一个仍有额度的账号。
24
+ - 支持 API 账号额度耗尽后自动切换到下一个仍有额度的账号,并可配置全局额度刷新并发数。
25
25
  - 支持上游代理配置,覆盖 OAuth、模型刷新和接口转发。
26
26
  - 模型列表优先读取本机 Codex 模型缓存,并支持手动刷新。
27
27
 
@@ -84,13 +84,16 @@ http://127.0.0.1:8787/v1
84
84
  管理页是推荐入口,可以完成:
85
85
 
86
86
  - OpenAI Codex OAuth 登录。
87
- - 导入一个或多个账号 JSON
87
+ - 导入一个或多个账号 JSON,或校验并导入包含多个账号 JSON 的 ZIP 压缩包。
88
88
  - 切换当前账号。
89
- - 导出单个账号或勾选导出多个账号。
89
+ - 导出单个账号或勾选导出多个账号,并在账号卡片上显示导出状态。
90
+ - 勾选后批量删除账号。
90
91
  - 将已保存账号应用到本机 Codex。
91
92
  - 配置默认文本模型和上游代理。
92
93
  - 开启当前 API 账号额度耗尽后的自动切换。
94
+ - 为账号池较多的场景调整全局额度刷新并发数。
93
95
  - 测试 `models`、`responses`、`chat.completions`、`images.generations`、`images.edits`。
96
+ - 当检测到新版本时显示全局顶部更新提示,分别引导桌面端下载 GitHub Release 和 npm 用户更新包。
94
97
 
95
98
  ![AI Zero Token 管理页](docs/images/admin-dashboard.jpg)
96
99
 
@@ -179,9 +182,12 @@ AI Zero Token 的账号状态默认保存在:
179
182
 
180
183
  - OAuth 登录。
181
184
  - 从单个 profile、数组或 `profiles` bundle 导入。
185
+ - 从包含多个账号 JSON 的 ZIP 压缩包批量导入,导入前会先校验。
182
186
  - 导出单个账号。
183
187
  - 使用复选框批量导出已选择账号。
184
- - 删除账号和切换当前账号。
188
+ - 显示导出记录,包括导出次数、最近导出时间和导出方式。
189
+ - 删除单个账号或批量删除已选择账号。
190
+ - 切换当前账号。
185
191
 
186
192
  导出的账号 JSON 包含认证 token,等同于登录凭据,只应在可信环境中使用。
187
193
 
@@ -229,6 +235,8 @@ AI_ZERO_TOKEN_HOME=/path/to/home azt start
229
235
 
230
236
  管理页里的配置会保存在同一个本地状态目录。额度耗尽自动切换会保存为 `autoSwitch.enabled`;开启后,网关会根据最近一次保存的额度快照判断当前 API 账号是否耗尽,并把 API 流量切到下一个仍有额度的账号。
231
237
 
238
+ 全局额度刷新并发数可以在管理页设置里调整,默认 `16`。账号很多时可以调高;遇到上游限流或失败增多时建议调低。
239
+
232
240
  默认请求体上限是 `32 MiB`,用于让 JSON base64 图片在本地图片编辑场景里更实用。可以用下面的环境变量覆盖:
233
241
 
234
242
  ```bash
@@ -0,0 +1 @@
1
+ import{t as e}from"./jsx-runtime-DqpGtLhh.js";var t=e();function n(e){return(0,t.jsxs)(`div`,{className:`service-row`,children:[(0,t.jsx)(`label`,{children:e.label}),e.code?(0,t.jsx)(`code`,{children:e.value}):(0,t.jsx)(`strong`,{children:e.value})]})}export{n as t};
@@ -0,0 +1,2 @@
1
+ import{a as e,r as t,t as n}from"./jsx-runtime-DqpGtLhh.js";import{t as r}from"./earth-DFdZaQIi.js";import{t as i}from"./refresh-cw-CAAH2rqe.js";import{t as a}from"./search-B2hz41D3.js";import{C as o,_ as s,a as c,b as l,d as u,f as d,g as f,h as p,i as m,m as h,n as g,o as _,p as v,r as y,s as b,t as x,u as S,v as C,w,y as T}from"./profiles-DMOjJORP.js";import{_ as E,d as D,p as O,r as k,x as A}from"./index-DRe-tByu.js";import{t as j}from"./InfoRow-0ULI9iI3.js";var M=e(t(),1),N=n();function P(e){let t=e.config?.codex?.accountId,n=e.profiles.length<=0?``:e.profiles.length===1?`profile-count-1`:e.profiles.length===2?`profile-count-2`:e.profiles.length===3?`profile-count-3`:`profile-count-many`;return(0,N.jsxs)(`section`,{className:`card`,id:`accounts`,children:[(0,N.jsxs)(`div`,{className:`section-head`,children:[(0,N.jsxs)(`div`,{children:[(0,N.jsx)(`h2`,{children:`账号额度预览`}),(0,N.jsx)(`p`,{children:`账号信息采用卡片式布局展示,支持搜索、状态筛选和额度排序。`})]}),(0,N.jsxs)(`div`,{className:`section-actions`,children:[(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onLocate,children:`定位当前账号`}),(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onExportSelected,children:`导出所选`}),(0,N.jsx)(`button`,{className:`btn-danger`,type:`button`,onClick:e.onRemoveSelected,disabled:e.selectedCount===0||e.busy===`bulk-remove`,children:`删除所选`}),(0,N.jsx)(`button`,{className:`btn-primary`,type:`button`,onClick:e.onAddAccount,children:`新增账号`}),(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onRefreshStatus,children:`刷新状态`}),(0,N.jsx)(`button`,{className:`btn-danger`,type:`button`,onClick:e.onClearAccounts,children:`清空账号`})]})]}),(0,N.jsxs)(`div`,{className:`filter-row`,children:[(0,N.jsxs)(`label`,{className:`search-box`,children:[(0,N.jsx)(a,{size:16}),(0,N.jsx)(`input`,{value:e.filter.search,onChange:t=>e.onFilter({...e.filter,search:t.target.value}),placeholder:`搜索邮箱、账号 ID 或 Profile ID`})]}),(0,N.jsxs)(`select`,{className:`control`,value:e.filter.status,onChange:t=>e.onFilter({...e.filter,status:t.target.value}),children:[(0,N.jsx)(`option`,{value:`all`,children:`全部状态`}),(0,N.jsx)(`option`,{value:`active`,children:`使用中`}),(0,N.jsx)(`option`,{value:`healthy`,children:`健康`}),(0,N.jsx)(`option`,{value:`warning`,children:`即将耗尽`}),(0,N.jsx)(`option`,{value:`exhausted`,children:`额度耗尽`}),(0,N.jsx)(`option`,{value:`invalid`,children:`登录失效`}),(0,N.jsx)(`option`,{value:`expired`,children:`已过期`})]}),(0,N.jsxs)(`select`,{className:`control`,value:e.filter.sort,onChange:t=>e.onFilter({...e.filter,sort:t.target.value}),children:[(0,N.jsx)(`option`,{value:`quota-desc`,children:`默认排序`}),(0,N.jsx)(`option`,{value:`latency-asc`,children:`按额度更新时间`}),(0,N.jsx)(`option`,{value:`expiry-asc`,children:`按过期时间`}),(0,N.jsx)(`option`,{value:`name-asc`,children:`按名称排序`}),(0,N.jsx)(`option`,{value:`quota-asc`,children:`按剩余额度升序`}),(0,N.jsx)(`option`,{value:`plan-desc`,children:`按套餐排序`}),(0,N.jsx)(`option`,{value:`email-asc`,children:`按邮箱排序`})]}),(0,N.jsxs)(`span`,{className:`account-selected-count`,children:[`已选择 `,e.selectedCount,` 个`]})]}),(0,N.jsx)(`div`,{className:`account-grid ${n}`,children:e.profiles.length===0?(0,N.jsx)(`div`,{className:`empty-state`,children:`还没有匹配的账号。可以新增账号或调整筛选条件。`}):e.profiles.map(n=>{let a=d(n),o=u(n),p=T(n),y=!!e.expandedProfiles[n.profileId],b=!!(t&&n.accountId===t),S=l(n,b),w=_(n),D=c(n),O=n.exportAudit,k=O?.exported?`已导出 ${O.count} 次`:`未导出`,M=typeof e.busy==`string`&&e.busy.startsWith(`profile:`)&&e.busy.endsWith(n.profileId),P=e.busy===`profile:sync-quota:${n.profileId}`;return(0,N.jsxs)(`article`,{className:`account-card plan-${g(n)} ${w?`is-auth-invalid`:``}`,"data-profile-card":n.profileId,title:w?x(n):void 0,children:[S&&(0,N.jsx)(`span`,{className:`usage-corner ${S.className}`,children:(0,N.jsx)(`span`,{children:S.label})}),(0,N.jsxs)(`div`,{className:`account-head`,children:[(0,N.jsxs)(`div`,{className:`account-title`,children:[(0,N.jsxs)(`div`,{className:`account-name`,children:[(0,N.jsx)(`span`,{className:`avatar`,children:v(n)}),(0,N.jsx)(`strong`,{children:h(n,e.showEmails)}),(0,N.jsx)(`button`,{"aria-label":`刷新额度`,className:`account-icon-btn`,disabled:M,onClick:()=>e.onAction(`sync-quota`,n),title:`刷新额度`,type:`button`,children:P?(0,N.jsx)(E,{className:`spin`,size:14}):(0,N.jsx)(i,{size:14})})]}),(0,N.jsxs)(`div`,{className:`badge-row`,children:[(0,N.jsx)(`span`,{className:`badge brand`,children:m(n)}),(0,N.jsx)(`span`,{className:`badge ${a.tone}`,children:a.label}),(0,N.jsx)(`span`,{className:`badge ${D.ok?`green`:`orange`}`,children:`gpt-image-2`}),(0,N.jsx)(`span`,{className:`badge ${O?.exported?`orange`:`muted`}`,children:k})]})]}),(0,N.jsxs)(`label`,{className:`account-select`,children:[(0,N.jsx)(`input`,{type:`checkbox`,checked:!!e.selectedProfiles[n.profileId],onChange:t=>e.onSelect(n.profileId,t.target.checked)}),(0,N.jsx)(`span`,{children:`选择`})]})]}),(0,N.jsxs)(`div`,{className:`account-metrics`,children:[(0,N.jsx)(L,{label:s(n,`primary`),value:o,tone:f(o)}),(0,N.jsx)(L,{label:s(n,`secondary`),value:p,tone:f(p)})]}),(0,N.jsxs)(`div`,{className:`usage-status-row`,children:[(0,N.jsxs)(`span`,{className:`usage-status ${n.isActive?`is-active`:``}`,children:[(0,N.jsx)(r,{size:14}),(0,N.jsx)(`span`,{children:`API`}),(0,N.jsx)(`span`,{className:`usage-dot ${n.isActive?`active`:``}`}),(0,N.jsx)(`span`,{className:`usage-state-text`,children:n.isActive?`使用中`:`未使用`})]}),(0,N.jsxs)(`span`,{className:`usage-status ${b?`is-active`:``}`,children:[(0,N.jsx)(A,{size:14}),(0,N.jsx)(`span`,{children:`Codex`}),(0,N.jsx)(`span`,{className:`usage-dot ${b?`active`:``}`}),(0,N.jsx)(`span`,{className:`usage-state-text`,children:b?`使用中`:`未使用`})]})]}),(0,N.jsxs)(`div`,{className:`compact-meta-row`,children:[(0,N.jsxs)(`div`,{className:`compact-reset-list`,children:[(0,N.jsxs)(`div`,{className:`compact-meta-item`,children:[(0,N.jsx)(`label`,{children:s(n,`primary`)}),(0,N.jsx)(`strong`,{children:C(n,`primary`)})]}),(0,N.jsxs)(`div`,{className:`compact-meta-item`,children:[(0,N.jsx)(`label`,{children:s(n,`secondary`)}),(0,N.jsx)(`strong`,{children:C(n,`secondary`)})]})]}),(0,N.jsx)(`div`,{className:`compact-meta-actions`,children:(0,N.jsxs)(`button`,{className:`details-toggle ${y?`is-expanded`:``}`,type:`button`,onClick:()=>e.onToggle(n.profileId),children:[(0,N.jsx)(`span`,{children:y?`收起详情`:`查看详情`}),(0,N.jsx)(I,{})]})})]}),y&&(0,N.jsxs)(`div`,{className:`meta-grid`,children:[(0,N.jsx)(j,{label:`套餐`,value:m(n)}),(0,N.jsx)(j,{label:`Account ID`,value:(e.showEmails,n.accountId),code:!0}),(0,N.jsx)(j,{label:`Profile ID`,value:(e.showEmails,n.profileId),code:!0}),(0,N.jsx)(j,{label:`认证状态`,value:x(n)}),(0,N.jsx)(j,{label:`生图能力`,value:D.ok?`gpt-image-2 可用`:D.detail}),(0,N.jsx)(j,{label:`导出记录`,value:F(O)}),(0,N.jsx)(j,{label:`过期时间`,value:n.expiresAt?new Date(n.expiresAt).toLocaleString(`zh-CN`):`-`}),(0,N.jsx)(j,{label:`额度快照`,value:n.quota?.capturedAt?new Date(n.quota.capturedAt).toLocaleString(`zh-CN`):`-`})]}),(0,N.jsxs)(`div`,{className:`account-actions`,children:[(0,N.jsx)(`button`,{className:`btn-secondary ${n.isActive?`is-current`:``}`,type:`button`,onClick:()=>e.onAction(`activate`,n),disabled:n.isActive||M||w,children:w?`网关不可用`:n.isActive?`网关使用中`:`应用网关`}),(0,N.jsx)(`button`,{className:`btn-secondary ${b?`is-current codex`:``}`,type:`button`,onClick:()=>e.onAction(`apply-codex`,n),disabled:b||M||w,children:w?`Codex 不可用`:b?`Codex 使用中`:`应用 Codex`}),(0,N.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:()=>e.onAction(`export`,n),disabled:M,children:`导出`}),(0,N.jsx)(`button`,{className:`btn-danger`,type:`button`,onClick:()=>e.onAction(`remove`,n),disabled:M,children:`删除`})]})]},n.profileId)})})]})}function F(e){if(!e?.exported)return`未导出`;let t=e.lastExportKind===`single`?`单账号导出`:e.lastExportKind===`batch`?`批量导出`:`全部导出`;return`${e.count} 次,最近 ${o(e.lastExportedAt)},方式 ${t}`}function I(){return(0,N.jsx)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,"aria-hidden":`true`,children:(0,N.jsx)(`path`,{d:`m6 9 6 6 6-6`})})}function L(e){return(0,N.jsxs)(`div`,{className:`quota-row`,children:[(0,N.jsxs)(`div`,{className:`quota-line`,children:[(0,N.jsxs)(`span`,{children:[e.label,` · 已用 `,e.value,`% / 剩余 `,100-e.value,`%`]}),(0,N.jsxs)(`strong`,{children:[`剩余 `,100-e.value,`%`]})]}),(0,N.jsx)(`div`,{className:`progress-track`,children:(0,N.jsx)(`div`,{className:`progress-bar ${e.tone}`,style:{width:`${e.value}%`}})})]})}function R(e){let[t,n]=(0,M.useState)({}),[r,i]=(0,M.useState)({}),[a,o]=(0,M.useState)({search:``,status:`all`,sort:`quota-desc`}),s=(0,M.useMemo)(()=>{let t=e.config?.profiles?[...e.config.profiles]:[],n=a.search.trim().toLowerCase(),r=t.filter(t=>{let r=[h(t,!0).toLowerCase(),t.accountId,t.profileId,t.email||``].join(` `).toLowerCase(),i=d(t),o=!!(e.codexAccountId&&t.accountId===e.codexAccountId);return n&&!r.includes(n)?!1:a.status===`active`?t.isActive||o:a.status===`healthy`?i.key===`healthy`:a.status===`warning`?i.key===`warning`:a.status===`exhausted`?i.key===`exhausted`:a.status===`expired`?i.key===`expired`:a.status===`invalid`?i.key===`invalid`:!0});return r.sort((t,n)=>{let r=p(t,e.codexAccountId)-p(n,e.codexAccountId);if(r!==0)return r;let i=y(n)-y(t);if(i!==0)return i;let o=S(n)-S(t);return o===0?a.sort===`latency-asc`?(n.quota?.capturedAt||0)-(t.quota?.capturedAt||0):a.sort===`expiry-asc`?(t.expiresAt||2**53-1)-(n.expiresAt||2**53-1):a.sort===`name-asc`?h(t,!0).localeCompare(h(n,!0),`zh-CN`):a.sort===`quota-asc`?100-u(n)-(100-u(t)):a.sort===`plan-desc`?y(n)-y(t):a.sort===`email-asc`?h(t,!0).localeCompare(h(n,!0)):u(n)-u(t):o}),r},[a,e.codexAccountId,e.config?.profiles]),c=Object.values(t).filter(Boolean).length,l=Object.keys(t).filter(e=>t[e]);async function f(t,n){let r=await O(`/_gateway/admin/profiles/export`,{method:`POST`,headers:{"Content-Type":`application/json`},body:w(n?{profileIds:n}:{profileId:t})});D(`ai-zero-token-${n?`profiles-${n.length}`:t||`active`}.json`,r.profile),r.config?e.setConfig(r.config):await e.refreshConfig({silent:!0}),e.setStatus(n?`已导出 ${n.length} 个账号。`:`账号配置已导出。`)}async function m(t,n){if(!(t===`remove`&&!window.confirm(`确认删除 ${h(n,e.showEmails)}?`))){if((t===`activate`||t===`apply-codex`)&&_(n)){e.setStatus(`${h(n,e.showEmails)} 登录已失效,不能应用到${t===`activate`?`网关`:`Codex`}。`);return}if((t===`activate`||t===`apply-codex`)&&b(n)){let r=t===`activate`?`网关`:`Codex`;if(!window.confirm(`${h(n,e.showEmails)} 的额度看起来已耗尽,仍要应用到${r}吗?`))return}if(t===`export`){await f(n.profileId);return}e.setBusy(`profile:${t}:${n.profileId}`);try{let r=await O({activate:`/_gateway/admin/profiles/activate`,"apply-codex":`/_gateway/admin/codex/apply`,"sync-quota":`/_gateway/admin/profiles/sync-quota`,remove:`/_gateway/admin/profiles/remove`}[t],{method:`POST`,headers:{"Content-Type":`application/json`},body:w({profileId:n.profileId})});e.setConfig(`config`in r?r.config:r),e.setStatus(t===`activate`?`已应用到网关。`:t===`apply-codex`?`已应用到本机 Codex。`:t===`sync-quota`?`额度信息已同步。`:`账号已删除。`)}catch(t){e.setStatus(k(t))}finally{e.setBusy(null)}}}async function g(){let t=l;if(t.length===0){e.setStatus(`请先勾选要删除的账号。`);return}let r=e.config?.profiles.filter(e=>t.includes(e.profileId)).slice(0,3).map(t=>h(t,e.showEmails)),i=r?.length?`\n\n${r.join(`
2
+ `)}${t.length>r.length?`\n等 ${t.length} 个账号`:``}`:``;if(window.confirm(`确认删除所选 ${t.length} 个账号?此操作不可撤销。${i}`)){e.setBusy(`bulk-remove`),e.setStatus(`正在删除 ${t.length} 个账号...`);try{let r=await O(`/_gateway/admin/profiles/remove-batch`,{method:`POST`,headers:{"Content-Type":`application/json`},body:w({profileIds:t})});e.setConfig(r),n({}),e.setStatus(`已删除 ${r.removedProfileCount??t.length} 个账号。`)}catch(t){e.setStatus(`删除所选失败: ${k(t)}`)}finally{e.setBusy(null)}}}return(0,N.jsx)(P,{config:e.config,profiles:s,showEmails:e.showEmails,filter:a,selectedProfiles:t,expandedProfiles:r,selectedCount:c,busy:e.busy,onFilter:o,onSelect:(e,t)=>n(n=>({...n,[e]:t})),onToggle:e=>i(t=>({...t,[e]:!t[e]})),onAction:m,onLocate:()=>e.activeProfile&&document.querySelector(`[data-profile-card="${e.activeProfile.profileId}"]`)?.scrollIntoView({behavior:`smooth`,block:`center`}),onExportSelected:()=>{let t=l;if(t.length===0){e.setStatus(`请先勾选要导出的账号。`);return}f(void 0,t).catch(t=>e.setStatus(t instanceof Error?t.message:String(t)))},onRemoveSelected:()=>void g(),onAddAccount:()=>e.setAccountModalOpen(!0),onRefreshStatus:()=>e.refreshConfig({runtime:!0}),onClearAccounts:()=>e.logout()})}export{R as AccountsPage};
@@ -0,0 +1 @@
1
+ import{n as e}from"./jsx-runtime-DqpGtLhh.js";var t=e(`activity`,[[`path`,{d:`M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2`,key:`169zse`}]]);export{t};
@@ -0,0 +1,8 @@
1
+ <svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <rect width="128" height="128" fill="none"/>
3
+ <rect x="8" y="8" width="112" height="112" rx="26" fill="#F8FAFC"/>
4
+ <text x="64" y="79" text-anchor="middle" font-family="Inter, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" font-size="38" font-weight="900" letter-spacing="-2" fill="#111827">azt</text>
5
+ <path d="M22 95c18 7 66 7 84 0" stroke="#22C55E" stroke-width="6" stroke-linecap="round"/>
6
+ <circle cx="30" cy="32" r="5" fill="#2563EB"/>
7
+ <circle cx="98" cy="32" r="5" fill="#F97316"/>
8
+ </svg>
@@ -0,0 +1 @@
1
+ import{n as e}from"./jsx-runtime-DqpGtLhh.js";var t=e(`circle-check`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m9 12 2 2 4-4`,key:`dzmm74`}]]);export{t};
@@ -0,0 +1 @@
1
+ import{n as e}from"./jsx-runtime-DqpGtLhh.js";var t=e(`clock-3`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M12 6v6h4`,key:`135r8i`}]]);export{t};
@@ -0,0 +1 @@
1
+ .docs-page{gap:14px;display:grid}.docs-page-head{justify-content:space-between;align-items:flex-start;gap:16px;display:flex}.docs-page-kicker{color:var(--brand);align-items:center;gap:8px;font-size:12px;font-weight:800;display:inline-flex}.docs-page-head h2{margin:6px 0 0;font-size:22px;line-height:1.2}.docs-page-head p{color:var(--text-muted);margin:8px 0 0;font-size:13px;line-height:1.6}.docs-page-actions{flex-wrap:wrap;justify-content:flex-end;gap:10px;display:flex}.docs-summary{grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;display:grid}.docs-summary-item{border:1px solid var(--line);min-width:0;box-shadow:var(--shadow-sm);background:#ffffffeb;border-radius:14px;padding:12px 13px}.docs-summary-item span{color:var(--text-muted);font-size:11px;font-weight:800;line-height:1.3;display:block}.docs-summary-item strong{color:var(--text);overflow-wrap:anywhere;margin-top:5px;font-size:13px;line-height:1.45;display:block}.docs-layout{grid-template-columns:minmax(0,1fr);align-items:start;gap:12px;display:grid}.docs-main{gap:12px;min-width:0;display:grid}.docs-tab-bar{border:1px solid var(--line);background:#ffffffe6;border-radius:14px;align-items:center;gap:6px;width:fit-content;padding:4px;display:inline-flex}.docs-tab-bar button{min-height:34px;color:var(--text-muted);background:0 0;border:0;border-radius:10px;padding:0 12px;font-size:13px;font-weight:800}.docs-tab-bar button.is-active{color:var(--brand);background:#635bff1a}.docs-panel,.docs-snippet{border:1px solid var(--line);box-shadow:var(--shadow-sm);background:#fffffff0;border-radius:16px}.docs-panel{padding:16px}.docs-panel-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;display:grid}.docs-panel-wide{grid-column:1/-1}.docs-panel-head{justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:14px;display:flex}.docs-panel-head h3{margin:0;font-size:15px;line-height:1.25}.docs-panel-head p,.docs-snippet p{color:var(--text-muted);margin:6px 0 0;font-size:12px;line-height:1.55}.docs-step-list{gap:12px;margin:0;padding-left:18px;display:grid}.docs-step-list li{gap:6px;display:grid}.docs-step-list strong{font-size:13px;line-height:1.3}.docs-step-list span{color:var(--text-muted);font-size:12px;line-height:1.55}.docs-step-list code,.docs-code-sample code,.docs-markdown p code,.docs-markdown li code{color:var(--brand-strong);background:#635bff14;border-radius:7px;padding:1px 5px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}.docs-mini-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-bottom:12px;display:grid}.docs-mini-copy{border:1px solid var(--line);text-align:left;min-width:0;color:var(--text);background:#f8fafc;border-radius:14px;gap:6px;padding:12px;display:grid}.docs-mini-copy span{color:var(--text-muted);font-size:11px;font-weight:800}.docs-mini-copy strong{overflow-wrap:anywhere;min-width:0;font-size:12px;line-height:1.4}.docs-mini-copy svg{color:var(--brand);justify-self:end}.docs-code-sample,.docs-snippet pre,.docs-code{border:1px solid var(--line);color:var(--text);background:#f8fafc;border-radius:14px;margin:0;overflow:auto}.docs-code-sample{padding:14px;font-size:12px;line-height:1.6}.docs-endpoint-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.docs-endpoint{border:1px solid var(--line);background:#f8fafc;border-radius:12px;justify-content:space-between;align-items:center;gap:8px;min-height:44px;padding:0 12px;font-size:12px;font-weight:800;display:flex}.docs-preview-panel{gap:14px;display:grid}.docs-skill-summary{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;display:grid}.docs-summary-tile{border:1px solid var(--line);background:#f8fafc;border-radius:14px;min-width:0;padding:12px 13px}.docs-summary-tile span{color:var(--text-muted);font-size:11px;font-weight:800;line-height:1.25;display:block}.docs-summary-tile strong{color:var(--text);overflow-wrap:anywhere;margin-top:5px;font-size:13px;line-height:1.45;display:block}.docs-source-fold{border:1px solid var(--line);background:#fff;border-radius:14px;overflow:hidden}.docs-source-fold summary{cursor:pointer;color:var(--text);justify-content:space-between;align-items:center;gap:12px;padding:12px 14px;font-size:13px;font-weight:800;list-style:none;display:flex}.docs-source-fold summary::-webkit-details-marker{display:none}.docs-source-fold summary span{color:var(--text-muted);font-size:11px;font-weight:700}.docs-source{border-top:1px solid var(--line);max-height:420px;color:var(--text);background:#f8fafc;margin:0;padding:14px;font-size:12px;line-height:1.65;overflow:auto}.docs-source code{white-space:pre;overflow-wrap:normal;word-break:normal;color:inherit;background:0 0;padding:0}.docs-markdown{gap:10px;max-height:620px;padding-right:4px;display:grid;overflow:auto}.docs-markdown h1,.docs-markdown h2,.docs-markdown h3,.docs-markdown h4{margin:12px 0 0;line-height:1.3}.docs-markdown h1{font-size:22px}.docs-markdown h2{font-size:18px}.docs-markdown h3{font-size:15px}.docs-markdown h4{font-size:13px}.docs-markdown p,.docs-markdown li{color:var(--text-soft);margin:0;font-size:13px;line-height:1.7}.docs-markdown ul{gap:6px;margin:0;padding-left:20px;display:grid}.docs-code{padding:14px 14px 12px;font-size:12px;line-height:1.6;position:relative}.docs-code-lang{color:var(--text-muted);text-transform:uppercase;letter-spacing:0;margin-bottom:10px;font-size:11px;font-weight:800;display:inline-flex}.docs-code code{color:var(--text);white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word;background:0 0;margin:0;padding:0;display:block}.docs-example-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;display:grid}.docs-note-panel{grid-column:1/-1;padding:16px}.docs-note-panel ul{color:var(--text-soft);gap:8px;margin:10px 0 0;padding-left:18px;font-size:13px;line-height:1.6;display:grid}.docs-action-row{flex-wrap:wrap;gap:10px;margin-top:14px;display:flex}.docs-snippet{padding:16px}.docs-snippet-head{justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:12px;display:flex}.docs-snippet-head strong{font-size:14px;line-height:1.3;display:block}.docs-snippet pre{padding:14px;font-size:12px;line-height:1.6}@media (width<=1280px){.docs-layout{grid-template-columns:minmax(0,1fr)}}@media (width<=1120px){.docs-page-head,.docs-layout,.docs-panel-grid,.docs-example-grid,.docs-summary,.docs-mini-grid,.docs-endpoint-grid{grid-template-columns:1fr}.docs-page-actions{justify-content:flex-start}}