1实现原理 · 为什么它能做到
实现基础是一份手写的 Gemini Web 客户端:把开源 gemini_webapi 逐文件移植为 TypeScript(client/types/utils),因此能在没有官方 API Key 的情况下出文本与图像。
| `scripts/gemini-webapi/*` | TypeScript port of `gemini_webapi` (GeminiClient, types, utils) |
端点集中在 constants.ts:INIT 取页面令牌、StreamGenerate 走私有流式接口、ROTATE_COOKIES 轮换会话 cookie、UPLOAD 传参考图、BATCH_EXEC 走 batchexecute。
'https://gemini.google.com/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate',
认证第一步是取 access token:init 时用已有 cookie 调 get_access_token,拿不到有效 token 就抛错——所有生成请求都要带上它。
const [token, valid] = await get_access_token(this.cookies, this.proxy, vb);
凭据来源是浏览器会话 cookie:load-browser-cookies 会探测本机调试端口(/json/version,默认 127.0.0.1)并按需 spawn 浏览器进程读取真实登录态。
const res = await fetch_with_timeout(`http://127.0.0.1:${port}/json/version`, { timeout_ms: 5_000 });
cookie 与轮换结果被持久化到本地文件:cookie-file 以 JSON 写盘,另有 rotate-1psidts 把刷新得到的 1PSIDTS 缓存到文件。
await writeFile(p, JSON.stringify(payload, null, 2), 'utf8');
图像能力落在 types/image.ts:下载远端图像并写盘(writeFile(dest, buf)),参考图则经 upload-file 走 content-push 上传通道。
await writeFile(dest, buf);
多轮会话不是服务端会话,而是把 Gemini 的 chat metadata 存进本地 sessions/<id>.json,靠 --sessionId 复用同一 metadata 续聊。
Session files stored in data directory under `sessions/<id>.json`.
逆向使用被显式要求“先同意”:首次必须校验 consent 文件(accepted 且 disclaimerVersion 1.0),拒绝则退出。
1. Check if consent file exists with `accepted: true` and `disclaimerVersion: "1.0"`
请求层带重试装饰器 running:客户端未初始化时自动 init,遇到 APIError 且有剩余重试次数则 sleep 1s 后重试。
if (e instanceof APIError && r > 0) {
2核心能力
3外部依赖
| 类型 | 依赖 |
|---|---|
| network | gemini.google.com(INIT 页面取令牌 + StreamGenerate 私有生成接口 + batchexecute) |
| network | accounts.google.com(RotateCookies 轮换会话 cookie) |
| network | content-push.googleapis.com(参考图上传) |
| network | lh3.googleusercontent.com(返回图像资源 gg-dl) |
| cli | bun / npx(以 npx -y bun 直接运行脚本,无运行时抽象) |
| cli | 本机 Chrome/Chromium/Edge 可执行文件(按需 spawn 以读取会话 cookie) |
4风险提醒 风险提醒:红色 · 谨慎使用
- 账号风险(逆向接口 + 冒充网页会话) — 私有端点与网页会话冒充随时可能触发风控或失效;exceptions.ts 亦备有 TemporarilyBlocked。建议只用小号/非关键账号。
- 浏览器凭据被读取并落盘 — 经本机调试端口从真实浏览器会话取 __Secure-1PSID/1PSIDTS 并写入 cookies.json 与 1PSIDTS 缓存;这些文件等价于登录态,需按密钥对待。
- --promptfiles 是无过滤的本地文件读取入口 — 被指向的文件内容会拼进 prompt 发往 Google;上层 skill 或用户若不慎指向密钥文件,等于主动外泄。
- consent 只是文档级约束 — 脚本自身不校验 consent.json,靠 agent 按 SKILL.md 执行;在无 skill 规范的宿主里可能被直接调用。
- 接口随时可能失效 — 依赖逆向私有端点,Google 改前端即可能整体失效,且无官方支持渠道。
- 内容出境与合规 — 提示词与参考图都会离开本机(含上传到 content-push.googleapis.com),企业内网/涉密内容不适用。
5第二遍独立确认
- [ok] 逆向 API 而非官方 API(red 主因之一) — constants.ts 直指 gemini.google.com 的 StreamGenerate 私有路径;SKILL.md/description 自述 reverse-engineered,并设 consent 门禁。
- [ok] 读取浏览器会话 cookie(red 主因之二) — load-browser-cookies.ts 探测 127.0.0.1 调试端口并按需 spawn 浏览器;SKILL.md 自述 First run opens browser for Google auth。
- [ok] 凭据落盘 — cookie-file.ts 的 writeFile(p, JSON.stringify(payload, null, 2)) 与 rotate-1psidts.ts 的 writeFile(cachePath, v) 两处写盘,路径由 paths.ts 解析。
- [ok] 无 TLS 降级 / 无 stealth 伪装 / 无任意代码执行 — 相关行为在 bundle 正文与 §3 风险扫描中均零命中;调度只有 init 重试与 sleep。
- [ok] consent 门禁存在但为“软门禁” — SKILL.md 要求先建 consent.json 再使用;但脚本自身不校验该文件,属文档级约束,已写入 risks。
- [ok] 功能声明与实现相符 — 文本/图像/vision/多轮四项在 SKILL.md Options 与 main.ts/client.ts 均有对应实现,未见夸大。
6结论
a98a97c1f7c81879…dd0bf35595