slek-ai-cli 1.1.2 → 1.1.3

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