gemini-reverse 1.0.7 → 1.0.9

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.
@@ -0,0 +1,75 @@
1
+ # Changelog
2
+
3
+ > **Language / Bahasa:** [English](CHANGELOG-EN.md) · [Indonesia](CHANGELOG-IDN.md)  |  **README:** [EN](README.md) · [IDN](README-IDN.md)
4
+
5
+ All notable changes to this project will be documented in this file.
6
+
7
+ ---
8
+
9
+ ## [1.0.7]
10
+
11
+ ### Added
12
+ - **Extended Thinking** — `extended_thinking` parameter on `generateContent`, `generateContentStream`, `sendMessage`, and `sendMessageStream`. Enables deeper reasoning mode on supported models. Model header is extended at request time with the thinking flag and session ID.
13
+ - **`StreamingFrameParser`** — New stateful incremental frame parser class replacing the stateless `parseResponseByFrame`. Buffers partial frames efficiently across network chunks without rescanning already-processed bytes.
14
+ - **Quota & Usage Info** — `client.quotas`, `client.usageInfo`, and `client.abuseStatus` properties are populated automatically at initialization via `CHECK_GEMINI_QUOTA`, `CHECK_QUOTA`, `GET_USAGE_INFO`, and `GET_ABUSE_STATUS` RPCs.
15
+ - **Activity Watchdog** — Background timer (`_startActivityWatchdog`) sends periodic heartbeats every 60–300 seconds to keep the session alive without requiring explicit user requests.
16
+ - **`_checkAccountStatus()`** — Internal method that guards RPC calls from running on restricted accounts.
17
+ - **`_parseRpcResults()`** — Internal helper iterator for extracting typed bodies from batch RPC responses, including rejection code handling (code 7 → mark unauthenticated).
18
+ - **`_sessionid`** — Per-session UUID generated at constructor and refreshed on each `init()`. Used as a field in the model header for each generation request.
19
+ - **`source_path` in `_batchExecute`** — Optional parameter (default `/app`, `/usage` for `_fetchUsageInfo`).
20
+ - **`model_number` in `AvailableModel`** — New field mirrored from model header into request payload (`inner[79]`).
21
+ - **`buildModelIdNumberMapping()`** — Static method on `AvailableModel` returning a `modelId → modelNumber` mapping.
22
+ - **New models** — `BASIC_LITE` (`gemini-3-lite`), `PLUS_LITE` (`gemini-3-lite-plus`), `ADVANCED_LITE` (`gemini-3-lite-advanced`).
23
+ - **New GRPC keys** — Added from `pr`: `GET_CONVERSATION_TURN`, `UPDATE_CONVERSATION`, `MARK_LAST_CONVERSATION_TURN`, `GENERATE_HEADLINE`, `GET_GEM`, `DELETE_GEM_AND_CONVERSATIONS`, `CREATE_TASK`, `GET_TASK`, `GET_ALL_TASKS`, `GET_TASKS_IN_CONVERSATION`, `GET_CANDIDATES`, `LIST_DISCOVERY_CARDS`, `GET_DISCOVERY_CARD`, `LIST_DISCOVERY_BANNERS`, `LIST_GEMINI_APP_ARTIFACTS`, `DELETE_GEMINI_APP_ARTIFACTS`, `LIST_MEMORIES`, `CREATE_MEMORY`, `UPDATE_MEMORY`, `DELETE_MEMORY`, `DELETE_ALL_MEMORIES`, `CHECK_GEMINI_QUOTA`, `CHECK_QUOTA`, `GET_ABUSE_STATUS`, `UPDATE_USER_PREFERENCES`, `READ_USER_PREFERENCES`, `CONTINUE_SHARED_CONVERSATION`, `GET_USAGE_INFO`.
24
+ - **Auto-refresh jitter** — Cookie refresh interval now applies ±15 seconds of random jitter to prevent synchronized background requests.
25
+ - **`_fetchPreferences()` / `_syncActivity()`** — Replaced monolithic `_sendBardSettings` / `_sendBardActivity` with dedicated methods that cleanly separate preference sync from activity heartbeat.
26
+ - **Case-insensitive model resolution** — `_resolveModelByName` now matches model names and display names case-insensitively.
27
+ - **`_fetchRecentChats` account guard** — Skips fetching chat history if account status is not available.
28
+
29
+ ### Changed
30
+ - **Model header format** — Updated from `[4]` to `[4,5,6,8]` with a `model_number` appended at the end. Header `x-goog-ext-73010990-jspb` changed from `[0]` to `[0,0,0]`.
31
+ - **`Headers.BATCH_EXEC`** — Updated to match the new model header format.
32
+ - **Inner request array** — Size increased from 69 to 81 elements. `inner[68]` changed from `2` to `1`. New slots `inner[79]` (model_number) and `inner[80]` (thinking mode: `2` = extended, `1` = standard).
33
+ - **`_initRpc`** — Now calls `_fetchPreferences`, `_syncActivity`, `_fetchRecentChats`, `_fetchQuota`, `_fetchExtraQuota`, `_fetchAbuseStatus`, `_fetchUsageInfo` in sequence.
34
+ - **`THINKING` models removed** — `BASIC_THINKING`, `PLUS_THINKING`, `ADVANCED_THINKING` replaced by the `LITE` model family.
35
+ - **Upload cookies** — `uploadFile` now forwards session cookies alongside the `Push-ID` and REFERER headers.
36
+ - **HTML entity decoding** — Extended to cover numeric entities (`{`, ``) and additional named entities.
37
+ - **Video download headers** — `_downloadFile` in `video.js` now sends `Origin` and `Referer` headers.
38
+ - **`fetchGems` language** — Uses `this.language` instead of hardcoded `'en'`.
39
+ - **Auto-init on first call** — `generateContent` and `generateContentStream` call `init()` automatically if the client is not running.
40
+
41
+ ### Fixed
42
+ - **Upload failure** — Fixed missing `Push-ID` header, content-type, and REFERER headers in `uploadFile`.
43
+ - **Quota key format** — Null entries in quota ID list are now filtered before joining, producing clean numeric keys (e.g. `11`, `4`) instead of `-11`, `-4`.
44
+
45
+ ---
46
+
47
+ ## [1.0.8]
48
+
49
+ ### Fixed
50
+ - Fixed quota key format — null entries in quota ID list are now filtered before joining, producing clean numeric keys (e.g. `11`, `4`) instead of `-11`, `-4`.
51
+
52
+ ---
53
+
54
+ ## [1.0.6]
55
+
56
+ ### Fixed
57
+ - Fixed file upload — upload now correctly sends cookies and required headers.
58
+ - Fixed media download for generated video and audio.
59
+
60
+ ---
61
+
62
+ ## [1.0.5]
63
+
64
+ ### Fixed
65
+ - Fixed image generation parsing — `GeneratedImage` now uses `client_ref` pattern instead of requiring direct cookie injection.
66
+
67
+ ---
68
+
69
+ ## [1.0.4]
70
+
71
+ ### Added
72
+ - Initial public release.
73
+ - `GeminiClient` with cookie-based authentication.
74
+ - Chat sessions, streaming, image generation, deep research, gems.
75
+ - TypeScript declarations.
@@ -0,0 +1,75 @@
1
+ # Changelog
2
+
3
+ > **Language / Bahasa:** [English](CHANGELOG-EN.md) · [Indonesia](CHANGELOG-IDN.md)  |  **README:** [EN](README.md) · [IDN](README-IDN.md)
4
+
5
+ Semua perubahan penting pada proyek ini didokumentasikan di file ini.
6
+
7
+ ---
8
+
9
+ ## [1.0.7]
10
+
11
+ ### Ditambahkan
12
+ - **Extended Thinking** — Parameter `extended_thinking` pada `generateContent`, `generateContentStream`, `sendMessage`, dan `sendMessageStream`. Mengaktifkan mode penalaran yang lebih dalam pada model yang mendukung. Model header dimodifikasi saat request dengan menambahkan flag thinking dan session ID.
13
+ - **`StreamingFrameParser`** — Class parser frame stateful incremental yang menggantikan `parseResponseByFrame` yang stateless. Buffer frame parsial secara efisien lintas chunk jaringan tanpa perlu memindai ulang byte yang sudah diproses.
14
+ - **Quota & Info Penggunaan** — Property `client.quotas`, `client.usageInfo`, dan `client.abuseStatus` diisi otomatis saat inisialisasi via RPC `CHECK_GEMINI_QUOTA`, `CHECK_QUOTA`, `GET_USAGE_INFO`, dan `GET_ABUSE_STATUS`.
15
+ - **Activity Watchdog** — Timer background (`_startActivityWatchdog`) yang mengirim heartbeat berkala setiap 60–300 detik untuk menjaga sesi tetap aktif tanpa perlu request eksplisit dari pengguna.
16
+ - **`_checkAccountStatus()`** — Method internal untuk memastikan RPC tidak dijalankan pada akun yang dibatasi.
17
+ - **`_parseRpcResults()`** — Helper iterator internal untuk mengekstrak body dari respons batch RPC, termasuk penanganan rejection code (code 7 → tandai sebagai unauthenticated).
18
+ - **`_sessionid`** — UUID per-sesi yang dibuat di constructor dan diperbarui setiap `init()`. Digunakan sebagai field di model header untuk setiap request generasi.
19
+ - **`source_path` di `_batchExecute`** — Parameter opsional (default `/app`, `/usage` untuk `_fetchUsageInfo`).
20
+ - **`model_number` di `AvailableModel`** — Field baru yang dicerminkan dari model header ke payload request (`inner[79]`).
21
+ - **`buildModelIdNumberMapping()`** — Method static pada `AvailableModel` yang mengembalikan mapping `modelId → modelNumber`.
22
+ - **Model baru** — `BASIC_LITE` (`gemini-3-lite`), `PLUS_LITE` (`gemini-3-lite-plus`), `ADVANCED_LITE` (`gemini-3-lite-advanced`).
23
+ - **GRPC key baru** — Ditambahkan dari `pr`: `GET_CONVERSATION_TURN`, `UPDATE_CONVERSATION`, `MARK_LAST_CONVERSATION_TURN`, `GENERATE_HEADLINE`, `GET_GEM`, `DELETE_GEM_AND_CONVERSATIONS`, `CREATE_TASK`, `GET_TASK`, `GET_ALL_TASKS`, `GET_TASKS_IN_CONVERSATION`, `GET_CANDIDATES`, `LIST_DISCOVERY_CARDS`, `GET_DISCOVERY_CARD`, `LIST_DISCOVERY_BANNERS`, `LIST_GEMINI_APP_ARTIFACTS`, `DELETE_GEMINI_APP_ARTIFACTS`, `LIST_MEMORIES`, `CREATE_MEMORY`, `UPDATE_MEMORY`, `DELETE_MEMORY`, `DELETE_ALL_MEMORIES`, `CHECK_GEMINI_QUOTA`, `CHECK_QUOTA`, `GET_ABUSE_STATUS`, `UPDATE_USER_PREFERENCES`, `READ_USER_PREFERENCES`, `CONTINUE_SHARED_CONVERSATION`, `GET_USAGE_INFO`.
24
+ - **Jitter auto-refresh** — Interval refresh cookie kini menerapkan jitter acak ±15 detik untuk mencegah request background yang tersinkronisasi.
25
+ - **`_fetchPreferences()` / `_syncActivity()`** — Menggantikan `_sendBardSettings` / `_sendBardActivity` yang monolitik dengan method khusus yang memisahkan sinkronisasi preferensi dari heartbeat aktivitas.
26
+ - **Resolusi model case-insensitive** — `_resolveModelByName` kini mencocokkan nama model dan display name tanpa memperhatikan huruf besar/kecil.
27
+ - **Guard akun di `_fetchRecentChats`** — Melewati pengambilan riwayat chat jika status akun tidak tersedia.
28
+
29
+ ### Diubah
30
+ - **Format model header** — Diperbarui dari `[4]` menjadi `[4,5,6,8]` dengan `model_number` ditambahkan di akhir. Header `x-goog-ext-73010990-jspb` berubah dari `[0]` menjadi `[0,0,0]`.
31
+ - **`Headers.BATCH_EXEC`** — Diperbarui mengikuti format model header baru.
32
+ - **Array request inner** — Ukuran bertambah dari 69 menjadi 81 elemen. `inner[68]` berubah dari `2` menjadi `1`. Slot baru `inner[79]` (model_number) dan `inner[80]` (mode thinking: `2` = extended, `1` = standar).
33
+ - **`_initRpc`** — Sekarang memanggil `_fetchPreferences`, `_syncActivity`, `_fetchRecentChats`, `_fetchQuota`, `_fetchExtraQuota`, `_fetchAbuseStatus`, `_fetchUsageInfo` secara berurutan.
34
+ - **Model THINKING dihapus** — `BASIC_THINKING`, `PLUS_THINKING`, `ADVANCED_THINKING` digantikan oleh keluarga model LITE.
35
+ - **Cookie upload** — `uploadFile` kini meneruskan cookie sesi bersama header `Push-ID` dan REFERER.
36
+ - **Decode HTML entity** — Diperluas untuk mencakup entity numerik (`{`, ``) dan entity bernama tambahan.
37
+ - **Header download video** — `_downloadFile` di `video.js` kini mengirim header `Origin` dan `Referer`.
38
+ - **Bahasa `fetchGems`** — Menggunakan `this.language` alih-alih hardcode `'en'`.
39
+ - **Auto-init saat panggilan pertama** — `generateContent` dan `generateContentStream` memanggil `init()` otomatis jika client belum berjalan.
40
+
41
+ ### Diperbaiki
42
+ - **Kegagalan upload** — Memperbaiki header `Push-ID`, content-type, dan REFERER yang hilang di `uploadFile`.
43
+ - **Format kunci quota** — Entry null pada daftar ID quota kini difilter sebelum digabung, menghasilkan kunci numerik yang bersih (mis. `11`, `4`) alih-alih `-11`, `-4`.
44
+
45
+ ---
46
+
47
+ ## [1.0.8]
48
+
49
+ ### Diperbaiki
50
+ - Memperbaiki format kunci quota — entry null pada daftar ID quota kini difilter sebelum digabung, menghasilkan kunci numerik yang bersih (mis. `11`, `4`) alih-alih `-11`, `-4`.
51
+
52
+ ---
53
+
54
+ ## [1.0.6]
55
+
56
+ ### Diperbaiki
57
+ - Memperbaiki upload file — upload kini mengirim cookie dan header yang diperlukan dengan benar.
58
+ - Memperbaiki unduhan media untuk video dan audio yang dihasilkan.
59
+
60
+ ---
61
+
62
+ ## [1.0.5]
63
+
64
+ ### Diperbaiki
65
+ - Memperbaiki parsing image generation — `GeneratedImage` kini menggunakan pola `client_ref` alih-alih membutuhkan injeksi cookie langsung.
66
+
67
+ ---
68
+
69
+ ## [1.0.4]
70
+
71
+ ### Ditambahkan
72
+ - Rilis publik pertama.
73
+ - `GeminiClient` dengan autentikasi berbasis cookie.
74
+ - Chat session, streaming, image generation, deep research, gems.
75
+ - Deklarasi TypeScript.
package/README-IDN.md ADDED
@@ -0,0 +1,800 @@
1
+ ![Banner](https://napkinsdev.s3.us-east-1.amazonaws.com/next-s3-uploads/2a5843b5-3f5e-4ccd-bd61-f1ba6d6ae267/fb866bbfc5b3.png)
2
+
3
+ # Gemini-Reverse
4
+
5
+ Client Node.js tidak resmi untuk [Google Gemini](https://gemini.google.com), terinspirasi dari [Gemini-API](https://github.com/HanaokaYuzu/Gemini-API) — proyek reverse engineering Python oleh [@HanaokaYuzu](https://github.com/HanaokaYuzu).
6
+
7
+ > **Language / Bahasa:** [English](README.md) · [Indonesia](README-IDN.md)  |  **Changelog:** [EN](CHANGELOG-EN.md) · [IDN](CHANGELOG-IDN.md)
8
+
9
+ ## Fitur
10
+
11
+ - **Cookie Persisten** — Secara otomatis memperbarui cookie di latar belakang dengan jitter acak untuk mencegah request yang tersinkronisasi. Dioptimalkan untuk layanan yang selalu aktif.
12
+ - **Pembuatan Gambar** — Mendukung pembuatan dan pengeditan gambar secara native dengan bahasa natural.
13
+ - **Pembuatan Video & Audio** — Mendukung pembuatan video dan konten audio/musik secara native.
14
+ - **Deep Research** — Alur kerja penelitian mendalam lengkap dengan pembuatan rencana, polling status, dan pengambilan hasil.
15
+ - **Extended Thinking** — Mengaktifkan mode penalaran yang lebih dalam pada model yang mendukung.
16
+ - **System Prompt** — Mendukung kustomisasi system prompt model dengan [Gemini Gems](https://gemini.google.com/gems/view).
17
+ - **Dukungan Ekstensi** — Mendukung pembuatan konten dengan ekstensi Gemini seperti YouTube dan Gmail.
18
+ - **Output Terklasifikasi** — Mengkategorikan teks, pikiran, gambar, video, dan audio dalam respons.
19
+ - **Mode Streaming** — Mendukung streaming dengan parser frame stateful incremental, menghasilkan output parsial saat dibuat.
20
+ - **Penemuan Model Dinamis** — Menemukan model yang tersedia secara otomatis dari akun Anda saat inisialisasi.
21
+ - **Quota & Info Penggunaan** — Mengekspos batas quota akun, penggunaan komputasi, dan status abuse setelah inisialisasi.
22
+ - **Activity Watchdog** — Task heartbeat background yang menjaga sesi tetap aktif secara otomatis.
23
+ - **Dukungan TypeScript** — Deklarasi tipe TypeScript lengkap sudah tersedia.
24
+
25
+ ## Daftar Isi
26
+
27
+ - [Fitur](#fitur)
28
+ - [Daftar Isi](#daftar-isi)
29
+ - [Instalasi](#instalasi)
30
+ - [Autentikasi](#autentikasi)
31
+ - [Penggunaan](#penggunaan)
32
+ - [Inisialisasi](#inisialisasi)
33
+ - [Generate Konten](#generate-konten)
34
+ - [Generate Konten dengan File](#generate-konten-dengan-file)
35
+ - [Percakapan Multi-Giliran](#percakapan-multi-giliran)
36
+ - [Lanjutkan Percakapan Sebelumnya](#lanjutkan-percakapan-sebelumnya)
37
+ - [Baca Riwayat Percakapan](#baca-riwayat-percakapan)
38
+ - [Daftar Chat Terbaru](#daftar-chat-terbaru)
39
+ - [Hapus Percakapan](#hapus-percakapan)
40
+ - [Mode Sementara](#mode-sementara)
41
+ - [Mode Streaming](#mode-streaming)
42
+ - [Extended Thinking](#extended-thinking)
43
+ - [Pilih Model Bahasa](#pilih-model-bahasa)
44
+ - [Daftar Model Tersedia](#daftar-model-tersedia)
45
+ - [Terapkan System Prompt dengan Gemini Gems](#terapkan-system-prompt-dengan-gemini-gems)
46
+ - [Kelola Custom Gems](#kelola-custom-gems)
47
+ - [Buat Custom Gem](#buat-custom-gem)
48
+ - [Perbarui Gem yang Ada](#perbarui-gem-yang-ada)
49
+ - [Hapus Custom Gem](#hapus-custom-gem)
50
+ - [Ambil Proses Berpikir Model](#ambil-proses-berpikir-model)
51
+ - [Ambil Gambar dalam Respons](#ambil-gambar-dalam-respons)
52
+ - [Generate dan Edit Gambar](#generate-dan-edit-gambar)
53
+ - [Ambil Video dan Audio](#ambil-video-dan-audio)
54
+ - [Generate Konten dengan Ekstensi Gemini](#generate-konten-dengan-ekstensi-gemini)
55
+ - [Periksa dan Ganti Kandidat Balasan](#periksa-dan-ganti-kandidat-balasan)
56
+ - [Deep Research](#deep-research)
57
+ - [Status Akun](#status-akun)
58
+ - [Quota dan Info Penggunaan](#quota-dan-info-penggunaan)
59
+ - [Penanganan Error](#penanganan-error)
60
+ - [Persistensi Cookie](#persistensi-cookie)
61
+ - [TypeScript](#typescript)
62
+ - [Struktur Proyek](#struktur-proyek)
63
+ - [Referensi](#referensi)
64
+
65
+ ## Instalasi
66
+
67
+ ```bash
68
+ npm install gemini-reverse
69
+ ```
70
+
71
+ ## Autentikasi
72
+
73
+ - Buka [gemini.google.com](https://gemini.google.com) dan masuk dengan akun Google Anda
74
+ - Tekan F12 untuk membuka DevTools, buka tab `Application` → `Cookies` → `https://gemini.google.com`
75
+ - Salin nilai `__Secure-1PSID` (dan opsional `__Secure-1PSIDTS`)
76
+
77
+ > `__Secure-1PSIDTS` bersifat opsional — client akan mencoba memperbarui dan menyimpan cache-nya secara otomatis setelah inisialisasi pertama yang berhasil.
78
+
79
+ ## Penggunaan
80
+
81
+ ### Inisialisasi
82
+
83
+ Import package dan inisialisasi client dengan cookie Anda. Setelah inisialisasi berhasil, client akan memperbarui `__Secure-1PSIDTS` secara otomatis di latar belakang dengan jitter acak, dan memulai watchdog heartbeat untuk menjaga sesi tetap aktif.
84
+
85
+ ```js
86
+ const { GeminiClient } = require('gemini-reverse');
87
+
88
+ const client = new GeminiClient({
89
+ secure_1psid: 'SECURE_1PSID_ANDA',
90
+ secure_1psidts: 'SECURE_1PSIDTS_ANDA', // opsional
91
+ proxy: null, // opsional, mis. 'http://host:port'
92
+ });
93
+
94
+ await client.init({
95
+ timeout: 300000, // timeout request dalam ms, default 300000
96
+ autoClose: false, // tutup client otomatis setelah tidak aktif
97
+ closeDelay: 300000, // delay sebelum menutup dalam ms
98
+ autoRefresh: true, // perbarui cookie + mulai activity watchdog
99
+ refreshInterval: 540000 // interval refresh cookie dalam ms
100
+ });
101
+ ```
102
+
103
+ > `autoClose` dan `closeDelay` adalah argumen opsional untuk menutup client secara otomatis setelah tidak aktif dalam jangka waktu tertentu. Pada layanan yang selalu aktif seperti chatbot, disarankan untuk mengatur `autoClose` ke `true` dengan nilai `closeDelay` yang wajar untuk manajemen resource yang lebih baik.
104
+
105
+ ### Generate Konten
106
+
107
+ Ajukan pertanyaan satu-giliran dengan memanggil `generateContent`, yang mengembalikan objek `ModelOutput` berisi teks yang dihasilkan, gambar, pikiran, dan metadata percakapan.
108
+
109
+ ```js
110
+ const response = await client.generateContent({ prompt: 'Halo Dunia!' });
111
+ console.log(response.text);
112
+ ```
113
+
114
+ ### Generate Konten dengan File
115
+
116
+ Gemini mendukung input file, termasuk gambar dan dokumen. Kirimkan array path file atau objek `Buffer` bersama prompt teks Anda.
117
+
118
+ ```js
119
+ const response = await client.generateContent({
120
+ prompt: 'Deskripsikan isi file-file ini.',
121
+ files: ['./dokumen.pdf', './foto.png'],
122
+ });
123
+ console.log(response.text);
124
+ ```
125
+
126
+ ### Percakapan Multi-Giliran
127
+
128
+ Gunakan `startChat` untuk membuat objek `ChatSession` dan kirim pesan melaluinya. Riwayat percakapan ditangani secara otomatis dan diperbarui setiap giliran.
129
+
130
+ ```js
131
+ const chat = client.startChat();
132
+
133
+ const res1 = await chat.sendMessage({ prompt: 'Nama saya Alice.' });
134
+ console.log(res1.text);
135
+
136
+ const res2 = await chat.sendMessage({ prompt: 'Siapa nama saya?' });
137
+ console.log(res2.text); // mengingat konteks
138
+ ```
139
+
140
+ ### Lanjutkan Percakapan Sebelumnya
141
+
142
+ Kirimkan metadata `ChatSession` sebelumnya ke `startChat` untuk melanjutkan percakapan. Anda dapat menyimpan metadata ke file atau database untuk memulihkannya setelah proses di-restart.
143
+
144
+ ```js
145
+ const chat = client.startChat();
146
+ await chat.sendMessage({ prompt: 'Cuaca hari ini cerah.' });
147
+
148
+ // Simpan metadata sesi
149
+ const savedMetadata = chat.metadata;
150
+ const savedCid = chat.cid;
151
+
152
+ // Lanjutkan di sesi baru
153
+ const previousChat = client.startChat({ metadata: savedMetadata });
154
+ const response = await previousChat.sendMessage({ prompt: 'Apa pesan saya sebelumnya?' });
155
+ console.log(response.text);
156
+ ```
157
+
158
+ ### Baca Riwayat Percakapan
159
+
160
+ Ambil riwayat percakapan lengkap dengan memanggil `readChat` menggunakan ID chat. Mengembalikan objek `ChatHistory` berisi daftar objek `ChatTurn` dari terbaru ke terlama.
161
+
162
+ ```js
163
+ const chat = client.startChat();
164
+ await chat.sendMessage({ prompt: 'Apa ibu kota Prancis?' });
165
+
166
+ const history = await client.readChat(chat.cid);
167
+ if (history) {
168
+ for (const turn of history.turns) {
169
+ console.log(`[${turn.role.toUpperCase()}] ${turn.text}`);
170
+ }
171
+ }
172
+ ```
173
+
174
+ Anda juga bisa membaca riwayat langsung dari sesi:
175
+
176
+ ```js
177
+ const history = await chat.readHistory(10); // ambil 10 giliran terakhir
178
+ ```
179
+
180
+ ### Daftar Chat Terbaru
181
+
182
+ Gunakan `listChats` untuk mendapatkan daftar sesi chat terbaru yang di-cache saat inisialisasi.
183
+
184
+ ```js
185
+ const chats = client.listChats();
186
+ if (chats) {
187
+ for (const info of chats) {
188
+ console.log(`${info.cid}: ${info.title} (disematkan: ${info.is_pinned})`);
189
+ }
190
+ }
191
+ ```
192
+
193
+ ### Hapus Percakapan
194
+
195
+ Hapus chat tertentu dari riwayat Gemini di server dengan memanggil `deleteChat` menggunakan ID chat.
196
+
197
+ ```js
198
+ const chat = client.startChat();
199
+ await chat.sendMessage({ prompt: 'Ini percakapan sementara.' });
200
+
201
+ await client.deleteChat(chat.cid);
202
+ console.log(`Chat dihapus: ${chat.cid}`);
203
+ ```
204
+
205
+ ### Mode Sementara
206
+
207
+ Kirimkan `temporary: true` ke `generateContent` atau `sendMessage` agar percakapan tidak tersimpan ke riwayat Gemini.
208
+
209
+ ```js
210
+ const response = await client.generateContent({
211
+ prompt: 'Halo Dunia!',
212
+ temporary: true,
213
+ });
214
+ console.log(response.text);
215
+
216
+ // Juga berfungsi dalam sesi chat
217
+ const chat = client.startChat();
218
+ await chat.sendMessage({ prompt: 'Cuaca hari ini cerah.', temporary: false });
219
+ const res2 = await chat.sendMessage({ prompt: 'Apa pesan terakhir saya?', temporary: true });
220
+ console.log(res2.text);
221
+ ```
222
+
223
+ ### Mode Streaming
224
+
225
+ Untuk respons yang panjang, gunakan mode streaming untuk menerima output parsial saat dihasilkan. Respons menggunakan `StreamingFrameParser` stateful secara internal sehingga frame parsial di-buffer secara efisien. Atribut `text_delta` hanya berisi **karakter baru** sejak yield terakhir.
226
+
227
+ ```js
228
+ for await (const chunk of client.generateContentStream({
229
+ prompt: 'Apa perbedaan antara promise dan async/await?',
230
+ })) {
231
+ process.stdout.write(chunk.text_delta);
232
+ }
233
+ console.log();
234
+ ```
235
+
236
+ Streaming juga berfungsi dalam sesi chat:
237
+
238
+ ```js
239
+ const chat = client.startChat();
240
+ for await (const chunk of chat.sendMessageStream({ prompt: 'Ceritakan sebuah kisah panjang.' })) {
241
+ process.stdout.write(chunk.text_delta);
242
+ }
243
+ ```
244
+
245
+ ### Extended Thinking
246
+
247
+ Kirimkan `extended_thinking: true` untuk mengaktifkan mode penalaran yang lebih dalam. Model akan meluangkan lebih banyak waktu untuk merencanakan sebelum merespons. Didukung pada model tier Pro dan Advanced.
248
+
249
+ ```js
250
+ const { Model } = require('gemini-reverse');
251
+
252
+ const response = await client.generateContent({
253
+ prompt: 'Selesaikan langkah demi langkah: Jika sebuah kereta melaju 120 km/jam dan harus menempuh 450 km, berapa lama waktu yang dibutuhkan?',
254
+ model: Model.ADVANCED_PRO,
255
+ extended_thinking: true,
256
+ });
257
+
258
+ if (response.thoughts) {
259
+ console.log('Proses berpikir:', response.thoughts);
260
+ }
261
+ console.log('Jawaban:', response.text);
262
+ ```
263
+
264
+ Juga berfungsi dalam streaming dan chat:
265
+
266
+ ```js
267
+ const chat = client.startChat({ model: Model.ADVANCED_FLASH });
268
+ const res = await chat.sendMessage({
269
+ prompt: 'Jelaskan masalah P vs NP.',
270
+ extended_thinking: true,
271
+ });
272
+ console.log(res.text);
273
+ ```
274
+
275
+ ### Pilih Model Bahasa
276
+
277
+ Tentukan model bahasa yang digunakan dengan mengoper argumen `model`. Model tersedia ditemukan secara dinamis saat inisialisasi berdasarkan tier akun Anda.
278
+
279
+ ```js
280
+ const { Model } = require('gemini-reverse');
281
+
282
+ // Menggunakan konstanta bawaan
283
+ const response1 = await client.generateContent({
284
+ prompt: 'Apa versi model Anda?',
285
+ model: Model.BASIC_FLASH,
286
+ });
287
+
288
+ // Menggunakan string nama model (case-insensitive)
289
+ const chat = client.startChat({ model: 'gemini-3-pro' });
290
+
291
+ // Menggunakan dict model header kustom
292
+ const chat2 = client.startChat({
293
+ model: {
294
+ model_name: 'kustom',
295
+ model_header: {
296
+ 'x-goog-ext-525001261-jspb': '[1,null,null,null,"MODEL_ID",null,null,0,[4,5,6,8],null,null,1,null,null,1]',
297
+ 'x-goog-ext-73010989-jspb': '[0]',
298
+ 'x-goog-ext-73010990-jspb': '[0,0,0]',
299
+ },
300
+ },
301
+ });
302
+ ```
303
+
304
+ **Konstanta model bawaan:**
305
+
306
+ | Konstanta | `model_name` | Catatan |
307
+ |---|---|---|
308
+ | `Model.UNSPECIFIED` | `unspecified` | Default, Gemini memilih sendiri |
309
+ | `Model.BASIC_PRO` | `gemini-3-pro` | Tier gratis |
310
+ | `Model.BASIC_FLASH` | `gemini-3-flash` | Tier gratis, tercepat |
311
+ | `Model.BASIC_LITE` | `gemini-3-lite` | Tier gratis, ringan |
312
+ | `Model.PLUS_PRO` | `gemini-3-pro-plus` | Tier Plus |
313
+ | `Model.PLUS_FLASH` | `gemini-3-flash-plus` | Tier Plus |
314
+ | `Model.PLUS_LITE` | `gemini-3-lite-plus` | Tier Plus |
315
+ | `Model.ADVANCED_PRO` | `gemini-3-pro-advanced` | Tier Advanced |
316
+ | `Model.ADVANCED_FLASH` | `gemini-3-flash-advanced` | Tier Advanced |
317
+ | `Model.ADVANCED_LITE` | `gemini-3-lite-advanced` | Tier Advanced |
318
+
319
+ ### Daftar Model Tersedia
320
+
321
+ Client menemukan model mana yang dapat diakses akun Anda secara dinamis saat inisialisasi. Gunakan `listModels` untuk memeriksanya.
322
+
323
+ ```js
324
+ await client.init();
325
+
326
+ const models = client.listModels();
327
+ if (models) {
328
+ for (const model of models) {
329
+ console.log(`${model.model_id} → ${model.model_name || model.display_name}`);
330
+ console.log(` capacity: ${model.capacity}, model_number: ${model.model_number}, advanced_only: ${model.advanced_only}`);
331
+ }
332
+ }
333
+ ```
334
+
335
+ ### Terapkan System Prompt dengan Gemini Gems
336
+
337
+ System prompt dapat diterapkan pada percakapan melalui [Gemini Gems](https://gemini.google.com/gems/view). Kirimkan argumen `gem` ke `generateContent` atau `startChat` — bisa berupa objek `Gem` atau string ID gem.
338
+
339
+ ```js
340
+ // Ambil semua gems untuk akun
341
+ await client.fetchGems();
342
+ const gems = client.gems;
343
+
344
+ // Dapatkan gem tertentu
345
+ const codingPartner = gems.get({ name: 'Coding partner' });
346
+
347
+ const response = await client.generateContent({
348
+ prompt: 'Apa system prompt Anda?',
349
+ gem: codingPartner,
350
+ });
351
+ console.log(response.text);
352
+
353
+ // Gunakan gem dalam chat multi-giliran
354
+ const chat = client.startChat({ gem: codingPartner });
355
+ const res2 = await chat.sendMessage({ prompt: 'Bantu saya menulis binary search.' });
356
+ console.log(res2.text);
357
+ ```
358
+
359
+ > Ada beberapa gems sistem bawaan yang tersembunyi secara default. Gunakan `fetchGems({ includeHidden: true })` untuk menyertakannya.
360
+
361
+ ### Kelola Custom Gems
362
+
363
+ Anda dapat membuat, memperbarui, dan menghapus custom gems secara programatik. Gems sistem bawaan tidak dapat dimodifikasi.
364
+
365
+ #### Buat Custom Gem
366
+
367
+ ```js
368
+ const newGem = await client.createGem({
369
+ name: 'Tutor Python',
370
+ prompt: 'Anda adalah tutor pemrograman Python yang membantu. Selalu berikan contoh kode yang dapat dijalankan.',
371
+ description: 'Gem khusus untuk pemrograman Python',
372
+ });
373
+
374
+ console.log(`Dibuat: ${newGem.id}`);
375
+
376
+ // Gunakan gem baru segera
377
+ const response = await client.generateContent({
378
+ prompt: 'Jelaskan list comprehension.',
379
+ gem: newGem,
380
+ });
381
+ console.log(response.text);
382
+ ```
383
+
384
+ #### Perbarui Gem yang Ada
385
+
386
+ > Saat memperbarui gem, semua parameter (`name`, `prompt`, `description`) harus disediakan meskipun hanya satu yang berubah.
387
+
388
+ ```js
389
+ await client.fetchGems();
390
+ const pythonTutor = client.gems.get({ name: 'Tutor Python' });
391
+
392
+ const updatedGem = await client.updateGem({
393
+ gem: pythonTutor,
394
+ name: 'Tutor Python Lanjutan',
395
+ prompt: 'Anda adalah tutor pemrograman Python ahli. Fokus pada performa dan praktik terbaik.',
396
+ description: 'Asisten pemrograman Python tingkat lanjut',
397
+ });
398
+
399
+ console.log(`Diperbarui: ${updatedGem.id}`);
400
+ ```
401
+
402
+ #### Hapus Custom Gem
403
+
404
+ ```js
405
+ await client.fetchGems();
406
+ const gemToDelete = client.gems.get({ name: 'Tutor Python Lanjutan' });
407
+
408
+ await client.deleteGem(gemToDelete); // bisa juga kirim string ID gem
409
+ console.log(`Dihapus: ${gemToDelete.name}`);
410
+ ```
411
+
412
+ ### Ambil Proses Berpikir Model
413
+
414
+ Saat menggunakan model yang mendukung kemampuan berpikir, proses penalaran internal model diekspos melalui `response.thoughts`.
415
+
416
+ ```js
417
+ const response = await client.generateContent({
418
+ prompt: 'Berapa 17 × 23?',
419
+ model: Model.BASIC_FLASH,
420
+ });
421
+
422
+ if (response.thoughts) {
423
+ console.log('Pemikiran:', response.thoughts);
424
+ }
425
+ console.log('Jawaban:', response.text);
426
+ ```
427
+
428
+ ### Ambil Gambar dalam Respons
429
+
430
+ Gambar dalam respons disimpan sebagai daftar objek `Image` yang dapat diakses melalui `response.images`. Setiap gambar memiliki `url`, `title`, dan deskripsi `alt`.
431
+
432
+ ```js
433
+ const response = await client.generateContent({ prompt: 'Kirimkan beberapa foto kucing.' });
434
+
435
+ for (const image of response.images) {
436
+ console.log(`${image.title}: ${image.url}`);
437
+ console.log(`Alt: ${image.alt}`);
438
+ }
439
+ ```
440
+
441
+ ### Generate dan Edit Gambar
442
+
443
+ Minta Gemini untuk menghasilkan atau mengedit gambar menggunakan bahasa natural. Gambar yang dihasilkan dikembalikan sebagai objek `GeneratedImage` dan dapat disimpan ke disk.
444
+
445
+ > Google memiliki batasan ketersediaan pembuatan gambar yang bervariasi menurut wilayah dan jenis akun. Pengguna di bawah 18 tahun tidak dapat menggunakan fitur ini.
446
+
447
+ ```js
448
+ const response = await client.generateContent({
449
+ prompt: 'Buat gambar realistis kucing berpakaian astronot.',
450
+ });
451
+
452
+ for (let i = 0; i < response.images.length; i++) {
453
+ const image = response.images[i];
454
+ const savedPath = await image.save({
455
+ path: './temp',
456
+ filename: `kucing_angkasa_${i}.png`,
457
+ verbose: true,
458
+ });
459
+ console.log(`Disimpan ke: ${savedPath}`);
460
+ }
461
+ ```
462
+
463
+ > Saat meminta Gemini "mengirim" gambar, ia mengembalikan gambar web (`WebImage`). Saat meminta "menghasilkan" gambar, ia mengembalikan gambar yang dihasilkan AI (`GeneratedImage`). Keduanya dikategorikan otomatis di `response.images`.
464
+
465
+ ### Ambil Video dan Audio
466
+
467
+ Gemini dapat menghasilkan video pendek dan audio/musik. Dikembalikan sebagai objek `GeneratedVideo` dan `GeneratedMedia` di `response.videos` dan `response.media`.
468
+
469
+ > Anda mungkin memerlukan langganan Gemini aktif untuk mengakses pembuatan video dan audio.
470
+
471
+ ```js
472
+ // Generate video
473
+ const videoResponse = await client.generateContent({
474
+ prompt: 'Buat video pendek ombak di pantai.',
475
+ });
476
+
477
+ for (const video of videoResponse.videos) {
478
+ const result = await video.save({ savePath: './temp', verbose: true });
479
+ console.log('Video:', result.video);
480
+ console.log('Thumbnail:', result.video_thumbnail);
481
+ }
482
+
483
+ // Generate audio/musik
484
+ const mediaResponse = await client.generateContent({
485
+ prompt: 'Buat melodi piano yang menenangkan.',
486
+ });
487
+
488
+ for (const media of mediaResponse.media) {
489
+ // downloadType: 'audio' | 'video' | 'both' (default)
490
+ const result = await media.save({ savePath: './temp', downloadType: 'both', verbose: true });
491
+ console.log('MP3:', result.audio);
492
+ console.log('MP4:', result.video);
493
+ }
494
+ ```
495
+
496
+ > `GeneratedMedia.save()` menerima parameter `downloadType`: `"audio"`, `"video"`, atau `"both"` (default). Metode save akan polling otomatis jika konten masih dalam proses generate (HTTP 206).
497
+
498
+ ### Generate Konten dengan Ekstensi Gemini
499
+
500
+ Untuk menggunakan ekstensi Gemini (Gmail, YouTube, dll.), Anda harus mengaktifkannya terlebih dahulu di [situs Gemini](https://gemini.google.com/extensions). Referensikan dalam prompt dengan awalan `@` atau dalam bahasa natural.
501
+
502
+ > Anda harus mengaktifkan Gemini Apps Activity di akun untuk menggunakan ekstensi.
503
+
504
+ ```js
505
+ const gmailResponse = await client.generateContent({
506
+ prompt: '@Gmail Apa pesan terbaru di kotak masuk saya?',
507
+ });
508
+ console.log(gmailResponse.text);
509
+
510
+ const youtubeResponse = await client.generateContent({
511
+ prompt: '@YouTube Apa video terbaru dari channel Fireship?',
512
+ });
513
+ console.log(youtubeResponse.text);
514
+ ```
515
+
516
+ ### Periksa dan Ganti Kandidat Balasan
517
+
518
+ Respons Gemini terkadang berisi beberapa kandidat balasan dengan konten yang berbeda. Anda dapat memeriksa semua kandidat dan memilih satu untuk melanjutkan alur percakapan.
519
+
520
+ ```js
521
+ const chat = client.startChat();
522
+ const response = await chat.sendMessage({ prompt: 'Rekomendasikan buku fiksi ilmiah.' });
523
+
524
+ // Daftar semua kandidat
525
+ response.candidates.forEach((candidate, i) => {
526
+ console.log(`[${i}] ${candidate.text.slice(0, 80)}...`);
527
+ });
528
+
529
+ if (response.candidates.length > 1) {
530
+ // Pilih kandidat kedua untuk dilanjutkan
531
+ chat.chooseCandidate(1);
532
+
533
+ const followup = await chat.sendMessage({ prompt: 'Ceritakan lebih lanjut tentangnya.' });
534
+ console.log(followup.text);
535
+ } else {
536
+ console.log('Hanya ada satu kandidat.');
537
+ }
538
+ ```
539
+
540
+ ### Deep Research
541
+
542
+ Fitur deep research Gemini adalah agen otonom yang menjelajahi web, menganalisis sumber, dan menghasilkan laporan komprehensif.
543
+
544
+ > Anda mungkin memerlukan langganan Gemini aktif untuk mengakses deep research.
545
+
546
+ **Metode satu panggilan cepat:**
547
+
548
+ ```js
549
+ const result = await client.deepResearch(
550
+ 'Bandingkan 3 penyedia cloud teratas dan penawaran AI mereka',
551
+ 10000, // interval polling dalam ms
552
+ 600000, // timeout dalam ms
553
+ (status) => console.log(`Status: ${status.state} — ${status.notes.slice(0, 1).join(', ')}`),
554
+ );
555
+
556
+ console.log(`Selesai: ${result.done}`);
557
+ console.log(result.text);
558
+ ```
559
+
560
+ **Alur kerja langkah demi langkah** untuk kontrol lebih:
561
+
562
+ ```js
563
+ // Langkah 1: Buat rencana penelitian
564
+ const plan = await client.createDeepResearchPlan(
565
+ 'Apa kemajuan terbaru dalam komputasi kuantum?'
566
+ );
567
+
568
+ console.log(`Judul: ${plan.title}`);
569
+ console.log(`ETA: ${plan.eta_text}`);
570
+ for (const step of plan.steps) {
571
+ console.log(` - ${step}`);
572
+ }
573
+
574
+ // Langkah 2: Mulai penelitian
575
+ const startOutput = await client.startDeepResearch(plan);
576
+ console.log('Penelitian dimulai:', startOutput.text.slice(0, 100));
577
+
578
+ // Langkah 3: Poll hingga selesai
579
+ const result = await client.waitForDeepResearch(
580
+ plan,
581
+ 10000, // interval polling dalam ms
582
+ 600000, // timeout dalam ms
583
+ (status) => console.log(`[${status.state}] ${status.notes[0] || ''}`),
584
+ );
585
+
586
+ console.log(result.text);
587
+ ```
588
+
589
+ ### Status Akun
590
+
591
+ Client mendeteksi tier kemampuan akun Anda saat inisialisasi dan mengeksposnya melalui `client.accountStatus`.
592
+
593
+ ```js
594
+ const { AccountStatus } = require('gemini-reverse');
595
+
596
+ await client.init();
597
+
598
+ if (client.accountStatus === AccountStatus.AVAILABLE) {
599
+ console.log('Akun terotorisasi penuh.');
600
+ } else if (client.accountStatus === AccountStatus.UNAUTHENTICATED) {
601
+ console.error('Cookie kedaluwarsa atau tidak valid.');
602
+ } else if (client.accountStatus === AccountStatus.LOCATION_REJECTED) {
603
+ console.error('Gemini tidak tersedia di wilayah Anda.');
604
+ } else {
605
+ console.warn(`Status akun: ${client.accountStatus.name} — ${client.accountStatus.description}`);
606
+ }
607
+ ```
608
+
609
+ **Semua nilai status akun:**
610
+
611
+ | Konstanta | Kode | Deskripsi |
612
+ |---|---|---|
613
+ | `AccountStatus.AVAILABLE` | 1000 | Akun terotorisasi dan memiliki akses normal |
614
+ | `AccountStatus.ACCESS_TEMPORARILY_UNAVAILABLE` | 1014 | Akses dibatasi, mungkin wilayah/sementara |
615
+ | `AccountStatus.UNAUTHENTICATED` | 1016 | Cookie kedaluwarsa atau tidak valid |
616
+ | `AccountStatus.ACCOUNT_REJECTED` | 1021 | Akses akun ditolak |
617
+ | `AccountStatus.ACCOUNT_UNTRUSTED` | 1033 | Tidak lulus pemeriksaan keamanan/kepercayaan |
618
+ | `AccountStatus.TOS_PENDING` | 1040 | Harus menerima Syarat Layanan terbaru |
619
+ | `AccountStatus.TOS_OUT_OF_DATE` | 1042 | Syarat Layanan sudah usang |
620
+ | `AccountStatus.ACCOUNT_REJECTED_BY_GUARDIAN` | 1054 | Diblokir oleh orang tua atau wali |
621
+ | `AccountStatus.GUARDIAN_APPROVAL_REQUIRED` | 1057 | Memerlukan persetujuan orang tua |
622
+ | `AccountStatus.LOCATION_REJECTED` | 1060 | Tidak tersedia di negara/wilayah Anda |
623
+
624
+ ### Quota dan Info Penggunaan
625
+
626
+ Setelah inisialisasi, client mengambil batas quota akun dan metrik penggunaan komputasi secara otomatis. Akses melalui `client.quotas`, `client.usageInfo`, dan `client.abuseStatus`.
627
+
628
+ ```js
629
+ await client.init();
630
+
631
+ // Batas quota (Flash, Pro, fitur ekstra)
632
+ const quotas = client.quotas;
633
+ for (const [id, q] of Object.entries(quotas)) {
634
+ if (q.label) {
635
+ const remaining = q.remaining !== null ? `${q.remaining}/${q.total}` : 'tak terbatas';
636
+ console.log(`${q.label}: ${remaining} kredit (${q.usage_percentage?.toFixed(1) ?? '?'}% terpakai)`);
637
+ }
638
+ }
639
+
640
+ // Penggunaan komputasi (jendela 5 jam dan mingguan)
641
+ const usage = client.usageInfo;
642
+ if (usage.tier) {
643
+ console.log(`Tier akun: ${usage.tier.label}`);
644
+ }
645
+ if (usage.current_5h) {
646
+ console.log(`Jendela 5 jam: ${usage.current_5h.remaining_credits} kredit tersisa (${usage.current_5h.usage_percentage}% terpakai)`);
647
+ }
648
+ if (usage.weekly) {
649
+ console.log(`Mingguan: ${usage.weekly.remaining_credits} kredit tersisa`);
650
+ }
651
+
652
+ // Status abuse
653
+ const abuse = client.abuseStatus;
654
+ if (abuse) {
655
+ console.log(`Akun bersih: ${abuse.is_clean}`);
656
+ }
657
+ ```
658
+
659
+ ## Penanganan Error
660
+
661
+ ```js
662
+ const {
663
+ AuthError,
664
+ APIError,
665
+ GeminiError,
666
+ TimeoutError,
667
+ UsageLimitExceeded,
668
+ ModelInvalid,
669
+ TemporarilyBlocked,
670
+ } = require('gemini-reverse');
671
+
672
+ try {
673
+ const response = await chat.sendMessage({ prompt: 'Halo!' });
674
+ console.log(response.text);
675
+ } catch (e) {
676
+ if (e instanceof AuthError) {
677
+ console.error('Cookie kedaluwarsa atau tidak valid. Perbarui cookie Anda.');
678
+ } else if (e instanceof UsageLimitExceeded) {
679
+ console.error('Batas penggunaan tercapai. Coba lagi nanti atau ganti model.');
680
+ } else if (e instanceof TemporarilyBlocked) {
681
+ console.error('IP diblokir sementara oleh Google. Coba gunakan proxy atau tunggu.');
682
+ } else if (e instanceof TimeoutError) {
683
+ console.error('Request timeout. Coba tingkatkan nilai timeout di init().');
684
+ } else if (e instanceof ModelInvalid) {
685
+ console.error('Model tidak valid atau tidak tersedia. Coba model lain.');
686
+ } else if (e instanceof APIError) {
687
+ console.error('API error:', e.message);
688
+ } else {
689
+ throw e;
690
+ }
691
+ }
692
+ ```
693
+
694
+ ## Persistensi Cookie
695
+
696
+ Jika aplikasi Anda berjalan di lingkungan container (mis. Docker), Anda dapat menyimpan cache cookie yang diperbarui otomatis ke volume dengan mengatur variabel environment `GEMINI_COOKIE_PATH` ke path yang dapat ditulis.
697
+
698
+ ```yaml
699
+ # docker-compose.yml
700
+ services:
701
+ app:
702
+ environment:
703
+ GEMINI_COOKIE_PATH: /tmp/gemini_cache
704
+ volumes:
705
+ - ./gemini_cookies:/tmp/gemini_cache
706
+ ```
707
+
708
+ Secara default, cache disimpan di `utils/temp/` relatif terhadap direktori package.
709
+
710
+ ## TypeScript
711
+
712
+ Package ini menyertakan deklarasi TypeScript lengkap.
713
+
714
+ ```ts
715
+ import {
716
+ GeminiClient,
717
+ ChatSession,
718
+ ModelOutput,
719
+ ChatHistory,
720
+ ChatTurn,
721
+ ChatInfo,
722
+ Gem,
723
+ AvailableModel,
724
+ Model,
725
+ AccountStatus,
726
+ DeepResearchPlan,
727
+ DeepResearchResult,
728
+ StreamingFrameParser,
729
+ } from 'gemini-reverse';
730
+
731
+ const client = new GeminiClient({ secure_1psid: '...' });
732
+ await client.init();
733
+
734
+ const chat: ChatSession = client.startChat({ model: 'gemini-3-flash' });
735
+ const response: ModelOutput = await chat.sendMessage({
736
+ prompt: 'Halo!',
737
+ extended_thinking: false,
738
+ });
739
+
740
+ console.log(response.text);
741
+
742
+ const history: ChatHistory | null = await chat.readHistory();
743
+ if (history) {
744
+ history.turns.forEach((turn: ChatTurn) => console.log(turn.role, turn.text));
745
+ }
746
+
747
+ const models: AvailableModel[] | null = client.listModels();
748
+
749
+ // Akses quota dan usage setelah init
750
+ console.log(client.quotas);
751
+ console.log(client.usageInfo);
752
+ console.log(client.abuseStatus);
753
+ ```
754
+
755
+ ## Struktur Proyek
756
+
757
+ ```
758
+ gemini-reverse/
759
+ ├── index.js # entry point & ekspor
760
+ ├── index.d.ts # deklarasi TypeScript
761
+ ├── client.js # GeminiClient + ChatSession
762
+ ├── constants.js # Endpoint, GRPC, Headers, Model, AccountStatus, ErrorCode
763
+ ├── exceptions.js # kelas error kustom
764
+ ├── types/
765
+ │ ├── availablemodel.js # AvailableModel (model dinamis dari API)
766
+ │ ├── candidate.js # Candidate
767
+ │ ├── chathistory.js # ChatTurn, ChatHistory
768
+ │ ├── chatinfo.js # ChatInfo
769
+ │ ├── gem.js # Gem, GemJar
770
+ │ ├── grpc.js # RPCData
771
+ │ ├── image.js # Image, WebImage, GeneratedImage
772
+ │ ├── modeloutput.js # ModelOutput
773
+ │ ├── research.js # DeepResearchPlan, DeepResearchStatus
774
+ │ ├── researchresult.js # DeepResearchResult
775
+ │ └── video.js # Video, GeneratedVideo, GeneratedMedia
776
+ ├── utils/
777
+ │ ├── accessToken.js # penanganan cookie & request init
778
+ │ ├── parsing.js # utilitas parsing respons + StreamingFrameParser
779
+ │ ├── research.js # ekstraktor payload deep research
780
+ │ ├── rotate.js # rotasi cookie
781
+ │ └── upload.js # helper upload file
782
+ └── components/
783
+ ├── chatMixin.js # metode riwayat chat
784
+ ├── gemMixin.js # metode manajemen gem
785
+ └── researchMixin.js # metode deep research
786
+ ```
787
+
788
+ ## Referensi
789
+
790
+ [Google AI Studio](https://ai.google.dev/tutorials/ai-studio_quickstart)
791
+
792
+ [Gemini-API (Python)](https://github.com/HanaokaYuzu/Gemini-API) oleh [@HanaokaYuzu](https://github.com/HanaokaYuzu)
793
+
794
+ [acheong08/Bard](https://github.com/acheong08/Bard)
795
+
796
+ ---
797
+
798
+ **Peringatan:** Ini adalah package tidak resmi dan tidak berafiliasi dengan atau didukung oleh Google. Autentikasi berbasis cookie mungkin tidak berfungsi jika Google mengubah API internalnya. Gunakan dengan risiko Anda sendiri.
799
+
800
+ **Lisensi:** MIT
package/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
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).
6
6
 
7
+ > **Language / Bahasa:** [English](README.md) · [Indonesia](README-IDN.md) &nbsp;|&nbsp; **Changelog:** [EN](CHANGELOG-EN.md) · [IDN](CHANGELOG-IDN.md)
8
+
7
9
  ## Features
8
10
 
9
11
  - **Persistent Cookies** — Automatically refreshes cookies in the background with jitter to prevent synchronized requests. Optimized for always-on services.
package/client.js CHANGED
@@ -317,7 +317,7 @@ class GeminiClient {
317
317
  const resetTs = getNestedValue(item, [3, 0]);
318
318
  const total = getNestedValue(item, [4]);
319
319
  const remaining = getNestedValue(item, [5]);
320
- const quotaId = quotaIdList.join('-');
320
+ const quotaId = quotaIdList.filter(x => x != null).join('-') || String(actionId);
321
321
  const label = actionLabels[actionId] || `Gemini ${key}`;
322
322
  this._quotas[quotaId] = { usage_percentage: usageLevel, reset_time: resetTs, total, remaining, action_id: actionId, label: `${label} [${quotaId}]` };
323
323
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gemini-reverse",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "description": "Unofficial Node.js client for gemini.google.com — inspired by Gemini-API (Python). Supports streaming, chat sessions, gems, file uploads, and TypeScript.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",