tmpa-cli 1.0.0 β†’ 1.0.2

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.
Files changed (3) hide show
  1. package/README.md +91 -0
  2. package/bin/index.js +117 -176
  3. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # πŸš€ TMPA CLI (The Multi Platform AI)
2
+
3
+ [![npm version](https://img.shields.io/npm/v/tmpa-cli.svg?color=blue)](https://www.npmjs.com/package/tmpa-cli)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ **TMPA CLI** is an interactive Node.js-based Command Line Interface (CLI) application that allows you to chat with various AI models (such as Google Gemini API or your own Custom REST API) directly from your terminal without opening a browser.
7
+
8
+ ---
9
+
10
+ ## ✨ Key Features
11
+
12
+ - 🎨 **Modern Terminal UI**: Features a colorful futuristic ASCII Art banner and real-time loading indicators.
13
+ - 🌐 **Multi-Endpoint API**: Supports **Google Gemini API** (*default*) as well as your own **Custom REST API Endpoint**.
14
+ - βš™οΈ **Flexible Configuration**: Your API Key and Endpoint settings are securely saved in a local configuration file (`~/.tmpa_config.json`).
15
+ - ⚑ **Instant Commands**: Internal command support such as `/config`, `/clear`, and `/exit`.
16
+ - πŸͺΆ **Lightweight & Fast**: Zero heavy dependencies, runs smoothly on Termux (Android), Linux, macOS, and Windows Terminal.
17
+
18
+ ---
19
+
20
+ ## πŸ“¦ Installation
21
+
22
+ ### 1. Official Installation via npm (Recommended)
23
+
24
+ Make sure you have **Node.js** (v18+) installed in your terminal. Run the following global command:
25
+
26
+ ```bash
27
+ npm install -g tmpa-cli
28
+
29
+ 2. Installation via GitHub
30
+ ​You can also install directly from the GitHub repository:
31
+
32
+ npm install -g git+[https://github.com/zayyanathariz63-pixel/TMPA-CLI.git](https://github.com/zayyanathariz63-pixel/TMPA-CLI.git)
33
+
34
+ πŸš€ Usage
35
+ ​Once installed, simply open your terminal and type:
36
+
37
+ bash :
38
+ tmpa
39
+
40
+ πŸ”‘ First-Time Setup
41
+ ​When you run the application for the first time, you will be prompted to enter:
42
+
43
+ 1. API Key: Your API Key (e.g., from Google AI Studio).
44
+ 2. API Endpoint URL:
45
+ ​Press Enter to use the default Gemini API (gemini-2.5-flash).
46
+ ​Or enter your own Custom REST API URL.
47
+
48
+ β€‹πŸ•ΉοΈ In-CLI Commands
49
+ ​While inside the TMPA > chat session, you can use the following control commands:
50
+
51
+ Command Description
52
+ /config --> Update or change stored API Key and API Endpoint
53
+ /clear --> Clear the terminal screen and display the main banner
54
+ /exit --> Exit the TMPA CLI application
55
+
56
+ Preview Example :
57
+ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—
58
+ β•šβ•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—
59
+ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β–ˆβ–ˆβ–ˆβ–ˆβ•”β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘
60
+ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β• β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘
61
+ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β•šβ•β• β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘
62
+ β•šβ•β• β•šβ•β• β•šβ•β•β•šβ•β• β•šβ•β• β•šβ•β•
63
+ ───────────────────────────────────────────────────
64
+ The Multi Platform AI [Interactive Mode]
65
+ /config : Change API | /clear : Clear Screen | /exit : Exit
66
+ ───────────────────────────────────────────────────
67
+
68
+ TMPA > hello, who are you?
69
+ TMPA CLI processing...
70
+ TMPA CLI : I am an AI assistant ready to help you!
71
+
72
+ πŸ“ Project Structure :
73
+ tmpa-cli/
74
+ β”œβ”€β”€ bin/
75
+ β”‚ └── index.js # Main CLI script (Node.js)
76
+ β”œβ”€β”€ .gitignore # Git ignore rules
77
+ β”œβ”€β”€ package.json # npm package manifest
78
+ └── README.md # Official project documentation
79
+
80
+ ## πŸ“Œ Roadmap (Fitur Yang Akan Datang)
81
+
82
+ - [ ] πŸ” **Sistem Login Integration**: Auth langsung dari CLI ke akun TMPA.
83
+ - [ ] πŸ”— **Perintah `/connect`**: Fitur interaktif untuk memilih dan terhubung ke berbagai endpoint/website TMPA secara langsung.
84
+ - [ ] 🎨 **Tema UI Kustom**: Pilihan warna dan tampilan terminal.
85
+
86
+
87
+ πŸ“„ License
88
+ ​This project is licensed under the MIT License.
89
+ β€‹πŸ‘€ Developer
90
+ ​Developed by Zayyan Athariz - GitHub: @zayyanathariz63-pixel
91
+ ​npm: tmpa-cli
package/bin/index.js CHANGED
@@ -4,212 +4,153 @@ const readline = require('readline');
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
6
  const os = require('os');
7
+ const { execSync } = require('child_process');
7
8
 
8
- const CONFIG_PATH = path.join(os.homedir(), '.tmpa_config.json');
9
- const DEFAULT_ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent";
9
+ const CONFIG_FILE = path.join(os.homedir(), '.tmpa_config.json');
10
10
 
11
- const color = {
12
- gray: (t) => `\x1b[90m${t}\x1b[0m`,
13
- bold: (t) => `\x1b[1m${t}\x1b[0m`,
14
- yellow: (t) => `\x1b[33m${t}\x1b[0m`,
15
- green: (t) => `\x1b[32m${t}\x1b[0m`,
16
- red: (t) => `\x1b[31m${t}\x1b[0m`,
17
- rgb: (r, g, b, t) => `\x1b[38;2;${r};${g};${b}m${t}\x1b[0m`
18
- };
19
-
20
- function getConfig() {
21
- try {
22
- if (fs.existsSync(CONFIG_PATH)) {
23
- return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8'));
11
+ function loadConfig() {
12
+ if (fs.existsSync(CONFIG_FILE)) {
13
+ try {
14
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
15
+ } catch (e) {
16
+ return {};
24
17
  }
25
- } catch (err) {
26
- return null;
27
18
  }
28
- return null;
19
+ return {};
29
20
  }
30
21
 
31
22
  function saveConfig(config) {
32
- try {
33
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8');
34
- } catch (err) {
35
- console.error(color.red('Gagal menyimpan konfigurasi!'));
36
- }
37
- }
38
-
39
- async function callTmpaApi(prompt, config) {
40
- const apiKey = config.apiKey;
41
- // Gunakan fallback defaultEndpoint jika config lama belum punya apiEndpoint
42
- const apiEndpoint = config.apiEndpoint || DEFAULT_ENDPOINT;
43
-
44
- if (apiEndpoint.includes('googleapis.com')) {
45
- const url = apiEndpoint.includes('key=') ? apiEndpoint : `${apiEndpoint}?key=${apiKey}`;
46
- const response = await fetch(url, {
47
- method: 'POST',
48
- headers: { 'Content-Type': 'application/json' },
49
- body: JSON.stringify({
50
- contents: [{ parts: [{ text: prompt }] }]
51
- })
52
- });
53
-
54
- if (!response.ok) {
55
- const errData = await response.json().catch(() => ({}));
56
- throw new Error(errData.error?.message || `HTTP Error ${response.status}`);
57
- }
58
-
59
- const data = await response.json();
60
- const text = data.candidates?.[0]?.content?.parts?.[0]?.text;
61
- if (!text) throw new Error('Respons kosong dari API.');
62
- return text.trim();
63
- } else {
64
- const response = await fetch(apiEndpoint, {
65
- method: 'POST',
66
- headers: {
67
- 'Content-Type': 'application/json',
68
- 'Authorization': `Bearer ${apiKey}`,
69
- 'x-api-key': apiKey
70
- },
71
- body: JSON.stringify({ prompt: prompt, messages: [{ role: 'user', content: prompt }] })
72
- });
73
-
74
- if (!response.ok) {
75
- const errData = await response.json().catch(() => ({}));
76
- throw new Error(errData.message || errData.error || `HTTP Error ${response.status}`);
77
- }
78
-
79
- const data = await response.json();
80
- const text = data.reply || data.response || data.choices?.[0]?.message?.content || data.text || (typeof data === 'string' ? data : JSON.stringify(data));
81
- return String(text).trim();
82
- }
23
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
83
24
  }
84
25
 
85
26
  function showBanner() {
86
27
  console.clear();
87
- const logoLines = [
88
- " β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— ",
89
- " β•šβ•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—",
90
- " β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β–ˆβ–ˆβ–ˆβ–ˆβ•”β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘",
91
- " β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β• β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘",
92
- " β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β•šβ•β• β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘",
93
- " β•šβ•β• β•šβ•β• β•šβ•β•β•šβ•β• β•šβ•β• β•šβ•β•"
94
- ];
95
- const colors = [[0, 210, 255], [35, 180, 252], [70, 150, 249], [105, 120, 246], [140, 90, 243], [180, 60, 240]];
96
-
97
- console.log("");
98
- logoLines.forEach((line, i) => {
99
- const [r, g, b] = colors[i];
100
- console.log(color.rgb(r, g, b, color.bold(line)));
101
- });
102
-
103
- console.log(color.gray(` ───────────────────────────────────────────────────`));
104
- console.log(` ${color.bold('The Multi Platform AI')} ${color.green('[Interactive Mode]')}`);
105
- console.log(color.gray(` /config : Ubah API | /clear : Hapus Layar | /exit : Keluar`));
106
- console.log(color.gray(` ───────────────────────────────────────────────────\n`));
28
+ console.log(`\x1b[36m
29
+ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—
30
+ β•šβ•β•β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—
31
+ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β–ˆβ–ˆβ–ˆβ–ˆβ•”β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘
32
+ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β• β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘
33
+ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β•šβ•β• β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘
34
+ β•šβ•β• β•šβ•β• β•šβ•β•β•šβ•β• β•šβ•β• β•šβ•β•
35
+ \x1b[0m───────────────────────────────────────────────────
36
+ \x1b[32mThe Multi Platform AI [Interactive Mode]\x1b[0m
37
+ /config : Ubah API | /connect : Hubungkan Web | /uninstall : Hapus CLI | /exit : Keluar
38
+ ───────────────────────────────────────────────────\n`);
107
39
  }
108
40
 
109
- function promptConfig(callback) {
110
- console.clear();
111
- console.log(color.rgb(0, 210, 255, color.bold('\n=== KONFIGURASI TMPA CLI ===\n')));
112
- console.log('Masukkan pengaturan API milik kamu untuk melanjutkan.\n');
113
-
114
- const rlConfig = readline.createInterface({
115
- input: process.stdin,
116
- output: process.stdout
117
- });
118
-
119
- rlConfig.question(color.yellow('1. Masukkan API Key: '), (key) => {
120
- const cleanKey = key.trim();
121
- if (!cleanKey) {
122
- console.log(color.red('API Key tidak boleh kosong!\n'));
123
- rlConfig.close();
124
- return promptConfig(callback);
125
- }
126
-
127
- rlConfig.question(color.yellow(`2. Masukkan URL Endpoint API\n (Tekan Enter untuk default Gemini API): `), (endpoint) => {
128
- const cleanEndpoint = endpoint.trim() || DEFAULT_ENDPOINT;
129
-
130
- saveConfig({
131
- apiKey: cleanKey,
132
- apiEndpoint: cleanEndpoint
133
- });
134
-
135
- console.log(color.green('\nKonfigurasi disimpan!'));
136
- rlConfig.close();
137
- setTimeout(callback, 1000);
41
+ const rl = readline.createInterface({
42
+ input: process.stdin,
43
+ output: process.stdout
44
+ });
45
+
46
+ let config = loadConfig();
47
+
48
+ function askConfig(callback) {
49
+ rl.question('πŸ”‘ Masukkan API Key TMPA/Gemini: ', (apiKey) => {
50
+ rl.question('🌐 Masukkan Endpoint API (Tekan Enter untuk Default Gemini): ', (endpoint) => {
51
+ config.apiKey = apiKey.trim();
52
+ config.endpoint = endpoint.trim() || 'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent';
53
+ saveConfig(config);
54
+ console.log('βœ… Konfigurasi berhasil disimpan!\n');
55
+ if (callback) callback();
138
56
  });
139
57
  });
140
58
  }
141
59
 
142
- async function startInteractiveSession() {
143
- const config = getConfig();
144
-
145
- if (!config || !config.apiKey) {
146
- promptConfig(startInteractiveSession);
147
- return;
60
+ async function handleChat(prompt) {
61
+ if (!config.apiKey) {
62
+ console.log('⚠️ API Key belum diset!');
63
+ return askConfig(() => startPrompt());
148
64
  }
149
65
 
150
- showBanner();
66
+ console.log('\x1b[33mTMPA CLI memproses...\x1b[0m');
151
67
 
152
- const rl = readline.createInterface({
153
- input: process.stdin,
154
- output: process.stdout,
155
- prompt: color.rgb(0, 210, 255, color.bold('TMPA > '))
156
- });
68
+ try {
69
+ const url = config.endpoint.includes('googleapis.com')
70
+ ? `${config.endpoint}?key=${config.apiKey}`
71
+ : config.endpoint;
157
72
 
158
- rl.prompt();
73
+ const bodyData = config.endpoint.includes('googleapis.com')
74
+ ? { contents: [{ parts: [{ text: prompt }] }] }
75
+ : { prompt: prompt, apiKey: config.apiKey };
159
76
 
160
- rl.on('line', async (line) => {
161
- const input = line.trim();
77
+ const response = await fetch(url, {
78
+ method: 'POST',
79
+ headers: { 'Content-Type': 'application/json' },
80
+ body: JSON.stringify(bodyData)
81
+ });
162
82
 
163
- if (input === '/exit' || input === 'exit') {
164
- console.log(color.gray('\nSampai jumpa di TMPA CLI!\n'));
165
- process.exit(0);
83
+ const data = await response.json();
84
+ let reply = 'Tidak ada respon dari server.';
85
+
86
+ if (data.candidates && data.candidates[0]?.content?.parts[0]?.text) {
87
+ reply = data.candidates[0].content.parts[0].text;
88
+ } else if (data.reply) {
89
+ reply = data.reply;
90
+ } else if (data.message) {
91
+ reply = data.message;
166
92
  }
167
93
 
168
- if (input === '/clear' || input === 'clear') {
169
- showBanner();
170
- rl.prompt();
171
- return;
172
- }
94
+ console.log(`\n\x1b[34mTMPA CLI :\x1b[0m ${reply}\n`);
95
+ } catch (error) {
96
+ console.log(`\n❌ Error: ${error.message}\n`);
97
+ }
173
98
 
174
- if (input === '/config') {
175
- rl.close();
176
- promptConfig(startInteractiveSession);
177
- return;
178
- }
99
+ startPrompt();
100
+ }
179
101
 
180
- if (input === '') {
181
- rl.prompt();
182
- return;
102
+ function handleUninstall() {
103
+ rl.question('⚠️ Apakah Anda yakin ingin menghapus TMPA CLI? (y/N): ', (answer) => {
104
+ if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'ya') {
105
+ console.log('\n⏳ Menghapus TMPA CLI dan konfigurasi...');
106
+ try {
107
+ if (fs.existsSync(CONFIG_FILE)) {
108
+ fs.unlinkSync(CONFIG_FILE);
109
+ }
110
+ console.log('βœ… Konfigurasi lokal dibersihkan.');
111
+ execSync('npm uninstall -g tmpa-cli', { stdio: 'inherit' });
112
+ console.log('\nπŸ‘‹ TMPA CLI berhasil di-uninstall. Sampai jumpa!\n');
113
+ process.exit(0);
114
+ } catch (err) {
115
+ console.log('\n❌ Gagal uninstall otomatis. Jalankan: npm uninstall -g tmpa-cli secara manual.\n');
116
+ process.exit(0);
117
+ }
118
+ } else {
119
+ console.log('Batal menghapus.\n');
120
+ startPrompt();
183
121
  }
122
+ });
123
+ }
184
124
 
185
- const loadingFrames = [
186
- 'TMPA CLI memproses.',
187
- 'TMPA CLI memproses..',
188
- 'TMPA CLI memproses...',
189
- 'TMPA CLI memproses..'
190
- ];
191
-
192
- let frameIndex = 0;
193
- const loadingInterval = setInterval(() => {
194
- readline.cursorTo(process.stdout, 0);
195
- process.stdout.write(color.gray(loadingFrames[frameIndex]));
196
- frameIndex = (frameIndex + 1) % loadingFrames.length;
197
- }, 250);
198
-
199
- let answer = "";
200
- try {
201
- answer = await callTmpaApi(input, config);
202
- } catch (error) {
203
- answer = color.red(`Error: ${error.message}`);
204
- } finally {
205
- clearInterval(loadingInterval);
206
- readline.cursorTo(process.stdout, 0);
207
- readline.clearLine(process.stdout, 0);
208
- }
125
+ function startPrompt() {
126
+ rl.question('\x1b[36mTMPA >\x1b[0m ', (input) => {
127
+ const cmd = input.trim();
209
128
 
210
- console.log(`${color.bold('TMPA CLI :')} ${answer}\n`);
211
- rl.prompt();
129
+ if (cmd === '/exit') {
130
+ console.log('Sampai jumpa! πŸ‘‹');
131
+ process.exit(0);
132
+ } else if (cmd === '/clear') {
133
+ showBanner();
134
+ startPrompt();
135
+ } else if (cmd === '/config') {
136
+ askConfig(() => startPrompt());
137
+ } else if (cmd === '/connect') {
138
+ console.log('\nπŸ”— [COMING SOON] Fitur login & integrasi website TMPA akan segera hadir pada update berikutnya!\n');
139
+ startPrompt();
140
+ } else if (cmd === '/uninstall') {
141
+ handleUninstall();
142
+ } else if (cmd === '') {
143
+ startPrompt();
144
+ } else {
145
+ handleChat(cmd);
146
+ }
212
147
  });
213
148
  }
214
149
 
215
- startInteractiveSession();
150
+ // Jalankan Program
151
+ showBanner();
152
+ if (!config.apiKey) {
153
+ askConfig(() => startPrompt());
154
+ } else {
155
+ startPrompt();
156
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmpa-cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "bin": {