ai-zero-token 1.0.0 → 1.0.2

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 (67) hide show
  1. package/README.md +235 -58
  2. package/dist/api.js +0 -1
  3. package/dist/cli/commands/ask.js +131 -5
  4. package/dist/cli/commands/clear.js +0 -1
  5. package/dist/cli/commands/help.js +15 -10
  6. package/dist/cli/commands/login.js +0 -1
  7. package/dist/cli/commands/models.js +0 -1
  8. package/dist/cli/commands/serve.js +42 -4
  9. package/dist/cli/commands/start.js +10 -0
  10. package/dist/cli/commands/status.js +1 -1
  11. package/dist/cli/index.js +4 -1
  12. package/dist/cli/shared.js +57 -6
  13. package/dist/cli.js +0 -1
  14. package/dist/core/context.js +7 -2
  15. package/dist/core/models/openai-codex-models.js +0 -1
  16. package/dist/core/providers/http-client.js +97 -9
  17. package/dist/core/providers/openai-codex/chat.js +217 -24
  18. package/dist/core/providers/openai-codex/oauth.js +15 -4
  19. package/dist/core/providers/openai-codex/pkce.js +0 -1
  20. package/dist/core/services/auth-service.js +89 -16
  21. package/dist/core/services/chat-service.js +24 -14
  22. package/dist/core/services/config-service.js +0 -1
  23. package/dist/core/services/image-service.js +360 -0
  24. package/dist/core/services/model-service.js +4 -2
  25. package/dist/core/store/profile-store.js +79 -6
  26. package/dist/core/store/settings-store.js +1 -2
  27. package/dist/core/types.js +0 -1
  28. package/dist/http.js +0 -1
  29. package/dist/models.js +0 -1
  30. package/dist/oauth.js +0 -1
  31. package/dist/pkce.js +0 -1
  32. package/dist/server/admin-page.js +2615 -0
  33. package/dist/server/app.js +566 -39
  34. package/dist/server/index.js +13 -3
  35. package/dist/store.js +0 -1
  36. package/package.json +14 -6
  37. package/dist/api.js.map +0 -1
  38. package/dist/cli/commands/ask.js.map +0 -1
  39. package/dist/cli/commands/clear.js.map +0 -1
  40. package/dist/cli/commands/help.js.map +0 -1
  41. package/dist/cli/commands/login.js.map +0 -1
  42. package/dist/cli/commands/models.js.map +0 -1
  43. package/dist/cli/commands/serve.js.map +0 -1
  44. package/dist/cli/commands/status.js.map +0 -1
  45. package/dist/cli/index.js.map +0 -1
  46. package/dist/cli/shared.js.map +0 -1
  47. package/dist/cli.js.map +0 -1
  48. package/dist/core/context.js.map +0 -1
  49. package/dist/core/models/openai-codex-models.js.map +0 -1
  50. package/dist/core/providers/http-client.js.map +0 -1
  51. package/dist/core/providers/openai-codex/chat.js.map +0 -1
  52. package/dist/core/providers/openai-codex/oauth.js.map +0 -1
  53. package/dist/core/providers/openai-codex/pkce.js.map +0 -1
  54. package/dist/core/services/auth-service.js.map +0 -1
  55. package/dist/core/services/chat-service.js.map +0 -1
  56. package/dist/core/services/config-service.js.map +0 -1
  57. package/dist/core/services/model-service.js.map +0 -1
  58. package/dist/core/store/profile-store.js.map +0 -1
  59. package/dist/core/store/settings-store.js.map +0 -1
  60. package/dist/core/types.js.map +0 -1
  61. package/dist/http.js.map +0 -1
  62. package/dist/models.js.map +0 -1
  63. package/dist/oauth.js.map +0 -1
  64. package/dist/pkce.js.map +0 -1
  65. package/dist/server/app.js.map +0 -1
  66. package/dist/server/index.js.map +0 -1
  67. package/dist/store.js.map +0 -1
package/README.md CHANGED
@@ -1,12 +1,51 @@
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
+ ## 这次迭代亮点
17
+
18
+ - 直接代理 `gpt-image-2`,把图片生成能力暴露成 OpenAI 风格 `images.generations` 接口
19
+ - 启动 `azt start` 后即可获得本地管理页和本地网关,适合脚本、前端和自动化流程接入
20
+ - 支持多账号保存、切换当前账号、查看账号套餐 plan,以及当前账号是否支持生图
21
+ - `free` 账号会在管理页直接预警,并在网关层明确拦截生图请求
22
+
23
+ 如果你只关心一句话,可以把这个项目理解为:
24
+
25
+ > 一个把账号授权能力代理成本地 OpenAI 风格接口的实验工具,这次重点补齐了 `gpt-image-2` 生图接口。
26
+
27
+ ## 一条命令开始
28
+
29
+ ```bash
30
+ npm install -g ai-zero-token
31
+ azt start
32
+ ```
33
+
34
+ 启动后会打开本地管理页,并暴露 OpenAI 风格接口:
35
+
36
+ ```text
37
+ http://127.0.0.1:8787/v1
38
+ ```
39
+
40
+ ## 界面预览
41
+
42
+ ![AI Zero Token 管理页预览](docs/images/admin-dashboard.jpg)
43
+
44
+ ## 生图结果预览
6
45
 
7
- 让个人用户优先复用自己已经拥有的账号订阅和账号授权能力,在本地直接接入高质量 LLM,而不是再单独为优质 API 额度和接口调用链付费。
46
+ 下图通过 AI Zero Token 本地网关调用 `POST /v1/images/generations` 生成,模型为 `gpt-image-2`。
8
47
 
9
- 很多用户已经有 ChatGPT、Claude、Gemini 之类产品的账号、订阅或可用授权能力,但缺少一个统一、可脚本化、可本地集成的入口。AI Zero Token 要做的,就是把这类已有授权能力整理成一个可直接使用的命令行工具和本地接口。
48
+ ![AI Zero Token gpt-image-2 生图结果预览](docs/images/gpt-image-2-preview.png)
10
49
 
11
50
  ## 为什么做这个项目
12
51
 
@@ -19,21 +58,36 @@ AI Zero Token 是一个本地优先的单用户 AI CLI 和本地网关。
19
58
 
20
59
  AI Zero Token 就是围绕这些问题设计的。
21
60
 
61
+ ## 它能解决什么
62
+
63
+ - 想把 ChatGPT 的图片生成能力接到自己的前端、小工具或脚本里
64
+ - 想先用本地网关验证产品原型,而不是一上来就重做整套后端
65
+ - 想统一成 OpenAI 风格接口,减少接入成本
66
+ - 想研究一条完整的 OAuth -> token -> 本地状态 -> 网关 -> 上游请求链路
67
+
68
+ ## 架构概览
69
+
70
+ ![AI Zero Token 实验工具架构图](docs/images/architecture-diagram.png)
71
+
22
72
  ## 当前能做什么
23
73
 
74
+ - 通过 `POST /v1/images/generations` 代理 `gpt-image-2` 生图请求
24
75
  - 通过 OpenAI Codex OAuth 登录
25
76
  - 在本地保存 `access_token` 和 `refresh_token`
77
+ - 支持保存多个账号 profile,并手动切换当前生效账号
26
78
  - 在 token 过期时自动刷新
27
- - 通过 CLI 发起真实模型请求
28
- - 启动本地 HTTP 网关
79
+ - 通过 `azt start` 一键启动本地 HTTP 网关和管理页面
80
+ - 在管理页面里完成多账号登录、查看账号状态、切换当前账号、切换默认模型、测试接口
29
81
  - 暴露 OpenAI 风格接口:
30
82
  - `GET /v1/models`
31
83
  - `POST /v1/responses`
32
-
84
+ - `POST /v1/chat/completions`
85
+ - `POST /v1/images/generations`
33
86
 
34
87
  ## 适合谁用
35
88
 
36
89
  - 想把账号授权能力包装成本地工具的人
90
+ - 想把 `gpt-image-2` 生图接口直接接给脚本、前端或自动化流程的人
37
91
  - 想做自己的 AI 网关、AI CLI、AI 桌面端的人
38
92
  - 想学习一条完整 OAuth -> token -> CLI -> HTTP API 链路的人
39
93
  - 想把 AI 能力接入脚本、前端或自动化流程的人
@@ -62,7 +116,7 @@ npm install
62
116
  直接运行源码:
63
117
 
64
118
  ```bash
65
- bun src/cli.ts help
119
+ bun src/cli.ts start
66
120
  ```
67
121
 
68
122
  ### 从 npm 安装 CLI
@@ -76,7 +130,7 @@ npm install -g ai-zero-token
76
130
  安装后验证:
77
131
 
78
132
  ```bash
79
- azt help
133
+ azt start
80
134
  ```
81
135
 
82
136
  如果你是为了开发、构建、`npm link`、`npm pack` 或准备发布,单独看:
@@ -85,45 +139,53 @@ azt help
85
139
 
86
140
  ## 快速开始
87
141
 
88
- 登录:
142
+ 启动本地网关和管理页面:
143
+
89
144
  ```bash
90
- azt login
145
+ azt start
91
146
  ```
92
147
 
93
- 查看当前状态:
148
+ 执行后会自动打开管理页,默认地址:
94
149
 
95
- ```bash
96
- azt status
150
+ ```text
151
+ http://127.0.0.1:8787
97
152
  ```
98
153
 
99
- 查看支持的模型:
154
+ 默认会监听:
100
155
 
101
- ```bash
102
- azt models
156
+ ```text
157
+ 0.0.0.0:8787
103
158
  ```
104
159
 
105
- 发起一次对话:
160
+ 这表示本机可以用 `127.0.0.1:8787` 访问,局域网内其他设备也可以用你的机器 IP 访问,比如 `http://172.26.66.132:8787`。
106
161
 
107
- ```bash
108
- azt ask "请只回复 OK"
109
- ```
162
+ 接下来在管理页里完成这几件事:
110
163
 
111
- 指定模型发起对话:
164
+ - 登录一个或多个 OpenAI Codex 账号
165
+ - 查看当前账号状态和过期时间
166
+ - 在已保存账号之间切换当前使用账号
167
+ - 切换默认模型
168
+ - 直接测试 `models`、`responses`、`chat.completions`
169
+ - 直接测试 `images.generations` 生图接口
112
170
 
113
- ```bash
114
- azt ask --model gpt-5.3-codex "请只回复 OK"
171
+ 如果你要把它接到自己的客户端,只需要把 Base URL 指向:
172
+
173
+ ```text
174
+ http://127.0.0.1:8787/v1
115
175
  ```
116
176
 
117
- 启动本地网关:
177
+ 如果你要让本地网页直接从浏览器请求这个网关,现在已经默认开启 CORS。
178
+
179
+ 如需限制来源,可以在启动前指定:
118
180
 
119
181
  ```bash
120
- azt serve
182
+ AZT_CORS_ORIGIN=http://127.0.0.1:8124 azt start
121
183
  ```
122
184
 
123
- 清空本地状态:
185
+ 多个来源可用英文逗号分隔:
124
186
 
125
187
  ```bash
126
- azt clear
188
+ AZT_CORS_ORIGIN=http://127.0.0.1:8124,http://localhost:3000 azt start
127
189
  ```
128
190
 
129
191
  如果你当前还没有全局命令,也可以把上面的 `azt` 临时替换成:
@@ -132,67 +194,68 @@ azt clear
132
194
  bun src/cli.ts
133
195
  ```
134
196
 
135
- 例如:
136
-
137
- ```bash
138
- bun src/cli.ts login
139
- ```
197
+ 例如:`bun src/cli.ts start`
140
198
 
141
199
  ## 网关使用说明
142
200
 
143
- 如果你主要把 AI Zero Token 当作本地网关来使用,建议按下面的顺序操作。
201
+ 如果你主要把 AI Zero Token 当作本地网关来使用,推荐只记住一个命令:
144
202
 
145
- ### 1. 先完成登录
203
+ ### 1. 启动
146
204
 
147
205
  ```bash
148
- azt login
206
+ azt start
149
207
  ```
150
208
 
151
- 这一步会打开浏览器,完成 OpenAI Codex OAuth 登录,并把可用 token 保存到本地。
209
+ 启动后会自动打开管理页。管理页就是默认工作入口,你可以在里面直接:
152
210
 
153
- ### 2. 启动本地网关
211
+ - 触发 OpenAI Codex OAuth 登录并新增账号
212
+ - 查看当前账号、已保存账号列表、过期时间、token 摘要
213
+ - 查看账号套餐 plan 和当前账号是否支持生图
214
+ - 在多个已保存账号之间切换当前使用账号
215
+ - 删除单个本地账号,或一键清空全部本地账号
216
+ - 切换默认模型
217
+ - 测试 `models` / `responses` / `chat.completions`
218
+ - 测试 `images.generations`
154
219
 
155
- ```bash
156
- azt serve
157
- ```
158
-
159
- CLI 一旦执行 `serve`,就会进入本地网关模式。
220
+ 管理页里邮箱默认脱敏显示,需要手动点击“查看邮箱”才会显示明文。
160
221
 
161
222
  默认监听地址:
162
223
 
163
224
  ```text
164
- http://127.0.0.1:8787
225
+ http://0.0.0.0:8787
165
226
  ```
166
227
 
167
- ### 3. 先确认网关状态
228
+ 本机浏览器访问:
168
229
 
169
- 健康检查:
170
-
171
- ```bash
172
- curl http://127.0.0.1:8787/_gateway/health
230
+ ```text
231
+ http://127.0.0.1:8787
173
232
  ```
174
233
 
175
- 查看当前网关状态:
234
+ 默认 CORS 来源:
176
235
 
177
- ```bash
178
- curl http://127.0.0.1:8787/_gateway/status
236
+ ```text
237
+ *
179
238
  ```
180
239
 
181
- ### 4. 查看模型列表
240
+ ### 2. 把它接到你的客户端
182
241
 
183
- 内部模型接口:
242
+ 客户端或 SDK 里把 Base URL 改成:
184
243
 
185
- ```bash
186
- curl http://127.0.0.1:8787/_gateway/models
244
+ ```text
245
+ http://127.0.0.1:8787/v1
187
246
  ```
188
247
 
248
+ 如果客户端必须填写 API Key,可以填任意非空占位值;真正起作用的是本地网关地址。
249
+
250
+ ### 3. 查看模型列表
251
+
189
252
  OpenAI 风格模型接口:
190
253
 
191
254
  ```bash
192
255
  curl http://127.0.0.1:8787/v1/models
193
256
  ```
194
257
 
195
- ### 5. 调用对话接口
258
+ ### 4. 调用对话接口
196
259
 
197
260
  最小请求示例:
198
261
 
@@ -210,13 +273,63 @@ curl http://127.0.0.1:8787/v1/responses \
210
273
  -d '{"model":"gpt-5.4","instructions":"你是一个简洁助手","input":"请只回复 OK"}'
211
274
  ```
212
275
 
276
+ 兼容 `chat.completions` 的最小请求示例:
277
+
278
+ ```bash
279
+ curl http://127.0.0.1:8787/v1/chat/completions \
280
+ -H "content-type: application/json" \
281
+ -d '{
282
+ "model": "gpt-5.4",
283
+ "messages": [
284
+ {
285
+ "role": "user",
286
+ "content": "请只回复 OK"
287
+ }
288
+ ]
289
+ }'
290
+ ```
291
+
292
+ ### 5. 调用生图接口
293
+
294
+ OpenAI 风格 `images.generations` 示例:
295
+
296
+ ```bash
297
+ curl http://127.0.0.1:8787/v1/images/generations \
298
+ -H "content-type: application/json" \
299
+ -d '{
300
+ "model": "gpt-image-2",
301
+ "prompt": "生成一张白底红苹果商品图,构图简洁,光线干净。",
302
+ "size": "1024x1024",
303
+ "quality": "low",
304
+ "response_format": "b64_json"
305
+ }'
306
+ ```
307
+
308
+ 响应会返回 OpenAI 同类型结构的 `data[].b64_json`。如果你在管理页里测试,这张图片会直接显示预览。
309
+ 如果请求里不显式传 `model`,当前默认会使用 `gpt-image-2`。
310
+
311
+ 生图能力和账号套餐有关:
312
+
313
+ - `plus` 或更高套餐账号可正常调用 `images.generations`
314
+ - `free` 账号不支持生图,网关会直接返回明确错误,而不是继续请求上游
315
+ - 管理页会显示当前账号的 `plan` 和“生图能力”状态
316
+ - 当当前账号是 `free` 且你选中 `Images` 测试时,“发送请求”按钮会被直接禁用
317
+
213
318
  ### 6. 当前支持的接口
214
319
 
215
320
  - `GET /_gateway/health`
216
321
  - `GET /_gateway/status`
217
322
  - `GET /_gateway/models`
323
+ - `GET /_gateway/admin/config`
324
+ - `POST /_gateway/admin/login`
325
+ - `POST /_gateway/admin/logout`
326
+ - `POST /_gateway/admin/profiles/activate`
327
+ - `POST /_gateway/admin/profiles/remove`
328
+ - `PUT /_gateway/admin/settings`
218
329
  - `GET /v1/models`
219
330
  - `POST /v1/responses`
331
+ - `POST /v1/chat/completions`
332
+ - `POST /v1/images/generations`
220
333
 
221
334
  ### 7. 当前支持的主要参数
222
335
 
@@ -226,14 +339,78 @@ curl http://127.0.0.1:8787/v1/responses \
226
339
  - `input`
227
340
  - `instructions`
228
341
  - `stream`
342
+ - `tools`
343
+ - `tool_choice`
344
+ - `include`
345
+ - `text`
346
+ - `store`
347
+ - `parallel_tool_calls`
348
+ - `experimental_codex.body`
349
+ - `experimental_codex.allow_unknown_model`
350
+ - `experimental_codex.include_raw`
351
+
352
+ `POST /v1/chat/completions` 当前主要支持:
353
+
354
+ - `model`
355
+ - `messages`
356
+ - `tools`
357
+ - `tool_choice`
358
+ - `response_format`
359
+ - `temperature`
360
+ - `top_p`
361
+ - `presence_penalty`
362
+ - `frequency_penalty`
363
+ - `metadata`
364
+ - `stop`
365
+ - `store`
366
+ - `parallel_tool_calls`
367
+
368
+ `POST /v1/images/generations` 当前主要支持:
369
+
370
+ - `prompt`
371
+ - `model`
372
+ - `n`
373
+ - `size`
374
+ - `quality`
375
+ - `background`
376
+ - `output_format`
377
+ - `output_compression`
378
+ - `moderation`
379
+ - `response_format`
380
+ - `user`
229
381
 
230
382
  ### 8. 当前限制
231
383
 
232
384
  - `stream=true` 目前只识别,不返回真实流式结果
233
385
  - 还没有完整覆盖 OpenAI Responses API 的全部字段
234
- - 还没有实现 `/v1/chat/completions`
386
+ - `chat.completions` 暂不支持 `n > 1`
387
+ - `images.generations` 暂不支持 `n > 1`
388
+ - `images.generations` 当前只返回 `b64_json`,暂不支持托管图片 `url`
389
+ - `images.generations` 当前只透传 GPT Image 路径,不兼容 DALL·E 专有参数
390
+ - `images.generations` 对账号套餐有要求;`free` 账号会被网关直接拦截并返回“不支持图片生成”
235
391
  - 网关当前默认面向本地单用户使用
236
392
 
393
+ ## 兼容说明
394
+
395
+ 代码里仍然保留了 `login`、`status`、`models`、`ask`、`serve`、`clear` 等 CLI 命令,主要用于调试、兼容和后续扩展。
396
+
397
+ README 不再把这些命令作为推荐使用方式。默认使用路径就是:
398
+
399
+ ```bash
400
+ azt start
401
+ ```
402
+
403
+ ## 交流与反馈
404
+
405
+ 如果你在使用过程中遇到安装问题、账号切换问题、生图异常,或者想交流自己的接入场景,可以通过下面两种方式联系我:
406
+
407
+ - GitHub Issues: [https://github.com/fchangjun/AI-Zero-Token/issues](https://github.com/fchangjun/AI-Zero-Token/issues)
408
+ - 微信交流:先加我微信,备注 `AI Zero Token`,我会再拉你进交流群
409
+
410
+ 如果你已经把二维码放到仓库里,可以直接查看:
411
+
412
+ ![AI Zero Token 微信联系二维码](docs/images/wechat-contact.png)
413
+
237
414
  ## 本地状态
238
415
 
239
416
  项目会在仓库目录下写入:
@@ -243,7 +420,7 @@ curl http://127.0.0.1:8787/v1/responses \
243
420
 
244
421
  它们分别用于保存:
245
422
 
246
- - OAuth 认证信息
423
+ - OAuth 认证信息和多个本地账号 profile
247
424
  - 默认模型和服务配置
248
425
 
249
426
  ## 项目结构
package/dist/api.js CHANGED
@@ -3,4 +3,3 @@ import { askOpenAICodex } from "./core/providers/openai-codex/chat.js";
3
3
  export {
4
4
  askOpenAICodex
5
5
  };
6
- //# sourceMappingURL=api.js.map
@@ -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 { model, prompt } = parseAskArgs(args);
6
- if (!prompt) {
7
- throw new Error('ask \u9700\u8981\u4E00\u4E2A prompt\uFF0C\u4F8B\u5982 bun src/cli.js ask "\u4F60\u597D"');
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
- console.log("model:", ctx);
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
@@ -8,4 +8,3 @@ async function runClearCommand() {
8
8
  export {
9
9
  runClearCommand
10
10
  };
11
- //# sourceMappingURL=clear.js.map
@@ -2,20 +2,26 @@
2
2
  function printHelp() {
3
3
  console.log(`\u7528\u6CD5:
4
4
 
5
- bun src/cli.js login
6
- bun src/cli.js models
7
- bun src/cli.js status
8
- bun src/cli.js ask "\u4F60\u597D\uFF0C\u8BF7\u7B80\u5355\u4ECB\u7ECD\u4E00\u4E0B\u81EA\u5DF1"
9
- bun src/cli.js ask --model gpt-5.3-codex "\u4F60\u597D"
10
- bun src/cli.js serve
11
- bun src/cli.js clear
5
+ azt login
6
+ azt models
7
+ azt status
8
+ azt ask "\u4F60\u597D\uFF0C\u8BF7\u7B80\u5355\u4ECB\u7ECD\u4E00\u4E0B\u81EA\u5DF1"
9
+ azt ask --model gpt-5.3-codex "\u4F60\u597D"
10
+ azt ask --payload-file ./codex-body.json --dump-raw ./codex-raw.json
11
+ azt ask --payload-file ./codex-body.json --write-artifacts-dir ./artifacts
12
+ azt start
13
+ azt serve
14
+ azt clear
12
15
 
13
16
  \u8BF4\u660E:
14
17
 
15
- login \u8D70\u771F\u5B9E OpenAI Codex OAuth\uFF0C\u4FDD\u5B58 access/refresh token
18
+ login \u8D70\u771F\u5B9E OpenAI Codex OAuth\uFF0C\u65B0\u589E\u5E76\u4FDD\u5B58\u4E00\u4E2A\u8D26\u53F7 profile
16
19
  models \u67E5\u770B\u8FD9\u4E2A demo \u5F53\u524D\u5185\u7F6E\u652F\u6301\u7684\u6A21\u578B\u5217\u8868
17
- status \u67E5\u770B\u5F53\u524D demo \u4FDD\u5B58\u7684\u8D26\u53F7\u548C\u8FC7\u671F\u65F6\u95F4
20
+ status \u67E5\u770B\u5F53\u524D demo \u5F53\u524D\u6FC0\u6D3B\u8D26\u53F7\u3001\u8D26\u53F7\u6570\u91CF\u548C\u8FC7\u671F\u65F6\u95F4
18
21
  ask \u7528\u4FDD\u5B58\u7684 token \u8C03\u771F\u5B9E Codex Responses API
22
+ \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
23
+ \u5982\u54CD\u5E94\u91CC\u542B image_b64 / partial_image_b64\uFF0C\u53EF\u7528 --write-artifacts-dir \u76F4\u63A5\u5199\u6210\u56FE\u7247\u6587\u4EF6
24
+ start \u542F\u52A8\u672C\u5730 HTTP \u7F51\u5173\u5E76\u81EA\u52A8\u6253\u5F00\u7BA1\u7406\u9875\u9762
19
25
  serve \u542F\u52A8\u672C\u5730 HTTP \u7F51\u5173
20
26
  clear \u6E05\u7A7A demo \u7684\u672C\u5730\u72B6\u6001
21
27
  `);
@@ -23,4 +29,3 @@ function printHelp() {
23
29
  export {
24
30
  printHelp
25
31
  };
26
- //# sourceMappingURL=help.js.map
@@ -18,4 +18,3 @@ async function runLoginCommand() {
18
18
  export {
19
19
  runLoginCommand
20
20
  };
21
- //# sourceMappingURL=login.js.map
@@ -11,4 +11,3 @@ async function runModelsCommand() {
11
11
  export {
12
12
  runModelsCommand
13
13
  };
14
- //# sourceMappingURL=models.js.map