Spaces:
Runtime error
Runtime error
akashyadav758 Claude Opus 4.8 (1M context) commited on
Commit ·
5c7f4b5
1
Parent(s): 90bcc1f
Harden monitor (gate all routes), swap ChatGPT backend to free-Chatgpt-api, clean repo
Browse files- Security: gate /, /api/*, /chrome.log behind API_KEY (Bearer / ?key= / apikey
cookie). Previously these were public — anyone could view+drive the logged-in
ChatGPT/Gemini/Google sessions. This was the likely "abusive" flag cause.
- ChatGPT backend: replace chatgpt-free-api with the free-Chatgpt-api rewrite
(drop-in: same 127.0.0.1:9225, same /api/chat + /v1/chat/completions). Update
Dockerfile COPY paths.
- Cleanup: drop leftover per-service Dockerfiles, redundant sub-READMEs, dev-only
docs/test scripts, the prebuilt linux binary, and cdp_* debug scripts.
- Docs: fix references, mark all routes key-gated, note the Space is now private.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Dockerfile +3 -3
- HOWTOUSE.md +8 -8
- README.md +22 -21
- SETUP.md +2 -2
- chatgpt-free-api/Dockerfile +0 -17
- chatgpt-free-api/README.md +0 -56
- chatgpt-free-api/test.sh +0 -124
- docs/architecture.md +1 -1
- docs/deployment.md +2 -2
- docs/troubleshooting.md +2 -2
- flow-agent/Dockerfile +0 -16
- flow-agent/README.md +0 -468
- flow-agent/SNIFFING.md +0 -205
- flow-agent/error.md +0 -74
- flow-agent/test_api.py +0 -319
- {chatgpt-free-api → free-Chatgpt-api}/.gitignore +3 -0
- {chatgpt-free-api → free-Chatgpt-api}/agent.go +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/api_bridge.go +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/chatgpt.go +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/config.go +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/config.json +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/cookies.go +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/cookies_test.go +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/extension.go +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/go.mod +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/go.sum +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/gpt-extension/background.js +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/gpt-extension/icon128.png +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/gpt-extension/icon16.png +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/gpt-extension/icon48.png +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/gpt-extension/icon_large.png +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/gpt-extension/manifest.json +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/gpt-extension/popup.html +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/gpt-extension/popup.js +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/handlers.go +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/helpers.go +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/main.go +0 -0
- {chatgpt-free-api → free-Chatgpt-api}/sniff.go +0 -0
- free-gemini-api/.dockerignore +0 -8
- free-gemini-api/Dockerfile +0 -21
- free-gemini-api/README.md +0 -401
- monitor/main.go +36 -11
Dockerfile
CHANGED
|
@@ -13,7 +13,7 @@ RUN go mod tidy && CGO_ENABLED=0 go build -ldflags="-s -w" -o /profilesync .
|
|
| 13 |
# Build the ChatGPT API server (talks to gpt-extension on 127.0.0.1:9225).
|
| 14 |
FROM golang:1.26-bookworm AS chatgpt-build
|
| 15 |
WORKDIR /src
|
| 16 |
-
COPY
|
| 17 |
RUN go mod download && CGO_ENABLED=0 go build -ldflags="-s -w" -o /agent .
|
| 18 |
|
| 19 |
# Build the Gemini API server (talks to gemini-extension on 127.0.0.1:9226).
|
|
@@ -27,7 +27,7 @@ FROM akashyadav758/chrome:latest
|
|
| 27 |
USER root
|
| 28 |
|
| 29 |
# Copy extensions into the container
|
| 30 |
-
COPY
|
| 31 |
COPY free-gemini-api/gemini-extension /opt/gemini-extension
|
| 32 |
COPY flow-agent/Flow-extension /opt/flow-extension
|
| 33 |
|
|
@@ -39,7 +39,7 @@ COPY --from=profilesync-build /profilesync /opt/profilesync/profilesync
|
|
| 39 |
|
| 40 |
# Backend API servers (run on localhost; the monitor gateway fronts them).
|
| 41 |
COPY --from=chatgpt-build /agent /opt/chatgpt/agent
|
| 42 |
-
COPY
|
| 43 |
COPY --from=gemini-build /free-gemini-api /opt/gemini-srv/free-gemini-api
|
| 44 |
COPY flow-agent/ /opt/flow-srv/
|
| 45 |
|
|
|
|
| 13 |
# Build the ChatGPT API server (talks to gpt-extension on 127.0.0.1:9225).
|
| 14 |
FROM golang:1.26-bookworm AS chatgpt-build
|
| 15 |
WORKDIR /src
|
| 16 |
+
COPY free-Chatgpt-api/ ./
|
| 17 |
RUN go mod download && CGO_ENABLED=0 go build -ldflags="-s -w" -o /agent .
|
| 18 |
|
| 19 |
# Build the Gemini API server (talks to gemini-extension on 127.0.0.1:9226).
|
|
|
|
| 27 |
USER root
|
| 28 |
|
| 29 |
# Copy extensions into the container
|
| 30 |
+
COPY free-Chatgpt-api/gpt-extension /opt/gpt-extension
|
| 31 |
COPY free-gemini-api/gemini-extension /opt/gemini-extension
|
| 32 |
COPY flow-agent/Flow-extension /opt/flow-extension
|
| 33 |
|
|
|
|
| 39 |
|
| 40 |
# Backend API servers (run on localhost; the monitor gateway fronts them).
|
| 41 |
COPY --from=chatgpt-build /agent /opt/chatgpt/agent
|
| 42 |
+
COPY free-Chatgpt-api/config.json /opt/chatgpt/config.json
|
| 43 |
COPY --from=gemini-build /free-gemini-api /opt/gemini-srv/free-gemini-api
|
| 44 |
COPY flow-agent/ /opt/flow-srv/
|
| 45 |
|
HOWTOUSE.md
CHANGED
|
@@ -3,14 +3,14 @@
|
|
| 3 |
One base URL fronts all three services. Every `/gpt`, `/gemini`, `/flow` call needs the
|
| 4 |
`API_KEY` as a Bearer token. The monitor UI (`/`) needs no key.
|
| 5 |
|
| 6 |
-
- **Base URL:** `https://akash1313-selfapi.hf.space`
|
| 7 |
- **Auth header:** `Authorization: Bearer YOUR_API_KEY` *(your real key is in the gitignored `./api-key`)*
|
| 8 |
|
| 9 |
> ⚠️ **You must be logged in** to ChatGPT / Gemini / Flow inside the browser
|
| 10 |
-
> (`https://akash1313-selfapi.hf.space/`) for real responses. The servers reply even when
|
| 11 |
> logged out, but with `extensionConnected:false` / empty data. Check first:
|
| 12 |
> ```bash
|
| 13 |
-
> curl -H "Authorization: Bearer YOUR_API_KEY" https://akash1313-selfapi.hf.space/gpt/health
|
| 14 |
> ```
|
| 15 |
> `extensionConnected:true` = ready.
|
| 16 |
|
|
@@ -27,12 +27,12 @@ One base URL fronts all three services. Every `/gpt`, `/gemini`, `/flow` call ne
|
|
| 27 |
|
| 28 |
```bash
|
| 29 |
# Simple chat
|
| 30 |
-
curl -X POST https://akash1313-selfapi.hf.space/gpt/api/chat \
|
| 31 |
-H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
|
| 32 |
-d '{"prompt":"Write a haiku about the sea","wait_for_response":true}'
|
| 33 |
|
| 34 |
# OpenAI-compatible (drop-in for OpenAI SDKs — set base_url=.../gpt/v1)
|
| 35 |
-
curl -X POST https://akash1313-selfapi.hf.space/gpt/v1/chat/completions \
|
| 36 |
-H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
|
| 37 |
-d '{"model":"gpt-5","messages":[{"role":"user","content":"hello"}],"stream":false}'
|
| 38 |
```
|
|
@@ -51,7 +51,7 @@ Optional fields: `"model"`, `"thinking_effort"`, `"conversation_id"` (continue a
|
|
| 51 |
| `/gemini/output/<file>` | GET | — (generated media) |
|
| 52 |
|
| 53 |
```bash
|
| 54 |
-
curl -X POST https://akash1313-selfapi.hf.space/gemini/chat \
|
| 55 |
-H "Authorization: Bearer YOUR_API_KEY" \
|
| 56 |
-d "prompt=Explain quantum entanglement simply&new_chat=true"
|
| 57 |
```
|
|
@@ -70,12 +70,12 @@ curl -X POST https://akash1313-selfapi.hf.space/gemini/chat \
|
|
| 70 |
|
| 71 |
```bash
|
| 72 |
# Text-to-video (aspect: portrait|landscape ; duration: 4,6,8,10)
|
| 73 |
-
curl -X POST https://akash1313-selfapi.hf.space/flow/generate/video \
|
| 74 |
-H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
|
| 75 |
-d '{"prompt":"a cat surfing a wave, cinematic","aspect":"landscape","duration":8}'
|
| 76 |
|
| 77 |
# Text-to-image
|
| 78 |
-
curl -X POST https://akash1313-selfapi.hf.space/flow/generate/image \
|
| 79 |
-H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
|
| 80 |
-d '{"prompt":"neon city at night","aspect":"square"}'
|
| 81 |
```
|
|
|
|
| 3 |
One base URL fronts all three services. Every `/gpt`, `/gemini`, `/flow` call needs the
|
| 4 |
`API_KEY` as a Bearer token. The monitor UI (`/`) needs no key.
|
| 5 |
|
| 6 |
+
- **Base URL:** `https://akash1313-selfapi-v2.hf.space`
|
| 7 |
- **Auth header:** `Authorization: Bearer YOUR_API_KEY` *(your real key is in the gitignored `./api-key`)*
|
| 8 |
|
| 9 |
> ⚠️ **You must be logged in** to ChatGPT / Gemini / Flow inside the browser
|
| 10 |
+
> (`https://akash1313-selfapi-v2.hf.space/`) for real responses. The servers reply even when
|
| 11 |
> logged out, but with `extensionConnected:false` / empty data. Check first:
|
| 12 |
> ```bash
|
| 13 |
+
> curl -H "Authorization: Bearer YOUR_API_KEY" https://akash1313-selfapi-v2.hf.space/gpt/health
|
| 14 |
> ```
|
| 15 |
> `extensionConnected:true` = ready.
|
| 16 |
|
|
|
|
| 27 |
|
| 28 |
```bash
|
| 29 |
# Simple chat
|
| 30 |
+
curl -X POST https://akash1313-selfapi-v2.hf.space/gpt/api/chat \
|
| 31 |
-H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
|
| 32 |
-d '{"prompt":"Write a haiku about the sea","wait_for_response":true}'
|
| 33 |
|
| 34 |
# OpenAI-compatible (drop-in for OpenAI SDKs — set base_url=.../gpt/v1)
|
| 35 |
+
curl -X POST https://akash1313-selfapi-v2.hf.space/gpt/v1/chat/completions \
|
| 36 |
-H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
|
| 37 |
-d '{"model":"gpt-5","messages":[{"role":"user","content":"hello"}],"stream":false}'
|
| 38 |
```
|
|
|
|
| 51 |
| `/gemini/output/<file>` | GET | — (generated media) |
|
| 52 |
|
| 53 |
```bash
|
| 54 |
+
curl -X POST https://akash1313-selfapi-v2.hf.space/gemini/chat \
|
| 55 |
-H "Authorization: Bearer YOUR_API_KEY" \
|
| 56 |
-d "prompt=Explain quantum entanglement simply&new_chat=true"
|
| 57 |
```
|
|
|
|
| 70 |
|
| 71 |
```bash
|
| 72 |
# Text-to-video (aspect: portrait|landscape ; duration: 4,6,8,10)
|
| 73 |
+
curl -X POST https://akash1313-selfapi-v2.hf.space/flow/generate/video \
|
| 74 |
-H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
|
| 75 |
-d '{"prompt":"a cat surfing a wave, cinematic","aspect":"landscape","duration":8}'
|
| 76 |
|
| 77 |
# Text-to-image
|
| 78 |
+
curl -X POST https://akash1313-selfapi-v2.hf.space/flow/generate/image \
|
| 79 |
-H "Authorization: Bearer YOUR_API_KEY" -H "Content-Type: application/json" \
|
| 80 |
-d '{"prompt":"neon city at night","aspect":"square"}'
|
| 81 |
```
|
README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
---
|
| 2 |
-
title: Selfapi
|
| 3 |
emoji: 📊
|
| 4 |
colorFrom: gray
|
| 5 |
colorTo: blue
|
|
@@ -8,9 +8,9 @@ app_port: 3001
|
|
| 8 |
pinned: false
|
| 9 |
---
|
| 10 |
|
| 11 |
-
# Selfapi — Headless Chrome + Unpacked Extensions
|
| 12 |
|
| 13 |
-
A [Hugging Face Space](https://huggingface.co/spaces/akash1313/selfapi) that runs a
|
| 14 |
persistent, headless **Chrome for Testing** browser inside Docker with three custom
|
| 15 |
unpacked extensions auto-loaded, plus a live web monitor to watch the browser in real time.
|
| 16 |
|
|
@@ -25,12 +25,12 @@ automate workflows for ChatGPT, Google Gemini, and Google Flow.
|
|
| 25 |
|
| 26 |
| Extension | Source dir | Loaded at | What it does |
|
| 27 |
|-----------|-----------|-----------|--------------|
|
| 28 |
-
| **Gpt Agent Api** | `
|
| 29 |
| **Gemini Api Agent** | `free-gemini-api/gemini-extension` | `/opt/gemini-extension` | Auto-syncs Google Gemini cookies to a local Free Gemini API server |
|
| 30 |
| **Flow Agent** | `flow-agent/Flow-extension` | `/opt/flow-extension` | Automates Google Flow — T2V / V2V / I2V video + T2I / I2I image generation |
|
| 31 |
|
| 32 |
All three are Manifest V3 extensions with background service workers. Each subfolder
|
| 33 |
-
also contains the matching backend server
|
| 34 |
|
| 35 |
---
|
| 36 |
|
|
@@ -42,9 +42,9 @@ run yourself — see each subproject's README for setup):
|
|
| 42 |
|
| 43 |
| Project | Server | Talks to | Purpose |
|
| 44 |
|---------|--------|----------|---------|
|
| 45 |
-
|
|
| 46 |
-
|
|
| 47 |
-
|
|
| 48 |
|
| 49 |
> ✅ The HF Space now runs **all three servers in the same container** as Chrome, and the
|
| 50 |
> monitor doubles as a **single API gateway** that fronts them on one public port — see
|
|
@@ -162,7 +162,7 @@ Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/14
|
|
| 162 |
|
| 163 |
A **macOS UA on a Linux box is an inconsistency** that Cloudflare Turnstile flags, causing
|
| 164 |
the ChatGPT "verify you are human" check to loop forever (the tick never clears). The browser
|
| 165 |
-
UA lives in `start_hf.sh`; the ChatGPT backend (`
|
| 166 |
UA and Chrome version (145)** so the `cf_clearance` cookie — which Cloudflare binds to the
|
| 167 |
exact UA — stays valid across the browser and the backend.
|
| 168 |
|
|
@@ -172,11 +172,11 @@ exact UA — stays valid across the browser and the backend.
|
|
| 172 |
|
| 173 |
| URL | Description |
|
| 174 |
|-----|-------------|
|
| 175 |
-
| `https://akash1313-selfapi.hf.space/` | Live Chrome monitor UI (open
|
| 176 |
-
| `https://akash1313-selfapi.hf.space/chrome.log` | Streaming Chrome log
|
| 177 |
-
| `https://akash1313-selfapi.hf.space/gpt/…` | ChatGPT API (gateway → `127.0.0.1:9225`) |
|
| 178 |
-
| `https://akash1313-selfapi.hf.space/gemini/…` | Gemini API (gateway → `127.0.0.1:8000`) |
|
| 179 |
-
| `https://akash1313-selfapi.hf.space/flow/…` | Flow API (gateway → `127.0.0.1:8101`) |
|
| 180 |
| `127.0.0.1:9222` (in-container) | Chrome DevTools Protocol |
|
| 181 |
| `:9223` | CDP proxied for external tools (via socat) |
|
| 182 |
|
|
@@ -190,21 +190,22 @@ gateway strips the prefix and forwards to the server on localhost:
|
|
| 190 |
|
| 191 |
| Prefix | Backend | Example endpoints |
|
| 192 |
|--------|---------|-------------------|
|
| 193 |
-
| `/gpt/` |
|
| 194 |
| `/gemini/` | free-gemini-api `:8000` | `/gemini/chat`, `/gemini/status`, `/gemini/output/*` |
|
| 195 |
| `/flow/` | flow-agent `:8101` | `/flow/generate/video`, `/flow/generate/image`, `/flow/health` |
|
| 196 |
|
| 197 |
### Auth — required
|
| 198 |
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
|
|
|
| 203 |
|
| 204 |
1. Set the Space secret **`API_KEY`** (Settings → Variables and secrets) to a strong random string.
|
| 205 |
2. Call it:
|
| 206 |
```bash
|
| 207 |
-
curl -X POST https://akash1313-selfapi.hf.space/gpt/api/chat \
|
| 208 |
-H "Authorization: Bearer $API_KEY" \
|
| 209 |
-H "Content-Type: application/json" \
|
| 210 |
-d '{"message":"hello"}'
|
|
@@ -222,7 +223,7 @@ chrome-space/
|
|
| 222 |
├── start_hf.sh # container entrypoint / Chrome boot sequence
|
| 223 |
├── profilesync/ # Go tool: Chrome profile <-> Postgres snapshot
|
| 224 |
├── monitor/ # Go 3-tab live monitor (GPT / Gemini / Flow)
|
| 225 |
-
├──
|
| 226 |
├── free-gemini-api/ # Free Gemini API server + gemini-extension
|
| 227 |
└── flow-agent/ # Flow automation CLI + Flow-extension
|
| 228 |
```
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Selfapi-v2
|
| 3 |
emoji: 📊
|
| 4 |
colorFrom: gray
|
| 5 |
colorTo: blue
|
|
|
|
| 8 |
pinned: false
|
| 9 |
---
|
| 10 |
|
| 11 |
+
# Selfapi-v2 — Headless Chrome + Unpacked Extensions
|
| 12 |
|
| 13 |
+
A [Hugging Face Space](https://huggingface.co/spaces/akash1313/selfapi-v2) that runs a
|
| 14 |
persistent, headless **Chrome for Testing** browser inside Docker with three custom
|
| 15 |
unpacked extensions auto-loaded, plus a live web monitor to watch the browser in real time.
|
| 16 |
|
|
|
|
| 25 |
|
| 26 |
| Extension | Source dir | Loaded at | What it does |
|
| 27 |
|-----------|-----------|-----------|--------------|
|
| 28 |
+
| **Gpt Agent Api** | `free-Chatgpt-api/gpt-extension` | `/opt/gpt-extension` | Auto-syncs ChatGPT cookies to a local ChatGPT Free API server |
|
| 29 |
| **Gemini Api Agent** | `free-gemini-api/gemini-extension` | `/opt/gemini-extension` | Auto-syncs Google Gemini cookies to a local Free Gemini API server |
|
| 30 |
| **Flow Agent** | `flow-agent/Flow-extension` | `/opt/flow-extension` | Automates Google Flow — T2V / V2V / I2V video + T2I / I2I image generation |
|
| 31 |
|
| 32 |
All three are Manifest V3 extensions with background service workers. Each subfolder
|
| 33 |
+
also contains the matching backend server.
|
| 34 |
|
| 35 |
---
|
| 36 |
|
|
|
|
| 42 |
|
| 43 |
| Project | Server | Talks to | Purpose |
|
| 44 |
|---------|--------|----------|---------|
|
| 45 |
+
| `free-Chatgpt-api` | Go | `127.0.0.1:9225` | ChatGPT backend API from your logged-in session |
|
| 46 |
+
| `free-gemini-api` | Go reverse-proxy | local server | Gemini text / Imagen 3 images / Gemini video |
|
| 47 |
+
| `flow-agent` | Python FastAPI + CLI | `127.0.0.1:8100` | Google Flow T2V / V2V / I2V video + T2I / I2I images |
|
| 48 |
|
| 49 |
> ✅ The HF Space now runs **all three servers in the same container** as Chrome, and the
|
| 50 |
> monitor doubles as a **single API gateway** that fronts them on one public port — see
|
|
|
|
| 162 |
|
| 163 |
A **macOS UA on a Linux box is an inconsistency** that Cloudflare Turnstile flags, causing
|
| 164 |
the ChatGPT "verify you are human" check to loop forever (the tick never clears). The browser
|
| 165 |
+
UA lives in `start_hf.sh`; the ChatGPT backend (`free-Chatgpt-api/cookies.go`) uses the **same
|
| 166 |
UA and Chrome version (145)** so the `cf_clearance` cookie — which Cloudflare binds to the
|
| 167 |
exact UA — stays valid across the browser and the backend.
|
| 168 |
|
|
|
|
| 172 |
|
| 173 |
| URL | Description |
|
| 174 |
|-----|-------------|
|
| 175 |
+
| `https://akash1313-selfapi-v2.hf.space/` | Live Chrome monitor UI — **key required** (open `/?key=YOUR_API_KEY` once; it sets a cookie) |
|
| 176 |
+
| `https://akash1313-selfapi-v2.hf.space/chrome.log` | Streaming Chrome log — **key required** |
|
| 177 |
+
| `https://akash1313-selfapi-v2.hf.space/gpt/…` | ChatGPT API (gateway → `127.0.0.1:9225`) |
|
| 178 |
+
| `https://akash1313-selfapi-v2.hf.space/gemini/…` | Gemini API (gateway → `127.0.0.1:8000`) |
|
| 179 |
+
| `https://akash1313-selfapi-v2.hf.space/flow/…` | Flow API (gateway → `127.0.0.1:8101`) |
|
| 180 |
| `127.0.0.1:9222` (in-container) | Chrome DevTools Protocol |
|
| 181 |
| `:9223` | CDP proxied for external tools (via socat) |
|
| 182 |
|
|
|
|
| 190 |
|
| 191 |
| Prefix | Backend | Example endpoints |
|
| 192 |
|--------|---------|-------------------|
|
| 193 |
+
| `/gpt/` | free-Chatgpt-api `:9225` | `/gpt/api/chat`, `/gpt/v1/chat/completions`, `/gpt/health` |
|
| 194 |
| `/gemini/` | free-gemini-api `:8000` | `/gemini/chat`, `/gemini/status`, `/gemini/output/*` |
|
| 195 |
| `/flow/` | flow-agent `:8101` | `/flow/generate/video`, `/flow/generate/image`, `/flow/health` |
|
| 196 |
|
| 197 |
### Auth — required
|
| 198 |
|
| 199 |
+
**Every** route — the API prefixes, the monitor UI (`/`), `/api/*`, `/chrome.log`, `/logs/*` — is
|
| 200 |
+
gated by an **`API_KEY`** Space secret. Send it as a Bearer token, `?key=`, or (for the browser UI)
|
| 201 |
+
open `/?key=YOUR_API_KEY` once to set an `apikey` cookie. **Fail-closed:** if `API_KEY` is unset
|
| 202 |
+
every route returns `503`, so the logged-in accounts are never exposed. This Space is also
|
| 203 |
+
**private** on Hugging Face, so the URL itself requires HF auth — defense in depth.
|
| 204 |
|
| 205 |
1. Set the Space secret **`API_KEY`** (Settings → Variables and secrets) to a strong random string.
|
| 206 |
2. Call it:
|
| 207 |
```bash
|
| 208 |
+
curl -X POST https://akash1313-selfapi-v2.hf.space/gpt/api/chat \
|
| 209 |
-H "Authorization: Bearer $API_KEY" \
|
| 210 |
-H "Content-Type: application/json" \
|
| 211 |
-d '{"message":"hello"}'
|
|
|
|
| 223 |
├── start_hf.sh # container entrypoint / Chrome boot sequence
|
| 224 |
├── profilesync/ # Go tool: Chrome profile <-> Postgres snapshot
|
| 225 |
├── monitor/ # Go 3-tab live monitor (GPT / Gemini / Flow)
|
| 226 |
+
├── free-Chatgpt-api/ # ChatGPT Free API server + gpt-extension
|
| 227 |
├── free-gemini-api/ # Free Gemini API server + gemini-extension
|
| 228 |
└── flow-agent/ # Flow automation CLI + Flow-extension
|
| 229 |
```
|
SETUP.md
CHANGED
|
@@ -9,8 +9,8 @@ Very short guide. Runs the browser + all 3 API servers on your own machine/serve
|
|
| 9 |
|
| 10 |
## 2. Get the code
|
| 11 |
```bash
|
| 12 |
-
git clone https://huggingface.co/spaces/akash1313/selfapi
|
| 13 |
-
cd selfapi
|
| 14 |
```
|
| 15 |
|
| 16 |
## 3. Run it
|
|
|
|
| 9 |
|
| 10 |
## 2. Get the code
|
| 11 |
```bash
|
| 12 |
+
git clone https://huggingface.co/spaces/akash1313/selfapi-v2
|
| 13 |
+
cd selfapi-v2
|
| 14 |
```
|
| 15 |
|
| 16 |
## 3. Run it
|
chatgpt-free-api/Dockerfile
DELETED
|
@@ -1,17 +0,0 @@
|
|
| 1 |
-
# ChatGPT Free API agent — talks to the gpt-extension over ws/http on 127.0.0.1:9225.
|
| 2 |
-
FROM golang:1.26-bookworm AS builder
|
| 3 |
-
WORKDIR /app
|
| 4 |
-
COPY go.mod go.sum ./
|
| 5 |
-
RUN go mod download
|
| 6 |
-
COPY . .
|
| 7 |
-
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o agent .
|
| 8 |
-
|
| 9 |
-
FROM debian:12-slim
|
| 10 |
-
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates && \
|
| 11 |
-
rm -rf /var/lib/apt/lists/*
|
| 12 |
-
WORKDIR /app
|
| 13 |
-
COPY --from=builder /app/agent .
|
| 14 |
-
COPY config.json .
|
| 15 |
-
|
| 16 |
-
EXPOSE 9225
|
| 17 |
-
CMD ["./agent"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chatgpt-free-api/README.md
DELETED
|
@@ -1,56 +0,0 @@
|
|
| 1 |
-
# ChatGPT Free API Agent
|
| 2 |
-
|
| 3 |
-
A flat-structure Go server that connects with the Chrome extension (`gpt-extension`) to execute ChatGPT backend API requests directly from your authenticated browser session.
|
| 4 |
-
|
| 5 |
-
## Setup & Running
|
| 6 |
-
|
| 7 |
-
1. **Build & Run the Server:**
|
| 8 |
-
```bash
|
| 9 |
-
go build -o agent .
|
| 10 |
-
./agent
|
| 11 |
-
```
|
| 12 |
-
2. **Load Chrome Extension:**
|
| 13 |
-
- Open `chrome://extensions` in Chrome.
|
| 14 |
-
- Enable **Developer mode** (top-right).
|
| 15 |
-
- Click **Load unpacked** and select the `gpt-extension` folder (in the parent directory).
|
| 16 |
-
3. **Login to ChatGPT:**
|
| 17 |
-
- Open `https://chatgpt.com/` and log in. The extension will automatically connect to the running Go agent (green status badge = connected).
|
| 18 |
-
|
| 19 |
-
---
|
| 20 |
-
|
| 21 |
-
## API Usage
|
| 22 |
-
|
| 23 |
-
### 1. Send Chat Request
|
| 24 |
-
```bash
|
| 25 |
-
curl -X POST "http://127.0.0.1:9225/api/chat" \
|
| 26 |
-
-H "Content-Type: application/json" \
|
| 27 |
-
-d '{"prompt": "Why is sky blue?", "conversation_id": ""}'
|
| 28 |
-
```
|
| 29 |
-
|
| 30 |
-
### 2. Bulk Sequential Requests
|
| 31 |
-
```bash
|
| 32 |
-
curl -X POST "http://127.0.0.1:9225/api/chat/bulk" \
|
| 33 |
-
-H "Content-Type: application/json" \
|
| 34 |
-
-d '{"prompts": ["hello", "how are you"]}'
|
| 35 |
-
```
|
| 36 |
-
|
| 37 |
-
### 3. Edit Existing Message
|
| 38 |
-
```bash
|
| 39 |
-
curl -X POST "http://127.0.0.1:9225/api/chat/edit" \
|
| 40 |
-
-H "Content-Type: application/json" \
|
| 41 |
-
-d '{"prompt": "updated prompt", "conversation_id": "conv-id", "message_id": "msg-id", "parent_id": "parent-id"}'
|
| 42 |
-
```
|
| 43 |
-
|
| 44 |
-
### 4. Health & Cookie Status
|
| 45 |
-
- **Health Check:** `GET http://127.0.0.1:9225/health`
|
| 46 |
-
- **Cookie Status:** `GET http://127.0.0.1:9225/api/cookies/status`
|
| 47 |
-
|
| 48 |
-
---
|
| 49 |
-
|
| 50 |
-
## Testing
|
| 51 |
-
|
| 52 |
-
Run the integration tests (requires agent running on port 9225):
|
| 53 |
-
```bash
|
| 54 |
-
./test.sh # Run all tests (text, thread, bulk, image gen/edit)
|
| 55 |
-
./test_image_only.sh # Run only image generation/editing tests
|
| 56 |
-
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
chatgpt-free-api/test.sh
DELETED
|
@@ -1,124 +0,0 @@
|
|
| 1 |
-
#!/bin/bash
|
| 2 |
-
|
| 3 |
-
# Unset proxy variables for local communication
|
| 4 |
-
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY no_proxy
|
| 5 |
-
|
| 6 |
-
# Colors for output
|
| 7 |
-
GREEN='\033[0;32m'
|
| 8 |
-
RED='\033[0;31m'
|
| 9 |
-
YELLOW='\033[0;33m'
|
| 10 |
-
BLUE='\033[0;34m'
|
| 11 |
-
NC='\033[0m' # No Color
|
| 12 |
-
|
| 13 |
-
echo -e "${BLUE}==================================================${NC}"
|
| 14 |
-
echo -e "${BLUE} ChatGPT Free API Integration Test ${NC}"
|
| 15 |
-
echo -e "${BLUE}==================================================${NC}"
|
| 16 |
-
|
| 17 |
-
# Check if agent is running on port 9225
|
| 18 |
-
echo "🔍 Checking if agent is running on port 9225..."
|
| 19 |
-
if ! curl -s --connect-timeout 2 http://127.0.0.1:9225/health >/dev/null; then
|
| 20 |
-
echo -e "${RED}❌ Error: Agent is not running on port 9225!${NC}"
|
| 21 |
-
echo -e "💡 Please start the server separately first by running: ${YELLOW}./agent${NC} or ${YELLOW}go run main.go${NC}"
|
| 22 |
-
exit 1
|
| 23 |
-
fi
|
| 24 |
-
echo -e "${GREEN}✅ Agent detected on port 9225! Running tests...${NC}"
|
| 25 |
-
|
| 26 |
-
# Test 1: Check cookie status
|
| 27 |
-
echo -e "\n${BLUE}[Test 1] Checking cookie status...${NC}"
|
| 28 |
-
STATUS_RESP=$(curl -s http://127.0.0.1:9225/api/cookies/status)
|
| 29 |
-
echo "Response: $STATUS_RESP"
|
| 30 |
-
if [[ "$STATUS_RESP" == *"has_cookies\":true"* ]]; then
|
| 31 |
-
TEST1_STATUS="${GREEN}PASSED${NC}"
|
| 32 |
-
else
|
| 33 |
-
TEST1_STATUS="${RED}FAILED (No cookies sync)${NC}"
|
| 34 |
-
fi
|
| 35 |
-
|
| 36 |
-
# Test 2: Send basic chat test
|
| 37 |
-
echo -e "\n${BLUE}[Test 2] Sending basic text chat prompt...${NC}"
|
| 38 |
-
CHAT_RESP=$(curl -s -X POST http://127.0.0.1:9225/api/chat \
|
| 39 |
-
-H "Content-Type: application/json" \
|
| 40 |
-
-d '{"prompt": "Tell me a short programmer joke."}')
|
| 41 |
-
echo "Response: $CHAT_RESP"
|
| 42 |
-
|
| 43 |
-
CONV_ID=$(echo "$CHAT_RESP" | grep -o '"conversation_id":"[^"]*' | cut -d'"' -f4)
|
| 44 |
-
if [ -n "$CONV_ID" ]; then
|
| 45 |
-
TEST2_STATUS="${GREEN}PASSED${NC}"
|
| 46 |
-
else
|
| 47 |
-
TEST2_STATUS="${RED}FAILED (No response or conversation ID)${NC}"
|
| 48 |
-
fi
|
| 49 |
-
|
| 50 |
-
# Test 3: Conversation thread continuity
|
| 51 |
-
if [ -n "$CONV_ID" ]; then
|
| 52 |
-
echo -e "\n${BLUE}[Test 3] Testing thread continuity...${NC}"
|
| 53 |
-
THREAD_RESP=$(curl -s -X POST http://127.0.0.1:9225/api/chat \
|
| 54 |
-
-H "Content-Type: application/json" \
|
| 55 |
-
-d "{\"prompt\": \"Explain that joke.\", \"conversation_id\": \"$CONV_ID\"}")
|
| 56 |
-
echo "Response: $THREAD_RESP"
|
| 57 |
-
if [[ "$THREAD_RESP" == *"ok\":true"* ]]; then
|
| 58 |
-
TEST3_STATUS="${GREEN}PASSED${NC}"
|
| 59 |
-
else
|
| 60 |
-
TEST3_STATUS="${RED}FAILED (Thread reply failed)${NC}"
|
| 61 |
-
fi
|
| 62 |
-
else
|
| 63 |
-
TEST3_STATUS="${YELLOW}SKIPPED (No conversation ID)${NC}"
|
| 64 |
-
fi
|
| 65 |
-
|
| 66 |
-
# Test 4: Bulk chat request
|
| 67 |
-
echo -e "\n${BLUE}[Test 4] Testing bulk chat request...${NC}"
|
| 68 |
-
BULK_RESP=$(curl -s -X POST http://127.0.0.1:9225/api/chat/bulk \
|
| 69 |
-
-H "Content-Type: application/json" \
|
| 70 |
-
-d '{"prompts": ["What is 2+2?", "What is Go?"], "wait_for_response": true}')
|
| 71 |
-
echo "Response: $BULK_RESP"
|
| 72 |
-
if [[ "$BULK_RESP" == *"ok\":true"* ]]; then
|
| 73 |
-
TEST4_STATUS="${GREEN}PASSED${NC}"
|
| 74 |
-
else
|
| 75 |
-
TEST4_STATUS="${RED}FAILED${NC}"
|
| 76 |
-
fi
|
| 77 |
-
|
| 78 |
-
# Test 5: Image generation (GPT-2)
|
| 79 |
-
echo -e "\n${BLUE}[Test 5] Testing GPT-2 image generation (Cute baby dragon)...${NC}"
|
| 80 |
-
echo "⏳ Polling for GPT-2 generation, this will take 15-35 seconds..."
|
| 81 |
-
GEN_RESP=$(curl -s -X POST http://127.0.0.1:9225/api/chat \
|
| 82 |
-
-H "Content-Type: application/json" \
|
| 83 |
-
-d '{"prompt": "Create a cute 3D cartoon baby dragon sitting on a small chest of gold. Directly generate the image now without asking any questions or confirmation."}')
|
| 84 |
-
echo "Response: $GEN_RESP"
|
| 85 |
-
|
| 86 |
-
# Extract generated image path
|
| 87 |
-
GEN_IMAGE_PATH=$(echo "$GEN_RESP" | grep -o '"images":\["[^"]*' | cut -d'"' -f4)
|
| 88 |
-
if [ -n "$GEN_IMAGE_PATH" ] && [ -f "$GEN_IMAGE_PATH" ]; then
|
| 89 |
-
TEST5_STATUS="${GREEN}PASSED ($GEN_IMAGE_PATH)${NC}"
|
| 90 |
-
else
|
| 91 |
-
TEST5_STATUS="${RED}FAILED (Image file not generated/saved)${NC}"
|
| 92 |
-
fi
|
| 93 |
-
|
| 94 |
-
# Test 6: Image editing (GPT-2 Edit/Vision)
|
| 95 |
-
if [ -n "$GEN_IMAGE_PATH" ] && [ -f "$GEN_IMAGE_PATH" ]; then
|
| 96 |
-
echo -e "\n${BLUE}[Test 6] Testing GPT-2 image editing (Adding wizard hat)...${NC}"
|
| 97 |
-
echo "⏳ Uploading source image and polling for edit, this will take 15-35 seconds..."
|
| 98 |
-
EDIT_RESP=$(curl -s -X POST http://127.0.0.1:9225/api/chat/edit \
|
| 99 |
-
-F "prompt=Add a small glowing wizard hat to the head of this dragon." \
|
| 100 |
-
-F "image=@$GEN_IMAGE_PATH")
|
| 101 |
-
echo "Response: $EDIT_RESP"
|
| 102 |
-
|
| 103 |
-
EDIT_IMAGE_PATH=$(echo "$EDIT_RESP" | grep -o '"images":\["[^"]*' | cut -d'"' -f4)
|
| 104 |
-
if [ -n "$EDIT_IMAGE_PATH" ] && [ -f "$EDIT_IMAGE_PATH" ]; then
|
| 105 |
-
TEST6_STATUS="${GREEN}PASSED ($EDIT_IMAGE_PATH)${NC}"
|
| 106 |
-
else
|
| 107 |
-
TEST6_STATUS="${RED}FAILED (Edited image file not generated/saved)${NC}"
|
| 108 |
-
fi
|
| 109 |
-
else
|
| 110 |
-
TEST6_STATUS="${YELLOW}SKIPPED (No source image generated in Test 5)${NC}"
|
| 111 |
-
fi
|
| 112 |
-
|
| 113 |
-
echo -e "\n${BLUE}==================================================${NC}"
|
| 114 |
-
echo -e "${BLUE} Test Summary ${NC}"
|
| 115 |
-
echo -e "${BLUE}==================================================${NC}"
|
| 116 |
-
echo -e "1. Cookie Status: $TEST1_STATUS"
|
| 117 |
-
echo -e "2. Basic Chat: $TEST2_STATUS"
|
| 118 |
-
echo -e "3. Thread Continuity: $TEST3_STATUS"
|
| 119 |
-
echo -e "4. Bulk Chat: $TEST4_STATUS"
|
| 120 |
-
echo -e "5. GPT-2 Gen: $TEST5_STATUS"
|
| 121 |
-
echo -e "6. GPT-2 Image Editing: $TEST6_STATUS"
|
| 122 |
-
echo -e "${BLUE}==================================================${NC}"
|
| 123 |
-
|
| 124 |
-
echo -e "\n${GREEN}✅ Tests completed!${NC}\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/architecture.md
CHANGED
|
@@ -40,5 +40,5 @@ the 3 extensions. Each server logs to `/home/chrome/{chatgpt,gemini,flow}.log`.
|
|
| 40 |
## Components added on top of the base image
|
| 41 |
- `monitor/` — Go UI, now also the **reverse-proxy gateway** (`gatewayHandler`, `authOK`).
|
| 42 |
- `profilesync/` — Go tool, Chrome profile ⇄ Postgres snapshot.
|
| 43 |
-
- `
|
| 44 |
built/installed into the image by the `Dockerfile`.
|
|
|
|
| 40 |
## Components added on top of the base image
|
| 41 |
- `monitor/` — Go UI, now also the **reverse-proxy gateway** (`gatewayHandler`, `authOK`).
|
| 42 |
- `profilesync/` — Go tool, Chrome profile ⇄ Postgres snapshot.
|
| 43 |
+
- `free-Chatgpt-api/`, `free-gemini-api/` (Go) + `flow-agent/` (Python/FastAPI) — the 3 servers,
|
| 44 |
built/installed into the image by the `Dockerfile`.
|
docs/deployment.md
CHANGED
|
@@ -16,13 +16,13 @@ The `origin` remote embeds a token: `https://<user>:<hf_token>@huggingface.co/sp
|
|
| 16 |
- The token **must belong to the Space owner** (`akash1313`) with `repo.write`. A token from a
|
| 17 |
different account → `pre-receive hook declined: You are not authorized to push`.
|
| 18 |
- Rotate/replace the token in the URL with:
|
| 19 |
-
`git remote set-url origin https://akash1313:<NEW_TOKEN>@huggingface.co/spaces/akash1313/selfapi`
|
| 20 |
- Local token files (`hf-token`, `neon-db`, `api-key`) are gitignored — never commit them.
|
| 21 |
|
| 22 |
### Watch the build
|
| 23 |
```bash
|
| 24 |
curl -s -H "Authorization: Bearer <hf_token>" \
|
| 25 |
-
https://huggingface.co/api/spaces/akash1313/selfapi/runtime
|
| 26 |
# stage: RUNNING_BUILDING → RUNNING_APP_STARTING → RUNNING (errors: BUILD_ERROR / RUNTIME_ERROR)
|
| 27 |
```
|
| 28 |
|
|
|
|
| 16 |
- The token **must belong to the Space owner** (`akash1313`) with `repo.write`. A token from a
|
| 17 |
different account → `pre-receive hook declined: You are not authorized to push`.
|
| 18 |
- Rotate/replace the token in the URL with:
|
| 19 |
+
`git remote set-url origin https://akash1313:<NEW_TOKEN>@huggingface.co/spaces/akash1313/selfapi-v2`
|
| 20 |
- Local token files (`hf-token`, `neon-db`, `api-key`) are gitignored — never commit them.
|
| 21 |
|
| 22 |
### Watch the build
|
| 23 |
```bash
|
| 24 |
curl -s -H "Authorization: Bearer <hf_token>" \
|
| 25 |
+
https://huggingface.co/api/spaces/akash1313/selfapi-v2/runtime
|
| 26 |
# stage: RUNNING_BUILDING → RUNNING_APP_STARTING → RUNNING (errors: BUILD_ERROR / RUNTIME_ERROR)
|
| 27 |
```
|
| 28 |
|
docs/troubleshooting.md
CHANGED
|
@@ -7,7 +7,7 @@ Every issue we actually hit, and the fix.
|
|
| 7 |
| Symptom | Cause → Fix |
|
| 8 |
|---------|-------------|
|
| 9 |
| `pre-receive hook declined: You are not authorized to push` | The token in the `origin` URL isn't the Space owner's. Use an `akash1313` token with `repo.write` (see [deployment.md](deployment.md)). |
|
| 10 |
-
| `Repository not found` on push | Remote points to a Space that doesn't exist. `git remote -v` and fix the name (we deploy to `akash1313/selfapi`). |
|
| 11 |
| Build `BUILD_ERROR` after bumping a server | `go.mod` Go version > build-stage image. Raise the `FROM golang:X` in the Dockerfile. |
|
| 12 |
| Python `externally-managed-environment` during pip | Debian 12 PEP-668. We use `pip3 install --break-system-packages` (already in Dockerfile). |
|
| 13 |
| Frequent restarts | Normal: **every `git push` and every secret change rebuilds.** Batch edits into one push. Not a CPU/RAM issue (cpu-basic = 2 vCPU / 16 GB; usage ~1–2 GB). |
|
|
@@ -26,7 +26,7 @@ Every issue we actually hit, and the fix.
|
|
| 26 |
| Symptom | Cause → Fix |
|
| 27 |
|---------|-------------|
|
| 28 |
| Login lost after restart | `DATABASE_URL` unset or DB unreachable (HF). Set the Neon secret; verify a row in `chrome_profile`. |
|
| 29 |
-
| ChatGPT Cloudflare "verify you are human" loops | UA/OS mismatch. The UA **must be Linux** (the Space runs Linux) — it's set in `start_hf.sh` and `
|
| 30 |
| `extensionConnected:false` / `has_flow_key:false` on `/…/health` | The in-Chrome extension hasn't connected to its server yet. It connects when (a) Chrome has loaded the extension and (b) **you're logged in** to that service. Open the monitor UI, log in, wait. Extensions auto-retry (~5 s reconnect alarm). |
|
| 31 |
| Extension never connects + API calls **hang forever** (`DidStartWorkerFail` in `chrome.log`) | Chrome was launched with renderer-capping flags (`--renderer-process-limit=1`, `--process-per-site`, `--no-zygote`). Each extension's MV3 service worker is its own renderer and the chat flow injects via `executeScript`; capping renderers starves them → workers fail to start and `executeScript` hangs. **Removed from `start_hf.sh`** — keep Chrome multi-process (HF has 16 GB). The UI chatting fine while the API hangs is the tell. |
|
| 32 |
| Backend server logs | `GET /logs/{chatgpt,gemini,flow,monitor}` (API-key gated) tails each server's log without shelling into the container. |
|
|
|
|
| 7 |
| Symptom | Cause → Fix |
|
| 8 |
|---------|-------------|
|
| 9 |
| `pre-receive hook declined: You are not authorized to push` | The token in the `origin` URL isn't the Space owner's. Use an `akash1313` token with `repo.write` (see [deployment.md](deployment.md)). |
|
| 10 |
+
| `Repository not found` on push | Remote points to a Space that doesn't exist. `git remote -v` and fix the name (we deploy to `akash1313/selfapi-v2`). |
|
| 11 |
| Build `BUILD_ERROR` after bumping a server | `go.mod` Go version > build-stage image. Raise the `FROM golang:X` in the Dockerfile. |
|
| 12 |
| Python `externally-managed-environment` during pip | Debian 12 PEP-668. We use `pip3 install --break-system-packages` (already in Dockerfile). |
|
| 13 |
| Frequent restarts | Normal: **every `git push` and every secret change rebuilds.** Batch edits into one push. Not a CPU/RAM issue (cpu-basic = 2 vCPU / 16 GB; usage ~1–2 GB). |
|
|
|
|
| 26 |
| Symptom | Cause → Fix |
|
| 27 |
|---------|-------------|
|
| 28 |
| Login lost after restart | `DATABASE_URL` unset or DB unreachable (HF). Set the Neon secret; verify a row in `chrome_profile`. |
|
| 29 |
+
| ChatGPT Cloudflare "verify you are human" loops | UA/OS mismatch. The UA **must be Linux** (the Space runs Linux) — it's set in `start_hf.sh` and `free-Chatgpt-api/cookies.go` (Chrome 145). If it still loops, it's the datacenter IP (HF=AWS) → use a residential `--proxy-server`. |
|
| 30 |
| `extensionConnected:false` / `has_flow_key:false` on `/…/health` | The in-Chrome extension hasn't connected to its server yet. It connects when (a) Chrome has loaded the extension and (b) **you're logged in** to that service. Open the monitor UI, log in, wait. Extensions auto-retry (~5 s reconnect alarm). |
|
| 31 |
| Extension never connects + API calls **hang forever** (`DidStartWorkerFail` in `chrome.log`) | Chrome was launched with renderer-capping flags (`--renderer-process-limit=1`, `--process-per-site`, `--no-zygote`). Each extension's MV3 service worker is its own renderer and the chat flow injects via `executeScript`; capping renderers starves them → workers fail to start and `executeScript` hangs. **Removed from `start_hf.sh`** — keep Chrome multi-process (HF has 16 GB). The UI chatting fine while the API hangs is the tell. |
|
| 32 |
| Backend server logs | `GET /logs/{chatgpt,gemini,flow,monitor}` (API-key gated) tails each server's log without shelling into the container. |
|
flow-agent/Dockerfile
DELETED
|
@@ -1,16 +0,0 @@
|
|
| 1 |
-
# Flow Agent FastAPI server — talks to the Flow extension.
|
| 2 |
-
# Public HTTP API : 127.0.0.1:8101 (uvicorn / FastAPI)
|
| 3 |
-
# Extension callback HTTP : 127.0.0.1:8100 (HTTP_PORT, ExtensionBridge — hardcoded in extension)
|
| 4 |
-
# cookie/token WebSocket : 127.0.0.1:9227 (WS_PORT)
|
| 5 |
-
FROM python:3.11-slim
|
| 6 |
-
|
| 7 |
-
WORKDIR /app
|
| 8 |
-
COPY requirements.txt .
|
| 9 |
-
RUN pip install --no-cache-dir -r requirements.txt
|
| 10 |
-
COPY . .
|
| 11 |
-
|
| 12 |
-
ENV WS_PORT=9227 \
|
| 13 |
-
HTTP_PORT=8100
|
| 14 |
-
|
| 15 |
-
EXPOSE 8100 8101 9227
|
| 16 |
-
CMD ["uvicorn", "cli.api:app", "--host", "127.0.0.1", "--port", "8101"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
flow-agent/README.md
DELETED
|
@@ -1,468 +0,0 @@
|
|
| 1 |
-
<div align="center">
|
| 2 |
-
|
| 3 |
-
# ⚡ Flow Agent
|
| 4 |
-
|
| 5 |
-
### Generate AI videos & images via HTTP/HTTPS API Server or CLI — no API key, no limits.
|
| 6 |
-
|
| 7 |
-
**Omni Flash** for cinematic video generation · **Nano Banana 2** for unlimited image creation
|
| 8 |
-
FastAPI Integration · Auto watermark removal · Reference-based editing · Zero setup.
|
| 9 |
-
|
| 10 |
-
---
|
| 11 |
-
|
| 12 |
-
🎬 `T2V` `V2V` `I2V` — Video generation with auto watermark clean *(uses credits)*
|
| 13 |
-
🖼️ `T2I` `I2I` — Unlimited image generation with reference support *(no credits needed)*
|
| 14 |
-
🔑 Uses your Google account via Chrome extension — **no API key required**
|
| 15 |
-
🌐 FastAPI HTTP/HTTPS Server — **perfect for n8n & external automation**
|
| 16 |
-
|
| 17 |
-
</div>
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
## ✅ Features & Status
|
| 21 |
-
|
| 22 |
-
| Feature | What it does | Time | Status |
|
| 23 |
-
|---------|-------------|------|--------|
|
| 24 |
-
| **T2V** | Generate video from text prompt | ~44s | ✅ Working |
|
| 25 |
-
| **T2I** | Generate image from text prompt | ~10-30s | ✅ Working |
|
| 26 |
-
| **V2V** | Edit/restyle existing video | ~3min | ✅ Working |
|
| 27 |
-
| **I2I** | Edit image with reference | ~10-30s | ✅ Working |
|
| 28 |
-
| **I2V** | Animate a still image into video | ~44s | ✅ Working |
|
| 29 |
-
| **FL** | First + Last frame video control | ~44s | ✅ Working |
|
| 30 |
-
| **R2V** | Reference-based video generation | ~44s | ✅ Working |
|
| 31 |
-
| **Upload** | Upload video/image to Flow | ~12s | ✅ Working |
|
| 32 |
-
| **Watermark Remove** | Auto-remove Gemini watermark (~1s) | ~1s | ✅ Auto |
|
| 33 |
-
| **Auto-Retry** | Auto-open/refresh Flow tab for token | auto | ✅ Built-in |
|
| 34 |
-
| **API Sniffer** | Discover new endpoints/payloads | - | ✅ Working |
|
| 35 |
-
|
| 36 |
-
---
|
| 37 |
-
|
| 38 |
-
## 📋 Prerequisites
|
| 39 |
-
|
| 40 |
-
| Requirement | Details |
|
| 41 |
-
|-------------|---------|
|
| 42 |
-
| **Python** | 3.9 or higher |
|
| 43 |
-
| **Chrome** | Latest version |
|
| 44 |
-
| **Google Account** | Logged into Flow |
|
| 45 |
-
| **ffmpeg** | Only for V2V merge (optional) |
|
| 46 |
-
|
| 47 |
-
---
|
| 48 |
-
|
| 49 |
-
## 🛠️ Installation (Step by Step)
|
| 50 |
-
|
| 51 |
-
### Step 1: Clone the repo
|
| 52 |
-
|
| 53 |
-
```bash
|
| 54 |
-
git clone https://github.com/kodelyx/flow-agent.git
|
| 55 |
-
cd flow-agent
|
| 56 |
-
```
|
| 57 |
-
|
| 58 |
-
### Step 2: Install Python dependencies
|
| 59 |
-
|
| 60 |
-
```bash
|
| 61 |
-
pip install -r requirements.txt
|
| 62 |
-
```
|
| 63 |
-
|
| 64 |
-
This installs `websockets`, `opencv-python-headless`, and `numpy`.
|
| 65 |
-
|
| 66 |
-
### Step 3: Install the Chrome Extension
|
| 67 |
-
|
| 68 |
-
1. Open Chrome browser
|
| 69 |
-
2. Go to `chrome://extensions` in the address bar
|
| 70 |
-
3. Toggle **"Developer mode"** ON (top-right corner)
|
| 71 |
-
4. Click **"Load unpacked"**
|
| 72 |
-
5. Select the `extension/` folder from this repo
|
| 73 |
-
6. You should see the **Flow Agent** extension appear
|
| 74 |
-
|
| 75 |
-
### Step 4: Open Google Flow
|
| 76 |
-
|
| 77 |
-
1. Open [labs.google/fx/tools/flow](https://labs.google/fx/tools/flow) in Chrome
|
| 78 |
-
2. Make sure you're **logged into your Google account**
|
| 79 |
-
3. The extension icon should show a **green badge** = connected
|
| 80 |
-
4. The extension auto-opens this tab when needed
|
| 81 |
-
|
| 82 |
-
> ⚠️ The Flow tab auto-opens when you run a command. No manual tab management needed!
|
| 83 |
-
|
| 84 |
-
---
|
| 85 |
-
|
| 86 |
-
## 🚀 Usage
|
| 87 |
-
|
| 88 |
-
### Text → Video (T2V)
|
| 89 |
-
|
| 90 |
-
Generate a new video from a text description.
|
| 91 |
-
|
| 92 |
-
```bash
|
| 93 |
-
# Basic (portrait 9:16, 10 seconds)
|
| 94 |
-
python -m cli.generate "A samurai drawing his katana on a cliff at golden sunset"
|
| 95 |
-
|
| 96 |
-
# Landscape mode (16:9)
|
| 97 |
-
python -m cli.generate "Eagle soaring over snowy mountains" --aspect landscape
|
| 98 |
-
|
| 99 |
-
# Custom output file
|
| 100 |
-
python -m cli.generate "A dragon breathing fire" -o dragon.mp4
|
| 101 |
-
|
| 102 |
-
# Shorter duration (4/6/8/10 seconds)
|
| 103 |
-
python -m cli.generate "Dog playing in the park" --duration 6
|
| 104 |
-
|
| 105 |
-
# Generate multiple variations
|
| 106 |
-
python -m cli.generate "Cyberpunk city at night" --count 4
|
| 107 |
-
|
| 108 |
-
# I2V — animate a still image
|
| 109 |
-
python -m cli.generate "Character comes alive" --start photo.png
|
| 110 |
-
|
| 111 |
-
# FL — First + Last frame (controlled transition)
|
| 112 |
-
python -m cli.generate "Person walks forward" --start start.png --end end.png
|
| 113 |
-
|
| 114 |
-
# R2V — Reference images (character consistency)
|
| 115 |
-
python -m cli.generate "Character in new scene" --ref char1.png char2.png
|
| 116 |
-
```
|
| 117 |
-
|
| 118 |
-
**CLI Options:**
|
| 119 |
-
| Flag | Short | Default | Description |
|
| 120 |
-
|------|-------|---------|-------------|
|
| 121 |
-
| `--output` | `-o` | `omni_output.mp4` | Output filename |
|
| 122 |
-
| `--aspect` | `-a` | `portrait` | `portrait` or `landscape` |
|
| 123 |
-
| `--duration` | `-d` | `10` | `4`, `6`, `8`, or `10` seconds |
|
| 124 |
-
| `--count` | `-c` | `1` | Generate 1-4 videos |
|
| 125 |
-
| `--edit` | `-e` | - | Pass media_id for V2V edit mode |
|
| 126 |
-
| `--start` | `-s` | - | Start frame image (I2V / FL mode) |
|
| 127 |
-
| `--end` | | - | End frame image (use with --start for FL) |
|
| 128 |
-
| `--ref` | `-r` | - | Reference image(s) for R2V mode |
|
| 129 |
-
| `--no-clean` | | - | Skip auto watermark removal |
|
| 130 |
-
|
| 131 |
-
---
|
| 132 |
-
|
| 133 |
-
### Text → Image (T2I)
|
| 134 |
-
|
| 135 |
-
Generate images from a text description.
|
| 136 |
-
|
| 137 |
-
```bash
|
| 138 |
-
# Basic (portrait 9:16)
|
| 139 |
-
python -m cli.image "A dragon breathing fire in a cyberpunk city"
|
| 140 |
-
|
| 141 |
-
# Landscape
|
| 142 |
-
python -m cli.image "Mountain sunset" --aspect landscape -o sunset.png
|
| 143 |
-
|
| 144 |
-
# Square (for logos, icons)
|
| 145 |
-
python -m cli.image "Minimal logo design" --aspect square
|
| 146 |
-
|
| 147 |
-
# Generate 4 variations
|
| 148 |
-
python -m cli.image "Abstract art" --count 4
|
| 149 |
-
|
| 150 |
-
# I2I: Edit with reference image
|
| 151 |
-
python -m cli.image "Make it anime style" --ref original.png -o anime.png
|
| 152 |
-
```
|
| 153 |
-
|
| 154 |
-
**CLI Options:**
|
| 155 |
-
| Flag | Short | Default | Description |
|
| 156 |
-
|------|-------|---------|-------------|
|
| 157 |
-
| `--output` | `-o` | `output/image.png` | Output filename |
|
| 158 |
-
| `--aspect` | `-a` | `portrait` | `portrait`, `landscape`, `square`, `4x3`, `3x4` |
|
| 159 |
-
| `--count` | `-c` | `1` | Generate 1-4 variations |
|
| 160 |
-
| `--ref` | `-r` | - | Reference image(s) for I2I |
|
| 161 |
-
|
| 162 |
-
### Upload Video
|
| 163 |
-
|
| 164 |
-
Upload a local video to Google Flow. Returns a `media_id` needed for V2V editing.
|
| 165 |
-
|
| 166 |
-
```bash
|
| 167 |
-
# Single video
|
| 168 |
-
python -m cli.upload my_video.mp4
|
| 169 |
-
|
| 170 |
-
# Batch upload all .mp4 in a folder
|
| 171 |
-
python -m cli.upload chunks/ --batch
|
| 172 |
-
```
|
| 173 |
-
|
| 174 |
-
The `media_id` is **automatically saved** to `media-id.js`:
|
| 175 |
-
```
|
| 176 |
-
my_video.mp4 : 49f7d936-01e3-41ad-917a-2f9bb6ead00b
|
| 177 |
-
```
|
| 178 |
-
|
| 179 |
-
---
|
| 180 |
-
|
| 181 |
-
### Video → Video Edit (V2V)
|
| 182 |
-
|
| 183 |
-
Apply style changes to an uploaded video (e.g., convert to anime).
|
| 184 |
-
|
| 185 |
-
```bash
|
| 186 |
-
# Step 1: Upload your video
|
| 187 |
-
python -m cli.upload my_video.mp4
|
| 188 |
-
# → media_id saved to media-id.js
|
| 189 |
-
|
| 190 |
-
# Step 2: Edit with style prompt
|
| 191 |
-
python -m cli.edit "Transform into vibrant anime style, Studio Ghibli aesthetic" \
|
| 192 |
-
--media-id 49f7d936-01e3-41ad-917a-2f9bb6ead00b \
|
| 193 |
-
--video-file my_video.mp4 \
|
| 194 |
-
--output output_anime/ \
|
| 195 |
-
--merge
|
| 196 |
-
|
| 197 |
-
# Without local video file (specify duration manually)
|
| 198 |
-
python -m cli.edit "Make it look cyberpunk neon" \
|
| 199 |
-
-m MEDIA_ID \
|
| 200 |
-
--total-seconds 30 \
|
| 201 |
-
-o output_cyber/
|
| 202 |
-
```
|
| 203 |
-
|
| 204 |
-
**How it works:** Long videos are automatically split into 10s segments, each processed in parallel, then merged with ffmpeg.
|
| 205 |
-
|
| 206 |
-
**CLI Options:**
|
| 207 |
-
| Flag | Short | Required | Description |
|
| 208 |
-
|------|-------|----------|-------------|
|
| 209 |
-
| `--media-id` | `-m` | Yes | Flow media ID (from upload) |
|
| 210 |
-
| `--video-file` | `-v` | No | Local file (auto-detects duration/fps) |
|
| 211 |
-
| `--total-seconds` | `-t` | No | Duration if no local file |
|
| 212 |
-
| `--output` | `-o` | No | Output directory (default: `output/`) |
|
| 213 |
-
| `--aspect` | `-a` | No | `portrait` or `landscape` |
|
| 214 |
-
| `--merge` | | No | Merge segments with ffmpeg |
|
| 215 |
-
|
| 216 |
-
---
|
| 217 |
-
|
| 218 |
-
### Image → Video (I2V)
|
| 219 |
-
|
| 220 |
-
Animate a still image into a video. Requires Python scripting (no CLI yet):
|
| 221 |
-
|
| 222 |
-
```python
|
| 223 |
-
import asyncio
|
| 224 |
-
from omniflash import (
|
| 225 |
-
ExtensionBridge, upload_image, generate_video_i2v,
|
| 226 |
-
poll_status, download_video, ASPECTS, DEFAULT_PROJECT,
|
| 227 |
-
)
|
| 228 |
-
|
| 229 |
-
async def main():
|
| 230 |
-
# Connect to extension
|
| 231 |
-
bridge = ExtensionBridge()
|
| 232 |
-
await bridge.start()
|
| 233 |
-
await bridge.wait_for_extension(30)
|
| 234 |
-
|
| 235 |
-
# Upload your image
|
| 236 |
-
img_id = await upload_image(bridge, "my_image.png")
|
| 237 |
-
print(f"Image uploaded: {img_id}")
|
| 238 |
-
|
| 239 |
-
# Generate video from image
|
| 240 |
-
media_ids = await generate_video_i2v(
|
| 241 |
-
bridge,
|
| 242 |
-
prompt="The character comes alive, dramatic movement, cinematic",
|
| 243 |
-
aspect=ASPECTS['portrait'], # or ASPECTS['landscape']
|
| 244 |
-
project_id=DEFAULT_PROJECT,
|
| 245 |
-
image_media_id=img_id,
|
| 246 |
-
duration=8, # 4, 6, 8, or 10 seconds
|
| 247 |
-
)
|
| 248 |
-
|
| 249 |
-
# Wait for video to finish
|
| 250 |
-
if media_ids:
|
| 251 |
-
await poll_status(bridge, media_ids[0], DEFAULT_PROJECT)
|
| 252 |
-
await download_video(bridge, media_ids[0], "output_i2v.mp4")
|
| 253 |
-
|
| 254 |
-
await bridge.close()
|
| 255 |
-
|
| 256 |
-
asyncio.run(main())
|
| 257 |
-
```
|
| 258 |
-
|
| 259 |
-
---
|
| 260 |
-
|
| 261 |
-
### API Sniffer
|
| 262 |
-
|
| 263 |
-
Capture all API requests made by the Flow UI. Useful for discovering new endpoints or debugging.
|
| 264 |
-
|
| 265 |
-
```bash
|
| 266 |
-
# Start sniffer, then use Flow UI normally
|
| 267 |
-
python -m cli.sniff
|
| 268 |
-
|
| 269 |
-
# Save captured requests to file
|
| 270 |
-
python -m cli.sniff --save sniffed.json
|
| 271 |
-
```
|
| 272 |
-
|
| 273 |
-
See [SNIFFING.md](SNIFFING.md) for the full API discovery guide.
|
| 274 |
-
|
| 275 |
-
---
|
| 276 |
-
|
| 277 |
-
## 🌐 HTTP/HTTPS API Server (for n8n & external integrations)
|
| 278 |
-
|
| 279 |
-
A FastAPI-based API server is included to trigger all Flow Agent features remotely (e.g., from n8n HTTP Request nodes, custom webhooks, or automation workflows).
|
| 280 |
-
|
| 281 |
-
### Start the API Server
|
| 282 |
-
|
| 283 |
-
Run the server from the project directory:
|
| 284 |
-
```bash
|
| 285 |
-
# Standard HTTP (defaults to port 8000)
|
| 286 |
-
venv/bin/python -m cli.api --host 0.0.0.0 --port 8000
|
| 287 |
-
|
| 288 |
-
# Optional HTTPS (auto-generates self-signed SSL certificates)
|
| 289 |
-
venv/bin/python -m cli.api --host 0.0.0.0 --port 8443 --ssl
|
| 290 |
-
```
|
| 291 |
-
|
| 292 |
-
### Core API Endpoints
|
| 293 |
-
|
| 294 |
-
| Method | Endpoint | Description |
|
| 295 |
-
|--------|----------|-------------|
|
| 296 |
-
| **GET** | `/health` | Check Extension Bridge health and token status. |
|
| 297 |
-
| **POST** | `/generate/video` | T2V, I2V, FL, R2V video generation. Supports query `?download=true` to stream the binary `.mp4` file. |
|
| 298 |
-
| **POST** | `/generate/image` | T2I, I2I image generation. Supports `?download=true` to stream the binary `.png` file. |
|
| 299 |
-
| **POST** | `/upload/image` | Upload an image to Flow (via file upload or local file path). |
|
| 300 |
-
| **POST** | `/upload/video` | Upload a video to Flow (via file upload or local file path). |
|
| 301 |
-
| **POST** | `/edit/video` | V2V video editing (restyling segment duration). |
|
| 302 |
-
| **GET** | `/download/{filename}` | Download generated image/video files from the `output/` folder. |
|
| 303 |
-
|
| 304 |
-
### Verify with Integration Tests
|
| 305 |
-
|
| 306 |
-
A comprehensive integration test script is provided to verify all endpoints:
|
| 307 |
-
```bash
|
| 308 |
-
venv/bin/python test_api.py
|
| 309 |
-
```
|
| 310 |
-
|
| 311 |
-
See [error.md](error.md) for detailed troubleshooting instructions if you encounter port conflicts or proxy network issues.
|
| 312 |
-
|
| 313 |
-
---
|
| 314 |
-
|
| 315 |
-
## 🐍 Python API (for developers)
|
| 316 |
-
|
| 317 |
-
Use the `omniflash` package directly in your own scripts:
|
| 318 |
-
|
| 319 |
-
```python
|
| 320 |
-
from omniflash import (
|
| 321 |
-
ExtensionBridge, # WebSocket bridge to Chrome extension
|
| 322 |
-
generate_video, # T2V: text → video
|
| 323 |
-
edit_video, # V2V: video → video
|
| 324 |
-
upload_image, # Upload image → get media_id
|
| 325 |
-
generate_video_i2v, # I2V: image → video
|
| 326 |
-
poll_status, # Poll until video is ready
|
| 327 |
-
download_video, # Download finished video
|
| 328 |
-
ASPECTS, # {'portrait': '...', 'landscape': '...'}
|
| 329 |
-
DEFAULT_PROJECT, # Your default project ID
|
| 330 |
-
)
|
| 331 |
-
from omniflash.upload import upload_video # Upload video file
|
| 332 |
-
from omniflash import media_store # Read/write media-id.js
|
| 333 |
-
|
| 334 |
-
# media_store examples:
|
| 335 |
-
media_store.save("video.mp4", "uuid-here") # Save entry
|
| 336 |
-
mid = media_store.get("video.mp4") # Get media_id
|
| 337 |
-
all_entries = media_store.read_entries() # Get all entries
|
| 338 |
-
```
|
| 339 |
-
|
| 340 |
-
> **Backward compatible:** `from omni import ExtensionBridge, generate_video, ...` still works.
|
| 341 |
-
|
| 342 |
-
---
|
| 343 |
-
|
| 344 |
-
## 📁 Project Structure
|
| 345 |
-
|
| 346 |
-
```
|
| 347 |
-
flow-agent/
|
| 348 |
-
├── omniflash/ # Core Python package
|
| 349 |
-
│ ├── __init__.py # Public API exports
|
| 350 |
-
│ ├── bridge.py # ExtensionBridge (WS + HTTP + auto-retry)
|
| 351 |
-
│ ├── config.py # All config hardcoded here
|
| 352 |
-
│ ├── media_store.py # media-id.js read/write
|
| 353 |
-
│ ├── upload.py # Video upload (GCS resumable)
|
| 354 |
-
│ ├── watermark.py # Auto watermark removal (embedded assets)
|
| 355 |
-
│ └── generators/ # API functions
|
| 356 |
-
│ ├── common.py # poll_status, download_video
|
| 357 |
-
│ ├── t2v.py # Text → Video
|
| 358 |
-
│ ├── t2i.py # Text → Image + I2I
|
| 359 |
-
│ ├── v2v.py # Video → Video (edit)
|
| 360 |
-
│ └── i2v.py # Image → Video + upload_image
|
| 361 |
-
├── cli/ # CLI entry points
|
| 362 |
-
│ ├── generate.py # python -m cli.generate
|
| 363 |
-
│ ├── image.py # python -m cli.image
|
| 364 |
-
│ ├── upload.py # python -m cli.upload
|
| 365 |
-
│ ├── edit.py # python -m cli.edit
|
| 366 |
-
│ └── sniff.py # python -m cli.sniff
|
| 367 |
-
├── extension/ # Chrome extension
|
| 368 |
-
│ ├── manifest.json # Extension manifest
|
| 369 |
-
│ ├── background.js # WS client, API proxy
|
| 370 |
-
│ ├── content.js # Page ↔ background bridge
|
| 371 |
-
│ └── injected.js # Fetch interceptor, reCAPTCHA
|
| 372 |
-
├── omni.py # Backward-compatible wrapper
|
| 373 |
-
├── .gitignore # Git ignore rules
|
| 374 |
-
├── media-id.js # Auto-updated filename → media_id
|
| 375 |
-
├── requirements.txt # Python dependencies
|
| 376 |
-
├── SNIFFING.md # API discovery guide
|
| 377 |
-
└── README.md
|
| 378 |
-
```
|
| 379 |
-
|
| 380 |
-
---
|
| 381 |
-
|
| 382 |
-
## ⚙️ How It Works
|
| 383 |
-
|
| 384 |
-
```
|
| 385 |
-
┌─────────────────────────────────┐
|
| 386 |
-
│ Your Terminal / Python Script │
|
| 387 |
-
│ python -m cli.generate "..." │
|
| 388 |
-
└──────────┬──────────────────────┘
|
| 389 |
-
│ import omniflash
|
| 390 |
-
▼
|
| 391 |
-
┌─────────────────────────────────┐
|
| 392 |
-
│ omniflash package │
|
| 393 |
-
│ ExtensionBridge (WS + HTTP) │
|
| 394 |
-
└──────────┬──────────────────────┘
|
| 395 |
-
│ WebSocket (:9222)
|
| 396 |
-
│ HTTP callback (:8100)
|
| 397 |
-
▼
|
| 398 |
-
┌─────────────────────────────────┐
|
| 399 |
-
│ Chrome Extension (Flow Agent) │
|
| 400 |
-
│ Auth token + reCAPTCHA solving │
|
| 401 |
-
└──────────┬──────────────────────┘
|
| 402 |
-
│ HTTPS (browser cookies)
|
| 403 |
-
▼
|
| 404 |
-
┌─────────────────────────────────┐
|
| 405 |
-
│ Google Omni API (aisandbox) │
|
| 406 |
-
│ Video generation / editing │
|
| 407 |
-
└─────────────────────────────────┘
|
| 408 |
-
```
|
| 409 |
-
|
| 410 |
-
1. Python starts a WebSocket server + HTTP callback server
|
| 411 |
-
2. Chrome extension auto-connects and provides authentication
|
| 412 |
-
3. Script sends API requests through the extension
|
| 413 |
-
4. Extension solves reCAPTCHA and proxies with browser cookies
|
| 414 |
-
5. Script polls for completion, then downloads the result
|
| 415 |
-
|
| 416 |
-
---
|
| 417 |
-
|
| 418 |
-
## 🎯 Models & Endpoints
|
| 419 |
-
|
| 420 |
-
| Model | Key | Duration | Type |
|
| 421 |
-
|-------|-----|----------|------|
|
| 422 |
-
| Omni Flash T2V 4s | `abra_t2v_4s` | 4 sec | Text → Video |
|
| 423 |
-
| Omni Flash T2V 6s | `abra_t2v_6s` | 6 sec | Text → Video |
|
| 424 |
-
| Omni Flash T2V 8s | `abra_t2v_8s` | 8 sec | Text → Video |
|
| 425 |
-
| Omni Flash T2V 10s | `abra_t2v_10s` | 10 sec | Text → Video |
|
| 426 |
-
| Omni Flash Edit | `abra_edit` | 10 sec | Video → Video |
|
| 427 |
-
|
| 428 |
-
| Endpoint | Path |
|
| 429 |
-
|----------|------|
|
| 430 |
-
| T2V | `/v1/video:batchAsyncGenerateVideoText` |
|
| 431 |
-
| I2V | `/v1/video:batchAsyncGenerateVideoStartImage` |
|
| 432 |
-
| V2V Edit | `/v1/video:batchAsyncGenerateVideoEditVideo` |
|
| 433 |
-
| Upload Image | `/v1/flow/uploadImage` |
|
| 434 |
-
| Poll Status | `/v1/video:batchCheckAsyncVideoGenerationStatus` |
|
| 435 |
-
| Get Media | `/v1/video/media/{media_id}` |
|
| 436 |
-
|
| 437 |
-
---
|
| 438 |
-
|
| 439 |
-
## 🔧 Troubleshooting
|
| 440 |
-
|
| 441 |
-
| Problem | Solution |
|
| 442 |
-
|---------|----------|
|
| 443 |
-
| Extension not connecting | Make sure Flow tab is open and you're logged in |
|
| 444 |
-
| `Address already in use` | Another script is using port 9222/8100. Kill it first |
|
| 445 |
-
| `TIMEOUT` error | Extension may have disconnected. Reload Flow tab |
|
| 446 |
-
| `reCAPTCHA failed` | Reload Flow tab, wait a few seconds, try again |
|
| 447 |
-
| `No media in response` | Check your prompt. Some prompts get blocked |
|
| 448 |
-
| `curl failed` | Upload too large or network issue. Retry |
|
| 449 |
-
| Video quality poor | Use longer duration (10s) and detailed prompts |
|
| 450 |
-
| V2V merge fails | Install ffmpeg: `brew install ffmpeg` |
|
| 451 |
-
|
| 452 |
-
---
|
| 453 |
-
|
| 454 |
-
## ⚠️ Important Notes
|
| 455 |
-
|
| 456 |
-
- **Flow tab auto-opens** — no manual tab management needed
|
| 457 |
-
- Uses your Google account's **free Flow credits** (check remaining in Flow UI)
|
| 458 |
-
- Extension auto-reconnects and auto-retries (3 attempts)
|
| 459 |
-
- **Watermark auto-removed** on every generated video (~1s)
|
| 460 |
-
- `media-id.js` auto-updates on every upload (video or image)
|
| 461 |
-
- All generated videos save to the `output/` directory by default
|
| 462 |
-
- Old `from omni import ...` syntax still works (backward compatible)
|
| 463 |
-
|
| 464 |
-
---
|
| 465 |
-
|
| 466 |
-
## 📄 License
|
| 467 |
-
|
| 468 |
-
MIT
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
flow-agent/SNIFFING.md
DELETED
|
@@ -1,205 +0,0 @@
|
|
| 1 |
-
# 🔍 API Sniffing Guide
|
| 2 |
-
|
| 3 |
-
How to discover new Google Flow API endpoints using the Chrome extension's built-in request sniffer.
|
| 4 |
-
|
| 5 |
-
## How It Works
|
| 6 |
-
|
| 7 |
-
```
|
| 8 |
-
Flow UI (browser)
|
| 9 |
-
↓ user clicks "Generate" / "Upload" etc.
|
| 10 |
-
↓
|
| 11 |
-
fetch() call to aisandbox-pa.googleapis.com
|
| 12 |
-
↓ intercepted by injected.js (monkey-patched fetch)
|
| 13 |
-
↓
|
| 14 |
-
postMessage → content.js → background.js
|
| 15 |
-
↓
|
| 16 |
-
HTTP POST → http://127.0.0.1:8100/api/ext/callback
|
| 17 |
-
↓
|
| 18 |
-
Your sniff server logs the URL + payload
|
| 19 |
-
```
|
| 20 |
-
|
| 21 |
-
The extension's `injected.js` monkey-patches `window.fetch` to intercept ALL outgoing requests to `aisandbox-pa.googleapis.com`. Every request's URL, method, and body are forwarded to your local server.
|
| 22 |
-
|
| 23 |
-
## Quick Start
|
| 24 |
-
|
| 25 |
-
### 1. Start the Sniff Server
|
| 26 |
-
|
| 27 |
-
```python
|
| 28 |
-
python sniff.py
|
| 29 |
-
```
|
| 30 |
-
|
| 31 |
-
This starts:
|
| 32 |
-
- WebSocket server on `ws://127.0.0.1:9222` (extension connects here)
|
| 33 |
-
- HTTP server on `http://127.0.0.1:8100` (receives sniffed data)
|
| 34 |
-
|
| 35 |
-
### 2. Open Flow UI
|
| 36 |
-
|
| 37 |
-
Go to [labs.google/fx/tools/flow](https://labs.google/fx/tools/flow) in Chrome.
|
| 38 |
-
The extension will auto-connect to your sniff server.
|
| 39 |
-
|
| 40 |
-
### 3. Perform the Action
|
| 41 |
-
|
| 42 |
-
Do whatever you want to discover the API for:
|
| 43 |
-
- Upload an image/video
|
| 44 |
-
- Generate a video
|
| 45 |
-
- Change settings
|
| 46 |
-
- Click any button
|
| 47 |
-
|
| 48 |
-
### 4. Read the Logs
|
| 49 |
-
|
| 50 |
-
The sniff server prints every intercepted request:
|
| 51 |
-
```
|
| 52 |
-
🔍 SNIFFED: https://aisandbox-pa.googleapis.com/v1/video:batchAsyncGenerateVideoText
|
| 53 |
-
Method: POST
|
| 54 |
-
Payload: {"mediaGenerationContext":{"batchId":"..."},"clientContext":{...},"requests":[...]}
|
| 55 |
-
```
|
| 56 |
-
|
| 57 |
-
## Sniff Server Code
|
| 58 |
-
|
| 59 |
-
Save this as `sniff.py`:
|
| 60 |
-
|
| 61 |
-
```python
|
| 62 |
-
#!/usr/bin/env python3
|
| 63 |
-
"""Sniff server — captures all Flow UI API requests."""
|
| 64 |
-
|
| 65 |
-
import asyncio, json, websockets, logging
|
| 66 |
-
from http.server import HTTPServer, BaseHTTPRequestHandler
|
| 67 |
-
import threading
|
| 68 |
-
|
| 69 |
-
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s', datefmt='%H:%M:%S')
|
| 70 |
-
log = logging.getLogger('sniff')
|
| 71 |
-
|
| 72 |
-
# Store all sniffed requests
|
| 73 |
-
all_requests = []
|
| 74 |
-
|
| 75 |
-
class Handler(BaseHTTPRequestHandler):
|
| 76 |
-
def do_POST(self):
|
| 77 |
-
length = int(self.headers.get('Content-Length', 0))
|
| 78 |
-
body = json.loads(self.rfile.read(length)) if length else {}
|
| 79 |
-
|
| 80 |
-
if body.get('type') == 'sniffed_video_request':
|
| 81 |
-
url = body.get('url', '')
|
| 82 |
-
method = body.get('method', '?')
|
| 83 |
-
payload = body.get('payload', '')
|
| 84 |
-
|
| 85 |
-
log.info('🔍 %s %s', method, url)
|
| 86 |
-
if payload:
|
| 87 |
-
log.info(' %s', str(payload)[:1000])
|
| 88 |
-
|
| 89 |
-
all_requests.append({
|
| 90 |
-
'url': url,
|
| 91 |
-
'method': method,
|
| 92 |
-
'payload': payload,
|
| 93 |
-
'timestamp': body.get('timestamp'),
|
| 94 |
-
})
|
| 95 |
-
|
| 96 |
-
self.send_response(200)
|
| 97 |
-
self.send_header('Content-Type', 'application/json')
|
| 98 |
-
self.send_header('Access-Control-Allow-Origin', '*')
|
| 99 |
-
self.end_headers()
|
| 100 |
-
self.wfile.write(b'{"ok":true}')
|
| 101 |
-
|
| 102 |
-
def do_OPTIONS(self):
|
| 103 |
-
self.send_response(200)
|
| 104 |
-
self.send_header('Access-Control-Allow-Origin', '*')
|
| 105 |
-
self.send_header('Access-Control-Allow-Methods', 'POST')
|
| 106 |
-
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
|
| 107 |
-
self.end_headers()
|
| 108 |
-
|
| 109 |
-
def log_message(self, *a): pass
|
| 110 |
-
|
| 111 |
-
# Start HTTP server
|
| 112 |
-
srv = HTTPServer(('127.0.0.1', 8100), Handler)
|
| 113 |
-
threading.Thread(target=srv.serve_forever, daemon=True).start()
|
| 114 |
-
log.info('HTTP callback on :8100')
|
| 115 |
-
|
| 116 |
-
async def on_connect(ws):
|
| 117 |
-
log.info('✅ Extension connected!')
|
| 118 |
-
async for raw in ws:
|
| 119 |
-
data = json.loads(raw)
|
| 120 |
-
if data.get('type') == 'token_captured':
|
| 121 |
-
log.info('🔑 Token captured')
|
| 122 |
-
|
| 123 |
-
async def main():
|
| 124 |
-
async with websockets.serve(on_connect, '127.0.0.1', 9222):
|
| 125 |
-
log.info('⚡ WS server on :9222')
|
| 126 |
-
log.info('👉 Open Flow UI and perform any action...')
|
| 127 |
-
await asyncio.Future()
|
| 128 |
-
|
| 129 |
-
asyncio.run(main())
|
| 130 |
-
```
|
| 131 |
-
|
| 132 |
-
## What Gets Captured
|
| 133 |
-
|
| 134 |
-
| Action in Flow UI | API Endpoint |
|
| 135 |
-
|-------------------|-------------|
|
| 136 |
-
| Generate video (T2V) | `/v1/video:batchAsyncGenerateVideoText` |
|
| 137 |
-
| Generate video (I2V) | `/v1/video:batchAsyncGenerateVideoStartImage` |
|
| 138 |
-
| Generate video (Edit) | `/v1/video:batchAsyncGenerateVideoEditVideo` |
|
| 139 |
-
| Poll video status | `/v1/video:batchCheckAsyncVideoGenerationStatus` |
|
| 140 |
-
| Upload image | `/v1/flow/uploadImage` |
|
| 141 |
-
| Generate image | `/v1/projects/{id}/flowMedia:batchGenerateImages` |
|
| 142 |
-
| Get credits | `/v1/credits` |
|
| 143 |
-
| Get media | `/v1/media/{media_id}` |
|
| 144 |
-
| Upscale video | `/v1/video:batchAsyncGenerateVideoUpsampleVideo` |
|
| 145 |
-
|
| 146 |
-
## How to Find New Endpoints
|
| 147 |
-
|
| 148 |
-
### Example: Finding Video Upload
|
| 149 |
-
|
| 150 |
-
1. Start `sniff.py`
|
| 151 |
-
2. Open Flow UI
|
| 152 |
-
3. Drag & drop a video file into Flow
|
| 153 |
-
4. Check logs — you'll see the upload URL and payload format
|
| 154 |
-
5. Add the new endpoint to `models.json`
|
| 155 |
-
|
| 156 |
-
### Example: Finding Model Keys
|
| 157 |
-
|
| 158 |
-
1. Start `sniff.py`
|
| 159 |
-
2. Open Flow UI
|
| 160 |
-
3. Select different model (e.g., Omni Flash 4s)
|
| 161 |
-
4. Click Generate
|
| 162 |
-
5. Check logs — look for `videoModelKey` in the payload
|
| 163 |
-
|
| 164 |
-
```json
|
| 165 |
-
"requests": [{
|
| 166 |
-
"videoModelKey": "abra_t2v_4s", ← this is what you need
|
| 167 |
-
...
|
| 168 |
-
}]
|
| 169 |
-
```
|
| 170 |
-
|
| 171 |
-
## Architecture
|
| 172 |
-
|
| 173 |
-
```
|
| 174 |
-
┌─────────────────────────────────────────────┐
|
| 175 |
-
│ injected.js (MAIN world) │
|
| 176 |
-
│ - Monkey-patches window.fetch │
|
| 177 |
-
│ - Captures URL + body of every request │
|
| 178 |
-
│ - Posts to content.js via postMessage │
|
| 179 |
-
└──────────────────┬──────────────────────────┘
|
| 180 |
-
│ postMessage
|
| 181 |
-
┌──────────────────▼──────────────────────────┐
|
| 182 |
-
│ content.js (ISOLATED world) │
|
| 183 |
-
│ - Listens for __FLOWKIT_SNIFF__ messages │
|
| 184 |
-
│ - Forwards to background.js │
|
| 185 |
-
└──────────────────┬──────────────────────────┘
|
| 186 |
-
│ chrome.runtime.sendMessage
|
| 187 |
-
┌──────────────────▼──────────────────────────┐
|
| 188 |
-
│ background.js (Service Worker) │
|
| 189 |
-
│ - Receives SNIFFED_AISANDBOX_REQUEST │
|
| 190 |
-
│ - POSTs to http://127.0.0.1:8100/callback │
|
| 191 |
-
└──────────────────┬──────────────────────────┘
|
| 192 |
-
│ HTTP POST
|
| 193 |
-
┌──────────────────▼──────────────────────────┐
|
| 194 |
-
│ sniff.py (Your server) │
|
| 195 |
-
│ - Logs every request │
|
| 196 |
-
│ - Saves URL + method + payload │
|
| 197 |
-
└─────────────────────────────────────────────┘
|
| 198 |
-
```
|
| 199 |
-
|
| 200 |
-
## Tips
|
| 201 |
-
|
| 202 |
-
- **Filter by keyword**: Modify the sniff server to only log URLs containing specific words (e.g., `upload`, `video`, `generate`)
|
| 203 |
-
- **Save to file**: Add `json.dump(all_requests, open('sniffed.json', 'w'))` to save all captured requests
|
| 204 |
-
- **Compare payloads**: Run the same action with different settings and diff the payloads to find which fields control what
|
| 205 |
-
- **Telemetry noise**: Ignore URLs containing `batchLog`, `fetchUserRecommendations`, `frontendEvents` — these are analytics, not API calls
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
flow-agent/error.md
DELETED
|
@@ -1,74 +0,0 @@
|
|
| 1 |
-
# 🛠️ Flow Agent API Troubleshooting Guide (error.md)
|
| 2 |
-
|
| 3 |
-
This document contains standard error scenarios and how to resolve them quickly.
|
| 4 |
-
|
| 5 |
-
---
|
| 6 |
-
|
| 7 |
-
## 1. Error: `OSError: [Errno 48] Address already in use`
|
| 8 |
-
* **Symptom**: The server fails to start and exits with: `OSError: [Errno 48] Address already in use` (typically for port `8000`, `8100`, or `9222`).
|
| 9 |
-
* **Cause**: Another instance of the API server or a background flow-agent process is already running and occupying the port.
|
| 10 |
-
* **Resolution**:
|
| 11 |
-
Run the following commands in your terminal to find and kill the process:
|
| 12 |
-
```bash
|
| 13 |
-
# Clear API server port (8000)
|
| 14 |
-
kill -9 $(lsof -t -i:8000) 2>/dev/null || true
|
| 15 |
-
|
| 16 |
-
# Clear Extension bridge HTTP callback port (8100)
|
| 17 |
-
kill -9 $(lsof -t -i:8100) 2>/dev/null || true
|
| 18 |
-
|
| 19 |
-
# Clear Extension bridge WebSocket port (9222)
|
| 20 |
-
kill -9 $(lsof -t -i:9222) 2>/dev/null || true
|
| 21 |
-
```
|
| 22 |
-
|
| 23 |
-
---
|
| 24 |
-
|
| 25 |
-
## 2. Error: `Internal Server Error (500) - RuntimeError: curl failed`
|
| 26 |
-
* **Symptom**: Step `Uploading Video to Flow` fails with Status Code `500` and the server console prints: `RuntimeError: curl failed: ...`
|
| 27 |
-
* **Cause**: Your terminal session has sandbox/proxy environment variables active (`http_proxy`, `https_proxy`, `HTTP_PROXY`, or `HTTPS_PROXY` might be set by the AI agent sandbox). This causes `curl` to route all Google Cloud Storage uploads through a proxy that blocks the connection.
|
| 28 |
-
* **Resolution**:
|
| 29 |
-
Clear the proxy variables in your terminal window before starting the server and running the test script:
|
| 30 |
-
```bash
|
| 31 |
-
# 1. Unset the proxy variables
|
| 32 |
-
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY
|
| 33 |
-
|
| 34 |
-
# 2. Restart the API server in this terminal
|
| 35 |
-
venv/bin/python -m cli.api --port 8000
|
| 36 |
-
```
|
| 37 |
-
*(Make sure to also run `unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY` in your testing/client terminal window as well).*
|
| 38 |
-
|
| 39 |
-
---
|
| 40 |
-
|
| 41 |
-
## 3. Error: `Google Flow extension is not connected or unauthorized`
|
| 42 |
-
* **Symptom**: `/health` returns `has_flow_key: false` and generation calls return: `"Google Flow extension is not connected or unauthorized. Make sure Google Flow tab is open in Chrome."`
|
| 43 |
-
* **Cause**: The Extension Bridge WS is connected, but the Chrome extension is unable to capture the auth token (`flowKey`) because Google Flow is either not open, has gone idle, or your Google account has logged out.
|
| 44 |
-
* **Resolution**:
|
| 45 |
-
1. Open Chrome.
|
| 46 |
-
2. Make sure you are logged into your Google account at **[labs.google/fx/tools/flow](https://labs.google/fx/tools/flow)**.
|
| 47 |
-
3. Reload the page. The extension icon in your extension bar should show a green indicator.
|
| 48 |
-
4. Once logged in, the extension will automatically push the token to the server and heal the state.
|
| 49 |
-
|
| 50 |
-
---
|
| 51 |
-
|
| 52 |
-
## 4. Error: `Failed (0): TIMEOUT`
|
| 53 |
-
* **Symptom**: Request fails after 90 seconds with `TIMEOUT`.
|
| 54 |
-
* **Cause**: Google Flow took too long to respond, or there is an active **reCAPTCHA challenge** popped up on your Chrome browser that requires manual verification.
|
| 55 |
-
* **Resolution**:
|
| 56 |
-
1. Open Chrome and inspect the Google Flow tab.
|
| 57 |
-
2. If a reCAPTCHA prompt is present, solve it.
|
| 58 |
-
3. Reload the tab to refresh the connection, wait 5 seconds, and try your request again.
|
| 59 |
-
|
| 60 |
-
---
|
| 61 |
-
|
| 62 |
-
## 5. Error: `zsh: no such file or directory: venv/bin/python`
|
| 63 |
-
* **Symptom**: Running server or test script returns: `zsh: no such file or directory: venv/bin/python` or similar file errors.
|
| 64 |
-
* **Cause**: The command is run from the parent workspace folder (`N8N-Agent`) instead of the cloned `flow-agent` project folder where the virtual environment (`venv`) resides.
|
| 65 |
-
* **Resolution**:
|
| 66 |
-
Always change your directory to the `flow-agent` folder before running commands, or use the absolute paths:
|
| 67 |
-
```bash
|
| 68 |
-
# Go to the correct directory first
|
| 69 |
-
cd /path/to/flow-agent
|
| 70 |
-
|
| 71 |
-
# Then run the command
|
| 72 |
-
venv/bin/python -m cli.api --port 8000
|
| 73 |
-
```
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
flow-agent/test_api.py
DELETED
|
@@ -1,319 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""Integration Test Script for Flow Agent API.
|
| 3 |
-
|
| 4 |
-
Tests the following end-to-end flow:
|
| 5 |
-
1. GET /health
|
| 6 |
-
2. POST /generate/image (T2I)
|
| 7 |
-
3. POST /upload/image
|
| 8 |
-
4. POST /generate/video (I2V)
|
| 9 |
-
5. POST /upload/video
|
| 10 |
-
6. POST /edit/video (V2V)
|
| 11 |
-
"""
|
| 12 |
-
|
| 13 |
-
import os
|
| 14 |
-
import sys
|
| 15 |
-
import time
|
| 16 |
-
|
| 17 |
-
# Add virtual environment site-packages to sys.path to run with system python
|
| 18 |
-
venv_site_packages = os.path.join(os.path.dirname(os.path.abspath(__file__)), "venv", "lib", "python3.14", "site-packages")
|
| 19 |
-
if os.path.exists(venv_site_packages):
|
| 20 |
-
sys.path.insert(0, venv_site_packages)
|
| 21 |
-
|
| 22 |
-
# Clear proxy environment variables to bypass sandbox proxy for local requests
|
| 23 |
-
os.environ.pop("HTTP_PROXY", None)
|
| 24 |
-
os.environ.pop("HTTPS_PROXY", None)
|
| 25 |
-
os.environ.pop("http_proxy", None)
|
| 26 |
-
os.environ.pop("https_proxy", None)
|
| 27 |
-
|
| 28 |
-
# Auto-install requests if not present
|
| 29 |
-
try:
|
| 30 |
-
import requests
|
| 31 |
-
except ImportError:
|
| 32 |
-
print("📦 Installing 'requests' library for testing...")
|
| 33 |
-
os.system(f"{sys.executable} -m pip install requests")
|
| 34 |
-
import requests
|
| 35 |
-
|
| 36 |
-
API_URL = "http://localhost:8000"
|
| 37 |
-
|
| 38 |
-
def log_step(name):
|
| 39 |
-
print(f"\n{'=' * 60}")
|
| 40 |
-
print(f"🚀 STEP: {name}")
|
| 41 |
-
print(f"{'=' * 60}")
|
| 42 |
-
|
| 43 |
-
def check_health():
|
| 44 |
-
log_step("Checking Server Health")
|
| 45 |
-
try:
|
| 46 |
-
r = requests.get(f"{API_URL}/health")
|
| 47 |
-
print(f"Status Code: {r.status_code}")
|
| 48 |
-
print(f"Response: {r.text}")
|
| 49 |
-
if r.status_code != 200:
|
| 50 |
-
print("❌ Server health check failed!")
|
| 51 |
-
sys.exit(1)
|
| 52 |
-
data = r.json()
|
| 53 |
-
if not data.get("extension_connected"):
|
| 54 |
-
print("❌ Chrome extension is NOT connected. Please check Chrome devtools.")
|
| 55 |
-
sys.exit(1)
|
| 56 |
-
if not data.get("has_flow_key"):
|
| 57 |
-
print("⚠️ Chrome extension is connected but NOT logged in / authorized.")
|
| 58 |
-
print(" Please make sure labs.google/fx/tools/flow is open and logged in Chrome.")
|
| 59 |
-
sys.exit(1)
|
| 60 |
-
print("✅ Health Check Passed!")
|
| 61 |
-
except Exception as e:
|
| 62 |
-
print(f"❌ Failed to connect to server: {e}")
|
| 63 |
-
sys.exit(1)
|
| 64 |
-
|
| 65 |
-
def generate_image():
|
| 66 |
-
log_step("Text-to-Image (T2I) Generation")
|
| 67 |
-
payload = {
|
| 68 |
-
"prompt": "a simple red apple on a clean wooden table, professional studio lighting, 8k resolution",
|
| 69 |
-
"aspect": "square",
|
| 70 |
-
"count": 1
|
| 71 |
-
}
|
| 72 |
-
r = requests.post(f"{API_URL}/generate/image", json=payload)
|
| 73 |
-
print(f"Status Code: {r.status_code}")
|
| 74 |
-
print(f"Response: {r.text}")
|
| 75 |
-
|
| 76 |
-
if r.status_code != 200:
|
| 77 |
-
print("❌ Image generation failed!")
|
| 78 |
-
sys.exit(1)
|
| 79 |
-
|
| 80 |
-
data = r.json()
|
| 81 |
-
outputs = data.get("outputs", [])
|
| 82 |
-
if not outputs:
|
| 83 |
-
print("❌ No outputs returned in image generation!")
|
| 84 |
-
sys.exit(1)
|
| 85 |
-
|
| 86 |
-
img_info = outputs[0]
|
| 87 |
-
remote_url = img_info.get("remote_url")
|
| 88 |
-
filename = img_info.get("filename")
|
| 89 |
-
|
| 90 |
-
# Download generated image locally for next steps
|
| 91 |
-
local_img_path = os.path.join("output", "test_t2i.png")
|
| 92 |
-
os.makedirs("output", exist_ok=True)
|
| 93 |
-
|
| 94 |
-
print(f"📥 Downloading image from {remote_url}...")
|
| 95 |
-
img_r = requests.get(remote_url)
|
| 96 |
-
with open(local_img_path, "wb") as f:
|
| 97 |
-
f.write(img_r.content)
|
| 98 |
-
|
| 99 |
-
print(f"✅ Saved test image to {local_img_path}")
|
| 100 |
-
return local_img_path
|
| 101 |
-
|
| 102 |
-
def upload_image(image_path):
|
| 103 |
-
log_step("Uploading Image to Flow")
|
| 104 |
-
with open(image_path, "rb") as f:
|
| 105 |
-
files = {"file": (os.path.basename(image_path), f, "image/png")}
|
| 106 |
-
r = requests.post(f"{API_URL}/upload/image", files=files)
|
| 107 |
-
|
| 108 |
-
print(f"Status Code: {r.status_code}")
|
| 109 |
-
print(f"Response: {r.text}")
|
| 110 |
-
|
| 111 |
-
if r.status_code != 200:
|
| 112 |
-
print("❌ Image upload failed!")
|
| 113 |
-
sys.exit(1)
|
| 114 |
-
|
| 115 |
-
data = r.json()
|
| 116 |
-
media_id = data.get("media_id")
|
| 117 |
-
if not media_id:
|
| 118 |
-
print("❌ No media_id returned from upload!")
|
| 119 |
-
sys.exit(1)
|
| 120 |
-
|
| 121 |
-
print(f"✅ Image uploaded successfully! media_id: {media_id}")
|
| 122 |
-
return media_id
|
| 123 |
-
|
| 124 |
-
def generate_image_i2i(image_media_id):
|
| 125 |
-
log_step("Image-to-Image (I2I) Generation")
|
| 126 |
-
payload = {
|
| 127 |
-
"prompt": "transform the red apple into a vibrant cartoon style, Pixar 3D aesthetic",
|
| 128 |
-
"aspect": "square",
|
| 129 |
-
"count": 1,
|
| 130 |
-
"ref": [image_media_id]
|
| 131 |
-
}
|
| 132 |
-
r = requests.post(f"{API_URL}/generate/image", json=payload)
|
| 133 |
-
print(f"Status Code: {r.status_code}")
|
| 134 |
-
print(f"Response: {r.text}")
|
| 135 |
-
|
| 136 |
-
if r.status_code != 200:
|
| 137 |
-
print("❌ Image-to-Image (I2I) generation failed!")
|
| 138 |
-
sys.exit(1)
|
| 139 |
-
|
| 140 |
-
data = r.json()
|
| 141 |
-
outputs = data.get("outputs", [])
|
| 142 |
-
if not outputs:
|
| 143 |
-
print("❌ No outputs returned in I2I generation!")
|
| 144 |
-
sys.exit(1)
|
| 145 |
-
|
| 146 |
-
img_info = outputs[0]
|
| 147 |
-
remote_url = img_info.get("remote_url")
|
| 148 |
-
|
| 149 |
-
local_i2i_path = os.path.join("output", "test_i2i.png")
|
| 150 |
-
print(f"📥 Downloading I2I image from {remote_url}...")
|
| 151 |
-
img_r = requests.get(remote_url)
|
| 152 |
-
with open(local_i2i_path, "wb") as f:
|
| 153 |
-
f.write(img_r.content)
|
| 154 |
-
|
| 155 |
-
print(f"✅ Saved I2I image to {local_i2i_path}")
|
| 156 |
-
return local_i2i_path
|
| 157 |
-
|
| 158 |
-
def generate_video_t2v():
|
| 159 |
-
log_step("Text-to-Video (T2V) Generation")
|
| 160 |
-
payload = {
|
| 161 |
-
"prompt": "a beautiful golden retriever playing in a park, high quality, 8k, cinematic lighting",
|
| 162 |
-
"aspect": "portrait",
|
| 163 |
-
"duration": 4,
|
| 164 |
-
"count": 1
|
| 165 |
-
}
|
| 166 |
-
r = requests.post(f"{API_URL}/generate/video", json=payload)
|
| 167 |
-
print(f"Status Code: {r.status_code}")
|
| 168 |
-
print(f"Response: {r.text}")
|
| 169 |
-
|
| 170 |
-
if r.status_code != 200:
|
| 171 |
-
print("❌ T2V video generation failed!")
|
| 172 |
-
sys.exit(1)
|
| 173 |
-
|
| 174 |
-
data = r.json()
|
| 175 |
-
outputs = data.get("outputs", [])
|
| 176 |
-
if not outputs:
|
| 177 |
-
print("❌ No outputs returned in T2V video generation!")
|
| 178 |
-
sys.exit(1)
|
| 179 |
-
|
| 180 |
-
video_info = outputs[0]
|
| 181 |
-
download_url = video_info.get("download_url")
|
| 182 |
-
|
| 183 |
-
local_video_path = os.path.join("output", "test_t2v.mp4")
|
| 184 |
-
|
| 185 |
-
print(f"📥 Downloading video from {API_URL}{download_url}...")
|
| 186 |
-
video_r = requests.get(f"{API_URL}{download_url}")
|
| 187 |
-
with open(local_video_path, "wb") as f:
|
| 188 |
-
f.write(video_r.content)
|
| 189 |
-
|
| 190 |
-
print(f"✅ Saved T2V video to {local_video_path}")
|
| 191 |
-
return local_video_path
|
| 192 |
-
|
| 193 |
-
def generate_video_i2v(image_media_id):
|
| 194 |
-
log_step("Image-to-Video (I2V) Generation")
|
| 195 |
-
payload = {
|
| 196 |
-
"prompt": "the apple slowly rolls forward, dynamic cinematic panning, highly realistic",
|
| 197 |
-
"aspect": "portrait",
|
| 198 |
-
"duration": 4, # Short duration for faster testing
|
| 199 |
-
"count": 1,
|
| 200 |
-
"start": image_media_id # Animate using the uploaded image
|
| 201 |
-
}
|
| 202 |
-
r = requests.post(f"{API_URL}/generate/video", json=payload)
|
| 203 |
-
print(f"Status Code: {r.status_code}")
|
| 204 |
-
print(f"Response: {r.text}")
|
| 205 |
-
|
| 206 |
-
if r.status_code != 200:
|
| 207 |
-
print("❌ Video generation failed!")
|
| 208 |
-
sys.exit(1)
|
| 209 |
-
|
| 210 |
-
data = r.json()
|
| 211 |
-
outputs = data.get("outputs", [])
|
| 212 |
-
if not outputs:
|
| 213 |
-
print("❌ No outputs returned in video generation!")
|
| 214 |
-
sys.exit(1)
|
| 215 |
-
|
| 216 |
-
video_info = outputs[0]
|
| 217 |
-
download_url = video_info.get("download_url")
|
| 218 |
-
filename = video_info.get("filename")
|
| 219 |
-
|
| 220 |
-
local_video_path = os.path.join("output", "test_i2v.mp4")
|
| 221 |
-
|
| 222 |
-
print(f"📥 Downloading video from {API_URL}{download_url}...")
|
| 223 |
-
video_r = requests.get(f"{API_URL}{download_url}")
|
| 224 |
-
with open(local_video_path, "wb") as f:
|
| 225 |
-
f.write(video_r.content)
|
| 226 |
-
|
| 227 |
-
print(f"✅ Saved test video to {local_video_path}")
|
| 228 |
-
return local_video_path
|
| 229 |
-
|
| 230 |
-
def upload_video(video_path):
|
| 231 |
-
log_step("Uploading Video to Flow")
|
| 232 |
-
with open(video_path, "rb") as f:
|
| 233 |
-
files = {"file": (os.path.basename(video_path), f, "video/mp4")}
|
| 234 |
-
r = requests.post(f"{API_URL}/upload/video", files=files)
|
| 235 |
-
|
| 236 |
-
print(f"Status Code: {r.status_code}")
|
| 237 |
-
print(f"Response: {r.text}")
|
| 238 |
-
|
| 239 |
-
if r.status_code != 200:
|
| 240 |
-
print("❌ Video upload failed!")
|
| 241 |
-
sys.exit(1)
|
| 242 |
-
|
| 243 |
-
data = r.json()
|
| 244 |
-
media_id = data.get("media_id")
|
| 245 |
-
if not media_id:
|
| 246 |
-
print("❌ No media_id returned from upload!")
|
| 247 |
-
sys.exit(1)
|
| 248 |
-
|
| 249 |
-
print(f"✅ Video uploaded successfully! media_id: {media_id}")
|
| 250 |
-
return media_id
|
| 251 |
-
|
| 252 |
-
def edit_video_v2v(video_media_id):
|
| 253 |
-
log_step("Video-to-Video (V2V) Editing")
|
| 254 |
-
payload = {
|
| 255 |
-
"prompt": "make it look like an anime sketch style, drawing pencil style",
|
| 256 |
-
"video_media_id": video_media_id,
|
| 257 |
-
"aspect": "portrait",
|
| 258 |
-
"fps": 24,
|
| 259 |
-
"duration": 4
|
| 260 |
-
}
|
| 261 |
-
r = requests.post(f"{API_URL}/edit/video", json=payload)
|
| 262 |
-
print(f"Status Code: {r.status_code}")
|
| 263 |
-
print(f"Response: {r.text}")
|
| 264 |
-
|
| 265 |
-
if r.status_code != 200:
|
| 266 |
-
print("❌ V2V video editing failed!")
|
| 267 |
-
sys.exit(1)
|
| 268 |
-
|
| 269 |
-
data = r.json()
|
| 270 |
-
outputs = data.get("outputs", [])
|
| 271 |
-
if not outputs:
|
| 272 |
-
print("❌ No outputs returned in video edit!")
|
| 273 |
-
sys.exit(1)
|
| 274 |
-
|
| 275 |
-
video_info = outputs[0]
|
| 276 |
-
download_url = video_info.get("download_url")
|
| 277 |
-
|
| 278 |
-
local_edit_path = os.path.join("output", "test_v2v.mp4")
|
| 279 |
-
|
| 280 |
-
print(f"📥 Downloading edited video from {API_URL}{download_url}...")
|
| 281 |
-
video_r = requests.get(f"{API_URL}{download_url}")
|
| 282 |
-
with open(local_edit_path, "wb") as f:
|
| 283 |
-
f.write(video_r.content)
|
| 284 |
-
|
| 285 |
-
print(f"✅ Saved edited video to {local_edit_path}")
|
| 286 |
-
|
| 287 |
-
def main():
|
| 288 |
-
print("🎬 Starting Flow Agent API Integration Test...")
|
| 289 |
-
|
| 290 |
-
# 1. Health check
|
| 291 |
-
check_health()
|
| 292 |
-
|
| 293 |
-
# 2. Text to Image (T2I)
|
| 294 |
-
local_image = generate_image()
|
| 295 |
-
|
| 296 |
-
# 2b. Text to Video (T2V)
|
| 297 |
-
local_t2v_video = generate_video_t2v()
|
| 298 |
-
|
| 299 |
-
# 3. Upload Image to GCS/Flow
|
| 300 |
-
image_media_id = upload_image(local_image)
|
| 301 |
-
|
| 302 |
-
# 4. Image to Image (I2I)
|
| 303 |
-
local_i2i_image = generate_image_i2i(image_media_id)
|
| 304 |
-
|
| 305 |
-
# 5. Image to Video (I2V)
|
| 306 |
-
local_video = generate_video_i2v(image_media_id)
|
| 307 |
-
|
| 308 |
-
# 6. Upload Video to GCS/Flow
|
| 309 |
-
video_media_id = upload_video(local_video)
|
| 310 |
-
|
| 311 |
-
# 7. Video to Video (V2V)
|
| 312 |
-
edit_video_v2v(video_media_id)
|
| 313 |
-
|
| 314 |
-
print("\n" + "=" * 60)
|
| 315 |
-
print("🎉 ALL TESTS PASSED SUCCESSFULLY! Flow Agent API is 100% verified.")
|
| 316 |
-
print("=" * 60)
|
| 317 |
-
|
| 318 |
-
if __name__ == "__main__":
|
| 319 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{chatgpt-free-api → free-Chatgpt-api}/.gitignore
RENAMED
|
@@ -11,3 +11,6 @@ debug_poll.json
|
|
| 11 |
|
| 12 |
# Local Output Files
|
| 13 |
output/
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
# Local Output Files
|
| 13 |
output/
|
| 14 |
+
|
| 15 |
+
# prebuilt linux binary (Docker builds from source)
|
| 16 |
+
*.linux-amd64
|
{chatgpt-free-api → free-Chatgpt-api}/agent.go
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/api_bridge.go
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/chatgpt.go
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/config.go
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/config.json
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/cookies.go
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/cookies_test.go
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/extension.go
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/go.mod
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/go.sum
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/gpt-extension/background.js
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/gpt-extension/icon128.png
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/gpt-extension/icon16.png
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/gpt-extension/icon48.png
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/gpt-extension/icon_large.png
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/gpt-extension/manifest.json
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/gpt-extension/popup.html
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/gpt-extension/popup.js
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/handlers.go
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/helpers.go
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/main.go
RENAMED
|
File without changes
|
{chatgpt-free-api → free-Chatgpt-api}/sniff.go
RENAMED
|
File without changes
|
free-gemini-api/.dockerignore
DELETED
|
@@ -1,8 +0,0 @@
|
|
| 1 |
-
dist/
|
| 2 |
-
output/
|
| 3 |
-
cookies.json
|
| 4 |
-
.env
|
| 5 |
-
.git/
|
| 6 |
-
.DS_Store
|
| 7 |
-
goapi
|
| 8 |
-
*.exe
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
free-gemini-api/Dockerfile
DELETED
|
@@ -1,21 +0,0 @@
|
|
| 1 |
-
# Build stage
|
| 2 |
-
FROM golang:1.25-alpine AS builder
|
| 3 |
-
WORKDIR /app
|
| 4 |
-
COPY go.mod go.sum ./
|
| 5 |
-
RUN go mod download
|
| 6 |
-
COPY . .
|
| 7 |
-
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o free-gemini-api .
|
| 8 |
-
|
| 9 |
-
# Runtime stage
|
| 10 |
-
FROM alpine:3.19
|
| 11 |
-
RUN apk --no-cache add ca-certificates curl
|
| 12 |
-
WORKDIR /app
|
| 13 |
-
COPY --from=builder /app/free-gemini-api .
|
| 14 |
-
RUN mkdir -p /app/output
|
| 15 |
-
|
| 16 |
-
EXPOSE 8000 9226
|
| 17 |
-
|
| 18 |
-
ENV WS_PORT="9226" \
|
| 19 |
-
PORT="8000"
|
| 20 |
-
|
| 21 |
-
CMD ["./free-gemini-api"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
free-gemini-api/README.md
DELETED
|
@@ -1,401 +0,0 @@
|
|
| 1 |
-
<div align="center">
|
| 2 |
-
|
| 3 |
-
# 🔓 Free Gemini API
|
| 4 |
-
|
| 5 |
-
### Unleash the Full Power of Google Gemini 3.5 Flash, Imagen 3, and Gemini Video — Completely Free
|
| 6 |
-
|
| 7 |
-
[](https://go.dev)
|
| 8 |
-
[](LICENSE)
|
| 9 |
-
[](https://hub.docker.com/r/akashyadav758/free-gemini-api)
|
| 10 |
-
|
| 11 |
-
**No API Keys. No Token Costs. No Complex Setup. Zero Watermarks.**
|
| 12 |
-
|
| 13 |
-
*A high-performance Go reverse-proxy API server wrapping the consumer Gemini portal (`gemini.google.com`). Access text generation, multi-turn chat, image creation (Imagen 3), video creation (Gemini Video), video-to-video styling, and music generation through a simple unified REST interface.*
|
| 14 |
-
|
| 15 |
-
[Architectural Benefits](#-architectural-benefits) • [Key Features](#-key-features) • [Quick Start](#-quick-start) • [API Endpoints](#-api-endpoints) • [SDK Integration](#-sdk-integration) • [Architecture](#-architecture)
|
| 16 |
-
|
| 17 |
-
</div>
|
| 18 |
-
|
| 19 |
-
---
|
| 20 |
-
|
| 21 |
-
## 🚀 Architectural Benefits
|
| 22 |
-
|
| 23 |
-
This project is engineered to solve the real-world operational challenges of web-scraping and reverse-proxying consumer endpoints, delivering production-grade reliability:
|
| 24 |
-
|
| 25 |
-
### 🎨 In-Place Reverse Alpha-Blending Watermark Remover
|
| 26 |
-
Google adds visual watermarks to the bottom-right corner of all generated images and videos. The Go API server includes a highly optimized, fully automated native Go implementation ([gemini/watermark.go](file:///Users/akashyadav/Server/Go-Map/Free-gemini-api/free-gemini-api-kodelyx/gemini/watermark.go)) that executes in-process immediately upon download.
|
| 27 |
-
* **The Mathematics:** It reverses the blending equation:
|
| 28 |
-
$$\text{watermarked} = \alpha \times \text{logo} + (1 - \alpha) \times \text{original}$$
|
| 29 |
-
$${\text{original}} = \frac{\text{watermarked} - \alpha \times \text{logo}}{1 - \alpha}$$
|
| 30 |
-
* **Layout Adaptability:** Dynamically identifies coordinates and dimensions based on the file aspect ratio (Portrait `720x1280` maps to a `48px` watermark; standard landscape/large scales dynamically map to a `96px` watermark), yielding perfectly clean, professional-grade media outputs without black borders or distortion.
|
| 31 |
-
* **Zero Dependencies:** Completely native—runs without requiring Python 3, `numpy`, or `opencv-python`!
|
| 32 |
-
|
| 33 |
-
### 🔄 403 Forbidden Auto-Redirect Bypasser
|
| 34 |
-
Downloading media from Google's content servers (`googleusercontent.com`) directly often results in `403 Forbidden` errors. This client implements a custom redirect follow loop:
|
| 35 |
-
* **The Pipeline:** It disables Go's default automatic HTTP redirect handler, manually intercepts HTTP `302/307` locations, and dynamically injects the user's active session cookies on *every* redirect hop (e.g. cross-domain transfers to `lh3.usercontent` and `fife.usercontent`), guaranteeing 100% download success.
|
| 36 |
-
|
| 37 |
-
### 🔌 Zero-Spam Chrome Extension WebSocket Bridge
|
| 38 |
-
Keeping cookies updated across server restarts is solved via our lightweight Chrome Extension (`/extension`):
|
| 39 |
-
* **One-Shot Sync:** Utilizing a persistent WebSocket connection, the extension instantly syncs active cookies on startup or when the browser detects a change, completely avoiding heavy periodic background alarm polls and preserving laptop battery.
|
| 40 |
-
|
| 41 |
-
### 🔍 Parser-Level Deduplication & Rapid Refusal Detection
|
| 42 |
-
* **Deduplication:** The JSON response parser identifies and filters out duplicate image/video references returned by the Gemini consumer API, saving bandwidth and disk space.
|
| 43 |
-
* **Refusal Detection:** Recognizes generation limit responses early (e.g., *“I can't create more videos for you today...”*), immediately terminating the long-polling routine and returning structured JSON failures instead of hanging for minutes.
|
| 44 |
-
|
| 45 |
-
---
|
| 46 |
-
|
| 47 |
-
## 🔥 Comparison
|
| 48 |
-
|
| 49 |
-
| Feature | Standard Cloud API | **Free Gemini API** |
|
| 50 |
-
|:---|:---|:---|
|
| 51 |
-
| **Pricing** | Pay per token / image / video | **100% Free** |
|
| 52 |
-
| **API Key Required** | Yes | **No** (Uses Cookie Session) |
|
| 53 |
-
| **Video Generation** | Highly restricted (Expensive) | **Gemini Video Enabled** (Standard Daily Quota) |
|
| 54 |
-
| **Image Generation** | Cost per image | **Imagen 3 Enabled** (Unlimited - Uses Gemini 3.5) |
|
| 55 |
-
| **Watermark Removal** | None (Or paid edit) | **Automatic In-Place Clean** |
|
| 56 |
-
| **Setup Overhead** | Complex SDK configurations | **Single REST API / Docker Container** |
|
| 57 |
-
|
| 58 |
-
---
|
| 59 |
-
|
| 60 |
-
## ✨ Key Features
|
| 61 |
-
|
| 62 |
-
* **AI Chat:** Multi-turn conversational memory with Server-Sent Events (SSE) streaming support (OpenAI-compatible `data: [DONE]` format).
|
| 63 |
-
* **Imagen 3 Image Gen:** Generate stunning visuals from text prompts, perform image-to-image editing (e.g., altering clothing, backgrounds, or assets), or upload up to 10 images simultaneously for multi-image analysis.
|
| 64 |
-
* **Gemini Video Gen:** Generate text-to-video clips, image-to-video reference animations, or perform complex video-to-video visual edits (e.g., turning a beach video loop into a Studio Ghibli hand-drawn anime style).
|
| 65 |
-
* **Music Generation:** Produce vocal and instrumental tracks on-demand, returned as local static URL references.
|
| 66 |
-
* **Automated Session Hot-Reload:** Hot-reloads session settings and tokens on cookie sync updates, allowing uninterrupted server operation.
|
| 67 |
-
|
| 68 |
-
---
|
| 69 |
-
|
| 70 |
-
## 🏁 Quick Start
|
| 71 |
-
|
| 72 |
-
### Step 1: Install Chrome Cookie Bridge Extension
|
| 73 |
-
1. Open Google Chrome and go to `chrome://extensions/`.
|
| 74 |
-
2. Enable **Developer mode** (top-right toggle).
|
| 75 |
-
3. Click **Load unpacked** (top-left) and select the `extension` folder located in this repository.
|
| 76 |
-
4. Log into [gemini.google.com](https://gemini.google.com) on this browser instance.
|
| 77 |
-
5. The extension will automatically detect your active session and sync your cookies to the Go backend WebSocket server (default port `9222`).
|
| 78 |
-
|
| 79 |
-
---
|
| 80 |
-
|
| 81 |
-
### Step 2: Run the Server
|
| 82 |
-
|
| 83 |
-
Choose your preferred deployment method below:
|
| 84 |
-
|
| 85 |
-
#### Option A: Pre-built Binary (Recommended & Easiest)
|
| 86 |
-
1. Download the latest release binary matching your operating system from the GitHub Releases.
|
| 87 |
-
2. Make the binary executable and launch it:
|
| 88 |
-
* **macOS / Linux:**
|
| 89 |
-
```bash
|
| 90 |
-
chmod +x free-gemini-api-mac-arm64
|
| 91 |
-
./free-gemini-api-mac-arm64
|
| 92 |
-
```
|
| 93 |
-
* **Windows:** Double-click on `free-gemini-api-win-x64.exe` or run it from CMD/PowerShell.
|
| 94 |
-
|
| 95 |
-
#### Option B: From Source (For Local Dev)
|
| 96 |
-
Ensure you have Go 1.21+ installed on your system. Watermark removal and all endpoints run natively without any Python requirements!
|
| 97 |
-
```bash
|
| 98 |
-
# Run the Go server
|
| 99 |
-
go run .
|
| 100 |
-
```
|
| 101 |
-
|
| 102 |
-
#### Option C: Docker Setup
|
| 103 |
-
Run the pre-built Docker container:
|
| 104 |
-
```bash
|
| 105 |
-
docker run -d --name free-gemini-api \
|
| 106 |
-
-e WS_PORT=9222 \
|
| 107 |
-
-e PORT=8000 \
|
| 108 |
-
-p 8000:8000 \
|
| 109 |
-
-p 9222:9222 \
|
| 110 |
-
akashyadav758/free-gemini-api:latest
|
| 111 |
-
```
|
| 112 |
-
-e WS_PORT=9222 \
|
| 113 |
-
-e PORT=8000 \
|
| 114 |
-
-p 8000:8000 \
|
| 115 |
-
-p 9222:9222 \
|
| 116 |
-
akashyadav758/free-gemini-api:latest
|
| 117 |
-
```
|
| 118 |
-
|
| 119 |
-
---
|
| 120 |
-
|
| 121 |
-
### Step 3: Environment Variables (`.env`)
|
| 122 |
-
Create a `.env` file in the root of the project to customize connection configurations:
|
| 123 |
-
```env
|
| 124 |
-
WS_PORT=8001 # WebSocket cookie bridge port (configured in extension)
|
| 125 |
-
PORT=8000 # Port on which this Go API server runs
|
| 126 |
-
```
|
| 127 |
-
|
| 128 |
-
---
|
| 129 |
-
|
| 130 |
-
## 📡 API Endpoints
|
| 131 |
-
|
| 132 |
-
### 1. `POST /chat` — Unified Interface
|
| 133 |
-
Handles text conversations, streaming, image generation, multi-image analysis, and video processing.
|
| 134 |
-
|
| 135 |
-
#### parameters
|
| 136 |
-
|
| 137 |
-
| Field | Type | Description |
|
| 138 |
-
|:---|:---|:---|
|
| 139 |
-
| `prompt` | `string` | **Required.** The instruction or text query. |
|
| 140 |
-
| `user_id` | `string` | Optional. Isolated session identity. |
|
| 141 |
-
| `new_chat` | `bool` | Optional. Clear conversational history context. |
|
| 142 |
-
| `stream` | `bool` | Optional. Stream responses back via SSE. |
|
| 143 |
-
| `image` | `file(s)` | Optional. Multi-part form image upload (supports up to 10 files). |
|
| 144 |
-
|
| 145 |
-
---
|
| 146 |
-
|
| 147 |
-
### End-Point Integrations & Examples
|
| 148 |
-
|
| 149 |
-
#### A. Text Generation (Standard JSON)
|
| 150 |
-
```bash
|
| 151 |
-
curl -X POST http://localhost:8000/chat \
|
| 152 |
-
-H "Content-Type: application/json" \
|
| 153 |
-
-d '{
|
| 154 |
-
"prompt": "Explain quantum computing in three clear bullet points.",
|
| 155 |
-
"user_id": "test_user_1",
|
| 156 |
-
"new_chat": true
|
| 157 |
-
}'
|
| 158 |
-
```
|
| 159 |
-
|
| 160 |
-
#### B. Streaming Text Chat (Server-Sent Events)
|
| 161 |
-
```bash
|
| 162 |
-
curl -N -X POST http://localhost:8000/chat \
|
| 163 |
-
-H "Content-Type: application/json" \
|
| 164 |
-
-d '{
|
| 165 |
-
"prompt": "Write a short creative story about a developer and an AI assistant.",
|
| 166 |
-
"user_id": "test_user_1",
|
| 167 |
-
"stream": true
|
| 168 |
-
}'
|
| 169 |
-
```
|
| 170 |
-
|
| 171 |
-
#### C. Imagen 3 Image Generation (Text → Image)
|
| 172 |
-
Prompt the system to generate pictures, which are automatically downloaded, cleaned of watermarks, and saved locally:
|
| 173 |
-
```bash
|
| 174 |
-
curl -X POST http://localhost:8000/chat \
|
| 175 |
-
-H "Content-Type: application/json" \
|
| 176 |
-
-d '{
|
| 177 |
-
"prompt": "Generate a beautiful landscape image of a sunset over snow-covered mountains, highly detailed.",
|
| 178 |
-
"user_id": "test_user_1",
|
| 179 |
-
"new_chat": true
|
| 180 |
-
}'
|
| 181 |
-
```
|
| 182 |
-
|
| 183 |
-
#### D. Image Editing & Variations (Image + Text Prompt)
|
| 184 |
-
Upload an existing image and tell the AI to edit it in place:
|
| 185 |
-
```bash
|
| 186 |
-
curl -X POST http://localhost:8000/chat \
|
| 187 |
-
-F "prompt=Change the background of this image to a busy neon cyberpunk street" \
|
| 188 |
-
-F "image=@/path/to/my_photo.png" \
|
| 189 |
-
-F "user_id=test_user_1" \
|
| 190 |
-
-F "new_chat=true"
|
| 191 |
-
```
|
| 192 |
-
|
| 193 |
-
#### E. Gemini Video Generation (Image → Video / Text → Video)
|
| 194 |
-
Provide an image reference and animate it, or generate fresh video segments from text:
|
| 195 |
-
```bash
|
| 196 |
-
curl -X POST http://localhost:8000/chat \
|
| 197 |
-
-F "prompt=Animate this scene: make the ocean waves crash dynamically on the shore with smooth cinematic motions" \
|
| 198 |
-
-F "image=@/path/to/beach_scene.png" \
|
| 199 |
-
-F "user_id=test_user_1" \
|
| 200 |
-
-F "new_chat=true"
|
| 201 |
-
```
|
| 202 |
-
|
| 203 |
-
#### F. Video-to-Video Visual Styling (Video File Input)
|
| 204 |
-
Upload a video and completely rewrite its artistic style:
|
| 205 |
-
```bash
|
| 206 |
-
curl -X POST http://localhost:8000/chat \
|
| 207 |
-
-F "prompt=Edit this video: Convert the entire visual style of this video into a beautiful Studio Ghibli hand-drawn anime style." \
|
| 208 |
-
-F "image=@/path/to/original_video.mp4;type=video/mp4" \
|
| 209 |
-
-F "user_id=test_user_1" \
|
| 210 |
-
-F "new_chat=true"
|
| 211 |
-
```
|
| 212 |
-
|
| 213 |
-
---
|
| 214 |
-
|
| 215 |
-
### 2. `POST /music` — Song & Audio Track Generation
|
| 216 |
-
Generates audio tracks from description prompts.
|
| 217 |
-
```bash
|
| 218 |
-
curl -X POST http://localhost:8000/music \
|
| 219 |
-
-H "Content-Type: application/json" \
|
| 220 |
-
-d '{
|
| 221 |
-
"prompt": "A relaxing lofi chillhop beat with smooth piano and rain sounds",
|
| 222 |
-
"user_id": "test_user_1",
|
| 223 |
-
"new_chat": true
|
| 224 |
-
}'
|
| 225 |
-
```
|
| 226 |
-
|
| 227 |
-
---
|
| 228 |
-
|
| 229 |
-
### Response Structure (JSON Example)
|
| 230 |
-
```json
|
| 231 |
-
{
|
| 232 |
-
"text": "I have successfully generated your media loop based on the prompt...",
|
| 233 |
-
"conversation_id": "c_3eeb67f56f0c82f2",
|
| 234 |
-
"response_id": "r_5fd71304b0a2190b",
|
| 235 |
-
"choice_id": "rc_82705406bcc3a2f7",
|
| 236 |
-
"images": [
|
| 237 |
-
"http://localhost:8000/static/img_r_5fd71304b0a2190b_0.png"
|
| 238 |
-
],
|
| 239 |
-
"videos": [
|
| 240 |
-
"http://localhost:8000/static/vid_r_5fd71304b0a2190b_0.mp4"
|
| 241 |
-
],
|
| 242 |
-
"music": [],
|
| 243 |
-
"elapsed": 43.56
|
| 244 |
-
}
|
| 245 |
-
```
|
| 246 |
-
*(All generated files served dynamically from `/static/*` have already undergone in-place watermark removal).*
|
| 247 |
-
|
| 248 |
-
---
|
| 249 |
-
|
| 250 |
-
## 🐍 SDK Integration
|
| 251 |
-
|
| 252 |
-
### Python Example
|
| 253 |
-
A production-ready script utilizing Python's `requests` library to interface with the REST server:
|
| 254 |
-
|
| 255 |
-
```python
|
| 256 |
-
import requests
|
| 257 |
-
import json
|
| 258 |
-
|
| 259 |
-
BASE_URL = "http://localhost:8000"
|
| 260 |
-
|
| 261 |
-
def generate_text(prompt: str, user_id: str = "py_user"):
|
| 262 |
-
url = f"{BASE_URL}/chat"
|
| 263 |
-
payload = {
|
| 264 |
-
"prompt": prompt,
|
| 265 |
-
"user_id": user_id,
|
| 266 |
-
"new_chat": True
|
| 267 |
-
}
|
| 268 |
-
headers = {"Content-Type": "application/json"}
|
| 269 |
-
|
| 270 |
-
response = requests.post(url, json=payload, headers=headers)
|
| 271 |
-
return response.json()
|
| 272 |
-
|
| 273 |
-
def edit_image_to_video(prompt: str, image_path: str, user_id: str = "py_user"):
|
| 274 |
-
url = f"{BASE_URL}/chat"
|
| 275 |
-
files = {
|
| 276 |
-
"image": (image_path, open(image_path, "rb"), "image/png")
|
| 277 |
-
}
|
| 278 |
-
data = {
|
| 279 |
-
"prompt": prompt,
|
| 280 |
-
"user_id": user_id,
|
| 281 |
-
"new_chat": "true"
|
| 282 |
-
}
|
| 283 |
-
|
| 284 |
-
response = requests.post(url, files=files, data=data)
|
| 285 |
-
return response.json()
|
| 286 |
-
|
| 287 |
-
if __name__ == "__main__":
|
| 288 |
-
# Generate simple text
|
| 289 |
-
print("Sending text prompt...")
|
| 290 |
-
text_res = generate_text("Explain machine learning in one sentence.")
|
| 291 |
-
print("Response:", text_res["text"])
|
| 292 |
-
|
| 293 |
-
# Generate / Edit Image-to-Video
|
| 294 |
-
# print(edit_image_to_video("Animate a flying eagle in the clouds", "eagle.png"))
|
| 295 |
-
```
|
| 296 |
-
|
| 297 |
-
### Node.js Example (Streaming Support)
|
| 298 |
-
An asynchronous ES6 script displaying streaming chunks using standard node-fetch:
|
| 299 |
-
|
| 300 |
-
```javascript
|
| 301 |
-
const BASE_URL = "http://localhost:8000";
|
| 302 |
-
|
| 303 |
-
async function streamStory(prompt) {
|
| 304 |
-
const response = await fetch(`${BASE_URL}/chat`, {
|
| 305 |
-
method: "POST",
|
| 306 |
-
headers: { "Content-Type": "application/json" },
|
| 307 |
-
body: JSON.stringify({
|
| 308 |
-
prompt: prompt,
|
| 309 |
-
stream: true,
|
| 310 |
-
user_id: "node_user",
|
| 311 |
-
new_chat: true
|
| 312 |
-
})
|
| 313 |
-
});
|
| 314 |
-
|
| 315 |
-
const reader = response.body.getReader();
|
| 316 |
-
const decoder = new TextDecoder("utf-8");
|
| 317 |
-
|
| 318 |
-
while (true) {
|
| 319 |
-
const { done, value } = await reader.read();
|
| 320 |
-
if (done) break;
|
| 321 |
-
|
| 322 |
-
const chunk = decoder.decode(value);
|
| 323 |
-
// Print SSE stream lines
|
| 324 |
-
console.log(chunk);
|
| 325 |
-
}
|
| 326 |
-
}
|
| 327 |
-
|
| 328 |
-
streamStory("Write a short futuristic sci-fi paragraph.");
|
| 329 |
-
```
|
| 330 |
-
|
| 331 |
-
---
|
| 332 |
-
|
| 333 |
-
## 🏗️ Architecture
|
| 334 |
-
|
| 335 |
-
```
|
| 336 |
-
┌────────────────────────┐
|
| 337 |
-
│ REST Client │
|
| 338 |
-
│ (Python, Node, cURL) │
|
| 339 |
-
└───────────┬────────────┘
|
| 340 |
-
│
|
| 341 |
-
│ HTTP REST / SSE (Port 8000)
|
| 342 |
-
▼
|
| 343 |
-
┌─────────────────────────────────────────────────────────────────────────────────────────┐
|
| 344 |
-
│ Go API Gateway (Fiber v3 Server) │
|
| 345 |
-
│ │
|
| 346 |
-
│ ┌────────────────┐ ┌──────────────────┐ ┌────────────────┐ ┌────────────┐ │
|
| 347 |
-
│ │ Unified Chat │ │ Streaming Engine │ │ Media Down- │ │ In-Place │ │
|
| 348 |
-
│ │ Endpoints │ │ (Server-Sent │ │ loader & Cookie│ │ Watermark │ │
|
| 349 |
-
│ │ (POST /chat) │ │ Events) │ │ Injector │ │ Processor │ │
|
| 350 |
-
│ └───────┬────────┘ └────────┬─────────┘ └────────┬───────┘ └──────┬─────┘ │
|
| 351 |
-
└──────────┼───────────────────────┼────────────────────────┼────────────────────┼────────┘
|
| 352 |
-
│ │ │ │
|
| 353 |
-
│ JSON RPC payload │ Chunk Streams │ Follows manual │ Executes
|
| 354 |
-
│ │ │ redirects │ watermark.go
|
| 355 |
-
▼ ▼ ▼ ▼
|
| 356 |
-
┌─────────────────────────────────────────────────────────────────────────────────────────┐
|
| 357 |
-
│ Google Gemini Web API Backend │
|
| 358 |
-
│ (consumer.rpc.StreamGenerate / hNvQHb video poller / Imagen 3 Engine) │
|
| 359 |
-
└─────────────────────────────────────────────────────────▲───────────────────────────────┘
|
| 360 |
-
│
|
| 361 |
-
│ Live Cookie Synchronization
|
| 362 |
-
│ (WebSocket Bridge Port 9222)
|
| 363 |
-
┌────────┴────────┐
|
| 364 |
-
│ Chrome Extension│
|
| 365 |
-
│ Cookie Bridge │
|
| 366 |
-
└─────────────────┘
|
| 367 |
-
```
|
| 368 |
-
|
| 369 |
-
### Directory Structure
|
| 370 |
-
```
|
| 371 |
-
├── main.go # Fiber HTTP engine startup, Universal REST routers
|
| 372 |
-
├── .env # Port and WebSocket bridge configs
|
| 373 |
-
├── cookies.json # Live cookie storage, written dynamically via extension
|
| 374 |
-
├── gemini/
|
| 375 |
-
│ ├── client.go # Session initialization, API calls, redirect tracking
|
| 376 |
-
│ ├── models.go # API schemas and structures
|
| 377 |
-
│ ├── monitor.go # WebSocket bridge and cookies listener
|
| 378 |
-
│ └── watermark.go # Native Go reverse alpha-blending watermark remover
|
| 379 |
-
└── extension/
|
| 380 |
-
├── manifest.json # Chrome browser extension manifest definitions
|
| 381 |
-
├── background.js # WebSocket connection to Go backend, cookie event handlers
|
| 382 |
-
├── popup.html # Interactive developer status panel UI
|
| 383 |
-
└── popup.js # Local settings controller
|
| 384 |
-
```
|
| 385 |
-
|
| 386 |
-
---
|
| 387 |
-
|
| 388 |
-
## ⚠️ Limits & Notes
|
| 389 |
-
|
| 390 |
-
* **Video Quota limits:** Gemini Video generation is limited to standard consumer daily accounts (~3-5 videos daily). The system returns an immediate refusal object with `videos: null` once limits are triggered.
|
| 391 |
-
* **WebSocket Cookie Sync:** Ensure you load the extension located under `/extension` in your browser to automatically stream and sync active cookies to the backend Go server on port `9222`.
|
| 392 |
-
|
| 393 |
-
---
|
| 394 |
-
|
| 395 |
-
<div align="center">
|
| 396 |
-
|
| 397 |
-
**⭐ Star this repository if this project helps you! ⭐**
|
| 398 |
-
|
| 399 |
-
*Disclaimer: This is an unofficial proxy wrapper built for educational and personal research projects. Adhere to Google Terms of Service.*
|
| 400 |
-
|
| 401 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
monitor/main.go
CHANGED
|
@@ -546,6 +546,11 @@ func authOK(w http.ResponseWriter, r *http.Request) bool {
|
|
| 546 |
if got == "" {
|
| 547 |
got = r.URL.Query().Get("key")
|
| 548 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 549 |
if got != apiKey {
|
| 550 |
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
| 551 |
return false
|
|
@@ -553,6 +558,18 @@ func authOK(w http.ResponseWriter, r *http.Request) bool {
|
|
| 553 |
return true
|
| 554 |
}
|
| 555 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 556 |
// gatewayHandler builds an API-key-gated reverse proxy that strips prefix and
|
| 557 |
// forwards to target (e.g. /gpt/api/chat -> http://127.0.0.1:9225/api/chat).
|
| 558 |
func gatewayHandler(prefix, target string) http.HandlerFunc {
|
|
@@ -606,20 +623,28 @@ func main() {
|
|
| 606 |
http.NotFound(w, r)
|
| 607 |
return
|
| 608 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 609 |
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
| 610 |
w.Write(indexHTML)
|
| 611 |
})
|
| 612 |
-
http.HandleFunc("/api/tabs", tabsHandler)
|
| 613 |
-
http.HandleFunc("/api/screen", screenHandler)
|
| 614 |
-
http.HandleFunc("/api/input", inputHandler)
|
| 615 |
-
http.HandleFunc("/api/navigate", navigateHandler)
|
| 616 |
-
http.HandleFunc("/api/reload", reloadHandler)
|
| 617 |
-
http.HandleFunc("/api/back", historyHandler(-1))
|
| 618 |
-
http.HandleFunc("/api/forward", historyHandler(+1))
|
| 619 |
-
http.HandleFunc("/api/newtab", newtabHandler)
|
| 620 |
-
http.HandleFunc("/api/closetab", closetabHandler)
|
| 621 |
-
http.HandleFunc("/api/stats", statsHandler)
|
| 622 |
-
http.HandleFunc("/chrome.log", chromeLogHandler)
|
| 623 |
http.HandleFunc("/logs/", backendLogHandler)
|
| 624 |
|
| 625 |
// API gateway → the three backend servers on localhost (API-key gated).
|
|
|
|
| 546 |
if got == "" {
|
| 547 |
got = r.URL.Query().Get("key")
|
| 548 |
}
|
| 549 |
+
if got == "" {
|
| 550 |
+
if c, err := r.Cookie("apikey"); err == nil {
|
| 551 |
+
got = c.Value
|
| 552 |
+
}
|
| 553 |
+
}
|
| 554 |
if got != apiKey {
|
| 555 |
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
| 556 |
return false
|
|
|
|
| 558 |
return true
|
| 559 |
}
|
| 560 |
|
| 561 |
+
// gate wraps a handler so it requires the API key (Bearer header, ?key=, or the
|
| 562 |
+
// apikey cookie). Every monitor route that can view or drive the logged-in
|
| 563 |
+
// browser is gated — nothing is publicly reachable.
|
| 564 |
+
func gate(h http.HandlerFunc) http.HandlerFunc {
|
| 565 |
+
return func(w http.ResponseWriter, r *http.Request) {
|
| 566 |
+
if !authOK(w, r) {
|
| 567 |
+
return
|
| 568 |
+
}
|
| 569 |
+
h(w, r)
|
| 570 |
+
}
|
| 571 |
+
}
|
| 572 |
+
|
| 573 |
// gatewayHandler builds an API-key-gated reverse proxy that strips prefix and
|
| 574 |
// forwards to target (e.g. /gpt/api/chat -> http://127.0.0.1:9225/api/chat).
|
| 575 |
func gatewayHandler(prefix, target string) http.HandlerFunc {
|
|
|
|
| 623 |
http.NotFound(w, r)
|
| 624 |
return
|
| 625 |
}
|
| 626 |
+
if !authOK(w, r) {
|
| 627 |
+
return
|
| 628 |
+
}
|
| 629 |
+
// If the key arrived via ?key=, persist it as a cookie so the in-page
|
| 630 |
+
// /api/* fetches (same origin) stay authorized without re-passing it.
|
| 631 |
+
if k := r.URL.Query().Get("key"); k != "" && k == apiKey {
|
| 632 |
+
http.SetCookie(w, &http.Cookie{Name: "apikey", Value: k, Path: "/", HttpOnly: true, SameSite: http.SameSiteStrictMode})
|
| 633 |
+
}
|
| 634 |
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
| 635 |
w.Write(indexHTML)
|
| 636 |
})
|
| 637 |
+
http.HandleFunc("/api/tabs", gate(tabsHandler))
|
| 638 |
+
http.HandleFunc("/api/screen", gate(screenHandler))
|
| 639 |
+
http.HandleFunc("/api/input", gate(inputHandler))
|
| 640 |
+
http.HandleFunc("/api/navigate", gate(navigateHandler))
|
| 641 |
+
http.HandleFunc("/api/reload", gate(reloadHandler))
|
| 642 |
+
http.HandleFunc("/api/back", gate(historyHandler(-1)))
|
| 643 |
+
http.HandleFunc("/api/forward", gate(historyHandler(+1)))
|
| 644 |
+
http.HandleFunc("/api/newtab", gate(newtabHandler))
|
| 645 |
+
http.HandleFunc("/api/closetab", gate(closetabHandler))
|
| 646 |
+
http.HandleFunc("/api/stats", gate(statsHandler))
|
| 647 |
+
http.HandleFunc("/chrome.log", gate(chromeLogHandler))
|
| 648 |
http.HandleFunc("/logs/", backendLogHandler)
|
| 649 |
|
| 650 |
// API gateway → the three backend servers on localhost (API-key gated).
|