ai-zero-token 1.0.1 → 1.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.
- package/CHANGELOG.md +14 -0
- package/README.md +235 -69
- package/dist/api.js +0 -1
- package/dist/cli/commands/ask.js +131 -5
- package/dist/cli/commands/clear.js +0 -1
- package/dist/cli/commands/help.js +17 -11
- package/dist/cli/commands/login.js +0 -1
- package/dist/cli/commands/models.js +14 -4
- package/dist/cli/commands/serve.js +41 -4
- package/dist/cli/commands/start.js +10 -0
- package/dist/cli/commands/status.js +1 -1
- package/dist/cli/index.js +5 -2
- package/dist/cli/shared.js +57 -6
- package/dist/cli.js +0 -1
- package/dist/core/context.js +10 -2
- package/dist/core/models/openai-codex-models.js +89 -1
- package/dist/core/providers/http-client.js +137 -14
- package/dist/core/providers/openai-codex/chat.js +217 -24
- package/dist/core/providers/openai-codex/oauth.js +15 -4
- package/dist/core/providers/openai-codex/pkce.js +0 -1
- package/dist/core/services/auth-service.js +125 -16
- package/dist/core/services/chat-service.js +24 -14
- package/dist/core/services/config-service.js +4 -5
- package/dist/core/services/image-service.js +405 -0
- package/dist/core/services/model-service.js +35 -8
- package/dist/core/services/version-service.js +97 -0
- package/dist/core/store/profile-store.js +79 -6
- package/dist/core/store/settings-store.js +1 -2
- package/dist/core/types.js +0 -1
- package/dist/http.js +0 -1
- package/dist/models.js +0 -1
- package/dist/oauth.js +0 -1
- package/dist/pkce.js +0 -1
- package/dist/server/admin-page.js +3165 -0
- package/dist/server/app.js +599 -40
- package/dist/server/index.js +0 -1
- package/dist/store.js +0 -1
- package/docs/API_USAGE.md +120 -0
- package/package.json +14 -3
- package/dist/api.js.map +0 -1
- package/dist/cli/commands/ask.js.map +0 -1
- package/dist/cli/commands/clear.js.map +0 -1
- package/dist/cli/commands/help.js.map +0 -1
- package/dist/cli/commands/login.js.map +0 -1
- package/dist/cli/commands/models.js.map +0 -1
- package/dist/cli/commands/serve.js.map +0 -1
- package/dist/cli/commands/status.js.map +0 -1
- package/dist/cli/index.js.map +0 -1
- package/dist/cli/shared.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/core/context.js.map +0 -1
- package/dist/core/models/openai-codex-models.js.map +0 -1
- package/dist/core/providers/http-client.js.map +0 -1
- package/dist/core/providers/openai-codex/chat.js.map +0 -1
- package/dist/core/providers/openai-codex/oauth.js.map +0 -1
- package/dist/core/providers/openai-codex/pkce.js.map +0 -1
- package/dist/core/services/auth-service.js.map +0 -1
- package/dist/core/services/chat-service.js.map +0 -1
- package/dist/core/services/config-service.js.map +0 -1
- package/dist/core/services/model-service.js.map +0 -1
- package/dist/core/store/profile-store.js.map +0 -1
- package/dist/core/store/settings-store.js.map +0 -1
- package/dist/core/types.js.map +0 -1
- package/dist/http.js.map +0 -1
- package/dist/models.js.map +0 -1
- package/dist/oauth.js.map +0 -1
- package/dist/pkce.js.map +0 -1
- package/dist/server/app.js.map +0 -1
- package/dist/server/index.js.map +0 -1
- package/dist/store.js.map +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.3 - 2026-04-24
|
|
4
|
+
|
|
5
|
+
- Added dynamic Codex model discovery from the local `~/.codex/models_cache.json` cache, with static model fallback when the cache is unavailable.
|
|
6
|
+
- Added `azt models --refresh` and a management-page action to re-read the local Codex model list without rebuilding the package.
|
|
7
|
+
- Added runtime version checks against npm, including a prominent update panel in the management UI when a newer version is available.
|
|
8
|
+
- Added 10-minute automatic refresh for quota snapshots and version status in the management UI.
|
|
9
|
+
- Improved quota display so account cards show used and remaining quota percentages clearly.
|
|
10
|
+
- Improved quota syncing so inactive or missing login state does not break runtime refresh.
|
|
11
|
+
- Improved image generation error handling with transient retries and clearer failure details.
|
|
12
|
+
- Preserved response headers when using the curl HTTP fallback so quota metadata can still be captured.
|
|
13
|
+
- Added Vibe Coding / OpenAI-compatible client integration documentation.
|
|
14
|
+
|
package/README.md
CHANGED
|
@@ -1,12 +1,53 @@
|
|
|
1
1
|
# AI Zero Token
|
|
2
2
|
|
|
3
|
+
> 实验工具,不建议直接作为生产环境网关使用。
|
|
4
|
+
|
|
3
5
|
AI Zero Token 是一个本地优先的单用户 AI CLI 和本地网关。
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
它把账号授权能力整理成 OpenAI 风格接口,重点是把图片生成能力也代理出来:
|
|
8
|
+
|
|
9
|
+
- `POST /v1/images/generations`
|
|
10
|
+
- `POST /v1/responses`
|
|
11
|
+
- `POST /v1/chat/completions`
|
|
12
|
+
- `GET /v1/models`
|
|
13
|
+
|
|
14
|
+
很多用户已经有 ChatGPT 之类产品的账号、订阅或可用授权能力,但缺少一个统一、可脚本化、可本地集成的入口。AI Zero Token 要做的,就是把这类已有授权能力整理成一个可直接使用的命令行工具和本地接口。
|
|
15
|
+
|
|
16
|
+
## 这次迭代亮点
|
|
6
17
|
|
|
7
|
-
|
|
18
|
+
- 直接代理 `gpt-image-2`,把图片生成能力暴露成 OpenAI 风格 `images.generations` 接口
|
|
19
|
+
- 启动 `azt start` 后即可获得本地管理页和本地网关,适合脚本、前端和自动化流程接入
|
|
20
|
+
- 支持多账号保存、切换当前账号、查看账号套餐 plan,以及当前账号是否支持生图
|
|
21
|
+
- 模型列表会优先同步本机 `~/.codex/models_cache.json`,不需要每次为新模型重新 build
|
|
22
|
+
- 管理页会每 10 分钟自动同步额度快照和版本状态,并提示当前版本是否可更新
|
|
23
|
+
- `free` 账号会在管理页直接预警,并在网关层明确拦截生图请求
|
|
8
24
|
|
|
9
|
-
|
|
25
|
+
如果你只关心一句话,可以把这个项目理解为:
|
|
26
|
+
|
|
27
|
+
> 一个把账号授权能力代理成本地 OpenAI 风格接口的实验工具,这次重点补齐了 `gpt-image-2` 生图接口。
|
|
28
|
+
|
|
29
|
+
## 一条命令开始
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install -g ai-zero-token
|
|
33
|
+
azt start
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
启动后会打开本地管理页,并暴露 OpenAI 风格接口:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
http://127.0.0.1:8787/v1
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## 界面预览
|
|
43
|
+
|
|
44
|
+

|
|
45
|
+
|
|
46
|
+
## 生图结果预览
|
|
47
|
+
|
|
48
|
+
下图通过 AI Zero Token 本地网关调用 `POST /v1/images/generations` 生成,模型为 `gpt-image-2`。
|
|
49
|
+
|
|
50
|
+

|
|
10
51
|
|
|
11
52
|
## 为什么做这个项目
|
|
12
53
|
|
|
@@ -19,21 +60,37 @@ AI Zero Token 是一个本地优先的单用户 AI CLI 和本地网关。
|
|
|
19
60
|
|
|
20
61
|
AI Zero Token 就是围绕这些问题设计的。
|
|
21
62
|
|
|
63
|
+
## 它能解决什么
|
|
64
|
+
|
|
65
|
+
- 想把 ChatGPT 的图片生成能力接到自己的前端、小工具或脚本里
|
|
66
|
+
- 想先用本地网关验证产品原型,而不是一上来就重做整套后端
|
|
67
|
+
- 想统一成 OpenAI 风格接口,减少接入成本
|
|
68
|
+
- 想研究一条完整的 OAuth -> token -> 本地状态 -> 网关 -> 上游请求链路
|
|
69
|
+
|
|
70
|
+
## 架构概览
|
|
71
|
+
|
|
72
|
+

|
|
73
|
+
|
|
22
74
|
## 当前能做什么
|
|
23
75
|
|
|
76
|
+
- 通过 `POST /v1/images/generations` 代理 `gpt-image-2` 生图请求
|
|
24
77
|
- 通过 OpenAI Codex OAuth 登录
|
|
25
78
|
- 在本地保存 `access_token` 和 `refresh_token`
|
|
79
|
+
- 支持保存多个账号 profile,并手动切换当前生效账号
|
|
26
80
|
- 在 token 过期时自动刷新
|
|
27
|
-
- 通过
|
|
28
|
-
-
|
|
81
|
+
- 通过 `azt start` 一键启动本地 HTTP 网关和管理页面
|
|
82
|
+
- 在管理页面里完成多账号登录、查看账号状态、切换当前账号、切换默认模型、测试接口
|
|
83
|
+
- 模型列表优先读取本机 Codex 最新缓存,并支持在 CLI / 管理页手动同步
|
|
29
84
|
- 暴露 OpenAI 风格接口:
|
|
30
85
|
- `GET /v1/models`
|
|
31
86
|
- `POST /v1/responses`
|
|
32
|
-
|
|
87
|
+
- `POST /v1/chat/completions`
|
|
88
|
+
- `POST /v1/images/generations`
|
|
33
89
|
|
|
34
90
|
## 适合谁用
|
|
35
91
|
|
|
36
92
|
- 想把账号授权能力包装成本地工具的人
|
|
93
|
+
- 想把 `gpt-image-2` 生图接口直接接给脚本、前端或自动化流程的人
|
|
37
94
|
- 想做自己的 AI 网关、AI CLI、AI 桌面端的人
|
|
38
95
|
- 想学习一条完整 OAuth -> token -> CLI -> HTTP API 链路的人
|
|
39
96
|
- 想把 AI 能力接入脚本、前端或自动化流程的人
|
|
@@ -62,7 +119,7 @@ npm install
|
|
|
62
119
|
直接运行源码:
|
|
63
120
|
|
|
64
121
|
```bash
|
|
65
|
-
bun src/cli.ts
|
|
122
|
+
bun src/cli.ts start
|
|
66
123
|
```
|
|
67
124
|
|
|
68
125
|
### 从 npm 安装 CLI
|
|
@@ -76,7 +133,8 @@ npm install -g ai-zero-token
|
|
|
76
133
|
安装后验证:
|
|
77
134
|
|
|
78
135
|
```bash
|
|
79
|
-
azt
|
|
136
|
+
azt start
|
|
137
|
+
azt models --refresh
|
|
80
138
|
```
|
|
81
139
|
|
|
82
140
|
如果你是为了开发、构建、`npm link`、`npm pack` 或准备发布,单独看:
|
|
@@ -85,59 +143,57 @@ azt help
|
|
|
85
143
|
|
|
86
144
|
## 快速开始
|
|
87
145
|
|
|
88
|
-
|
|
146
|
+
启动本地网关和管理页面:
|
|
147
|
+
|
|
89
148
|
```bash
|
|
90
|
-
azt
|
|
149
|
+
azt start
|
|
91
150
|
```
|
|
92
151
|
|
|
93
|
-
|
|
152
|
+
执行后会自动打开管理页,默认地址:
|
|
94
153
|
|
|
95
|
-
```
|
|
96
|
-
|
|
154
|
+
```text
|
|
155
|
+
http://127.0.0.1:8787
|
|
97
156
|
```
|
|
98
157
|
|
|
99
|
-
|
|
158
|
+
默认会监听:
|
|
100
159
|
|
|
101
|
-
```
|
|
102
|
-
|
|
160
|
+
```text
|
|
161
|
+
0.0.0.0:8787
|
|
103
162
|
```
|
|
104
163
|
|
|
105
|
-
|
|
164
|
+
这表示本机可以用 `127.0.0.1:8787` 访问,局域网内其他设备也可以用你的机器 IP 访问,比如 `http://172.26.66.132:8787`。
|
|
106
165
|
|
|
107
|
-
|
|
108
|
-
azt ask "请只回复 OK"
|
|
109
|
-
```
|
|
166
|
+
接下来在管理页里完成这几件事:
|
|
110
167
|
|
|
111
|
-
|
|
168
|
+
- 登录一个或多个 OpenAI Codex 账号
|
|
169
|
+
- 查看当前账号状态和过期时间
|
|
170
|
+
- 在已保存账号之间切换当前使用账号
|
|
171
|
+
- 切换默认模型
|
|
172
|
+
- 直接测试 `models`、`responses`、`chat.completions`
|
|
173
|
+
- 直接测试 `images.generations` 生图接口
|
|
112
174
|
|
|
113
|
-
|
|
114
|
-
|
|
175
|
+
如果你要把它接到自己的客户端,只需要把 Base URL 指向:
|
|
176
|
+
|
|
177
|
+
```text
|
|
178
|
+
http://127.0.0.1:8787/v1
|
|
115
179
|
```
|
|
116
180
|
|
|
117
|
-
|
|
181
|
+
Vibe Coding、OpenAI-compatible SDK 和脚本接入可以参考:
|
|
118
182
|
|
|
119
|
-
|
|
120
|
-
azt serve
|
|
121
|
-
```
|
|
183
|
+
- [API 使用说明](docs/API_USAGE.md)
|
|
122
184
|
|
|
123
185
|
如果你要让本地网页直接从浏览器请求这个网关,现在已经默认开启 CORS。
|
|
124
186
|
|
|
125
187
|
如需限制来源,可以在启动前指定:
|
|
126
188
|
|
|
127
189
|
```bash
|
|
128
|
-
AZT_CORS_ORIGIN=http://127.0.0.1:8124 azt
|
|
190
|
+
AZT_CORS_ORIGIN=http://127.0.0.1:8124 azt start
|
|
129
191
|
```
|
|
130
192
|
|
|
131
193
|
多个来源可用英文逗号分隔:
|
|
132
194
|
|
|
133
195
|
```bash
|
|
134
|
-
AZT_CORS_ORIGIN=http://127.0.0.1:8124,http://localhost:3000 azt
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
清空本地状态:
|
|
138
|
-
|
|
139
|
-
```bash
|
|
140
|
-
azt clear
|
|
196
|
+
AZT_CORS_ORIGIN=http://127.0.0.1:8124,http://localhost:3000 azt start
|
|
141
197
|
```
|
|
142
198
|
|
|
143
199
|
如果你当前还没有全局命令,也可以把上面的 `azt` 临时替换成:
|
|
@@ -146,34 +202,39 @@ azt clear
|
|
|
146
202
|
bun src/cli.ts
|
|
147
203
|
```
|
|
148
204
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
```bash
|
|
152
|
-
bun src/cli.ts login
|
|
153
|
-
```
|
|
205
|
+
例如:`bun src/cli.ts start`
|
|
154
206
|
|
|
155
207
|
## 网关使用说明
|
|
156
208
|
|
|
157
|
-
如果你主要把 AI Zero Token
|
|
209
|
+
如果你主要把 AI Zero Token 当作本地网关来使用,推荐只记住一个命令:
|
|
158
210
|
|
|
159
|
-
### 1.
|
|
211
|
+
### 1. 启动
|
|
160
212
|
|
|
161
213
|
```bash
|
|
162
|
-
azt
|
|
214
|
+
azt start
|
|
163
215
|
```
|
|
164
216
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
### 2. 启动本地网关
|
|
217
|
+
启动后会自动打开管理页。管理页就是默认工作入口,你可以在里面直接:
|
|
168
218
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
219
|
+
- 触发 OpenAI Codex OAuth 登录并新增账号
|
|
220
|
+
- 查看当前账号、已保存账号列表、过期时间、token 摘要
|
|
221
|
+
- 查看账号套餐 plan 和当前账号是否支持生图
|
|
222
|
+
- 在多个已保存账号之间切换当前使用账号
|
|
223
|
+
- 删除单个本地账号,或一键清空全部本地账号
|
|
224
|
+
- 切换默认模型
|
|
225
|
+
- 测试 `models` / `responses` / `chat.completions`
|
|
226
|
+
- 测试 `images.generations`
|
|
172
227
|
|
|
173
|
-
|
|
228
|
+
管理页里邮箱默认脱敏显示,需要手动点击“查看邮箱”才会显示明文。
|
|
174
229
|
|
|
175
230
|
默认监听地址:
|
|
176
231
|
|
|
232
|
+
```text
|
|
233
|
+
http://0.0.0.0:8787
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
本机浏览器访问:
|
|
237
|
+
|
|
177
238
|
```text
|
|
178
239
|
http://127.0.0.1:8787
|
|
179
240
|
```
|
|
@@ -184,27 +245,17 @@ http://127.0.0.1:8787
|
|
|
184
245
|
*
|
|
185
246
|
```
|
|
186
247
|
|
|
187
|
-
###
|
|
188
|
-
|
|
189
|
-
健康检查:
|
|
190
|
-
|
|
191
|
-
```bash
|
|
192
|
-
curl http://127.0.0.1:8787/_gateway/health
|
|
193
|
-
```
|
|
248
|
+
### 2. 把它接到你的客户端
|
|
194
249
|
|
|
195
|
-
|
|
250
|
+
客户端或 SDK 里把 Base URL 改成:
|
|
196
251
|
|
|
197
|
-
```
|
|
198
|
-
|
|
252
|
+
```text
|
|
253
|
+
http://127.0.0.1:8787/v1
|
|
199
254
|
```
|
|
200
255
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
内部模型接口:
|
|
256
|
+
如果客户端必须填写 API Key,可以填任意非空占位值;真正起作用的是本地网关地址。
|
|
204
257
|
|
|
205
|
-
|
|
206
|
-
curl http://127.0.0.1:8787/_gateway/models
|
|
207
|
-
```
|
|
258
|
+
### 3. 查看模型列表
|
|
208
259
|
|
|
209
260
|
OpenAI 风格模型接口:
|
|
210
261
|
|
|
@@ -212,7 +263,7 @@ OpenAI 风格模型接口:
|
|
|
212
263
|
curl http://127.0.0.1:8787/v1/models
|
|
213
264
|
```
|
|
214
265
|
|
|
215
|
-
###
|
|
266
|
+
### 4. 调用对话接口
|
|
216
267
|
|
|
217
268
|
最小请求示例:
|
|
218
269
|
|
|
@@ -230,13 +281,64 @@ curl http://127.0.0.1:8787/v1/responses \
|
|
|
230
281
|
-d '{"model":"gpt-5.4","instructions":"你是一个简洁助手","input":"请只回复 OK"}'
|
|
231
282
|
```
|
|
232
283
|
|
|
284
|
+
兼容 `chat.completions` 的最小请求示例:
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
curl http://127.0.0.1:8787/v1/chat/completions \
|
|
288
|
+
-H "content-type: application/json" \
|
|
289
|
+
-d '{
|
|
290
|
+
"model": "gpt-5.4",
|
|
291
|
+
"messages": [
|
|
292
|
+
{
|
|
293
|
+
"role": "user",
|
|
294
|
+
"content": "请只回复 OK"
|
|
295
|
+
}
|
|
296
|
+
]
|
|
297
|
+
}'
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### 5. 调用生图接口
|
|
301
|
+
|
|
302
|
+
OpenAI 风格 `images.generations` 示例:
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
curl http://127.0.0.1:8787/v1/images/generations \
|
|
306
|
+
-H "content-type: application/json" \
|
|
307
|
+
-d '{
|
|
308
|
+
"model": "gpt-image-2",
|
|
309
|
+
"prompt": "生成一张白底红苹果商品图,构图简洁,光线干净。",
|
|
310
|
+
"size": "1024x1024",
|
|
311
|
+
"quality": "low",
|
|
312
|
+
"response_format": "b64_json"
|
|
313
|
+
}'
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
响应会返回 OpenAI 同类型结构的 `data[].b64_json`。如果你在管理页里测试,这张图片会直接显示预览。
|
|
317
|
+
如果请求里不显式传 `model`,当前默认会使用 `gpt-image-2`。
|
|
318
|
+
|
|
319
|
+
生图能力和账号套餐有关:
|
|
320
|
+
|
|
321
|
+
- `plus` 或更高套餐账号可正常调用 `images.generations`
|
|
322
|
+
- `free` 账号不支持生图,网关会直接返回明确错误,而不是继续请求上游
|
|
323
|
+
- 管理页会显示当前账号的 `plan` 和“生图能力”状态
|
|
324
|
+
- 当当前账号是 `free` 且你选中 `Images` 测试时,“发送请求”按钮会被直接禁用
|
|
325
|
+
|
|
233
326
|
### 6. 当前支持的接口
|
|
234
327
|
|
|
235
328
|
- `GET /_gateway/health`
|
|
236
329
|
- `GET /_gateway/status`
|
|
237
330
|
- `GET /_gateway/models`
|
|
331
|
+
- `POST /_gateway/models/refresh`
|
|
332
|
+
- `GET /_gateway/admin/config`
|
|
333
|
+
- `POST /_gateway/admin/login`
|
|
334
|
+
- `POST /_gateway/admin/logout`
|
|
335
|
+
- `POST /_gateway/admin/profiles/activate`
|
|
336
|
+
- `POST /_gateway/admin/profiles/remove`
|
|
337
|
+
- `PUT /_gateway/admin/settings`
|
|
238
338
|
- `GET /v1/models`
|
|
239
339
|
- `POST /v1/responses`
|
|
340
|
+
- `POST /v1/chat/completions`
|
|
341
|
+
- `POST /v1/images/generations`
|
|
240
342
|
|
|
241
343
|
### 7. 当前支持的主要参数
|
|
242
344
|
|
|
@@ -246,14 +348,78 @@ curl http://127.0.0.1:8787/v1/responses \
|
|
|
246
348
|
- `input`
|
|
247
349
|
- `instructions`
|
|
248
350
|
- `stream`
|
|
351
|
+
- `tools`
|
|
352
|
+
- `tool_choice`
|
|
353
|
+
- `include`
|
|
354
|
+
- `text`
|
|
355
|
+
- `store`
|
|
356
|
+
- `parallel_tool_calls`
|
|
357
|
+
- `experimental_codex.body`
|
|
358
|
+
- `experimental_codex.allow_unknown_model`
|
|
359
|
+
- `experimental_codex.include_raw`
|
|
360
|
+
|
|
361
|
+
`POST /v1/chat/completions` 当前主要支持:
|
|
362
|
+
|
|
363
|
+
- `model`
|
|
364
|
+
- `messages`
|
|
365
|
+
- `tools`
|
|
366
|
+
- `tool_choice`
|
|
367
|
+
- `response_format`
|
|
368
|
+
- `temperature`
|
|
369
|
+
- `top_p`
|
|
370
|
+
- `presence_penalty`
|
|
371
|
+
- `frequency_penalty`
|
|
372
|
+
- `metadata`
|
|
373
|
+
- `stop`
|
|
374
|
+
- `store`
|
|
375
|
+
- `parallel_tool_calls`
|
|
376
|
+
|
|
377
|
+
`POST /v1/images/generations` 当前主要支持:
|
|
378
|
+
|
|
379
|
+
- `prompt`
|
|
380
|
+
- `model`
|
|
381
|
+
- `n`
|
|
382
|
+
- `size`
|
|
383
|
+
- `quality`
|
|
384
|
+
- `background`
|
|
385
|
+
- `output_format`
|
|
386
|
+
- `output_compression`
|
|
387
|
+
- `moderation`
|
|
388
|
+
- `response_format`
|
|
389
|
+
- `user`
|
|
249
390
|
|
|
250
391
|
### 8. 当前限制
|
|
251
392
|
|
|
252
393
|
- `stream=true` 目前只识别,不返回真实流式结果
|
|
253
394
|
- 还没有完整覆盖 OpenAI Responses API 的全部字段
|
|
254
|
-
-
|
|
395
|
+
- `chat.completions` 暂不支持 `n > 1`
|
|
396
|
+
- `images.generations` 暂不支持 `n > 1`
|
|
397
|
+
- `images.generations` 当前只返回 `b64_json`,暂不支持托管图片 `url`
|
|
398
|
+
- `images.generations` 当前只透传 GPT Image 路径,不兼容 DALL·E 专有参数
|
|
399
|
+
- `images.generations` 对账号套餐有要求;`free` 账号会被网关直接拦截并返回“不支持图片生成”
|
|
255
400
|
- 网关当前默认面向本地单用户使用
|
|
256
401
|
|
|
402
|
+
## 兼容说明
|
|
403
|
+
|
|
404
|
+
代码里仍然保留了 `login`、`status`、`models`、`ask`、`serve`、`clear` 等 CLI 命令,主要用于调试、兼容和后续扩展。
|
|
405
|
+
|
|
406
|
+
README 不再把这些命令作为推荐使用方式。默认使用路径就是:
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
azt start
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## 交流与反馈
|
|
413
|
+
|
|
414
|
+
如果你在使用过程中遇到安装问题、账号切换问题、生图异常,或者想交流自己的接入场景,可以通过下面两种方式联系我:
|
|
415
|
+
|
|
416
|
+
- GitHub Issues: [https://github.com/fchangjun/AI-Zero-Token/issues](https://github.com/fchangjun/AI-Zero-Token/issues)
|
|
417
|
+
- 微信交流:先加我微信,备注 `AI Zero Token`,我会再拉你进交流群
|
|
418
|
+
|
|
419
|
+
如果你已经把二维码放到仓库里,可以直接查看:
|
|
420
|
+
|
|
421
|
+

|
|
422
|
+
|
|
257
423
|
## 本地状态
|
|
258
424
|
|
|
259
425
|
项目会在仓库目录下写入:
|
|
@@ -263,7 +429,7 @@ curl http://127.0.0.1:8787/v1/responses \
|
|
|
263
429
|
|
|
264
430
|
它们分别用于保存:
|
|
265
431
|
|
|
266
|
-
- OAuth
|
|
432
|
+
- OAuth 认证信息和多个本地账号 profile
|
|
267
433
|
- 默认模型和服务配置
|
|
268
434
|
|
|
269
435
|
## 项目结构
|
package/dist/api.js
CHANGED
package/dist/cli/commands/ask.js
CHANGED
|
@@ -1,15 +1,141 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
2
4
|
import { createGatewayContext } from "../../core/context.js";
|
|
3
5
|
import { parseAskArgs } from "../shared.js";
|
|
6
|
+
function collectEncodedArtifacts(value, trail = [], items = []) {
|
|
7
|
+
if (Array.isArray(value)) {
|
|
8
|
+
value.forEach((item, index) => {
|
|
9
|
+
collectEncodedArtifacts(item, [...trail, String(index)], items);
|
|
10
|
+
});
|
|
11
|
+
return items;
|
|
12
|
+
}
|
|
13
|
+
if (!value || typeof value !== "object") {
|
|
14
|
+
return items;
|
|
15
|
+
}
|
|
16
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
17
|
+
const nextTrail = [...trail, key];
|
|
18
|
+
if (typeof nested === "string" && /(?:^|_)image_b64$/i.test(key) && /^[A-Za-z0-9+/=\r\n]+$/.test(nested) && nested.length > 100) {
|
|
19
|
+
items.push({
|
|
20
|
+
key,
|
|
21
|
+
path: nextTrail.join("."),
|
|
22
|
+
value: nested.replace(/\s+/g, "")
|
|
23
|
+
});
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
collectEncodedArtifacts(nested, nextTrail, items);
|
|
27
|
+
}
|
|
28
|
+
return items;
|
|
29
|
+
}
|
|
30
|
+
function detectImageExtension(buffer) {
|
|
31
|
+
if (buffer.length >= 8 && buffer[0] === 137 && buffer[1] === 80 && buffer[2] === 78 && buffer[3] === 71) {
|
|
32
|
+
return "png";
|
|
33
|
+
}
|
|
34
|
+
if (buffer.length >= 3 && buffer[0] === 255 && buffer[1] === 216 && buffer[2] === 255) {
|
|
35
|
+
return "jpg";
|
|
36
|
+
}
|
|
37
|
+
if (buffer.length >= 12 && buffer.subarray(0, 4).toString("ascii") === "RIFF" && buffer.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
38
|
+
return "webp";
|
|
39
|
+
}
|
|
40
|
+
if (buffer.length >= 4 && buffer.subarray(0, 4).toString("ascii") === "GIF8") {
|
|
41
|
+
return "gif";
|
|
42
|
+
}
|
|
43
|
+
return "bin";
|
|
44
|
+
}
|
|
45
|
+
async function writeEncodedArtifacts(raw, outputDir) {
|
|
46
|
+
const encodedArtifacts = collectEncodedArtifacts(raw);
|
|
47
|
+
if (encodedArtifacts.length === 0) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
await mkdir(outputDir, { recursive: true });
|
|
51
|
+
const writtenFiles = [];
|
|
52
|
+
for (const [index, artifact] of encodedArtifacts.entries()) {
|
|
53
|
+
const buffer = Buffer.from(artifact.value, "base64");
|
|
54
|
+
const extension = detectImageExtension(buffer);
|
|
55
|
+
const safeName = artifact.path.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
56
|
+
const filename = `${String(index + 1).padStart(2, "0")}-${safeName}.${extension}`;
|
|
57
|
+
const target = path.join(outputDir, filename);
|
|
58
|
+
await writeFile(target, buffer);
|
|
59
|
+
writtenFiles.push(target);
|
|
60
|
+
}
|
|
61
|
+
return writtenFiles;
|
|
62
|
+
}
|
|
4
63
|
async function runAskCommand(args) {
|
|
5
|
-
const {
|
|
6
|
-
|
|
7
|
-
|
|
64
|
+
const {
|
|
65
|
+
model,
|
|
66
|
+
prompt,
|
|
67
|
+
payloadFile,
|
|
68
|
+
dumpRawFile,
|
|
69
|
+
writeArtifactsDir,
|
|
70
|
+
printRaw,
|
|
71
|
+
allowUnknownModel
|
|
72
|
+
} = parseAskArgs(args);
|
|
73
|
+
let codexBody;
|
|
74
|
+
if (payloadFile) {
|
|
75
|
+
const content = await readFile(payloadFile, "utf8");
|
|
76
|
+
const parsed = JSON.parse(content);
|
|
77
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
78
|
+
throw new Error(`payload \u6587\u4EF6\u5FC5\u987B\u662F JSON object: ${payloadFile}`);
|
|
79
|
+
}
|
|
80
|
+
codexBody = parsed;
|
|
81
|
+
}
|
|
82
|
+
if (!prompt && !codexBody) {
|
|
83
|
+
throw new Error('ask \u9700\u8981\u4E00\u4E2A prompt\uFF0C\u4F8B\u5982 azt ask "\u4F60\u597D"\uFF0C\u6216\u8005\u901A\u8FC7 --payload-file \u4F20\u5B9E\u9A8C\u8BF7\u6C42\u4F53');
|
|
8
84
|
}
|
|
9
85
|
const ctx = createGatewayContext();
|
|
10
|
-
|
|
86
|
+
const result = await ctx.chatService.chat({
|
|
87
|
+
model,
|
|
88
|
+
input: prompt || void 0,
|
|
89
|
+
experimental: {
|
|
90
|
+
codexBody,
|
|
91
|
+
allowUnknownModel: allowUnknownModel || Boolean(codexBody)
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
console.log(`provider: ${result.provider}`);
|
|
95
|
+
console.log(`model: ${result.model}`);
|
|
96
|
+
console.log("\u6A21\u578B\u56DE\u590D:");
|
|
97
|
+
console.log(result.text || "(\u8FD4\u56DE\u6210\u529F\uFF0C\u4F46\u6CA1\u6709\u89E3\u6790\u51FA output_text)");
|
|
98
|
+
if (result.artifacts.length > 0) {
|
|
99
|
+
console.log("\u5019\u9009\u4EA7\u7269\u5F15\u7528:");
|
|
100
|
+
for (const artifact of result.artifacts) {
|
|
101
|
+
console.log(`- [${artifact.source}] ${artifact.path} = ${artifact.value}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (writeArtifactsDir) {
|
|
105
|
+
const writtenFiles = await writeEncodedArtifacts(result.raw, writeArtifactsDir);
|
|
106
|
+
if (writtenFiles.length === 0) {
|
|
107
|
+
console.log("\u672A\u53D1\u73B0\u53EF\u5199\u51FA\u7684 base64 \u56FE\u7247\u4EA7\u7269\u3002");
|
|
108
|
+
} else {
|
|
109
|
+
console.log("\u56FE\u7247\u4EA7\u7269\u5DF2\u5199\u5165:");
|
|
110
|
+
for (const file of writtenFiles) {
|
|
111
|
+
console.log(`- ${file}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (dumpRawFile) {
|
|
116
|
+
await writeFile(
|
|
117
|
+
dumpRawFile,
|
|
118
|
+
`${JSON.stringify(
|
|
119
|
+
{
|
|
120
|
+
provider: result.provider,
|
|
121
|
+
model: result.model,
|
|
122
|
+
text: result.text,
|
|
123
|
+
artifacts: result.artifacts,
|
|
124
|
+
raw: result.raw
|
|
125
|
+
},
|
|
126
|
+
null,
|
|
127
|
+
2
|
|
128
|
+
)}
|
|
129
|
+
`,
|
|
130
|
+
"utf8"
|
|
131
|
+
);
|
|
132
|
+
console.log(`raw \u5DF2\u5199\u5165: ${dumpRawFile}`);
|
|
133
|
+
}
|
|
134
|
+
if (printRaw) {
|
|
135
|
+
console.log("raw:");
|
|
136
|
+
console.log(JSON.stringify(result.raw, null, 2));
|
|
137
|
+
}
|
|
11
138
|
}
|
|
12
139
|
export {
|
|
13
140
|
runAskCommand
|
|
14
141
|
};
|
|
15
|
-
//# sourceMappingURL=ask.js.map
|
|
@@ -2,20 +2,27 @@
|
|
|
2
2
|
function printHelp() {
|
|
3
3
|
console.log(`\u7528\u6CD5:
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
azt login
|
|
6
|
+
azt models
|
|
7
|
+
azt models --refresh
|
|
8
|
+
azt status
|
|
9
|
+
azt ask "\u4F60\u597D\uFF0C\u8BF7\u7B80\u5355\u4ECB\u7ECD\u4E00\u4E0B\u81EA\u5DF1"
|
|
10
|
+
azt ask --model gpt-5.3-codex "\u4F60\u597D"
|
|
11
|
+
azt ask --payload-file ./codex-body.json --dump-raw ./codex-raw.json
|
|
12
|
+
azt ask --payload-file ./codex-body.json --write-artifacts-dir ./artifacts
|
|
13
|
+
azt start
|
|
14
|
+
azt serve
|
|
15
|
+
azt clear
|
|
12
16
|
|
|
13
17
|
\u8BF4\u660E:
|
|
14
18
|
|
|
15
|
-
login \u8D70\u771F\u5B9E OpenAI Codex OAuth\uFF0C\u4FDD\u5B58
|
|
16
|
-
models \u67E5\u770B\
|
|
17
|
-
status \u67E5\u770B\u5F53\u524D demo \
|
|
19
|
+
login \u8D70\u771F\u5B9E OpenAI Codex OAuth\uFF0C\u65B0\u589E\u5E76\u4FDD\u5B58\u4E00\u4E2A\u8D26\u53F7 profile
|
|
20
|
+
models \u67E5\u770B\u5F53\u524D\u53EF\u7528\u6A21\u578B\u5217\u8868\uFF1B\u4F18\u5148\u8BFB\u53D6 ~/.codex/models_cache.json\uFF0C--refresh \u53EF\u624B\u52A8\u91CD\u8BFB
|
|
21
|
+
status \u67E5\u770B\u5F53\u524D demo \u5F53\u524D\u6FC0\u6D3B\u8D26\u53F7\u3001\u8D26\u53F7\u6570\u91CF\u548C\u8FC7\u671F\u65F6\u95F4
|
|
18
22
|
ask \u7528\u4FDD\u5B58\u7684 token \u8C03\u771F\u5B9E Codex Responses API
|
|
23
|
+
\u5B9E\u9A8C\u6A21\u5F0F\u53EF\u7528 --payload-file \u900F\u4F20\u989D\u5916\u8BF7\u6C42\u4F53\uFF0C\u914D\u5408 --dump-raw / --print-raw \u89C2\u5BDF SSE \u539F\u59CB\u4E8B\u4EF6
|
|
24
|
+
\u5982\u54CD\u5E94\u91CC\u542B image_b64 / partial_image_b64\uFF0C\u53EF\u7528 --write-artifacts-dir \u76F4\u63A5\u5199\u6210\u56FE\u7247\u6587\u4EF6
|
|
25
|
+
start \u542F\u52A8\u672C\u5730 HTTP \u7F51\u5173\u5E76\u81EA\u52A8\u6253\u5F00\u7BA1\u7406\u9875\u9762
|
|
19
26
|
serve \u542F\u52A8\u672C\u5730 HTTP \u7F51\u5173
|
|
20
27
|
clear \u6E05\u7A7A demo \u7684\u672C\u5730\u72B6\u6001
|
|
21
28
|
`);
|
|
@@ -23,4 +30,3 @@ function printHelp() {
|
|
|
23
30
|
export {
|
|
24
31
|
printHelp
|
|
25
32
|
};
|
|
26
|
-
//# sourceMappingURL=help.js.map
|