tmpa-cli 1.0.21 → 1.0.22

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 +132 -84
  2. package/package.json +1 -1
package/bin/index.js CHANGED
@@ -135,7 +135,7 @@ ${C.c4} ██║ ██║ ╚═╝ ██║██║ ██║ █
135
135
  ${C.c4} ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝${C.reset}
136
136
  ${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
137
137
  ${C.reset}The Multi Platform AI ${C.green}[MCP Hub & Interactive Provider Engine]${C.reset}
138
- ${C.gray}/config | /models | /skill | /mcp | /new-mcp | /scan | /exit${C.reset}
138
+ ${C.gray}/config | /models | /skill | /mcp | /new-mcp | /scan | /uninstall | /exit${C.reset}
139
139
  ${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
140
140
  `);
141
141
  }
@@ -253,7 +253,7 @@ function showMCPHub() {
253
253
  console.log('');
254
254
  }
255
255
 
256
- // INTERACTIVE ARROW-KEY SELECTOR FOR TERMUX & COMPUTERS
256
+ // INTERACTIVE ARROW-KEY SELECTOR UNTUK MCP PROVIDERS
257
257
  function interactiveProviderSelector(allItems, onSelect, onCancel) {
258
258
  let filteredItems = [...allItems];
259
259
  let selectedIndex = 0;
@@ -369,6 +369,115 @@ function interactiveProviderSelector(allItems, onSelect, onCancel) {
369
369
  renderMenu();
370
370
  }
371
371
 
372
+ // INTERACTIVE ARROW-KEY SELECTOR UNTUK MODEL AI (/models)
373
+ function interactiveModelSelector(allModels, onSelect, onCancel) {
374
+ let filteredModels = [...allModels];
375
+ let selectedIndex = 0;
376
+ let currentPage = 0;
377
+ const pageSize = 10;
378
+
379
+ const wasRaw = process.stdin.isRaw;
380
+
381
+ function cleanup() {
382
+ process.stdin.removeListener('keypress', handleKeypress);
383
+ if (process.stdin.setRawMode) process.stdin.setRawMode(wasRaw || false);
384
+ }
385
+
386
+ function renderMenu() {
387
+ console.clear();
388
+ const totalPages = Math.ceil(filteredModels.length / pageSize) || 1;
389
+ if (currentPage >= totalPages) currentPage = totalPages - 1;
390
+ if (currentPage < 0) currentPage = 0;
391
+
392
+ const startIdx = currentPage * pageSize;
393
+ const pageItems = filteredModels.slice(startIdx, startIdx + pageSize);
394
+
395
+ if (selectedIndex >= pageItems.length) selectedIndex = Math.max(0, pageItems.length - 1);
396
+
397
+ console.log(`${C.c1}============================================================${C.reset}`);
398
+ console.log(`${C.c1} 🤖 PILIH MODEL AI (${config.provider || 'Provider'}) ${C.reset}`);
399
+ console.log(`${C.c1}============================================================${C.reset}`);
400
+ console.log(`${C.gray}Model Aktif Saat Ini: ${C.green}${config.model || 'Belum Diatur'}${C.reset}\n`);
401
+
402
+ if (pageItems.length === 0) {
403
+ console.log(`${C.red} Tidak ada model yang cocok dengan pencarian.${C.reset}\n`);
404
+ } else {
405
+ pageItems.forEach((m, idx) => {
406
+ const globalNum = startIdx + idx;
407
+ const isFocused = idx === selectedIndex;
408
+ const isCurrentActive = m.id === config.model;
409
+
410
+ const pointer = isFocused ? `${C.cyan}❯${C.reset}` : ' ';
411
+ const activeBadge = isCurrentActive ? `${C.green}(Aktif)${C.reset}` : '';
412
+ const numStr = globalNum.toString().padStart(3, ' ');
413
+ const modelNameStyled = isFocused ? `${C.bold}${C.yellow}${m.id}${C.reset}` : m.id;
414
+
415
+ console.log(`${pointer} ${C.gray}${numStr}.${C.reset} • ${modelNameStyled} ${activeBadge}`);
416
+ });
417
+ }
418
+
419
+ console.log(`\n${C.darkGray}────────────────────────────────────────────────────────────${C.reset}`);
420
+ console.log(`${C.cyan}Halaman ${currentPage + 1}/${totalPages}${C.reset} | Total: ${filteredModels.length} Model AI`);
421
+ console.log(`${C.gray}🎮 Navigasi: [▲/▼] Panah Termux/Keyboard | [Enter] Pilih${C.reset}`);
422
+ console.log(`${C.gray}⌨️ Atau: [f] Cari Nama Model | [n] Next | [p] Prev | [q] Batal${C.reset}`);
423
+ console.log(`${C.darkGray}────────────────────────────────────────────────────────────${C.reset}\n`);
424
+ }
425
+
426
+ readline.emitKeypressEvents(process.stdin);
427
+ if (process.stdin.setRawMode) process.stdin.setRawMode(true);
428
+
429
+ function handleKeypress(str, key) {
430
+ const totalPages = Math.ceil(filteredModels.length / pageSize) || 1;
431
+ const startIdx = currentPage * pageSize;
432
+ const pageItems = filteredModels.slice(startIdx, startIdx + pageSize);
433
+
434
+ if (key.name === 'up') {
435
+ if (selectedIndex > 0) {
436
+ selectedIndex--;
437
+ } else if (currentPage > 0) {
438
+ currentPage--;
439
+ selectedIndex = pageSize - 1;
440
+ }
441
+ renderMenu();
442
+ } else if (key.name === 'down') {
443
+ if (selectedIndex < pageItems.length - 1) {
444
+ selectedIndex++;
445
+ } else if (currentPage < totalPages - 1) {
446
+ currentPage++;
447
+ selectedIndex = 0;
448
+ }
449
+ renderMenu();
450
+ } else if (key.name === 'return') {
451
+ cleanup();
452
+ const selectedModelObj = pageItems[selectedIndex];
453
+ if (selectedModelObj) onSelect(selectedModelObj.id);
454
+ } else if (str === 'q' || str === 'Q' || (key.ctrl && key.name === 'c')) {
455
+ cleanup();
456
+ onCancel();
457
+ } else if (str === 'n' || str === 'N') {
458
+ if (currentPage < totalPages - 1) currentPage++;
459
+ selectedIndex = 0;
460
+ renderMenu();
461
+ } else if (str === 'p' || str === 'P') {
462
+ if (currentPage > 0) currentPage--;
463
+ selectedIndex = 0;
464
+ renderMenu();
465
+ } else if (str === 'f' || str === 'F') {
466
+ cleanup();
467
+ rl.question(`\n${C.yellow}[>] Ketik kata kunci nama model (misal: llama, gemini, claude):${C.reset} `, (query) => {
468
+ const q = query.trim().toLowerCase();
469
+ filteredModels = allModels.filter(m => m.id.toLowerCase().includes(q));
470
+ currentPage = 0;
471
+ selectedIndex = 0;
472
+ interactiveModelSelector(filteredModels, onSelect, onCancel);
473
+ });
474
+ }
475
+ }
476
+
477
+ process.stdin.on('keypress', handleKeypress);
478
+ renderMenu();
479
+ }
480
+
372
481
  // WIZARD KONFIGURASI MCP BARU (/new-mcp)
373
482
  function createNewMCPWizard(callback) {
374
483
  interactiveProviderSelector(
@@ -642,81 +751,6 @@ function askConfig(callback) {
642
751
  });
643
752
  }
644
753
 
645
- function selectModelCLI(allModels) {
646
- let filteredModels = [...allModels];
647
- let currentPage = 0;
648
- const pageSize = 12;
649
-
650
- function displayList() {
651
- console.clear();
652
- const totalPages = Math.ceil(filteredModels.length / pageSize) || 1;
653
- if (currentPage >= totalPages) currentPage = totalPages - 1;
654
- if (currentPage < 0) currentPage = 0;
655
-
656
- const startIdx = currentPage * pageSize;
657
- const pageItems = filteredModels.slice(startIdx, startIdx + pageSize);
658
-
659
- console.log(`${C.cyan}=== Models Selector for ${config.provider} (${filteredModels.length} models) ===${C.reset}`);
660
- console.log(`${C.gray}Current Active: ${C.green}${config.model || 'None'}${C.reset}`);
661
- console.log(`${C.darkGray}────────────────────────────────────────────────────────────${C.reset}`);
662
-
663
- if (pageItems.length === 0) {
664
- console.log(`${C.red} No models found matching filter.${C.reset}`);
665
- } else {
666
- pageItems.forEach((m, idx) => {
667
- const itemNum = startIdx + idx + 1;
668
- const isCurrent = m.id === config.model;
669
- const bullet = isCurrent ? `${C.green}●${C.reset}` : `${C.gray}○${C.reset}`;
670
- console.log(` ${bullet} ${C.yellow}${itemNum.toString().padStart(3, ' ')}.${C.reset} ${m.id}`);
671
- });
672
- }
673
-
674
- console.log(`${C.darkGray}────────────────────────────────────────────────────────────${C.reset}`);
675
- console.log(`${C.cyan}Page ${currentPage + 1}/${totalPages}${C.reset} | ${C.gray}Commands: [n]ext | [p]rev | [f]ilter <keyword> | [q]uit${C.reset}`);
676
-
677
- rl.question(`\n${C.yellow}[>] Enter Number / Model ID / Command:${C.reset} `, (answer) => {
678
- const input = answer.trim();
679
-
680
- if (input.toLowerCase() === 'n') {
681
- if (currentPage < totalPages - 1) currentPage++;
682
- displayList();
683
- } else if (input.toLowerCase() === 'p') {
684
- if (currentPage > 0) currentPage--;
685
- displayList();
686
- } else if (input.toLowerCase() === 'q' || input.toLowerCase() === 'exit') {
687
- console.log(`${C.gray}Model selection cancelled.${C.reset}\n`);
688
- startPrompt();
689
- } else if (input.toLowerCase().startsWith('f ')) {
690
- const query = input.slice(2).trim().toLowerCase();
691
- filteredModels = allModels.filter(m => m.id.toLowerCase().includes(query));
692
- currentPage = 0;
693
- displayList();
694
- } else if (!isNaN(input) && input !== '') {
695
- const num = parseInt(input);
696
- if (num >= 1 && num <= filteredModels.length) {
697
- const selected = filteredModels[num - 1].id;
698
- config.model = selected;
699
- saveConfig(config);
700
- console.log(`\n${C.green}[+] Model changed to: ${config.model}${C.reset}\n`);
701
- startPrompt();
702
- } else {
703
- console.log(`${C.red}[x] Invalid number!${C.reset}`);
704
- setTimeout(displayList, 1000);
705
- }
706
- } else if (input.length > 0) {
707
- config.model = input;
708
- saveConfig(config);
709
- console.log(`\n${C.green}[+] Custom Model ID set to: ${config.model}${C.reset}\n`);
710
- startPrompt();
711
- } else {
712
- startPrompt();
713
- }
714
- });
715
- }
716
-
717
- displayList();
718
- }
719
-
720
754
  async function fetchAvailableModels() {
721
755
  if (!config.apiKey) {
722
756
  console.log(`${C.red}[x] API Key is not set. Use /config first.${C.reset}\n`);
@@ -745,7 +779,21 @@ async function fetchAvailableModels() {
745
779
  const data = await response.json();
746
780
 
747
781
  if (data.data && Array.isArray(data.data) && data.data.length > 0) {
748
- selectModelCLI(data.data);
782
+ interactiveModelSelector(
783
+ data.data,
784
+ (selectedModelId) => {
785
+ config.model = selectedModelId;
786
+ saveConfig(config);
787
+ console.clear();
788
+ console.log(`${C.green}[✔] Model berhasil diubah ke: ${config.model}${C.reset}\n`);
789
+ startPrompt();
790
+ },
791
+ () => {
792
+ console.clear();
793
+ console.log(`${C.gray}Pemilihan model dibatalkan.${C.reset}\n`);
794
+ startPrompt();
795
+ }
796
+ );
749
797
  } else {
750
798
  console.log(`${C.red}[x] Could not retrieve models list automatically from ${config.provider}.${C.reset}`);
751
799
  startPrompt();
@@ -824,23 +872,23 @@ async function handleChat(prompt, forcedTool = null) {
824
872
  }
825
873
 
826
874
  function handleUninstall() {
827
- rl.question(`${C.red}[!] Are you sure you want to uninstall TMPA CLI? (y/N):${C.reset} `, (answer) => {
875
+ rl.question(`${C.red}[!] Apakah kamu yakin ingin menghapus TMPA CLI? (y/N):${C.reset} `, (answer) => {
828
876
  if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
829
- console.log(`\n${C.yellow}[...] Removing TMPA CLI and cleaning configuration...${C.reset}`);
877
+ console.log(`\n${C.yellow}[...] Membersihkan konfigurasi dan menghapus TMPA CLI...${C.reset}`);
830
878
  try {
831
879
  if (fs.existsSync(CONFIG_FILE)) {
832
880
  fs.unlinkSync(CONFIG_FILE);
833
881
  }
834
- console.log(`${C.green}[+] Local configuration cleared.${C.reset}`);
882
+ console.log(`${C.green}[+] Konfigurasi lokal berhasil dibersihkan.${C.reset}`);
835
883
  execSync('npm uninstall -g tmpa-cli', { stdio: 'inherit' });
836
- console.log(`\n${C.green}[+] TMPA CLI uninstalled successfully. Goodbye!${C.reset}\n`);
884
+ console.log(`\n${C.green}[+] TMPA CLI berhasil di-uninstall sepenuhnya. Sampai jumpa!${C.reset}\n`);
837
885
  process.exit(0);
838
886
  } catch (err) {
839
- console.log(`\n${C.red}[x] Failed to uninstall automatically. Run: npm uninstall -g tmpa-cli manually.${C.reset}\n`);
887
+ console.log(`\n${C.red}[x] Gagal uninstall otomatis. Silakan jalankan: npm uninstall -g tmpa-cli secara manual.${C.reset}\n`);
840
888
  process.exit(0);
841
889
  }
842
890
  } else {
843
- console.log(`${C.gray}Uninstall process cancelled.${C.reset}\n`);
891
+ console.log(`${C.gray}Proses uninstall dibatalkan.${C.reset}\n`);
844
892
  startPrompt();
845
893
  }
846
894
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tmpa-cli",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "bin": {