slek-ai-cli 1.1.5 → 1.1.7
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/auth.js +59 -84
- package/cli.js +89 -163
- package/package.json +3 -2
package/auth.js
CHANGED
|
@@ -10,23 +10,9 @@ const axios = require('axios');
|
|
|
10
10
|
const chalk = require('chalk');
|
|
11
11
|
const boxen = require('boxen');
|
|
12
12
|
|
|
13
|
-
// ─── Firebase
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const FIREBASE_API_KEY = 'AIzaSyBoQHn_adTTj1ZaYZBMHCMSAblCGCIbQG4';
|
|
17
|
-
const GOOGLE_CLIENT_ID = '763614479011-32ci230ubh0kuvg1g1ie3jtoeir9orap.apps.googleusercontent.com
|
|
18
|
-
'; // e.g. 123456.apps.googleusercontent.com
|
|
19
|
-
|
|
20
|
-
// Additional Firebase config (for future SDK use if needed)
|
|
21
|
-
const FIREBASE_CONFIG = {
|
|
22
|
-
apiKey: 'AIzaSyBoQHn_adTTj1ZaYZBMHCMSAblCGCIbQG4',
|
|
23
|
-
authDomain: 'charm-f004f.firebaseapp.com',
|
|
24
|
-
projectId: 'charm-f004f',
|
|
25
|
-
storageBucket: 'charm-f004f.firebasestorage.app',
|
|
26
|
-
messagingSenderId: '763614479011',
|
|
27
|
-
appId: '1:763614479011:web:5cd0082b14b78b9c5eb516',
|
|
28
|
-
measurementId: 'G-0G0F5DT6PC',
|
|
29
|
-
};
|
|
13
|
+
// ─── Firebase config (hardcoded — all values from Firebase Console) ───────────
|
|
14
|
+
const FIREBASE_API_KEY = 'AIzaSyBoQHn_adTTj1ZaYZBMHCMSAblCGCIbQG4';
|
|
15
|
+
const FIREBASE_AUTH_DOMAIN = 'charm-f004f.firebaseapp.com';
|
|
30
16
|
|
|
31
17
|
const REDIRECT_URI = 'http://localhost:9876/callback';
|
|
32
18
|
const AUTH_PORT = 9876;
|
|
@@ -66,7 +52,7 @@ function getUser() {
|
|
|
66
52
|
return loadToken();
|
|
67
53
|
}
|
|
68
54
|
|
|
69
|
-
// ─── Open browser cross-platform
|
|
55
|
+
// ─── Open browser cross-platform ─────────────────────────────────────────────
|
|
70
56
|
function openBrowser(targetUrl) {
|
|
71
57
|
try {
|
|
72
58
|
const platform = process.platform;
|
|
@@ -84,73 +70,72 @@ function openBrowser(targetUrl) {
|
|
|
84
70
|
}
|
|
85
71
|
}
|
|
86
72
|
|
|
87
|
-
// ───
|
|
88
|
-
// Uses Firebase's
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
'
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
);
|
|
73
|
+
// ─── Firebase Google Sign-In URL ──────────────────────────────────────────────
|
|
74
|
+
// Uses Firebase's own OAuth redirect — no separate Google Client ID needed.
|
|
75
|
+
function buildFirebaseAuthUrl() {
|
|
76
|
+
const base = `https://${FIREBASE_AUTH_DOMAIN}/__/auth/handler`;
|
|
77
|
+
const params = new URLSearchParams({
|
|
78
|
+
apiKey: FIREBASE_API_KEY,
|
|
79
|
+
appName: '[DEFAULT]',
|
|
80
|
+
authType: 'signInViaRedirect',
|
|
81
|
+
redirectUrl: REDIRECT_URI,
|
|
82
|
+
providerId: 'google.com',
|
|
83
|
+
scopes: 'profile,email',
|
|
84
|
+
v: '10',
|
|
85
|
+
});
|
|
86
|
+
return `${base}?${params.toString()}`;
|
|
87
|
+
}
|
|
103
88
|
|
|
104
|
-
|
|
89
|
+
// ─── Exchange Firebase redirect token ────────────────────────────────────────
|
|
90
|
+
// After Firebase redirect, we get an id_token in the callback URL fragment/query.
|
|
91
|
+
async function handleFirebaseCallback(queryParams) {
|
|
92
|
+
// Firebase returns id_token directly in the callback
|
|
93
|
+
const idToken = queryParams.id_token || queryParams.idToken;
|
|
94
|
+
|
|
95
|
+
if (idToken) {
|
|
96
|
+
// Verify and get user info from Firebase
|
|
97
|
+
const res = await axios.post(
|
|
98
|
+
`https://identitytoolkit.googleapis.com/v1/accounts:lookup?key=${FIREBASE_API_KEY}`,
|
|
99
|
+
{ idToken }
|
|
100
|
+
);
|
|
101
|
+
const user = res.data.users?.[0];
|
|
102
|
+
if (!user) throw new Error('User not found');
|
|
103
|
+
return {
|
|
104
|
+
name: user.displayName || user.email,
|
|
105
|
+
email: user.email,
|
|
106
|
+
photo: user.photoUrl || '',
|
|
107
|
+
idToken,
|
|
108
|
+
expiresAt: Date.now() + 3600 * 1000, // 1 hour
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Fallback: if Firebase sends an auth code instead
|
|
113
|
+
const code = queryParams.code;
|
|
114
|
+
if (!code) throw new Error('No token or code received from Firebase');
|
|
105
115
|
|
|
106
|
-
// Step 2: Sign in to Firebase using the Google ID token.
|
|
107
116
|
const firebaseRes = await axios.post(
|
|
108
117
|
`https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp?key=${FIREBASE_API_KEY}`,
|
|
109
118
|
{
|
|
110
|
-
postBody: `id_token=${
|
|
119
|
+
postBody: `id_token=${code}&providerId=google.com`,
|
|
111
120
|
requestUri: REDIRECT_URI,
|
|
112
121
|
returnIdpCredential: true,
|
|
113
122
|
returnSecureToken: true,
|
|
114
123
|
}
|
|
115
124
|
);
|
|
116
125
|
|
|
117
|
-
const { displayName, email, idToken, expiresIn, photoUrl } = firebaseRes.data;
|
|
118
|
-
|
|
126
|
+
const { displayName, email, idToken: token, expiresIn, photoUrl } = firebaseRes.data;
|
|
119
127
|
return {
|
|
120
128
|
name: displayName,
|
|
121
129
|
email,
|
|
122
|
-
photo: photoUrl,
|
|
123
|
-
idToken,
|
|
130
|
+
photo: photoUrl || '',
|
|
131
|
+
idToken: token,
|
|
124
132
|
expiresAt: Date.now() + parseInt(expiresIn) * 1000,
|
|
125
133
|
};
|
|
126
134
|
}
|
|
127
135
|
|
|
128
136
|
// ─── Login ────────────────────────────────────────────────────────────────────
|
|
129
137
|
async function login() {
|
|
130
|
-
|
|
131
|
-
if (
|
|
132
|
-
!GOOGLE_CLIENT_ID || GOOGLE_CLIENT_ID === 'YOUR_GOOGLE_CLIENT_ID'
|
|
133
|
-
) {
|
|
134
|
-
console.log(
|
|
135
|
-
boxen(
|
|
136
|
-
chalk.red('✗ Google Client ID not set!\n\n') +
|
|
137
|
-
chalk.white('Open ') + chalk.yellow('auth.js') + chalk.white(' and replace:\n\n') +
|
|
138
|
-
chalk.cyan('YOUR_GOOGLE_CLIENT_ID') +
|
|
139
|
-
chalk.white('\n\nwith your OAuth Client ID from Google Cloud Console.'),
|
|
140
|
-
{ padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'red' }
|
|
141
|
-
)
|
|
142
|
-
);
|
|
143
|
-
process.exit(1);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Build Google OAuth URL
|
|
147
|
-
const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');
|
|
148
|
-
authUrl.searchParams.set('client_id', GOOGLE_CLIENT_ID);
|
|
149
|
-
authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
|
|
150
|
-
authUrl.searchParams.set('response_type', 'code');
|
|
151
|
-
authUrl.searchParams.set('scope', 'openid email profile');
|
|
152
|
-
authUrl.searchParams.set('access_type', 'offline');
|
|
153
|
-
authUrl.searchParams.set('prompt', 'select_account');
|
|
138
|
+
const authUrl = buildFirebaseAuthUrl();
|
|
154
139
|
|
|
155
140
|
return new Promise((resolve, reject) => {
|
|
156
141
|
const server = http.createServer(async (req, res) => {
|
|
@@ -163,26 +148,17 @@ async function login() {
|
|
|
163
148
|
return;
|
|
164
149
|
}
|
|
165
150
|
|
|
166
|
-
const code = parsed.query.code;
|
|
167
|
-
const error = parsed.query.error;
|
|
168
|
-
|
|
169
151
|
// Send success page to browser immediately
|
|
170
152
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
171
153
|
res.end(`
|
|
172
154
|
<html>
|
|
173
155
|
<body style="
|
|
174
|
-
background
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
height: 100vh;
|
|
181
|
-
margin: 0;
|
|
182
|
-
">
|
|
183
|
-
<div style="text-align: center;">
|
|
184
|
-
<h1 style="color: #76b900;">✓ Login Successful!</h1>
|
|
185
|
-
<p style="color: #aaa;">You can close this tab and return to the terminal.</p>
|
|
156
|
+
background:#0a0a0a;color:#fff;font-family:monospace;
|
|
157
|
+
display:flex;align-items:center;justify-content:center;
|
|
158
|
+
height:100vh;margin:0;">
|
|
159
|
+
<div style="text-align:center;">
|
|
160
|
+
<h1 style="color:#76b900;">✓ Login Successful!</h1>
|
|
161
|
+
<p style="color:#aaa;">You can close this tab and return to the terminal.</p>
|
|
186
162
|
</div>
|
|
187
163
|
</body>
|
|
188
164
|
</html>
|
|
@@ -190,6 +166,7 @@ async function login() {
|
|
|
190
166
|
|
|
191
167
|
server.close();
|
|
192
168
|
|
|
169
|
+
const error = parsed.query.error;
|
|
193
170
|
if (error) {
|
|
194
171
|
console.log(chalk.red('\n ✗ Login cancelled.\n'));
|
|
195
172
|
reject(new Error(error));
|
|
@@ -198,7 +175,7 @@ async function login() {
|
|
|
198
175
|
|
|
199
176
|
try {
|
|
200
177
|
process.stdout.write(chalk.cyan('\n ⏳ Verifying with Firebase...\n'));
|
|
201
|
-
const userData = await
|
|
178
|
+
const userData = await handleFirebaseCallback(parsed.query);
|
|
202
179
|
saveToken(userData);
|
|
203
180
|
|
|
204
181
|
console.log(
|
|
@@ -236,12 +213,10 @@ async function login() {
|
|
|
236
213
|
{ padding: 1, margin: { left: 2 }, borderStyle: 'double', borderColor: 'green' }
|
|
237
214
|
)
|
|
238
215
|
);
|
|
239
|
-
|
|
240
|
-
// Small delay to ensure server is ready before opening browser
|
|
241
216
|
setTimeout(() => openBrowser(authUrl.toString()), 500);
|
|
242
217
|
});
|
|
243
218
|
|
|
244
|
-
// Auto-cancel
|
|
219
|
+
// Auto-cancel after 2 minutes
|
|
245
220
|
setTimeout(() => {
|
|
246
221
|
server.close();
|
|
247
222
|
console.log(chalk.red('\n ✗ Login timed out (2 min). Please try again.\n'));
|
package/cli.js
CHANGED
|
@@ -1,28 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
path: require('path').join(__dirname, '.env')
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
// ─── Auth check ───────────────────────────────────────────────────────────────
|
|
4
|
+
// ─── Auth ─────────────────────────────────────────────────────────────────────
|
|
9
5
|
const auth = require('./auth');
|
|
10
6
|
|
|
11
7
|
const arg = process.argv[2];
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
if (arg === '
|
|
15
|
-
|
|
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
|
-
}
|
|
9
|
+
if (arg === 'login') { auth.login().catch(() => process.exit(1)); return; }
|
|
10
|
+
if (arg === 'logout') { auth.logout(); process.exit(0); }
|
|
11
|
+
if (arg === 'status') { auth.status(); process.exit(0); }
|
|
26
12
|
|
|
27
13
|
// Block access if not logged in
|
|
28
14
|
if (!auth.isLoggedIn()) {
|
|
@@ -40,31 +26,31 @@ if (!auth.isLoggedIn()) {
|
|
|
40
26
|
}
|
|
41
27
|
|
|
42
28
|
const readline = require('readline');
|
|
43
|
-
const axios
|
|
44
|
-
const chalk
|
|
45
|
-
const boxen
|
|
29
|
+
const axios = require('axios');
|
|
30
|
+
const chalk = require('chalk');
|
|
31
|
+
const boxen = require('boxen');
|
|
46
32
|
const gradient = require('gradient-string');
|
|
47
|
-
const figlet
|
|
33
|
+
const figlet = require('figlet');
|
|
48
34
|
|
|
49
|
-
// ─── NOTE: ora v5 is required (CommonJS compatible) ──────────────────────────
|
|
50
|
-
// If ora gives errors, run: npm install ora@5
|
|
51
35
|
let ora;
|
|
52
36
|
try {
|
|
53
37
|
ora = require('ora');
|
|
54
38
|
} catch {
|
|
55
|
-
// Fallback spinner if ora fails
|
|
56
39
|
ora = (opts) => ({
|
|
57
|
-
start: () => {
|
|
40
|
+
start: () => {
|
|
41
|
+
process.stdout.write(chalk.cyan(' ⏳ ' + (opts.text || 'Loading...') + '\n'));
|
|
42
|
+
return { stop: () => {}, fail: () => {} };
|
|
43
|
+
},
|
|
58
44
|
stop: () => {},
|
|
59
45
|
fail: () => {},
|
|
60
46
|
});
|
|
61
47
|
}
|
|
62
48
|
|
|
63
|
-
// ───
|
|
49
|
+
// ─── API Config ───────────────────────────────────────────────────────────────
|
|
64
50
|
const NVIDIA_API_BASE = 'https://slek-ai-portal.vercel.app/api';
|
|
65
|
-
const DEFAULT_MODEL
|
|
51
|
+
const DEFAULT_MODEL = 'qwen/qwen3.5-122b-a10b';
|
|
66
52
|
|
|
67
|
-
// ─── User config path
|
|
53
|
+
// ─── User config path ─────────────────────────────────────────────────────────
|
|
68
54
|
const CONFIG_PATH = require('path').join(
|
|
69
55
|
process.env.APPDATA || process.env.HOME || __dirname,
|
|
70
56
|
'.slek-config.json'
|
|
@@ -75,24 +61,28 @@ function loadConfig() {
|
|
|
75
61
|
if (require('fs').existsSync(CONFIG_PATH)) {
|
|
76
62
|
return JSON.parse(require('fs').readFileSync(CONFIG_PATH, 'utf8'));
|
|
77
63
|
}
|
|
78
|
-
} catch {
|
|
64
|
+
} catch (_) {}
|
|
79
65
|
return {};
|
|
80
66
|
}
|
|
81
67
|
|
|
82
68
|
function saveConfig(data) {
|
|
83
69
|
const current = loadConfig();
|
|
84
|
-
require('fs').writeFileSync(
|
|
70
|
+
require('fs').writeFileSync(
|
|
71
|
+
CONFIG_PATH,
|
|
72
|
+
JSON.stringify({ ...current, ...data }, null, 2),
|
|
73
|
+
'utf8'
|
|
74
|
+
);
|
|
85
75
|
}
|
|
86
76
|
|
|
87
|
-
// ───
|
|
77
|
+
// ─── Gradients ────────────────────────────────────────────────────────────────
|
|
88
78
|
const nvidiaGradient = gradient(['#76b900', '#00c8ff']);
|
|
89
|
-
const userGradient
|
|
90
|
-
const aiGradient
|
|
91
|
-
const thinkGradient
|
|
79
|
+
const userGradient = gradient(['#ff6b6b', '#feca57']);
|
|
80
|
+
const aiGradient = gradient(['#48dbfb', '#ff9ff3']);
|
|
81
|
+
const thinkGradient = gradient(['#f7971e', '#ffd200']);
|
|
92
82
|
|
|
93
|
-
// ─── State
|
|
94
|
-
let chatHistory
|
|
95
|
-
let apiKey
|
|
83
|
+
// ─── State ────────────────────────────────────────────────────────────────────
|
|
84
|
+
let chatHistory = [];
|
|
85
|
+
let apiKey = loadConfig().apiKey || '';
|
|
96
86
|
const currentUser = auth.getUser();
|
|
97
87
|
|
|
98
88
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
@@ -103,17 +93,14 @@ function clearLine() {
|
|
|
103
93
|
// ─── Banner ───────────────────────────────────────────────────────────────────
|
|
104
94
|
async function showBanner() {
|
|
105
95
|
console.clear();
|
|
106
|
-
const banner = figlet.textSync('SLEK AI', {
|
|
107
|
-
font: 'ANSI Shadow',
|
|
108
|
-
horizontalLayout: 'default',
|
|
109
|
-
});
|
|
96
|
+
const banner = figlet.textSync('SLEK AI', { font: 'ANSI Shadow' });
|
|
110
97
|
console.log(nvidiaGradient(banner));
|
|
111
98
|
console.log(
|
|
112
99
|
boxen(
|
|
113
100
|
chalk.bold.white('🚀 SLEK AI CLI') + chalk.gray(' — Powered by NVIDIA API\n') +
|
|
114
|
-
chalk.gray('
|
|
101
|
+
chalk.gray('─'.repeat(42)) + '\n' +
|
|
115
102
|
chalk.cyan(' User : ') + chalk.green(currentUser ? currentUser.name : 'Unknown') + '\n' +
|
|
116
|
-
chalk.gray('
|
|
103
|
+
chalk.gray('─'.repeat(42)) + '\n' +
|
|
117
104
|
chalk.white(' Type ') + chalk.yellow('/help') + chalk.white(' for available commands'),
|
|
118
105
|
{
|
|
119
106
|
padding: 1,
|
|
@@ -128,87 +115,37 @@ async function showBanner() {
|
|
|
128
115
|
|
|
129
116
|
// ─── Help ─────────────────────────────────────────────────────────────────────
|
|
130
117
|
function showHelp() {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
padding: 1,
|
|
143
|
-
|
|
144
|
-
borderStyle: 'round',
|
|
145
|
-
borderColor: 'cyan',
|
|
146
|
-
}
|
|
118
|
+
console.log(
|
|
119
|
+
boxen(
|
|
120
|
+
chalk.bold.cyan('📋 Available Commands\n') +
|
|
121
|
+
chalk.gray('═'.repeat(42)) + '\n\n' +
|
|
122
|
+
chalk.yellow('/help ') + chalk.white('Show this help menu\n') +
|
|
123
|
+
chalk.yellow('/clear ') + chalk.white('Clear chat history\n') +
|
|
124
|
+
chalk.yellow('/history ') + chalk.white('Show chat history\n') +
|
|
125
|
+
chalk.yellow('/save ') + chalk.white('Save chat to file\n') +
|
|
126
|
+
chalk.yellow('/info ') + chalk.white('Show current settings\n') +
|
|
127
|
+
chalk.yellow('/exit ') + chalk.white('Exit the CLI\n\n') +
|
|
128
|
+
chalk.gray('Or just type anything to chat with AI!'),
|
|
129
|
+
{ padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'cyan' }
|
|
130
|
+
)
|
|
147
131
|
);
|
|
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
132
|
}
|
|
190
133
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
// ─── Show Info ────────────────────────────────────────────────────────────────
|
|
134
|
+
// ─── Info ─────────────────────────────────────────────────────────────────────
|
|
194
135
|
function showInfo() {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
padding: 1,
|
|
203
|
-
|
|
204
|
-
borderStyle: 'single',
|
|
205
|
-
borderColor: 'yellow',
|
|
206
|
-
}
|
|
136
|
+
console.log(
|
|
137
|
+
'\n' + boxen(
|
|
138
|
+
chalk.bold.white('⚙ Current Settings\n') +
|
|
139
|
+
chalk.gray('─'.repeat(40)) + '\n\n' +
|
|
140
|
+
chalk.cyan('User : ') + chalk.green(currentUser ? currentUser.name : 'Unknown') + '\n' +
|
|
141
|
+
chalk.cyan('Messages : ') + chalk.white(chatHistory.length + ' in history') + '\n' +
|
|
142
|
+
chalk.cyan('API URL : ') + chalk.gray(NVIDIA_API_BASE),
|
|
143
|
+
{ padding: 1, margin: { left: 2 }, borderStyle: 'single', borderColor: 'yellow' }
|
|
144
|
+
)
|
|
207
145
|
);
|
|
208
|
-
console.log('\n' + infoBox);
|
|
209
146
|
}
|
|
210
147
|
|
|
211
|
-
// ───
|
|
148
|
+
// ─── History ──────────────────────────────────────────────────────────────────
|
|
212
149
|
function showHistory() {
|
|
213
150
|
if (chatHistory.length === 0) {
|
|
214
151
|
console.log(chalk.yellow('\n No chat history yet.\n'));
|
|
@@ -239,7 +176,7 @@ async function saveChat() {
|
|
|
239
176
|
|
|
240
177
|
let content = `SLEK AI CLI - Chat Log\n`;
|
|
241
178
|
content += `Date: ${new Date().toLocaleString()}\n`;
|
|
242
|
-
content += `Model: ${
|
|
179
|
+
content += `Model: ${DEFAULT_MODEL}\n`;
|
|
243
180
|
content += `${'═'.repeat(50)}\n\n`;
|
|
244
181
|
|
|
245
182
|
chatHistory.forEach(msg => {
|
|
@@ -252,13 +189,13 @@ async function saveChat() {
|
|
|
252
189
|
});
|
|
253
190
|
|
|
254
191
|
fs.writeFileSync(filepath, content, 'utf8');
|
|
255
|
-
console.log(chalk.green(`\n
|
|
192
|
+
console.log(chalk.green(`\n ✔ Chat saved to: ${chalk.yellow(filename)}\n`));
|
|
256
193
|
}
|
|
257
194
|
|
|
258
195
|
// ─── Stream API Call ──────────────────────────────────────────────────────────
|
|
259
196
|
async function callNvidiaAPIStream(userMessage) {
|
|
260
197
|
if (!apiKey) {
|
|
261
|
-
throw new Error('API key not
|
|
198
|
+
throw new Error('API key not set. Ask the admin for your SLEK API key.');
|
|
262
199
|
}
|
|
263
200
|
|
|
264
201
|
chatHistory.push({ role: 'user', content: userMessage });
|
|
@@ -273,19 +210,17 @@ async function callNvidiaAPIStream(userMessage) {
|
|
|
273
210
|
...chatHistory,
|
|
274
211
|
];
|
|
275
212
|
|
|
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
213
|
const response = await axios.post(
|
|
287
214
|
`${NVIDIA_API_BASE}/chat-free`,
|
|
288
|
-
|
|
215
|
+
{
|
|
216
|
+
model: DEFAULT_MODEL,
|
|
217
|
+
messages,
|
|
218
|
+
max_tokens: 16384,
|
|
219
|
+
temperature: 0.60,
|
|
220
|
+
top_p: 0.95,
|
|
221
|
+
stream: true,
|
|
222
|
+
chat_template_kwargs: { enable_thinking: true },
|
|
223
|
+
},
|
|
289
224
|
{
|
|
290
225
|
headers: {
|
|
291
226
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -298,8 +233,8 @@ async function callNvidiaAPIStream(userMessage) {
|
|
|
298
233
|
);
|
|
299
234
|
|
|
300
235
|
return new Promise((resolve, reject) => {
|
|
301
|
-
let fullResponse
|
|
302
|
-
let inThinkBlock
|
|
236
|
+
let fullResponse = '';
|
|
237
|
+
let inThinkBlock = false;
|
|
303
238
|
|
|
304
239
|
process.stdout.write('\n ' + aiGradient('✦ SLEK AI: ') + '\n\n ');
|
|
305
240
|
|
|
@@ -325,12 +260,10 @@ async function callNvidiaAPIStream(userMessage) {
|
|
|
325
260
|
continue;
|
|
326
261
|
}
|
|
327
262
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
}
|
|
333
|
-
} catch { /* ignore malformed SSE lines */ }
|
|
263
|
+
process.stdout.write(
|
|
264
|
+
inThinkBlock ? chalk.dim.yellow(token) : chalk.white(token)
|
|
265
|
+
);
|
|
266
|
+
} catch (_) { /* ignore malformed SSE lines */ }
|
|
334
267
|
}
|
|
335
268
|
}
|
|
336
269
|
});
|
|
@@ -346,7 +279,7 @@ async function callNvidiaAPIStream(userMessage) {
|
|
|
346
279
|
});
|
|
347
280
|
}
|
|
348
281
|
|
|
349
|
-
// ─── Process
|
|
282
|
+
// ─── Process Input ────────────────────────────────────────────────────────────
|
|
350
283
|
async function processInput(input, rl) {
|
|
351
284
|
const trimmed = input.trim();
|
|
352
285
|
if (!trimmed) return;
|
|
@@ -354,14 +287,14 @@ async function processInput(input, rl) {
|
|
|
354
287
|
if (trimmed.startsWith('/')) {
|
|
355
288
|
const cmd = trimmed.toLowerCase().split(' ')[0];
|
|
356
289
|
switch (cmd) {
|
|
357
|
-
case '/help': showHelp();
|
|
290
|
+
case '/help': showHelp(); break;
|
|
358
291
|
case '/clear':
|
|
359
292
|
chatHistory = [];
|
|
360
|
-
console.log(chalk.green('\n
|
|
293
|
+
console.log(chalk.green('\n ✔ Chat history cleared!\n'));
|
|
361
294
|
break;
|
|
362
|
-
case '/history': showHistory();
|
|
363
|
-
case '/save': await saveChat();
|
|
364
|
-
case '/info': showInfo();
|
|
295
|
+
case '/history': showHistory(); break;
|
|
296
|
+
case '/save': await saveChat(); break;
|
|
297
|
+
case '/info': showInfo(); break;
|
|
365
298
|
case '/exit':
|
|
366
299
|
case '/quit':
|
|
367
300
|
console.log('\n' + nvidiaGradient(' 👋 Goodbye! Thanks for using SLEK AI CLI\n'));
|
|
@@ -375,9 +308,9 @@ async function processInput(input, rl) {
|
|
|
375
308
|
}
|
|
376
309
|
|
|
377
310
|
const spinner = ora({
|
|
378
|
-
text:
|
|
379
|
-
spinner:
|
|
380
|
-
color:
|
|
311
|
+
text: chalk.cyan(' Connecting to NVIDIA API...'),
|
|
312
|
+
spinner: 'dots',
|
|
313
|
+
color: 'cyan',
|
|
381
314
|
prefixText: ' ',
|
|
382
315
|
}).start();
|
|
383
316
|
|
|
@@ -393,13 +326,10 @@ async function processInput(input, rl) {
|
|
|
393
326
|
err.message ||
|
|
394
327
|
'Unknown error';
|
|
395
328
|
console.log(
|
|
396
|
-
'\n' +
|
|
397
|
-
|
|
398
|
-
padding: 1,
|
|
399
|
-
|
|
400
|
-
borderStyle: 'round',
|
|
401
|
-
borderColor: 'red',
|
|
402
|
-
}) + '\n'
|
|
329
|
+
'\n' + boxen(
|
|
330
|
+
chalk.red('✗ Error: ') + chalk.white(errMsg),
|
|
331
|
+
{ padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'red' }
|
|
332
|
+
) + '\n'
|
|
403
333
|
);
|
|
404
334
|
}
|
|
405
335
|
}
|
|
@@ -408,9 +338,7 @@ async function processInput(input, rl) {
|
|
|
408
338
|
function showPrompt(rl) {
|
|
409
339
|
const prompt =
|
|
410
340
|
'\n' +
|
|
411
|
-
chalk.gray(' ┌─') +
|
|
412
|
-
userGradient(' You ') +
|
|
413
|
-
chalk.gray('─────────────────────────────────\n') +
|
|
341
|
+
chalk.gray(' ┌─') + userGradient(' You ') + chalk.gray('─────────────────────────────────\n') +
|
|
414
342
|
chalk.gray(' └▶ ');
|
|
415
343
|
rl.setPrompt(prompt);
|
|
416
344
|
rl.prompt();
|
|
@@ -420,11 +348,9 @@ function showPrompt(rl) {
|
|
|
420
348
|
async function main() {
|
|
421
349
|
await showBanner();
|
|
422
350
|
|
|
423
|
-
|
|
424
|
-
|
|
425
351
|
const rl = readline.createInterface({
|
|
426
|
-
input:
|
|
427
|
-
output:
|
|
352
|
+
input: process.stdin,
|
|
353
|
+
output: process.stdout,
|
|
428
354
|
terminal: true,
|
|
429
355
|
});
|
|
430
356
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "slek-ai-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.7",
|
|
4
4
|
"description": "SLEK AI CLI — Powered by NVIDIA API",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"figlet": "^1.7.0",
|
|
18
18
|
"gradient-string": "^2.0.2",
|
|
19
19
|
"open": "^8.4.2",
|
|
20
|
-
"ora": "^5.4.1"
|
|
20
|
+
"ora": "^5.4.1",
|
|
21
|
+
"slek-ai-cli": "^1.1.6"
|
|
21
22
|
}
|
|
22
23
|
}
|