whackw-kali/chatgpt-image-cli
GitHub: whackw-kali/chatgpt-image-cli
通过逆向 OpenClaw 的浏览器 OAuth 认证流程,实现在无需 API Key 的情况下调用 ChatGPT gpt-image-2 免费生成图片的命令行工具。
Stars: 0 | Forks: 0
# ChatGPT Image CLI
## 快速导航
- [人类使用指南](#人类使用指南human-usage-guide) — 手动操作步骤
- [AI Agent 使用指南](#ai-agent-使用指南ai-agent-usage-guide) — AI 工具调用方式
- [完整技术文档](#完整技术文档full-technical-documentation) — 逆向细节、API 数据流
# 人类使用指南
## 环境要求
| 项目 | 要求 |
|-------------|------------------|
| Python | 3.8+ |
| 系统 | macOS / Linux / Windows |
| 浏览器 | 能打开 ChatGPT 授权页 |
| ChatGPT 账号 | ChatGPT 账号(Plus 订阅限额更高) |
## 安装
```
# 克隆仓库 / Clone the repo
git clone https://github.com/whackw-kali/chatgpt-image-cli.git
cd chatgpt-image-cli
# 直接运行(无需安装依赖,仅用 Python 标准库)
# 直接运行(无依赖,仅限 Python stdlib)
python chatgpt_image.py --help
```
## 使用步骤
### 第一步:OAuth 登录
```
python chatgpt_image.py login
```
**操作流程:**
```
$ python chatgpt_image.py login
Starting browser OAuth flow...
Opening browser for authentication...
If browser doesn't open, go to:
https://auth.openai.com/oauth/authorize?...
Waiting for authentication callback...
```
1. 浏览器自动打开 ChatGPT 授权页
2. 输入账号密码,点击「授权」
3. 页面提示「Authentication Complete」,脚本自动继续
4. 看到「Login successful! Tokens saved.」即完成
**Token 存储位置:**
- 独立 token:`~/.chatgpt_image_tokens.json`
- OpenClaw token:`~/.openclaw/agents/main/agent/auth-profiles.json`
### 第二步:生成图片
```
# 基本用法(单张)/ Basic (single image)
python chatgpt_image.py generate "一只可爱的橘猫" -o cat.png
# 生成多张(最多4张)/ Multiple images (max 4)
python chatgpt_image.py generate "赛博朋克城市夜景" -n 4 -o cyberpunk.png
# 指定尺寸 / Specify size
python chatgpt_image.py generate "山水画" --size 3840x2160 -o landscape.png
```
**支持的尺寸:**
| 尺寸 | 说明 |
|-------------|-------------------|
| `1024x1024` | 正方形(默认) |
| `1536x1024` | 横版 3:2 |
| `1024x1536` | 竖版 2:3 |
| `2048x2048` | 正方形高清 |
| `2048x1152` | 横版 16:9 |
| `3840x2160` | 横版 4K |
| `2160x3840` | 竖版 4K |
### 第三步:查看 token 状态
```
python chatgpt_image.py status
```
**输出示例:**
```
OpenClaw: Logged in as eyJhbG...NiIs...
Token expires in 14310.2 minutes.
Own token: Logged in.
Token expires in 58.3 minutes.
```
## 命令速查
| 命令 | 说明 |
|---------------|-------------------|
| `python chatgpt_image.py login` | OAuth 登录(首次使用) |
| `python chatgpt_image.py generate "描述" -o out.png` | 生成单张图片 |
| `python chatgpt_image.py generate "描述" -n 4 -o out.png` | 生成多张图片 |
| `python chatgpt_image.py status` | 查看登录状态 |
## 常见问题
| 问题 | 原因 | 解决方案 |
|-----------------|-------------|-------------------|
| "fetch failed \| other side closed" | Token 过期 | `python chatgpt_image.py login` |
| "Token expired" | access_token 过期 | 同上 |
| 浏览器没打开 | headless 环境 | 手动复制终端输出的 URL |
| 端口被占用 | 1455 端口被占 | 脚本自动找空闲端口,通常不影响 |
# AI Agent 使用指南
## 工具定位
本工具可作为 AI Agent 的**图片生成工具**,通过子进程调用 `chatgpt_image.py`,传入 prompt 和输出路径,返回生成的图片文件路径。
## 集成方式
### 方式一:子进程调用(推荐)
AI Agent 通过 `terminal` 工具调用脚本。
**Prompt 模板:**
```
调用以下命令生成图片:
Run: python ~/hermesAgent/chatgpt-image-cli/chatgpt_image.py generate "图片描述 / Image description" -o /tmp/output.png
返回生成的图片路径。/ Return the generated image path.
```
**完整调用流程:**
```
# 1. 检查 token 是否有效 / Check if token is valid
# 运行 / Run: python chatgpt_image.py status
# 解析输出判断 / Parse output to check if OpenClaw or local token is valid
# 2. 生成图片 / Generate image
# 运行 / Run: python chatgpt_image.py generate "描述 / description" -o /path/to/output.png
# 脚本输出 "Saved:" 即成功 / Script output "Saved:" means success
# 3. 返回图片路径 / Return image path
# 图片已保存在指定路径 / Image saved at specified path
```
### 方式二:读取 OpenClaw Token 直接调用 API
如果需要完全自主控制 API 调用流程,可直接读取 OpenClaw 的 token。
**读取 Token:**
```
import json
from pathlib import Path
auth_file = Path.home() / ".openclaw/agents/main/agent/auth-profiles.json"
with open(auth_file) as f:
data = json.load(f)
for profile in data.get("profiles", {}).values():
if profile.get("provider") == "openai-codex":
access_token = profile["access"]
refresh_token = profile["refresh"]
expires_ms = profile["expires"] # 毫秒时间戳 / millisecond timestamp
break
```
**调用图片生成 API:**
```
import urllib.request
import json
request_body = {
"model": "gpt-5.5",
"input": [{
"role": "user",
"content": [{"type": "input_text", "text": "图片描述 / Image description"}]
}],
"instructions": "You are an image generation assistant.",
"tools": [{"type": "image_generation"}],
"store": False,
"stream": True
}
headers = {
"Authorization": f"Bearer {access_token}",
"Content-Type": "application/json",
"Accept": "text/event-stream"
}
req = urllib.request.Request(
"https://chatgpt.com/backend-api/codex/responses",
data=json.dumps(request_body).encode(),
headers=headers,
method="POST"
)
with urllib.request.urlopen(req, timeout=300) as resp:
for line in resp:
event_data = json.loads(line.decode("utf-8").strip()[6:])
if event_data.get("type") == "response.output_item.done":
item = event_data.get("item", {})
if item.get("type") == "image_generation_call":
import base64
image_bytes = base64.b64decode(item["result"])
with open("output.png", "wb") as f:
f.write(image_bytes)
```
### 方式三:集成到 Hermes Agent Skill
创建 skill 文件让 AI Agent 学会调用。
**Skill 路径:** `skill/SKILL.md`(已包含在项目中)
```
## 使用触发条件 / Trigger Conditions
用户要求生成图片,且提到以下关键词之一:
User wants image generation with keywords:
- "ChatGPT 图片生成 / ChatGPT image"
- "gpt-image"
- "DALL-E"
- "AI 画图 / AI draw"
- "生成一张图片 / Generate an image"
- "帮我画 / Draw for me"
## 调用命令 / Call Command
python ~/hermesAgent/chatgpt-image-cli/chatgpt_image.py generate "用户描述 / User description" -o /tmp/img_$(date +%s).png
## 输出处理 / Output Handling
1. 检查命令输出包含 "Saved:" / Check output contains "Saved:"
2. 从输出中提取图片路径 / Extract image path from output
3. 返回图片路径给用户 / Return image path to user
## Token 状态检查 / Token Status Check
python chatgpt_image.py status
## 过期处理 / Expired Token Handling
如果 token 过期("fetch failed"),提示用户运行:
If token expired ("fetch failed"), tell user to run:
python chatgpt_image.py login
```
## Token 优先级说明
AI Agent 调用时,脚本按以下顺序尝试获取 token。
```
1. ~/.openclaw/agents/main/agent/auth-profiles.json
(OpenClaw 已登录的 ChatGPT token)
2. ~/.chatgpt_image_tokens.json
(本工具独立 OAuth 登录的 token)
```
优先使用 OpenClaw token,无需额外登录。
## 流式响应关键事件
| 事件 type | 含义 | 处理 |
|-----------|---------------|---------------|
| `response.created` | 开始生成 | 记录 response_id |
| `response.output_item.done` + `image_generation_call` | 图片生成完成 | 提取 base64,保存 PNG |
| `response.completed` | 生成结束 | 退出循环 |
| `error` / `response.failed` | 生成失败 | 打印错误信息 |
| `data: [DONE]` | 流结束 | 退出循环 |
# 完整技术文档
## 逆向工程过程
### 背景
OpenClaw 支持通过浏览器 OAuth 登录 ChatGPT,使用 `gpt-image-2` 免费生成图片。token 存储在本地文件,复用该 token 即可绕过 API Key 直接调用。
### 逆向步骤
```
步骤 1 / Step 1: 找到 OpenClaw 的 token 存储 / Find OpenClaw token storage
~/.openclaw/agents/main/agent/auth-profiles.json
步骤 2 / Step 2: 分析 token 数据结构 / Analyze token data structure
- access_token: OAuth access token
- refresh_token: 用于刷新 / Used to refresh
- expires: 过期时间(毫秒时间戳)/ expiration (millisecond timestamp)
- provider: "openai-codex"
步骤 3 / Step 3: 抓取 OpenClaw 的网络请求 / Capture OpenClaw network requests
- 通过 openclaw infer image generate 命令 / via openclaw infer image generate
- 分析 HTTP 请求头、请求体、响应格式 / analyze HTTP headers, body, response format
步骤 4 / Step 4: 提取关键信息 / Extract key info
- Auth Base: https://auth.openai.com
- Client ID: app_EMoamEEZ73f0CkXaXp7hrann
- Redirect: http://localhost:1455/auth/callback
- Codex API: https://chatgpt.com/backend-api/codex/responses
- Scope: openid profile email offline_access
```
### 逆向结果汇总
| 组件 | 值 |
|-----------------|-----------|
| Client ID | `app_EMoamEEZ73f0CkXaXp7hrann` |
| Auth Base | `https://auth.openai.com` |
| Token Endpoint | `https://auth.openai.com/oauth/token` |
| Authorize Endpoint | `https://auth.openai.com/oauth/authorize` |
| Codex API | `https://chatgpt.com/backend-api/codex/responses` |
| 认证方式 | OAuth PKCE (Proof Key for Code Exchange) |
## 技术架构
```
┌─────────────────────────────────────────────────────────────────┐
│ 用户空间 / User Space │
│ ┌──────────────┐ ┌─────────────────┐ ┌──────────────┐ │
│ │ 浏览器 │◄──►│ 本地 HTTP Server │◄──►│ OAuth 授权页 │ │
│ │ (手动操作) │ │ (localhost:1455) │ │ auth.openai │ │
│ └──────────────┘ └─────────────────┘ └──────────────┘ │
│ ▲ │
│ ┌──────────────┐ │ ┌────────────────┐ │
│ │ chatgpt_ │───────────┘ │ OpenClaw │ │
│ │ image.py │◄─────────────────────────│ auth-profiles │ │
│ │ (本工具) │ 读取/写入 token │ .json │ │
│ └──────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ ChatGPT API 空间 / ChatGPT API Space │
│ ┌───────────────────────┐ ┌──────────────────────────────┐ │
│ │ POST /oauth/token │ │ POST /backend-api/ │ │
│ │ (换取 token) │ │ codex/responses (生成图片) │ │
│ └───────────────────────┘ └──────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
## PKCE OAuth 详细流程
### 流程图
```
Client (本工具) Auth Server Browser
│
│ 1. 生成 code_verifier (64字节随机 / 64-byte random)
│ 2. 计算 code_challenge = BASE64URL(SHA256(verifier))
│ 3. 启动本地 HTTP Server (localhost:1455)
│
│ 4. 构造授权 URL,打开浏览器 ──────────────────────────►
│ ?response_type=code
│ &client_id=app_EMoamEEZ73f0CkXaXp7hrann
│ &redirect_uri=http://localhost:1455/auth/callback
│ &scope=openid profile email offline_access
│ &code_challenge=xxx
│ &code_challenge_method=S256
│ &state=随机防CSRF / random CSRF protection
│
│ ◄──────────────────── 5. 显示登录页 / Show login page
│ ◄──────────────────── 6. 用户授权 / User authorizes
│
│ 7. 浏览器重定向 / Browser redirects
│ localhost:1455/auth/callback?code=xxx&state=yyy
│◄──────────────────────────────────────────────────────────────
│
│ 8. POST /oauth/token (用 code + code_verifier 换 token)
│─────────────────────────────────────────────────────────────►
│
│ 9. 收到 access_token + refresh_token + expires_in ◄────────
│
│ 10. 保存到 ~/.chatgpt_image_tokens.json
```
### code_verifier 和 code_challenge 生成
```
import base64
import hashlib
import secrets
# code_verifier: 64字节随机字符串 / 64-byte random string
code_verifier = secrets.token_urlsafe(64)
# code_challenge: SHA256 → Base64URL(去掉 padding / remove padding)
code_challenge = base64.urlsafe_b64encode(
hashlib.sha256(code_verifier.encode()).digest()
).decode().rstrip("=")
```
### 授权 URL 参数
```
params = {
"response_type": "code",
"client_id": "app_EMoamEEZ73f0CkXaXp7hrann",
"redirect_uri": "http://localhost:1455/auth/callback",
"scope": "openid profile email offline_access",
"code_challenge": code_challenge, # 上面计算的值
"code_challenge_method": "S256", # 固定 / fixed
"state": secrets.token_hex(16), # 防 CSRF / CSRF protection
"id_token_add_organizations": "true",
"codex_cli_simplified_flow": "true",
"originator": "chatgpt-image-cli",
}
auth_url = f"https://auth.openai.com/oauth/authorize?{urllib.parse.urlencode(params)}"
```
### Token 交换请求
```
# POST https://auth.openai.com/oauth/token
data = {
"grant_type": "authorization_code",
"client_id": "app_EMoamEEZ73f0CkXaXp7hrann",
"code": authorization_code,
"code_verifier": code_verifier,
"redirect_uri": "http://localhost:1455/auth/callback",
}
# 响应 / Response
{
"access_token": "eyJhbGc...",
"refresh_token": "rt_aaM74...",
"expires_in": 3600,
"token_type": "Bearer"
}
```
## API 调用详解
### 请求
```
POST https://chatgpt.com/backend-api/codex/responses
Authorization: Bearer
Content-Type: application/json
Accept: text/event-stream
```
### 请求体
```
{
"model": "gpt-5.5",
"input": [{
"role": "user",
"content": [
{"type": "input_text", "text": "一只可爱的橘猫 / A cute orange cat"}
]
}],
"instructions": "You are an image generation assistant.",
"tools": [{"type": "image_generation"}],
"store": false,
"stream": true
}
```
| 字段 | 说明 |
|-------------|-------------------|
| `model` | 内部名,映射到 `gpt-image-2` |
| `input[].content[].text` | 图片描述 prompt |
| `tools[].type` | 固定 `image_generation` 触发生成 |
| `stream` | `true` = SSE 流式响应 |
### SSE 响应解析
```
for line in resp: # resp 是 HTTP 响应流 / resp is HTTP stream
line = line.decode("utf-8").strip()
if not line.startswith("data: "):
continue
event = json.loads(line[6:]) # 去掉 "data: " 前缀 / remove "data: " prefix
# 图片生成完成 / Image generation done
if event.get("type") == "response.output_item.done":
item = event.get("item", {})
if item.get("type") == "image_generation_call":
image_base64 = item.get("result")
image_bytes = base64.b64decode(image_base64)
with open("output.png", "wb") as f:
f.write(image_bytes)
# 完成信号 / Completion signal
if event.get("type") == "response.completed":
break
```
## Token 管理
### Token 数据结构
```
@dataclass
class TokenData:
access_token: str # 调用 API 用 / for API calls
refresh_token: str # 过期时刷新用 / for refresh when expired
expires_at: float # Unix 时间戳(秒)/ Unix timestamp (seconds)
```
### 验证逻辑
```
def is_token_valid(tokens: TokenData) -> bool:
# 提前 60 秒判定过期 / Consider expired 60 seconds early
return time.time() < (tokens.expires_at - 60)
```
### 刷新流程
```
Token 过期 → 用 refresh_token 换新 access_token
→ 更新 token 文件
→ 重试 API 调用
```
### OpenClaw Token 格式
```
{
"profiles": {
"openai-codex:邮箱@gmail.com": {
"type": "oauth",
"provider": "openai-codex",
"access": "eyJhbGc...",
"refresh": "rt_aaM74...",
"expires": 1778338608095, // 毫秒时间戳 / millisecond timestamp
"email": "邮箱@gmail.com"
}
}
}
```
## 项目文件结构
```
chatgpt-image-cli/
├── chatgpt_image.py # 主脚本(人类和 AI 共同使用)/ Main script (for human and AI)
├── skill/
│ └── SKILL.md # AI Agent Skill 文档 / AI Agent Skill documentation
└── README.md # 本文档 / This document
```
## 许可证
MIT
标签:AIGC, AI绘画, API数据流, ChatGPT, ChatGPT Plus, GPT-Image-2, IP 地址批量处理, OAuth认证, OpenAI, Promptflow, Python, 二进制发布, 云资产清单, 免API Key, 免费资源, 内存规避, 图片生成, 开源工具, 无后门, 浏览器自动化, 白嫖, 绕过API限制, 网络安全, 脚本, 逆向工具, 逆向工程, 隐私保护