tmpa-cli 1.0.10 → 1.0.11

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 +67 -69
  2. package/package.json +1 -1
package/bin/index.js CHANGED
@@ -58,7 +58,7 @@ ${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 : Interactive Model Selector | /uninstall : Remove | /exit : Exit${C.reset}
61
+ ${C.gray}/config : Set API | /models : View & Select Models | /uninstall : Remove | /exit : Exit${C.reset}
62
62
  ${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
63
63
  `);
64
64
  }
@@ -109,81 +109,80 @@ function askConfig(callback) {
109
109
  });
110
110
  }
111
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
- });
112
+ function selectModelCLI(allModels) {
113
+ let filteredModels = [...allModels];
114
+ let currentPage = 0;
115
+ const pageSize = 12;
127
116
 
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
- }
117
+ function displayList() {
118
+ console.clear();
119
+ const totalPages = Math.ceil(filteredModels.length / pageSize) || 1;
120
+ if (currentPage >= totalPages) currentPage = totalPages - 1;
121
+ if (currentPage < 0) currentPage = 0;
131
122
 
132
- async function interactiveModelSelect(models) {
133
- let selectedIndex = 0;
134
- let pageOffset = 0;
135
- const pageSize = 10; // Menampilkan 10 item per halaman agar rapi
123
+ const startIdx = currentPage * pageSize;
124
+ const pageItems = filteredModels.slice(startIdx, startIdx + pageSize);
136
125
 
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);
126
+ console.log(`${C.cyan}=== Models Selector for ${config.provider} (${filteredModels.length} models) ===${C.reset}`);
127
+ console.log(`${C.gray}Current Active: ${C.green}${config.model || 'None'}${C.reset}`);
128
+ console.log(`${C.darkGray}────────────────────────────────────────────────────────────${C.reset}`);
141
129
 
142
- renderInteractiveMenu(models, selectedIndex, pageOffset, pageSize);
130
+ if (pageItems.length === 0) {
131
+ console.log(`${C.red} No models found matching filter.${C.reset}`);
132
+ } else {
133
+ pageItems.forEach((m, idx) => {
134
+ const itemNum = startIdx + idx + 1;
135
+ const isCurrent = m.id === config.model;
136
+ const bullet = isCurrent ? `${C.green}●${C.reset}` : `${C.gray}○${C.reset}`;
137
+ console.log(` ${bullet} ${C.yellow}${itemNum.toString().padStart(3, ' ')}.${C.reset} ${m.id}`);
138
+ });
139
+ }
143
140
 
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--;
141
+ console.log(`${C.darkGray}────────────────────────────────────────────────────────────${C.reset}`);
142
+ console.log(`${C.cyan}Page ${currentPage + 1}/${totalPages}${C.reset} | ${C.gray}Commands: [n]ext | [p]rev | [f]ilter <keyword> | [q]uit${C.reset}`);
143
+
144
+ rl.question(`\n${C.yellow}[>] Enter Number / Model ID / Command:${C.reset} `, (answer) => {
145
+ const input = answer.trim();
146
+
147
+ if (input.toLowerCase() === 'n') {
148
+ if (currentPage < totalPages - 1) currentPage++;
149
+ displayList();
150
+ } else if (input.toLowerCase() === 'p') {
151
+ if (currentPage > 0) currentPage--;
152
+ displayList();
153
+ } else if (input.toLowerCase() === 'q' || input.toLowerCase() === 'exit') {
154
+ console.log(`${C.gray}Model selection cancelled.${C.reset}\n`);
155
+ startPrompt();
156
+ } else if (input.toLowerCase().startsWith('f ')) {
157
+ const query = input.slice(2).trim().toLowerCase();
158
+ filteredModels = allModels.filter(m => m.id.toLowerCase().includes(query));
159
+ currentPage = 0;
160
+ displayList();
161
+ } else if (!isNaN(input) && input !== '') {
162
+ const num = parseInt(input);
163
+ if (num >= 1 && num <= filteredModels.length) {
164
+ const selected = filteredModels[num - 1].id;
165
+ config.model = selected;
166
+ saveConfig(config);
167
+ console.log(`\n${C.green}[+] Model changed to: ${config.model}${C.reset}\n`);
168
+ startPrompt();
169
+ } else {
170
+ console.log(`${C.red}[x] Invalid number!${C.reset}`);
171
+ setTimeout(displayList, 1000);
150
172
  }
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;
173
+ } else if (input.length > 0) {
174
+ // Jika memasukkan nama model spesifik secara langsung
175
+ config.model = input;
162
176
  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();
177
+ console.log(`\n${C.green}[+] Custom Model ID set to: ${config.model}${C.reset}\n`);
178
+ startPrompt();
179
+ } else {
180
+ startPrompt();
172
181
  }
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
- }
182
+ });
183
+ }
184
184
 
185
- process.stdin.on('keypress', onKeypress);
186
- });
185
+ displayList();
187
186
  }
188
187
 
189
188
  async function fetchAvailableModels() {
@@ -214,8 +213,7 @@ async function fetchAvailableModels() {
214
213
  const data = await response.json();
215
214
 
216
215
  if (data.data && Array.isArray(data.data) && data.data.length > 0) {
217
- await interactiveModelSelect(data.data);
218
- startPrompt();
216
+ selectModelCLI(data.data);
219
217
  } else {
220
218
  console.log(`${C.red}[x] Could not retrieve models list automatically from ${config.provider}.${C.reset}`);
221
219
  startPrompt();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmpa-cli",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "bin": {