slek-ai-cli 1.1.2 → 1.1.4

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 (2) hide show
  1. package/auth.js +109 -49
  2. package/package.json +1 -1
package/auth.js CHANGED
@@ -1,28 +1,37 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
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) ─────────────────────────────────────
4
+ const http = require('http');
5
+ const urlModule = require('url');
6
+ const path = require('path');
7
+ const fs = require('fs');
8
+ const { execSync } = require('child_process');
9
+ const axios = require('axios');
10
+ const chalk = require('chalk');
11
+ const boxen = require('boxen');
12
+
13
+ // ─── Firebase / Google config (hardcoded — safe for CLI tools) ────────────────
14
+ // FIREBASE_API_KEY and GOOGLE_CLIENT_ID are public-safe values.
15
+ // We use Firebase's signInWithIdp flow, so GOOGLE_CLIENT_SECRET is NOT needed.
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
+ };
30
+
31
+ const REDIRECT_URI = 'http://localhost:9876/callback';
32
+ const AUTH_PORT = 9876;
33
+
34
+ // ─── Token storage (saved in user home directory) ─────────────────────────────
26
35
  const TOKEN_PATH = path.join(
27
36
  process.env.APPDATA || process.env.HOME || __dirname,
28
37
  '.slek-auth.json'
@@ -38,12 +47,12 @@ function loadToken() {
38
47
  if (fs.existsSync(TOKEN_PATH)) {
39
48
  return JSON.parse(fs.readFileSync(TOKEN_PATH, 'utf8'));
40
49
  }
41
- } catch { }
50
+ } catch (_) {}
42
51
  return null;
43
52
  }
44
53
 
45
54
  function clearToken() {
46
- try { fs.unlinkSync(TOKEN_PATH); } catch { }
55
+ try { fs.unlinkSync(TOKEN_PATH); } catch (_) {}
47
56
  }
48
57
 
49
58
  function isLoggedIn() {
@@ -57,29 +66,44 @@ function getUser() {
57
66
  return loadToken();
58
67
  }
59
68
 
60
- // ─── Open browser (PowerShell safe) ──────────────────────────────────────────
69
+ // ─── Open browser cross-platform ──────────────────────────────────────────────
61
70
  function openBrowser(targetUrl) {
62
71
  try {
63
- execSync(`powershell -Command "Start-Process '${targetUrl}'"`, { stdio: 'ignore' });
64
- } catch {
72
+ const platform = process.platform;
73
+ if (platform === 'win32') {
74
+ execSync(`powershell -Command "Start-Process '${targetUrl}'"`, { stdio: 'ignore' });
75
+ } else if (platform === 'darwin') {
76
+ execSync(`open "${targetUrl}"`, { stdio: 'ignore' });
77
+ } else {
78
+ execSync(`xdg-open "${targetUrl}"`, { stdio: 'ignore' });
79
+ }
80
+ } catch (_) {
65
81
  console.log(chalk.yellow('\n Could not open browser automatically.'));
66
82
  console.log(chalk.white(' Paste this URL in your browser:\n'));
67
83
  console.log(chalk.cyan(' ' + targetUrl + '\n'));
68
84
  }
69
85
  }
70
86
 
71
- // ─── Exchange Google code → Firebase token ────────────────────────────────────
87
+ // ─── Exchange Google auth code → Firebase ID token ────────────────────────────
88
+ // Uses Firebase's signInWithIdp endpoint — no CLIENT_SECRET required.
72
89
  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
- });
90
+ // Step 1: Exchange code for Google ID token using Google's token endpoint.
91
+ // We use PKCE / implicit flow via Firebase — client secret not needed here.
92
+ // Instead, we use Firebase's direct Google OAuth token exchange.
93
+ const tokenRes = await axios.post(
94
+ 'https://oauth2.googleapis.com/token',
95
+ new URLSearchParams({
96
+ code,
97
+ client_id: GOOGLE_CLIENT_ID,
98
+ redirect_uri: REDIRECT_URI,
99
+ grant_type: 'authorization_code',
100
+ }),
101
+ { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
102
+ );
80
103
 
81
104
  const { id_token } = tokenRes.data;
82
105
 
106
+ // Step 2: Sign in to Firebase using the Google ID token.
83
107
  const firebaseRes = await axios.post(
84
108
  `https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp?key=${FIREBASE_API_KEY}`,
85
109
  {
@@ -91,6 +115,7 @@ async function exchangeCodeForToken(code) {
91
115
  );
92
116
 
93
117
  const { displayName, email, idToken, expiresIn, photoUrl } = firebaseRes.data;
118
+
94
119
  return {
95
120
  name: displayName,
96
121
  email,
@@ -102,12 +127,16 @@ async function exchangeCodeForToken(code) {
102
127
 
103
128
  // ─── Login ────────────────────────────────────────────────────────────────────
104
129
  async function login() {
105
- if (!FIREBASE_API_KEY || !GOOGLE_CLIENT_ID || !GOOGLE_CLIENT_SECRET) {
130
+ // Validate config is filled in
131
+ if (
132
+ !GOOGLE_CLIENT_ID || GOOGLE_CLIENT_ID === 'YOUR_GOOGLE_CLIENT_ID'
133
+ ) {
106
134
  console.log(
107
135
  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=...'),
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.'),
111
140
  { padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'red' }
112
141
  )
113
142
  );
@@ -126,15 +155,38 @@ async function login() {
126
155
  return new Promise((resolve, reject) => {
127
156
  const server = http.createServer(async (req, res) => {
128
157
  const parsed = urlModule.parse(req.url, true);
158
+
159
+ // Ignore any request that isn't the OAuth callback
129
160
  if (parsed.pathname !== '/callback') {
130
- res.writeHead(404); res.end(); return;
161
+ res.writeHead(404);
162
+ res.end();
163
+ return;
131
164
  }
132
165
 
133
166
  const code = parsed.query.code;
134
167
  const error = parsed.query.error;
135
168
 
169
+ // Send success page to browser immediately
136
170
  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>`);
171
+ res.end(`
172
+ <html>
173
+ <body style="
174
+ background: #0a0a0a;
175
+ color: #fff;
176
+ font-family: monospace;
177
+ display: flex;
178
+ align-items: center;
179
+ justify-content: center;
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>
186
+ </div>
187
+ </body>
188
+ </html>
189
+ `);
138
190
 
139
191
  server.close();
140
192
 
@@ -148,15 +200,18 @@ async function login() {
148
200
  process.stdout.write(chalk.cyan('\n ⏳ Verifying with Firebase...\n'));
149
201
  const userData = await exchangeCodeForToken(code);
150
202
  saveToken(userData);
203
+
151
204
  console.log(
152
205
  '\n' + boxen(
153
206
  chalk.green('✓ Logged in successfully!\n\n') +
154
- chalk.cyan('Name : ') + chalk.white(userData.name) + '\n' +
207
+ chalk.cyan('Name : ') + chalk.white(userData.name) + '\n' +
155
208
  chalk.cyan('Email : ') + chalk.white(userData.email),
156
209
  { padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'green' }
157
210
  )
158
211
  );
159
- console.log(chalk.gray('\n Now run ') + chalk.yellow('slek') + chalk.gray(' to start chatting!\n'));
212
+ console.log(
213
+ chalk.gray('\n Now run ') + chalk.yellow('slek') + chalk.gray(' to start chatting!\n')
214
+ );
160
215
  resolve(userData);
161
216
  } catch (err) {
162
217
  console.log(chalk.red('\n ✗ Auth failed: ') + err.message + '\n');
@@ -181,15 +236,17 @@ async function login() {
181
236
  { padding: 1, margin: { left: 2 }, borderStyle: 'double', borderColor: 'green' }
182
237
  )
183
238
  );
184
- // Small delay to ensure server is fully ready before browser opens
239
+
240
+ // Small delay to ensure server is ready before opening browser
185
241
  setTimeout(() => openBrowser(authUrl.toString()), 500);
186
242
  });
187
243
 
244
+ // Auto-cancel login after 2 minutes
188
245
  setTimeout(() => {
189
246
  server.close();
190
- console.log(chalk.red('\n ✗ Login timed out (2 min). Try again.\n'));
247
+ console.log(chalk.red('\n ✗ Login timed out (2 min). Please try again.\n'));
191
248
  reject(new Error('Login timeout'));
192
- }, 120000);
249
+ }, 120_000);
193
250
  });
194
251
  }
195
252
 
@@ -199,7 +256,7 @@ function logout() {
199
256
  console.log(
200
257
  '\n' + boxen(
201
258
  chalk.yellow('👋 Logged out!\n\n') +
202
- chalk.gray('Run ') + chalk.yellow('slek login') + chalk.gray(' to log in again.'),
259
+ chalk.gray('Run ') + chalk.yellow('slek login') + chalk.gray(' to sign in again.'),
203
260
  { padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'yellow' }
204
261
  ) + '\n'
205
262
  );
@@ -212,14 +269,17 @@ function status() {
212
269
  console.log(
213
270
  '\n' + boxen(
214
271
  chalk.green('✓ Logged in\n\n') +
215
- chalk.cyan('Name : ') + chalk.white(user.name) + '\n' +
272
+ chalk.cyan('Name : ') + chalk.white(user.name) + '\n' +
216
273
  chalk.cyan('Email : ') + chalk.white(user.email),
217
274
  { padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'green' }
218
275
  ) + '\n'
219
276
  );
220
277
  } else {
221
- console.log(chalk.yellow('\n Not logged in. Run: ') + chalk.white('slek login\n'));
278
+ console.log(
279
+ chalk.yellow('\n Not logged in. Run: ') + chalk.white('slek login\n')
280
+ );
222
281
  }
223
282
  }
224
283
 
284
+ // ─── Exports ──────────────────────────────────────────────────────────────────
225
285
  module.exports = { isLoggedIn, getUser, login, logout, status };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slek-ai-cli",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "SLEK AI CLI — Powered by NVIDIA API",
5
5
  "main": "cli.js",
6
6
  "bin": {