npm package: @k.nirmal/gemini-kit
Description: Unofficial Node.js wrapper for Google Gemini — interact via cookies. Supports ask, speech, image upload, image extraction, sharing, and continuous conversations.
Author: Nirmal Gmage
Latest version: 1.0.1

# <img src="https://www.gstatic.com/lamda/images/gemini_sparkle_v002_d4735304ff6292a690345.svg" width="35px" alt="Gemini Icon" /> Gemini-Kit (Node.js)

[![npm version](https://img.shields.io/npm/v/@k.nirmal/gemini-kit.svg)](https://www.npmjs.com/package/@k.nirmal/gemini-kit)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Node.js >=18](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)

**Gemini-Kit** is an unofficial Node.js wrapper for Google Gemini, developed through reverse-engineering.  
It uses your browser cookie values to communicate with Google Gemini — no official API key required.

> ⚠️ For **testing / educational purposes only**. Reverse-engineering Google services may violate their Terms of Service.

---

## ✨ Features

| Feature | Description |
|---|---|
| 💬 **Single & Multi-turn Chat** | Ask questions and maintain conversation context |
| 🖼️ **Image Fetching** | Extract inline image URLs returned by Gemini |
| 🎨 **Generated Image Extraction** | Save AI-generated images from responses |
| 📸 **Image Upload** | Upload images and ask questions about them |
| 🔊 **Text-to-Speech** | Convert any Gemini response to audio bytes |
| 🔗 **Share Conversations** | Generate shareable conversation links |
| 🍪 **Cookie Parsing** | Parse cookies from Chrome net-export log files |

---

## 📦 Installation

```bash
npm install @k.nirmal/gemini-kit
```

---

## 🍪 Get Cookie File (chrome-net-export-log)

<details>
<summary>Click to expand step-by-step guide</summary>

For a detailed video guide, [click here](https://youtu.be/IUCJg2KWcJs).

### 1. Close All Tabs

Ensure all tabs are closed in Google Chrome.

### 2. Access Network Export

- Open a new tab and navigate to `chrome://net-export/`.

### 3. Configure Logging Settings

- Check the box labelled **"Include cookies and credentials"**.
- Set the **Maximum log size** to `1 MB`.
- Click **"Start logging"**.

### 4. Perform Actions

- Open a new tab and go to [gemini.google.com](https://gemini.google.com).
- Log in to your Gemini account.
- Send a sample message and wait for Gemini's response.

### 5. Stop Logging

- Return to the logging tab and click **"Stop logging"**.

### 6. Save the JSON File

- The cookies will be saved in a JSON file (e.g. `chrome-net-export-log.json`).

### 7. Extract Cookies in Node.js

```js
const { getCookiesFromFile } = require('geminikit');
const fs = require('fs');

const raw = fs.readFileSync('chrome-net-export-log.json', 'utf8');
const cookies = getCookiesFromFile(raw);
console.log(cookies);
```

</details>

---

## 🚀 Quick Start

```js
const { createGemini, getCookiesFromFile } = require('geminikit');
const fs = require('fs');

(async () => {
  const cookies = getCookiesFromFile(fs.readFileSync('chrome-net-export-log.json', 'utf8'));
  const gemini = await createGemini(cookies);

  const res = await gemini.ask('Hello! Who are you?');
  console.log(res.text);
})();
```

---

## 📖 API Reference

### `createGemini(cookies)` → `Promise<Gemini>`

Factory helper. Creates and initialises a `Gemini` client in one call.

```js
const gemini = await createGemini(cookies);
```

---

### `new Gemini(cookies)`

Construct a client manually. Call `await gemini.ready()` before using any methods.

```js
const { Gemini } = require('geminikit');
const gemini = new Gemini(cookies);
await gemini.ready();
```

---

### `getCookiesFromFile(text)` → `Object`

Parse cookies from the raw text content of a Chrome net-export-log JSON file.

```js
const { getCookiesFromFile } = require('geminikit');
const cookies = getCookiesFromFile(fs.readFileSync('chrome-net-export-log.json', 'utf8'));
```

---

### `gemini.ask(text, [opts])` → `Promise<Object>`

Send a message and get a structured response.

**Parameters:**

| Name | Type | Description |
|---|---|---|
| `text` | `string` | The message to send |
| `opts.user` | `Object` | Previous response (for conversation continuity) |
| `opts.photo` | `[name, url]` | Photo array for vision questions |

**Returns:**

```js
{
  text: string,               // Gemini's response text
  conversation_id: string,    // Conversation ID (pass back in opts.user)
  response_id: string,
  choice_id: string,
  req_id: number,
  fsid: string,
  bott: string,
  SNlM0e: string,
  image_urls: string[],           // Inline image URLs in the response
  generated_image_urls: string[]  // AI-generated image URLs
}
```

**Example — Single message:**

```js
const res = await gemini.ask('Tell me a joke');
console.log(res.text);
```

**Example — Continuous conversation:**

```js
const res1 = await gemini.ask('Tell me a joke');
console.log(res1.text);

const res2 = await gemini.ask('Another one!', { user: res1 });
console.log(res2.text);
```

**Example — Ask with a photo:**

```js
const fs = require('fs');
const imgBytes = fs.readFileSync('cat.jpg');
const imgUrl = await gemini.uploadImage(imgBytes);

const res = await gemini.ask('What is in this photo?', {
  photo: ['cat.jpg', imgUrl]
});
console.log(res.text);
```

---

### `gemini.share(conversationId, responseId, choiceId, reqId, fsid, [opts])` → `Promise<string>`

Generate a shareable URL for a conversation.

**Parameters:**

| Name | Type | Description |
|---|---|---|
| `conversationId` | `string` | From response object |
| `responseId` | `string` | From response object |
| `choiceId` | `string` | From response object |
| `reqId` | `number` | From response object |
| `fsid` | `string` | From response object |
| `opts.title` | `string` | Custom share title (optional) |

**Example:**

```js
const res = await gemini.ask('Hi!');
const url = await gemini.share(
  res.conversation_id,
  res.response_id,
  res.choice_id,
  res.req_id,
  res.fsid,
  { title: 'My Gemini Chat' }
);
console.log(url); // https://gemini.google.com/share/...
```

---

### `gemini.speech(text, [opts])` → `Promise<Buffer>`

Convert text to speech and return raw audio bytes (WAV format).

**Parameters:**

| Name | Type | Default | Description |
|---|---|---|---|
| `text` | `string` | — | Text to convert |
| `opts.langCode` | `string` | `"en-GB"` | BCP-47 language code |

**Example:**

```js
const fs = require('fs');

const audio = await gemini.speech('Hello, world!');
fs.writeFileSync('output.wav', audio);
```

**With a different language:**

```js
const audio = await gemini.speech('Bonjour!', { langCode: 'fr-FR' });
fs.writeFileSync('output.wav', audio);
```

---

### `gemini.uploadImage(imageBuffer)` → `Promise<string>`

Upload an image buffer to Google's servers and get a URL back.  
Use the returned URL with `gemini.ask()` via the `photo` option.

**Example:**

```js
const fs = require('fs');
const imgBytes = fs.readFileSync('photo.jpg');
const url = await gemini.uploadImage(imgBytes);
console.log('Uploaded to:', url);
```

---

### `gemini.getImgBytes(url)` → `Promise<Buffer>`

Download raw image bytes from a URL using the authenticated session.  
Useful for saving `image_urls` or `generated_image_urls` from `ask()`.

**Example:**

```js
const res = await gemini.ask('Show me some wallpapers');
console.log(res.text);

for (const url of res.image_urls) {
  const bytes = await gemini.getImgBytes(url);
  const name = url.split('/').pop();
  require('fs').writeFileSync(name, bytes);
}
```

---

### `gemini.refreshCookies()` → `Promise<[string, string]>`

Re-fetch `SNlM0e` and `bott` tokens from the Gemini homepage.  
Called automatically on construction. Useful if your session expires.

```js
await gemini.refreshCookies();
```

---

### `gemini.getCookies()` → `Object`

Return the cookies currently in use.

```js
const cookies = gemini.getCookies();
```

---

## 💡 Full Examples

### Save All Response Images

```js
const { createGemini, getCookiesFromFile } = require('geminikit');
const fs = require('fs');

(async () => {
  const cookies = getCookiesFromFile(fs.readFileSync('chrome-net-export-log.json', 'utf8'));
  const gemini = await createGemini(cookies);

  const res = await gemini.ask('Send me some nature wallpapers');
  console.log(res.text);

  for (const url of res.image_urls) {
    const name = url.split('/').pop();
    const bytes = await gemini.getImgBytes(url);
    fs.writeFileSync(name, bytes);
    console.log('Saved:', name);
  }
})();
```

### Save AI-Generated Images

```js
(async () => {
  const res = await gemini.ask('Generate an image of a cat holding a rose.');
  console.log(res.text);

  for (const url of res.generated_image_urls) {
    const name = url.split('/').pop().slice(0, 10) + '.png';
    const bytes = await gemini.getImgBytes(url);
    fs.writeFileSync(name, bytes);
    console.log('Saved generated image:', name);
  }
})();
```

### Text-to-Speech to File

```js
(async () => {
  const audio = await gemini.speech('Welcome to GeminiKit!');
  fs.writeFileSync('welcome.wav', audio);
  console.log('Audio saved to welcome.wav');
})();
```

---

## 🔢 Response Object Fields

| Field | Type | Description |
|---|---|---|
| `text` | `string` | Gemini's text reply |
| `conversation_id` | `string` | Continue the conversation |
| `response_id` | `string` | ID of this response |
| `choice_id` | `string` | Selected response variant ID |
| `req_id` | `number` | Request ID |
| `fsid` | `string` | File session ID |
| `bott` | `string` | Internal Gemini version token |
| `SNlM0e` | `string` | CSRF/session token |
| `image_urls` | `string[]` | Inline image URLs from response |
| `generated_image_urls` | `string[]` | AI-generated image URLs |

---

## 📁 Project Structure

```
geminikit/
├── index.js          # Package entry point
├── src/
│   ├── gemini.js     # Gemini class (all API methods)
│   ├── headers.js    # HTTP headers
│   └── helpers.js    # Cookie parsing & utilities
├── package.json
├── README.md
└── LICENSE
```

---

## 🤝 Contributing

Pull requests are welcome! Please open an issue first to discuss what you would like to change.

---

## 📄 License

[MIT](LICENSE) — © Nirmal Gmage