[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-81385":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":8,"htmlUrl":8,"language":9,"languages":8,"totalLinesOfCode":8,"stars":10,"forks":11,"watchers":10,"openIssues":12,"contributorsCount":12,"subscribersCount":12,"size":12,"stars1d":12,"stars7d":12,"stars30d":12,"stars90d":12,"forks30d":12,"starsTrendScore":12,"compositeScore":13,"rankGlobal":8,"rankLanguage":8,"license":8,"archived":14,"fork":14,"defaultBranch":15,"hasWiki":14,"hasPages":14,"topics":16,"createdAt":8,"pushedAt":8,"updatedAt":17,"readmeContent":18,"aiSummary":19,"trendingCount":12,"starSnapshotCount":12,"syncStatus":20,"lastSyncTime":21,"discoverSource":22},81385,"myichuan","mooooooooner\u002Fmyichuan","mooooooooner",null,"TypeScript",37,4,0,2.1,false,"main",[],"2026-06-12 02:04:14","﻿# Magai Proxy Reverse-Engineering & Integration Guide\n\n本仓库用于在 CTF\u002F研究场景下，将 `https:\u002F\u002Fbeta.magai.co` 的聊天链路封装为 OpenAI\u002FAnthropic 兼容接口，并提供多账号轮询与 Web 管理门户。\n\n## 0. 一键部署（推荐：完全不懂代码也能用）\n\n> 前置：装好 [Node 20+](https:\u002F\u002Fnodejs.org\u002F)。装完打开新的终端窗口（让 `node`\u002F`npm` 进 PATH）。其它依赖（pnpm 等）脚本会自动处理。\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fmooooooooner\u002Fmagai.co.git\ncd magai.co\n```\n\n**Windows（PowerShell）：**\n\n```powershell\npowershell -ExecutionPolicy Bypass -File scripts\\setup.ps1\n```\n\n**macOS \u002F Linux：**\n\n```bash\nbash scripts\u002Fsetup.sh\n```\n\n脚本会：\n\n1. 检查 Node \u002F pnpm（缺 pnpm 自动用 corepack 启用）\n2. 帮你生成 `apps\u002Fserver\u002F.env`，并交互式地让你输入：\n   - `PROXY_API_KEY`（你的 client 用的密钥；按 Enter 自动生成 32 位随机串）\n   - 要自动注册多少个 Magai 账号（推荐 3）\n3. `pnpm install` 安装依赖\n4. 调注册脚本批量创建账号、写入 `accounts.json`，并自动回填 email\u002Fpassword 兜底\n5. 注入默认模型清单（Claude Sonnet 4.6）\n6. 询问是否立刻启动 server（:8787）+ web portal（:5174）\n\n**之后需要自己手动导入一份cookie，以及手动导入你想要的模型映射**！！！\n\n启动后浏览器打开 `http:\u002F\u002F127.0.0.1:5174`，输入刚刚生成的 `PROXY_API_KEY` 就能用。\n\n非交互模式（CI \u002F 脚本里调）：\n\n```powershell\n# Windows\npowershell -ExecutionPolicy Bypass -File scripts\\setup.ps1 -RegisterCount 3 -ProxyApiKey my-key -NoStart\n```\n\n```bash\n# macOS \u002F Linux\nbash scripts\u002Fsetup.sh --register-count 3 --proxy-key my-key --no-start\n```\n\n> ⚠️ `accounts.json` \u002F `.env` 含明文密码，**已被 `.gitignore` 排除**——切勿手动 `git add -f` 它们。\n\n---\n\n## 1. 项目能力\n\n- OpenAI 兼容：`POST \u002Fv1\u002Fchat\u002Fcompletions`\n- OpenAI 图片兼容：`POST \u002Fv1\u002Fimages\u002Fgenerations`\n- Anthropic 兼容：`POST \u002Fanthropic\u002Fv1\u002Fmessages`、`POST \u002Fv1\u002Fmessages`\n- 自动链路：`refresh_token -> Supabase access_token -> next-action short JWT -> \u002Fapi\u002Fchat`\n- 自动发现：`userId`、`chatId`\n- 固定模型清单：`\u002Fv1\u002Fmodels` 返回账号已导入的 known models（不再自动抓取）\n- 多账号轮询：账号池启停、删除、导入、按请求轮询（round-robin）\n- Web 门户：管理账号凭证、查看状态、导入 JSON\n\n---\n\n## 2. 目录结构\n\n- `apps\u002Fserver\u002Fsrc\u002Findex.ts`：代理主逻辑（鉴权、账号池、发现、上游调用、协议适配）\n- `apps\u002Fserver\u002F.env.example`：服务环境变量模板\n- `apps\u002Fserver\u002F.env`：服务实际配置（敏感）\n- `apps\u002Fserver\u002Faccounts.json`：账号池持久化文件（敏感，运行后可能自动生成）\n- `apps\u002Fweb-portal`：React + Vite + Tailwind 管理门户\n\n---\n\n## 3. 代理架构\n\n```text\nClient\n  -> Proxy (\u002Fv1\u002F*)\n    -> account pool (round-robin)\n    -> Supabase auth refresh (access_token)\n    -> Next action 40cd... (short JWT)\n    -> Next action \u002F Supabase REST (discover user\u002Fchat\u002Fmodels)\n    -> Magai \u002Fapi\u002Fchat (NDJSON)\n    -> Repackage to OpenAI \u002F Anthropic format\n```\n\n说明：\n- 默认从“启用账号”中轮询选择。\n- 可在请求中显式指定 `accountId`，跳过轮询。\n\n---\n\n## 4. 环境配置（服务端）\n\n先复制：\n\n```bash\ncp apps\u002Fserver\u002F.env.example apps\u002Fserver\u002F.env\n```\n\n最小必填：\n\n- `PROXY_API_KEY`\n- `MAGAI_COOKIE`\n- `SUPABASE_PUBLISHABLE_KEY`\n- `SUPABASE_REFRESH_TOKEN`\n\n> 重要：\n> - `MAGAI_COOKIE` 需要你自己从浏览器会话中获取并填入 `.env`，仓库不会提供可用 cookie。\n> - 模型 ID 映射（`id\u002Fname\u002FapiName`）也需要你自己抓取并通过 `\u002Fv1\u002Fmodels\u002Fimport` 或 Web 门户导入；不导入时 `\u002Fv1\u002Fmodels` 可能为空。\n\n推荐字段：\n\n```env\nPORT=8787\nPROXY_API_KEY=test-key\n\nMAGAI_BASE_URL=https:\u002F\u002Fbeta.magai.co\nMAGAI_COOKIE=\u003Ccookie>\nMAGAI_NEXT_ACTION=40cd8b2ec4704e0f3c267bd98f93b0f9806e121b77\nMAGAI_IMAGE_ACTION=7fa3b9255f2ff4eef604b8c9a7bbc1b37ceb871dae\nMAGAI_IMAGE_PRESET=v1 Pro\n\nSUPABASE_URL=https:\u002F\u002Fbkatrpghmzbpjhegvkev.supabase.co\nSUPABASE_PUBLISHABLE_KEY=sb_publishable_xxx\nSUPABASE_REFRESH_TOKEN=\u003Clatest>\n\n# multi-account persistence\nMAGAI_ACCOUNTS_FILE=accounts.json\nMAGAI_MODEL_CATALOG_FILE=model-catalog.json\n\n# optional fallback\nMAGAI_DEFAULT_CHAT_ID=\nMAGAI_DEFAULT_MODEL_ID=\nMAGAI_DEFAULT_MODEL_NAME=Claude Sonnet 4.6\nMAGAI_DEFAULT_MODEL_API_NAME=anthropic\u002Fclaude-4.6-sonnet-20260217\nMAGAI_ALWAYS_NEW_CHAT=1\nMAGAI_USER_ID=\nMAGAI_CHAT_SNAPSHOT_ACTION=40a34afcf0167f40f2afa1b3ff5a65dc8451eac3a6\nMAGAI_MODEL_CATALOG_JSON=\n```\n\n---\n\n## 5. 运行方式\n\n### 5.1 启动服务端\n\n```bash\npnpm --filter @apps\u002Fserver dev\n```\n\n### 5.2 启动 Web 门户\n\n```bash\npnpm --filter @apps\u002Fweb-portal dev\n```\n\n默认地址：\n- Proxy: `http:\u002F\u002F127.0.0.1:8787`\n- Portal: `http:\u002F\u002F127.0.0.1:5174`\n\n### 5.3 构建\n\n```bash\npnpm -r build\n```\n\n---\n\n## 6. API 使用\n\n### 6.1 健康检查\n\n```bash\ncurl http:\u002F\u002F127.0.0.1:8787\u002Fhealth\n```\n\n### 6.2 模型列表（轮询）\n\n```bash\ncurl http:\u002F\u002F127.0.0.1:8787\u002Fv1\u002Fmodels \\\n  -H \"Authorization: Bearer test-key\"\n```\n\n### 6.3 导入模型清单（固定返回源）\n\n```bash\ncurl http:\u002F\u002F127.0.0.1:8787\u002Fv1\u002Fmodels\u002Fimport \\\n  -H \"Authorization: Bearer test-key\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"models\":[\n      {\n        \"id\":\"16c133bc-bab9-41af-b3d4-08dd9157dbca\",\n        \"name\":\"Claude Sonnet 4.6\",\n        \"apiName\":\"anthropic\u002Fclaude-4.6-sonnet-20260217\"\n      },\n      {\n        \"id\":\"2ba9020c-96f1-4712-bea4-e3c27a145da1\",\n        \"name\":\"DeepSeek v4 Flash\",\n        \"apiName\":\"deepseek\u002Fdeepseek-v4-flash-20260423\"\n      }\n    ]\n  }'\n```\n\n### 6.4 OpenAI chat\n\n```bash\ncurl http:\u002F\u002F127.0.0.1:8787\u002Fv1\u002Fchat\u002Fcompletions \\\n  -H \"Authorization: Bearer test-key\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"model\":\"claude-sonnet-4-6\",\n    \"messages\":[{\"role\":\"user\",\"content\":\"reply exactly: OK\"}],\n    \"accountId\":\"default\"\n  }'\n```\n\n### 6.5 Anthropic chat\n\n```bash\ncurl http:\u002F\u002F127.0.0.1:8787\u002Fanthropic\u002Fv1\u002Fmessages \\\n  -H \"x-api-key: test-key\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"model\":\"claude-sonnet-4-6\",\n    \"messages\":[{\"role\":\"user\",\"content\":\"ping\"}],\n    \"metadata\":{\"accountId\":\"default\"}\n  }'\n```\n\n### 6.6 OpenAI images\n\n```bash\ncurl http:\u002F\u002F127.0.0.1:8787\u002Fv1\u002Fimages\u002Fgenerations \\\n  -H \"Authorization: Bearer test-key\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"model\":\"claude-sonnet-4-6\",\n    \"prompt\":\"a tiny robot reading a book\",\n    \"size\":\"1024x1024\",\n    \"quality\":\"hd\",\n    \"style\":\"vivid\",\n    \"n\":1,\n    \"response_format\":\"url\"\n  }'\n```\n\n说明：\n- `size` 支持：`1024x1024`、`1024x1536`、`1536x1024`、`1024x1792`、`1792x1024`\n- `response_format` 支持：`url`、`b64_json`\n- 可选传入：`accountId`、`chatId`\n\n---\n\n## 7. 账号池管理 API\n\n- `GET \u002Fv1\u002Faccounts`：查看账号池（脱敏视图）\n- `POST \u002Fv1\u002Faccounts\u002Fimport`：导入账号数组\n- `PATCH \u002Fv1\u002Faccounts\u002F:id`：更新 `enabled`\u002F`name`\n- `DELETE \u002Fv1\u002Faccounts\u002F:id`：删除账号\n- `POST \u002Fv1\u002Fmodels\u002Fimport`：导入全局模型映射（`[{id,name,apiName}]`）\n\n导入示例：\n\n```json\n{\n  \"accounts\": [\n    {\n      \"id\": \"acc-1\",\n      \"name\": \"magai-1\",\n      \"enabled\": true,\n      \"magaiCookie\": \"...\",\n      \"supabaseRefreshToken\": \"...\"\n    }\n  ]\n}\n```\n\n---\n\n## 8. Web 门户使用\n\n1. 打开 `http:\u002F\u002F127.0.0.1:5174`\n2. 输入 `PROXY_API_KEY`（默认示例是 `test-key`）\n3. 粘贴导出的账号 JSON\n4. 点击导入，查看账号池状态与轮询信息\n5. 在 “Import Known Models” 区域粘贴你自己抓取的模型映射 JSON（`id\u002Fname\u002FapiName`）并导入\n6. 在 “Image Studio” 区域配置 `model \u002F size \u002F quality \u002F style \u002F n \u002F response_format` 并点击 `Generate Image`\n\n---\n\n## 9. 浏览器控制台一键导出凭证\n\n在 `https:\u002F\u002Fbeta.magai.co` 控制台执行：\n\n```js\n(() => {\n  const out = [];\n  const seen = new Set();\n  const cookie = document.cookie || \"\";\n  const walk = (v, hit = []) => {\n    if (!v || typeof v !== \"object\") return [];\n    const res = [];\n    for (const [k, val] of Object.entries(v)) {\n      if (k.toLowerCase().includes(\"refresh_token\") && typeof val === \"string\" && val) {\n        res.push({ token: val, hint: hit.join(\".\") });\n      }\n      if (val && typeof val === \"object\") res.push(...walk(val, [...hit, k]));\n    }\n    return res;\n  };\n\n  for (const [k, v] of Object.entries(localStorage)) {\n    try {\n      const parsed = JSON.parse(v);\n      const hits = walk(parsed, [k]);\n      for (const h of hits) {\n        if (seen.has(h.token)) continue;\n        seen.add(h.token);\n        out.push({\n          id: `acc-${out.length + 1}`,\n          name: `magai-${out.length + 1}`,\n          enabled: true,\n          magaiCookie: cookie,\n          supabaseRefreshToken: h.token\n        });\n      }\n    } catch {}\n  }\n\n  const json = JSON.stringify(out, null, 2);\n  console.log(json);\n  copy(json);\n  return { count: out.length, copied: true };\n})();\n```\n\n---\n\n## 9. 浏览器控制台提取“历史使用模型”（id + apiName）\n\n在 `https:\u002F\u002Fbeta.magai.co\u002Fchat` 打开控制台执行：\n\n```js\n(async () => {\n  const tokenRaw = localStorage.getItem(\"sb-bkatrpghmzbpjhegvkev-auth-token\");\n  if (!tokenRaw) throw new Error(\"no supabase token in localStorage\");\n  const tokenObj = JSON.parse(tokenRaw);\n  const accessToken = tokenObj.access_token;\n  const userId = tokenObj.user?.id;\n\n  const supabaseUrl = \"https:\u002F\u002Fbkatrpghmzbpjhegvkev.supabase.co\";\n  const apikey = \"sb_publishable_abLi4B3uk35xfTdT1d5Z1g_QVGG3JNo\";\n  const url = `${supabaseUrl}\u002Frest\u002Fv1\u002Fspark?select=com_ai_model,chat_json,created_at&created_by=eq.${userId}&order=created_at.desc&limit=1000`;\n\n  const resp = await fetch(url, {\n    headers: { apikey, authorization: `Bearer ${accessToken}`, accept: \"application\u002Fjson\" }\n  });\n  const rows = await resp.json();\n  const byId = new Map();\n\n  for (const r of rows) {\n    const id = r?.com_ai_model || \"\";\n    let apiName = null;\n    try {\n      const cj = typeof r.chat_json === \"string\" ? JSON.parse(r.chat_json) : r.chat_json;\n      const hit = (Array.isArray(cj?.timeline) ? cj.timeline : []).find(x => typeof x?.modelDisplay === \"string\" && x.modelDisplay);\n      if (hit) apiName = hit.modelDisplay;\n    } catch {}\n    if (!id) continue;\n    if (!byId.has(id)) byId.set(id, { id, name: apiName || `model-${id.slice(0, 8)}`, apiName });\n  }\n\n  const out = Array.from(byId.values());\n  console.log(out);\n  copy(JSON.stringify(out, null, 2));\n})();\n```\n\n把输出粘贴到 Web 门户的 “Import Known Models” 文本框，点击导入即可。\n\n## 9.5 Token 持久化机制（2026-05-21 重构）\n\n> 解决\"refresh token 过期太快 \u002F 被 reuse 吊销 \u002F 重启后失效\"等问题。\n\n### 链路\n\n| Token | 寿命 | 续期路径 |\n|---|---|---|\n| Magai short JWT (next-action) | 几分钟 | 自动重新调 next-action |\n| Supabase access_token | 1 小时（GoTrue 默认 `JWT_EXPIRY=3600`） | refresh_token grant；失败时 password grant 兜底 |\n| Supabase refresh_token | 受 GoTrue 配置影响（reuse interval \u002F inactivity timeout \u002F 家族吊销） | 每次刷新会自动轮换，写回 `accounts.json` |\n| 账号密码 | 永久（除非账号被封） | 注册\u002F登录脚本写出，作为最后兜底 |\n\n### 关键改动\n\n1. **password grant 兜底**：refresh 任意失败（`refresh_token_already_used` \u002F `invalid_grant` \u002F 网络错误 \u002F 401 等）会自动用 `supabaseEmail + supabasePassword` 重新登录拿到新的 refresh_token，整条链路自愈。\n2. **并发去重**：`account.refreshPromise` 让 N 个并发请求只触发 1 次刷新调用，彻底消除\"两条请求同时用旧 refresh_token → 第二条触发 reuse detection → 整个 session family 被吊销\"的死局。\n3. **提前 5 分钟续期**：缓存阈值从 30s 改成 300s，远离 Supabase reuse-interval 危险窗口。\n4. **原子写盘**：`accounts.json` 改用 tmp + rename，并通过单链 Promise 队列串行化。再不会出现\"两个账号同时刷新 → 写盘竞争 → 新 refresh_token 被旧值覆盖\"。\n5. **后台心跳**：每 50 分钟主动遍历启用账号刷新一次。\n   - 让 refresh_token 始终\"在使用中\"，触发不了 inactivity timeout\n   - 用户请求路径上零延迟（access_token 永远是热的）\n   - 单点续期 + 单飞 promise，天然消除并发竞争\n\n环境变量：\n\n```env\n# 默认 50 分钟。设 0 仍会被钳到 60s。\nMAGAI_HEARTBEAT_INTERVAL_MS=3000000\n# 1 = 关闭后台心跳（不推荐）\nMAGAI_HEARTBEAT_DISABLE=0\n```\n\n### accounts.json 新增字段（向后兼容）\n\n```jsonc\n{\n  \"id\": \"auto-...\",\n  \"name\": \"frank0508upp@outlook.com\",\n  \"enabled\": true,\n  \"magaiCookie\": \"...\",\n  \"supabaseRefreshToken\": \"...\",\n  \"supabaseEmail\": \"frank0508upp@outlook.com\",   \u002F\u002F 新增：password grant 兜底\n  \"supabasePassword\": \"trfn590\"                  \u002F\u002F 新增：password grant 兜底\n}\n```\n\n### 给老账号回填密码\n\n注册脚本现在自动写 `supabaseEmail\u002FsupabasePassword`。如果你已有的 `accounts.json` 是改造前生成的，跑一次回填脚本（基于 `registered.json` join）：\n\n```bash\npnpm --filter @apps\u002Fserver exec tsx src\u002Fbackfill-credentials.ts\n```\n\n输出形如：`[backfill] filled=4 already=0 no-match=0 total=4`。\n\n---\n\n## 10. 排障\n\n1. 账号列表为空\n- 检查 Web 门户 API Key 是否与 `PROXY_API_KEY` 一致。\n- 检查服务端是否启动在 `8787`。\n\n2. `\u002Fv1\u002Fmodels` 为空\n- 先导入 known models（`\u002Fv1\u002Fmodels\u002Fimport` 或 Web 门户导入）。\n- 若仍为空，检查账号是否存在 `magaiDefaultModelId` 或导入 JSON 是否包含 `id`\u002F`name`。\n\n3. 报 `failed to discover userId`\n- 优先检查 cookie。\n- 配置 `MAGAI_USER_ID` 兜底。\n\n4. 报 `chatId not discovered automatically`\n- 先请求一次 `\u002Fv1\u002Fmodels` 触发发现。\n- 或在请求中传 `chatId`。\n- 或配置 `MAGAI_DEFAULT_CHAT_ID`。\n\n5. `refresh_token_already_used`\n- 重构后已显著缓解：并发去重 + 提前续期 + password grant 兜底，绝大多数场景会自愈。\n- 若仍出现且账号没有 `supabaseEmail\u002FsupabasePassword`，请跑回填脚本或重新注册。\n\n---\n\n## 11. 安全建议\n\n- 不要将 `.env`、`accounts.json`、抓包文件提交到公开仓库。\n- 比赛协作中，建立 refresh token 更新流程与责任人。\n- 上游结构会变化，建议保留抓包并定期回归。\n\n---\n\n## 12. 批量注册与流式验证（2026-05-20）\n\n### 12.1 批量注册脚本\n\n```bash\npnpm --filter @apps\u002Fserver register --count 1\n```\n\n脚本位置：`apps\u002Fserver\u002Fsrc\u002Fregister.ts`  \n用途：\n- 自动完成 5 步注册流程并拿到 `supabaseRefreshToken`\n- 自动合并到 `apps\u002Fserver\u002Faccounts.json`\n- 记录到 `apps\u002Fserver\u002Fregistered.json`\n\n已修复路径解析问题：默认输出路径现在基于脚本目录计算，不再受运行时 `cwd` 影响。  \n即使使用 `pnpm --filter @apps\u002Fserver exec tsx src\u002Fregister.ts`，也会稳定写入：\n- `apps\u002Fserver\u002Faccounts.json`\n- `apps\u002Fserver\u002Fregistered.json`\n\n### 12.2 流式接口验证（凭证可用性）\n\n启动服务：\n\n```bash\npnpm --filter @apps\u002Fserver dev\n```\n\n流式测试（OpenAI 兼容）：\n\n```bash\ncurl -N http:\u002F\u002F127.0.0.1:8787\u002Fv1\u002Fchat\u002Fcompletions \\\n  -H \"Authorization: Bearer test-key\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"model\":\"claude-sonnet-4-6\",\n    \"stream\": true,\n    \"messages\":[{\"role\":\"user\",\"content\":\"请只回复OK\"}]\n  }'\n```\n\n通过标准：\n- HTTP 200\n- 持续收到 `data: {...chat.completion.chunk...}`\n- 末尾收到 `data: [DONE]`\n","该项目是一个用于CTF竞赛或研究场景下的代理工具，旨在将`https:\u002F\u002Fbeta.magai.co`的聊天服务封装为兼容OpenAI和Anthropic API的接口。其核心功能包括多账号轮询机制、自动注册Magai账号以及提供一个Web管理门户来维护账号信息与状态监控。技术上，项目使用TypeScript编写，并支持一键部署脚本，简化了环境搭建过程。此外，它还提供了详细的配置指南和非交互模式下的参数化安装选项，便于自动化集成。此工具特别适合需要模拟或扩展AI聊天服务的应用场景，如安全测试、逆向工程研究等。",2,"2026-06-11 04:04:52","CREATED_QUERY"]