slek-ai-cli 1.1.10 → 1.1.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.
Files changed (4) hide show
  1. package/.env +1 -0
  2. package/auth.js +96 -128
  3. package/cli.js +168 -93
  4. package/package.json +1 -1
package/.env CHANGED
@@ -5,3 +5,4 @@ FIREBASE_API_KEY="AIzaSyBoQHn_adTTj1ZaYZBMHCMSAblCGCIbQG4"
5
5
  FIREBASE_AUTH_DOMAIN="charm-f004f.firebaseapp.com"
6
6
  GOOGLE_CLIENT_ID="763614479011-v2m44ees6jhjaprj5ndoa0up83gf1qah.apps.googleusercontent.com"
7
7
  GOOGLE_CLIENT_SECRET="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"
8
+ FIREBASE_PROJECT_ID="charm-f004f"
package/auth.js CHANGED
@@ -1,34 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- const http = require('http');
5
- const urlModule = require('url');
6
- const path = require('path');
7
- const fs = require('fs');
4
+ const http = require('http');
5
+ const urlModule = require('url');
6
+ const path = require('path');
7
+ const fs = require('fs');
8
8
  const { execSync } = require('child_process');
9
- const axios = require('axios');
10
- const chalk = require('chalk');
11
- const boxen = require('boxen');
9
+ const axios = require('axios');
10
+ const chalk = require('chalk');
11
+ const boxen = require('boxen');
12
12
 
13
- // Load environment variables from .env
14
13
  require('dotenv').config({ path: path.join(__dirname, '.env') });
15
14
 
16
- // ─── Firebase / Google config ────────────────────────────────────────────────
17
- const FIREBASE_API_KEY = process.env.FIREBASE_API_KEY || 'AIzaSyBoQHn_adTTj1ZaYZBMHCMSAblCGCIbQG4';
18
- const FIREBASE_AUTH_DOMAIN = process.env.FIREBASE_AUTH_DOMAIN || 'charm-f004f.firebaseapp.com';
19
- const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID || '763614479011-v2m44ees6jhjaprj5ndoa0up83gf1qah.apps.googleusercontent.com';
15
+ const FIREBASE_API_KEY = process.env.FIREBASE_API_KEY || 'AIzaSyBoQHn_adTTj1ZaYZBMHCMSAblCGCIbQG4';
16
+ const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID || '763614479011-v2m44ees6jhjaprj5ndoa0up83gf1qah.apps.googleusercontent.com';
20
17
  const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET || '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';
18
+ const REDIRECT_URI = 'http://localhost:9876/callback';
19
+ const AUTH_PORT = 9876;
21
20
 
22
- const REDIRECT_URI = 'http://localhost:9876/callback';
23
- const AUTH_PORT = 9876;
24
-
25
- // ─── Token storage (saved in user home directory) ─────────────────────────────
26
21
  const TOKEN_PATH = path.join(
27
22
  process.env.APPDATA || process.env.HOME || __dirname,
28
23
  '.slek-auth.json'
29
24
  );
30
25
 
31
- // ─── Token helpers ────────────────────────────────────────────────────────────
32
26
  function saveToken(data) {
33
27
  fs.writeFileSync(TOKEN_PATH, JSON.stringify(data, null, 2), 'utf8');
34
28
  }
@@ -38,26 +32,66 @@ function loadToken() {
38
32
  if (fs.existsSync(TOKEN_PATH)) {
39
33
  return JSON.parse(fs.readFileSync(TOKEN_PATH, 'utf8'));
40
34
  }
41
- } catch (_) {}
35
+ } catch (_) { }
42
36
  return null;
43
37
  }
44
38
 
45
39
  function clearToken() {
46
- try { fs.unlinkSync(TOKEN_PATH); } catch (_) {}
40
+ try { fs.unlinkSync(TOKEN_PATH); } catch (_) { }
47
41
  }
48
42
 
49
43
  function isLoggedIn() {
50
44
  const token = loadToken();
51
45
  if (!token) return false;
52
- if (token.expiresAt && Date.now() > token.expiresAt) return false;
46
+ // Consider logged in if token exists (refresh will handle expiry)
53
47
  return true;
54
48
  }
55
49
 
56
- function getUser() {
50
+ // ─── Auto-refresh Firebase token ─────────────────────────────────────────────
51
+ async function refreshIdToken(token) {
52
+ try {
53
+ const res = await axios.post(
54
+ `https://securetoken.googleapis.com/v1/token?key=${FIREBASE_API_KEY}`,
55
+ {
56
+ grant_type: 'refresh_token',
57
+ refresh_token: token.refreshToken,
58
+ }
59
+ );
60
+ const updated = {
61
+ ...token,
62
+ idToken: res.data.id_token,
63
+ refreshToken: res.data.refresh_token,
64
+ expiresAt: Date.now() + parseInt(res.data.expires_in) * 1000,
65
+ };
66
+ saveToken(updated);
67
+ return updated;
68
+ } catch (_) {
69
+ clearToken();
70
+ return null;
71
+ }
72
+ }
73
+
74
+ // ─── Get user with auto-refresh ───────────────────────────────────────────────
75
+ async function getUser() {
76
+ let token = loadToken();
77
+ if (!token) return null;
78
+ // Refresh if expired or about to expire (within 5 min)
79
+ if (!token.expiresAt || Date.now() > token.expiresAt - 5 * 60 * 1000) {
80
+ if (token.refreshToken) {
81
+ token = await refreshIdToken(token);
82
+ } else {
83
+ clearToken();
84
+ return null;
85
+ }
86
+ }
87
+ return token;
88
+ }
89
+
90
+ // ─── Sync version for quick checks ───────────────────────────────────────────
91
+ function getUserSync() {
57
92
  return loadToken();
58
93
  }
59
94
 
60
- // ─── Open browser cross-platform ─────────────────────────────────────────────
61
95
  function openBrowser(targetUrl) {
62
96
  try {
63
97
  const platform = process.platform;
@@ -75,7 +109,6 @@ function openBrowser(targetUrl) {
75
109
  }
76
110
  }
77
111
 
78
- // ─── Google OAuth URL ────────────────────────────────────────────────────────
79
112
  function buildGoogleAuthUrl() {
80
113
  const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');
81
114
  authUrl.searchParams.set('client_id', GOOGLE_CLIENT_ID);
@@ -87,7 +120,6 @@ function buildGoogleAuthUrl() {
87
120
  return authUrl.toString();
88
121
  }
89
122
 
90
- // ─── Exchange Google code to Firebase token ──────────────────────────────────
91
123
  async function exchangeCodeForToken(code) {
92
124
  const tokenRes = await axios.post('https://oauth2.googleapis.com/token', {
93
125
  code,
@@ -109,86 +141,42 @@ async function exchangeCodeForToken(code) {
109
141
  }
110
142
  );
111
143
 
112
- const { displayName, email, idToken, expiresIn, photoUrl } = firebaseRes.data;
144
+ const { displayName, email, idToken, refreshToken, expiresIn, photoUrl } = firebaseRes.data;
113
145
  return {
114
- name: displayName,
146
+ name: displayName,
115
147
  email,
116
- photo: photoUrl || '',
148
+ photo: photoUrl || '',
117
149
  idToken,
118
- expiresAt: Date.now() + parseInt(expiresIn) * 1000,
150
+ refreshToken,
151
+ expiresAt: Date.now() + parseInt(expiresIn) * 1000,
119
152
  };
120
153
  }
121
154
 
122
- // ─── Login ────────────────────────────────────────────────────────────────────
123
155
  async function login() {
124
- if (!GOOGLE_CLIENT_ID || !GOOGLE_CLIENT_SECRET) {
125
- console.log(
126
- boxen(
127
- chalk.red('✗ Google OAuth Client configuration missing!\n\n') +
128
- chalk.white('Add GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET to .env'),
129
- { padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'red' }
130
- )
131
- );
132
- process.exit(1);
133
- }
134
-
135
156
  const authUrl = buildGoogleAuthUrl();
136
157
 
137
158
  return new Promise((resolve, reject) => {
138
159
  const server = http.createServer(async (req, res) => {
139
160
  const parsed = urlModule.parse(req.url, true);
161
+ if (parsed.pathname !== '/callback') { res.writeHead(404); res.end(); return; }
140
162
 
141
- // Ignore any request that isn't the OAuth callback
142
- if (parsed.pathname !== '/callback') {
143
- res.writeHead(404);
144
- res.end();
145
- return;
146
- }
147
-
148
- // Send success page to browser immediately
149
163
  res.writeHead(200, { 'Content-Type': 'text/html' });
150
- res.end(`
151
- <html>
152
- <body style="
153
- background:#0a0a0a;color:#fff;font-family:monospace;
154
- display:flex;align-items:center;justify-content:center;
155
- height:100vh;margin:0;">
156
- <div style="text-align:center;">
157
- <h1 style="color:#76b900;">✓ Login Successful!</h1>
158
- <p style="color:#aaa;">You can close this tab and return to the terminal.</p>
159
- </div>
160
- </body>
161
- </html>
162
- `);
163
-
164
+ 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>`);
164
165
  server.close();
165
166
 
166
- const code = parsed.query.code;
167
- const error = parsed.query.error;
168
- if (error) {
169
- console.log(chalk.red('\n ✗ Login cancelled.\n'));
170
- reject(new Error(error));
171
- return;
172
- }
167
+ if (parsed.query.error) { console.log(chalk.red('\n Login cancelled.\n')); reject(new Error(parsed.query.error)); return; }
173
168
 
174
169
  try {
175
170
  process.stdout.write(chalk.cyan('\n ⏳ Verifying with Firebase...\n'));
176
- const userData = await exchangeCodeForToken(code);
171
+ const userData = await exchangeCodeForToken(parsed.query.code);
177
172
  saveToken(userData);
178
-
179
- console.log(
180
- '\n' + boxen(
181
- chalk.green(' Logged in successfully!\n\n') +
182
- chalk.cyan('Name : ') + chalk.white(userData.name) + '\n' +
183
- chalk.cyan('Email : ') + chalk.white(userData.email),
184
- { padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'green' }
185
- )
186
- );
187
- console.log(
188
- chalk.gray('\n Now run ') + chalk.yellow('slek') + chalk.gray(' to start chatting!\n')
189
- console.log(
190
- chalk.gray('\n Now Press ') + chalk.yellow('Ctrl+C') + chalk.gray(' to end the command\n')
191
- );
173
+ console.log('\n' + boxen(
174
+ chalk.green('✓ Logged in successfully!\n\n') +
175
+ chalk.cyan('Name : ') + chalk.white(userData.name) + '\n' +
176
+ chalk.cyan('Email : ') + chalk.white(userData.email),
177
+ { padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'green' }
178
+ ));
179
+ console.log(chalk.gray('\n Now run ') + chalk.yellow('slek') + chalk.gray(' to start chatting!\n'));
192
180
  resolve(userData);
193
181
  } catch (err) {
194
182
  console.log(chalk.red('\n ✗ Auth failed: ') + err.message + '\n');
@@ -196,65 +184,45 @@ async function login() {
196
184
  }
197
185
  });
198
186
 
199
- server.on('error', (err) => {
200
- console.log(chalk.red('\n ✗ Server error: ') + err.message);
201
- reject(err);
202
- });
187
+ server.on('error', err => { console.log(chalk.red('\n ✗ Server error: ') + err.message); reject(err); });
203
188
 
204
189
  server.listen(AUTH_PORT, '127.0.0.1', () => {
205
- console.log(
206
- boxen(
207
- chalk.bold.white('🔐 SLEK AI — Google Login\n') +
208
- chalk.gray('─'.repeat(38)) + '\n\n' +
209
- chalk.cyan('Opening browser...\n') +
210
- chalk.gray('Waiting for Google authentication...\n\n') +
211
- chalk.gray('If browser does not open, paste this URL:\n') +
212
- chalk.yellow(authUrl.toString()),
213
- { padding: 1, margin: { left: 2 }, borderStyle: 'double', borderColor: 'green' }
214
- )
215
- );
190
+ console.log(boxen(
191
+ chalk.bold.white('🔐 SLEK AI — Google Login\n') +
192
+ chalk.gray('─'.repeat(38)) + '\n\n' +
193
+ chalk.cyan('Opening browser...\n') +
194
+ chalk.gray('Waiting for Google authentication...\n\n') +
195
+ chalk.gray('If browser does not open, paste this URL:\n') +
196
+ chalk.yellow(authUrl.toString()),
197
+ { padding: 1, margin: { left: 2 }, borderStyle: 'double', borderColor: 'green' }
198
+ ));
216
199
  setTimeout(() => openBrowser(authUrl.toString()), 500);
217
200
  });
218
201
 
219
- // Auto-cancel after 2 minutes
220
- setTimeout(() => {
221
- server.close();
222
- console.log(chalk.red('\n ✗ Login timed out (2 min). Please try again.\n'));
223
- reject(new Error('Login timeout'));
224
- }, 120_000);
202
+ setTimeout(() => { server.close(); console.log(chalk.red('\n ✗ Login timed out. Try again.\n')); reject(new Error('timeout')); }, 120_000);
225
203
  });
226
204
  }
227
205
 
228
- // ─── Logout ───────────────────────────────────────────────────────────────────
229
206
  function logout() {
230
207
  clearToken();
231
- console.log(
232
- '\n' + boxen(
233
- chalk.yellow('👋 Logged out!\n\n') +
234
- chalk.gray('Run ') + chalk.yellow('slek login') + chalk.gray(' to sign in again.'),
235
- { padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'yellow' }
236
- ) + '\n'
237
- );
208
+ console.log('\n' + boxen(
209
+ chalk.yellow('👋 Logged out!\n\n') + chalk.gray('Run ') + chalk.yellow('slek login') + chalk.gray(' to sign in again.'),
210
+ { padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'yellow' }
211
+ ) + '\n');
238
212
  }
239
213
 
240
- // ─── Status ───────────────────────────────────────────────────────────────────
241
214
  function status() {
242
- const user = getUser();
215
+ const user = getUserSync();
243
216
  if (user) {
244
- console.log(
245
- '\n' + boxen(
246
- chalk.green(' Logged in\n\n') +
247
- chalk.cyan('Name : ') + chalk.white(user.name) + '\n' +
248
- chalk.cyan('Email : ') + chalk.white(user.email),
249
- { padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'green' }
250
- ) + '\n'
251
- );
217
+ console.log('\n' + boxen(
218
+ chalk.green('✓ Logged in\n\n') +
219
+ chalk.cyan('Name : ') + chalk.white(user.name) + '\n' +
220
+ chalk.cyan('Email : ') + chalk.white(user.email),
221
+ { padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'green' }
222
+ ) + '\n');
252
223
  } else {
253
- console.log(
254
- chalk.yellow('\n Not logged in. Run: ') + chalk.white('slek login\n')
255
- );
224
+ console.log(chalk.yellow('\n Not logged in. Run: ') + chalk.white('slek login\n'));
256
225
  }
257
226
  }
258
227
 
259
- // ─── Exports ──────────────────────────────────────────────────────────────────
260
- module.exports = { isLoggedIn, getUser, login, logout, status };
228
+ module.exports = { isLoggedIn, getUser, getUserSync, login, logout, status };
package/cli.js CHANGED
@@ -1,14 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- // ─── Auth ─────────────────────────────────────────────────────────────────────
4
+ require('dotenv').config({
5
+ path: require('path').join(__dirname, '.env')
6
+ });
7
+
8
+ // ─── Auth check ───────────────────────────────────────────────────────────────
5
9
  const auth = require('./auth');
6
10
 
7
11
  const arg = process.argv[2];
8
12
 
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); }
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
+ }
12
26
 
13
27
  // Block access if not logged in
14
28
  if (!auth.isLoggedIn()) {
@@ -26,31 +40,31 @@ if (!auth.isLoggedIn()) {
26
40
  }
27
41
 
28
42
  const readline = require('readline');
29
- const axios = require('axios');
30
- const chalk = require('chalk');
31
- const boxen = require('boxen');
43
+ const axios = require('axios');
44
+ const chalk = require('chalk');
45
+ const boxen = require('boxen');
32
46
  const gradient = require('gradient-string');
33
- const figlet = require('figlet');
47
+ const figlet = require('figlet');
34
48
 
49
+ // ─── NOTE: ora v5 is required (CommonJS compatible) ──────────────────────────
50
+ // If ora gives errors, run: npm install ora@5
35
51
  let ora;
36
52
  try {
37
53
  ora = require('ora');
38
54
  } catch {
55
+ // Fallback spinner if ora fails
39
56
  ora = (opts) => ({
40
- start: () => {
41
- process.stdout.write(chalk.cyan(' ⏳ ' + (opts.text || 'Loading...') + '\n'));
42
- return { stop: () => {}, fail: () => {} };
43
- },
57
+ start: () => { process.stdout.write(chalk.cyan(' ⏳ ' + (opts.text || 'Loading...') + '\n')); return { stop: () => {}, fail: () => {} }; },
44
58
  stop: () => {},
45
59
  fail: () => {},
46
60
  });
47
61
  }
48
62
 
49
- // ─── API Config ───────────────────────────────────────────────────────────────
50
- const NVIDIA_API_BASE = 'https://slek-ai-portal.vercel.app/api';
51
- const DEFAULT_MODEL = 'qwen/qwen3.5-122b-a10b';
63
+ // ─── NVIDIA API Config ────────────────────────────────────────────────────────
64
+ const NVIDIA_API_BASE = 'https://integrate.api.nvidia.com/v1';
65
+ const DEFAULT_MODEL = 'qwen/qwen3.5-122b-a10b';
52
66
 
53
- // ─── User config path ─────────────────────────────────────────────────────────
67
+ // ─── User config path (stored in user's home, not in project) ─────────────────
54
68
  const CONFIG_PATH = require('path').join(
55
69
  process.env.APPDATA || process.env.HOME || __dirname,
56
70
  '.slek-config.json'
@@ -61,29 +75,25 @@ function loadConfig() {
61
75
  if (require('fs').existsSync(CONFIG_PATH)) {
62
76
  return JSON.parse(require('fs').readFileSync(CONFIG_PATH, 'utf8'));
63
77
  }
64
- } catch (_) {}
78
+ } catch { }
65
79
  return {};
66
80
  }
67
81
 
68
82
  function saveConfig(data) {
69
83
  const current = loadConfig();
70
- require('fs').writeFileSync(
71
- CONFIG_PATH,
72
- JSON.stringify({ ...current, ...data }, null, 2),
73
- 'utf8'
74
- );
84
+ require('fs').writeFileSync(CONFIG_PATH, JSON.stringify({ ...current, ...data }, null, 2), 'utf8');
75
85
  }
76
86
 
77
- // ─── Gradients ────────────────────────────────────────────────────────────────
87
+ // ─── Gradient Colors ─────────────────────────────────────────────────────────
78
88
  const nvidiaGradient = gradient(['#76b900', '#00c8ff']);
79
- const userGradient = gradient(['#ff6b6b', '#feca57']);
80
- const aiGradient = gradient(['#48dbfb', '#ff9ff3']);
81
- const thinkGradient = gradient(['#f7971e', '#ffd200']);
89
+ const userGradient = gradient(['#ff6b6b', '#feca57']);
90
+ const aiGradient = gradient(['#48dbfb', '#ff9ff3']);
91
+ const thinkGradient = gradient(['#f7971e', '#ffd200']);
82
92
 
83
- // ─── State ────────────────────────────────────────────────────────────────────
84
- let chatHistory = [];
85
- let apiKey = loadConfig().apiKey || '';
86
- const currentUser = auth.getUser();
93
+ // ─── State ───────────────────────────────────────────────────────────────────
94
+ let chatHistory = [];
95
+ let apiKey = process.env.NVIDIA_API_KEY || '';
96
+ const currentUser = auth.getUserSync();
87
97
 
88
98
  // ─── Helpers ──────────────────────────────────────────────────────────────────
89
99
  function clearLine() {
@@ -93,14 +103,17 @@ function clearLine() {
93
103
  // ─── Banner ───────────────────────────────────────────────────────────────────
94
104
  async function showBanner() {
95
105
  console.clear();
96
- const banner = figlet.textSync('SLEK AI', { font: 'ANSI Shadow' });
106
+ const banner = figlet.textSync('SLEK AI', {
107
+ font: 'ANSI Shadow',
108
+ horizontalLayout: 'default',
109
+ });
97
110
  console.log(nvidiaGradient(banner));
98
111
  console.log(
99
112
  boxen(
100
113
  chalk.bold.white('🚀 SLEK AI CLI') + chalk.gray(' — Powered by NVIDIA API\n') +
101
- chalk.gray(''.repeat(42)) + '\n' +
114
+ chalk.gray(''.repeat(42)) + '\n' +
102
115
  chalk.cyan(' User : ') + chalk.green(currentUser ? currentUser.name : 'Unknown') + '\n' +
103
- chalk.gray(''.repeat(42)) + '\n' +
116
+ chalk.gray(''.repeat(42)) + '\n' +
104
117
  chalk.white(' Type ') + chalk.yellow('/help') + chalk.white(' for available commands'),
105
118
  {
106
119
  padding: 1,
@@ -115,37 +128,87 @@ async function showBanner() {
115
128
 
116
129
  // ─── Help ─────────────────────────────────────────────────────────────────────
117
130
  function showHelp() {
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
- )
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
+ }
131
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
+ }
132
189
  }
133
190
 
134
- // ─── Info ─────────────────────────────────────────────────────────────────────
191
+
192
+
193
+ // ─── Show Info ────────────────────────────────────────────────────────────────
135
194
  function showInfo() {
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
- )
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
+ }
145
207
  );
208
+ console.log('\n' + infoBox);
146
209
  }
147
210
 
148
- // ─── History ──────────────────────────────────────────────────────────────────
211
+ // ─── Show History ─────────────────────────────────────────────────────────────
149
212
  function showHistory() {
150
213
  if (chatHistory.length === 0) {
151
214
  console.log(chalk.yellow('\n No chat history yet.\n'));
@@ -176,7 +239,7 @@ async function saveChat() {
176
239
 
177
240
  let content = `SLEK AI CLI - Chat Log\n`;
178
241
  content += `Date: ${new Date().toLocaleString()}\n`;
179
- content += `Model: ${DEFAULT_MODEL}\n`;
242
+ content += `Model: ${currentModel}\n`;
180
243
  content += `${'═'.repeat(50)}\n\n`;
181
244
 
182
245
  chatHistory.forEach(msg => {
@@ -189,13 +252,14 @@ async function saveChat() {
189
252
  });
190
253
 
191
254
  fs.writeFileSync(filepath, content, 'utf8');
192
- console.log(chalk.green(`\n Chat saved to: ${chalk.yellow(filename)}\n`));
255
+ console.log(chalk.green(`\n Chat saved to: ${chalk.yellow(filename)}\n`));
193
256
  }
194
257
 
195
258
  // ─── Stream API Call ──────────────────────────────────────────────────────────
196
259
  async function callNvidiaAPIStream(userMessage) {
197
- if (!apiKey) {
198
- throw new Error('API key not set. Ask the admin for your SLEK API key.');
260
+ const user = await auth.getUser(); // auto-refresh if expired
261
+ if (!user || !user.idToken) {
262
+ throw new Error('Not authenticated. Please run: slek login');
199
263
  }
200
264
 
201
265
  chatHistory.push({ role: 'user', content: userMessage });
@@ -210,20 +274,22 @@ async function callNvidiaAPIStream(userMessage) {
210
274
  ...chatHistory,
211
275
  ];
212
276
 
277
+ const payload = {
278
+ model: DEFAULT_MODEL,
279
+ messages,
280
+ max_tokens: 16384,
281
+ temperature: 0.60,
282
+ top_p: 0.95,
283
+ stream: true,
284
+ chat_template_kwargs: { enable_thinking: true },
285
+ };
286
+
213
287
  const response = await axios.post(
214
288
  `${NVIDIA_API_BASE}/chat-free`,
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
+ payload,
224
290
  {
225
291
  headers: {
226
- Authorization: `Bearer ${apiKey}`,
292
+ Authorization: `Bearer ${user.idToken}`,
227
293
  'Content-Type': 'application/json',
228
294
  Accept: 'text/event-stream',
229
295
  },
@@ -233,8 +299,8 @@ async function callNvidiaAPIStream(userMessage) {
233
299
  );
234
300
 
235
301
  return new Promise((resolve, reject) => {
236
- let fullResponse = '';
237
- let inThinkBlock = false;
302
+ let fullResponse = '';
303
+ let inThinkBlock = false;
238
304
 
239
305
  process.stdout.write('\n ' + aiGradient('✦ SLEK AI: ') + '\n\n ');
240
306
 
@@ -260,10 +326,12 @@ async function callNvidiaAPIStream(userMessage) {
260
326
  continue;
261
327
  }
262
328
 
263
- process.stdout.write(
264
- inThinkBlock ? chalk.dim.yellow(token) : chalk.white(token)
265
- );
266
- } catch (_) { /* ignore malformed SSE lines */ }
329
+ if (inThinkBlock) {
330
+ process.stdout.write(chalk.dim.yellow(token));
331
+ } else {
332
+ process.stdout.write(chalk.white(token));
333
+ }
334
+ } catch { /* ignore malformed SSE lines */ }
267
335
  }
268
336
  }
269
337
  });
@@ -279,7 +347,7 @@ async function callNvidiaAPIStream(userMessage) {
279
347
  });
280
348
  }
281
349
 
282
- // ─── Process Input ────────────────────────────────────────────────────────────
350
+ // ─── Process User Input ───────────────────────────────────────────────────────
283
351
  async function processInput(input, rl) {
284
352
  const trimmed = input.trim();
285
353
  if (!trimmed) return;
@@ -287,14 +355,14 @@ async function processInput(input, rl) {
287
355
  if (trimmed.startsWith('/')) {
288
356
  const cmd = trimmed.toLowerCase().split(' ')[0];
289
357
  switch (cmd) {
290
- case '/help': showHelp(); break;
358
+ case '/help': showHelp(); break;
291
359
  case '/clear':
292
360
  chatHistory = [];
293
- console.log(chalk.green('\n Chat history cleared!\n'));
361
+ console.log(chalk.green('\n Chat history cleared!\n'));
294
362
  break;
295
- case '/history': showHistory(); break;
296
- case '/save': await saveChat(); break;
297
- case '/info': showInfo(); break;
363
+ case '/history': showHistory(); break;
364
+ case '/save': await saveChat(); break;
365
+ case '/info': showInfo(); break;
298
366
  case '/exit':
299
367
  case '/quit':
300
368
  console.log('\n' + nvidiaGradient(' 👋 Goodbye! Thanks for using SLEK AI CLI\n'));
@@ -308,9 +376,9 @@ async function processInput(input, rl) {
308
376
  }
309
377
 
310
378
  const spinner = ora({
311
- text: chalk.cyan(' Connecting to SLEK Server...'),
312
- spinner: 'dots',
313
- color: 'cyan',
379
+ text: chalk.cyan(' Connecting to NVIDIA API...'),
380
+ spinner: 'dots',
381
+ color: 'cyan',
314
382
  prefixText: ' ',
315
383
  }).start();
316
384
 
@@ -326,10 +394,13 @@ async function processInput(input, rl) {
326
394
  err.message ||
327
395
  'Unknown error';
328
396
  console.log(
329
- '\n' + boxen(
330
- chalk.red('✗ Error: ') + chalk.white(errMsg),
331
- { padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'red' }
332
- ) + '\n'
397
+ '\n' +
398
+ boxen(chalk.red('✗ Error: ') + chalk.white(errMsg), {
399
+ padding: 1,
400
+ margin: { left: 2 },
401
+ borderStyle: 'round',
402
+ borderColor: 'red',
403
+ }) + '\n'
333
404
  );
334
405
  }
335
406
  }
@@ -338,7 +409,9 @@ async function processInput(input, rl) {
338
409
  function showPrompt(rl) {
339
410
  const prompt =
340
411
  '\n' +
341
- chalk.gray(' ┌─') + userGradient(' You ') + chalk.gray('─────────────────────────────────\n') +
412
+ chalk.gray(' ┌─') +
413
+ userGradient(' You ') +
414
+ chalk.gray('─────────────────────────────────\n') +
342
415
  chalk.gray(' └▶ ');
343
416
  rl.setPrompt(prompt);
344
417
  rl.prompt();
@@ -348,9 +421,11 @@ function showPrompt(rl) {
348
421
  async function main() {
349
422
  await showBanner();
350
423
 
424
+
425
+
351
426
  const rl = readline.createInterface({
352
- input: process.stdin,
353
- output: process.stdout,
427
+ input: process.stdin,
428
+ output: process.stdout,
354
429
  terminal: true,
355
430
  });
356
431
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slek-ai-cli",
3
- "version": "1.1.10",
3
+ "version": "1.1.12",
4
4
  "description": "SLEK AI CLI — Powered by NVIDIA API",
5
5
  "main": "cli.js",
6
6
  "bin": {