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 +1 -1
- package/src/auth.js +16 -22
- package/src/commands/account.js +76 -23
- package/src/commands/earnings.js +119 -70
- package/src/commands/publish.js +133 -95
- package/src/firebase.js +25 -0
package/package.json
CHANGED
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
|
|
7
|
-
const {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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=${
|
|
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');
|
package/src/commands/account.js
CHANGED
|
@@ -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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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() {
|
package/src/commands/earnings.js
CHANGED
|
@@ -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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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 };
|
package/src/commands/publish.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const inquirer = require('inquirer');
|
|
3
|
-
const { getAuthToken } = require('../auth');
|
|
4
|
-
const {
|
|
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,
|
|
18
|
+
// If no mission ID provided, fetch real missions from Firestore
|
|
18
19
|
if (!missionId) {
|
|
19
|
-
console.log(chalk.gray('
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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(`
|
|
53
|
-
console.log(chalk.green(`
|
|
54
|
-
console.log(chalk.gray(` 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 {
|
|
74
|
+
const { selectedIndex } = await inquirer.prompt([
|
|
58
75
|
{
|
|
59
|
-
type: '
|
|
60
|
-
name: '
|
|
61
|
-
message: '\
|
|
62
|
-
|
|
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 =
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
172
|
+
console.log(chalk.hex('#F24E1E')('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n'));
|
|
115
173
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
|
package/src/firebase.js
ADDED
|
@@ -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 };
|