slek-ai-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 +70 -0
- package/auth.js +225 -0
- package/cli.js +452 -0
- package/package.json +22 -0
- package/setup.js +78 -0
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# ๐ NVIDIA AI CLI
|
|
2
|
+
|
|
3
|
+
เฆเฆเฆเฆฟ เฆธเงเฆจเงเฆฆเฆฐ AI Command Line Interface เฆฏเฆพ NVIDIA API เฆฌเงเฆฏเฆฌเฆนเฆพเฆฐ เฆเฆฐเงเฅค
|
|
4
|
+
|
|
5
|
+
## โจ Features
|
|
6
|
+
|
|
7
|
+
- ๐ค NVIDIA API เฆฆเฆฟเฆฏเฆผเง AI chat
|
|
8
|
+
- ๐ Real-time streaming response
|
|
9
|
+
- ๐จ Colorful terminal UI
|
|
10
|
+
- ๐ Chat history
|
|
11
|
+
- ๐พ Chat save เฆเฆฐเฆพเฆฐ เฆธเงเฆฌเฆฟเฆงเฆพ
|
|
12
|
+
- ๐ Multiple AI models
|
|
13
|
+
- โก Fast & lightweight
|
|
14
|
+
|
|
15
|
+
## ๐ฆ Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Dependencies install เฆเฆฐเงเฆจ
|
|
19
|
+
npm install
|
|
20
|
+
|
|
21
|
+
# Setup เฆเฆฐเงเฆจ (API key เฆฆเฆฟเฆจ)
|
|
22
|
+
npm run setup
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## ๐ NVIDIA API Key เฆชเฆพเฆฌเงเฆจ เฆเงเฆญเฆพเฆฌเง?
|
|
26
|
+
|
|
27
|
+
1. [https://build.nvidia.com](https://build.nvidia.com) เฆ เฆฏเฆพเฆจ
|
|
28
|
+
2. Sign up / Login เฆเฆฐเงเฆจ (Free!)
|
|
29
|
+
3. เฆฏเงเฆเงเฆจเง model เฆ click เฆเฆฐเงเฆจ
|
|
30
|
+
4. "Get API Key" เฆฌเฆพเฆเฆจเง click เฆเฆฐเงเฆจ
|
|
31
|
+
5. Key copy เฆเฆฐเงเฆจ
|
|
32
|
+
|
|
33
|
+
## ๐ Run เฆเฆฐเงเฆจ
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
node cli.js
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## ๐ฌ Commands
|
|
40
|
+
|
|
41
|
+
| Command | เฆเฆพเฆ |
|
|
42
|
+
|-------------|----------------------------|
|
|
43
|
+
| `/help` | Help เฆฆเงเฆเฆพเฆจ |
|
|
44
|
+
| `/model` | AI model เฆฌเฆฆเฆฒเฆพเฆจ |
|
|
45
|
+
| `/models` | เฆธเฆฌ models เฆฆเงเฆเฆพเฆจ |
|
|
46
|
+
| `/clear` | Chat history เฆฎเงเฆเงเฆจ |
|
|
47
|
+
| `/history` | Chat history เฆฆเงเฆเฆพเฆจ |
|
|
48
|
+
| `/save` | Chat file เฆ save เฆเฆฐเงเฆจ |
|
|
49
|
+
| `/info` | Current settings เฆฆเงเฆเฆพเฆจ |
|
|
50
|
+
| `/exit` | เฆฌเฆจเงเฆง เฆเฆฐเงเฆจ |
|
|
51
|
+
|
|
52
|
+
## ๐ค Available Models
|
|
53
|
+
|
|
54
|
+
- **Llama 3.1 70B** - เฆธเฆฌเฆเงเฆฏเฆผเง เฆถเฆเงเฆคเฆฟเฆถเฆพเฆฒเง
|
|
55
|
+
- **Llama 3.1 8B** - เฆฆเงเฆฐเงเฆค response
|
|
56
|
+
- **Nemotron 70B** - NVIDIA เฆเฆฐ เฆจเฆฟเฆเฆธเงเฆฌ model
|
|
57
|
+
- **Mixtral 8x7B** - Mistral AI
|
|
58
|
+
- **Phi-3 Medium** - Microsoft
|
|
59
|
+
- **Gemma 7B** - Google
|
|
60
|
+
|
|
61
|
+
## ๐ File Structure
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
AI CLI/
|
|
65
|
+
โโโ cli.js # Main CLI file
|
|
66
|
+
โโโ setup.js # Setup script
|
|
67
|
+
โโโ package.json # Dependencies
|
|
68
|
+
โโโ .env # API key (เฆเฆเฆฟ share เฆเฆฐเฆฌเงเฆจ เฆจเฆพ!)
|
|
69
|
+
โโโ README.md # Documentation
|
|
70
|
+
```
|
package/auth.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
require('dotenv').config({ path: require('path').join(__dirname, '.env') });
|
|
5
|
+
|
|
6
|
+
const http = require('http');
|
|
7
|
+
const urlModule = require('url');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const { execSync } = require('child_process');
|
|
11
|
+
const axios = require('axios');
|
|
12
|
+
const chalk = require('chalk');
|
|
13
|
+
const boxen = require('boxen');
|
|
14
|
+
const gradient = require('gradient-string');
|
|
15
|
+
|
|
16
|
+
const nvidiaGradient = gradient(['#76b900', '#00c8ff']);
|
|
17
|
+
|
|
18
|
+
// โโโ Firebase / Google config โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
19
|
+
const FIREBASE_API_KEY = process.env.FIREBASE_API_KEY;
|
|
20
|
+
const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID;
|
|
21
|
+
const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET;
|
|
22
|
+
const REDIRECT_URI = 'http://localhost:9876/callback';
|
|
23
|
+
const AUTH_PORT = 9876;
|
|
24
|
+
|
|
25
|
+
// โโโ Token file path (saved in user home) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
26
|
+
const TOKEN_PATH = path.join(
|
|
27
|
+
process.env.APPDATA || process.env.HOME || __dirname,
|
|
28
|
+
'.slek-auth.json'
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// โโโ Token helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
32
|
+
function saveToken(data) {
|
|
33
|
+
fs.writeFileSync(TOKEN_PATH, JSON.stringify(data, null, 2), 'utf8');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function loadToken() {
|
|
37
|
+
try {
|
|
38
|
+
if (fs.existsSync(TOKEN_PATH)) {
|
|
39
|
+
return JSON.parse(fs.readFileSync(TOKEN_PATH, 'utf8'));
|
|
40
|
+
}
|
|
41
|
+
} catch { }
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function clearToken() {
|
|
46
|
+
try { fs.unlinkSync(TOKEN_PATH); } catch { }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isLoggedIn() {
|
|
50
|
+
const token = loadToken();
|
|
51
|
+
if (!token) return false;
|
|
52
|
+
if (token.expiresAt && Date.now() > token.expiresAt) return false;
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function getUser() {
|
|
57
|
+
return loadToken();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// โโโ Open browser (PowerShell safe) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
61
|
+
function openBrowser(targetUrl) {
|
|
62
|
+
try {
|
|
63
|
+
execSync(`powershell -Command "Start-Process '${targetUrl}'"`, { stdio: 'ignore' });
|
|
64
|
+
} catch {
|
|
65
|
+
console.log(chalk.yellow('\n Could not open browser automatically.'));
|
|
66
|
+
console.log(chalk.white(' Paste this URL in your browser:\n'));
|
|
67
|
+
console.log(chalk.cyan(' ' + targetUrl + '\n'));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// โโโ Exchange Google code โ Firebase token โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
72
|
+
async function exchangeCodeForToken(code) {
|
|
73
|
+
const tokenRes = await axios.post('https://oauth2.googleapis.com/token', {
|
|
74
|
+
code,
|
|
75
|
+
client_id: GOOGLE_CLIENT_ID,
|
|
76
|
+
client_secret: GOOGLE_CLIENT_SECRET,
|
|
77
|
+
redirect_uri: REDIRECT_URI,
|
|
78
|
+
grant_type: 'authorization_code',
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const { id_token } = tokenRes.data;
|
|
82
|
+
|
|
83
|
+
const firebaseRes = await axios.post(
|
|
84
|
+
`https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp?key=${FIREBASE_API_KEY}`,
|
|
85
|
+
{
|
|
86
|
+
postBody: `id_token=${id_token}&providerId=google.com`,
|
|
87
|
+
requestUri: REDIRECT_URI,
|
|
88
|
+
returnIdpCredential: true,
|
|
89
|
+
returnSecureToken: true,
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const { displayName, email, idToken, expiresIn, photoUrl } = firebaseRes.data;
|
|
94
|
+
return {
|
|
95
|
+
name: displayName,
|
|
96
|
+
email,
|
|
97
|
+
photo: photoUrl,
|
|
98
|
+
idToken,
|
|
99
|
+
expiresAt: Date.now() + parseInt(expiresIn) * 1000,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// โโโ Login โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
104
|
+
async function login() {
|
|
105
|
+
if (!FIREBASE_API_KEY || !GOOGLE_CLIENT_ID || !GOOGLE_CLIENT_SECRET) {
|
|
106
|
+
console.log(
|
|
107
|
+
boxen(
|
|
108
|
+
chalk.red('โ Firebase config missing!\n\n') +
|
|
109
|
+
chalk.white('Add to your ') + chalk.yellow('.env') + chalk.white(':\n\n') +
|
|
110
|
+
chalk.cyan('FIREBASE_API_KEY=...\nGOOGLE_CLIENT_ID=...\nGOOGLE_CLIENT_SECRET=...'),
|
|
111
|
+
{ padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'red' }
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Build Google OAuth URL
|
|
118
|
+
const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');
|
|
119
|
+
authUrl.searchParams.set('client_id', GOOGLE_CLIENT_ID);
|
|
120
|
+
authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
|
|
121
|
+
authUrl.searchParams.set('response_type', 'code');
|
|
122
|
+
authUrl.searchParams.set('scope', 'openid email profile');
|
|
123
|
+
authUrl.searchParams.set('access_type', 'offline');
|
|
124
|
+
authUrl.searchParams.set('prompt', 'select_account');
|
|
125
|
+
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
const server = http.createServer(async (req, res) => {
|
|
128
|
+
const parsed = urlModule.parse(req.url, true);
|
|
129
|
+
if (parsed.pathname !== '/callback') {
|
|
130
|
+
res.writeHead(404); res.end(); return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const code = parsed.query.code;
|
|
134
|
+
const error = parsed.query.error;
|
|
135
|
+
|
|
136
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
137
|
+
res.end(`<html><body style="background:#0a0a0a;color:#fff;font-family:monospace;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;"><div style="text-align:center;"><h1 style="color:#76b900;">โ Login Successful!</h1><p style="color:#aaa;">You can close this tab and return to the terminal.</p></div></body></html>`);
|
|
138
|
+
|
|
139
|
+
server.close();
|
|
140
|
+
|
|
141
|
+
if (error) {
|
|
142
|
+
console.log(chalk.red('\n โ Login cancelled.\n'));
|
|
143
|
+
reject(new Error(error));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
process.stdout.write(chalk.cyan('\n โณ Verifying with Firebase...\n'));
|
|
149
|
+
const userData = await exchangeCodeForToken(code);
|
|
150
|
+
saveToken(userData);
|
|
151
|
+
console.log(
|
|
152
|
+
'\n' + boxen(
|
|
153
|
+
chalk.green('โ Logged in successfully!\n\n') +
|
|
154
|
+
chalk.cyan('Name : ') + chalk.white(userData.name) + '\n' +
|
|
155
|
+
chalk.cyan('Email : ') + chalk.white(userData.email),
|
|
156
|
+
{ padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'green' }
|
|
157
|
+
)
|
|
158
|
+
);
|
|
159
|
+
console.log(chalk.gray('\n Now run ') + chalk.yellow('slek') + chalk.gray(' to start chatting!\n'));
|
|
160
|
+
resolve(userData);
|
|
161
|
+
} catch (err) {
|
|
162
|
+
console.log(chalk.red('\n โ Auth failed: ') + err.message + '\n');
|
|
163
|
+
reject(err);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
server.on('error', (err) => {
|
|
168
|
+
console.log(chalk.red('\n โ Server error: ') + err.message);
|
|
169
|
+
reject(err);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
server.listen(AUTH_PORT, '127.0.0.1', () => {
|
|
173
|
+
console.log(
|
|
174
|
+
boxen(
|
|
175
|
+
chalk.bold.white('๐ SLEK AI โ Google Login\n') +
|
|
176
|
+
chalk.gray('โ'.repeat(38)) + '\n\n' +
|
|
177
|
+
chalk.cyan('Opening browser...\n') +
|
|
178
|
+
chalk.gray('Waiting for Google authentication...\n\n') +
|
|
179
|
+
chalk.gray('If browser does not open, paste this URL:\n') +
|
|
180
|
+
chalk.yellow(authUrl.toString()),
|
|
181
|
+
{ padding: 1, margin: { left: 2 }, borderStyle: 'double', borderColor: 'green' }
|
|
182
|
+
)
|
|
183
|
+
);
|
|
184
|
+
// Small delay to ensure server is fully ready before browser opens
|
|
185
|
+
setTimeout(() => openBrowser(authUrl.toString()), 500);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
setTimeout(() => {
|
|
189
|
+
server.close();
|
|
190
|
+
console.log(chalk.red('\n โ Login timed out (2 min). Try again.\n'));
|
|
191
|
+
reject(new Error('Login timeout'));
|
|
192
|
+
}, 120000);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// โโโ Logout โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
197
|
+
function logout() {
|
|
198
|
+
clearToken();
|
|
199
|
+
console.log(
|
|
200
|
+
'\n' + boxen(
|
|
201
|
+
chalk.yellow('๐ Logged out!\n\n') +
|
|
202
|
+
chalk.gray('Run ') + chalk.yellow('slek login') + chalk.gray(' to log in again.'),
|
|
203
|
+
{ padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'yellow' }
|
|
204
|
+
) + '\n'
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// โโโ Status โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
209
|
+
function status() {
|
|
210
|
+
const user = getUser();
|
|
211
|
+
if (user) {
|
|
212
|
+
console.log(
|
|
213
|
+
'\n' + boxen(
|
|
214
|
+
chalk.green('โ Logged in\n\n') +
|
|
215
|
+
chalk.cyan('Name : ') + chalk.white(user.name) + '\n' +
|
|
216
|
+
chalk.cyan('Email : ') + chalk.white(user.email),
|
|
217
|
+
{ padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'green' }
|
|
218
|
+
) + '\n'
|
|
219
|
+
);
|
|
220
|
+
} else {
|
|
221
|
+
console.log(chalk.yellow('\n Not logged in. Run: ') + chalk.white('slek login\n'));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
module.exports = { isLoggedIn, getUser, login, logout, status };
|
package/cli.js
ADDED
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
require('dotenv').config({
|
|
5
|
+
path: require('path').join(__dirname, '.env')
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// โโโ Auth check โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
9
|
+
const auth = require('./auth');
|
|
10
|
+
|
|
11
|
+
const arg = process.argv[2];
|
|
12
|
+
|
|
13
|
+
// Handle: slek login / slek logout / slek status
|
|
14
|
+
if (arg === 'login') {
|
|
15
|
+
auth.login().catch(() => process.exit(1));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (arg === 'logout') {
|
|
19
|
+
auth.logout();
|
|
20
|
+
process.exit(0);
|
|
21
|
+
}
|
|
22
|
+
if (arg === 'status') {
|
|
23
|
+
auth.status();
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Block access if not logged in
|
|
28
|
+
if (!auth.isLoggedIn()) {
|
|
29
|
+
const chalk = require('chalk');
|
|
30
|
+
const boxen = require('boxen');
|
|
31
|
+
console.log(
|
|
32
|
+
'\n' + boxen(
|
|
33
|
+
chalk.yellow('โ You are not logged in!\n\n') +
|
|
34
|
+
chalk.white('Run this command to login:\n\n') +
|
|
35
|
+
chalk.green(' slek login'),
|
|
36
|
+
{ padding: 1, margin: { left: 2 }, borderStyle: 'double', borderColor: 'yellow' }
|
|
37
|
+
) + '\n'
|
|
38
|
+
);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const readline = require('readline');
|
|
43
|
+
const axios = require('axios');
|
|
44
|
+
const chalk = require('chalk');
|
|
45
|
+
const boxen = require('boxen');
|
|
46
|
+
const gradient = require('gradient-string');
|
|
47
|
+
const figlet = require('figlet');
|
|
48
|
+
|
|
49
|
+
// โโโ NOTE: ora v5 is required (CommonJS compatible) โโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
50
|
+
// If ora gives errors, run: npm install ora@5
|
|
51
|
+
let ora;
|
|
52
|
+
try {
|
|
53
|
+
ora = require('ora');
|
|
54
|
+
} catch {
|
|
55
|
+
// Fallback spinner if ora fails
|
|
56
|
+
ora = (opts) => ({
|
|
57
|
+
start: () => { process.stdout.write(chalk.cyan(' โณ ' + (opts.text || 'Loading...') + '\n')); return { stop: () => {}, fail: () => {} }; },
|
|
58
|
+
stop: () => {},
|
|
59
|
+
fail: () => {},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// โโโ NVIDIA API Config โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
64
|
+
const NVIDIA_API_BASE = 'https://slek-ai-portal.vercel.app/api';
|
|
65
|
+
const DEFAULT_MODEL = 'qwen/qwen3.5-122b-a10b';
|
|
66
|
+
|
|
67
|
+
// โโโ User config path (stored in user's home, not in project) โโโโโโโโโโโโโโโโโ
|
|
68
|
+
const CONFIG_PATH = require('path').join(
|
|
69
|
+
process.env.APPDATA || process.env.HOME || __dirname,
|
|
70
|
+
'.slek-config.json'
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
function loadConfig() {
|
|
74
|
+
try {
|
|
75
|
+
if (require('fs').existsSync(CONFIG_PATH)) {
|
|
76
|
+
return JSON.parse(require('fs').readFileSync(CONFIG_PATH, 'utf8'));
|
|
77
|
+
}
|
|
78
|
+
} catch { }
|
|
79
|
+
return {};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function saveConfig(data) {
|
|
83
|
+
const current = loadConfig();
|
|
84
|
+
require('fs').writeFileSync(CONFIG_PATH, JSON.stringify({ ...current, ...data }, null, 2), 'utf8');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// โโโ Gradient Colors โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
88
|
+
const nvidiaGradient = gradient(['#76b900', '#00c8ff']);
|
|
89
|
+
const userGradient = gradient(['#ff6b6b', '#feca57']);
|
|
90
|
+
const aiGradient = gradient(['#48dbfb', '#ff9ff3']);
|
|
91
|
+
const thinkGradient = gradient(['#f7971e', '#ffd200']);
|
|
92
|
+
|
|
93
|
+
// โโโ State โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
94
|
+
let chatHistory = [];
|
|
95
|
+
let apiKey = process.env.NVIDIA_API_KEY || '';
|
|
96
|
+
const currentUser = auth.getUser();
|
|
97
|
+
|
|
98
|
+
// โโโ Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
99
|
+
function clearLine() {
|
|
100
|
+
process.stdout.write('\r\x1b[K');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// โโโ Banner โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
104
|
+
async function showBanner() {
|
|
105
|
+
console.clear();
|
|
106
|
+
const banner = figlet.textSync('SLEK AI', {
|
|
107
|
+
font: 'ANSI Shadow',
|
|
108
|
+
horizontalLayout: 'default',
|
|
109
|
+
});
|
|
110
|
+
console.log(nvidiaGradient(banner));
|
|
111
|
+
console.log(
|
|
112
|
+
boxen(
|
|
113
|
+
chalk.bold.white('๐ SLEK AI CLI') + chalk.gray(' โ Powered by NVIDIA API\n') +
|
|
114
|
+
chalk.gray('โ'.repeat(42)) + '\n' +
|
|
115
|
+
chalk.cyan(' User : ') + chalk.green(currentUser ? currentUser.name : 'Unknown') + '\n' +
|
|
116
|
+
chalk.gray('โ'.repeat(42)) + '\n' +
|
|
117
|
+
chalk.white(' Type ') + chalk.yellow('/help') + chalk.white(' for available commands'),
|
|
118
|
+
{
|
|
119
|
+
padding: 1,
|
|
120
|
+
margin: { top: 0, bottom: 1, left: 2, right: 2 },
|
|
121
|
+
borderStyle: 'double',
|
|
122
|
+
borderColor: 'green',
|
|
123
|
+
backgroundColor: '#000',
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// โโโ Help โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
130
|
+
function showHelp() {
|
|
131
|
+
const helpBox = boxen(
|
|
132
|
+
chalk.bold.cyan('๐ Available Commands\n') +
|
|
133
|
+
chalk.gray('โ'.repeat(42)) + '\n\n' +
|
|
134
|
+
chalk.yellow('/help ') + chalk.white('Show this help menu\n') +
|
|
135
|
+
chalk.yellow('/clear ') + chalk.white('Clear chat history\n') +
|
|
136
|
+
chalk.yellow('/history ') + chalk.white('Show chat history\n') +
|
|
137
|
+
chalk.yellow('/save ') + chalk.white('Save chat to file\n') +
|
|
138
|
+
chalk.yellow('/info ') + chalk.white('Show current settings\n') +
|
|
139
|
+
chalk.yellow('/exit ') + chalk.white('Exit the CLI\n\n') +
|
|
140
|
+
chalk.gray('Or just type anything to chat with AI!'),
|
|
141
|
+
{
|
|
142
|
+
padding: 1,
|
|
143
|
+
margin: { left: 2 },
|
|
144
|
+
borderStyle: 'round',
|
|
145
|
+
borderColor: 'cyan',
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
console.log(helpBox);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// โโโ Show Models โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
152
|
+
function showModels() {
|
|
153
|
+
console.log('\n' + chalk.bold.cyan(' ๐ค Available Models:\n'));
|
|
154
|
+
Object.entries(MODELS).forEach(([key, model]) => {
|
|
155
|
+
const active = model.id === currentModel ? chalk.green(' โ ACTIVE') : '';
|
|
156
|
+
const thinking = model.thinking ? chalk.yellow(' [๐ง Thinking]') : '';
|
|
157
|
+
console.log(` ${chalk.yellow(key + '.')} ${chalk.white(model.name)}${thinking}${active}`);
|
|
158
|
+
});
|
|
159
|
+
console.log();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// โโโ Change Model โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
163
|
+
async function changeModel(rl) {
|
|
164
|
+
showModels();
|
|
165
|
+
return new Promise(resolve => {
|
|
166
|
+
rl.question(chalk.cyan(' Enter model number (1-7): '), answer => {
|
|
167
|
+
const chosen = MODELS[answer.trim()];
|
|
168
|
+
if (chosen) {
|
|
169
|
+
currentModel = chosen.id;
|
|
170
|
+
enableThinking = chosen.thinking || false;
|
|
171
|
+
const thinkNote = chosen.thinking ? chalk.yellow(' (Thinking mode ON)') : '';
|
|
172
|
+
console.log(chalk.green(`\n โ Model: ${chalk.yellow(chosen.name.trim())}${thinkNote}\n`));
|
|
173
|
+
} else {
|
|
174
|
+
console.log(chalk.red('\n โ Invalid choice\n'));
|
|
175
|
+
}
|
|
176
|
+
resolve();
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// โโโ Toggle Thinking โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
182
|
+
function toggleThinking() {
|
|
183
|
+
enableThinking = !enableThinking;
|
|
184
|
+
if (enableThinking) {
|
|
185
|
+
console.log(chalk.green('\n ๐ง Thinking mode: ON\n'));
|
|
186
|
+
} else {
|
|
187
|
+
console.log(chalk.gray('\n ๐ค Thinking mode: OFF\n'));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
// โโโ Show Info โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
194
|
+
function showInfo() {
|
|
195
|
+
const infoBox = boxen(
|
|
196
|
+
chalk.bold.white('โ Current Settings\n') +
|
|
197
|
+
chalk.gray('โ'.repeat(40)) + '\n\n' +
|
|
198
|
+
chalk.cyan('User : ') + chalk.green(currentUser ? currentUser.name : 'Unknown') + '\n' +
|
|
199
|
+
chalk.cyan('Messages : ') + chalk.white(chatHistory.length + ' in history') + '\n' +
|
|
200
|
+
chalk.cyan('API URL : ') + chalk.gray(NVIDIA_API_BASE),
|
|
201
|
+
{
|
|
202
|
+
padding: 1,
|
|
203
|
+
margin: { left: 2 },
|
|
204
|
+
borderStyle: 'single',
|
|
205
|
+
borderColor: 'yellow',
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
console.log('\n' + infoBox);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// โโโ Show History โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
212
|
+
function showHistory() {
|
|
213
|
+
if (chatHistory.length === 0) {
|
|
214
|
+
console.log(chalk.yellow('\n No chat history yet.\n'));
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
console.log('\n' + chalk.bold.cyan(' ๐ Chat History:\n'));
|
|
218
|
+
chatHistory.forEach((msg, i) => {
|
|
219
|
+
if (msg.role === 'user') {
|
|
220
|
+
console.log(chalk.gray(` [${i + 1}] `) + userGradient('You: ') + chalk.white(msg.content));
|
|
221
|
+
} else {
|
|
222
|
+
const preview = msg.content.substring(0, 80) + (msg.content.length > 80 ? '...' : '');
|
|
223
|
+
console.log(chalk.gray(` [${i + 1}] `) + aiGradient('AI: ') + chalk.gray(preview));
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
console.log();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// โโโ Save Chat โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
230
|
+
async function saveChat() {
|
|
231
|
+
if (chatHistory.length === 0) {
|
|
232
|
+
console.log(chalk.yellow('\n No chat history to save.\n'));
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const fs = require('fs');
|
|
236
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
237
|
+
const filename = `chat-${timestamp}.txt`;
|
|
238
|
+
const filepath = require('path').join(process.cwd(), filename);
|
|
239
|
+
|
|
240
|
+
let content = `SLEK AI CLI - Chat Log\n`;
|
|
241
|
+
content += `Date: ${new Date().toLocaleString()}\n`;
|
|
242
|
+
content += `Model: ${currentModel}\n`;
|
|
243
|
+
content += `${'โ'.repeat(50)}\n\n`;
|
|
244
|
+
|
|
245
|
+
chatHistory.forEach(msg => {
|
|
246
|
+
if (msg.role === 'user') {
|
|
247
|
+
content += `YOU:\n${msg.content}\n\n`;
|
|
248
|
+
} else {
|
|
249
|
+
content += `AI:\n${msg.content}\n\n`;
|
|
250
|
+
content += `${'โ'.repeat(40)}\n\n`;
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
fs.writeFileSync(filepath, content, 'utf8');
|
|
255
|
+
console.log(chalk.green(`\n โ Chat saved to: ${chalk.yellow(filename)}\n`));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// โโโ Stream API Call โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
259
|
+
async function callNvidiaAPIStream(userMessage) {
|
|
260
|
+
if (!apiKey) {
|
|
261
|
+
throw new Error('API key not configured. Please set NVIDIA_API_KEY in your .env file.');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
chatHistory.push({ role: 'user', content: userMessage });
|
|
265
|
+
|
|
266
|
+
const messages = [
|
|
267
|
+
{
|
|
268
|
+
role: 'system',
|
|
269
|
+
content:
|
|
270
|
+
'You are a helpful, smart, and friendly AI assistant. ' +
|
|
271
|
+
'Provide clear, accurate, and well-structured responses.',
|
|
272
|
+
},
|
|
273
|
+
...chatHistory,
|
|
274
|
+
];
|
|
275
|
+
|
|
276
|
+
const payload = {
|
|
277
|
+
model: DEFAULT_MODEL,
|
|
278
|
+
messages,
|
|
279
|
+
max_tokens: 16384,
|
|
280
|
+
temperature: 0.60,
|
|
281
|
+
top_p: 0.95,
|
|
282
|
+
stream: true,
|
|
283
|
+
chat_template_kwargs: { enable_thinking: true },
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const response = await axios.post(
|
|
287
|
+
`${NVIDIA_API_BASE}/chat-free`,
|
|
288
|
+
payload,
|
|
289
|
+
{
|
|
290
|
+
headers: {
|
|
291
|
+
Authorization: `Bearer ${apiKey}`,
|
|
292
|
+
'Content-Type': 'application/json',
|
|
293
|
+
Accept: 'text/event-stream',
|
|
294
|
+
},
|
|
295
|
+
responseType: 'stream',
|
|
296
|
+
timeout: 120000,
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
return new Promise((resolve, reject) => {
|
|
301
|
+
let fullResponse = '';
|
|
302
|
+
let inThinkBlock = false;
|
|
303
|
+
|
|
304
|
+
process.stdout.write('\n ' + aiGradient('โฆ SLEK AI: ') + '\n\n ');
|
|
305
|
+
|
|
306
|
+
response.data.on('data', chunk => {
|
|
307
|
+
const lines = chunk.toString().split('\n');
|
|
308
|
+
for (const line of lines) {
|
|
309
|
+
if (line.startsWith('data: ') && !line.includes('[DONE]')) {
|
|
310
|
+
try {
|
|
311
|
+
const data = JSON.parse(line.slice(6));
|
|
312
|
+
const token = data.choices?.[0]?.delta?.content || '';
|
|
313
|
+
if (!token) continue;
|
|
314
|
+
|
|
315
|
+
fullResponse += token;
|
|
316
|
+
|
|
317
|
+
if (token.includes('<think>')) {
|
|
318
|
+
inThinkBlock = true;
|
|
319
|
+
process.stdout.write('\n ' + thinkGradient('๐ญ Thinking...') + '\n ');
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (token.includes('</think>')) {
|
|
323
|
+
inThinkBlock = false;
|
|
324
|
+
process.stdout.write('\n\n ' + aiGradient('โฆ Answer:') + '\n\n ');
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (inThinkBlock) {
|
|
329
|
+
process.stdout.write(chalk.dim.yellow(token));
|
|
330
|
+
} else {
|
|
331
|
+
process.stdout.write(chalk.white(token));
|
|
332
|
+
}
|
|
333
|
+
} catch { /* ignore malformed SSE lines */ }
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
response.data.on('end', () => {
|
|
339
|
+
const cleanResponse = fullResponse.replace(/<think>[\s\S]*?<\/think>/g, '').trim();
|
|
340
|
+
chatHistory.push({ role: 'assistant', content: cleanResponse });
|
|
341
|
+
console.log('\n');
|
|
342
|
+
resolve(cleanResponse);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
response.data.on('error', reject);
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// โโโ Process User Input โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
350
|
+
async function processInput(input, rl) {
|
|
351
|
+
const trimmed = input.trim();
|
|
352
|
+
if (!trimmed) return;
|
|
353
|
+
|
|
354
|
+
if (trimmed.startsWith('/')) {
|
|
355
|
+
const cmd = trimmed.toLowerCase().split(' ')[0];
|
|
356
|
+
switch (cmd) {
|
|
357
|
+
case '/help': showHelp(); break;
|
|
358
|
+
case '/clear':
|
|
359
|
+
chatHistory = [];
|
|
360
|
+
console.log(chalk.green('\n โ Chat history cleared!\n'));
|
|
361
|
+
break;
|
|
362
|
+
case '/history': showHistory(); break;
|
|
363
|
+
case '/save': await saveChat(); break;
|
|
364
|
+
case '/info': showInfo(); break;
|
|
365
|
+
case '/exit':
|
|
366
|
+
case '/quit':
|
|
367
|
+
console.log('\n' + nvidiaGradient(' ๐ Goodbye! Thanks for using SLEK AI CLI\n'));
|
|
368
|
+
rl.close();
|
|
369
|
+
process.exit(0);
|
|
370
|
+
break;
|
|
371
|
+
default:
|
|
372
|
+
console.log(chalk.red(`\n โ Unknown command: ${cmd}. Type /help for help.\n`));
|
|
373
|
+
}
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const spinner = ora({
|
|
378
|
+
text: chalk.cyan(' Connecting to NVIDIA API...'),
|
|
379
|
+
spinner: 'dots',
|
|
380
|
+
color: 'cyan',
|
|
381
|
+
prefixText: ' ',
|
|
382
|
+
}).start();
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
spinner.stop();
|
|
386
|
+
clearLine();
|
|
387
|
+
await callNvidiaAPIStream(trimmed);
|
|
388
|
+
} catch (err) {
|
|
389
|
+
spinner.stop();
|
|
390
|
+
const errMsg =
|
|
391
|
+
err.response?.data?.detail ||
|
|
392
|
+
err.response?.data?.message ||
|
|
393
|
+
err.message ||
|
|
394
|
+
'Unknown error';
|
|
395
|
+
console.log(
|
|
396
|
+
'\n' +
|
|
397
|
+
boxen(chalk.red('โ Error: ') + chalk.white(errMsg), {
|
|
398
|
+
padding: 1,
|
|
399
|
+
margin: { left: 2 },
|
|
400
|
+
borderStyle: 'round',
|
|
401
|
+
borderColor: 'red',
|
|
402
|
+
}) + '\n'
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// โโโ Prompt โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
408
|
+
function showPrompt(rl) {
|
|
409
|
+
const prompt =
|
|
410
|
+
'\n' +
|
|
411
|
+
chalk.gray(' โโ') +
|
|
412
|
+
userGradient(' You ') +
|
|
413
|
+
chalk.gray('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n') +
|
|
414
|
+
chalk.gray(' โโถ ');
|
|
415
|
+
rl.setPrompt(prompt);
|
|
416
|
+
rl.prompt();
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// โโโ Main โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
420
|
+
async function main() {
|
|
421
|
+
await showBanner();
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
const rl = readline.createInterface({
|
|
426
|
+
input: process.stdin,
|
|
427
|
+
output: process.stdout,
|
|
428
|
+
terminal: true,
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
showPrompt(rl);
|
|
432
|
+
|
|
433
|
+
rl.on('line', async line => {
|
|
434
|
+
await processInput(line, rl);
|
|
435
|
+
showPrompt(rl);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
rl.on('close', () => {
|
|
439
|
+
console.log('\n' + nvidiaGradient(' ๐ Goodbye!\n'));
|
|
440
|
+
process.exit(0);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
rl.on('SIGINT', () => {
|
|
444
|
+
console.log(chalk.yellow('\n\n Press Ctrl+C again or type /exit to quit.\n'));
|
|
445
|
+
showPrompt(rl);
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
main().catch(err => {
|
|
450
|
+
console.error(chalk.red('Fatal Error:'), err.message);
|
|
451
|
+
process.exit(1);
|
|
452
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "slek-ai-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "SLEK AI CLI โ Powered by NVIDIA API",
|
|
5
|
+
"main": "cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"slek": "cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node cli.js"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"axios": "^1.6.0",
|
|
14
|
+
"boxen": "^5.1.2",
|
|
15
|
+
"chalk": "^4.1.2",
|
|
16
|
+
"dotenv": "^16.0.0",
|
|
17
|
+
"figlet": "^1.7.0",
|
|
18
|
+
"gradient-string": "^2.0.2",
|
|
19
|
+
"open": "^8.4.2",
|
|
20
|
+
"ora": "^5.4.1"
|
|
21
|
+
}
|
|
22
|
+
}
|
package/setup.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Quick Setup Script for NVIDIA AI CLI
|
|
6
|
+
* Run: node setup.js
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const readline = require('readline');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const chalk = require('chalk');
|
|
13
|
+
const gradient = require('gradient-string');
|
|
14
|
+
const boxen = require('boxen');
|
|
15
|
+
const figlet = require('figlet');
|
|
16
|
+
|
|
17
|
+
const nvidiaGradient = gradient(['#76b900', '#00c8ff']);
|
|
18
|
+
|
|
19
|
+
async function setup() {
|
|
20
|
+
console.clear();
|
|
21
|
+
|
|
22
|
+
const banner = figlet.textSync('Setup', { font: 'Big' });
|
|
23
|
+
console.log(nvidiaGradient(banner));
|
|
24
|
+
|
|
25
|
+
console.log(
|
|
26
|
+
boxen(
|
|
27
|
+
chalk.bold.white('๐ NVIDIA AI CLI Setup\n\n') +
|
|
28
|
+
chalk.cyan('Step 1: ') + chalk.white('Go to ') + chalk.cyan.underline('https://build.nvidia.com') + '\n' +
|
|
29
|
+
chalk.cyan('Step 2: ') + chalk.white('Sign up / Log in for free\n') +
|
|
30
|
+
chalk.cyan('Step 3: ') + chalk.white('Click on any model โ Get API Key\n') +
|
|
31
|
+
chalk.cyan('Step 4: ') + chalk.white('Copy your API key & paste below'),
|
|
32
|
+
{
|
|
33
|
+
padding: 1,
|
|
34
|
+
margin: { left: 2, bottom: 1 },
|
|
35
|
+
borderStyle: 'double',
|
|
36
|
+
borderColor: 'green',
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
42
|
+
|
|
43
|
+
const question = (q) => new Promise(resolve => rl.question(q, resolve));
|
|
44
|
+
|
|
45
|
+
const apiKey = await question(chalk.cyan(' Enter your NVIDIA API Key: '));
|
|
46
|
+
|
|
47
|
+
if (!apiKey.trim()) {
|
|
48
|
+
console.log(chalk.red('\n โ No API key entered. Setup cancelled.\n'));
|
|
49
|
+
rl.close();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Save to .env file
|
|
54
|
+
const envPath = path.join(__dirname, '.env');
|
|
55
|
+
fs.writeFileSync(envPath, `NVIDIA_API_KEY=${apiKey.trim()}\n`);
|
|
56
|
+
|
|
57
|
+
console.log(
|
|
58
|
+
'\n' +
|
|
59
|
+
boxen(
|
|
60
|
+
chalk.green('โ
Setup Complete!\n\n') +
|
|
61
|
+
chalk.white('API Key saved to .env file\n\n') +
|
|
62
|
+
chalk.gray('Now run: ') + chalk.yellow('node cli.js') + chalk.gray(' to start chatting!'),
|
|
63
|
+
{
|
|
64
|
+
padding: 1,
|
|
65
|
+
margin: { left: 2 },
|
|
66
|
+
borderStyle: 'round',
|
|
67
|
+
borderColor: 'green',
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
rl.close();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
setup().catch(err => {
|
|
76
|
+
console.error(chalk.red('Setup error:'), err.message);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
});
|