omnibiofex 2.8.0 → 2.8.2

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.0",
3
+ "version": "2.8.2",
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,7 +1,9 @@
1
1
  const chalk = require('chalk');
2
2
  const open = require('open');
3
3
  const { getAuthToken, isAuthenticated } = require('../auth');
4
+ const { db, auth } = require('../firebase');
4
5
  const { PremiumSpinner, sleep } = require('../utils/display');
6
+ const { collection, query, where, getDocs } = require('firebase/firestore');
5
7
 
6
8
  async function credits() {
7
9
  if (!isAuthenticated()) {
@@ -59,30 +61,81 @@ async function usage() {
59
61
  return;
60
62
  }
61
63
 
62
- console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
63
- console.log(chalk.white.bold('šŸ“Š USAGE STATISTICS'));
64
- console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
64
+ const spinner = new PremiumSpinner('Fetching usage statistics');
65
+ spinner.start();
66
+
67
+ try {
68
+ const user = auth.currentUser;
69
+
70
+ if (!user) {
71
+ spinner.fail('Not authenticated');
72
+ console.error(chalk.red('Please run: obx login'));
73
+ return;
74
+ }
75
+
76
+ // Fetch ALL missions (real data)
77
+ const allMissionsQuery = query(
78
+ collection(db, 'missions'),
79
+ where('uid', '==', user.uid)
80
+ );
81
+
82
+ const allMissionsSnapshot = await getDocs(allMissionsQuery);
83
+ const allMissions = [];
84
+
85
+ allMissionsSnapshot.forEach((docSnap) => {
86
+ allMissions.push({
87
+ id: docSnap.id,
88
+ ...docSnap.data()
89
+ });
90
+ });
91
+
92
+ // Calculate real statistics
93
+ const totalMissions = allMissions.length;
94
+ const completedMissions = allMissions.filter(m => m.status === 'completed').length;
95
+ const activeMissions = allMissions.filter(m => m.status === 'running' || m.status === 'pending').length;
96
+ const publishedMissions = allMissions.filter(m => m.isPublished === true).length;
97
+ const totalRCCSpent = allMissions.reduce((sum, m) => sum + (m.rccCost || 0), 0);
98
+
99
+ // Fetch published missions for earnings
100
+ const publishedQuery = query(
101
+ collection(db, 'missions'),
102
+ where('uid', '==', user.uid),
103
+ where('isPublished', '==', true)
104
+ );
105
+
106
+ const publishedSnapshot = await getDocs(publishedQuery);
107
+ const publishedMissionsData = [];
108
+
109
+ publishedSnapshot.forEach((docSnap) => {
110
+ publishedMissionsData.push({
111
+ id: docSnap.id,
112
+ ...docSnap.data()
113
+ });
114
+ });
115
+
116
+ const totalViews = publishedMissionsData.reduce((sum, m) => sum + (m.views || 0), 0);
117
+ const totalEarnings = publishedMissionsData.reduce((sum, m) => sum + (m.earnings || 0), 0);
118
+
119
+ spinner.succeed('Usage statistics loaded');
120
+
121
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
122
+ console.log(chalk.white.bold('šŸ“Š USAGE STATISTICS'));
123
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
65
124
 
66
- // Simulated usage data
67
- const usageData = {
68
- totalMissions: 15,
69
- completedMissions: 12,
70
- activeMissions: 3,
71
- totalRCCSpent: 1250,
72
- publishedResearch: 8,
73
- totalViews: 56800,
74
- totalEarnings: 2840
75
- };
76
-
77
- console.log(chalk.white(` šŸŽÆ Total Missions: ${chalk.green(usageData.totalMissions)}`));
78
- console.log(chalk.white(` āœ… Completed: ${chalk.green(usageData.completedMissions)}`));
79
- console.log(chalk.white(` ā³ Active: ${chalk.yellow(usageData.activeMissions)}`));
80
- console.log(chalk.white(` ⛽ RCC Spent: ${chalk.green(usageData.totalRCCSpent.toLocaleString())}`));
81
- console.log(chalk.white(` šŸ“¤ Published: ${chalk.green(usageData.publishedResearch)}`));
82
- console.log(chalk.white(` šŸ‘ Total Views: ${chalk.green(usageData.totalViews.toLocaleString())}`));
83
- console.log(chalk.white(` šŸ’° Total Earnings: ${chalk.green('₹' + usageData.totalEarnings.toLocaleString())}`));
84
-
85
- console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════\n'));
125
+ console.log(chalk.white(` šŸŽÆ Total Missions: ${chalk.green(totalMissions)}`));
126
+ console.log(chalk.white(` āœ… Completed: ${chalk.green(completedMissions)}`));
127
+ console.log(chalk.white(` ā³ Active: ${chalk.yellow(activeMissions)}`));
128
+ console.log(chalk.white(` ⛽ RCC Spent: ${chalk.green(totalRCCSpent.toLocaleString())}`));
129
+ console.log(chalk.white(` šŸ“¤ Published: ${chalk.green(publishedMissions)}`));
130
+ console.log(chalk.white(` šŸ‘ Total Views: ${chalk.green(totalViews.toLocaleString())}`));
131
+ console.log(chalk.white(` šŸ’° Total Earnings: ${chalk.green('₹' + totalEarnings.toLocaleString())}`));
132
+
133
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════\n'));
134
+
135
+ } catch (error) {
136
+ spinner.fail('Failed to fetch usage statistics');
137
+ console.error(chalk.red(error.message));
138
+ }
86
139
  }
87
140
 
88
141
  async function buy() {
@@ -1,6 +1,8 @@
1
1
  const chalk = require('chalk');
2
- const { isAuthenticated } = require('../auth');
2
+ const { getAuthToken, isAuthenticated } = require('../auth');
3
+ const { db, auth } = require('../firebase');
3
4
  const { PremiumSpinner, sleep } = require('../utils/display');
5
+ const { collection, query, where, orderBy, limit, getDocs } = require('firebase/firestore');
4
6
 
5
7
  async function earnings() {
6
8
  if (!isAuthenticated()) {
@@ -10,75 +12,122 @@ async function earnings() {
10
12
 
11
13
  const spinner = new PremiumSpinner('Fetching your earnings data');
12
14
  spinner.start();
13
- await sleep(800);
14
- spinner.succeed('Earnings data loaded');
15
-
16
- console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
17
- console.log(chalk.white.bold('šŸ’° EARNINGS DASHBOARD'));
18
- console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
19
-
20
- // Simulated earnings data
21
- const earningsData = {
22
- totalEarnings: 2840,
23
- thisMonth: 840,
24
- lastMonth: 1200,
25
- pending: 420,
26
- totalViews: 56800,
27
- thisMonthViews: 14200,
28
- publishedResearch: 12,
29
- averagePerView: 0.05,
30
- revenueShare: 80,
31
- nextPayout: '2026-08-01',
32
- payoutMethod: 'Bank Transfer'
33
- };
34
-
35
- console.log(chalk.white.bold('šŸ“Š OVERVIEW'));
36
- console.log(chalk.gray('─'.repeat(60)));
37
- console.log(chalk.white(`\n šŸ’° Total Earnings: ${chalk.green('₹' + earningsData.totalEarnings.toLocaleString())}`));
38
- console.log(chalk.white(` šŸ“… This Month: ${chalk.green('₹' + earningsData.thisMonth.toLocaleString())}`));
39
- console.log(chalk.white(` šŸ“… Last Month: ${chalk.gray('₹' + earningsData.lastMonth.toLocaleString())}`));
40
- console.log(chalk.white(` ā³ Pending Payout: ${chalk.yellow('₹' + earningsData.pending.toLocaleString())}`));
41
-
42
- console.log(chalk.white.bold('\n\nšŸ“ˆ TRAFFIC METRICS'));
43
- console.log(chalk.gray('─'.repeat(60)));
44
- console.log(chalk.white(`\n šŸ‘ Total Views: ${chalk.green(earningsData.totalViews.toLocaleString())}`));
45
- console.log(chalk.white(` šŸ‘ This Month: ${chalk.green(earningsData.thisMonthViews.toLocaleString())}`));
46
- console.log(chalk.white(` šŸ“„ Published Research: ${chalk.green(earningsData.publishedResearch)}`));
47
- console.log(chalk.white(` šŸ’µ Average Per View: ${chalk.green('₹' + earningsData.averagePerView)}`));
48
-
49
- console.log(chalk.white.bold('\n\nšŸ’³ PAYOUT DETAILS'));
50
- console.log(chalk.gray('─'.repeat(60)));
51
- console.log(chalk.white(`\n šŸ“Š Revenue Share: ${chalk.green(earningsData.revenueShare + '%')}`));
52
- console.log(chalk.white(` šŸ“… Next Payout: ${chalk.green(earningsData.nextPayout)}`));
53
- console.log(chalk.white(` šŸ’³ Payout Method: ${chalk.green(earningsData.payoutMethod)}`));
54
-
55
- console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
56
- console.log(chalk.white.bold('šŸ“Š MONTHLY BREAKDOWN'));
57
- console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
58
-
59
- const monthlyData = [
60
- { month: 'July 2026', earnings: 840, views: 14200, research: 3 },
61
- { month: 'June 2026', earnings: 1200, views: 21000, research: 4 },
62
- { month: 'May 2026', earnings: 800, views: 15600, research: 3 },
63
- { month: 'April 2026', earnings: 0, views: 6000, research: 2 }
64
- ];
65
-
66
- monthlyData.forEach(m => {
67
- console.log(chalk.white(`\n šŸ“… ${chalk.hex('#F24E1E')(m.month)}`));
68
- console.log(chalk.white(` šŸ’° Earnings: ${chalk.green('₹' + m.earnings.toLocaleString())}`));
69
- console.log(chalk.white(` šŸ‘ Views: ${chalk.gray(m.views.toLocaleString())}`));
70
- console.log(chalk.white(` šŸ“„ Research: ${chalk.gray(m.research)}`));
71
- });
72
-
73
- console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════\n'));
74
-
75
- console.log(chalk.white.bold('šŸ’” TIPS TO INCREASE EARNINGS'));
76
- console.log(chalk.gray('─'.repeat(60)));
77
- console.log(chalk.white('\n āœ“ Publish more research to increase views'));
78
- console.log(chalk.white(' āœ“ Share your research on social media'));
79
- console.log(chalk.white(' āœ“ Use relevant tags for better discoverability'));
80
- console.log(chalk.white(' āœ“ Create high-quality, comprehensive research'));
81
- console.log(chalk.white(' āœ“ Update research regularly with new findings\n'));
15
+
16
+ try {
17
+ const user = auth.currentUser;
18
+
19
+ if (!user) {
20
+ spinner.fail('Not authenticated');
21
+ console.error(chalk.red('Please run: obx login'));
22
+ return;
23
+ }
24
+
25
+ // Fetch real published missions with earnings data
26
+ const publishedQuery = query(
27
+ collection(db, 'missions'),
28
+ where('uid', '==', user.uid),
29
+ where('isPublished', '==', true),
30
+ orderBy('publishedAt', 'desc'),
31
+ limit(100)
32
+ );
33
+
34
+ const querySnapshot = await getDocs(publishedQuery);
35
+ const publishedMissions = [];
36
+
37
+ querySnapshot.forEach((docSnap) => {
38
+ publishedMissions.push({
39
+ id: docSnap.id,
40
+ ...docSnap.data()
41
+ });
42
+ });
43
+
44
+ spinner.succeed('Earnings data loaded');
45
+
46
+ // Calculate real earnings from actual data
47
+ const totalViews = publishedMissions.reduce((sum, m) => sum + (m.views || 0), 0);
48
+ const totalDownloads = publishedMissions.reduce((sum, m) => sum + (m.downloads || 0), 0);
49
+ const totalCitations = publishedMissions.reduce((sum, m) => sum + (m.citations || 0), 0);
50
+ const totalEarnings = publishedMissions.reduce((sum, m) => sum + (m.earnings || 0), 0);
51
+
52
+ // Calculate monthly breakdown
53
+ const now = new Date();
54
+ const thisMonth = now.getMonth();
55
+ const thisYear = now.getFullYear();
56
+ const lastMonth = thisMonth === 0 ? 11 : thisMonth - 1;
57
+ const lastMonthYear = thisMonth === 0 ? thisYear - 1 : thisYear;
58
+
59
+ const thisMonthMissions = publishedMissions.filter(m => {
60
+ const publishedDate = m.publishedAt?.toDate?.();
61
+ return publishedDate && publishedDate.getMonth() === thisMonth && publishedDate.getFullYear() === thisYear;
62
+ });
63
+
64
+ const lastMonthMissions = publishedMissions.filter(m => {
65
+ const publishedDate = m.publishedAt?.toDate?.();
66
+ return publishedDate && publishedDate.getMonth() === lastMonth && publishedDate.getFullYear() === lastMonthYear;
67
+ });
68
+
69
+ const thisMonthEarnings = thisMonthMissions.reduce((sum, m) => sum + (m.earnings || 0), 0);
70
+ const thisMonthViews = thisMonthMissions.reduce((sum, m) => sum + (m.views || 0), 0);
71
+ const lastMonthEarnings = lastMonthMissions.reduce((sum, m) => sum + (m.earnings || 0), 0);
72
+
73
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
74
+ console.log(chalk.white.bold('šŸ’° EARNINGS DASHBOARD'));
75
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
76
+
77
+ console.log(chalk.white.bold('šŸ“Š OVERVIEW'));
78
+ console.log(chalk.gray('─'.repeat(60)));
79
+ console.log(chalk.white(`\n šŸ’° Total Earnings: ${chalk.green('₹' + totalEarnings.toLocaleString())}`));
80
+ console.log(chalk.white(` šŸ“… This Month: ${chalk.green('₹' + thisMonthEarnings.toLocaleString())}`));
81
+ console.log(chalk.white(` šŸ“… Last Month: ${chalk.gray('₹' + lastMonthEarnings.toLocaleString())}`));
82
+ console.log(chalk.white(` šŸ“„ Published Research: ${chalk.green(publishedMissions.length)}`));
83
+
84
+ console.log(chalk.white.bold('\n\nšŸ“ˆ TRAFFIC METRICS'));
85
+ console.log(chalk.gray('─'.repeat(60)));
86
+ console.log(chalk.white(`\n šŸ‘ Total Views: ${chalk.green(totalViews.toLocaleString())}`));
87
+ console.log(chalk.white(` šŸ‘ This Month: ${chalk.green(thisMonthViews.toLocaleString())}`));
88
+ console.log(chalk.white(` šŸ’¾ Total Downloads: ${chalk.green(totalDownloads.toLocaleString())}`));
89
+ console.log(chalk.white(` šŸ“š Total Citations: ${chalk.green(totalCitations.toLocaleString())}`));
90
+
91
+ if (totalViews > 0) {
92
+ const averagePerView = totalEarnings / totalViews;
93
+ console.log(chalk.white(` šŸ’µ Average Per View: ${chalk.green('₹' + averagePerView.toFixed(3))}`));
94
+ }
95
+
96
+ console.log(chalk.white.bold('\n\nšŸ’³ PAYOUT DETAILS'));
97
+ console.log(chalk.gray('─'.repeat(60)));
98
+ console.log(chalk.white(`\n šŸ“Š Revenue Share: ${chalk.green('80%')}`));
99
+ console.log(chalk.white(` šŸ“… Next Payout: ${chalk.green('1st of next month')}`));
100
+ console.log(chalk.white(` šŸ’³ Payout Method: ${chalk.green('Bank Transfer / UPI')}`));
101
+
102
+ if (publishedMissions.length > 0) {
103
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
104
+ console.log(chalk.white.bold('šŸ“Š YOUR PUBLISHED RESEARCH'));
105
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
106
+
107
+ publishedMissions.slice(0, 10).forEach((m, i) => {
108
+ const publishedDate = m.publishedAt?.toDate?.().toLocaleDateString() || 'N/A';
109
+ console.log(chalk.white(`\n ${i + 1}. ${chalk.hex('#F24E1E')(m.id)}`));
110
+ console.log(chalk.white(` Topic: ${(m.message || 'No topic').substring(0, 50)}${(m.message?.length || 0) > 50 ? '...' : ''}`));
111
+ console.log(chalk.white(` šŸ‘ Views: ${chalk.green((m.views || 0).toLocaleString())}`));
112
+ console.log(chalk.white(` šŸ’° Earnings: ${chalk.green('₹' + (m.earnings || 0).toLocaleString())}`));
113
+ console.log(chalk.gray(` šŸ“… Published: ${publishedDate}`));
114
+ });
115
+ }
116
+
117
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════\n'));
118
+
119
+ console.log(chalk.white.bold('šŸ’” TIPS TO INCREASE EARNINGS'));
120
+ console.log(chalk.gray('─'.repeat(60)));
121
+ console.log(chalk.white('\n āœ“ Publish more research to increase views'));
122
+ console.log(chalk.white(' āœ“ Share your research on social media'));
123
+ console.log(chalk.white(' āœ“ Use relevant tags for better discoverability'));
124
+ console.log(chalk.white(' āœ“ Create high-quality, comprehensive research'));
125
+ console.log(chalk.white(' āœ“ Update research regularly with new findings\n'));
126
+
127
+ } catch (error) {
128
+ spinner.fail('Failed to fetch earnings data');
129
+ console.error(chalk.red(error.message));
130
+ }
82
131
  }
83
132
 
84
133
  module.exports = { earnings };
@@ -1,8 +1,9 @@
1
1
  const chalk = require('chalk');
2
2
  const inquirer = require('inquirer');
3
- const { getAuthToken } = require('../auth');
4
- const { isAuthenticated } = require('../auth');
3
+ const { getAuthToken, isAuthenticated } = require('../auth');
4
+ const { db, auth } = require('../firebase');
5
5
  const { PremiumSpinner, sleep } = require('../utils/display');
6
+ const { collection, query, where, orderBy, limit, getDocs, doc, updateDoc } = require('firebase/firestore');
6
7
 
7
8
  async function publish(missionId) {
8
9
  if (!isAuthenticated()) {
@@ -14,56 +15,80 @@ async function publish(missionId) {
14
15
  console.log(chalk.white.bold('šŸ“¤ PUBLISH RESEARCH'));
15
16
  console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
16
17
 
17
- // If no mission ID provided, show recent missions
18
+ // If no mission ID provided, fetch real missions from Firestore
18
19
  if (!missionId) {
19
- console.log(chalk.gray('Usage: obx publish <mission-id>'));
20
- console.log(chalk.gray('Example: obx publish mission_atlas_123\n'));
20
+ console.log(chalk.gray('Fetching your completed missions...\n'));
21
21
 
22
- const { action } = await inquirer.prompt([
23
- {
24
- type: 'list',
25
- name: 'action',
26
- message: 'What would you like to do?',
27
- choices: [
28
- { name: 'šŸ“‹ View my recent missions', value: 'view' },
29
- { name: 'šŸ” Search for a specific mission', value: 'search' },
30
- { name: 'āŒ Cancel', value: 'cancel' }
31
- ]
22
+ const spinner = new PremiumSpinner('Loading missions from database');
23
+ spinner.start();
24
+
25
+ try {
26
+ const user = auth.currentUser;
27
+
28
+ if (!user) {
29
+ spinner.fail('Not authenticated');
30
+ console.error(chalk.red('Please run: obx login'));
31
+ return;
32
32
  }
33
- ]);
34
33
 
35
- if (action === 'cancel') return;
36
-
37
- if (action === 'view') {
38
- console.log(chalk.yellow('\nšŸ“‹ Recent Missions (Last 10):'));
39
- console.log(chalk.gray('─'.repeat(60)));
34
+ // Fetch real completed missions from Firestore
35
+ const missionsQuery = query(
36
+ collection(db, 'missions'),
37
+ where('uid', '==', user.uid),
38
+ where('status', '==', 'completed'),
39
+ where('isPublished', '==', false),
40
+ orderBy('createdAt', 'desc'),
41
+ limit(10)
42
+ );
43
+
44
+ const querySnapshot = await getDocs(missionsQuery);
45
+ const missions = [];
40
46
 
41
- // Simulated recent missions
42
- const missions = [
43
- { id: 'mission_atlas_123', title: 'Quantum Battery Optimization', status: 'completed', date: '2026-07-01' },
44
- { id: 'mission_helix_456', title: 'CRISPR Gene Editing Analysis', status: 'completed', date: '2026-06-28' },
45
- { id: 'mission_nova_789', title: 'Neural Interface Research', status: 'completed', date: '2026-06-25' },
46
- { id: 'mission_orion_101', title: 'Sustainable Energy Materials', status: 'completed', date: '2026-06-22' },
47
- { id: 'mission_phoenix_202', title: 'AI Ethics Framework', status: 'completed', date: '2026-06-20' }
48
- ];
47
+ querySnapshot.forEach((docSnap) => {
48
+ missions.push({
49
+ id: docSnap.id,
50
+ ...docSnap.data()
51
+ });
52
+ });
49
53
 
54
+ spinner.succeed(`Found ${missions.length} unpublished missions`);
55
+
56
+ if (missions.length === 0) {
57
+ console.log(chalk.yellow('\nāš ļø No unpublished missions found.'));
58
+ console.log(chalk.gray('Create a mission first: obx mission create\n'));
59
+ return;
60
+ }
61
+
62
+ console.log(chalk.white('\nšŸ“‹ Your Unpublished Missions:'));
63
+ console.log(chalk.gray('─'.repeat(60)));
64
+
50
65
  missions.forEach((m, i) => {
66
+ const date = m.createdAt?.toDate?.().toLocaleDateString() || 'N/A';
51
67
  console.log(chalk.white(`\n${i + 1}. ${chalk.hex('#F24E1E')(m.id)}`));
52
- console.log(chalk.white(` Title: ${m.title}`));
53
- console.log(chalk.green(` Status: ${m.status}`));
54
- console.log(chalk.gray(` Date: ${m.date}`));
68
+ console.log(chalk.white(` Topic: ${(m.message || 'No topic').substring(0, 60)}${(m.message?.length || 0) > 60 ? '...' : ''}`));
69
+ console.log(chalk.green(` Type: ${m.taskType || 'Research'}`));
70
+ console.log(chalk.gray(` Date: ${date}`));
71
+ console.log(chalk.gray(` RCC Cost: ${m.rccCost || 0}`));
55
72
  });
56
73
 
57
- const { selectedMission } = await inquirer.prompt([
74
+ const { selectedIndex } = await inquirer.prompt([
58
75
  {
59
- type: 'input',
60
- name: 'selectedMission',
61
- message: '\nEnter mission ID to publish:',
62
- validate: (input) => input.length > 0 || 'Mission ID is required'
76
+ type: 'list',
77
+ name: 'selectedIndex',
78
+ message: '\nSelect mission to publish:',
79
+ choices: missions.map((m, i) => ({
80
+ name: `${i + 1}. ${m.id} - ${(m.message || 'No topic').substring(0, 50)}`,
81
+ value: i
82
+ }))
63
83
  }
64
84
  ]);
65
85
 
66
- missionId = selectedMission;
86
+ missionId = missions[selectedIndex].id;
87
+
88
+ } catch (error) {
89
+ spinner.fail('Failed to load missions');
90
+ console.error(chalk.red(error.message));
91
+ return;
67
92
  }
68
93
  }
69
94
 
@@ -72,70 +97,83 @@ async function publish(missionId) {
72
97
  spinner.start();
73
98
  await sleep(500);
74
99
 
75
- spinner.update('Generating SEO-optimized page');
76
- await sleep(500);
77
-
78
- spinner.update('Adding metadata and tags');
79
- await sleep(400);
80
-
81
- spinner.update('Submitting to Google for indexing');
82
- await sleep(600);
83
-
84
- spinner.update('Enabling revenue tracking');
85
- await sleep(400);
86
-
87
- spinner.succeed('Research published successfully!');
100
+ try {
101
+ spinner.update('Fetching mission data');
102
+ const missionRef = doc(db, 'missions', missionId);
103
+
104
+ // Get the mission document
105
+ const missionSnap = await getDocs(query(collection(db, 'missions'), where('__name__', '==', missionId)));
106
+
107
+ if (missionSnap.empty) {
108
+ spinner.fail('Mission not found');
109
+ console.error(chalk.red(`āœ— Mission ${missionId} not found`));
110
+ return;
111
+ }
112
+
113
+ const missionData = missionSnap.docs[0].data();
114
+
115
+ spinner.update('Generating SEO-optimized page');
116
+ await sleep(500);
117
+
118
+ // Generate slug from mission topic
119
+ const slug = (missionData.message || missionId)
120
+ .toLowerCase()
121
+ .replace(/[^a-z0-9]+/g, '-')
122
+ .replace(/^-|-$/g, '')
123
+ .substring(0, 60) + '-' + Date.now().toString(36);
124
+
125
+ spinner.update('Adding metadata and tags');
126
+ await sleep(400);
127
+
128
+ spinner.update('Submitting to Google for indexing');
129
+ await sleep(600);
130
+
131
+ spinner.update('Enabling revenue tracking');
132
+ await sleep(400);
133
+
134
+ // Update mission in Firestore
135
+ await updateDoc(missionRef, {
136
+ isPublished: true,
137
+ publishedAt: new Date(),
138
+ slug: slug,
139
+ views: 0,
140
+ downloads: 0,
141
+ citations: 0,
142
+ earnings: 0,
143
+ authorName: missionData.authorName || 'Anonymous Researcher',
144
+ authorEmail: missionData.authorEmail || 'unknown@example.com'
145
+ });
146
+
147
+ spinner.succeed('Research published successfully!');
88
148
 
89
- // Generate URL
90
- const slug = missionId.toLowerCase().replace(/[^a-z0-9]+/g, '-').substring(0, 60);
91
- const url = `https://x.omnibiofex.cloud/research/${slug}`;
149
+ const url = `https://x.omnibiofex.cloud/research/${slug}`;
92
150
 
93
- console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
94
- console.log(chalk.white.bold('āœ… PUBLICATION DETAILS'));
95
- console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
151
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
152
+ console.log(chalk.white.bold('āœ… PUBLICATION DETAILS'));
153
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
96
154
 
97
- console.log(chalk.white(`šŸ“„ Mission ID: ${chalk.hex('#F24E1E')(missionId)}`));
98
- console.log(chalk.white(`šŸ”— Public URL: ${chalk.blue.underline(url)}`));
99
- console.log(chalk.white(`šŸ’° Revenue Share: ${chalk.green('80%')} of all ad revenue`));
100
- console.log(chalk.white(`šŸ” Google Indexing: ${chalk.green('Requested')} (24 hours)`));
101
- console.log(chalk.white(`šŸ“Š Analytics: ${chalk.green('Enabled')}`));
155
+ console.log(chalk.white(`šŸ“„ Mission ID: ${chalk.hex('#F24E1E')(missionId)}`));
156
+ console.log(chalk.white(`šŸ”— Public URL: ${chalk.blue.underline(url)}`));
157
+ console.log(chalk.white(`šŸ’° Revenue Share: ${chalk.green('80%')} of all ad revenue`));
158
+ console.log(chalk.white(`šŸ” Google Indexing: ${chalk.green('Requested')} (24 hours)`));
159
+ console.log(chalk.white(`šŸ“Š Analytics: ${chalk.green('Enabled')}`));
102
160
 
103
- console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
104
- console.log(chalk.white.bold('šŸ’° START EARNING'));
105
- console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
161
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
162
+ console.log(chalk.white.bold('šŸ’° START EARNING'));
163
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
106
164
 
107
- console.log(chalk.gray('Your research is now live! Here\'s what happens next:'));
108
- console.log(chalk.white('\n āœ“ Google indexes your research within 24 hours'));
109
- console.log(chalk.white(' āœ“ Readers discover it through search'));
110
- console.log(chalk.white(' āœ“ Ads are displayed to readers'));
111
- console.log(chalk.white(' āœ“ You earn 80% of all ad revenue'));
112
- console.log(chalk.white(' āœ“ Monthly payouts to your account\n'));
165
+ console.log(chalk.gray('Your research is now live! Here\'s what happens next:'));
166
+ console.log(chalk.white('\n āœ“ Google indexes your research within 24 hours'));
167
+ console.log(chalk.white(' āœ“ Readers discover it through search'));
168
+ console.log(chalk.white(' āœ“ Ads are displayed to readers'));
169
+ console.log(chalk.white(' āœ“ You earn 80% of all ad revenue'));
170
+ console.log(chalk.white(' āœ“ Monthly payouts to your account\n'));
113
171
 
114
- console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
172
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
115
173
 
116
- const { nextAction } = await inquirer.prompt([
117
- {
118
- type: 'list',
119
- name: 'nextAction',
120
- message: 'What would you like to do next?',
121
- choices: [
122
- { name: 'šŸ“Š View earnings dashboard', value: 'earnings' },
123
- { name: 'šŸ“¤ Publish another research', value: 'publish' },
124
- { name: 'šŸ  Open web dashboard', value: 'dashboard' },
125
- { name: 'āœ… Done', value: 'done' }
126
- ]
127
- }
128
- ]);
129
-
130
- if (nextAction === 'earnings') {
131
- const { earnings } = require('./earnings');
132
- await earnings();
133
- } else if (nextAction === 'publish') {
134
- await publish();
135
- } else if (nextAction === 'dashboard') {
136
- const open = require('open');
137
- await open('https://x.omnibiofex.cloud/dash');
138
- console.log(chalk.green('\nāœ“ Opened web dashboard in your browser\n'));
174
+ } catch (error) {
175
+ spinner.fail('Failed to publish research');
176
+ console.error(chalk.red(error.message));
139
177
  }
140
178
  }
141
179
 
@@ -0,0 +1,25 @@
1
+ const { initializeApp, getApps } = require('firebase/app');
2
+ const { getAuth } = require('firebase/auth');
3
+ const { getFirestore } = require('firebase/firestore');
4
+
5
+ const firebaseConfig = {
6
+ apiKey: "AIzaSyDlgXId4pLlYqm-MDuhfz3dLH24KBRHkw8",
7
+ authDomain: "omnibiofex-x.firebaseapp.com",
8
+ projectId: "omnibiofex-x",
9
+ storageBucket: "omnibiofex-x.firebasestorage.app",
10
+ messagingSenderId: "292246591666",
11
+ appId: "1:292246591666:web:a182851585e4b0f79511ab"
12
+ };
13
+
14
+ // Initialize Firebase only once
15
+ let app;
16
+ if (getApps().length === 0) {
17
+ app = initializeApp(firebaseConfig);
18
+ } else {
19
+ app = getApps()[0];
20
+ }
21
+
22
+ const auth = getAuth(app);
23
+ const db = getFirestore(app);
24
+
25
+ module.exports = { app, auth, db };