tmpa-cli 1.0.9 → 1.0.10

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 (2) hide show
  1. package/bin/index.js +86 -48
  2. package/package.json +1 -1
package/bin/index.js CHANGED
@@ -58,12 +58,12 @@ ${C.c4} ██║ ██║ ╚═╝ ██║██║ ██║ █
58
58
  ${C.c4} ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝${C.reset}
59
59
  ${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
60
60
  ${C.reset}The Multi Platform AI ${C.green}[Interactive Mode]${C.reset}
61
- ${C.gray}/config : Set API | /models : View & Select Models | /uninstall : Remove | /exit : Exit${C.reset}
61
+ ${C.gray}/config : Set API | /models : Interactive Model Selector | /uninstall : Remove | /exit : Exit${C.reset}
62
62
  ${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
63
63
  `);
64
64
  }
65
65
 
66
- const rl = readline.createInterface({
66
+ let rl = readline.createInterface({
67
67
  input: process.stdin,
68
68
  output: process.stdout
69
69
  });
@@ -102,26 +102,102 @@ function askConfig(callback) {
102
102
  config.endpoint = selected.endpoint;
103
103
  config.model = selected.defaultModel;
104
104
  saveConfig(config);
105
- console.log(`${C.green}[+] Connected to ${selected.name}. Default model: ${selected.defaultModel}${C.reset}`);
106
- console.log(`${C.gray}(Tip: Run /models anytime to see all available models or type a custom model ID)${C.reset}\n`);
105
+ console.log(`${C.green}[+] Connected to ${selected.name}. Default model: ${selected.defaultModel}${C.reset}\n`);
107
106
  if (callback) callback();
108
107
  }
109
108
  });
110
109
  });
111
110
  }
112
111
 
112
+ function renderInteractiveMenu(models, selectedIndex, pageOffset, pageSize) {
113
+ console.clear();
114
+ console.log(`${C.cyan}=== Select Model for ${config.provider} ===${C.reset}`);
115
+ console.log(`${C.gray}Use Up/Down Arrow keys to navigate, press ENTER to select.${C.reset}`);
116
+ console.log(`${C.darkGray}────────────────────────────────────────────────────────────${C.reset}`);
117
+
118
+ const visibleModels = models.slice(pageOffset, pageOffset + pageSize);
119
+
120
+ visibleModels.forEach((m, idx) => {
121
+ const realIndex = pageOffset + idx;
122
+ const isSelected = realIndex === selectedIndex;
123
+ const bullet = isSelected ? `${C.green}●${C.reset}` : `${C.gray}○${C.reset}`;
124
+ const modelText = isSelected ? `${C.green}${C.c1}${m.id}${C.reset}` : `${C.gray}${m.id}${C.reset}`;
125
+ console.log(` ${bullet} ${modelText}`);
126
+ });
127
+
128
+ console.log(`${C.darkGray}────────────────────────────────────────────────────────────${C.reset}`);
129
+ console.log(`${C.yellow}Item ${selectedIndex + 1} of ${models.length}${C.reset} ${C.darkGray}| Current Active: ${config.model}${C.reset}\n`);
130
+ }
131
+
132
+ async function interactiveModelSelect(models) {
133
+ let selectedIndex = 0;
134
+ let pageOffset = 0;
135
+ const pageSize = 10; // Menampilkan 10 item per halaman agar rapi
136
+
137
+ // Matikan readline sementara untuk mengaktifkan keypress listener
138
+ rl.close();
139
+ readline.emitKeypressEvents(process.stdin);
140
+ if (process.stdin.isTTY) process.stdin.setRawMode(true);
141
+
142
+ renderInteractiveMenu(models, selectedIndex, pageOffset, pageSize);
143
+
144
+ return new Promise((resolve) => {
145
+ const onKeypress = (str, key) => {
146
+ if (key.name === 'up') {
147
+ if (selectedIndex > 0) {
148
+ selectedIndex--;
149
+ if (selectedIndex < pageOffset) pageOffset--;
150
+ }
151
+ renderInteractiveMenu(models, selectedIndex, pageOffset, pageSize);
152
+ } else if (key.name === 'down') {
153
+ if (selectedIndex < models.length - 1) {
154
+ selectedIndex++;
155
+ if (selectedIndex >= pageOffset + pageSize) pageOffset++;
156
+ }
157
+ renderInteractiveMenu(models, selectedIndex, pageOffset, pageSize);
158
+ } else if (key.name === 'return' || key.name === 'enter') {
159
+ cleanup();
160
+ const chosen = models[selectedIndex].id;
161
+ config.model = chosen;
162
+ saveConfig(config);
163
+ console.log(`\n${C.green}[+] Successfully switched to model: ${config.model}${C.reset}\n`);
164
+ resolve();
165
+ } else if (key.ctrl && key.name === 'c') {
166
+ cleanup();
167
+ process.exit(0);
168
+ } else if (key.name === 'escape') {
169
+ cleanup();
170
+ console.log(`\n${C.gray}Model selection cancelled.${C.reset}\n`);
171
+ resolve();
172
+ }
173
+ };
174
+
175
+ function cleanup() {
176
+ process.stdin.removeListener('keypress', onKeypress);
177
+ if (process.stdin.isTTY) process.stdin.setRawMode(false);
178
+ // Restart readline interface untuk percakapan selanjutnya
179
+ rl = readline.createInterface({
180
+ input: process.stdin,
181
+ output: process.stdout
182
+ });
183
+ }
184
+
185
+ process.stdin.on('keypress', onKeypress);
186
+ });
187
+ }
188
+
113
189
  async function fetchAvailableModels() {
114
190
  if (!config.apiKey) {
115
191
  console.log(`${C.red}[x] API Key is not set. Use /config first.${C.reset}\n`);
116
192
  return startPrompt();
117
193
  }
118
194
 
119
- console.log(`${C.yellow}[...] Fetching full model list from ${config.provider || 'Provider'}...${C.reset}`);
195
+ console.log(`${C.yellow}[...] Fetching models list from ${config.provider || 'Provider'}...${C.reset}`);
120
196
 
121
197
  try {
122
198
  let url = config.endpoint;
123
199
  if (url.includes('googleapis.com')) {
124
- console.log(`${C.yellow}[!] Gemini Default REST API uses endpoint-defined model: ${config.model}${C.reset}\n`);
200
+ console.log(`${C.yellow}[!] Gemini Default REST API uses fixed model: ${config.model}${C.reset}\n`);
125
201
  return startPrompt();
126
202
  }
127
203
 
@@ -137,50 +213,12 @@ async function fetchAvailableModels() {
137
213
 
138
214
  const data = await response.json();
139
215
 
140
- if (data.data && Array.isArray(data.data)) {
141
- console.log(`\n${C.cyan}=== Available Models for ${config.provider} (${data.data.length} total) ===${C.reset}`);
142
-
143
- // Ambil hingga 50 model populer/teratas untuk tampilan cepat
144
- const displayedModels = data.data.slice(0, 50);
145
- displayedModels.forEach((m, idx) => {
146
- const num = (idx + 1).toString().padStart(2, ' ');
147
- console.log(` ${C.yellow}${num}.${C.reset} ${m.id}`);
148
- });
149
-
150
- if (data.data.length > 50) {
151
- console.log(`${C.gray}... and ${data.data.length - 50} more models available on ${config.provider}.${C.reset}`);
152
- }
153
-
154
- console.log(`${C.darkGray}------------------------------------------------------------${C.reset}`);
155
- console.log(`${C.gray}Current active model: ${C.green}${config.model}${C.reset}`);
156
-
157
- rl.question(`\n${C.yellow}[>] Enter number (1-${displayedModels.length}) OR type specific Model ID (e.g. meta/llama-3.1-70b-instruct):${C.reset} `, (input) => {
158
- const val = input.trim();
159
- if (!val) {
160
- console.log(`${C.gray}Keeping current model: ${config.model}${C.reset}\n`);
161
- } else if (!isNaN(val) && parseInt(val) >= 1 && parseInt(val) <= displayedModels.length) {
162
- const selectedModel = displayedModels[parseInt(val) - 1].id;
163
- config.model = selectedModel;
164
- saveConfig(config);
165
- console.log(`${C.green}[+] Model updated to: ${config.model}${C.reset}\n`);
166
- } else {
167
- // User memasukkan ID kustom/spesifik sendiri
168
- config.model = val;
169
- saveConfig(config);
170
- console.log(`${C.green}[+] Custom Model ID saved: ${config.model}${C.reset}\n`);
171
- }
172
- startPrompt();
173
- });
216
+ if (data.data && Array.isArray(data.data) && data.data.length > 0) {
217
+ await interactiveModelSelect(data.data);
218
+ startPrompt();
174
219
  } else {
175
220
  console.log(`${C.red}[x] Could not retrieve models list automatically from ${config.provider}.${C.reset}`);
176
- rl.question(`${C.yellow}[>] Enter specific Model ID manually:${C.reset} `, (manualModel) => {
177
- if (manualModel.trim()) {
178
- config.model = manualModel.trim();
179
- saveConfig(config);
180
- console.log(`${C.green}[+] Model updated to: ${config.model}${C.reset}\n`);
181
- }
182
- startPrompt();
183
- });
221
+ startPrompt();
184
222
  }
185
223
  } catch (err) {
186
224
  console.log(`${C.red}[x] Error fetching models: ${err.message}${C.reset}\n`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmpa-cli",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "bin": {