gemini-reverse 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +548 -176
- package/client.js +668 -152
- package/components/chatMixin.js +129 -70
- package/components/index.js +2 -1
- package/components/researchMixin.js +202 -0
- package/constants.js +157 -36
- package/index.d.ts +254 -46
- package/index.js +35 -5
- package/package.json +1 -1
- package/types/availablemodel.js +76 -0
- package/types/candidate.js +31 -17
- package/types/chathistory.js +36 -0
- package/types/chatinfo.js +23 -0
- package/types/index.js +26 -2
- package/types/modeloutput.js +4 -1
- package/types/research.js +65 -0
- package/types/researchresult.js +21 -0
- package/types/video.js +195 -0
- package/utils/accessToken.js +15 -7
- package/utils/index.js +22 -3
- package/utils/parsing.js +67 -38
- package/utils/research.js +175 -0
package/README.md
CHANGED
|
@@ -1,25 +1,59 @@
|
|
|
1
|
-

|
|
2
2
|
|
|
3
3
|
# Gemini-Reverse
|
|
4
4
|
|
|
5
|
-
An unofficial Node.js client for [
|
|
6
|
-
|
|
7
|
-
This package ports the core concepts and functionality of the original Python library to Node.js, with full CommonJS and TypeScript support.
|
|
8
|
-
|
|
9
|
-
---
|
|
5
|
+
An unofficial Node.js client for [Google Gemini](https://gemini.google.com), inspired by [Gemini-API](https://github.com/HanaokaYuzu/Gemini-API) — a Python reverse engineering project by [@HanaokaYuzu](https://github.com/HanaokaYuzu).
|
|
10
6
|
|
|
11
7
|
## Features
|
|
12
8
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
9
|
+
- **Persistent Cookies** — Automatically refreshes cookies in the background. Optimized for always-on services.
|
|
10
|
+
- **Image Generation** — Natively supports generating and editing images with natural language.
|
|
11
|
+
- **Video & Audio Generation** — Supports generating videos and audio/music content natively.
|
|
12
|
+
- **Deep Research** — Full deep research workflow with plan creation, status polling, and result retrieval.
|
|
13
|
+
- **System Prompt** — Supports customizing the model's system prompt with [Gemini Gems](https://gemini.google.com/gems/view).
|
|
14
|
+
- **Extension Support** — Supports generating content with Gemini extensions such as YouTube and Gmail.
|
|
15
|
+
- **Classified Outputs** — Categorizes text, thoughts, images, videos, and audio in the response.
|
|
16
|
+
- **Streaming Mode** — Supports stream generation, yielding partial outputs as they are generated.
|
|
17
|
+
- **Dynamic Model Discovery** — Automatically discovers available models from your account at initialization.
|
|
18
|
+
- **TypeScript Support** — Full TypeScript type declarations included out of the box.
|
|
19
|
+
|
|
20
|
+
## Table of Contents
|
|
21
|
+
|
|
22
|
+
- [Features](#features)
|
|
23
|
+
- [Table of Contents](#table-of-contents)
|
|
24
|
+
- [Installation](#installation)
|
|
25
|
+
- [Authentication](#authentication)
|
|
26
|
+
- [Usage](#usage)
|
|
27
|
+
- [Initialization](#initialization)
|
|
28
|
+
- [Generate Content](#generate-content)
|
|
29
|
+
- [Generate Content with Files](#generate-content-with-files)
|
|
30
|
+
- [Conversations Across Multiple Turns](#conversations-across-multiple-turns)
|
|
31
|
+
- [Continue Previous Conversations](#continue-previous-conversations)
|
|
32
|
+
- [Read Conversation History](#read-conversation-history)
|
|
33
|
+
- [List Recent Chats](#list-recent-chats)
|
|
34
|
+
- [Delete a Conversation](#delete-a-conversation)
|
|
35
|
+
- [Temporary Mode](#temporary-mode)
|
|
36
|
+
- [Streaming Mode](#streaming-mode)
|
|
37
|
+
- [Select Language Model](#select-language-model)
|
|
38
|
+
- [List Available Models](#list-available-models)
|
|
39
|
+
- [Apply System Prompt with Gemini Gems](#apply-system-prompt-with-gemini-gems)
|
|
40
|
+
- [Manage Custom Gems](#manage-custom-gems)
|
|
41
|
+
- [Create a Custom Gem](#create-a-custom-gem)
|
|
42
|
+
- [Update an Existing Gem](#update-an-existing-gem)
|
|
43
|
+
- [Delete a Custom Gem](#delete-a-custom-gem)
|
|
44
|
+
- [Retrieve Model's Thought Process](#retrieve-models-thought-process)
|
|
45
|
+
- [Retrieve Images in Response](#retrieve-images-in-response)
|
|
46
|
+
- [Generate and Edit Images](#generate-and-edit-images)
|
|
47
|
+
- [Retrieve Videos and Audio](#retrieve-videos-and-audio)
|
|
48
|
+
- [Generate Content with Gemini Extensions](#generate-content-with-gemini-extensions)
|
|
49
|
+
- [Check and Switch to Other Reply Candidates](#check-and-switch-to-other-reply-candidates)
|
|
50
|
+
- [Deep Research](#deep-research)
|
|
51
|
+
- [Account Status](#account-status)
|
|
52
|
+
- [Error Handling](#error-handling)
|
|
53
|
+
- [Cookie Persistence](#cookie-persistence)
|
|
54
|
+
- [TypeScript](#typescript)
|
|
55
|
+
- [Project Structure](#project-structure)
|
|
56
|
+
- [References](#references)
|
|
23
57
|
|
|
24
58
|
## Installation
|
|
25
59
|
|
|
@@ -27,79 +61,67 @@ This package ports the core concepts and functionality of the original Python li
|
|
|
27
61
|
npm install gemini-reverse
|
|
28
62
|
```
|
|
29
63
|
|
|
30
|
-
---
|
|
31
|
-
|
|
32
64
|
## Authentication
|
|
33
65
|
|
|
34
|
-
|
|
66
|
+
- Go to [gemini.google.com](https://gemini.google.com) and log in with your Google account
|
|
67
|
+
- Press F12 to open DevTools, go to the `Application` tab → `Cookies` → `https://gemini.google.com`
|
|
68
|
+
- Copy the value of `__Secure-1PSID` (and optionally `__Secure-1PSIDTS`)
|
|
35
69
|
|
|
36
|
-
|
|
37
|
-
1. Open [gemini.google.com](https://gemini.google.com) in your browser and log in
|
|
38
|
-
2. Open DevTools → Application → Cookies → `https://gemini.google.com`
|
|
39
|
-
3. Copy the value of `__Secure-1PSID` (and optionally `__Secure-1PSIDTS`)
|
|
70
|
+
> `__Secure-1PSIDTS` is optional — the client will attempt to refresh and cache it automatically after the first successful initialization.
|
|
40
71
|
|
|
41
|
-
|
|
72
|
+
## Usage
|
|
42
73
|
|
|
43
|
-
|
|
74
|
+
### Initialization
|
|
44
75
|
|
|
45
|
-
|
|
76
|
+
Import the package and initialize a client with your cookies. After a successful initialization, the client will automatically refresh `__Secure-1PSIDTS` in the background as long as the process is alive.
|
|
46
77
|
|
|
47
78
|
```js
|
|
48
|
-
const { GeminiClient } = require('gemini-
|
|
49
|
-
|
|
50
|
-
const client = new GeminiClient({
|
|
51
|
-
secure_1psid: 'YOUR_SECURE_1PSID',
|
|
52
|
-
});
|
|
79
|
+
const { GeminiClient } = require('gemini-reverse');
|
|
53
80
|
|
|
54
|
-
await client.init();
|
|
55
|
-
|
|
56
|
-
const chat = client.startChat();
|
|
57
|
-
const response = await chat.sendMessage({ prompt: 'Hello, Gemini!' });
|
|
58
|
-
|
|
59
|
-
console.log(response.text);
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## Usage
|
|
65
|
-
|
|
66
|
-
### Initialize Client
|
|
67
|
-
|
|
68
|
-
```js
|
|
69
81
|
const client = new GeminiClient({
|
|
70
82
|
secure_1psid: 'YOUR_SECURE_1PSID',
|
|
71
83
|
secure_1psidts: 'YOUR_SECURE_1PSIDTS', // optional
|
|
72
|
-
proxy: 'http://host:port'
|
|
84
|
+
proxy: null, // optional, e.g. 'http://host:port'
|
|
73
85
|
});
|
|
74
86
|
|
|
75
87
|
await client.init({
|
|
76
88
|
timeout: 300000, // request timeout in ms, default 300000
|
|
77
|
-
autoClose: false, // auto
|
|
89
|
+
autoClose: false, // auto-close client after inactivity
|
|
78
90
|
closeDelay: 300000, // inactivity delay before closing in ms
|
|
79
|
-
autoRefresh: true, // auto
|
|
91
|
+
autoRefresh: true, // auto-refresh cookies in the background
|
|
80
92
|
refreshInterval: 540000 // cookie refresh interval in ms
|
|
81
93
|
});
|
|
82
94
|
```
|
|
83
95
|
|
|
84
|
-
|
|
96
|
+
> `autoClose` and `closeDelay` are optional arguments for automatically closing the client after a period of inactivity. In an always-on service like a chatbot, it is recommended to set `autoClose` to `true` with a reasonable `closeDelay` value for better resource management.
|
|
97
|
+
|
|
98
|
+
### Generate Content
|
|
99
|
+
|
|
100
|
+
Ask a single-turn question by calling `generateContent`, which returns a `ModelOutput` object containing the generated text, images, thoughts, and conversation metadata.
|
|
85
101
|
|
|
86
102
|
```js
|
|
87
|
-
const response = await client.generateContent({ prompt: '
|
|
103
|
+
const response = await client.generateContent({ prompt: 'Hello World!' });
|
|
88
104
|
console.log(response.text);
|
|
89
105
|
```
|
|
90
106
|
|
|
91
|
-
###
|
|
107
|
+
### Generate Content with Files
|
|
108
|
+
|
|
109
|
+
Gemini supports file input, including images and documents. Pass an array of file paths or `Buffer` objects alongside your text prompt.
|
|
92
110
|
|
|
93
111
|
```js
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
112
|
+
const response = await client.generateContent({
|
|
113
|
+
prompt: 'Describe the contents of these files.',
|
|
114
|
+
files: ['./document.pdf', './photo.png'],
|
|
115
|
+
});
|
|
116
|
+
console.log(response.text);
|
|
97
117
|
```
|
|
98
118
|
|
|
99
|
-
###
|
|
119
|
+
### Conversations Across Multiple Turns
|
|
120
|
+
|
|
121
|
+
Use `startChat` to create a `ChatSession` object and send messages through it. The conversation history is handled automatically and updated after each turn.
|
|
100
122
|
|
|
101
123
|
```js
|
|
102
|
-
const chat = client.startChat(
|
|
124
|
+
const chat = client.startChat();
|
|
103
125
|
|
|
104
126
|
const res1 = await chat.sendMessage({ prompt: 'My name is Alice.' });
|
|
105
127
|
console.log(res1.text);
|
|
@@ -108,191 +130,457 @@ const res2 = await chat.sendMessage({ prompt: 'What is my name?' });
|
|
|
108
130
|
console.log(res2.text); // remembers context
|
|
109
131
|
```
|
|
110
132
|
|
|
111
|
-
###
|
|
133
|
+
### Continue Previous Conversations
|
|
134
|
+
|
|
135
|
+
Pass a previous `ChatSession`'s metadata to `startChat` to resume a conversation. You can persist the metadata to a file or database to restore it across process restarts.
|
|
112
136
|
|
|
113
137
|
```js
|
|
114
|
-
const chat = client.startChat(
|
|
138
|
+
const chat = client.startChat();
|
|
139
|
+
await chat.sendMessage({ prompt: 'Fine weather today.' });
|
|
115
140
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
141
|
+
// Save the session metadata
|
|
142
|
+
const savedMetadata = chat.metadata;
|
|
143
|
+
const savedCid = chat.cid;
|
|
144
|
+
|
|
145
|
+
// Resume in a new session
|
|
146
|
+
const previousChat = client.startChat({ metadata: savedMetadata });
|
|
147
|
+
const response = await previousChat.sendMessage({ prompt: 'What was my previous message?' });
|
|
148
|
+
console.log(response.text);
|
|
119
149
|
```
|
|
120
150
|
|
|
121
|
-
###
|
|
151
|
+
### Read Conversation History
|
|
152
|
+
|
|
153
|
+
Fetch the full conversation history of a chat by calling `readChat` with the chat ID. It returns a `ChatHistory` object containing a list of `ChatTurn` objects ordered from newest to oldest.
|
|
122
154
|
|
|
123
155
|
```js
|
|
124
156
|
const chat = client.startChat();
|
|
125
|
-
|
|
157
|
+
await chat.sendMessage({ prompt: 'What is the capital of France?' });
|
|
158
|
+
|
|
159
|
+
const history = await client.readChat(chat.cid);
|
|
160
|
+
if (history) {
|
|
161
|
+
for (const turn of history.turns) {
|
|
162
|
+
console.log(`[${turn.role.toUpperCase()}] ${turn.text}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
You can also read the history directly from the session:
|
|
168
|
+
|
|
169
|
+
```js
|
|
170
|
+
const history = await chat.readHistory(10); // fetch last 10 turns
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### List Recent Chats
|
|
174
|
+
|
|
175
|
+
Use `listChats` to get a list of recent chat sessions cached at initialization.
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
const chats = client.listChats();
|
|
179
|
+
if (chats) {
|
|
180
|
+
for (const info of chats) {
|
|
181
|
+
console.log(`${info.cid}: ${info.title} (pinned: ${info.is_pinned})`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
126
184
|
```
|
|
127
185
|
|
|
128
|
-
###
|
|
186
|
+
### Delete a Conversation
|
|
187
|
+
|
|
188
|
+
Delete a specific chat from Gemini history on the server by calling `deleteChat` with the chat ID.
|
|
129
189
|
|
|
130
190
|
```js
|
|
131
191
|
const chat = client.startChat();
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
192
|
+
await chat.sendMessage({ prompt: 'This is a temporary conversation.' });
|
|
193
|
+
|
|
194
|
+
await client.deleteChat(chat.cid);
|
|
195
|
+
console.log(`Chat deleted: ${chat.cid}`);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Temporary Mode
|
|
199
|
+
|
|
200
|
+
Pass `temporary: true` to `generateContent` or `sendMessage` to prevent the conversation from being saved to Gemini history.
|
|
201
|
+
|
|
202
|
+
```js
|
|
203
|
+
const response = await client.generateContent({
|
|
204
|
+
prompt: 'Hello World!',
|
|
205
|
+
temporary: true,
|
|
135
206
|
});
|
|
136
207
|
console.log(response.text);
|
|
208
|
+
|
|
209
|
+
// Also works in chat sessions
|
|
210
|
+
const chat = client.startChat();
|
|
211
|
+
await chat.sendMessage({ prompt: 'Fine weather today.', temporary: false });
|
|
212
|
+
const res2 = await chat.sendMessage({ prompt: "What's my last message?", temporary: true });
|
|
213
|
+
console.log(res2.text);
|
|
137
214
|
```
|
|
138
215
|
|
|
139
|
-
###
|
|
216
|
+
### Streaming Mode
|
|
217
|
+
|
|
218
|
+
For longer responses, use streaming mode to receive partial outputs as they are generated. The `text_delta` attribute contains only the **new characters** received since the last yield, making it easy to display incremental updates.
|
|
140
219
|
|
|
141
220
|
```js
|
|
142
|
-
const
|
|
143
|
-
|
|
221
|
+
for await (const chunk of client.generateContentStream({
|
|
222
|
+
prompt: "What's the difference between 'await' and 'async for'?",
|
|
223
|
+
})) {
|
|
224
|
+
process.stdout.write(chunk.text_delta);
|
|
225
|
+
}
|
|
226
|
+
console.log();
|
|
227
|
+
```
|
|
144
228
|
|
|
145
|
-
|
|
146
|
-
response.candidates.forEach((c, i) => console.log(`[${i}] ${c.text}`));
|
|
229
|
+
Streaming also works inside a chat session:
|
|
147
230
|
|
|
148
|
-
|
|
149
|
-
chat.
|
|
231
|
+
```js
|
|
232
|
+
const chat = client.startChat();
|
|
233
|
+
for await (const chunk of chat.sendMessageStream({ prompt: 'Tell me a long story.' })) {
|
|
234
|
+
process.stdout.write(chunk.text_delta);
|
|
235
|
+
}
|
|
150
236
|
```
|
|
151
237
|
|
|
152
|
-
###
|
|
238
|
+
### Select Language Model
|
|
239
|
+
|
|
240
|
+
Specify which language model to use by passing a `model` argument. Available models are discovered dynamically at init time based on your account tier.
|
|
153
241
|
|
|
154
242
|
```js
|
|
155
|
-
|
|
156
|
-
|
|
243
|
+
const { Model } = require('gemini-reverse');
|
|
244
|
+
|
|
245
|
+
// Using a built-in constant
|
|
246
|
+
const response1 = await client.generateContent({
|
|
247
|
+
prompt: 'What is your model version?',
|
|
248
|
+
model: Model.BASIC_FLASH,
|
|
249
|
+
});
|
|
157
250
|
|
|
158
|
-
//
|
|
159
|
-
const
|
|
160
|
-
const chat = client.startChat({ model: Model.G_3_0_FLASH });
|
|
251
|
+
// Using a model name string
|
|
252
|
+
const chat = client.startChat({ model: 'gemini-3-pro' });
|
|
161
253
|
|
|
162
|
-
//
|
|
163
|
-
const
|
|
254
|
+
// Using a custom model header dict
|
|
255
|
+
const chat2 = client.startChat({
|
|
164
256
|
model: {
|
|
165
|
-
model_name: '
|
|
166
|
-
model_header: {
|
|
257
|
+
model_name: 'custom',
|
|
258
|
+
model_header: {
|
|
259
|
+
'x-goog-ext-525001261-jspb': '[1,null,null,null,"MODEL_ID",null,null,0,[4],null,null,2]',
|
|
260
|
+
},
|
|
167
261
|
},
|
|
168
262
|
});
|
|
169
263
|
```
|
|
170
264
|
|
|
171
|
-
**
|
|
265
|
+
**Built-in model constants:**
|
|
172
266
|
|
|
173
|
-
|
|
|
174
|
-
|
|
175
|
-
| `
|
|
176
|
-
| `gemini-3
|
|
177
|
-
| `gemini-3
|
|
178
|
-
| `
|
|
267
|
+
| Constant | `model_name` | Notes |
|
|
268
|
+
|---|---|---|
|
|
269
|
+
| `Model.UNSPECIFIED` | `unspecified` | Default, lets Gemini choose |
|
|
270
|
+
| `Model.BASIC_PRO` | `gemini-3-pro` | Free tier |
|
|
271
|
+
| `Model.BASIC_FLASH` | `gemini-3-flash` | Free tier, fastest |
|
|
272
|
+
| `Model.BASIC_THINKING` | `gemini-3-flash-thinking` | Free tier, thinking model |
|
|
273
|
+
| `Model.PLUS_PRO` | `gemini-3-pro-plus` | Plus tier |
|
|
274
|
+
| `Model.PLUS_FLASH` | `gemini-3-flash-plus` | Plus tier |
|
|
275
|
+
| `Model.PLUS_THINKING` | `gemini-3-flash-thinking-plus` | Plus tier |
|
|
276
|
+
| `Model.ADVANCED_PRO` | `gemini-3-pro-advanced` | Advanced tier |
|
|
277
|
+
| `Model.ADVANCED_FLASH` | `gemini-3-flash-advanced` | Advanced tier |
|
|
278
|
+
| `Model.ADVANCED_THINKING` | `gemini-3-flash-thinking-advanced` | Advanced tier |
|
|
179
279
|
|
|
180
|
-
###
|
|
280
|
+
### List Available Models
|
|
181
281
|
|
|
182
|
-
|
|
282
|
+
The client dynamically discovers which models your account can access during initialization. Use `listModels` to inspect them.
|
|
183
283
|
|
|
184
284
|
```js
|
|
185
|
-
|
|
285
|
+
await client.init();
|
|
186
286
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
287
|
+
const models = client.listModels();
|
|
288
|
+
if (models) {
|
|
289
|
+
for (const model of models) {
|
|
290
|
+
console.log(`${model.model_id} → ${model.model_name || model.display_name}`);
|
|
291
|
+
console.log(` capacity: ${model.capacity}, advanced_only: ${model.advanced_only}`);
|
|
292
|
+
}
|
|
190
293
|
}
|
|
191
294
|
```
|
|
192
295
|
|
|
193
|
-
###
|
|
296
|
+
### Apply System Prompt with Gemini Gems
|
|
297
|
+
|
|
298
|
+
System prompts can be applied to conversations via [Gemini Gems](https://gemini.google.com/gems/view). Pass the `gem` argument to `generateContent` or `startChat` — it can be a `Gem` object or a gem ID string.
|
|
194
299
|
|
|
195
300
|
```js
|
|
196
|
-
|
|
301
|
+
// Fetch all gems for the account
|
|
302
|
+
await client.fetchGems();
|
|
303
|
+
const gems = client.gems;
|
|
304
|
+
|
|
305
|
+
// Get a specific gem
|
|
306
|
+
const codingPartner = gems.get({ name: 'Coding partner' });
|
|
307
|
+
|
|
308
|
+
const response = await client.generateContent({
|
|
309
|
+
prompt: "What's your system prompt?",
|
|
310
|
+
gem: codingPartner,
|
|
311
|
+
});
|
|
312
|
+
console.log(response.text);
|
|
313
|
+
|
|
314
|
+
// Use a gem in a multi-turn chat
|
|
315
|
+
const chat = client.startChat({ gem: codingPartner });
|
|
316
|
+
const res2 = await chat.sendMessage({ prompt: 'Help me write a binary search.' });
|
|
317
|
+
console.log(res2.text);
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
> There are some system predefined gems that are hidden by default. Use `fetchGems({ includeHidden: true })` to include them.
|
|
321
|
+
|
|
322
|
+
### Manage Custom Gems
|
|
323
|
+
|
|
324
|
+
You can create, update, and delete custom gems programmatically. Predefined system gems cannot be modified.
|
|
325
|
+
|
|
326
|
+
#### Create a Custom Gem
|
|
327
|
+
|
|
328
|
+
```js
|
|
329
|
+
const newGem = await client.createGem({
|
|
330
|
+
name: 'Python Tutor',
|
|
331
|
+
prompt: 'You are a helpful Python programming tutor. Always provide runnable code examples.',
|
|
332
|
+
description: 'A specialized gem for Python programming',
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
console.log(`Created: ${newGem.id}`);
|
|
336
|
+
|
|
337
|
+
// Use the new gem immediately
|
|
338
|
+
const response = await client.generateContent({
|
|
339
|
+
prompt: 'Explain list comprehensions.',
|
|
340
|
+
gem: newGem,
|
|
341
|
+
});
|
|
342
|
+
console.log(response.text);
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### Update an Existing Gem
|
|
346
|
+
|
|
347
|
+
> When updating a gem, all parameters (`name`, `prompt`, `description`) must be provided even if only one is changing.
|
|
197
348
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
349
|
+
```js
|
|
350
|
+
await client.fetchGems();
|
|
351
|
+
const pythonTutor = client.gems.get({ name: 'Python Tutor' });
|
|
352
|
+
|
|
353
|
+
const updatedGem = await client.updateGem({
|
|
354
|
+
gem: pythonTutor,
|
|
355
|
+
name: 'Advanced Python Tutor',
|
|
356
|
+
prompt: 'You are an expert Python programming tutor. Focus on performance and best practices.',
|
|
357
|
+
description: 'An advanced Python programming assistant',
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
console.log(`Updated: ${updatedGem.id}`);
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
#### Delete a Custom Gem
|
|
364
|
+
|
|
365
|
+
```js
|
|
366
|
+
await client.fetchGems();
|
|
367
|
+
const gemToDelete = client.gems.get({ name: 'Advanced Python Tutor' });
|
|
368
|
+
|
|
369
|
+
await client.deleteGem(gemToDelete); // can also pass a gem ID string
|
|
370
|
+
console.log(`Deleted: ${gemToDelete.name}`);
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Retrieve Model's Thought Process
|
|
374
|
+
|
|
375
|
+
When using thinking-capable models, the model's internal reasoning is exposed via `response.thoughts`.
|
|
376
|
+
|
|
377
|
+
```js
|
|
378
|
+
const response = await client.generateContent({
|
|
379
|
+
prompt: 'What is 17 × 23?',
|
|
380
|
+
model: Model.BASIC_THINKING,
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
if (response.thoughts) {
|
|
384
|
+
console.log('Thoughts:', response.thoughts);
|
|
201
385
|
}
|
|
386
|
+
console.log('Answer:', response.text);
|
|
202
387
|
```
|
|
203
388
|
|
|
204
|
-
###
|
|
389
|
+
### Retrieve Images in Response
|
|
390
|
+
|
|
391
|
+
Images in the response are stored as a list of `Image` objects accessible via `response.images`. Each image has a `url`, `title`, and `alt` description.
|
|
205
392
|
|
|
206
393
|
```js
|
|
207
|
-
await client.
|
|
394
|
+
const response = await client.generateContent({ prompt: 'Send me some pictures of cats.' });
|
|
395
|
+
|
|
396
|
+
for (const image of response.images) {
|
|
397
|
+
console.log(`${image.title}: ${image.url}`);
|
|
398
|
+
console.log(`Alt: ${image.alt}`);
|
|
399
|
+
}
|
|
208
400
|
```
|
|
209
401
|
|
|
210
|
-
###
|
|
402
|
+
### Generate and Edit Images
|
|
403
|
+
|
|
404
|
+
Ask Gemini to generate or edit images using natural language. Generated images are returned as `GeneratedImage` objects and can be saved to disk.
|
|
405
|
+
|
|
406
|
+
> Google has limitations on image generation availability that vary by region and account type. Users under 18 cannot use this feature.
|
|
211
407
|
|
|
212
408
|
```js
|
|
213
|
-
|
|
214
|
-
|
|
409
|
+
const response = await client.generateContent({
|
|
410
|
+
prompt: 'Generate a photo-realistic image of a cat in a space suit.',
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
for (let i = 0; i < response.images.length; i++) {
|
|
414
|
+
const image = response.images[i];
|
|
415
|
+
const savedPath = await image.save({
|
|
416
|
+
path: './temp',
|
|
417
|
+
filename: `cat_space_${i}.png`,
|
|
418
|
+
verbose: true,
|
|
419
|
+
});
|
|
420
|
+
console.log(`Saved to: ${savedPath}`);
|
|
421
|
+
}
|
|
422
|
+
```
|
|
215
423
|
|
|
216
|
-
|
|
217
|
-
const gem = gems.get({ name: 'Coding partner' });
|
|
424
|
+
> When asking Gemini to "send" images, it returns web images (`WebImage`). When asking to "generate" images, it returns AI-generated images (`GeneratedImage`). Both are automatically categorized in `response.images`.
|
|
218
425
|
|
|
219
|
-
|
|
220
|
-
const myGems = gems.filter({ predefined: false });
|
|
426
|
+
### Retrieve Videos and Audio
|
|
221
427
|
|
|
222
|
-
|
|
223
|
-
const chat = client.startChat({ gem: gem });
|
|
428
|
+
Gemini can generate short videos and audio/music. These are returned as `GeneratedVideo` and `GeneratedMedia` objects in `response.videos` and `response.media` respectively.
|
|
224
429
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
430
|
+
> You may need an active Gemini subscription to access video and audio generation.
|
|
431
|
+
|
|
432
|
+
```js
|
|
433
|
+
// Generate a video
|
|
434
|
+
const videoResponse = await client.generateContent({
|
|
435
|
+
prompt: 'Generate a short video of waves on a beach.',
|
|
230
436
|
});
|
|
231
437
|
|
|
232
|
-
|
|
233
|
-
await
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
438
|
+
for (const video of videoResponse.videos) {
|
|
439
|
+
const result = await video.save({ savePath: './temp', verbose: true });
|
|
440
|
+
console.log('Video:', result.video);
|
|
441
|
+
console.log('Thumbnail:', result.video_thumbnail);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Generate audio/music
|
|
445
|
+
const mediaResponse = await client.generateContent({
|
|
446
|
+
prompt: 'Compose a short calming piano melody.',
|
|
237
447
|
});
|
|
238
448
|
|
|
239
|
-
|
|
240
|
-
|
|
449
|
+
for (const media of mediaResponse.media) {
|
|
450
|
+
// downloadType: 'audio' | 'video' | 'both' (default)
|
|
451
|
+
const result = await media.save({ savePath: './temp', downloadType: 'both', verbose: true });
|
|
452
|
+
console.log('MP3:', result.audio);
|
|
453
|
+
console.log('MP4:', result.video);
|
|
454
|
+
}
|
|
241
455
|
```
|
|
242
456
|
|
|
243
|
-
|
|
457
|
+
> `GeneratedMedia.save()` accepts a `downloadType` parameter: `"audio"`, `"video"`, or `"both"` (default). The save method polls automatically if content is still generating (HTTP 206).
|
|
458
|
+
|
|
459
|
+
### Generate Content with Gemini Extensions
|
|
460
|
+
|
|
461
|
+
To use Gemini extensions (Gmail, YouTube, etc.), you must first activate them on the [Gemini website](https://gemini.google.com/extensions). Reference them in prompts with the `@` prefix or in natural language.
|
|
462
|
+
|
|
463
|
+
> You must have Gemini Apps Activity enabled in your account to use extensions.
|
|
244
464
|
|
|
245
465
|
```js
|
|
246
|
-
await client.
|
|
466
|
+
const gmailResponse = await client.generateContent({
|
|
467
|
+
prompt: "@Gmail What's the latest message in my mailbox?",
|
|
468
|
+
});
|
|
469
|
+
console.log(gmailResponse.text);
|
|
470
|
+
|
|
471
|
+
const youtubeResponse = await client.generateContent({
|
|
472
|
+
prompt: "@YouTube What's the latest video from Fireship?",
|
|
473
|
+
});
|
|
474
|
+
console.log(youtubeResponse.text);
|
|
247
475
|
```
|
|
248
476
|
|
|
249
|
-
|
|
477
|
+
### Check and Switch to Other Reply Candidates
|
|
250
478
|
|
|
251
|
-
|
|
479
|
+
A Gemini response sometimes contains multiple reply candidates with different generated content. You can inspect all candidates and choose one to continue the conversation flow.
|
|
252
480
|
|
|
253
|
-
|
|
481
|
+
```js
|
|
482
|
+
const chat = client.startChat();
|
|
483
|
+
const response = await chat.sendMessage({ prompt: 'Recommend a science fiction book.' });
|
|
254
484
|
|
|
255
|
-
|
|
256
|
-
|
|
485
|
+
// List all candidates
|
|
486
|
+
response.candidates.forEach((candidate, i) => {
|
|
487
|
+
console.log(`[${i}] ${candidate.text.slice(0, 80)}...`);
|
|
488
|
+
});
|
|
257
489
|
|
|
258
|
-
|
|
259
|
-
|
|
490
|
+
if (response.candidates.length > 1) {
|
|
491
|
+
// Choose the second candidate to continue from
|
|
492
|
+
chat.chooseCandidate(1);
|
|
260
493
|
|
|
261
|
-
const
|
|
262
|
-
|
|
494
|
+
const followup = await chat.sendMessage({ prompt: 'Tell me more about it.' });
|
|
495
|
+
console.log(followup.text);
|
|
496
|
+
} else {
|
|
497
|
+
console.log('Only one candidate available.');
|
|
498
|
+
}
|
|
499
|
+
```
|
|
263
500
|
|
|
264
|
-
|
|
501
|
+
### Deep Research
|
|
502
|
+
|
|
503
|
+
Gemini's deep research feature is an autonomous agent that browses the web, analyzes sources, and produces a comprehensive report.
|
|
504
|
+
|
|
505
|
+
> You may need an active Gemini subscription to access deep research.
|
|
506
|
+
|
|
507
|
+
**Quick one-call method:**
|
|
508
|
+
|
|
509
|
+
```js
|
|
510
|
+
const result = await client.deepResearch(
|
|
511
|
+
'Compare the top 3 cloud providers and their AI offerings',
|
|
512
|
+
10000, // poll interval in ms
|
|
513
|
+
600000, // timeout in ms
|
|
514
|
+
(status) => console.log(`Status: ${status.state} — ${status.notes.slice(0, 1).join(', ')}`),
|
|
515
|
+
);
|
|
516
|
+
|
|
517
|
+
console.log(`Done: ${result.done}`);
|
|
518
|
+
console.log(result.text);
|
|
265
519
|
```
|
|
266
520
|
|
|
267
|
-
|
|
521
|
+
**Step-by-step workflow** for more control:
|
|
268
522
|
|
|
269
|
-
|
|
523
|
+
```js
|
|
524
|
+
// Step 1: Create a research plan
|
|
525
|
+
const plan = await client.createDeepResearchPlan(
|
|
526
|
+
'What are the latest advancements in quantum computing?'
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
console.log(`Title: ${plan.title}`);
|
|
530
|
+
console.log(`ETA: ${plan.eta_text}`);
|
|
531
|
+
for (const step of plan.steps) {
|
|
532
|
+
console.log(` - ${step}`);
|
|
533
|
+
}
|
|
270
534
|
|
|
535
|
+
// Step 2: Start the research
|
|
536
|
+
const startOutput = await client.startDeepResearch(plan);
|
|
537
|
+
console.log('Research started:', startOutput.text.slice(0, 100));
|
|
538
|
+
|
|
539
|
+
// Step 3: Poll for completion
|
|
540
|
+
const result = await client.waitForDeepResearch(
|
|
541
|
+
plan,
|
|
542
|
+
10000, // poll interval in ms
|
|
543
|
+
600000, // timeout in ms
|
|
544
|
+
(status) => console.log(`[${status.state}] ${status.notes[0] || ''}`),
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
console.log(result.text);
|
|
271
548
|
```
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
├── chatMixin.js
|
|
292
|
-
└── gemMixin.js
|
|
549
|
+
|
|
550
|
+
### Account Status
|
|
551
|
+
|
|
552
|
+
The client detects your account's capability tier at initialization and exposes it via `client.accountStatus`.
|
|
553
|
+
|
|
554
|
+
```js
|
|
555
|
+
const { AccountStatus } = require('gemini-reverse');
|
|
556
|
+
|
|
557
|
+
await client.init();
|
|
558
|
+
|
|
559
|
+
if (client.accountStatus === AccountStatus.AVAILABLE) {
|
|
560
|
+
console.log('Account is fully authorized.');
|
|
561
|
+
} else if (client.accountStatus === AccountStatus.UNAUTHENTICATED) {
|
|
562
|
+
console.error('Cookies are expired or invalid.');
|
|
563
|
+
} else if (client.accountStatus === AccountStatus.LOCATION_REJECTED) {
|
|
564
|
+
console.error('Gemini is not available in your region.');
|
|
565
|
+
} else {
|
|
566
|
+
console.warn(`Account status: ${client.accountStatus.name} — ${client.accountStatus.description}`);
|
|
567
|
+
}
|
|
293
568
|
```
|
|
294
569
|
|
|
295
|
-
|
|
570
|
+
**All account status values:**
|
|
571
|
+
|
|
572
|
+
| Constant | Code | Description |
|
|
573
|
+
|---|---|---|
|
|
574
|
+
| `AccountStatus.AVAILABLE` | 1000 | Account is authorized and has normal access |
|
|
575
|
+
| `AccountStatus.ACCESS_TEMPORARILY_UNAVAILABLE` | 1014 | Access restricted, possibly regional/temporary |
|
|
576
|
+
| `AccountStatus.UNAUTHENTICATED` | 1016 | Cookies have expired or are invalid |
|
|
577
|
+
| `AccountStatus.ACCOUNT_REJECTED` | 1021 | Account access rejected |
|
|
578
|
+
| `AccountStatus.ACCOUNT_UNTRUSTED` | 1033 | Did not pass safety/trust checks |
|
|
579
|
+
| `AccountStatus.TOS_PENDING` | 1040 | Must accept latest Terms of Service |
|
|
580
|
+
| `AccountStatus.TOS_OUT_OF_DATE` | 1042 | Terms of Service are out of date |
|
|
581
|
+
| `AccountStatus.ACCOUNT_REJECTED_BY_GUARDIAN` | 1054 | Blocked by parent or guardian |
|
|
582
|
+
| `AccountStatus.GUARDIAN_APPROVAL_REQUIRED` | 1057 | Requires parental approval |
|
|
583
|
+
| `AccountStatus.LOCATION_REJECTED` | 1060 | Not available in your country/region |
|
|
296
584
|
|
|
297
585
|
## Error Handling
|
|
298
586
|
|
|
@@ -305,41 +593,125 @@ const {
|
|
|
305
593
|
UsageLimitExceeded,
|
|
306
594
|
ModelInvalid,
|
|
307
595
|
TemporarilyBlocked,
|
|
308
|
-
} = require('gemini-
|
|
596
|
+
} = require('gemini-reverse');
|
|
309
597
|
|
|
310
598
|
try {
|
|
311
599
|
const response = await chat.sendMessage({ prompt: 'Hello!' });
|
|
600
|
+
console.log(response.text);
|
|
312
601
|
} catch (e) {
|
|
313
602
|
if (e instanceof AuthError) {
|
|
314
|
-
console.error('Cookie expired or invalid.');
|
|
603
|
+
console.error('Cookie expired or invalid. Please refresh your cookies.');
|
|
315
604
|
} else if (e instanceof UsageLimitExceeded) {
|
|
316
|
-
console.error('Usage limit reached. Try again later or switch
|
|
605
|
+
console.error('Usage limit reached. Try again later or switch to a different model.');
|
|
317
606
|
} else if (e instanceof TemporarilyBlocked) {
|
|
318
|
-
console.error('IP temporarily blocked. Try using a proxy.');
|
|
607
|
+
console.error('IP temporarily blocked by Google. Try using a proxy or wait a while.');
|
|
319
608
|
} else if (e instanceof TimeoutError) {
|
|
320
|
-
console.error('Request timed out.');
|
|
609
|
+
console.error('Request timed out. Try increasing the timeout value in init().');
|
|
321
610
|
} else if (e instanceof ModelInvalid) {
|
|
322
|
-
console.error('Invalid or unavailable model.');
|
|
611
|
+
console.error('Invalid or unavailable model. Try a different model.');
|
|
323
612
|
} else if (e instanceof APIError) {
|
|
324
613
|
console.error('API error:', e.message);
|
|
614
|
+
} else {
|
|
615
|
+
throw e;
|
|
325
616
|
}
|
|
326
617
|
}
|
|
327
618
|
```
|
|
328
619
|
|
|
329
|
-
|
|
620
|
+
## Cookie Persistence
|
|
330
621
|
|
|
331
|
-
|
|
622
|
+
If your application runs in a containerized environment (e.g. Docker), you can persist the auto-refreshed cookie cache to a volume by setting the `GEMINI_COOKIE_PATH` environment variable to a writable path.
|
|
332
623
|
|
|
333
|
-
|
|
624
|
+
```yaml
|
|
625
|
+
# docker-compose.yml
|
|
626
|
+
services:
|
|
627
|
+
app:
|
|
628
|
+
environment:
|
|
629
|
+
GEMINI_COOKIE_PATH: /tmp/gemini_cache
|
|
630
|
+
volumes:
|
|
631
|
+
- ./gemini_cookies:/tmp/gemini_cache
|
|
632
|
+
```
|
|
334
633
|
|
|
335
|
-
|
|
634
|
+
By default, the cache is stored in `utils/temp/` relative to the package directory.
|
|
635
|
+
|
|
636
|
+
## TypeScript
|
|
637
|
+
|
|
638
|
+
This package includes full TypeScript declarations.
|
|
639
|
+
|
|
640
|
+
```ts
|
|
641
|
+
import {
|
|
642
|
+
GeminiClient,
|
|
643
|
+
ChatSession,
|
|
644
|
+
ModelOutput,
|
|
645
|
+
ChatHistory,
|
|
646
|
+
ChatTurn,
|
|
647
|
+
ChatInfo,
|
|
648
|
+
Gem,
|
|
649
|
+
AvailableModel,
|
|
650
|
+
Model,
|
|
651
|
+
AccountStatus,
|
|
652
|
+
DeepResearchPlan,
|
|
653
|
+
DeepResearchResult,
|
|
654
|
+
} from 'gemini-reverse';
|
|
655
|
+
|
|
656
|
+
const client = new GeminiClient({ secure_1psid: '...' });
|
|
657
|
+
await client.init();
|
|
658
|
+
|
|
659
|
+
const chat: ChatSession = client.startChat({ model: 'gemini-3-flash' });
|
|
660
|
+
const response: ModelOutput = await chat.sendMessage({ prompt: 'Hello!' });
|
|
661
|
+
|
|
662
|
+
console.log(response.text);
|
|
663
|
+
|
|
664
|
+
const history: ChatHistory | null = await chat.readHistory();
|
|
665
|
+
if (history) {
|
|
666
|
+
history.turns.forEach((turn: ChatTurn) => console.log(turn.role, turn.text));
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
const models: AvailableModel[] | null = client.listModels();
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
## Project Structure
|
|
673
|
+
|
|
674
|
+
```
|
|
675
|
+
gemini-reverse/
|
|
676
|
+
├── index.js # entry point & exports
|
|
677
|
+
├── index.d.ts # TypeScript declarations
|
|
678
|
+
├── client.js # GeminiClient + ChatSession
|
|
679
|
+
├── constants.js # Endpoint, GRPC, Headers, Model, AccountStatus, ErrorCode
|
|
680
|
+
├── exceptions.js # custom error classes
|
|
681
|
+
├── types/
|
|
682
|
+
│ ├── availablemodel.js # AvailableModel (dynamic model from API)
|
|
683
|
+
│ ├── candidate.js # Candidate
|
|
684
|
+
│ ├── chathistory.js # ChatTurn, ChatHistory
|
|
685
|
+
│ ├── chatinfo.js # ChatInfo
|
|
686
|
+
│ ├── gem.js # Gem, GemJar
|
|
687
|
+
│ ├── grpc.js # RPCData
|
|
688
|
+
│ ├── image.js # Image, WebImage, GeneratedImage
|
|
689
|
+
│ ├── modeloutput.js # ModelOutput
|
|
690
|
+
│ ├── research.js # DeepResearchPlan, DeepResearchStatus
|
|
691
|
+
│ ├── researchresult.js # DeepResearchResult
|
|
692
|
+
│ └── video.js # Video, GeneratedVideo, GeneratedMedia
|
|
693
|
+
├── utils/
|
|
694
|
+
│ ├── accessToken.js # cookie handling & init request
|
|
695
|
+
│ ├── parsing.js # response parsing utilities
|
|
696
|
+
│ ├── research.js # deep research payload extractors
|
|
697
|
+
│ ├── rotate.js # cookie rotation (kept from original)
|
|
698
|
+
│ └── upload.js # file upload helpers
|
|
699
|
+
└── components/
|
|
700
|
+
├── chatMixin.js # chat history methods
|
|
701
|
+
├── gemMixin.js # gem management methods
|
|
702
|
+
└── researchMixin.js # deep research methods
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
## References
|
|
706
|
+
|
|
707
|
+
[Google AI Studio](https://ai.google.dev/tutorials/ai-studio_quickstart)
|
|
336
708
|
|
|
337
|
-
|
|
709
|
+
[Gemini-API (Python)](https://github.com/HanaokaYuzu/Gemini-API) by [@HanaokaYuzu](https://github.com/HanaokaYuzu)
|
|
338
710
|
|
|
339
|
-
|
|
711
|
+
[acheong08/Bard](https://github.com/acheong08/Bard)
|
|
340
712
|
|
|
341
713
|
---
|
|
342
714
|
|
|
343
|
-
|
|
715
|
+
**Disclaimer:** This is an unofficial package and is not affiliated with or endorsed by Google. Cookie-based authentication may break if Google changes its internal API. Use at your own risk.
|
|
344
716
|
|
|
345
|
-
MIT
|
|
717
|
+
**License:** MIT
|