matex-cli 1.0.0
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/README.md +261 -0
- package/USAGE_GUIDE.md +180 -0
- package/bin/matex +2 -0
- package/dist/api/client.d.ts +34 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +99 -0
- package/dist/api/client.js.map +1 -0
- package/dist/commands/ask.d.ts +3 -0
- package/dist/commands/ask.d.ts.map +1 -0
- package/dist/commands/ask.js +63 -0
- package/dist/commands/ask.js.map +1 -0
- package/dist/commands/chat.d.ts +3 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +95 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/config.d.ts +3 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +74 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/models.d.ts +3 -0
- package/dist/commands/models.d.ts.map +1 -0
- package/dist/commands/models.js +77 -0
- package/dist/commands/models.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/config.d.ts +56 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +135 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/spinner.d.ts +12 -0
- package/dist/utils/spinner.d.ts.map +1 -0
- package/dist/utils/spinner.js +58 -0
- package/dist/utils/spinner.js.map +1 -0
- package/package.json +44 -0
- package/src/api/client.ts +116 -0
- package/src/commands/ask.ts +62 -0
- package/src/commands/chat.ts +101 -0
- package/src/commands/config.ts +78 -0
- package/src/commands/models.ts +81 -0
- package/src/index.ts +50 -0
- package/src/utils/config.ts +120 -0
- package/src/utils/spinner.ts +57 -0
- package/tsconfig.json +27 -0
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "matex-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official CLI tool for MATEX AI - Access powerful AI models from your terminal",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"matex": "bin/matex"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "tsc --watch",
|
|
12
|
+
"test": "jest",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai",
|
|
17
|
+
"cli",
|
|
18
|
+
"matex",
|
|
19
|
+
"chatgpt",
|
|
20
|
+
"coding",
|
|
21
|
+
"assistant"
|
|
22
|
+
],
|
|
23
|
+
"author": "Ajay Sharma <ajaypoudel47@gmail.com>",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"axios": "^1.6.0",
|
|
27
|
+
"chalk": "^4.1.2",
|
|
28
|
+
"commander": "^11.1.0",
|
|
29
|
+
"conf": "^11.0.2",
|
|
30
|
+
"inquirer": "^8.2.5",
|
|
31
|
+
"ora": "^5.4.1",
|
|
32
|
+
"dotenv": "^16.3.1"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/inquirer": "^9.0.7",
|
|
36
|
+
"@types/node": "^20.10.0",
|
|
37
|
+
"typescript": "^5.3.0",
|
|
38
|
+
"jest": "^29.7.0",
|
|
39
|
+
"@types/jest": "^29.5.0"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=16.0.0"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import axios, { AxiosInstance } from 'axios';
|
|
2
|
+
|
|
3
|
+
export interface ChatMessage {
|
|
4
|
+
role: 'user' | 'assistant' | 'system';
|
|
5
|
+
content: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ChatRequest {
|
|
9
|
+
messages: ChatMessage[];
|
|
10
|
+
model: string;
|
|
11
|
+
temperature?: number;
|
|
12
|
+
max_tokens?: number;
|
|
13
|
+
stream?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class MatexAPIClient {
|
|
17
|
+
private client: AxiosInstance;
|
|
18
|
+
private apiKey: string;
|
|
19
|
+
private baseURL: string;
|
|
20
|
+
|
|
21
|
+
constructor(apiKey: string, baseURL: string = 'https://matexai-backend-550499663766.us-central1.run.app') {
|
|
22
|
+
this.apiKey = apiKey;
|
|
23
|
+
this.baseURL = baseURL;
|
|
24
|
+
|
|
25
|
+
this.client = axios.create({
|
|
26
|
+
baseURL: this.baseURL,
|
|
27
|
+
headers: {
|
|
28
|
+
'X-API-Key': this.apiKey,
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
},
|
|
31
|
+
timeout: 60000, // 60 seconds
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Send a chat request to the API
|
|
37
|
+
*/
|
|
38
|
+
async chat(request: ChatRequest): Promise<string> {
|
|
39
|
+
try {
|
|
40
|
+
const response = await this.client.post('/api/v1/chat', {
|
|
41
|
+
...request,
|
|
42
|
+
uid: 'cli-user', // Placeholder, backend uses API key for auth
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Handle streaming response
|
|
46
|
+
if (request.stream) {
|
|
47
|
+
return this.handleStreamingResponse(response.data);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return response.data;
|
|
51
|
+
} catch (error: any) {
|
|
52
|
+
if (error.response) {
|
|
53
|
+
throw new Error(`API Error: ${error.response.data.detail || error.response.statusText}`);
|
|
54
|
+
} else if (error.request) {
|
|
55
|
+
throw new Error('No response from server. Please check your internet connection.');
|
|
56
|
+
} else {
|
|
57
|
+
throw new Error(`Request failed: ${error.message}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Handle Server-Sent Events (SSE) streaming response
|
|
64
|
+
*/
|
|
65
|
+
private async handleStreamingResponse(data: any): Promise<string> {
|
|
66
|
+
let fullResponse = '';
|
|
67
|
+
|
|
68
|
+
// Parse SSE format: "data: {...}\n\n"
|
|
69
|
+
const lines = data.split('\n');
|
|
70
|
+
|
|
71
|
+
for (const line of lines) {
|
|
72
|
+
if (line.startsWith('data: ')) {
|
|
73
|
+
const jsonStr = line.substring(6);
|
|
74
|
+
|
|
75
|
+
if (jsonStr === '[DONE]') {
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const parsed = JSON.parse(jsonStr);
|
|
81
|
+
if (parsed.content) {
|
|
82
|
+
fullResponse += parsed.content;
|
|
83
|
+
}
|
|
84
|
+
} catch (e) {
|
|
85
|
+
// Skip invalid JSON
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return fullResponse;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* List available API keys (requires Firebase token, not for CLI use)
|
|
95
|
+
*/
|
|
96
|
+
async listAPIKeys(): Promise<any> {
|
|
97
|
+
const response = await this.client.get('/api/v1/api-keys/list');
|
|
98
|
+
return response.data;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Test API key validity
|
|
103
|
+
*/
|
|
104
|
+
async testConnection(): Promise<boolean> {
|
|
105
|
+
try {
|
|
106
|
+
await this.chat({
|
|
107
|
+
messages: [{ role: 'user', content: 'test' }],
|
|
108
|
+
model: 'matexcodex',
|
|
109
|
+
max_tokens: 10,
|
|
110
|
+
});
|
|
111
|
+
return true;
|
|
112
|
+
} catch (error) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { configManager } from '../utils/config';
|
|
4
|
+
import { MatexAPIClient } from '../api/client';
|
|
5
|
+
import { spinner } from '../utils/spinner';
|
|
6
|
+
|
|
7
|
+
export const askCommand = new Command('ask')
|
|
8
|
+
.description('Ask a single question to MATEX AI')
|
|
9
|
+
.argument('<question>', 'Your question')
|
|
10
|
+
.option('-m, --model <model>', 'AI model to use (matexcodex, matexai, elite, matexspirit)', configManager.getDefaultModel())
|
|
11
|
+
.option('-t, --temperature <number>', 'Temperature (0-1)', '0.7')
|
|
12
|
+
.option('--max-tokens <number>', 'Maximum tokens in response', '4000')
|
|
13
|
+
.action(async (question: string, options: any) => {
|
|
14
|
+
try {
|
|
15
|
+
// Check for API key
|
|
16
|
+
const apiKey = configManager.getAPIKey();
|
|
17
|
+
if (!apiKey) {
|
|
18
|
+
console.error(chalk.red('❌ No API key configured.'));
|
|
19
|
+
console.log(chalk.yellow('Run: matex config set-key <your-api-key>'));
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Create API client
|
|
24
|
+
const client = new MatexAPIClient(apiKey, configManager.getBaseURL());
|
|
25
|
+
|
|
26
|
+
// Show thinking indicator
|
|
27
|
+
spinner.start(`Thinking with ${options.model}...`);
|
|
28
|
+
|
|
29
|
+
// Send request
|
|
30
|
+
const response = await client.chat({
|
|
31
|
+
messages: [
|
|
32
|
+
{ role: 'user', content: question }
|
|
33
|
+
],
|
|
34
|
+
model: options.model,
|
|
35
|
+
temperature: parseFloat(options.temperature),
|
|
36
|
+
max_tokens: parseInt(options.maxTokens),
|
|
37
|
+
stream: false,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
spinner.succeed('Response received!');
|
|
41
|
+
|
|
42
|
+
// Display response
|
|
43
|
+
console.log(chalk.cyan('\n💬 MATEX AI:\n'));
|
|
44
|
+
console.log(chalk.white(response));
|
|
45
|
+
console.log();
|
|
46
|
+
|
|
47
|
+
} catch (error: any) {
|
|
48
|
+
spinner.fail('Request failed');
|
|
49
|
+
|
|
50
|
+
if (error.message.includes('403')) {
|
|
51
|
+
console.error(chalk.red('\n❌ Invalid or revoked API key.'));
|
|
52
|
+
console.log(chalk.yellow('Please check your API key or generate a new one from the MATEX AI platform.'));
|
|
53
|
+
} else if (error.message.includes('429')) {
|
|
54
|
+
console.error(chalk.red('\n❌ Rate limit exceeded.'));
|
|
55
|
+
console.log(chalk.yellow('Please wait a moment before trying again.'));
|
|
56
|
+
} else {
|
|
57
|
+
console.error(chalk.red(`\n❌ Error: ${error.message}`));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { configManager } from '../utils/config';
|
|
5
|
+
import { MatexAPIClient, ChatMessage } from '../api/client';
|
|
6
|
+
import { spinner } from '../utils/spinner';
|
|
7
|
+
|
|
8
|
+
export const chatCommand = new Command('chat')
|
|
9
|
+
.description('Start an interactive chat session with MATEX AI')
|
|
10
|
+
.option('-m, --model <model>', 'AI model to use (matexcodex, matexai, elite, matexspirit)', configManager.getDefaultModel())
|
|
11
|
+
.action(async (options: any) => {
|
|
12
|
+
try {
|
|
13
|
+
// Check for API key
|
|
14
|
+
const apiKey = configManager.getAPIKey();
|
|
15
|
+
if (!apiKey) {
|
|
16
|
+
console.error(chalk.red('❌ No API key configured.'));
|
|
17
|
+
console.log(chalk.yellow('Run: matex config set-key <your-api-key>'));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Create API client
|
|
22
|
+
const client = new MatexAPIClient(apiKey, configManager.getBaseURL());
|
|
23
|
+
|
|
24
|
+
// Welcome message
|
|
25
|
+
console.log(chalk.bold.cyan('\n🤖 MATEX AI Interactive Chat'));
|
|
26
|
+
console.log(chalk.gray(`Model: ${options.model}`));
|
|
27
|
+
console.log(chalk.gray('Type "exit" or "quit" to end the session\n'));
|
|
28
|
+
|
|
29
|
+
// Conversation history
|
|
30
|
+
const messages: ChatMessage[] = [];
|
|
31
|
+
|
|
32
|
+
// Chat loop
|
|
33
|
+
while (true) {
|
|
34
|
+
// Get user input
|
|
35
|
+
const { userMessage } = await inquirer.prompt([
|
|
36
|
+
{
|
|
37
|
+
type: 'input',
|
|
38
|
+
name: 'userMessage',
|
|
39
|
+
message: chalk.green('You:'),
|
|
40
|
+
prefix: '',
|
|
41
|
+
},
|
|
42
|
+
]);
|
|
43
|
+
|
|
44
|
+
// Check for exit
|
|
45
|
+
if (userMessage.toLowerCase() === 'exit' || userMessage.toLowerCase() === 'quit') {
|
|
46
|
+
console.log(chalk.cyan('\n👋 Goodbye!\n'));
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Skip empty messages
|
|
51
|
+
if (!userMessage.trim()) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Add user message to history
|
|
56
|
+
messages.push({ role: 'user', content: userMessage });
|
|
57
|
+
|
|
58
|
+
// Show thinking indicator
|
|
59
|
+
spinner.start('Thinking...');
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
// Send request
|
|
63
|
+
const response = await client.chat({
|
|
64
|
+
messages: messages,
|
|
65
|
+
model: options.model,
|
|
66
|
+
temperature: 0.7,
|
|
67
|
+
max_tokens: 4000,
|
|
68
|
+
stream: false,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
spinner.stop();
|
|
72
|
+
|
|
73
|
+
// Add assistant response to history
|
|
74
|
+
messages.push({ role: 'assistant', content: response });
|
|
75
|
+
|
|
76
|
+
// Display response
|
|
77
|
+
console.log(chalk.cyan('AI:'), chalk.white(response));
|
|
78
|
+
console.log();
|
|
79
|
+
|
|
80
|
+
} catch (error: any) {
|
|
81
|
+
spinner.fail('Request failed');
|
|
82
|
+
|
|
83
|
+
if (error.message.includes('403')) {
|
|
84
|
+
console.error(chalk.red('❌ Invalid or revoked API key.'));
|
|
85
|
+
break;
|
|
86
|
+
} else if (error.message.includes('429')) {
|
|
87
|
+
console.error(chalk.red('❌ Rate limit exceeded. Please wait a moment.'));
|
|
88
|
+
} else {
|
|
89
|
+
console.error(chalk.red(`❌ Error: ${error.message}`));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Remove failed message from history
|
|
93
|
+
messages.pop();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
} catch (error: any) {
|
|
98
|
+
console.error(chalk.red(`\n❌ Chat session error: ${error.message}`));
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { configManager } from '../utils/config';
|
|
4
|
+
import { spinner } from '../utils/spinner';
|
|
5
|
+
|
|
6
|
+
export const configCommand = new Command('config')
|
|
7
|
+
.description('Manage MATEX CLI configuration');
|
|
8
|
+
|
|
9
|
+
// Set API key
|
|
10
|
+
configCommand
|
|
11
|
+
.command('set-key <apiKey>')
|
|
12
|
+
.description('Set your MATEX AI API key')
|
|
13
|
+
.action((apiKey: string) => {
|
|
14
|
+
try {
|
|
15
|
+
// Validate API key format
|
|
16
|
+
if (!apiKey.startsWith('sk-matex-')) {
|
|
17
|
+
console.error(chalk.red('❌ Invalid API key format. API keys should start with "sk-matex-"'));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
configManager.setAPIKey(apiKey);
|
|
22
|
+
console.log(chalk.green('✅ API key saved successfully!'));
|
|
23
|
+
console.log(chalk.gray('Your API key is stored securely in ~/.matex/config.json'));
|
|
24
|
+
console.log(chalk.cyan('\n💡 Try it out: matex ask "What is 2+2?"'));
|
|
25
|
+
} catch (error: any) {
|
|
26
|
+
console.error(chalk.red(`❌ Failed to save API key: ${error.message}`));
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// Show configuration
|
|
32
|
+
configCommand
|
|
33
|
+
.command('show')
|
|
34
|
+
.description('Show current configuration')
|
|
35
|
+
.action(() => {
|
|
36
|
+
const config = configManager.getAll();
|
|
37
|
+
|
|
38
|
+
console.log(chalk.bold.cyan('\n📋 MATEX CLI Configuration\n'));
|
|
39
|
+
|
|
40
|
+
if (config.apiKey) {
|
|
41
|
+
const maskedKey = config.apiKey.substring(0, 12) + '...' + config.apiKey.substring(config.apiKey.length - 4);
|
|
42
|
+
console.log(chalk.white('API Key: ') + chalk.gray(maskedKey));
|
|
43
|
+
} else {
|
|
44
|
+
console.log(chalk.white('API Key: ') + chalk.red('Not set'));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log(chalk.white('Default Model: ') + chalk.cyan(config.defaultModel || 'matexcodex'));
|
|
48
|
+
console.log(chalk.white('Base URL: ') + chalk.gray(config.baseURL || 'https://matexai-backend-550499663766.us-central1.run.app'));
|
|
49
|
+
|
|
50
|
+
if (!config.apiKey) {
|
|
51
|
+
console.log(chalk.yellow('\n⚠️ No API key configured. Use "matex config set-key <your-api-key>" to get started.'));
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Clear configuration
|
|
56
|
+
configCommand
|
|
57
|
+
.command('clear')
|
|
58
|
+
.description('Clear all configuration')
|
|
59
|
+
.action(() => {
|
|
60
|
+
configManager.clear();
|
|
61
|
+
console.log(chalk.green('✅ Configuration cleared successfully!'));
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Set default model
|
|
65
|
+
configCommand
|
|
66
|
+
.command('set-model <model>')
|
|
67
|
+
.description('Set default AI model')
|
|
68
|
+
.action((model: string) => {
|
|
69
|
+
const validModels = ['matexcodex', 'matexai', 'elite', 'matexspirit'];
|
|
70
|
+
|
|
71
|
+
if (!validModels.includes(model.toLowerCase())) {
|
|
72
|
+
console.error(chalk.red(`❌ Invalid model. Choose from: ${validModels.join(', ')}`));
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
configManager.setDefaultModel(model.toLowerCase());
|
|
77
|
+
console.log(chalk.green(`✅ Default model set to: ${model}`));
|
|
78
|
+
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { configManager } from '../utils/config';
|
|
4
|
+
|
|
5
|
+
const AVAILABLE_MODELS = [
|
|
6
|
+
{
|
|
7
|
+
name: 'matexcodex',
|
|
8
|
+
description: 'Best for coding, technical tasks, and development',
|
|
9
|
+
tier: 'Free (5 min/day), Starter (4M tokens/month), Pro Plus (unlimited)',
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
name: 'matexai',
|
|
13
|
+
description: 'General purpose AI for conversations and knowledge',
|
|
14
|
+
tier: 'Pro, Pro Plus',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: 'elite',
|
|
18
|
+
description: 'Premium model with advanced capabilities',
|
|
19
|
+
tier: 'Elite Plan',
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'matexspirit',
|
|
23
|
+
description: 'Specialized model for creative and empathetic responses',
|
|
24
|
+
tier: 'Pro, Pro Plus',
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export const modelsCommand = new Command('models')
|
|
29
|
+
.description('Manage AI models');
|
|
30
|
+
|
|
31
|
+
// List available models
|
|
32
|
+
modelsCommand
|
|
33
|
+
.command('list')
|
|
34
|
+
.description('List all available AI models')
|
|
35
|
+
.action(() => {
|
|
36
|
+
console.log(chalk.bold.cyan('\n🤖 Available MATEX AI Models\n'));
|
|
37
|
+
|
|
38
|
+
AVAILABLE_MODELS.forEach((model) => {
|
|
39
|
+
console.log(chalk.bold.white(`${model.name}`));
|
|
40
|
+
console.log(chalk.gray(` ${model.description}`));
|
|
41
|
+
console.log(chalk.yellow(` Access: ${model.tier}`));
|
|
42
|
+
console.log();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const currentModel = configManager.getDefaultModel();
|
|
46
|
+
console.log(chalk.cyan(`Current default: ${currentModel}`));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Set default model
|
|
50
|
+
modelsCommand
|
|
51
|
+
.command('set <model>')
|
|
52
|
+
.description('Set default AI model')
|
|
53
|
+
.action((model: string) => {
|
|
54
|
+
const validModels = AVAILABLE_MODELS.map(m => m.name);
|
|
55
|
+
|
|
56
|
+
if (!validModels.includes(model.toLowerCase())) {
|
|
57
|
+
console.error(chalk.red(`❌ Invalid model. Choose from: ${validModels.join(', ')}`));
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
configManager.setDefaultModel(model.toLowerCase());
|
|
62
|
+
console.log(chalk.green(`✅ Default model set to: ${model}`));
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Show current model
|
|
66
|
+
modelsCommand
|
|
67
|
+
.command('current')
|
|
68
|
+
.description('Show current default model')
|
|
69
|
+
.action(() => {
|
|
70
|
+
const currentModel = configManager.getDefaultModel();
|
|
71
|
+
const modelInfo = AVAILABLE_MODELS.find(m => m.name === currentModel);
|
|
72
|
+
|
|
73
|
+
console.log(chalk.bold.cyan('\n📌 Current Default Model\n'));
|
|
74
|
+
console.log(chalk.bold.white(currentModel));
|
|
75
|
+
|
|
76
|
+
if (modelInfo) {
|
|
77
|
+
console.log(chalk.gray(modelInfo.description));
|
|
78
|
+
console.log(chalk.yellow(`Access: ${modelInfo.tier}`));
|
|
79
|
+
}
|
|
80
|
+
console.log();
|
|
81
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { configCommand } from './commands/config';
|
|
6
|
+
import { askCommand } from './commands/ask';
|
|
7
|
+
import { chatCommand } from './commands/chat';
|
|
8
|
+
import { modelsCommand } from './commands/models';
|
|
9
|
+
|
|
10
|
+
const program = new Command();
|
|
11
|
+
|
|
12
|
+
program
|
|
13
|
+
.name('matex')
|
|
14
|
+
.description('Official CLI tool for MATEX AI - Access powerful AI models from your terminal')
|
|
15
|
+
.version('1.0.0');
|
|
16
|
+
|
|
17
|
+
// ASCII Art Banner
|
|
18
|
+
const banner = `
|
|
19
|
+
${chalk.cyan('╔═══════════════════════════════════════╗')}
|
|
20
|
+
${chalk.cyan('║')} ${chalk.bold.white('MATEX AI')} ${chalk.gray('- Terminal Edition')} ${chalk.cyan('║')}
|
|
21
|
+
${chalk.cyan('╚═══════════════════════════════════════╝')}
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
// Show banner on help
|
|
25
|
+
program.on('--help', () => {
|
|
26
|
+
console.log(banner);
|
|
27
|
+
console.log(chalk.gray('\nExamples:'));
|
|
28
|
+
console.log(chalk.white(' $ matex config set-key sk-matex-xxxxx'));
|
|
29
|
+
console.log(chalk.white(' $ matex ask "What is 2+2?"'));
|
|
30
|
+
console.log(chalk.white(' $ matex chat'));
|
|
31
|
+
console.log(chalk.white(' $ matex models list'));
|
|
32
|
+
console.log();
|
|
33
|
+
console.log(chalk.gray('Get your API key from: ') + chalk.cyan('https://matexai.space/platform'));
|
|
34
|
+
console.log();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Add commands
|
|
38
|
+
program.addCommand(configCommand);
|
|
39
|
+
program.addCommand(askCommand);
|
|
40
|
+
program.addCommand(chatCommand);
|
|
41
|
+
program.addCommand(modelsCommand);
|
|
42
|
+
|
|
43
|
+
// Parse arguments
|
|
44
|
+
program.parse(process.argv);
|
|
45
|
+
|
|
46
|
+
// Show help if no command provided
|
|
47
|
+
if (!process.argv.slice(2).length) {
|
|
48
|
+
console.log(banner);
|
|
49
|
+
program.outputHelp();
|
|
50
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
|
|
5
|
+
export interface Config {
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
defaultModel?: string;
|
|
8
|
+
baseURL?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class ConfigManager {
|
|
12
|
+
private configDir: string;
|
|
13
|
+
private configPath: string;
|
|
14
|
+
|
|
15
|
+
constructor() {
|
|
16
|
+
this.configDir = path.join(os.homedir(), '.matex');
|
|
17
|
+
this.configPath = path.join(this.configDir, 'config.json');
|
|
18
|
+
|
|
19
|
+
// Ensure config directory exists
|
|
20
|
+
if (!fs.existsSync(this.configDir)) {
|
|
21
|
+
fs.mkdirSync(this.configDir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Load config from file
|
|
27
|
+
*/
|
|
28
|
+
private loadConfig(): Config {
|
|
29
|
+
if (!fs.existsSync(this.configPath)) {
|
|
30
|
+
return {};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const data = fs.readFileSync(this.configPath, 'utf-8');
|
|
35
|
+
return JSON.parse(data);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Save config to file
|
|
43
|
+
*/
|
|
44
|
+
private saveConfig(config: Config): void {
|
|
45
|
+
fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Set API key
|
|
50
|
+
*/
|
|
51
|
+
setAPIKey(apiKey: string): void {
|
|
52
|
+
const config = this.loadConfig();
|
|
53
|
+
config.apiKey = apiKey;
|
|
54
|
+
this.saveConfig(config);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get API key
|
|
59
|
+
*/
|
|
60
|
+
getAPIKey(): string | undefined {
|
|
61
|
+
return this.loadConfig().apiKey;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Set default model
|
|
66
|
+
*/
|
|
67
|
+
setDefaultModel(model: string): void {
|
|
68
|
+
const config = this.loadConfig();
|
|
69
|
+
config.defaultModel = model;
|
|
70
|
+
this.saveConfig(config);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Get default model
|
|
75
|
+
*/
|
|
76
|
+
getDefaultModel(): string {
|
|
77
|
+
return this.loadConfig().defaultModel || 'matexcodex';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Set base URL
|
|
82
|
+
*/
|
|
83
|
+
setBaseURL(url: string): void {
|
|
84
|
+
const config = this.loadConfig();
|
|
85
|
+
config.baseURL = url;
|
|
86
|
+
this.saveConfig(config);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get base URL
|
|
91
|
+
*/
|
|
92
|
+
getBaseURL(): string {
|
|
93
|
+
return this.loadConfig().baseURL || 'https://matexai-backend-550499663766.us-central1.run.app';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get all config
|
|
98
|
+
*/
|
|
99
|
+
getAll(): Config {
|
|
100
|
+
return this.loadConfig();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Clear all config
|
|
105
|
+
*/
|
|
106
|
+
clear(): void {
|
|
107
|
+
if (fs.existsSync(this.configPath)) {
|
|
108
|
+
fs.unlinkSync(this.configPath);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check if API key is set
|
|
114
|
+
*/
|
|
115
|
+
hasAPIKey(): boolean {
|
|
116
|
+
return !!this.getAPIKey();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const configManager = new ConfigManager();
|