ai-zero-token 2.0.2 → 2.0.4
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.
- package/CHANGELOG.md +20 -0
- package/README.md +31 -6
- package/README.zh-CN.md +31 -6
- package/admin-ui/dist/assets/accounts-CTjk9c4F.js +4 -0
- package/admin-ui/dist/assets/{docs-CihX3Xsm.js → docs-oNIugCIL.js} +3 -3
- package/admin-ui/dist/assets/{image-bed-BGjlDLks.js → image-bed-CQtIhjg_.js} +1 -1
- package/admin-ui/dist/assets/index-By4r-wy3.css +1 -0
- package/admin-ui/dist/assets/index-rgcJgVAu.js +10 -0
- package/admin-ui/dist/assets/{launch-BWw7Odq7.js → launch-B-2Zdz9m.js} +1 -1
- package/admin-ui/dist/assets/logs-JFuSf56b.js +1 -0
- package/admin-ui/dist/assets/{network-detect-cUdjg4zk.js → network-detect-SfvK6uhx.js} +1 -1
- package/admin-ui/dist/assets/{overview-CsjVVcvi.js → overview-X_WodIqE.js} +1 -1
- package/admin-ui/dist/assets/settings-0eXUAvcm.js +1 -0
- package/admin-ui/dist/assets/{tester-BIvH_8DY.js → tester-ocpF053C.js} +1 -1
- package/admin-ui/dist/index.html +2 -2
- package/build/mac-install-guide.txt +25 -0
- package/build/tray-icon-template.png +0 -0
- package/dist/core/providers/openai-codex/chat.js +77 -0
- package/dist/core/services/auth-service.js +88 -12
- package/dist/core/services/chat-service.js +1 -0
- package/dist/core/services/config-service.js +87 -23
- package/dist/core/services/version-service.js +1 -1
- package/dist/core/store/profile-store.js +73 -32
- package/dist/core/store/settings-store.js +14 -0
- package/dist/desktop/main.js +616 -15
- package/dist/server/app.js +512 -58
- package/dist/server/index.js +2 -1
- package/docs/API_USAGE.md +65 -1
- package/docs/DESKTOP_RELEASE.md +48 -3
- package/package.json +33 -2
- package/admin-ui/dist/assets/accounts-Ddq82u6R.js +0 -1
- package/admin-ui/dist/assets/index-CX8e0-BB.js +0 -10
- package/admin-ui/dist/assets/index-ywn2Jwpu.css +0 -1
- package/admin-ui/dist/assets/logs-DDdgDVwo.js +0 -1
- package/admin-ui/dist/assets/settings-Be99HpDD.js +0 -1
package/dist/server/index.js
CHANGED
|
@@ -41,7 +41,8 @@ async function startServer(params) {
|
|
|
41
41
|
const app = createApp({
|
|
42
42
|
corsOrigin: resolveCorsOrigin(),
|
|
43
43
|
bodyLimit,
|
|
44
|
-
onRestart: params?.onRestart
|
|
44
|
+
onRestart: params?.onRestart,
|
|
45
|
+
onRestartCodex: params?.onRestartCodex
|
|
45
46
|
});
|
|
46
47
|
try {
|
|
47
48
|
await app.listen({
|
package/docs/API_USAGE.md
CHANGED
|
@@ -41,6 +41,24 @@ Image model: gpt-image-2
|
|
|
41
41
|
|
|
42
42
|
Use `GET /v1/models` to see the models available through the current local Codex cache.
|
|
43
43
|
|
|
44
|
+
## OpenClaw Settings
|
|
45
|
+
|
|
46
|
+
Use the OpenAI-compatible provider mode in OpenClaw:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
Provider: OpenAI compatible
|
|
50
|
+
Base URL: http://127.0.0.1:8787/v1
|
|
51
|
+
API Key: local
|
|
52
|
+
Model: gpt-5.4
|
|
53
|
+
Chat endpoint: /chat/completions
|
|
54
|
+
Streaming: enabled
|
|
55
|
+
Tools / function calling: enabled
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The gateway accepts OpenClaw-style `chat.completions` requests with `tools`, `tool_choice`, `parallel_tool_calls`, `reasoning_effort`, assistant `tool_calls`, and tool-role result messages. It translates those fields to the upstream Codex Responses shape and returns OpenAI-style chat responses.
|
|
59
|
+
|
|
60
|
+
OpenClaw requests are visible in the management console request log when the client sends an OpenClaw user agent. The log keeps safe summaries only; it does not store full access tokens.
|
|
61
|
+
|
|
44
62
|
## Models
|
|
45
63
|
|
|
46
64
|
```bash
|
|
@@ -74,6 +92,50 @@ curl http://127.0.0.1:8787/v1/chat/completions \
|
|
|
74
92
|
"messages": [
|
|
75
93
|
{ "role": "user", "content": "Reply with OK only." }
|
|
76
94
|
]
|
|
95
|
+
}'
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Streaming chat completions:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
curl http://127.0.0.1:8787/v1/chat/completions \
|
|
102
|
+
-H "Content-Type: application/json" \
|
|
103
|
+
-d '{
|
|
104
|
+
"model": "gpt-5.4",
|
|
105
|
+
"stream": true,
|
|
106
|
+
"messages": [
|
|
107
|
+
{ "role": "user", "content": "Reply with OK only." }
|
|
108
|
+
]
|
|
109
|
+
}'
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Tool-call compatible request:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
curl http://127.0.0.1:8787/v1/chat/completions \
|
|
116
|
+
-H "Content-Type: application/json" \
|
|
117
|
+
-d '{
|
|
118
|
+
"model": "gpt-5.4",
|
|
119
|
+
"messages": [
|
|
120
|
+
{ "role": "user", "content": "What is the weather tool argument for Shanghai?" }
|
|
121
|
+
],
|
|
122
|
+
"tools": [
|
|
123
|
+
{
|
|
124
|
+
"type": "function",
|
|
125
|
+
"function": {
|
|
126
|
+
"name": "get_weather",
|
|
127
|
+
"description": "Get weather for a city.",
|
|
128
|
+
"parameters": {
|
|
129
|
+
"type": "object",
|
|
130
|
+
"properties": {
|
|
131
|
+
"city": { "type": "string" }
|
|
132
|
+
},
|
|
133
|
+
"required": ["city"]
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
],
|
|
138
|
+
"tool_choice": "auto"
|
|
77
139
|
}'
|
|
78
140
|
```
|
|
79
141
|
|
|
@@ -134,5 +196,7 @@ console.log(response.choices[0]?.message?.content);
|
|
|
134
196
|
|
|
135
197
|
- Login first through the management page or `azt login`.
|
|
136
198
|
- A model appearing in `/v1/models` means the local Codex cache lists it. Final availability still depends on the active account.
|
|
137
|
-
- `stream=true` is
|
|
199
|
+
- `stream=true` is supported for `/v1/chat/completions` through OpenAI-style SSE chunks. `/v1/responses` streaming is still not implemented.
|
|
200
|
+
- `n > 1` is not supported for `/v1/chat/completions`.
|
|
201
|
+
- Tool/function calling is supported for common OpenAI-compatible clients, including OpenClaw, but exact upstream behavior still depends on the active Codex model and account.
|
|
138
202
|
- The default listener is `0.0.0.0:8787`, so local-network clients can call the gateway by using the machine IP.
|
package/docs/DESKTOP_RELEASE.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
This project ships the desktop app with Electron. The desktop main process starts the existing local Fastify gateway and loads the React management UI served by that gateway.
|
|
4
4
|
|
|
5
|
+
## 2.0.4 Release Notes
|
|
6
|
+
|
|
7
|
+
Version `2.0.4` adds the macOS menu-bar account panel and OpenClaw compatibility work:
|
|
8
|
+
|
|
9
|
+
- Menu-bar quick account panel for switching gateway/Codex accounts.
|
|
10
|
+
- Menu actions for quota refresh, Base URL copy, console open, gateway restart, and quit.
|
|
11
|
+
- Desktop Codex restart hook after applying an account to local Codex.
|
|
12
|
+
- OpenClaw-compatible `chat.completions` streaming and tool-call fields.
|
|
13
|
+
- Real gateway request log entries for recent API traffic.
|
|
14
|
+
|
|
5
15
|
## 2.0.0 Release Notes
|
|
6
16
|
|
|
7
17
|
Version `2.0.0` is the first desktop-focused major release. It includes:
|
|
@@ -41,6 +51,13 @@ npm run dist:win
|
|
|
41
51
|
|
|
42
52
|
Creates macOS and Windows distributables. macOS builds should be produced on macOS. Windows builds are best produced on Windows CI or a runner with a complete Windows packaging environment.
|
|
43
53
|
|
|
54
|
+
`npm run dist:mac` must build both Apple Silicon and Intel macOS packages. It runs:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm run dist:mac:arm64
|
|
58
|
+
npm run dist:mac:x64
|
|
59
|
+
```
|
|
60
|
+
|
|
44
61
|
## UI Engineering Standards
|
|
45
62
|
|
|
46
63
|
Before building release artifacts, the desktop React UI should follow:
|
|
@@ -65,6 +82,8 @@ Unsigned builds are suitable for internal testing only. Public commercial distri
|
|
|
65
82
|
|
|
66
83
|
`electron-builder` reads the standard signing environment variables. Configure these in CI instead of committing credentials to the repository.
|
|
67
84
|
|
|
85
|
+
Until macOS Developer ID signing and notarization are configured, each macOS DMG must include `build/mac-install-guide.txt` so users can handle the Gatekeeper verification prompt.
|
|
86
|
+
|
|
68
87
|
## Release Artifacts
|
|
69
88
|
|
|
70
89
|
The packaged output is written to:
|
|
@@ -75,17 +94,42 @@ release/
|
|
|
75
94
|
|
|
76
95
|
The folder is intentionally ignored by git.
|
|
77
96
|
|
|
97
|
+
Every desktop GitHub Release must upload exactly these user-facing artifacts:
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
AI Zero Token-{version}-mac-arm64.dmg
|
|
101
|
+
AI Zero Token-{version}-mac-x64.dmg
|
|
102
|
+
AI Zero Token Setup {version}.exe
|
|
103
|
+
AI Zero Token-{version}-win.zip
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
For `2.0.4`, replace `{version}` with `2.0.4`.
|
|
107
|
+
|
|
108
|
+
Artifact purpose:
|
|
109
|
+
|
|
110
|
+
- `AI Zero Token-{version}-mac-arm64.dmg`: macOS Apple Silicon builds for M1/M2/M3/M4 devices.
|
|
111
|
+
- `AI Zero Token-{version}-mac-x64.dmg`: macOS Intel builds.
|
|
112
|
+
- `AI Zero Token Setup {version}.exe`: Windows installer and primary Windows download.
|
|
113
|
+
- `AI Zero Token-{version}-win.zip`: Windows portable zip.
|
|
114
|
+
|
|
115
|
+
Do not upload unpacked app directories, debug metadata, universal macOS builds, or auto-update metadata unless the release explicitly enables an auto-update channel.
|
|
116
|
+
|
|
117
|
+
macOS DMG files should include the in-package `mac-install-guide.txt` helper document. Do not upload this text file as a standalone GitHub Release asset.
|
|
118
|
+
|
|
78
119
|
### Publish Flow
|
|
79
120
|
|
|
80
121
|
1. Build the desktop package:
|
|
81
122
|
|
|
82
123
|
```bash
|
|
83
|
-
npm run dist:
|
|
124
|
+
npm run dist:mac
|
|
125
|
+
npm run dist:win
|
|
84
126
|
```
|
|
85
127
|
|
|
86
|
-
2.
|
|
128
|
+
2. Rename the generated files, if needed, so the GitHub Release uses the standard artifact names listed above.
|
|
129
|
+
|
|
130
|
+
3. Upload only the standard artifact files from `release/` to the matching GitHub Release tag.
|
|
87
131
|
|
|
88
|
-
|
|
132
|
+
4. Publish the npm package after confirming `package.json` and `package-lock.json` both point at the new version:
|
|
89
133
|
|
|
90
134
|
```bash
|
|
91
135
|
npm publish
|
|
@@ -99,6 +143,7 @@ App icon files live in:
|
|
|
99
143
|
build/icon.png
|
|
100
144
|
build/icon.icns
|
|
101
145
|
build/icon.ico
|
|
146
|
+
build/tray-icon-template.png
|
|
102
147
|
```
|
|
103
148
|
|
|
104
149
|
They are included in Electron packaging and npm packing.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-zero-token",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "Local-first OpenAI-compatible AI CLI and gateway with Codex OAuth, multi-account management, and gpt-image-2 image generation/editing.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "AI Zero Token Contributors",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"desktop:dev": "node scripts/dev.mjs desktop",
|
|
51
51
|
"dist": "npm run build && electron-builder",
|
|
52
52
|
"dist:dir": "npm run build && electron-builder --dir",
|
|
53
|
-
"dist:mac": "npm run
|
|
53
|
+
"dist:mac": "npm run dist:mac:arm64 && npm run dist:mac:x64",
|
|
54
54
|
"dist:mac:arm64": "npm run build && electron-builder --mac --arm64",
|
|
55
55
|
"dist:mac:x64": "npm run build && electron-builder --mac --x64",
|
|
56
56
|
"dist:win": "npm run build && electron-builder --win",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@fastify/cors": "^11.1.0",
|
|
65
65
|
"fastify": "^5.8.2",
|
|
66
|
+
"fflate": "^0.8.2",
|
|
66
67
|
"zod": "^4.3.6"
|
|
67
68
|
},
|
|
68
69
|
"devDependencies": {
|
|
@@ -89,11 +90,18 @@
|
|
|
89
90
|
"dist/**/*",
|
|
90
91
|
"admin-ui/dist/**/*",
|
|
91
92
|
"build/icon.*",
|
|
93
|
+
"build/tray-icon-template.png",
|
|
92
94
|
"package.json",
|
|
93
95
|
"README.md",
|
|
94
96
|
"README.zh-CN.md",
|
|
95
97
|
"LICENSE"
|
|
96
98
|
],
|
|
99
|
+
"extraResources": [
|
|
100
|
+
{
|
|
101
|
+
"from": "build/mac-install-guide.txt",
|
|
102
|
+
"to": "mac-install-guide.txt"
|
|
103
|
+
}
|
|
104
|
+
],
|
|
97
105
|
"extraMetadata": {
|
|
98
106
|
"main": "dist/desktop/main.js"
|
|
99
107
|
},
|
|
@@ -106,6 +114,27 @@
|
|
|
106
114
|
"zip"
|
|
107
115
|
]
|
|
108
116
|
},
|
|
117
|
+
"dmg": {
|
|
118
|
+
"contents": [
|
|
119
|
+
{
|
|
120
|
+
"x": 130,
|
|
121
|
+
"y": 160,
|
|
122
|
+
"type": "file"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"x": 410,
|
|
126
|
+
"y": 160,
|
|
127
|
+
"type": "link",
|
|
128
|
+
"path": "/Applications"
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"x": 270,
|
|
132
|
+
"y": 300,
|
|
133
|
+
"type": "file",
|
|
134
|
+
"path": "build/mac-install-guide.txt"
|
|
135
|
+
}
|
|
136
|
+
]
|
|
137
|
+
},
|
|
109
138
|
"win": {
|
|
110
139
|
"icon": "build/icon.ico",
|
|
111
140
|
"target": [
|
|
@@ -125,6 +154,8 @@
|
|
|
125
154
|
"build/icon.png",
|
|
126
155
|
"build/icon.icns",
|
|
127
156
|
"build/icon.ico",
|
|
157
|
+
"build/tray-icon-template.png",
|
|
158
|
+
"build/mac-install-guide.txt",
|
|
128
159
|
"CHANGELOG.md",
|
|
129
160
|
"docs/API_USAGE.md",
|
|
130
161
|
"docs/DESKTOP_RELEASE.md",
|
|
@@ -1 +0,0 @@
|
|
|
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{_ as o,a as s,b as c,d as l,f as u,g as d,h as f,i as p,m,n as h,o as g,p as _,r as v,s as y,t as b,u as x,v as S,w as C,y as w}from"./profiles-DMOjJORP.js";import{_ as T,d as E,p as D,r as O,x as k}from"./index-CX8e0-BB.js";import{t as A}from"./InfoRow-0ULI9iI3.js";var j=e(t(),1),M=n();function N(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,M.jsxs)(`section`,{className:`card`,id:`accounts`,children:[(0,M.jsxs)(`div`,{className:`section-head`,children:[(0,M.jsxs)(`div`,{children:[(0,M.jsx)(`h2`,{children:`账号额度预览`}),(0,M.jsx)(`p`,{children:`账号信息采用卡片式布局展示,支持搜索、状态筛选和额度排序。`})]}),(0,M.jsxs)(`div`,{className:`section-actions`,children:[(0,M.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onLocate,children:`定位当前账号`}),(0,M.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onExportSelected,children:`导出所选`}),(0,M.jsx)(`button`,{className:`btn-primary`,type:`button`,onClick:e.onAddAccount,children:`新增账号`}),(0,M.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:e.onRefreshStatus,children:`刷新状态`}),(0,M.jsx)(`button`,{className:`btn-danger`,type:`button`,onClick:e.onClearAccounts,children:`清空账号`})]})]}),(0,M.jsxs)(`div`,{className:`filter-row`,children:[(0,M.jsxs)(`label`,{className:`search-box`,children:[(0,M.jsx)(a,{size:16}),(0,M.jsx)(`input`,{value:e.filter.search,onChange:t=>e.onFilter({...e.filter,search:t.target.value}),placeholder:`搜索邮箱、账号 ID 或 Profile ID`})]}),(0,M.jsxs)(`select`,{className:`control`,value:e.filter.status,onChange:t=>e.onFilter({...e.filter,status:t.target.value}),children:[(0,M.jsx)(`option`,{value:`all`,children:`全部状态`}),(0,M.jsx)(`option`,{value:`active`,children:`使用中`}),(0,M.jsx)(`option`,{value:`healthy`,children:`健康`}),(0,M.jsx)(`option`,{value:`warning`,children:`即将耗尽`}),(0,M.jsx)(`option`,{value:`exhausted`,children:`额度耗尽`}),(0,M.jsx)(`option`,{value:`invalid`,children:`登录失效`}),(0,M.jsx)(`option`,{value:`expired`,children:`已过期`})]}),(0,M.jsxs)(`select`,{className:`control`,value:e.filter.sort,onChange:t=>e.onFilter({...e.filter,sort:t.target.value}),children:[(0,M.jsx)(`option`,{value:`quota-desc`,children:`默认排序`}),(0,M.jsx)(`option`,{value:`latency-asc`,children:`按额度更新时间`}),(0,M.jsx)(`option`,{value:`expiry-asc`,children:`按过期时间`}),(0,M.jsx)(`option`,{value:`name-asc`,children:`按名称排序`}),(0,M.jsx)(`option`,{value:`quota-asc`,children:`按剩余额度升序`}),(0,M.jsx)(`option`,{value:`plan-desc`,children:`按套餐排序`}),(0,M.jsx)(`option`,{value:`email-asc`,children:`按邮箱排序`})]}),(0,M.jsxs)(`span`,{className:`account-selected-count`,children:[`已选择 `,e.selectedCount,` 个`]})]}),(0,M.jsx)(`div`,{className:`account-grid ${n}`,children:e.profiles.length===0?(0,M.jsx)(`div`,{className:`empty-state`,children:`还没有匹配的账号。可以新增账号或调整筛选条件。`}):e.profiles.map(n=>{let a=u(n),f=l(n),v=w(n),y=!!e.expandedProfiles[n.profileId],x=!!(t&&n.accountId===t),C=c(n,x),E=g(n),D=s(n),O=typeof e.busy==`string`&&e.busy.startsWith(`profile:`)&&e.busy.endsWith(n.profileId),j=e.busy===`profile:sync-quota:${n.profileId}`;return(0,M.jsxs)(`article`,{className:`account-card plan-${h(n)} ${E?`is-auth-invalid`:``}`,"data-profile-card":n.profileId,title:E?b(n):void 0,children:[C&&(0,M.jsx)(`span`,{className:`usage-corner ${C.className}`,children:(0,M.jsx)(`span`,{children:C.label})}),(0,M.jsxs)(`div`,{className:`account-head`,children:[(0,M.jsxs)(`div`,{className:`account-title`,children:[(0,M.jsxs)(`div`,{className:`account-name`,children:[(0,M.jsx)(`span`,{className:`avatar`,children:_(n)}),(0,M.jsx)(`strong`,{children:m(n,e.showEmails)}),(0,M.jsx)(`button`,{"aria-label":`刷新额度`,className:`account-icon-btn`,disabled:O,onClick:()=>e.onAction(`sync-quota`,n),title:`刷新额度`,type:`button`,children:j?(0,M.jsx)(T,{className:`spin`,size:14}):(0,M.jsx)(i,{size:14})})]}),(0,M.jsxs)(`div`,{className:`badge-row`,children:[(0,M.jsx)(`span`,{className:`badge brand`,children:p(n)}),(0,M.jsx)(`span`,{className:`badge ${a.tone}`,children:a.label}),(0,M.jsx)(`span`,{className:`badge ${D.ok?`green`:`orange`}`,children:`gpt-image-2`})]})]}),(0,M.jsxs)(`label`,{className:`account-select`,children:[(0,M.jsx)(`input`,{type:`checkbox`,checked:!!e.selectedProfiles[n.profileId],onChange:t=>e.onSelect(n.profileId,t.target.checked)}),(0,M.jsx)(`span`,{children:`选择`})]})]}),(0,M.jsxs)(`div`,{className:`account-metrics`,children:[(0,M.jsx)(F,{label:o(n,`primary`),value:f,tone:d(f)}),(0,M.jsx)(F,{label:o(n,`secondary`),value:v,tone:d(v)})]}),(0,M.jsxs)(`div`,{className:`usage-status-row`,children:[(0,M.jsxs)(`span`,{className:`usage-status ${n.isActive?`is-active`:``}`,children:[(0,M.jsx)(r,{size:14}),(0,M.jsx)(`span`,{children:`API`}),(0,M.jsx)(`span`,{className:`usage-dot ${n.isActive?`active`:``}`}),(0,M.jsx)(`span`,{className:`usage-state-text`,children:n.isActive?`使用中`:`未使用`})]}),(0,M.jsxs)(`span`,{className:`usage-status ${x?`is-active`:``}`,children:[(0,M.jsx)(k,{size:14}),(0,M.jsx)(`span`,{children:`Codex`}),(0,M.jsx)(`span`,{className:`usage-dot ${x?`active`:``}`}),(0,M.jsx)(`span`,{className:`usage-state-text`,children:x?`使用中`:`未使用`})]})]}),(0,M.jsxs)(`div`,{className:`compact-meta-row`,children:[(0,M.jsxs)(`div`,{className:`compact-reset-list`,children:[(0,M.jsxs)(`div`,{className:`compact-meta-item`,children:[(0,M.jsx)(`label`,{children:o(n,`primary`)}),(0,M.jsx)(`strong`,{children:S(n,`primary`)})]}),(0,M.jsxs)(`div`,{className:`compact-meta-item`,children:[(0,M.jsx)(`label`,{children:o(n,`secondary`)}),(0,M.jsx)(`strong`,{children:S(n,`secondary`)})]})]}),(0,M.jsx)(`div`,{className:`compact-meta-actions`,children:(0,M.jsxs)(`button`,{className:`details-toggle ${y?`is-expanded`:``}`,type:`button`,onClick:()=>e.onToggle(n.profileId),children:[(0,M.jsx)(`span`,{children:y?`收起详情`:`查看详情`}),(0,M.jsx)(P,{})]})})]}),y&&(0,M.jsxs)(`div`,{className:`meta-grid`,children:[(0,M.jsx)(A,{label:`套餐`,value:p(n)}),(0,M.jsx)(A,{label:`Account ID`,value:(e.showEmails,n.accountId),code:!0}),(0,M.jsx)(A,{label:`Profile ID`,value:(e.showEmails,n.profileId),code:!0}),(0,M.jsx)(A,{label:`认证状态`,value:b(n)}),(0,M.jsx)(A,{label:`生图能力`,value:D.ok?`gpt-image-2 可用`:D.detail}),(0,M.jsx)(A,{label:`过期时间`,value:n.expiresAt?new Date(n.expiresAt).toLocaleString(`zh-CN`):`-`}),(0,M.jsx)(A,{label:`额度快照`,value:n.quota?.capturedAt?new Date(n.quota.capturedAt).toLocaleString(`zh-CN`):`-`})]}),(0,M.jsxs)(`div`,{className:`account-actions`,children:[(0,M.jsx)(`button`,{className:`btn-secondary ${n.isActive?`is-current`:``}`,type:`button`,onClick:()=>e.onAction(`activate`,n),disabled:n.isActive||O||E,children:E?`网关不可用`:n.isActive?`网关使用中`:`应用网关`}),(0,M.jsx)(`button`,{className:`btn-secondary ${x?`is-current codex`:``}`,type:`button`,onClick:()=>e.onAction(`apply-codex`,n),disabled:x||O||E,children:E?`Codex 不可用`:x?`Codex 使用中`:`应用 Codex`}),(0,M.jsx)(`button`,{className:`btn-secondary`,type:`button`,onClick:()=>e.onAction(`export`,n),disabled:O,children:`导出`}),(0,M.jsx)(`button`,{className:`btn-danger`,type:`button`,onClick:()=>e.onAction(`remove`,n),disabled:O,children:`删除`})]})]},n.profileId)})})]})}function P(){return(0,M.jsx)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`2`,"aria-hidden":`true`,children:(0,M.jsx)(`path`,{d:`m6 9 6 6 6-6`})})}function F(e){return(0,M.jsxs)(`div`,{className:`quota-row`,children:[(0,M.jsxs)(`div`,{className:`quota-line`,children:[(0,M.jsxs)(`span`,{children:[e.label,` · 已用 `,e.value,`% / 剩余 `,100-e.value,`%`]}),(0,M.jsxs)(`strong`,{children:[`剩余 `,100-e.value,`%`]})]}),(0,M.jsx)(`div`,{className:`progress-track`,children:(0,M.jsx)(`div`,{className:`progress-bar ${e.tone}`,style:{width:`${e.value}%`}})})]})}function I(e){let[t,n]=(0,j.useState)({}),[r,i]=(0,j.useState)({}),[a,o]=(0,j.useState)({search:``,status:`all`,sort:`quota-desc`}),s=(0,j.useMemo)(()=>{let t=e.config?.profiles?[...e.config.profiles]:[],n=a.search.trim().toLowerCase(),r=t.filter(t=>{let r=[m(t,!0).toLowerCase(),t.accountId,t.profileId,t.email||``].join(` `).toLowerCase(),i=u(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=f(t,e.codexAccountId)-f(n,e.codexAccountId);if(r!==0)return r;let i=v(n)-v(t);if(i!==0)return i;let o=x(n)-x(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`?m(t,!0).localeCompare(m(n,!0),`zh-CN`):a.sort===`quota-asc`?100-l(n)-(100-l(t)):a.sort===`plan-desc`?v(n)-v(t):a.sort===`email-asc`?m(t,!0).localeCompare(m(n,!0)):l(n)-l(t):o}),r},[a,e.codexAccountId,e.config?.profiles]),c=Object.values(t).filter(Boolean).length;async function d(t,n){let r=await D(`/_gateway/admin/profiles/export`,{method:`POST`,headers:{"Content-Type":`application/json`},body:C(n?{profileIds:n}:{profileId:t})});E(`ai-zero-token-${n?`profiles-${n.length}`:t||`active`}.json`,r.profile),e.setStatus(n?`已导出 ${n.length} 个账号。`:`账号配置已导出。`)}async function p(t,n){if(!(t===`remove`&&!window.confirm(`确认删除 ${m(n,e.showEmails)}?`))){if((t===`activate`||t===`apply-codex`)&&g(n)){e.setStatus(`${m(n,e.showEmails)} 登录已失效,不能应用到${t===`activate`?`网关`:`Codex`}。`);return}if((t===`activate`||t===`apply-codex`)&&y(n)){let r=t===`activate`?`网关`:`Codex`;if(!window.confirm(`${m(n,e.showEmails)} 的额度看起来已耗尽,仍要应用到${r}吗?`))return}if(t===`export`){await d(n.profileId);return}e.setBusy(`profile:${t}:${n.profileId}`);try{let r=await D({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:C({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(O(t))}finally{e.setBusy(null)}}}return(0,M.jsx)(N,{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:p,onLocate:()=>e.activeProfile&&document.querySelector(`[data-profile-card="${e.activeProfile.profileId}"]`)?.scrollIntoView({behavior:`smooth`,block:`center`}),onExportSelected:()=>{let n=Object.keys(t).filter(e=>t[e]);if(n.length===0){e.setStatus(`请先勾选要导出的账号。`);return}d(void 0,n).catch(t=>e.setStatus(t instanceof Error?t.message:String(t)))},onAddAccount:()=>e.setAccountModalOpen(!0),onRefreshStatus:()=>e.refreshConfig({runtime:!0}),onClearAccounts:()=>e.logout()})}export{I as AccountsPage};
|