vibex-sh 0.12.3 → 0.13.0

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.
Files changed (2) hide show
  1. package/index.js +118 -22
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -449,6 +449,7 @@ async function handleInit(options) {
449
449
 
450
450
  // Get token - required for authenticated session creation
451
451
  let token = process.env.VIBEX_TOKEN || await getStoredToken();
452
+ const tokenSource = process.env.VIBEX_TOKEN ? 'cli-env' : 'cli-config';
452
453
  if (!token) {
453
454
  console.error('\n ✗ Authentication required');
454
455
  console.error(' 💡 Run: npx vibex-sh login');
@@ -462,6 +463,7 @@ async function handleInit(options) {
462
463
  headers: {
463
464
  'Content-Type': 'application/json',
464
465
  'Authorization': `Bearer ${token}`,
466
+ 'X-Token-Source': tokenSource,
465
467
  },
466
468
  body: JSON.stringify({
467
469
  enabledParsers: enabledParsers.length > 0 ? enabledParsers : undefined,
@@ -556,7 +558,20 @@ async function handleSendLogs(options) {
556
558
  const { webUrl, socketUrl } = getProductionUrls();
557
559
 
558
560
  // Get token - REQUIRED for all operations
559
- let token = options.token || process.env.VIBEX_TOKEN || await getStoredToken();
561
+ // Track token source for debugging
562
+ let token;
563
+ let tokenSource = 'cli-config'; // default
564
+ if (options.token) {
565
+ token = options.token;
566
+ tokenSource = 'cli-option';
567
+ } else if (process.env.VIBEX_TOKEN) {
568
+ token = process.env.VIBEX_TOKEN;
569
+ tokenSource = 'cli-env';
570
+ } else {
571
+ token = await getStoredToken();
572
+ tokenSource = 'cli-config';
573
+ }
574
+
560
575
  if (!token) {
561
576
  console.error('\n ✗ Authentication required');
562
577
  console.error(' 💡 Run: npx vibex-sh login to authenticate');
@@ -602,11 +617,14 @@ async function handleSendLogs(options) {
602
617
 
603
618
  try {
604
619
  const createUrl = `${webUrl}/api/sessions/create`;
620
+ // Determine token source for this request
621
+ const requestTokenSource = options.token ? 'cli-option' : (process.env.VIBEX_TOKEN ? 'cli-env' : 'cli-config');
605
622
  const response = await httpRequest(createUrl, {
606
623
  method: 'POST',
607
624
  headers: {
608
625
  'Content-Type': 'application/json',
609
626
  'Authorization': `Bearer ${token}`,
627
+ 'X-Token-Source': requestTokenSource,
610
628
  },
611
629
  body: JSON.stringify({
612
630
  enabledParsers: enabledParsers.length > 0 ? enabledParsers : undefined,
@@ -728,7 +746,69 @@ async function handleSendLogs(options) {
728
746
  });
729
747
  };
730
748
 
731
- const connectWebSocket = () => {
749
+ // WebSocket token cache
750
+ let websocketToken = null;
751
+ let websocketTokenExpiresAt = 0;
752
+
753
+ // Fetch WebSocket token from API
754
+ const fetchWebSocketToken = async () => {
755
+ try {
756
+ // Check if we have a valid cached token
757
+ const now = Date.now();
758
+ if (websocketToken && websocketTokenExpiresAt > now + 60000) {
759
+ // Token is still valid (at least 1 minute remaining)
760
+ return websocketToken;
761
+ }
762
+
763
+ // Fetch new token
764
+ const tokenUrl = `${webUrl}/api/session/${sessionId}/websocket-token`;
765
+ const response = await httpRequest(tokenUrl, {
766
+ method: 'GET',
767
+ headers: {
768
+ 'Authorization': `Bearer ${token}`,
769
+ 'Content-Type': 'application/json',
770
+ },
771
+ });
772
+
773
+ if (!response.ok) {
774
+ const errorData = await response.json().catch(() => ({ message: response.status === 401 ? 'Authentication required' : 'Unknown error' }));
775
+
776
+ if (response.status === 401) {
777
+ console.error(chalk.red('\n ✗ Authentication failed'));
778
+ console.error(chalk.yellow(' 💡 Run: npx vibex-sh login to authenticate'));
779
+ console.error('');
780
+ process.exit(1);
781
+ } else if (response.status === 403) {
782
+ console.error(chalk.red(`\n ✗ Access denied: ${errorData.message || 'You do not have permission to access this session'}`));
783
+ process.exit(1);
784
+ } else if (response.status === 404) {
785
+ console.error(chalk.red(`\n ✗ Session not found: ${errorData.message || 'The session does not exist'}`));
786
+ process.exit(1);
787
+ } else if (response.status === 503) {
788
+ // Database not ready yet - retry after delay
789
+ console.warn(chalk.yellow(' ⚠️ Session database not ready yet, retrying in 2 seconds...'));
790
+ await new Promise(resolve => setTimeout(resolve, 2000));
791
+ return await fetchWebSocketToken(); // Retry
792
+ } else {
793
+ console.error(chalk.red(`\n ✗ Failed to get WebSocket token: ${errorData.message || 'Unknown error'}`));
794
+ process.exit(1);
795
+ }
796
+ }
797
+
798
+ const data = await response.json();
799
+ websocketToken = data.token;
800
+ websocketTokenExpiresAt = data.expiresAt;
801
+
802
+ return websocketToken;
803
+ } catch (err) {
804
+ console.error(chalk.red(`\n ✗ Error fetching WebSocket token: ${err.message}`));
805
+ console.error(chalk.yellow(' 💡 Run: npx vibex-sh login to authenticate'));
806
+ console.error('');
807
+ process.exit(1);
808
+ }
809
+ };
810
+
811
+ const connectWebSocket = async () => {
732
812
  // Prevent multiple simultaneous connections
733
813
  if (connectionLock) {
734
814
  console.log(' ⚠️ Connection already in progress, skipping...');
@@ -746,24 +826,32 @@ async function handleSendLogs(options) {
746
826
  reconnectTimeout = null;
747
827
  }
748
828
 
749
- connectionLock = true;
750
- connectionState = 'connecting';
751
- connectionStartTime = Date.now();
752
- if (!hasStdin || !progressBar) {
753
- console.log(chalk.blue(' 🔄 Connecting to WebSocket...'));
829
+ connectionLock = true;
830
+ connectionState = 'connecting';
831
+ connectionStartTime = Date.now();
832
+ if (!hasStdin || !progressBar) {
833
+ console.log(chalk.blue(' 🔄 Connecting to WebSocket...'));
834
+ }
835
+
836
+ try {
837
+ // Fetch WebSocket token first
838
+ const wsToken = await fetchWebSocketToken();
839
+ if (!wsToken) {
840
+ connectionLock = false;
841
+ connectionState = 'disconnected';
842
+ return;
754
843
  }
755
-
756
- try {
757
- // Close existing socket if any (cleanup)
758
- if (socket && socket.readyState !== WebSocket.CLOSED) {
759
- try {
760
- socket.close();
761
- } catch (e) {
762
- // Ignore errors when closing
763
- }
844
+
845
+ // Close existing socket if any (cleanup)
846
+ if (socket && socket.readyState !== WebSocket.CLOSED) {
847
+ try {
848
+ socket.close();
849
+ } catch (e) {
850
+ // Ignore errors when closing
764
851
  }
765
-
766
- socket = new WebSocket(`${socketUrl}?sessionId=${sessionId}`);
852
+ }
853
+
854
+ socket = new WebSocket(`${socketUrl}?sessionId=${sessionId}&token=${wsToken}`);
767
855
 
768
856
  socket.onopen = () => {
769
857
  const connectTime = Date.now() - connectionStartTime;
@@ -948,7 +1036,9 @@ async function handleSendLogs(options) {
948
1036
  console.log(` ↻ Reconnecting in ${delay}ms (attempt ${reconnectAttempts})...\n`);
949
1037
 
950
1038
  reconnectTimeout = setTimeout(() => {
951
- connectWebSocket();
1039
+ connectWebSocket().catch(err => {
1040
+ console.error(` ✗ Error reconnecting: ${err.message}`);
1041
+ });
952
1042
  }, delay);
953
1043
  } else if (event.code === 1000) {
954
1044
  console.log(' ✓ Normal closure, no reconnect needed\n');
@@ -962,7 +1052,9 @@ async function handleSendLogs(options) {
962
1052
  console.error(` ↻ URL: ${socketUrl}`);
963
1053
  // Retry connection
964
1054
  reconnectTimeout = setTimeout(() => {
965
- connectWebSocket();
1055
+ connectWebSocket().catch(err => {
1056
+ console.error(` ✗ Error retrying connection: ${err.message}`);
1057
+ });
966
1058
  }, 1000);
967
1059
  }
968
1060
  };
@@ -981,6 +1073,7 @@ async function handleSendLogs(options) {
981
1073
  const headers = {
982
1074
  'Content-Type': 'application/json',
983
1075
  'Authorization': `Bearer ${token}`,
1076
+ 'X-Token-Source': tokenSource, // Track token origin for debugging
984
1077
  };
985
1078
 
986
1079
  const response = await fetch(ingestUrl, {
@@ -1123,7 +1216,10 @@ async function handleSendLogs(options) {
1123
1216
  // Only start WebSocket connection if we have stdin (piped input)
1124
1217
  // Don't connect WebSocket when run without parameters
1125
1218
  if (hasStdin) {
1126
- connectWebSocket();
1219
+ connectWebSocket().catch(err => {
1220
+ console.error(chalk.red(`\n ✗ Failed to connect WebSocket: ${err.message}`));
1221
+ // Don't exit - HTTP POST will still work
1222
+ });
1127
1223
  }
1128
1224
 
1129
1225
  // Rate limiting queue for piped input
@@ -1289,7 +1385,7 @@ async function handleSendLogs(options) {
1289
1385
  : `${webUrl}/${sessionId}`;
1290
1386
 
1291
1387
  console.log(chalk.cyan(' ╔═══════════════════════════════════════╗'));
1292
- console.log(chalk.cyan(' ║ ✓ Upload Complete ║'));
1388
+ console.log(chalk.cyan(' ║ ✓ Upload Complete ║'));
1293
1389
  console.log(chalk.cyan(' ╚═══════════════════════════════════════╝'));
1294
1390
  console.log('');
1295
1391
  console.log(chalk.white(` 📊 Stats:`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibex-sh",
3
- "version": "0.12.3",
3
+ "version": "0.13.0",
4
4
  "description": "Zero-config observability CLI - pipe logs and visualize instantly",
5
5
  "type": "module",
6
6
  "bin": {