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.
- package/bin/index.js +86 -48
- 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 :
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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`);
|