@ikyyofc/gemini-cli 2.0.8 → 2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ikyyofc/gemini-cli",
3
- "version": "2.0.8",
3
+ "version": "2.0.9",
4
4
  "description": "AI Agent CLI — native function calling · GEMINI.md context · extensions",
5
5
  "type": "module",
6
6
  "bin": { "gemini": "./index.js" },
@@ -1,119 +0,0 @@
1
- // src/gemini.js — Gemini API client with native function calling
2
- import axios from "axios";
3
- import { fileTypeFromBuffer } from "file-type";
4
-
5
- const CONFIG = {
6
- URL: "https://us-central1-gemmy-ai-bdc03.cloudfunctions.net/gemini",
7
- MODEL: "gemini-pro-latest",
8
- HEADERS: {
9
- "User-Agent": "okhttp/5.3.2",
10
- "Accept-Encoding": "gzip",
11
- "content-type": "application/json; charset=UTF-8"
12
- }
13
- };
14
-
15
- const SUPPORTED_MIMES = new Set([
16
- "image/jpeg","image/png","image/gif","image/webp","image/heic","image/heif",
17
- "video/mp4","video/mpeg","video/mov","video/avi","video/x-flv","video/mpg",
18
- "video/webm","video/wmv","video/3gpp",
19
- "audio/wav","audio/mp3","audio/aiff","audio/aac","audio/ogg","audio/flac",
20
- "audio/mpeg","audio/ogg; codecs=opus",
21
- "application/pdf","text/plain","text/html","text/css","text/javascript",
22
- "text/x-typescript","text/csv","text/markdown","text/x-python",
23
- "application/json","application/xml","application/rtf"
24
- ]);
25
-
26
- async function getToken() {
27
- const { data } = await axios.post(
28
- "https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=AIzaSyAxof8_SbpDcww38NEQRhNh0Pzvbphh-IQ",
29
- { clientType: "CLIENT_TYPE_ANDROID" },
30
- { headers: {
31
- "User-Agent": "Dalvik/2.1.0 (Linux; U; Android 12; SM-S9280 Build/AP3A.240905.015.A2)",
32
- "Content-Type": "application/json",
33
- "X-Android-Package":"com.jetkite.gemmy",
34
- "X-Android-Cert": "037CD2976D308B4EFD63EC63C48DC6E7AB7E5AF2",
35
- "X-Firebase-GMPID": "1:652803432695:android:c4341db6033e62814f33f2"
36
- }}
37
- );
38
- return data.idToken;
39
- }
40
-
41
- /**
42
- * Core API call — supports:
43
- * - plain chat (messages only)
44
- * - file attachment (fileBuffer)
45
- * - native function calling (tools = [{ functionDeclarations: [...] }])
46
- * - function responses in history (parts with functionCall / functionResponse)
47
- */
48
- export async function callGemini({ messages = [], fileBuffer = null, tools = null, systemInstruction = null } = {}) {
49
- const token = await getToken();
50
-
51
- // Separate system message
52
- const sysMsg = messages.find(m => m.role === "system");
53
- const sysText = systemInstruction
54
- ?? (sysMsg ? (typeof sysMsg.content === "string" ? sysMsg.content : sysMsg.parts?.[0]?.text ?? "") : null);
55
-
56
- // Build contents array — support both {role,content} and {role,parts} formats
57
- const contents = messages
58
- .filter(m => m.role !== "system")
59
- .map(m => {
60
- if (m.parts) {
61
- // Already in Gemini parts format (used for functionCall / functionResponse turns)
62
- return { role: m.role === "assistant" ? "model" : m.role, parts: m.parts };
63
- }
64
- return {
65
- role: m.role === "assistant" ? "model" : m.role,
66
- parts: [{ text: typeof m.content === "string" ? m.content : JSON.stringify(m.content) }]
67
- };
68
- });
69
-
70
- // Attach file to last user message if provided
71
- if (fileBuffer) {
72
- const result = await fileTypeFromBuffer(fileBuffer);
73
- const mimeType = result?.mime ?? "application/octet-stream";
74
- if (!SUPPORTED_MIMES.has(mimeType)) throw new Error(`Unsupported file type: ${mimeType}`);
75
- const filePart = { inlineData: { mimeType, data: fileBuffer.toString("base64") } };
76
- const last = contents[contents.length - 1];
77
- if (last?.role === "user") last.parts.push(filePart);
78
- else contents.push({ role: "user", parts: [filePart] });
79
- }
80
-
81
- const payload = {
82
- model: CONFIG.MODEL,
83
- request: {
84
- contents,
85
- generationConfig: {
86
- thinkingConfig: { thinkingLevel: "HIGH" },
87
- temperature: 0
88
- },
89
- safetySettings: [
90
- { category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_NONE" },
91
- { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "BLOCK_NONE" },
92
- { category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_NONE" },
93
- { category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_NONE" }
94
- ],
95
- // ── Native function calling ──────────────────────────────────────
96
- ...(tools?.length && { tools }),
97
- // ── System instruction ───────────────────────────────────────────
98
- ...(sysText && { systemInstruction: { role: "user", parts: [{ text: sysText }] } })
99
- },
100
- stream: false
101
- };
102
-
103
- const { data } = await axios.post(CONFIG.URL, payload, {
104
- headers: { ...CONFIG.HEADERS, authorization: `Bearer ${token}` }
105
- });
106
-
107
- if (!data?.candidates?.length) throw new Error("No response candidates from API");
108
-
109
- const candidate = data.candidates[0];
110
- const parts = candidate.content?.parts ?? [];
111
-
112
- return { parts, candidate, raw: data };
113
- }
114
-
115
- /** Convenience: plain text chat (no tools) */
116
- export async function chat(messages = [], fileBuffer = null) {
117
- const { parts } = await callGemini({ messages, fileBuffer });
118
- return parts.map(p => p.text ?? "").join("");
119
- }