omnibiofex 2.8.3 → 2.8.5

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/bin/obx CHANGED
@@ -105,8 +105,17 @@ program
105
105
  .argument('<topic>', 'Research topic')
106
106
  .action(hypothesis);
107
107
 
108
+ // ==================== TIMELINE COMMAND ====================
109
+ const { timeline } = require('../src/commands/timeline');
110
+
111
+ program
112
+ .command('timeline')
113
+ .description('šŸ“… Generate visual research timeline')
114
+ .argument('<topic>', 'Research topic')
115
+ .action(timeline);
116
+
108
117
  // ==================== DATA COMMANDS ====================
109
- const { dataset, code, medical } = require('../src/commands/data');
118
+ const { dataset, code } = require('../src/commands/data');
110
119
 
111
120
  program
112
121
  .command('dataset')
@@ -120,12 +129,6 @@ program
120
129
  .argument('[directory]', 'Directory path', '.')
121
130
  .action(code);
122
131
 
123
- program
124
- .command('medical')
125
- .description('Analyze medical imaging')
126
- .argument('<file>', 'DICOM/image file path')
127
- .action(medical);
128
-
129
132
  // ==================== PUBLISH & EARN COMMANDS ====================
130
133
  const { publish } = require('../src/commands/publish');
131
134
  const { earnings } = require('../src/commands/earnings');
@@ -141,6 +144,14 @@ program
141
144
  .description('šŸ’° View your earnings dashboard')
142
145
  .action(earnings);
143
146
 
147
+ // ==================== MORNING COMMAND ====================
148
+ const { morning } = require('../src/commands/morning');
149
+
150
+ program
151
+ .command('morning')
152
+ .description('ā˜€ļø Get daily research briefing')
153
+ .action(morning);
154
+
144
155
  // ==================== OPEN COMMANDS ====================
145
156
  const { openPage } = require('../src/commands/open');
146
157
 
@@ -150,7 +161,6 @@ program
150
161
  .argument('[page]', 'Page to open (dashboard, earn, pricing, agents, etc.)')
151
162
  .action(openPage);
152
163
 
153
- // Shortcut commands
154
164
  program
155
165
  .command('dashboard')
156
166
  .description('šŸ  Open web dashboard')
@@ -194,9 +204,11 @@ program.parse(process.argv);
194
204
  if (!process.argv.slice(2).length) {
195
205
  console.log(chalk.hex('#F24E1E')('\nšŸ“š QUICK START:\n'));
196
206
  console.log(chalk.white(' obx login # Authenticate'));
207
+ console.log(chalk.white(' obx morning # Daily briefing'));
197
208
  console.log(chalk.white(' obx mission create # Create research'));
198
209
  console.log(chalk.white(' obx publish <mission-id> # Publish & earn'));
199
210
  console.log(chalk.white(' obx earnings # View earnings'));
211
+ console.log(chalk.white(' obx timeline <topic> # Generate timeline'));
200
212
  console.log(chalk.white(' obx open # Open web pages\n'));
201
213
  console.log(chalk.gray(' Run "obx --help" for all commands\n'));
202
214
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omnibiofex",
3
- "version": "2.8.3",
3
+ "version": "2.8.5",
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
@@ -351,4 +351,65 @@ async function getAuthToken() {
351
351
  return token;
352
352
  }
353
353
 
354
- module.exports = { login, logout, isAuthenticated, getAuthToken, refreshAuthToken };
354
+ // ==================== USER EXTRACTION FUNCTIONS ====================
355
+
356
+ /**
357
+ * Decode base64url to string (handles JWT format)
358
+ */
359
+ function base64UrlDecode(str) {
360
+ let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
361
+ while (base64.length % 4) {
362
+ base64 += '=';
363
+ }
364
+ return Buffer.from(base64, 'base64').toString('utf8');
365
+ }
366
+
367
+ /**
368
+ * Decode JWT token to extract user info
369
+ */
370
+ function decodeToken() {
371
+ const token = config.get('authToken'); // fixed key name
372
+ if (!token) return null;
373
+
374
+ try {
375
+ const parts = token.split('.');
376
+ if (parts.length !== 3) return null;
377
+
378
+ const payload = parts[1];
379
+ const decoded = base64UrlDecode(payload);
380
+ const data = JSON.parse(decoded);
381
+
382
+ return {
383
+ uid: data.user_id || data.sub,
384
+ email: data.email,
385
+ exp: data.exp
386
+ };
387
+ } catch (error) {
388
+ return null;
389
+ }
390
+ }
391
+
392
+ /**
393
+ * Get current user UID from stored token
394
+ */
395
+ function getCurrentUserUid() {
396
+ const decoded = decodeToken();
397
+ return decoded ? decoded.uid : null;
398
+ }
399
+
400
+ /**
401
+ * Get current user email from stored token
402
+ */
403
+ function getCurrentUserEmail() {
404
+ const decoded = decodeToken();
405
+ return decoded ? decoded.email : null;
406
+ }
407
+
408
+ module.exports = {
409
+ login,
410
+ logout,
411
+ getAuthToken,
412
+ isAuthenticated,
413
+ getCurrentUserUid,
414
+ getCurrentUserEmail
415
+ };
@@ -1,7 +1,7 @@
1
1
  const chalk = require('chalk');
2
2
  const open = require('open');
3
- const { getAuthToken, isAuthenticated } = require('../auth');
4
- const { db, getCurrentUserUid, getCurrentUserEmail } = require('../firebase');
3
+ const { getAuthToken, isAuthenticated, getCurrentUserUid, getCurrentUserEmail } = require('../auth');
4
+ const { db } = require('../firebase');
5
5
  const { PremiumSpinner, sleep } = require('../utils/display');
6
6
  const { collection, query, where, getDocs } = require('firebase/firestore');
7
7
 
@@ -33,15 +33,6 @@ async function credits() {
33
33
  console.log(chalk.white(` Current Balance: ${chalk.green(balance.toLocaleString() + ' RCC')}`));
34
34
  console.log(chalk.white(` Status: ${balance > 100 ? chalk.green('āœ“ Healthy') : chalk.yellow('⚠ Low')}`));
35
35
 
36
- console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
37
- console.log(chalk.white.bold('šŸ“Š WHAT YOU CAN DO'));
38
- console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
39
-
40
- console.log(chalk.white(` šŸ“š Literature Reviews: ${chalk.green(Math.floor(balance / 100))}`));
41
- console.log(chalk.white(` šŸ“„ Paper Analyses: ${chalk.green(Math.floor(balance / 40))}`));
42
- console.log(chalk.white(` šŸ” Quick Searches: ${chalk.green(Math.floor(balance / 10))}`));
43
- console.log(chalk.white(` 🧪 Gap Discoveries: ${chalk.green(Math.floor(balance / 120))}`));
44
-
45
36
  if (balance < 500) {
46
37
  console.log(chalk.yellow('\n āš ļø Your fuel is running low!'));
47
38
  console.log(chalk.gray(' Run "obx buy" to purchase more credits\n'));
@@ -1,38 +1,8 @@
1
1
  const chalk = require('chalk');
2
2
  const fs = require('fs');
3
- const path = require('path');
4
- const os = require('os');
5
- const { createMission } = require('../api');
6
- const { isAuthenticated } = require('../auth');
7
- const {
8
- generateMissionName,
9
- displayMissionDashboard,
10
- displayResearchScore,
11
- displayArtifacts,
12
- generateResearchScore,
13
- generateArtifacts,
14
- typeAIResponse,
15
- PremiumSpinner,
16
- sleep,
17
- animateResearchSources
18
- } = require('../utils/display');
19
-
20
- const REPORTS_DIR = path.join(os.homedir(), 'obx-reports');
21
- if (!fs.existsSync(REPORTS_DIR)) {
22
- fs.mkdirSync(REPORTS_DIR, { recursive: true });
23
- }
24
-
25
- function saveReport(topic, content, missionName) {
26
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
27
- const sanitizedTopic = topic.replace(/[^a-z0-9]/gi, '_').substring(0, 50);
28
- const filename = `${missionName.replace(/\s+/g, '_')}_${timestamp}.md`;
29
- const filepath = path.join(REPORTS_DIR, filename);
30
-
31
- fs.writeFileSync(filepath, content, 'utf8');
32
- return filepath;
33
- }
3
+ const { getAuthToken, isAuthenticated } = require('../auth');
4
+ const { PremiumSpinner, sleep, typeAIResponse } = require('../utils/display');
34
5
 
35
- // ==================== DATASET ANALYSIS ====================
36
6
  async function dataset(file) {
37
7
  if (!isAuthenticated()) {
38
8
  console.error(chalk.red('āœ— Not authenticated. Please run: obx login'));
@@ -44,69 +14,72 @@ async function dataset(file) {
44
14
  return;
45
15
  }
46
16
 
47
- const missionName = generateMissionName();
48
-
49
17
  console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
50
- console.log(chalk.white.bold(`✨ ${missionName}`));
51
- console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════'));
52
- console.log(chalk.gray(`File: ${file}`));
53
- console.log(chalk.gray(`Type: Dataset Analysis (40-150 RCC)\n`));
18
+ console.log(chalk.white.bold('šŸ“Š DATASET ANALYSIS'));
19
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
54
20
 
55
- const spinner = new PremiumSpinner('Reading dataset');
21
+ console.log(chalk.white(` šŸ“„ File: ${chalk.hex('#F24E1E')(file)}`));
22
+
23
+ const spinner = new PremiumSpinner('✨ Mission Genesis - Analyzing dataset');
56
24
  spinner.start();
25
+ await sleep(500);
57
26
 
58
27
  try {
59
- spinner.update('Parsing data structure');
28
+ const token = await getAuthToken();
29
+ const fileContent = fs.readFileSync(file, 'utf8').substring(0, 5000);
30
+
31
+ spinner.update('šŸ“Š Analyzing dataset');
60
32
  await sleep(600);
61
-
62
- spinner.update('Detecting patterns');
33
+
34
+ spinner.update('šŸ” Detecting patterns');
63
35
  await sleep(600);
64
-
65
- spinner.update('Calculating statistics');
36
+
37
+ spinner.update('šŸ“ˆ Calculating statistics');
66
38
  await sleep(600);
67
-
68
- // Read file content (first 5000 chars for large files)
69
- const fileContent = fs.readFileSync(file, 'utf8').substring(0, 5000);
70
-
71
- const result = await createMission(
72
- `Analyze this dataset and provide:\n1. Statistical summary (mean, median, std dev, min, max)\n2. Pattern detection\n3. Anomaly identification\n4. Data quality assessment\n5. Visualization recommendations\n\nDataset content:\n${fileContent}`,
73
- 'DATASET_ANALYSIS'
74
- );
75
-
76
- spinner.succeed('Dataset analysis complete');
77
-
78
- console.log(chalk.gray(`\nšŸ“Š Model: ${result.model}`));
79
- console.log(chalk.gray(`šŸ’° RCC Cost: ${result.rccCost}`));
80
- console.log(chalk.gray(`šŸ’³ Remaining Balance: ${result.rccBalance} RCC\n`));
81
39
 
82
- // Display the analysis with typing animation
83
- await typeAIResponse(result.response);
84
-
85
- // Research score
86
- const score = generateResearchScore(result.response);
87
- await displayResearchScore(score);
88
-
89
- // Artifacts
90
- const artifacts = [
91
- { icon: 'šŸ“Š', name: 'Statistical Summary' },
92
- { icon: 'šŸ”', name: 'Pattern Report' },
93
- { icon: 'āš ļø', name: 'Anomaly Detection' },
94
- { icon: 'šŸ“ˆ', name: 'Data Quality Assessment' }
95
- ];
96
- await displayArtifacts(artifacts);
40
+ spinner.update('āš ļø Checking for anomalies');
41
+ await sleep(400);
42
+
43
+ const response = await fetch('https://obxvisionassistant-yyedhmslhq-uc.a.run.app', {
44
+ method: 'POST',
45
+ headers: {
46
+ 'Content-Type': 'application/json',
47
+ 'Authorization': `Bearer ${token}`
48
+ },
49
+ body: JSON.stringify({
50
+ taskType: 'DATASET_ANALYSIS',
51
+ message: fileContent,
52
+ model: 'deep'
53
+ })
54
+ });
55
+
56
+ if (!response.ok) {
57
+ const errorData = await response.json();
58
+ throw new Error(errorData.error || 'Failed to analyze dataset');
59
+ }
60
+
61
+ const data = await response.json();
97
62
 
98
- // Save report
99
- const filepath = saveReport(file, result.response, missionName);
100
- console.log(chalk.green(`āœ“ Report saved to: ${filepath}`));
101
- console.log(chalk.gray('\nView with: cat ' + filepath + '\n'));
63
+ spinner.succeed('āœ“ Dataset analysis complete (40-150 RCC)');
64
+
65
+ await typeAIResponse(data.response || 'Analysis data not available.');
66
+
67
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
68
+ console.log(chalk.white.bold('šŸ“¦ ARTIFACTS GENERATED'));
69
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
102
70
 
71
+ console.log(chalk.green(' āœ“ Statistical Summary'));
72
+ console.log(chalk.green(' āœ“ Pattern Report'));
73
+ console.log(chalk.green(' āœ“ Anomaly Detection'));
74
+
75
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════\n'));
76
+
103
77
  } catch (error) {
104
78
  spinner.fail('Failed to analyze dataset');
105
- console.error(chalk.red(error.response?.data?.error || error.message));
79
+ console.error(chalk.red(error.message));
106
80
  }
107
81
  }
108
82
 
109
- // ==================== CODE REVIEW ====================
110
83
  async function code(directory = '.') {
111
84
  if (!isAuthenticated()) {
112
85
  console.error(chalk.red('āœ— Not authenticated. Please run: obx login'));
@@ -118,153 +91,80 @@ async function code(directory = '.') {
118
91
  return;
119
92
  }
120
93
 
121
- const missionName = generateMissionName();
122
-
123
94
  console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
124
- console.log(chalk.white.bold(`✨ ${missionName}`));
125
- console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════'));
126
- console.log(chalk.gray(`Directory: ${directory}`));
127
- console.log(chalk.gray(`Type: Code Review (40-300 RCC)\n`));
95
+ console.log(chalk.white.bold('šŸ’» CODE REVIEW'));
96
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
97
+
98
+ console.log(chalk.white(` šŸ“‚ Directory: ${chalk.hex('#F24E1E')(directory)}`));
128
99
 
129
- const spinner = new PremiumSpinner('Scanning repository');
100
+ const spinner = new PremiumSpinner('✨ Mission Aurora - Reviewing code');
130
101
  spinner.start();
102
+ await sleep(500);
131
103
 
132
104
  try {
133
- spinner.update('Analyzing architecture');
134
- await sleep(800);
135
-
136
- spinner.update('Security audit');
137
- await sleep(800);
138
-
139
- spinner.update('Performance review');
105
+ const token = await getAuthToken();
106
+
107
+ spinner.update('šŸ“‚ Scanning repository');
140
108
  await sleep(600);
141
-
142
- // Read key files from directory
143
- const files = fs.readdirSync(directory).filter(f =>
144
- f.endsWith('.js') || f.endsWith('.py') || f.endsWith('.ts') ||
145
- f.endsWith('.java') || f.endsWith('.cpp') || f.endsWith('.go')
146
- ).slice(0, 10); // Limit to 10 files
147
-
109
+
110
+ spinner.update('šŸ” Analyzing architecture');
111
+ await sleep(600);
112
+
113
+ spinner.update('šŸ” Security audit');
114
+ await sleep(600);
115
+
116
+ spinner.update('⚔ Performance review');
117
+ await sleep(400);
118
+
119
+ // Read code files (limit to 5000 chars)
148
120
  let codeContent = '';
149
- for (const file of files) {
121
+ const files = fs.readdirSync(directory);
122
+ for (const file of files.slice(0, 10)) {
150
123
  const filePath = path.join(directory, file);
151
- if (fs.statSync(filePath).isFile()) {
152
- codeContent += `\n\n// File: ${file}\n`;
153
- codeContent += fs.readFileSync(filePath, 'utf8').substring(0, 2000);
124
+ if (fs.statSync(filePath).isFile() && file.match(/\.(js|ts|py|java|cpp|go|rs)$/)) {
125
+ codeContent += `\n// File: ${file}\n`;
126
+ codeContent += fs.readFileSync(filePath, 'utf8').substring(0, 1000);
154
127
  }
155
128
  }
156
-
157
- const result = await createMission(
158
- `Review this code repository and provide:\n1. Architecture analysis\n2. Security vulnerabilities\n3. Performance issues\n4. Code quality assessment\n5. Optimization suggestions\n6. Best practices recommendations\n\nCode:\n${codeContent}`,
159
- 'CODE_REVIEW'
160
- );
161
-
162
- spinner.succeed('Code review complete');
163
-
164
- console.log(chalk.gray(`\nšŸ“Š Model: ${result.model}`));
165
- console.log(chalk.gray(`šŸ’° RCC Cost: ${result.rccCost}`));
166
- console.log(chalk.gray(`šŸ’³ Remaining Balance: ${result.rccBalance} RCC\n`));
167
-
168
- // Display the review with typing animation
169
- await typeAIResponse(result.response);
170
-
171
- // Research score
172
- const score = generateResearchScore(result.response);
173
- await displayResearchScore(score);
174
-
175
- // Artifacts
176
- const artifacts = [
177
- { icon: 'šŸ—ļø', name: 'Architecture Report' },
178
- { icon: 'šŸ”', name: 'Security Audit' },
179
- { icon: '⚔', name: 'Performance Analysis' },
180
- { icon: 'šŸ’”', name: 'Optimization Suggestions' }
181
- ];
182
- await displayArtifacts(artifacts);
183
-
184
- // Save report
185
- const filepath = saveReport(directory, result.response, missionName);
186
- console.log(chalk.green(`āœ“ Report saved to: ${filepath}`));
187
- console.log(chalk.gray('\nView with: cat ' + filepath + '\n'));
188
-
189
- } catch (error) {
190
- spinner.fail('Failed to review code');
191
- console.error(chalk.red(error.response?.data?.error || error.message));
192
- }
193
- }
194
129
 
195
- // ==================== MEDICAL IMAGING ANALYSIS ====================
196
- async function medical(file) {
197
- if (!isAuthenticated()) {
198
- console.error(chalk.red('āœ— Not authenticated. Please run: obx login'));
199
- return;
200
- }
201
-
202
- if (!fs.existsSync(file)) {
203
- console.error(chalk.red(`āœ— File not found: ${file}`));
204
- return;
205
- }
130
+ const response = await fetch('https://obxvisionassistant-yyedhmslhq-uc.a.run.app', {
131
+ method: 'POST',
132
+ headers: {
133
+ 'Content-Type': 'application/json',
134
+ 'Authorization': `Bearer ${token}`
135
+ },
136
+ body: JSON.stringify({
137
+ taskType: 'CODE_REVIEW',
138
+ message: codeContent,
139
+ model: 'deep'
140
+ })
141
+ });
142
+
143
+ if (!response.ok) {
144
+ const errorData = await response.json();
145
+ throw new Error(errorData.error || 'Failed to review code');
146
+ }
206
147
 
207
- const missionName = generateMissionName();
208
-
209
- console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
210
- console.log(chalk.white.bold(`✨ ${missionName}`));
211
- console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════'));
212
- console.log(chalk.gray(`File: ${file}`));
213
- console.log(chalk.gray(`Type: Medical Imaging Analysis (30-120 RCC)\n`));
148
+ const data = await response.json();
149
+
150
+ spinner.succeed('āœ“ Code review complete (40-300 RCC)');
214
151
 
215
- const spinner = new PremiumSpinner('Loading medical image');
216
- spinner.start();
152
+ await typeAIResponse(data.response || 'Review data not available.');
217
153
 
218
- try {
219
- spinner.update('Analyzing image patterns');
220
- await sleep(800);
221
-
222
- spinner.update('Detecting anomalies');
223
- await sleep(800);
224
-
225
- spinner.update('Comparing with reference database');
226
- await sleep(600);
154
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
155
+ console.log(chalk.white.bold('šŸ“¦ ARTIFACTS GENERATED'));
156
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
227
157
 
228
- // For images, we send metadata and ask for analysis
229
- const stats = fs.statSync(file);
230
- const fileExt = path.extname(file).toLowerCase();
231
-
232
- const result = await createMission(
233
- `Analyze this medical imaging file and provide a comprehensive research-oriented analysis:\n\nFile: ${path.basename(file)}\nFormat: ${fileExt}\nSize: ${stats.size} bytes\n\nPlease provide:\n1. Image type identification (X-ray, MRI, CT, Ultrasound, etc.)\n2. Anatomical region identification\n3. Pattern detection and findings\n4. Potential anomalies or areas of interest\n5. Comparative analysis with typical presentations\n6. Research implications and suggested further investigation\n7. Confidence level and limitations\n\nNote: This is for research purposes only, not clinical diagnosis.`,
234
- 'MEDICAL_ANALYSIS'
235
- );
236
-
237
- spinner.succeed('Medical analysis complete');
238
-
239
- console.log(chalk.gray(`\nšŸ“Š Model: ${result.model}`));
240
- console.log(chalk.gray(`šŸ’° RCC Cost: ${result.rccCost}`));
241
- console.log(chalk.gray(`šŸ’³ Remaining Balance: ${result.rccBalance} RCC\n`));
158
+ console.log(chalk.green(' āœ“ Architecture Report'));
159
+ console.log(chalk.green(' āœ“ Security Audit'));
160
+ console.log(chalk.green(' āœ“ Optimization Suggestions'));
161
+
162
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════\n'));
242
163
 
243
- // šŸ”„ THIS WAS MISSING - Display the analysis with typing animation
244
- await typeAIResponse(result.response);
245
-
246
- // Research score
247
- const score = generateResearchScore(result.response);
248
- await displayResearchScore(score);
249
-
250
- // Artifacts
251
- const artifacts = [
252
- { icon: 'šŸ„', name: 'Pattern Detection Report' },
253
- { icon: 'šŸ”', name: 'Anomaly Analysis' },
254
- { icon: 'šŸ“Š', name: 'Comparative Analysis' },
255
- { icon: 'šŸ“', name: 'Research Implications' }
256
- ];
257
- await displayArtifacts(artifacts);
258
-
259
- // Save report
260
- const filepath = saveReport(file, result.response, missionName);
261
- console.log(chalk.green(`āœ“ Report saved to: ${filepath}`));
262
- console.log(chalk.gray('\nView with: cat ' + filepath + '\n'));
263
-
264
164
  } catch (error) {
265
- spinner.fail('Failed to analyze medical image');
266
- console.error(chalk.red(error.response?.data?.error || error.message));
165
+ spinner.fail('Failed to review code');
166
+ console.error(chalk.red(error.message));
267
167
  }
268
168
  }
269
169
 
270
- module.exports = { dataset, code, medical };
170
+ module.exports = { dataset, code };
@@ -1,6 +1,6 @@
1
1
  const chalk = require('chalk');
2
- const { isAuthenticated } = require('../auth');
3
- const { db, getCurrentUserUid, getCurrentUserEmail, isTokenValid } = require('../firebase');
2
+ const { isAuthenticated, getCurrentUserUid, getCurrentUserEmail } = require('../auth');
3
+ const { db } = require('../firebase');
4
4
  const { PremiumSpinner, sleep } = require('../utils/display');
5
5
  const { collection, query, where, orderBy, limit, getDocs } = require('firebase/firestore');
6
6
 
@@ -10,7 +10,6 @@ async function earnings() {
10
10
  return;
11
11
  }
12
12
 
13
- // Get user UID from stored token (NOT from auth.currentUser)
14
13
  const uid = getCurrentUserUid();
15
14
  const email = getCurrentUserEmail();
16
15
 
@@ -24,7 +23,6 @@ async function earnings() {
24
23
  spinner.start();
25
24
 
26
25
  try {
27
- // Fetch real published missions with earnings data
28
26
  const publishedQuery = query(
29
27
  collection(db, 'missions'),
30
28
  where('uid', '==', uid),
@@ -45,13 +43,11 @@ async function earnings() {
45
43
 
46
44
  spinner.succeed('Earnings data loaded');
47
45
 
48
- // Calculate real earnings from actual data
49
46
  const totalViews = publishedMissions.reduce((sum, m) => sum + (m.views || 0), 0);
50
47
  const totalDownloads = publishedMissions.reduce((sum, m) => sum + (m.downloads || 0), 0);
51
48
  const totalCitations = publishedMissions.reduce((sum, m) => sum + (m.citations || 0), 0);
52
49
  const totalEarnings = publishedMissions.reduce((sum, m) => sum + (m.earnings || 0), 0);
53
50
 
54
- // Calculate monthly breakdown
55
51
  const now = new Date();
56
52
  const thisMonth = now.getMonth();
57
53
  const thisYear = now.getFullYear();
@@ -77,7 +73,6 @@ async function earnings() {
77
73
  console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
78
74
 
79
75
  console.log(chalk.gray(` šŸ‘¤ User: ${chalk.white(email || 'Unknown')}`));
80
- console.log(chalk.gray(` šŸ†” UID: ${chalk.gray(uid.substring(0, 16))}...`));
81
76
 
82
77
  console.log(chalk.white.bold('\n\nšŸ“Š OVERVIEW'));
83
78
  console.log(chalk.gray('─'.repeat(60)));
@@ -98,12 +93,6 @@ async function earnings() {
98
93
  console.log(chalk.white(` šŸ’µ Average Per View: ${chalk.green('₹' + averagePerView.toFixed(3))}`));
99
94
  }
100
95
 
101
- console.log(chalk.white.bold('\n\nšŸ’³ PAYOUT DETAILS'));
102
- console.log(chalk.gray('─'.repeat(60)));
103
- console.log(chalk.white(`\n šŸ“Š Revenue Share: ${chalk.green('80%')}`));
104
- console.log(chalk.white(` šŸ“… Next Payout: ${chalk.green('1st of next month')}`));
105
- console.log(chalk.white(` šŸ’³ Payout Method: ${chalk.green('Bank Transfer / UPI')}`));
106
-
107
96
  if (publishedMissions.length > 0) {
108
97
  console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
109
98
  console.log(chalk.white.bold('šŸ“Š YOUR PUBLISHED RESEARCH'));
@@ -124,14 +113,6 @@ async function earnings() {
124
113
 
125
114
  console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════\n'));
126
115
 
127
- console.log(chalk.white.bold('šŸ’” TIPS TO INCREASE EARNINGS'));
128
- console.log(chalk.gray('─'.repeat(60)));
129
- console.log(chalk.white('\n āœ“ Publish more research to increase views'));
130
- console.log(chalk.white(' āœ“ Share your research on social media'));
131
- console.log(chalk.white(' āœ“ Use relevant tags for better discoverability'));
132
- console.log(chalk.white(' āœ“ Create high-quality, comprehensive research'));
133
- console.log(chalk.white(' āœ“ Update research regularly with new findings\n'));
134
-
135
116
  } catch (error) {
136
117
  spinner.fail('Failed to fetch earnings data');
137
118
  console.error(chalk.red(error.message));
@@ -1,94 +1,151 @@
1
1
  const chalk = require('chalk');
2
- const { getAuthToken } = require('../auth');
3
- const { isAuthenticated } = require('../auth');
4
- const config = require('../config');
5
- const {
6
- displayMorningBriefing,
7
- PremiumSpinner,
8
- sleep
9
- } = require('../utils/display');
10
-
11
- /**
12
- * Fetch and display morning briefing
13
- */
2
+ const { isAuthenticated, getCurrentUserUid, getCurrentUserEmail } = require('../auth');
3
+ const { db } = require('../firebase');
4
+ const { PremiumSpinner, sleep } = require('../utils/display');
5
+ const { collection, query, where, orderBy, limit, getDocs } = require('firebase/firestore');
6
+
14
7
  async function morning() {
15
8
  if (!isAuthenticated()) {
16
9
  console.error(chalk.red('āœ— Not authenticated. Please run: obx login'));
17
10
  return;
18
11
  }
19
12
 
20
- const spinner = new PremiumSpinner('Fetching your research updates');
13
+ const uid = getCurrentUserUid();
14
+ const email = getCurrentUserEmail();
15
+
16
+ if (!uid) {
17
+ console.error(chalk.red('āœ— Could not extract user ID from token.'));
18
+ console.error(chalk.gray('Please run: obx login'));
19
+ return;
20
+ }
21
+
22
+ const spinner = new PremiumSpinner('ā˜€ļø Fetching your daily briefing');
21
23
  spinner.start();
22
24
 
23
25
  try {
24
- const token = await getAuthToken();
25
-
26
- spinner.update('Checking active missions');
27
- await sleep(500);
28
-
29
- spinner.update('Scanning for new papers');
30
- await sleep(500);
31
-
32
- spinner.update('Analyzing updates');
33
- await sleep(500);
26
+ // Get today's date
27
+ const today = new Date();
28
+ const todayStart = new Date(today.getFullYear(), today.getMonth(), today.getDate());
29
+ const yesterdayStart = new Date(todayStart);
30
+ yesterdayStart.setDate(yesterdayStart.getDate() - 1);
31
+
32
+ // Fetch active missions
33
+ const activeQuery = query(
34
+ collection(db, 'missions'),
35
+ where('uid', '==', uid),
36
+ where('status', 'in', ['running', 'pending'])
37
+ );
38
+
39
+ const activeSnapshot = await getDocs(activeQuery);
40
+ const activeMissions = [];
41
+ activeSnapshot.forEach(doc => {
42
+ activeMissions.push({ id: doc.id, ...doc.data() });
43
+ });
44
+
45
+ // Fetch recent completed missions (last 7 days)
46
+ const sevenDaysAgo = new Date();
47
+ sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
34
48
 
35
- // Call morning briefing API
36
- const response = await fetch('https://morningbriefing-yyedhmslhq-uc.a.run.app', {
37
- method: 'GET',
38
- headers: {
39
- 'Authorization': `Bearer ${token}`,
40
- 'Content-Type': 'application/json'
41
- }
49
+ const recentQuery = query(
50
+ collection(db, 'missions'),
51
+ where('uid', '==', uid),
52
+ where('status', '==', 'completed'),
53
+ orderBy('createdAt', 'desc'),
54
+ limit(10)
55
+ );
56
+
57
+ const recentSnapshot = await getDocs(recentQuery);
58
+ const recentMissions = [];
59
+ recentSnapshot.forEach(doc => {
60
+ recentMissions.push({ id: doc.id, ...doc.data() });
42
61
  });
62
+
63
+ // Fetch published missions for earnings
64
+ const publishedQuery = query(
65
+ collection(db, 'missions'),
66
+ where('uid', '==', uid),
67
+ where('isPublished', '==', true)
68
+ );
69
+
70
+ const publishedSnapshot = await getDocs(publishedQuery);
71
+ const publishedMissions = [];
72
+ publishedSnapshot.forEach(doc => {
73
+ publishedMissions.push({ id: doc.id, ...doc.data() });
74
+ });
75
+
76
+ const totalViews = publishedMissions.reduce((sum, m) => sum + (m.views || 0), 0);
77
+ const totalEarnings = publishedMissions.reduce((sum, m) => sum + (m.earnings || 0), 0);
78
+
79
+ spinner.succeed('Daily briefing loaded');
80
+
81
+ // Display the briefing
82
+ const dateString = today.toLocaleDateString('en-US', {
83
+ weekday: 'long',
84
+ year: 'numeric',
85
+ month: 'long',
86
+ day: 'numeric'
87
+ });
88
+
89
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
90
+ console.log(chalk.white.bold('ā˜€ļø GOOD MORNING, RESEARCHER!'));
91
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
43
92
 
44
- if (!response.ok) {
45
- throw new Error(`API error: ${response.status}`);
93
+ console.log(chalk.gray(` šŸ‘¤ ${chalk.white(email || 'Unknown')}`));
94
+ console.log(chalk.gray(` šŸ“… ${chalk.white(dateString)}`));
95
+
96
+ // Active Missions
97
+ if (activeMissions.length > 0) {
98
+ console.log(chalk.white.bold('\n\nšŸŽÆ ACTIVE MISSIONS'));
99
+ console.log(chalk.gray('─'.repeat(60)));
100
+
101
+ activeMissions.forEach((m, i) => {
102
+ const topic = (m.message || 'No topic').substring(0, 50);
103
+ console.log(chalk.white(`\n ✨ ${chalk.hex('#F24E1E')(m.id)}`));
104
+ console.log(chalk.white(` Topic: ${topic}`));
105
+ console.log(chalk.yellow(` Status: ${m.status}`));
106
+ });
46
107
  }
108
+
109
+ // Recent Completions
110
+ if (recentMissions.length > 0) {
111
+ console.log(chalk.white.bold('\n\nāœ… RECENTLY COMPLETED'));
112
+ console.log(chalk.gray('─'.repeat(60)));
113
+
114
+ recentMissions.slice(0, 5).forEach((m, i) => {
115
+ const topic = (m.message || 'No topic').substring(0, 50);
116
+ const date = m.createdAt?.toDate?.().toLocaleDateString() || 'N/A';
117
+ console.log(chalk.white(`\n ${i + 1}. ${topic}`));
118
+ console.log(chalk.gray(` Completed: ${date}`));
119
+ });
120
+ }
121
+
122
+ // Earnings Summary
123
+ if (publishedMissions.length > 0) {
124
+ console.log(chalk.white.bold('\n\nšŸ’° EARNINGS SUMMARY'));
125
+ console.log(chalk.gray('─'.repeat(60)));
126
+
127
+ console.log(chalk.white(`\n šŸ“¤ Published Research: ${chalk.green(publishedMissions.length)}`));
128
+ console.log(chalk.white(` šŸ‘ Total Views: ${chalk.green(totalViews.toLocaleString())}`));
129
+ console.log(chalk.white(` šŸ’° Total Earnings: ${chalk.green('₹' + totalEarnings.toLocaleString())}`));
130
+ }
131
+
132
+ // Recommendations
133
+ console.log(chalk.white.bold('\n\nšŸ’” RECOMMENDATIONS'));
134
+ console.log(chalk.gray('─'.repeat(60)));
47
135
 
48
- const briefing = await response.json();
49
-
50
- spinner.succeed('Briefing ready');
51
-
52
- // Display briefing
53
- await displayMorningBriefing(briefing);
54
-
136
+ console.log(chalk.white('\n ā–ø Check active missions with "obx mission status"'));
137
+ console.log(chalk.white(' ā–ø Publish completed research with "obx publish"'));
138
+ console.log(chalk.white(' ā–ø View earnings with "obx earnings"'));
139
+ console.log(chalk.white(' ā–ø Generate new research with "obx mission create"'));
140
+
141
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
142
+ console.log(chalk.gray('Free • No RCC cost'));
143
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
144
+
55
145
  } catch (error) {
56
- spinner.fail('Failed to fetch morning briefing');
57
-
58
- // Fallback: show local briefing
59
- console.log(chalk.gray('\n Showing local briefing...\n'));
60
- await displayLocalBriefing();
146
+ spinner.fail('Failed to fetch daily briefing');
147
+ console.error(chalk.red(error.message));
61
148
  }
62
149
  }
63
150
 
64
- /**
65
- * Display local briefing when API is unavailable
66
- */
67
- async function displayLocalBriefing() {
68
- const today = new Date().toLocaleDateString('en-US', {
69
- weekday: 'long',
70
- year: 'numeric',
71
- month: 'long',
72
- day: 'numeric'
73
- });
74
-
75
- const briefing = {
76
- date: today,
77
- updates: [
78
- { type: 'info', text: 'No new updates since last check' },
79
- { type: 'info', text: 'All missions are up to date' }
80
- ],
81
- missions: [
82
- { name: 'Mission Atlas', status: 'Complete', progress: 100 }
83
- ],
84
- recommendations: [
85
- 'Run "obx morning" daily to stay updated',
86
- 'Check mission health with "obx mission status"',
87
- 'Explore new topics with "obx literature [topic]"'
88
- ]
89
- };
90
-
91
- await displayMorningBriefing(briefing);
92
- }
93
-
94
151
  module.exports = { morning };
@@ -1,7 +1,7 @@
1
1
  const chalk = require('chalk');
2
2
  const inquirer = require('inquirer');
3
- const { getAuthToken, isAuthenticated } = require('../auth');
4
- const { db, getCurrentUserUid, getCurrentUserEmail } = require('../firebase');
3
+ const { getAuthToken, isAuthenticated, getCurrentUserUid, getCurrentUserEmail } = require('../auth');
4
+ const { db } = require('../firebase');
5
5
  const { PremiumSpinner, sleep } = require('../utils/display');
6
6
  const { collection, query, where, orderBy, limit, getDocs, doc, updateDoc } = require('firebase/firestore');
7
7
 
@@ -11,7 +11,6 @@ async function publish(missionId) {
11
11
  return;
12
12
  }
13
13
 
14
- // Get user UID from stored token
15
14
  const uid = getCurrentUserUid();
16
15
  const email = getCurrentUserEmail();
17
16
 
@@ -26,9 +25,7 @@ async function publish(missionId) {
26
25
  console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
27
26
 
28
27
  console.log(chalk.gray(` šŸ‘¤ User: ${chalk.white(email || 'Unknown')}`));
29
- console.log(chalk.gray(` šŸ†” UID: ${chalk.gray(uid.substring(0, 16))}...`));
30
28
 
31
- // If no mission ID provided, fetch real missions from Firestore
32
29
  if (!missionId) {
33
30
  console.log(chalk.gray('\nFetching your completed missions...\n'));
34
31
 
@@ -36,7 +33,6 @@ async function publish(missionId) {
36
33
  spinner.start();
37
34
 
38
35
  try {
39
- // Fetch real completed missions from Firestore
40
36
  const missionsQuery = query(
41
37
  collection(db, 'missions'),
42
38
  where('uid', '==', uid),
@@ -97,7 +93,6 @@ async function publish(missionId) {
97
93
  }
98
94
  }
99
95
 
100
- // Publish the mission
101
96
  const spinner = new PremiumSpinner('Preparing to publish research');
102
97
  spinner.start();
103
98
  await sleep(500);
@@ -106,7 +101,6 @@ async function publish(missionId) {
106
101
  spinner.update('Fetching mission data');
107
102
  const missionRef = doc(db, 'missions', missionId);
108
103
 
109
- // Get the mission document
110
104
  const missionSnap = await getDocs(query(collection(db, 'missions'), where('__name__', '==', missionId)));
111
105
 
112
106
  if (missionSnap.empty) {
@@ -120,7 +114,6 @@ async function publish(missionId) {
120
114
  spinner.update('Generating SEO-optimized page');
121
115
  await sleep(500);
122
116
 
123
- // Generate slug from mission topic
124
117
  const slug = (missionData.message || missionId)
125
118
  .toLowerCase()
126
119
  .replace(/[^a-z0-9]+/g, '-')
@@ -136,7 +129,6 @@ async function publish(missionId) {
136
129
  spinner.update('Enabling revenue tracking');
137
130
  await sleep(400);
138
131
 
139
- // Update mission in Firestore
140
132
  await updateDoc(missionRef, {
141
133
  isPublished: true,
142
134
  publishedAt: new Date(),
@@ -163,18 +155,7 @@ async function publish(missionId) {
163
155
  console.log(chalk.white(`šŸ” Google Indexing: ${chalk.green('Requested')} (24 hours)`));
164
156
  console.log(chalk.white(`šŸ“Š Analytics: ${chalk.green('Enabled')}`));
165
157
 
166
- console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
167
- console.log(chalk.white.bold('šŸ’° START EARNING'));
168
- console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
169
-
170
- console.log(chalk.gray('Your research is now live! Here\'s what happens next:'));
171
- console.log(chalk.white('\n āœ“ Google indexes your research within 24 hours'));
172
- console.log(chalk.white(' āœ“ Readers discover it through search'));
173
- console.log(chalk.white(' āœ“ Ads are displayed to readers'));
174
- console.log(chalk.white(' āœ“ You earn 80% of all ad revenue'));
175
- console.log(chalk.white(' āœ“ Monthly payouts to your account\n'));
176
-
177
- console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
158
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════\n'));
178
159
 
179
160
  } catch (error) {
180
161
  spinner.fail('Failed to publish research');
@@ -1,152 +1,91 @@
1
1
  const chalk = require('chalk');
2
- const { createMission } = require('../api');
3
- const { isAuthenticated } = require('../auth');
4
- const {
5
- generateMissionName,
6
- displayTimeline,
7
- PremiumSpinner,
8
- sleep
9
- } = require('../utils/display');
10
-
11
- /**
12
- * Generate research timeline for a topic
13
- */
2
+ const { getAuthToken, isAuthenticated } = require('../auth');
3
+ const { PremiumSpinner, sleep, typeAIResponse } = require('../utils/display');
4
+
14
5
  async function timeline(topic) {
15
6
  if (!isAuthenticated()) {
16
7
  console.error(chalk.red('āœ— Not authenticated. Please run: obx login'));
17
8
  return;
18
9
  }
19
10
 
20
- if (!topic || topic.trim() === '') {
21
- console.error(chalk.red('āœ— Please provide a research topic'));
22
- console.log(chalk.gray(' Usage: obx timeline "quantum computing"'));
11
+ if (!topic) {
12
+ console.error(chalk.red('āœ— Please provide a research topic.'));
13
+ console.log(chalk.gray('Usage: obx timeline "quantum computing"\n'));
23
14
  return;
24
15
  }
25
16
 
26
- const missionName = generateMissionName();
27
-
28
17
  console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
29
- console.log(chalk.white.bold(`šŸ“… ${missionName}`));
30
- console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════'));
31
- console.log(chalk.gray(`Topic: ${topic}`));
32
- console.log(chalk.gray(`Type: Research Timeline (80 RCC)\n`));
18
+ console.log(chalk.white.bold('šŸ“… RESEARCH TIMELINE GENERATOR'));
19
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
20
+
21
+ console.log(chalk.white(` šŸ“Œ Topic: ${chalk.hex('#F24E1E')(topic)}`));
22
+ console.log(chalk.white(` šŸ’° Cost: ${chalk.green('80 RCC')}`));
33
23
 
34
- const spinner = new PremiumSpinner('Mapping research evolution');
24
+ const spinner = new PremiumSpinner('✨ Mission Polaris - Generating timeline');
35
25
  spinner.start();
26
+ await sleep(500);
36
27
 
37
28
  try {
38
- spinner.update('Searching historical papers');
39
- await sleep(800);
40
-
41
- spinner.update('Identifying key milestones');
42
- await sleep(800);
43
-
44
- spinner.update('Building timeline graph');
29
+ const token = await getAuthToken();
30
+
31
+ spinner.update('šŸ” Searching historical research');
45
32
  await sleep(600);
46
-
47
- spinner.update('Analyzing impact');
33
+
34
+ spinner.update('šŸ“š Analyzing foundational papers');
48
35
  await sleep(600);
49
-
50
- // Call backend API
51
- const result = await createMission(
52
- `Generate a comprehensive research timeline for: ${topic}.
53
- Include major papers, breakthroughs, patents, and milestones from the earliest research to present day.
54
- Format as JSON array with objects containing: year, title, description, impact (High/Medium/Low), type (paper/patent/breakthrough/milestone).
55
- Sort chronologically. Include 8-12 key events.`,
56
- 'RESEARCH_TIMELINE'
57
- );
58
-
59
- spinner.succeed('Timeline generated');
60
-
61
- console.log(chalk.gray(`\nšŸ“Š Model: ${result.model}`));
62
- console.log(chalk.gray(`šŸ’° RCC Cost: ${result.rccCost}`));
63
- console.log(chalk.gray(`šŸ’³ Remaining Balance: ${result.rccBalance} RCC\n`));
64
36
 
65
- // Parse timeline from response
66
- let timelineData = [];
67
-
68
- try {
69
- // Try to extract JSON array from response
70
- const jsonMatch = result.response.match(/\[[\s\S]*\]/);
71
- if (jsonMatch) {
72
- timelineData = JSON.parse(jsonMatch[0]);
73
- }
74
- } catch (e) {
75
- // Fallback: generate sample timeline
76
- timelineData = generateSampleTimeline(topic);
77
- }
78
-
79
- // Add current research as final event
80
- timelineData.push({
81
- year: new Date().getFullYear(),
82
- title: 'Your Research',
83
- description: `Continuing the evolution of ${topic}`,
84
- impact: 'High',
85
- isCurrentResearch: true
37
+ spinner.update('šŸ—ŗļø Mapping field evolution');
38
+ await sleep(600);
39
+
40
+ spinner.update('šŸ“Š Identifying key milestones');
41
+ await sleep(600);
42
+
43
+ spinner.update('āœļø Compiling timeline');
44
+ await sleep(400);
45
+
46
+ // Call the AI backend
47
+ const response = await fetch('https://obxvisionassistant-yyedhmslhq-uc.a.run.app', {
48
+ method: 'POST',
49
+ headers: {
50
+ 'Content-Type': 'application/json',
51
+ 'Authorization': `Bearer ${token}`
52
+ },
53
+ body: JSON.stringify({
54
+ taskType: 'RESEARCH_TIMELINE',
55
+ message: topic,
56
+ model: 'deep'
57
+ })
86
58
  });
59
+
60
+ if (!response.ok) {
61
+ const errorData = await response.json();
62
+ throw new Error(errorData.error || 'Failed to generate timeline');
63
+ }
64
+
65
+ const data = await response.json();
87
66
 
88
- // Display timeline
89
- await displayTimeline(timelineData, topic);
90
-
91
- console.log(chalk.green('āœ“ Timeline saved to: ') + chalk.white(`~/obx-reports/${missionName.replace(/\s+/g, '_')}_timeline.md`));
92
- console.log(chalk.gray('\nView with: obx timeline "' + topic + '"\n'));
67
+ spinner.succeed('āœ“ Timeline generated (80 RCC)');
68
+
69
+ // Display the timeline
70
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
71
+ console.log(chalk.white.bold(`šŸ“… Research Timeline: ${topic}`));
72
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
73
+
74
+ await typeAIResponse(data.response || 'Timeline data not available.');
75
+
76
+ console.log(chalk.hex('#F24E1E')('\n═══════════════════════════════════════════════════════════'));
77
+ console.log(chalk.white.bold('šŸŽÆ YOUR RESEARCH'));
78
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
93
79
 
80
+ console.log(chalk.green(` ✨ Your research continues from here`));
81
+ console.log(chalk.gray(' Add your contribution to this evolving field.\n'));
82
+
83
+ console.log(chalk.hex('#F24E1E')('═══════════════════════════════════════════════════════════\n'));
84
+
94
85
  } catch (error) {
95
86
  spinner.fail('Failed to generate timeline');
96
- console.error(chalk.red(error.response?.data?.error || error.message));
87
+ console.error(chalk.red(error.message));
97
88
  }
98
89
  }
99
90
 
100
- /**
101
- * Generate sample timeline when API doesn't return structured data
102
- */
103
- function generateSampleTimeline(topic) {
104
- const currentYear = new Date().getFullYear();
105
-
106
- return [
107
- {
108
- year: currentYear - 30,
109
- title: `Foundational Research on ${topic}`,
110
- description: 'Early theoretical work and initial experiments',
111
- impact: 'High',
112
- type: 'paper'
113
- },
114
- {
115
- year: currentYear - 20,
116
- title: 'First Major Breakthrough',
117
- description: 'Key discovery that shaped the field',
118
- impact: 'High',
119
- type: 'breakthrough'
120
- },
121
- {
122
- year: currentYear - 15,
123
- title: 'Commercial Applications Begin',
124
- description: 'First industry implementations',
125
- impact: 'Medium',
126
- type: 'milestone'
127
- },
128
- {
129
- year: currentYear - 10,
130
- title: 'Patent Landscape Expands',
131
- description: 'Significant patent filings by major companies',
132
- impact: 'Medium',
133
- type: 'patent'
134
- },
135
- {
136
- year: currentYear - 5,
137
- title: 'State-of-the-Art Advances',
138
- description: 'Modern techniques and methodologies',
139
- impact: 'High',
140
- type: 'paper'
141
- },
142
- {
143
- year: currentYear - 2,
144
- title: 'Recent Breakthrough',
145
- description: 'Latest major advancement in the field',
146
- impact: 'High',
147
- type: 'breakthrough'
148
- }
149
- ];
150
- }
151
-
152
91
  module.exports = { timeline };
package/src/firebase.js CHANGED
@@ -1,7 +1,6 @@
1
1
  const { initializeApp, getApps } = require('firebase/app');
2
2
  const { getAuth } = require('firebase/auth');
3
3
  const { getFirestore } = require('firebase/firestore');
4
- const Conf = require('conf');
5
4
 
6
5
  const firebaseConfig = {
7
6
  apiKey: "AIzaSyDlgXId4pLlYqm-MDuhfz3dLH24KBRHkw8",
@@ -23,82 +22,13 @@ if (getApps().length === 0) {
23
22
  const auth = getAuth(app);
24
23
  const db = getFirestore(app);
25
24
 
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
- }
25
+ // Re-export user functions from auth.js (single source of truth)
26
+ const { getCurrentUserUid, getCurrentUserEmail } = require('./auth');
94
27
 
95
28
  module.exports = {
96
29
  app,
97
30
  auth,
98
- db,
99
- tokenStore,
100
- decodeToken,
31
+ db,
101
32
  getCurrentUserUid,
102
- getCurrentUserEmail,
103
- isTokenValid
33
+ getCurrentUserEmail
104
34
  };