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.
- package/auth.js +109 -49
- 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
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
69
|
+
// ─── Open browser cross-platform ──────────────────────────────────────────────
|
|
61
70
|
function openBrowser(targetUrl) {
|
|
62
71
|
try {
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
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('✗
|
|
109
|
-
chalk.white('
|
|
110
|
-
chalk.cyan('
|
|
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);
|
|
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(
|
|
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)
|
|
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(
|
|
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
|
-
|
|
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).
|
|
247
|
+
console.log(chalk.red('\n ✗ Login timed out (2 min). Please try again.\n'));
|
|
191
248
|
reject(new Error('Login timeout'));
|
|
192
|
-
},
|
|
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
|
|
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)
|
|
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(
|
|
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 };
|