tmpa-cli 1.0.21 → 1.0.23
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/bin/index.js +197 -116
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -39,15 +39,13 @@ const PROVIDERS = {
|
|
|
39
39
|
'3': { name: 'NVIDIA NIM', endpoint: 'https://integrate.api.nvidia.com/v1', defaultModel: 'meta/llama-3.1-70b-instruct' },
|
|
40
40
|
'4': { name: 'OpenAI', endpoint: 'https://api.openai.com/v1', defaultModel: 'gpt-4o-mini' },
|
|
41
41
|
'5': { name: 'Anthropic', endpoint: 'https://api.anthropic.com/v1', defaultModel: 'claude-3-5-haiku-20241022' },
|
|
42
|
-
'6': { name: 'Google Gemini', endpoint: 'https://generativelanguage.googleapis.com/v1beta
|
|
42
|
+
'6': { name: 'Google Gemini', endpoint: 'https://generativelanguage.googleapis.com/v1beta', defaultModel: 'gemini-2.5-flash' },
|
|
43
43
|
'7': { name: 'Custom / Auto-Detect', endpoint: '', defaultModel: '' }
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
// 100+ POPULAR MCP PRESETS LIST
|
|
47
47
|
const MCP_PRESETS = [
|
|
48
48
|
{ id: 'custom', name: '🛠️ Custom / Lainnya', category: 'General', type: 'custom', desc: 'Input Manual Name, Target, & Key' },
|
|
49
|
-
|
|
50
|
-
// Cloud, AI & UI
|
|
51
49
|
{ id: 'google-stitch', name: '⚡ Google Stitch', category: 'Cloud & AI', type: 'direct-api', endpoint: 'https://stitch.googleapis.com/v1', reqAuth: 'GOOGLE_STITCH_API_KEY', desc: 'UI & App Prototyping MCP by Google' },
|
|
52
50
|
{ id: 'supabase', name: '⚡ Supabase MCP', category: 'Cloud & AI', type: 'direct-api', endpoint: 'https://api.supabase.com/mcp', reqAuth: 'SUPABASE_ACCESS_TOKEN', desc: 'Database, Auth & Storage Management' },
|
|
53
51
|
{ id: 'cloudflare-workers', name: '☁️ Cloudflare Workers', category: 'Cloud & AI', type: 'stdio', command: 'npx -y @cloudflare/mcp-server-cloudflare', reqAuth: 'CLOUDFLARE_API_TOKEN', desc: 'Deploy & manage edge functions' },
|
|
@@ -56,30 +54,22 @@ const MCP_PRESETS = [
|
|
|
56
54
|
{ id: 'firebase', name: '🔥 Firebase MCP', category: 'Cloud & AI', type: 'stdio', command: 'npx -y firebase-mcp-server', reqAuth: 'FIREBASE_TOKEN', desc: 'Firestore, Auth & Realtime DB' },
|
|
57
55
|
{ id: 'huggingface', name: '🤗 HuggingFace Spaces', category: 'Cloud & AI', type: 'direct-api', endpoint: 'https://huggingface.co/api/mcp', reqAuth: 'HF_TOKEN', desc: 'Run open-source AI models & spaces' },
|
|
58
56
|
{ id: 'replicate', name: '🌀 Replicate MCP', category: 'Cloud & AI', type: 'stdio', command: 'npx -y @replicate/mcp-server', reqAuth: 'REPLICATE_API_TOKEN', desc: 'Run image/video AI generation models' },
|
|
59
|
-
|
|
60
|
-
// Developer Tools & VCS
|
|
61
57
|
{ id: 'github', name: '🐙 GitHub Official', category: 'Developer Tools', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-github', reqAuth: 'GITHUB_PERSONAL_ACCESS_TOKEN', desc: 'Repository, Issues, PRs & Gists' },
|
|
62
58
|
{ id: 'gitlab', name: '🦊 GitLab MCP', category: 'Developer Tools', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-gitlab', reqAuth: 'GITLAB_PERSONAL_ACCESS_TOKEN', desc: 'GitLab projects & CI/CD pipelines' },
|
|
63
59
|
{ id: 'postman', name: '🚀 Postman MCP', category: 'Developer Tools', type: 'stdio', command: 'npx -y @postman/mcp-server', reqAuth: 'POSTMAN_API_KEY', desc: 'API Collection testing & documentation' },
|
|
64
60
|
{ id: 'sentry', name: '🛡️ Sentry Error Tracker', category: 'Developer Tools', type: 'stdio', command: 'npx -y @sentry/mcp-server', reqAuth: 'SENTRY_AUTH_TOKEN', desc: 'Inspect application bugs & stacktraces' },
|
|
65
61
|
{ id: 'docker', name: '🐳 Docker Local MCP', category: 'Developer Tools', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-docker', desc: 'Manage local containers & images' },
|
|
66
|
-
|
|
67
|
-
// Databases & Storage
|
|
68
62
|
{ id: 'postgresql', name: '🗄️ PostgreSQL', category: 'Databases', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-postgres', reqAuth: 'DATABASE_URL', desc: 'Direct SQL query execution' },
|
|
69
63
|
{ id: 'mysql', name: '🐬 MySQL MCP', category: 'Databases', type: 'stdio', command: 'npx -y mysql-mcp-server', reqAuth: 'MYSQL_CONNECTION_STRING', desc: 'MySQL relational database runner' },
|
|
70
64
|
{ id: 'sqlite', name: '📦 SQLite Local', category: 'Databases', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-sqlite', desc: 'Lightweight local database query tool' },
|
|
71
65
|
{ id: 'mongodb', name: '🍃 MongoDB MCP', category: 'Databases', type: 'stdio', command: 'npx -y mongodb-mcp-server', reqAuth: 'MONGODB_URI', desc: 'NoSQL collection queries & aggregation' },
|
|
72
66
|
{ id: 'redis', name: '🔴 Redis Cache', category: 'Databases', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-redis', reqAuth: 'REDIS_URL', desc: 'Key-value data structure store' },
|
|
73
67
|
{ id: 'pinecone', name: '🌲 Pinecone Vector DB', category: 'Databases', type: 'direct-api', endpoint: 'https://api.pinecone.io/mcp', reqAuth: 'PINECONE_API_KEY', desc: 'Vector database for AI embeddings' },
|
|
74
|
-
|
|
75
|
-
// Search & Web Scraping
|
|
76
68
|
{ id: 'brave-search', name: '🦁 Brave Search', category: 'Search & Scraping', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-brave-search', reqAuth: 'BRAVE_API_KEY', desc: 'Real-time global web search engine' },
|
|
77
69
|
{ id: 'tavily', name: '🔍 Tavily AI Search', category: 'Search & Scraping', type: 'stdio', command: 'npx -y tavily-mcp', reqAuth: 'TAVILY_API_KEY', desc: 'Optimized search engine for LLMs' },
|
|
78
70
|
{ id: 'puppeteer', name: '🌐 Puppeteer Scraper', category: 'Search & Scraping', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-puppeteer', desc: 'Headless browser web automation & screenshot' },
|
|
79
71
|
{ id: 'firecrawl', name: '🔥 Firecrawl Scraper', category: 'Search & Scraping', type: 'direct-api', endpoint: 'https://api.firecrawl.dev/v1/mcp', reqAuth: 'FIRECRAWL_API_KEY', desc: 'Convert entire websites to clean Markdown' },
|
|
80
72
|
{ id: 'fetch', name: '📡 Fetch HTTP Utility', category: 'Search & Scraping', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-fetch', desc: 'HTTP request & web page parser' },
|
|
81
|
-
|
|
82
|
-
// Productivity & Communication
|
|
83
73
|
{ id: 'notion', name: '📄 Notion Workspace', category: 'Productivity', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-notion', reqAuth: 'NOTION_API_TOKEN', desc: 'Read & edit Notion pages and databases' },
|
|
84
74
|
{ id: 'slack', name: '💬 Slack Channel', category: 'Productivity', type: 'stdio', command: 'npx -y @modelcontextprotocol/server-slack', reqAuth: 'SLACK_BOT_TOKEN', desc: 'Send messages & read channels' },
|
|
85
75
|
{ id: 'discord', name: '🎮 Discord Bot MCP', category: 'Productivity', type: 'stdio', command: 'npx -y discord-mcp-server', reqAuth: 'DISCORD_BOT_TOKEN', desc: 'Interact with Discord servers & channels' },
|
|
@@ -135,7 +125,7 @@ ${C.c4} ██║ ██║ ╚═╝ ██║██║ ██║ █
|
|
|
135
125
|
${C.c4} ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝${C.reset}
|
|
136
126
|
${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
|
|
137
127
|
${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 | /
|
|
128
|
+
${C.gray}/config | /models | /custom-models | /skill | /mcp | /new-mcp | /scan | /uninstall${C.reset}
|
|
139
129
|
${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
|
|
140
130
|
`);
|
|
141
131
|
}
|
|
@@ -253,7 +243,7 @@ function showMCPHub() {
|
|
|
253
243
|
console.log('');
|
|
254
244
|
}
|
|
255
245
|
|
|
256
|
-
// INTERACTIVE ARROW-KEY SELECTOR
|
|
246
|
+
// INTERACTIVE ARROW-KEY SELECTOR UNTUK MCP PROVIDERS
|
|
257
247
|
function interactiveProviderSelector(allItems, onSelect, onCancel) {
|
|
258
248
|
let filteredItems = [...allItems];
|
|
259
249
|
let selectedIndex = 0;
|
|
@@ -369,6 +359,116 @@ function interactiveProviderSelector(allItems, onSelect, onCancel) {
|
|
|
369
359
|
renderMenu();
|
|
370
360
|
}
|
|
371
361
|
|
|
362
|
+
// INTERACTIVE ARROW-KEY SELECTOR UNTUK MODEL AI (/models) - CAPACITY HINGGA 600 MODEL
|
|
363
|
+
function interactiveModelSelector(allModels, onSelect, onCancel) {
|
|
364
|
+
// Cap pada 600 model untuk mencegah lag ekstrem di memori Termux
|
|
365
|
+
let filteredModels = allModels.slice(0, 600);
|
|
366
|
+
let selectedIndex = 0;
|
|
367
|
+
let currentPage = 0;
|
|
368
|
+
const pageSize = 10;
|
|
369
|
+
|
|
370
|
+
const wasRaw = process.stdin.isRaw;
|
|
371
|
+
|
|
372
|
+
function cleanup() {
|
|
373
|
+
process.stdin.removeListener('keypress', handleKeypress);
|
|
374
|
+
if (process.stdin.setRawMode) process.stdin.setRawMode(wasRaw || false);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function renderMenu() {
|
|
378
|
+
console.clear();
|
|
379
|
+
const totalPages = Math.ceil(filteredModels.length / pageSize) || 1;
|
|
380
|
+
if (currentPage >= totalPages) currentPage = totalPages - 1;
|
|
381
|
+
if (currentPage < 0) currentPage = 0;
|
|
382
|
+
|
|
383
|
+
const startIdx = currentPage * pageSize;
|
|
384
|
+
const pageItems = filteredModels.slice(startIdx, startIdx + pageSize);
|
|
385
|
+
|
|
386
|
+
if (selectedIndex >= pageItems.length) selectedIndex = Math.max(0, pageItems.length - 1);
|
|
387
|
+
|
|
388
|
+
console.log(`${C.c1}============================================================${C.reset}`);
|
|
389
|
+
console.log(`${C.c1} 🤖 PILIH MODEL AI (${config.provider || 'Provider'}) ${C.reset}`);
|
|
390
|
+
console.log(`${C.c1}============================================================${C.reset}`);
|
|
391
|
+
console.log(`${C.gray}Model Aktif Saat Ini: ${C.green}${config.model || 'Belum Diatur'}${C.reset}\n`);
|
|
392
|
+
|
|
393
|
+
if (pageItems.length === 0) {
|
|
394
|
+
console.log(`${C.red} Tidak ada model yang cocok dengan pencarian.${C.reset}\n`);
|
|
395
|
+
} else {
|
|
396
|
+
pageItems.forEach((m, idx) => {
|
|
397
|
+
const globalNum = startIdx + idx;
|
|
398
|
+
const isFocused = idx === selectedIndex;
|
|
399
|
+
const isCurrentActive = m.id === config.model;
|
|
400
|
+
|
|
401
|
+
const pointer = isFocused ? `${C.cyan}❯${C.reset}` : ' ';
|
|
402
|
+
const activeBadge = isCurrentActive ? `${C.green}(Aktif)${C.reset}` : '';
|
|
403
|
+
const numStr = globalNum.toString().padStart(3, ' ');
|
|
404
|
+
const modelNameStyled = isFocused ? `${C.bold}${C.yellow}${m.id}${C.reset}` : m.id;
|
|
405
|
+
|
|
406
|
+
console.log(`${pointer} ${C.gray}${numStr}.${C.reset} • ${modelNameStyled} ${activeBadge}`);
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
console.log(`\n${C.darkGray}────────────────────────────────────────────────────────────${C.reset}`);
|
|
411
|
+
console.log(`${C.cyan}Halaman ${currentPage + 1}/${totalPages}${C.reset} | Total Model Loaded: ${filteredModels.length}`);
|
|
412
|
+
console.log(`${C.gray}🎮 Navigasi: [▲/▼] Panah Termux | [Enter] Pilih${C.reset}`);
|
|
413
|
+
console.log(`${C.gray}⌨️ [f] Cari Nama Model | [n] Next | [p] Prev | [q] Batal${C.reset}`);
|
|
414
|
+
console.log(`${C.darkGray}────────────────────────────────────────────────────────────${C.reset}\n`);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
readline.emitKeypressEvents(process.stdin);
|
|
418
|
+
if (process.stdin.setRawMode) process.stdin.setRawMode(true);
|
|
419
|
+
|
|
420
|
+
function handleKeypress(str, key) {
|
|
421
|
+
const totalPages = Math.ceil(filteredModels.length / pageSize) || 1;
|
|
422
|
+
const startIdx = currentPage * pageSize;
|
|
423
|
+
const pageItems = filteredModels.slice(startIdx, startIdx + pageSize);
|
|
424
|
+
|
|
425
|
+
if (key.name === 'up') {
|
|
426
|
+
if (selectedIndex > 0) {
|
|
427
|
+
selectedIndex--;
|
|
428
|
+
} else if (currentPage > 0) {
|
|
429
|
+
currentPage--;
|
|
430
|
+
selectedIndex = pageSize - 1;
|
|
431
|
+
}
|
|
432
|
+
renderMenu();
|
|
433
|
+
} else if (key.name === 'down') {
|
|
434
|
+
if (selectedIndex < pageItems.length - 1) {
|
|
435
|
+
selectedIndex++;
|
|
436
|
+
} else if (currentPage < totalPages - 1) {
|
|
437
|
+
currentPage++;
|
|
438
|
+
selectedIndex = 0;
|
|
439
|
+
}
|
|
440
|
+
renderMenu();
|
|
441
|
+
} else if (key.name === 'return') {
|
|
442
|
+
cleanup();
|
|
443
|
+
const selectedModelObj = pageItems[selectedIndex];
|
|
444
|
+
if (selectedModelObj) onSelect(selectedModelObj.id);
|
|
445
|
+
} else if (str === 'q' || str === 'Q' || (key.ctrl && key.name === 'c')) {
|
|
446
|
+
cleanup();
|
|
447
|
+
onCancel();
|
|
448
|
+
} else if (str === 'n' || str === 'N') {
|
|
449
|
+
if (currentPage < totalPages - 1) currentPage++;
|
|
450
|
+
selectedIndex = 0;
|
|
451
|
+
renderMenu();
|
|
452
|
+
} else if (str === 'p' || str === 'P') {
|
|
453
|
+
if (currentPage > 0) currentPage--;
|
|
454
|
+
selectedIndex = 0;
|
|
455
|
+
renderMenu();
|
|
456
|
+
} else if (str === 'f' || str === 'F') {
|
|
457
|
+
cleanup();
|
|
458
|
+
rl.question(`\n${C.yellow}[>] Ketik nama/filter model (misal: llama, gemini, claude, qwen):${C.reset} `, (query) => {
|
|
459
|
+
const q = query.trim().toLowerCase();
|
|
460
|
+
filteredModels = allModels.filter(m => m.id.toLowerCase().includes(q)).slice(0, 600);
|
|
461
|
+
currentPage = 0;
|
|
462
|
+
selectedIndex = 0;
|
|
463
|
+
interactiveModelSelector(filteredModels, onSelect, onCancel);
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
process.stdin.on('keypress', handleKeypress);
|
|
469
|
+
renderMenu();
|
|
470
|
+
}
|
|
471
|
+
|
|
372
472
|
// WIZARD KONFIGURASI MCP BARU (/new-mcp)
|
|
373
473
|
function createNewMCPWizard(callback) {
|
|
374
474
|
interactiveProviderSelector(
|
|
@@ -376,7 +476,6 @@ function createNewMCPWizard(callback) {
|
|
|
376
476
|
(selectedProvider) => {
|
|
377
477
|
console.clear();
|
|
378
478
|
|
|
379
|
-
// CASE 1: CUSTOM MCP INPUT MANUAL
|
|
380
479
|
if (selectedProvider.id === 'custom') {
|
|
381
480
|
console.log(`\n${C.cyan}=== 🛠️ SETUP MCP MANUAL (CUSTOM) ===${C.reset}\n`);
|
|
382
481
|
rl.question(`${C.yellow}[1/4] Masukkan Nama MCP Server (misal: my-mcp):${C.reset} `, (nameInput) => {
|
|
@@ -421,9 +520,7 @@ function createNewMCPWizard(callback) {
|
|
|
421
520
|
});
|
|
422
521
|
});
|
|
423
522
|
});
|
|
424
|
-
}
|
|
425
|
-
// CASE 2: PRESET PROVIDER POPULER
|
|
426
|
-
else {
|
|
523
|
+
} else {
|
|
427
524
|
console.log(`\n${C.cyan}=== ⚡ SETUP PROVIDER: ${selectedProvider.name} ===${C.reset}`);
|
|
428
525
|
console.log(`${C.gray}${selectedProvider.desc}${C.reset}\n`);
|
|
429
526
|
|
|
@@ -610,7 +707,7 @@ function askConfig(callback) {
|
|
|
610
707
|
console.log(` ${C.yellow}3.${C.reset} NVIDIA NIM`);
|
|
611
708
|
console.log(` ${C.yellow}4.${C.reset} OpenAI`);
|
|
612
709
|
console.log(` ${C.yellow}5.${C.reset} Anthropic`);
|
|
613
|
-
console.log(` ${C.yellow}6.${C.reset} Google Gemini
|
|
710
|
+
console.log(` ${C.yellow}6.${C.reset} Google Gemini`);
|
|
614
711
|
console.log(` ${C.yellow}7.${C.reset} Custom / Auto-Detect Endpoint\n`);
|
|
615
712
|
|
|
616
713
|
rl.question(`${C.yellow}[>] Select Provider (1-7):${C.reset} `, (choice) => {
|
|
@@ -642,81 +739,7 @@ function askConfig(callback) {
|
|
|
642
739
|
});
|
|
643
740
|
}
|
|
644
741
|
|
|
645
|
-
|
|
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
|
-
|
|
742
|
+
// PERBAIKAN FETCH ALL MODELS UNIVERSAL UNTUK SEMUA PROVIDER
|
|
720
743
|
async function fetchAvailableModels() {
|
|
721
744
|
if (!config.apiKey) {
|
|
722
745
|
console.log(`${C.red}[x] API Key is not set. Use /config first.${C.reset}\n`);
|
|
@@ -727,35 +750,91 @@ async function fetchAvailableModels() {
|
|
|
727
750
|
|
|
728
751
|
try {
|
|
729
752
|
let url = config.endpoint;
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
return startPrompt();
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
let modelsUrl = url.endsWith('/models') ? url : `${url.replace(/\/chat\/completions$/, '')}/models`;
|
|
753
|
+
let fetchUrl = '';
|
|
754
|
+
let headers = {};
|
|
736
755
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
756
|
+
// 1. Google Gemini Native Models Fetch
|
|
757
|
+
if (url.includes('googleapis.com')) {
|
|
758
|
+
fetchUrl = `https://generativelanguage.googleapis.com/v1beta/models?key=${config.apiKey}`;
|
|
759
|
+
}
|
|
760
|
+
// 2. Anthropic Direct Fetch (jika endpoint anthropic)
|
|
761
|
+
else if (url.includes('anthropic.com')) {
|
|
762
|
+
fetchUrl = 'https://api.anthropic.com/v1/models';
|
|
763
|
+
headers = {
|
|
764
|
+
'x-api-key': config.apiKey,
|
|
765
|
+
'anthropic-version': '2023-06-01'
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
// 3. OpenRouter, Groq, NVIDIA, OpenAI & Custom Universal OpenAI Standard
|
|
769
|
+
else {
|
|
770
|
+
let baseUrl = url.replace(/\/chat\/completions$/, '').replace(/\/$/, '');
|
|
771
|
+
fetchUrl = baseUrl.endsWith('/models') ? baseUrl : `${baseUrl}/models`;
|
|
772
|
+
headers = {
|
|
740
773
|
'Authorization': `Bearer ${config.apiKey}`,
|
|
741
774
|
'Content-Type': 'application/json'
|
|
742
|
-
}
|
|
743
|
-
}
|
|
775
|
+
};
|
|
776
|
+
}
|
|
744
777
|
|
|
778
|
+
const response = await fetch(fetchUrl, { method: 'GET', headers: headers });
|
|
745
779
|
const data = await response.json();
|
|
746
780
|
|
|
747
|
-
|
|
748
|
-
|
|
781
|
+
let extractedModels = [];
|
|
782
|
+
|
|
783
|
+
// Parsing fleksibel dari berbagai bentuk JSON provider
|
|
784
|
+
if (data.data && Array.isArray(data.data)) {
|
|
785
|
+
extractedModels = data.data.map(m => ({ id: typeof m === 'string' ? m : (m.id || m.name) }));
|
|
786
|
+
} else if (data.models && Array.isArray(data.models)) {
|
|
787
|
+
extractedModels = data.models.map(m => ({ id: typeof m === 'string' ? m : (m.name?.replace('models/', '') || m.id) }));
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
if (extractedModels.length > 0) {
|
|
791
|
+
interactiveModelSelector(
|
|
792
|
+
extractedModels,
|
|
793
|
+
(selectedModelId) => {
|
|
794
|
+
config.model = selectedModelId;
|
|
795
|
+
saveConfig(config);
|
|
796
|
+
console.clear();
|
|
797
|
+
console.log(`${C.green}[✔] Model berhasil diubah ke: ${config.model}${C.reset}\n`);
|
|
798
|
+
startPrompt();
|
|
799
|
+
},
|
|
800
|
+
() => {
|
|
801
|
+
console.clear();
|
|
802
|
+
console.log(`${C.gray}Pemilihan model dibatalkan.${C.reset}\n`);
|
|
803
|
+
startPrompt();
|
|
804
|
+
}
|
|
805
|
+
);
|
|
749
806
|
} else {
|
|
750
|
-
console.log(`${C.red}[x]
|
|
807
|
+
console.log(`${C.red}[x] Gagal mengambil daftar model secara otomatis dari ${config.provider}.${C.reset}`);
|
|
808
|
+
console.log(`${C.yellow}💡 Tips: Gunakan perintah ${C.bold}/custom-models${C.reset}${C.yellow} untuk menginput nama model secara manual dengan presisi.${C.reset}\n`);
|
|
751
809
|
startPrompt();
|
|
752
810
|
}
|
|
753
811
|
} catch (err) {
|
|
754
|
-
console.log(`${C.red}[x] Error fetching models: ${err.message}${C.reset}
|
|
812
|
+
console.log(`${C.red}[x] Error fetching models: ${err.message}${C.reset}`);
|
|
813
|
+
console.log(`${C.yellow}💡 Kamu bisa menggunakan perintah ${C.bold}/custom-models${C.reset}${C.yellow} untuk memasukkan nama model manual.${C.reset}\n`);
|
|
755
814
|
startPrompt();
|
|
756
815
|
}
|
|
757
816
|
}
|
|
758
817
|
|
|
818
|
+
// PERINTAH BARU: /custom-models UNTUK PRESISI NAMA MODEL
|
|
819
|
+
function handleCustomModels() {
|
|
820
|
+
console.log(`\n${C.cyan}=== 🎯 SET CUSTOM MODEL (PRESISI HIGH-PRECISION) ===${C.reset}`);
|
|
821
|
+
console.log(`${C.gray}Provider Aktif : ${C.yellow}${config.provider || 'Belum diatur'}${C.reset}`);
|
|
822
|
+
console.log(`${C.gray}Model Saat Ini : ${C.green}${config.model || 'Belum diatur'}${C.reset}\n`);
|
|
823
|
+
|
|
824
|
+
rl.question(`${C.yellow}[>] Masukkan ID / Nama Model secara presisi sesuai provider:${C.reset} `, (inputModel) => {
|
|
825
|
+
const cleanModel = inputModel.trim();
|
|
826
|
+
if (!cleanModel) {
|
|
827
|
+
console.log(`${C.red}[x] Nama model tidak boleh kosong.${C.reset}\n`);
|
|
828
|
+
return startPrompt();
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
config.model = cleanModel;
|
|
832
|
+
saveConfig(config);
|
|
833
|
+
console.log(`\n${C.green}[✔] Model berhasil diperbarui secara presisi ke: ${C.bold}${config.model}${C.reset}\n`);
|
|
834
|
+
startPrompt();
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
|
|
759
838
|
async function handleChat(prompt, forcedTool = null) {
|
|
760
839
|
if (!config.apiKey) {
|
|
761
840
|
console.log(`${C.yellow}[!] API Key is not set.${C.reset}`);
|
|
@@ -777,7 +856,8 @@ async function handleChat(prompt, forcedTool = null) {
|
|
|
777
856
|
const fullPrompt = (prompt || "Jalankan instruksi tool ini.") + toolsContext;
|
|
778
857
|
|
|
779
858
|
if (url.includes('googleapis.com')) {
|
|
780
|
-
|
|
859
|
+
const activeModel = config.model || 'gemini-2.5-flash';
|
|
860
|
+
url = `https://generativelanguage.googleapis.com/v1beta/models/${activeModel}:generateContent?key=${config.apiKey}`;
|
|
781
861
|
bodyData = { contents: [{ parts: [{ text: fullPrompt }] }] };
|
|
782
862
|
} else {
|
|
783
863
|
if (!url.endsWith('/chat/completions')) {
|
|
@@ -800,7 +880,6 @@ async function handleChat(prompt, forcedTool = null) {
|
|
|
800
880
|
|
|
801
881
|
let aiResponse = "";
|
|
802
882
|
|
|
803
|
-
// DUKUNGAN UNTUK REASONING MODELS & STANDAR OPENAI/GEMINI
|
|
804
883
|
if (data.choices && data.choices[0] && data.choices[0].message) {
|
|
805
884
|
const msg = data.choices[0].message;
|
|
806
885
|
aiResponse = msg.content || msg.reasoning || msg.reasoning_content || "";
|
|
@@ -824,23 +903,23 @@ async function handleChat(prompt, forcedTool = null) {
|
|
|
824
903
|
}
|
|
825
904
|
|
|
826
905
|
function handleUninstall() {
|
|
827
|
-
rl.question(`${C.red}[!]
|
|
906
|
+
rl.question(`${C.red}[!] Apakah kamu yakin ingin menghapus TMPA CLI? (y/N):${C.reset} `, (answer) => {
|
|
828
907
|
if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
829
|
-
console.log(`\n${C.yellow}[...]
|
|
908
|
+
console.log(`\n${C.yellow}[...] Membersihkan konfigurasi dan menghapus TMPA CLI...${C.reset}`);
|
|
830
909
|
try {
|
|
831
910
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
832
911
|
fs.unlinkSync(CONFIG_FILE);
|
|
833
912
|
}
|
|
834
|
-
console.log(`${C.green}[+]
|
|
913
|
+
console.log(`${C.green}[+] Konfigurasi lokal berhasil dibersihkan.${C.reset}`);
|
|
835
914
|
execSync('npm uninstall -g tmpa-cli', { stdio: 'inherit' });
|
|
836
|
-
console.log(`\n${C.green}[+] TMPA CLI
|
|
915
|
+
console.log(`\n${C.green}[+] TMPA CLI berhasil di-uninstall sepenuhnya. Sampai jumpa!${C.reset}\n`);
|
|
837
916
|
process.exit(0);
|
|
838
917
|
} catch (err) {
|
|
839
|
-
console.log(`\n${C.red}[x]
|
|
918
|
+
console.log(`\n${C.red}[x] Gagal uninstall otomatis. Silakan jalankan: npm uninstall -g tmpa-cli secara manual.${C.reset}\n`);
|
|
840
919
|
process.exit(0);
|
|
841
920
|
}
|
|
842
921
|
} else {
|
|
843
|
-
console.log(`${C.gray}
|
|
922
|
+
console.log(`${C.gray}Proses uninstall dibatalkan.${C.reset}\n`);
|
|
844
923
|
startPrompt();
|
|
845
924
|
}
|
|
846
925
|
});
|
|
@@ -860,6 +939,8 @@ function startPrompt() {
|
|
|
860
939
|
askConfig(() => startPrompt());
|
|
861
940
|
} else if (cmd === '/models') {
|
|
862
941
|
fetchAvailableModels();
|
|
942
|
+
} else if (cmd === '/custom-models') {
|
|
943
|
+
handleCustomModels();
|
|
863
944
|
} else if (cmd === '/skill' || cmd === '/skills') {
|
|
864
945
|
listSkills();
|
|
865
946
|
startPrompt();
|