omnibiofex 2.8.1 → 2.8.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnibiofex",
3
- "version": "2.8.1",
3
+ "version": "2.8.3",
4
4
  "description": "OmniBioFex X - The Autonomous Research Terminal for AI-powered research missions",
5
5
  "main": "bin/obx",
6
6
  "bin": {
package/src/auth.js CHANGED
@@ -3,22 +3,19 @@ const { URL } = require('url');
3
3
  const axios = require('axios');
4
4
  const chalk = require('chalk');
5
5
  const inquirer = require('inquirer');
6
- const { initializeApp } = require('firebase/app');
7
- const { getAuth, sendSignInLinkToEmail } = require('firebase/auth');
8
- const config = require('./config');
9
-
10
- const firebaseConfig = {
11
- apiKey: "AIzaSyDlgXId4pLlYqm-MDuhfz3dLH24KBRHkw8",
12
- authDomain: "omnibiofex-x.firebaseapp.com",
13
- projectId: "omnibiofex-x",
14
- storageBucket: "omnibiofex-x.firebasestorage.app",
15
- messagingSenderId: "292246591666",
16
- appId: "1:292246591666:web:a182851585e4b0f79511ab",
17
- measurementId: "G-RLQH7BDNHB"
18
- };
19
-
20
- const app = initializeApp(firebaseConfig);
21
- const auth = getAuth(app);
6
+ const Conf = require('conf');
7
+ const { sendSignInLinkToEmail } = require('firebase/auth');
8
+ const { app, auth, db } = require('./firebase');
9
+
10
+ // Use the shared auth instance
11
+ const config = new Conf({
12
+ projectName: 'omnibiofex',
13
+ schema: {
14
+ token: { type: 'string' },
15
+ refreshToken: { type: 'string' },
16
+ expiresAt: { type: 'number' }
17
+ }
18
+ });
22
19
 
23
20
  const LOCAL_PORT = 8765;
24
21
  const CALLBACK_PATH = '/auth/callback';
@@ -157,12 +154,10 @@ function startLocalServer() {
157
154
  console.log('Local server received request:', req.url);
158
155
 
159
156
  if (url.pathname === CALLBACK_PATH) {
160
- // 🔥 FIX: Properly decode URL parameters
161
157
  let token = url.searchParams.get('token');
162
158
  let refreshToken = url.searchParams.get('refreshToken');
163
159
  const error = url.searchParams.get('error');
164
160
 
165
- // 🔥 FIX: Replace spaces with + (URL decoding issue)
166
161
  if (token && token.includes(' ')) {
167
162
  console.log(chalk.yellow('⚠️ Token contains spaces, fixing...'));
168
163
  token = token.replace(/ /g, '+');
@@ -177,7 +172,6 @@ function startLocalServer() {
177
172
  console.log('Token length:', token.length);
178
173
  console.log('Token starts with:', token.substring(0, 20));
179
174
 
180
- // 🔥 Validate token format
181
175
  if (!token.startsWith('eyJ')) {
182
176
  console.error('✗ Invalid token format');
183
177
  res.writeHead(400, { 'Content-Type': 'text/html' });
@@ -279,8 +273,10 @@ async function refreshAuthToken() {
279
273
  try {
280
274
  console.log(chalk.gray('🔄 Refreshing authentication token...'));
281
275
 
276
+ const apiKey = app.options.apiKey; // Get API key from the shared Firebase app
277
+
282
278
  const response = await axios.post(
283
- `https://securetoken.googleapis.com/v1/token?key=${firebaseConfig.apiKey}`,
279
+ `https://securetoken.googleapis.com/v1/token?key=${apiKey}`,
284
280
  new URLSearchParams({
285
281
  grant_type: 'refresh_token',
286
282
  refresh_token: refreshToken
@@ -294,7 +290,6 @@ async function refreshAuthToken() {
294
290
 
295
291
  const { id_token, refresh_token, expires_in } = response.data;
296
292
 
297
- // 🔥 Validate new token
298
293
  if (!id_token || !id_token.startsWith('eyJ')) {
299
294
  throw new Error('Invalid token received from refresh endpoint');
300
295
  }
@@ -347,7 +342,6 @@ async function getAuthToken() {
347
342
  process.exit(1);
348
343
  }
349
344
 
350
- // 🔥 Validate token before returning
351
345
  if (!token.startsWith('eyJ')) {
352
346
  console.error(chalk.red('Stored token is corrupted. Please login again.'));
353
347
  config.delete('authToken');
@@ -1,22 +1,9 @@
1
1
  const chalk = require('chalk');
2
2
  const open = require('open');
3
3
  const { getAuthToken, isAuthenticated } = require('../auth');
4
+ const { db, getCurrentUserUid, getCurrentUserEmail } = require('../firebase');
4
5
  const { PremiumSpinner, sleep } = require('../utils/display');
5
- const { initializeApp } = require('firebase/app');
6
- const { getAuth } = require('firebase/auth');
7
- const { getFirestore, collection, query, where, getDocs } = require('firebase/firestore');
8
-
9
- const firebaseConfig = {
10
- apiKey: "AIzaSyDlgXId4pLlYqm-MDuhfz3dLH24KBRHkw8",
11
- authDomain: "omnibiofex-x.firebaseapp.com",
12
- projectId: "omnibiofex-x",
13
- storageBucket: "omnibiofex-x.firebasestorage.app",
14
- messagingSenderId: "292246591666",
15
- appId: "1:292246591666:web:a182851585e4b0f79511ab"
16
- };
17
-
18
- const app = initializeApp(firebaseConfig);
19
- const db = getFirestore(app);
6
+ const { collection, query, where, getDocs } = require('firebase/firestore');
20
7
 
21
8
  async function credits() {
22
9
  if (!isAuthenticated()) {
@@ -74,23 +61,24 @@ async function usage() {
74
61
  return;
75
62
  }
76
63
 
64
+ // Get user UID from stored token
65
+ const uid = getCurrentUserUid();
66
+ const email = getCurrentUserEmail();
67
+
68
+ if (!uid) {
69
+ console.error(chalk.red('✗ Could not extract user ID from token.'));
70
+ console.error(chalk.gray('Please run: obx login'));
71
+ return;
72
+ }
73
+
77
74
  const spinner = new PremiumSpinner('Fetching usage statistics');
78
75
  spinner.start();
79
76
 
80
77
  try {
81
- const auth = getAuth(app);
82
- const user = auth.currentUser;
83
-
84
- if (!user) {
85
- spinner.fail('Not authenticated');
86
- console.error(chalk.red('Please run: obx login'));
87
- return;
88
- }
89
-
90
78
  // Fetch ALL missions (real data)
91
79
  const allMissionsQuery = query(
92
80
  collection(db, 'missions'),
93
- where('uid', '==', user.uid)
81
+ where('uid', '==', uid)
94
82
  );
95
83
 
96
84
  const allMissionsSnapshot = await getDocs(allMissionsQuery);
@@ -113,7 +101,7 @@ async function usage() {
113
101
  // Fetch published missions for earnings
114
102
  const publishedQuery = query(
115
103
  collection(db, 'missions'),
116
- where('uid', '==', user.uid),
104
+ where('uid', '==', uid),
117
105
  where('isPublished', '==', true)
118
106
  );
119
107
 
@@ -136,7 +124,10 @@ async function usage() {
136
124
  console.log(chalk.white.bold('📊 USAGE STATISTICS'));
137
125
  console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
138
126
 
139
- console.log(chalk.white(` 🎯 Total Missions: ${chalk.green(totalMissions)}`));
127
+ console.log(chalk.gray(` 👤 User: ${chalk.white(email || 'Unknown')}`));
128
+ console.log(chalk.gray(` 🆔 UID: ${chalk.gray(uid.substring(0, 16))}...`));
129
+
130
+ console.log(chalk.white(`\n 🎯 Total Missions: ${chalk.green(totalMissions)}`));
140
131
  console.log(chalk.white(` ✅ Completed: ${chalk.green(completedMissions)}`));
141
132
  console.log(chalk.white(` ⏳ Active: ${chalk.yellow(activeMissions)}`));
142
133
  console.log(chalk.white(` ⛽ RCC Spent: ${chalk.green(totalRCCSpent.toLocaleString())}`));
@@ -1,21 +1,8 @@
1
1
  const chalk = require('chalk');
2
- const { getAuthToken, isAuthenticated } = require('../auth');
2
+ const { isAuthenticated } = require('../auth');
3
+ const { db, getCurrentUserUid, getCurrentUserEmail, isTokenValid } = require('../firebase');
3
4
  const { PremiumSpinner, sleep } = require('../utils/display');
4
- const { initializeApp } = require('firebase/app');
5
- const { getAuth } = require('firebase/auth');
6
- const { getFirestore, collection, query, where, orderBy, limit, getDocs } = require('firebase/firestore');
7
-
8
- const firebaseConfig = {
9
- apiKey: "AIzaSyDlgXId4pLlYqm-MDuhfz3dLH24KBRHkw8",
10
- authDomain: "omnibiofex-x.firebaseapp.com",
11
- projectId: "omnibiofex-x",
12
- storageBucket: "omnibiofex-x.firebasestorage.app",
13
- messagingSenderId: "292246591666",
14
- appId: "1:292246591666:web:a182851585e4b0f79511ab"
15
- };
16
-
17
- const app = initializeApp(firebaseConfig);
18
- const db = getFirestore(app);
5
+ const { collection, query, where, orderBy, limit, getDocs } = require('firebase/firestore');
19
6
 
20
7
  async function earnings() {
21
8
  if (!isAuthenticated()) {
@@ -23,23 +10,24 @@ async function earnings() {
23
10
  return;
24
11
  }
25
12
 
13
+ // Get user UID from stored token (NOT from auth.currentUser)
14
+ const uid = getCurrentUserUid();
15
+ const email = getCurrentUserEmail();
16
+
17
+ if (!uid) {
18
+ console.error(chalk.red('✗ Could not extract user ID from token.'));
19
+ console.error(chalk.gray('Please run: obx login'));
20
+ return;
21
+ }
22
+
26
23
  const spinner = new PremiumSpinner('Fetching your earnings data');
27
24
  spinner.start();
28
25
 
29
26
  try {
30
- const auth = getAuth(app);
31
- const user = auth.currentUser;
32
-
33
- if (!user) {
34
- spinner.fail('Not authenticated');
35
- console.error(chalk.red('Please run: obx login'));
36
- return;
37
- }
38
-
39
27
  // Fetch real published missions with earnings data
40
28
  const publishedQuery = query(
41
29
  collection(db, 'missions'),
42
- where('uid', '==', user.uid),
30
+ where('uid', '==', uid),
43
31
  where('isPublished', '==', true),
44
32
  orderBy('publishedAt', 'desc'),
45
33
  limit(100)
@@ -87,8 +75,11 @@ async function earnings() {
87
75
  console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
88
76
  console.log(chalk.white.bold('💰 EARNINGS DASHBOARD'));
89
77
  console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
78
+
79
+ console.log(chalk.gray(` 👤 User: ${chalk.white(email || 'Unknown')}`));
80
+ console.log(chalk.gray(` 🆔 UID: ${chalk.gray(uid.substring(0, 16))}...`));
90
81
 
91
- console.log(chalk.white.bold('📊 OVERVIEW'));
82
+ console.log(chalk.white.bold('\n\n📊 OVERVIEW'));
92
83
  console.log(chalk.gray('─'.repeat(60)));
93
84
  console.log(chalk.white(`\n 💰 Total Earnings: ${chalk.green('₹' + totalEarnings.toLocaleString())}`));
94
85
  console.log(chalk.white(` 📅 This Month: ${chalk.green('₹' + thisMonthEarnings.toLocaleString())}`));
@@ -126,6 +117,9 @@ async function earnings() {
126
117
  console.log(chalk.white(` 💰 Earnings: ${chalk.green('₹' + (m.earnings || 0).toLocaleString())}`));
127
118
  console.log(chalk.gray(` 📅 Published: ${publishedDate}`));
128
119
  });
120
+ } else {
121
+ console.log(chalk.yellow('\n\n ⚠️ No published research yet.'));
122
+ console.log(chalk.gray(' Run "obx publish" to publish your first research and start earning.\n'));
129
123
  }
130
124
 
131
125
  console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════\n'));
@@ -1,22 +1,9 @@
1
1
  const chalk = require('chalk');
2
2
  const inquirer = require('inquirer');
3
3
  const { getAuthToken, isAuthenticated } = require('../auth');
4
+ const { db, getCurrentUserUid, getCurrentUserEmail } = require('../firebase');
4
5
  const { PremiumSpinner, sleep } = require('../utils/display');
5
- const { initializeApp } = require('firebase/app');
6
- const { getAuth } = require('firebase/auth');
7
- const { getFirestore, collection, query, where, orderBy, limit, getDocs, doc, updateDoc } = require('firebase/firestore');
8
-
9
- const firebaseConfig = {
10
- apiKey: "AIzaSyDlgXId4pLlYqm-MDuhfz3dLH24KBRHkw8",
11
- authDomain: "omnibiofex-x.firebaseapp.com",
12
- projectId: "omnibiofex-x",
13
- storageBucket: "omnibiofex-x.firebasestorage.app",
14
- messagingSenderId: "292246591666",
15
- appId: "1:292246591666:web:a182851585e4b0f79511ab"
16
- };
17
-
18
- const app = initializeApp(firebaseConfig);
19
- const db = getFirestore(app);
6
+ const { collection, query, where, orderBy, limit, getDocs, doc, updateDoc } = require('firebase/firestore');
20
7
 
21
8
  async function publish(missionId) {
22
9
  if (!isAuthenticated()) {
@@ -24,34 +11,35 @@ async function publish(missionId) {
24
11
  return;
25
12
  }
26
13
 
14
+ // Get user UID from stored token
15
+ const uid = getCurrentUserUid();
16
+ const email = getCurrentUserEmail();
17
+
18
+ if (!uid) {
19
+ console.error(chalk.red('✗ Could not extract user ID from token.'));
20
+ console.error(chalk.gray('Please run: obx login'));
21
+ return;
22
+ }
23
+
27
24
  console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
28
25
  console.log(chalk.white.bold('📤 PUBLISH RESEARCH'));
29
26
  console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
30
-
31
- const token = await getAuthToken();
27
+
28
+ console.log(chalk.gray(` 👤 User: ${chalk.white(email || 'Unknown')}`));
29
+ console.log(chalk.gray(` 🆔 UID: ${chalk.gray(uid.substring(0, 16))}...`));
32
30
 
33
31
  // If no mission ID provided, fetch real missions from Firestore
34
32
  if (!missionId) {
35
- console.log(chalk.gray('Fetching your completed missions...\n'));
33
+ console.log(chalk.gray('\nFetching your completed missions...\n'));
36
34
 
37
35
  const spinner = new PremiumSpinner('Loading missions from database');
38
36
  spinner.start();
39
37
 
40
38
  try {
41
- // Get authenticated user
42
- const auth = getAuth(app);
43
- const user = auth.currentUser;
44
-
45
- if (!user) {
46
- spinner.fail('Not authenticated');
47
- console.error(chalk.red('Please run: obx login'));
48
- return;
49
- }
50
-
51
39
  // Fetch real completed missions from Firestore
52
40
  const missionsQuery = query(
53
41
  collection(db, 'missions'),
54
- where('uid', '==', user.uid),
42
+ where('uid', '==', uid),
55
43
  where('status', '==', 'completed'),
56
44
  where('isPublished', '==', false),
57
45
  orderBy('createdAt', 'desc'),
@@ -117,6 +105,8 @@ async function publish(missionId) {
117
105
  try {
118
106
  spinner.update('Fetching mission data');
119
107
  const missionRef = doc(db, 'missions', missionId);
108
+
109
+ // Get the mission document
120
110
  const missionSnap = await getDocs(query(collection(db, 'missions'), where('__name__', '==', missionId)));
121
111
 
122
112
  if (missionSnap.empty) {
@@ -155,8 +145,8 @@ async function publish(missionId) {
155
145
  downloads: 0,
156
146
  citations: 0,
157
147
  earnings: 0,
158
- authorName: missionData.authorName || 'Anonymous Researcher',
159
- authorEmail: missionData.authorEmail || 'unknown@example.com'
148
+ authorName: email || 'Anonymous Researcher',
149
+ authorEmail: email || 'unknown@example.com'
160
150
  });
161
151
 
162
152
  spinner.succeed('Research published successfully!');
@@ -0,0 +1,104 @@
1
+ const { initializeApp, getApps } = require('firebase/app');
2
+ const { getAuth } = require('firebase/auth');
3
+ const { getFirestore } = require('firebase/firestore');
4
+ const Conf = require('conf');
5
+
6
+ const firebaseConfig = {
7
+ apiKey: "AIzaSyDlgXId4pLlYqm-MDuhfz3dLH24KBRHkw8",
8
+ authDomain: "omnibiofex-x.firebaseapp.com",
9
+ projectId: "omnibiofex-x",
10
+ storageBucket: "omnibiofex-x.firebasestorage.app",
11
+ messagingSenderId: "292246591666",
12
+ appId: "1:292246591666:web:a182851585e4b0f79511ab"
13
+ };
14
+
15
+ // Initialize Firebase only once
16
+ let app;
17
+ if (getApps().length === 0) {
18
+ app = initializeApp(firebaseConfig);
19
+ } else {
20
+ app = getApps()[0];
21
+ }
22
+
23
+ const auth = getAuth(app);
24
+ const db = getFirestore(app);
25
+
26
+ // Shared Conf storage for tokens
27
+ const tokenStore = new Conf({
28
+ projectName: 'omnibiofex',
29
+ schema: {
30
+ token: { type: 'string' },
31
+ refreshToken: { type: 'string' },
32
+ expiresAt: { type: 'number' }
33
+ }
34
+ });
35
+
36
+ /**
37
+ * Decode JWT token to extract user info
38
+ * @returns {Object|null} - { uid, email } or null if no token
39
+ */
40
+ function decodeToken() {
41
+ const token = tokenStore.get('token');
42
+ if (!token) return null;
43
+
44
+ try {
45
+ // JWT format: header.payload.signature
46
+ const parts = token.split('.');
47
+ if (parts.length !== 3) return null;
48
+
49
+ // Decode payload (base64url)
50
+ const payload = parts[1];
51
+ const decoded = Buffer.from(payload, 'base64').toString('utf8');
52
+ const data = JSON.parse(decoded);
53
+
54
+ return {
55
+ uid: data.user_id || data.sub,
56
+ email: data.email,
57
+ exp: data.exp
58
+ };
59
+ } catch (error) {
60
+ return null;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Get current user UID from stored token
66
+ * @returns {string|null} - User UID or null
67
+ */
68
+ function getCurrentUserUid() {
69
+ const decoded = decodeToken();
70
+ return decoded ? decoded.uid : null;
71
+ }
72
+
73
+ /**
74
+ * Get current user email from stored token
75
+ * @returns {string|null} - User email or null
76
+ */
77
+ function getCurrentUserEmail() {
78
+ const decoded = decodeToken();
79
+ return decoded ? decoded.email : null;
80
+ }
81
+
82
+ /**
83
+ * Check if token is valid (not expired)
84
+ * @returns {boolean}
85
+ */
86
+ function isTokenValid() {
87
+ const decoded = decodeToken();
88
+ if (!decoded || !decoded.exp) return false;
89
+
90
+ // JWT exp is in seconds, Date.now() is in milliseconds
91
+ const now = Math.floor(Date.now() / 1000);
92
+ return decoded.exp > now;
93
+ }
94
+
95
+ module.exports = {
96
+ app,
97
+ auth,
98
+ db,
99
+ tokenStore,
100
+ decodeToken,
101
+ getCurrentUserUid,
102
+ getCurrentUserEmail,
103
+ isTokenValid
104
+ };