tmpa-cli 1.0.11 → 1.0.12
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 +137 -15
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -6,7 +6,15 @@ const path = require('path');
|
|
|
6
6
|
const os = require('os');
|
|
7
7
|
const { execSync } = require('child_process');
|
|
8
8
|
|
|
9
|
+
const TMPA_DIR = path.join(os.homedir(), '.tmpa');
|
|
10
|
+
const SKILLS_DIR = path.join(TMPA_DIR, 'skills');
|
|
11
|
+
const MCP_DIR = path.join(TMPA_DIR, 'mcp');
|
|
9
12
|
const CONFIG_FILE = path.join(os.homedir(), '.tmpa_config.json');
|
|
13
|
+
const REGISTRY_FILE = path.join(TMPA_DIR, 'registry.json');
|
|
14
|
+
|
|
15
|
+
if (!fs.existsSync(TMPA_DIR)) fs.mkdirSync(TMPA_DIR, { recursive: true });
|
|
16
|
+
if (!fs.existsSync(SKILLS_DIR)) fs.mkdirSync(SKILLS_DIR, { recursive: true });
|
|
17
|
+
if (!fs.existsSync(MCP_DIR)) fs.mkdirSync(MCP_DIR, { recursive: true });
|
|
10
18
|
|
|
11
19
|
const C = {
|
|
12
20
|
reset: '\x1b[0m',
|
|
@@ -34,11 +42,7 @@ const PROVIDERS = {
|
|
|
34
42
|
|
|
35
43
|
function loadConfig() {
|
|
36
44
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
37
|
-
try {
|
|
38
|
-
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
39
|
-
} catch (e) {
|
|
40
|
-
return {};
|
|
41
|
-
}
|
|
45
|
+
try { return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); } catch (e) { return {}; }
|
|
42
46
|
}
|
|
43
47
|
return {};
|
|
44
48
|
}
|
|
@@ -47,6 +51,21 @@ function saveConfig(config) {
|
|
|
47
51
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
48
52
|
}
|
|
49
53
|
|
|
54
|
+
function loadRegistry() {
|
|
55
|
+
if (fs.existsSync(REGISTRY_FILE)) {
|
|
56
|
+
try { return JSON.parse(fs.readFileSync(REGISTRY_FILE, 'utf8')); } catch (e) { return { skills: {}, mcp: {} }; }
|
|
57
|
+
}
|
|
58
|
+
return { skills: {}, mcp: {} };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function saveRegistry(registry) {
|
|
62
|
+
fs.writeFileSync(REGISTRY_FILE, JSON.stringify(registry, null, 2));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
66
|
+
let config = loadConfig();
|
|
67
|
+
let registry = loadRegistry();
|
|
68
|
+
|
|
50
69
|
function showBanner() {
|
|
51
70
|
console.clear();
|
|
52
71
|
console.log(`
|
|
@@ -58,18 +77,11 @@ ${C.c4} ██║ ██║ ╚═╝ ██║██║ ██║ █
|
|
|
58
77
|
${C.c4} ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝${C.reset}
|
|
59
78
|
${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
|
|
60
79
|
${C.reset}The Multi Platform AI ${C.green}[Interactive Mode]${C.reset}
|
|
61
|
-
${C.gray}/config
|
|
80
|
+
${C.gray}/config | /models | /skill | /mcp | /connect | /scan | /exit${C.reset}
|
|
62
81
|
${C.darkGray}────────────────────────────────────────────────────────────${C.reset}
|
|
63
82
|
`);
|
|
64
83
|
}
|
|
65
84
|
|
|
66
|
-
let rl = readline.createInterface({
|
|
67
|
-
input: process.stdin,
|
|
68
|
-
output: process.stdout
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
let config = loadConfig();
|
|
72
|
-
|
|
73
85
|
function askConfig(callback) {
|
|
74
86
|
console.log(`\n${C.cyan}[+] Choose AI Provider:${C.reset}`);
|
|
75
87
|
console.log(` ${C.yellow}1.${C.reset} OpenRouter`);
|
|
@@ -171,7 +183,6 @@ function selectModelCLI(allModels) {
|
|
|
171
183
|
setTimeout(displayList, 1000);
|
|
172
184
|
}
|
|
173
185
|
} else if (input.length > 0) {
|
|
174
|
-
// Jika memasukkan nama model spesifik secara langsung
|
|
175
186
|
config.model = input;
|
|
176
187
|
saveConfig(config);
|
|
177
188
|
console.log(`\n${C.green}[+] Custom Model ID set to: ${config.model}${C.reset}\n`);
|
|
@@ -298,6 +309,106 @@ function handleUninstall() {
|
|
|
298
309
|
});
|
|
299
310
|
}
|
|
300
311
|
|
|
312
|
+
function listSkills() {
|
|
313
|
+
console.log(`\n${C.cyan}=== Registered Skills ===${C.reset}`);
|
|
314
|
+
const keys = Object.keys(registry.skills || {});
|
|
315
|
+
if (keys.length === 0) {
|
|
316
|
+
console.log(`${C.gray}Belum ada skill yang terhubung. Gunakan ${C.yellow}/connect skill <path>${C.gray} untuk menghubungkan.${C.reset}\n`);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
keys.forEach((name, i) => {
|
|
320
|
+
const item = registry.skills[name];
|
|
321
|
+
console.log(` ${C.yellow}${i + 1}.${C.reset} ${C.green}${name}${C.reset} -> ${C.gray}${item.path}${C.reset} [${item.status || 'Active'}]`);
|
|
322
|
+
});
|
|
323
|
+
console.log('');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function listMCP() {
|
|
327
|
+
console.log(`\n${C.cyan}=== Registered MCP (Model Context Protocol) Servers ===${C.reset}`);
|
|
328
|
+
const keys = Object.keys(registry.mcp || {});
|
|
329
|
+
if (keys.length === 0) {
|
|
330
|
+
console.log(`${C.gray}Belum ada MCP server terhubung. Gunakan ${C.yellow}/connect mcp <target>${C.gray} untuk menghubungkan.${C.reset}\n`);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
keys.forEach((name, i) => {
|
|
334
|
+
const item = registry.mcp[name];
|
|
335
|
+
console.log(` ${C.yellow}${i + 1}.${C.reset} ${C.green}${name}${C.reset} -> ${C.gray}${item.target}${C.reset} [${item.type || 'local'}]`);
|
|
336
|
+
});
|
|
337
|
+
console.log('');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function connectResource(inputArgs) {
|
|
341
|
+
const parts = inputArgs.trim().split(/\s+/);
|
|
342
|
+
const type = parts[0]?.toLowerCase();
|
|
343
|
+
const targetPath = parts.slice(1).join(' ');
|
|
344
|
+
|
|
345
|
+
if (!type || !targetPath) {
|
|
346
|
+
console.log(`${C.red}[x] Format salah! Gunakan:${C.reset}`);
|
|
347
|
+
console.log(` ${C.yellow}/connect skill <filepath_atau_folder>${C.reset}`);
|
|
348
|
+
console.log(` ${C.yellow}/connect mcp <filepath_atau_url>${C.reset}\n`);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const resolvedPath = path.resolve(targetPath);
|
|
353
|
+
|
|
354
|
+
if (type === 'skill') {
|
|
355
|
+
const skillName = path.basename(resolvedPath, path.extname(resolvedPath));
|
|
356
|
+
registry.skills = registry.skills || {};
|
|
357
|
+
registry.skills[skillName] = {
|
|
358
|
+
path: resolvedPath,
|
|
359
|
+
connectedAt: new Date().toISOString(),
|
|
360
|
+
status: 'Active'
|
|
361
|
+
};
|
|
362
|
+
saveRegistry(registry);
|
|
363
|
+
console.log(`${C.green}[+] Skill "${skillName}" berhasil dihubungkan dari: ${resolvedPath}${C.reset}\n`);
|
|
364
|
+
} else if (type === 'mcp') {
|
|
365
|
+
const mcpName = path.basename(resolvedPath, path.extname(resolvedPath));
|
|
366
|
+
registry.mcp = registry.mcp || {};
|
|
367
|
+
registry.mcp[mcpName] = {
|
|
368
|
+
target: targetPath,
|
|
369
|
+
type: targetPath.startsWith('http') ? 'remote' : 'local',
|
|
370
|
+
connectedAt: new Date().toISOString(),
|
|
371
|
+
status: 'Connected'
|
|
372
|
+
};
|
|
373
|
+
saveRegistry(registry);
|
|
374
|
+
console.log(`${C.green}[+] MCP Server "${mcpName}" berhasil dihubungkan!${C.reset}\n`);
|
|
375
|
+
} else {
|
|
376
|
+
console.log(`${C.red}[x] Tipe tidak dikenal. Gunakan "skill" atau "mcp".${C.reset}\n`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function scanResources() {
|
|
381
|
+
console.log(`${C.yellow}[...] Memindai folder ~/.tmpa/skills dan ~/.tmpa/mcp...${C.reset}`);
|
|
382
|
+
registry = loadRegistry();
|
|
383
|
+
|
|
384
|
+
if (fs.existsSync(SKILLS_DIR)) {
|
|
385
|
+
const files = fs.readdirSync(SKILLS_DIR);
|
|
386
|
+
files.forEach(file => {
|
|
387
|
+
const fullPath = path.join(SKILLS_DIR, file);
|
|
388
|
+
const name = path.basename(file, path.extname(file));
|
|
389
|
+
if (!registry.skills[name]) {
|
|
390
|
+
registry.skills[name] = { path: fullPath, connectedAt: new Date().toISOString(), status: 'Active' };
|
|
391
|
+
console.log(`${C.green}[+] Auto-detected skill: ${name}${C.reset}`);
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (fs.existsSync(MCP_DIR)) {
|
|
397
|
+
const files = fs.readdirSync(MCP_DIR);
|
|
398
|
+
files.forEach(file => {
|
|
399
|
+
const fullPath = path.join(MCP_DIR, file);
|
|
400
|
+
const name = path.basename(file, path.extname(file));
|
|
401
|
+
if (!registry.mcp[name]) {
|
|
402
|
+
registry.mcp[name] = { target: fullPath, type: 'local', connectedAt: new Date().toISOString(), status: 'Connected' };
|
|
403
|
+
console.log(`${C.green}[+] Auto-detected MCP: ${name}${C.reset}`);
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
saveRegistry(registry);
|
|
409
|
+
console.log(`${C.green}[+] Pemindaian selesai.${C.reset}\n`);
|
|
410
|
+
}
|
|
411
|
+
|
|
301
412
|
function startPrompt() {
|
|
302
413
|
rl.question(`${C.c1}TMPA >${C.reset} `, (input) => {
|
|
303
414
|
const cmd = input.trim();
|
|
@@ -312,6 +423,18 @@ function startPrompt() {
|
|
|
312
423
|
askConfig(() => startPrompt());
|
|
313
424
|
} else if (cmd === '/models') {
|
|
314
425
|
fetchAvailableModels();
|
|
426
|
+
} else if (cmd === '/skill') {
|
|
427
|
+
listSkills();
|
|
428
|
+
startPrompt();
|
|
429
|
+
} else if (cmd === '/mcp') {
|
|
430
|
+
listMCP();
|
|
431
|
+
startPrompt();
|
|
432
|
+
} else if (cmd.startsWith('/connect')) {
|
|
433
|
+
connectResource(cmd.replace('/connect', ''));
|
|
434
|
+
startPrompt();
|
|
435
|
+
} else if (cmd === '/scan') {
|
|
436
|
+
scanResources();
|
|
437
|
+
startPrompt();
|
|
315
438
|
} else if (cmd === '/uninstall') {
|
|
316
439
|
handleUninstall();
|
|
317
440
|
} else if (cmd === '') {
|
|
@@ -322,7 +445,6 @@ function startPrompt() {
|
|
|
322
445
|
});
|
|
323
446
|
}
|
|
324
447
|
|
|
325
|
-
// Program Execution
|
|
326
448
|
showBanner();
|
|
327
449
|
if (!config.apiKey) {
|
|
328
450
|
askConfig(() => startPrompt());
|