
Baoyu Danger Gemini Web
Generate images through the Gemini web UI from your coding agent when you cannot rely on a stable official image API alone.
Overview
baoyu-danger-gemini-web is an agent skill for the Build phase that automates Gemini web image generation via Chrome CDP and parses response JSON for downloadable image URLs.
Install
npx skills add https://github.com/jimliu/baoyu-skills --skill baoyu-danger-gemini-webWhat is this skill?
- Uses baoyu-chrome-cdp to drive a real browser session against Gemini web endpoints
- Parses batched wrb.fr response JSON to collect generated image URLs from nested media payloads
- Includes regression tests for legacy vs nested image markers when generated markers are absent
- Node/TypeScript script package with lockfile-pinned baoyu-chrome-cdp dependency
- Falls back through response part trees so agents still retrieve lh3.googleusercontent.com asset URLs
- Regression test covers response part fallback when legacy generated markers are absent
- Lockfile pins baoyu-chrome-cdp at ^0.1.1
Adoption & trust: 21k installs on skills.sh; 20.9k GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
You need generated images in your build workflow but only have access to Gemini through the web client, not a simple stable API hook your agent can call.
Who is it for?
Indie builders prototyping visuals quickly while already using baoyu Chrome CDP skills in a Node-friendly repo.
Skip if: Teams that require vendor-supported APIs, headless-only CI with no browser, or strict compliance environments that forbid unofficial web automation.
When should I use this skill?
You need Gemini web–sourced images inside an agent session and already use or can install baoyu Chrome CDP tooling.
What do I get? / Deliverables
Your agent runs the packaged scripts, drives Gemini web in a browser, and returns concrete image URLs you can drop into UI, docs, or launch assets.
- Collected generated image URLs from Gemini web responses
- Validated client/parser behavior via included unit tests
Recommended Skills
Journey fit
Build is where solo builders wire generative models into product workflows; this skill automates Gemini web image generation as an integration. It is an external-service bridge (browser + Gemini web protocol), not core app UI or backend logic.
How it compares
Browser-driven Gemini web integration—not the same as calling a documented Google AI SDK image endpoint.
Common Questions / FAQ
Who is baoyu-danger-gemini-web for?
Solo and indie developers who ship with coding agents and want Gemini-generated images without building a separate media microservice.
When should I use baoyu-danger-gemini-web?
During Build integrations when you need hero images or UI mocks, and during Launch or Grow when you want quick social or marketing visuals—always after you accept browser-based Gemini web automation.
Is baoyu-danger-gemini-web safe to install?
It uses browser automation and network access to third-party services; review the Security Audits panel on this Prism page and your org policy before adding it to sensitive repositories.
SKILL.md
READMESKILL.md - Baoyu Danger Gemini Web
{ "lockfileVersion": 1, "configVersion": 1, "workspaces": { "": { "name": "baoyu-danger-gemini-web-scripts", "dependencies": { "baoyu-chrome-cdp": "^0.1.1", }, }, }, "packages": { "baoyu-chrome-cdp": ["baoyu-chrome-cdp@0.1.1", "", {}, "sha512-OR3PQ7NzJDykCXl20TnkZuwvNQ0hsVC2czje93P72xQaA3vKOyPN/Q1CwEgKuYzP7Rka4Fdh4HvURj6AoNR7Tg=="], } } import assert from "node:assert/strict"; import test from "node:test"; import { collect_generated_image_urls_from_response_parts } from "./client.ts"; test("response part fallback finds generated images when legacy generated markers are absent", () => { const generatedUrl = "https://lh3.googleusercontent.com/gg-dl/example-generated-image"; const initialCandidate = ["rcid-1", ["image generated successfully"]]; const imageCandidate = [ "rcid-1", ["image generated successfully"], { nestedPayload: [{ media: generatedUrl }] }, ]; const responseJson = [ ["wrb.fr", null, JSON.stringify([null, [], null, null, [initialCandidate]])], ["wrb.fr", null, JSON.stringify([null, [], null, null, [imageCandidate]])], ]; assert.equal(initialCandidate[12], undefined); assert.equal( /http:\/\/googleusercontent\.com\/image_generation_content\/\d+/.test(String(initialCandidate[1]?.[0])), false, ); assert.deepEqual(collect_generated_image_urls_from_response_parts(responseJson, 0, 0), [generatedUrl]); }); import { Endpoint, ErrorCode, Headers, Model } from './constants.js'; import { GemMixin } from './components/gem-mixin.js'; import { APIError, AuthError, GeminiError, ImageGenerationError, ModelInvalid, TemporarilyBlocked, TimeoutError, UsageLimitExceeded, } from './exceptions.js'; import { Candidate, Gem, GeneratedImage, ModelOutput, RPCData, WebImage } from './types/index.js'; import { extract_json_from_response, get_access_token, get_nested_value, logger, parse_file_name, rotate_1psidts, rotate_tasks, fetch_with_timeout, sleep, upload_file, write_cookie_file, resolveGeminiWebCookiePath, } from './utils/index.js'; type InitOptions = { timeout?: number; auto_close?: boolean; close_delay?: number; auto_refresh?: boolean; refresh_interval?: number; verbose?: boolean; }; type RequestKwargs = RequestInit & { timeout_ms?: number }; const GENERATED_IMAGE_URL_PREFIX = 'https://lh3.googleusercontent.com/gg-dl/'; function normalize_headers(h?: HeadersInit): Record<string, string> { if (!h) return {}; if (Array.isArray(h)) return Object.fromEntries(h.map(([k, v]) => [k, v])); if (h instanceof Headers) { const out: Record<string, string> = {}; h.forEach((v, k) => { out[k] = v; }); return out; } return { ...(h as Record<string, string>) }; } function collect_strings(root: unknown, accept: (s: string) => boolean, limit: number = 20): string[] { const out: string[] = []; const seen = new Set<string>(); const stack: unknown[] = [root]; while (stack.length > 0 && out.length < limit) { const v = stack.pop(); if (typeof v === 'string') { if (accept(v) && !seen.has(v)) { seen.add(v); out.push(v); } continue; } if (Array.isArray(v)) { for (let i = 0; i < v.length; i++) stack.push(v[i]); continue; } if (v && typeof v === 'object') { for (const val of Object.values(v as Record<string, unknown>)) stack.push(val); } } return out; } function collect_generated_image_urls(root: unknown, limit: number = 4): string[] { return collect_strings(root, (s) => s.startsWith(GENERATED_IMAGE_URL_PREFIX), limit); } function parse_response_part_body(part: unknown): unknown[] | null { if (!Array.isArray(part)) return null; const part_body = get_nested_value<string | null>(part, [2], null); if (!part_body) return null; try { const part_json = JSON.parse(part_body) as unknown; return Array.isArray(part_json) ? part_json : null; } catch { return null;