Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e9cc088
ci: add Docker workflow and update Go version
youyao666 Apr 18, 2026
f1f7190
feat: harden dashboard and expand account runtime support
youyao666 Apr 19, 2026
5ef4488
Merge remote-tracking branch 'userfork/main' into feat/docker-packaging
youyao666 Apr 19, 2026
c8c25d0
feat: improve NewAPI compatibility and health endpoints
youyao666 Apr 19, 2026
409e753
chore: harden Docker runtime and health checks
youyao666 Apr 19, 2026
112c251
chore: format Go sources for CI
youyao666 Apr 19, 2026
33c0848
fix: stabilize Gemini cookie account handling
youyao666 Apr 30, 2026
53be9bd
merge: stabilize Gemini cookie account handling
youyao666 Apr 30, 2026
3769576
feat: add a stable pro deep think alias
youyao666 Apr 30, 2026
e42eab8
fix deep-think request mode to return sync final answers
youyao666 Apr 30, 2026
0c14749
improve tool-call parsing and add regression tests
youyao666 Apr 30, 2026
2a5a0a3
merge deep-think and tool-call parser updates
youyao666 Apr 30, 2026
149f9f8
fix deep-think polling and docker workflow
youyao666 May 1, 2026
8a6b3fd
feat: add multimodal message parsing
youyao666 May 1, 2026
a14e230
feat: capture larger Gemini payloads
youyao666 May 1, 2026
9345693
feat: add multimodal prompt handling
youyao666 May 1, 2026
0a4bb94
feat: parse batchexecute media payloads
youyao666 May 1, 2026
f45b493
feat: pass multimodal images through server
youyao666 May 1, 2026
58a39c7
Potential fix for pull request finding
MapleLeaf2007 May 2, 2026
8440ece
Merge branch 'main' into feat/multimodal-image-video
MapleLeaf2007 May 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: CI

on:
pull_request:
branches: [main]
push:
branches: [main]

permissions:
contents: read

jobs:
go-quality:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Check formatting
run: test -z "$(gofmt -l .)"

- name: Vet
run: go vet ./...

- name: Test
run: go test ./...

- name: Build
run: go build ./...
57 changes: 57 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Docker

on:
workflow_dispatch:
pull_request:
branches:
- main
push:
branches:
- main
tags:
- 'v*'

permissions:
contents: read
packages: write

jobs:
docker:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=sha
type=ref,event=tag

- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: ⚙️ 设置 Go 环境
uses: actions/setup-go@v4
with:
go-version: '1.21'
go-version: '1.25.5'
cache: true

- name: 📦 下载依赖
Expand Down
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ main.zip

# Local config
config.json
state.json

# Temporary/test files
test.txt
*.exe
*.exe
*.exe~
*.har
*.ndjson
__pycache__/
browser-profile/
capture_gemini_mitm.py
12 changes: 10 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@ FROM alpine:3.21

WORKDIR /app

RUN apk add --no-cache ca-certificates tzdata && update-ca-certificates
RUN apk add --no-cache ca-certificates tzdata wget su-exec && update-ca-certificates \
&& addgroup -S app && adduser -S -G app app \
&& mkdir -p /app && chown -R app:app /app

COPY --from=builder /out/geminiweb2api /app/geminiweb2api
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh

RUN chown app:app /app/geminiweb2api \
&& chmod +x /usr/local/bin/docker-entrypoint.sh

EXPOSE 8080

VOLUME ["/app"]

CMD ["/app/geminiweb2api"]
HEALTHCHECK --interval=30s --timeout=10s --start-period=45s --retries=3 CMD sh -c 'wget -q -O /dev/null http://127.0.0.1:8080/api/telemetry || exit 1'

ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
192 changes: 192 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,30 @@ docker run -d \
geminiweb2api:latest
```

Health check inside the container:

```bash
curl -s http://127.0.0.1:8080/api/telemetry
```

The image prefers running as a non-root user and includes a Docker `HEALTHCHECK` based on `/api/telemetry`.

If the mounted `/app` volume is not writable by the non-root user in your Docker environment, the entrypoint automatically falls back to root so the container can still start instead of crashing on permissions.

Use `GET /healthz` for upstream business health checks. It returns `503` when the service is up but no account is currently healthy.

Docker Compose:

```bash
cp config.json.example config.json
docker compose up -d --build
```

Optional environment overrides in `docker compose`:

- `GEMINIWEB2API_API_KEY`
- `GEMINIWEB2API_PUBLIC_ACCOUNT_STATUS`

### Configuration

Use `config.json` in the project root. You can start from `config.json.example`:
Expand Down Expand Up @@ -128,8 +145,14 @@ Use `config.json` in the project root. You can start from `config.json.example`:
Gemini web cookie string. Recommended when anonymous access is unstable or the environment requires sign-in state.
- `tokens`
Reserved field. Currently unused.
- `accounts`
Optional multi-account pool. When present, requests are assigned by session binding plus round-robin selection across healthy accounts. Each account supports `id`, `email`, `cookies`, `token`, `proxy`, `enabled`, and `weight`.
- `proxy`
Explicit proxy such as `http://127.0.0.1:7890`. The app also respects `HTTP_PROXY`, `HTTPS_PROXY`, and `ALL_PROXY`.
- `models`
Optional model ID list returned by `GET /v1/models`. If empty, the built-in default Gemini model list is used.
- `model_aliases`
Optional request model alias map. Example: map `gpt-4.1` to `gemini-3-pro` for upstream panels such as NewAPI.
- `gemini_url`
Override for the Gemini generation endpoint in reverse-proxy setups.
- `gemini_home_url`
Expand All @@ -140,9 +163,30 @@ Use `config.json` in the project root. You can start from `config.json.example`:
Log output path. Empty means stdout.
- `log_level`
`debug`, `info`, `warn`, or `error`.
- `public_account_status`
Defaults to `false`. When `false`, `GET /api/accounts` and `GET /api/accounts/bindings` require `Authorization: Bearer <api_key>`. Set to `true` only for trusted local deployments where unauthenticated read-only status is acceptable.
- `note`
Free-form note strings surfaced by `/api/telemetry` and the WebUI.

### Environment Variables

Production deployments can override selected `config.json` values with environment variables:

- `GEMINIWEB2API_API_KEY`
- `GEMINIWEB2API_PROXY`
- `GEMINIWEB2API_PORT`
- `GEMINIWEB2API_LOG_LEVEL`
- `GEMINIWEB2API_PUBLIC_ACCOUNT_STATUS`

Environment values take precedence over `config.json` at load time.

### Security Notes

- Do not commit `config.json`; it can contain API keys, Google cookies, tokens, and proxies.
- Keep `public_account_status` disabled for public or production deployments.
- Management APIs that mutate accounts always require `Authorization: Bearer <api_key>`.
- The authenticated account details endpoint can return full cookies and tokens; only expose the service behind trusted networks or authentication layers.

### Hot Reload

The process checks `config.json` every 5 seconds and reloads it automatically when the file changes. You do not need to restart the service after editing the config.
Expand Down Expand Up @@ -192,6 +236,109 @@ SID=...; APISID=...; SAPISID=...; ...

Do not commit real cookies to a public repository.

### Multi-Account Pool

You can now run the proxy in multi-account mode by filling `accounts` in `config.json`.

Example:

```json
{
"api_key": "your-api-key-here",
"accounts": [
{
"id": "acc-1",
"email": "first@example.com",
"cookies": "SID=...; APISID=...",
"token": "",
"proxy": "",
"enabled": true,
"weight": 1
},
{
"id": "acc-2",
"email": "second@example.com",
"cookies": "SID=...; APISID=...",
"token": "",
"proxy": "http://user:pass@proxy-host:port",
"enabled": true,
"weight": 1
}
]
}
```

Behavior:

- The same `X-Session-ID` stays bound to the same account while that account is healthy.
- New sessions are assigned by round-robin across healthy accounts.
- Failed accounts enter exponential backoff starting at 30 seconds, doubling up to 30 minutes.
- If an account has `proxy`, token refresh and Gemini requests for that account use that proxy.
- If account `proxy` is empty, the service falls back to the global `proxy` setting or the machine's proxy environment.
- If `accounts` is empty, the service falls back to the legacy single-account `cookies` and `token` fields.

### Session Binding Persistence

Session-to-account bindings are persisted in `state.json` beside `config.json`.

- Persisted: session/account binding, bind time, last used time
- Not persisted: short-lived runtime page tokens like `SNlM0e`, `BL`, `f.sid`

On restart, bindings are restored when the referenced account still exists.

### Account Pool APIs

- `GET /api/accounts`
Returns configured accounts and runtime state.
- `POST /api/accounts`
Creates or updates an account.
- `GET /api/accounts/bindings`
Returns current session-to-account bindings.
- `POST /api/accounts/{id}/enable`
Enables an account.
- `POST /api/accounts/{id}/disable`
Disables an account.
- `POST /api/accounts/{id}/refresh`
Refreshes token state for one account immediately.

All account APIs require `Authorization: Bearer <api_key>`.

### Google Account Manager v1.8 Compatibility

The legacy Google account manager can keep using its existing Gemini session callback:

```http
POST /api/session/cookies
Authorization: Bearer <api_key>
Content-Type: application/json
```

Body:

```json
{
"email": "account@gmail.com",
"cookies": "SID=...; __Secure-1PSID=...; ...",
"proxy": "http://user:pass@proxy-host:port",
"persist": true
}
```

When `email` is present, this endpoint now upserts the cookie into the multi-account pool instead of only updating the legacy single-account `cookies` field. The generated account ID uses the email directly, for example:

```text
account@gmail.com
```

In the Google account manager settings, set:

- `GEMINIWEB2API_URL` to this service, for example `http://127.0.0.1:8080`
- `GEMINIWEB2API_KEY` to this service's `api_key`
- `GEMINIWEB2API_PERSIST` to `true` if you want updates written to `config.json`
- Optional `GEMINIWEB2API_ACCOUNT_PROXY` if all callbacks from that manager should use the same outbound proxy in this service

Then use its existing `抓 Session` / `批量抓 Session` action. Successful callbacks should show the imported account in this service's account pool.

### Usage Examples

#### Health check
Expand Down Expand Up @@ -240,6 +387,51 @@ curl -N "http://127.0.0.1:8080/v1/chat/completions" \
}'
```

### Use Behind NewAPI

If you run a NewAPI panel or any OpenAI-compatible gateway, the recommended topology is:

1. Google cookie -> `geminiweb2api`
2. NewAPI upstream -> `geminiweb2api`
3. End users -> NewAPI

Recommended upstream settings in NewAPI:

- Base URL: `http://your-geminiweb2api-host:8080/v1`
- API Key: the `api_key` from `config.json`
- Model discovery: `GET /v1/models`
- Chat endpoint: `POST /v1/chat/completions`
- Responses endpoint: `POST /v1/responses`
- Health check: `GET /healthz`

Notes:

- `GET /v1/models` also requires `Authorization: Bearer <api_key>`.
- `POST /v1/responses` is supported as a minimal compatibility layer and is internally translated into `/v1/chat/completions` for text input.
- Streaming is supported with SSE and ends with `data: [DONE]`. The current implementation streams incremental chunks from the final Gemini content instead of a true token-by-token upstream stream.
- `stream_options.include_usage` is supported.
- `model_aliases` can be used to align NewAPI/OpenAI-style model names with Gemini model IDs.
- Common OpenAI/NewAPI fields such as `max_completion_tokens`, `top_p`, `presence_penalty`, `frequency_penalty`, `response_format`, and `user` are accepted for compatibility. Some are pass-through compatibility fields and may not materially change Gemini Web behavior.

Recommended model names for upstream mapping:

- `gemini-3-flash`
- `gemini-3`
- `gemini-3-pro`
- `gemini-2.5-flash`
- `gemini-2.5-pro`

Suggested first-choice default:

- `gemini-3-flash`

Example NewAPI health probe:

```bash
curl -s "http://127.0.0.1:8080/v1/models" \
-H "Authorization: Bearer your-api-key-here"
```

### Session Continuity

- Keep `X-Session-ID` stable for the same user or conversation.
Expand Down
Loading
Loading