vibex-sh 0.10.1 → 0.11.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.
- package/index.js +265 -143
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -11,6 +11,13 @@ import { fileURLToPath } from 'url';
|
|
|
11
11
|
import WebSocket from 'ws';
|
|
12
12
|
import crypto from 'crypto';
|
|
13
13
|
|
|
14
|
+
// Constants
|
|
15
|
+
const POLL_INTERVAL_MS = 1000;
|
|
16
|
+
const MAX_POLL_ATTEMPTS = 60;
|
|
17
|
+
const WEBSOCKET_CLOSE_TIMEOUT_MS = 2000;
|
|
18
|
+
const DEFAULT_WORKER_URL = 'https://ingest.vibex.sh';
|
|
19
|
+
const DEFAULT_WEB_URL = 'https://vibex.sh';
|
|
20
|
+
|
|
14
21
|
// Get version from package.json
|
|
15
22
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
23
|
const __dirname = dirname(__filename);
|
|
@@ -153,45 +160,16 @@ function normalizeToHybrid(message, level, payload) {
|
|
|
153
160
|
return hybrid;
|
|
154
161
|
}
|
|
155
162
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
function getUrls(options) {
|
|
163
|
-
const { web, socket, server } = options;
|
|
164
|
-
|
|
165
|
-
// Priority 1: Explicit --web and --socket flags (highest priority)
|
|
166
|
-
if (web) {
|
|
167
|
-
return {
|
|
168
|
-
webUrl: web,
|
|
169
|
-
socketUrl: socket || deriveSocketUrl(web),
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// Priority 2: --server flag (shorthand for --web)
|
|
174
|
-
if (server) {
|
|
175
|
-
return {
|
|
176
|
-
webUrl: server,
|
|
177
|
-
socketUrl: socket || deriveSocketUrl(server),
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Priority 3: Environment variables
|
|
182
|
-
if (process.env.VIBEX_WEB_URL) {
|
|
183
|
-
return {
|
|
184
|
-
webUrl: process.env.VIBEX_WEB_URL,
|
|
185
|
-
socketUrl: process.env.VIBEX_SOCKET_URL || socket || deriveSocketUrl(process.env.VIBEX_WEB_URL),
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Priority 4: Production defaults
|
|
163
|
+
/**
|
|
164
|
+
* Get production URLs
|
|
165
|
+
* CLI only supports production server
|
|
166
|
+
*/
|
|
167
|
+
function getProductionUrls() {
|
|
190
168
|
// Always use production worker WebSocket endpoint
|
|
191
|
-
const
|
|
169
|
+
const workerUrl = process.env.VIBEX_WORKER_URL || DEFAULT_WORKER_URL;
|
|
192
170
|
return {
|
|
193
|
-
webUrl:
|
|
194
|
-
socketUrl:
|
|
171
|
+
webUrl: DEFAULT_WEB_URL,
|
|
172
|
+
socketUrl: workerUrl.replace('https://', 'wss://').replace('http://', 'ws://'),
|
|
195
173
|
};
|
|
196
174
|
}
|
|
197
175
|
|
|
@@ -230,7 +208,7 @@ function getStoredConfig() {
|
|
|
230
208
|
return null;
|
|
231
209
|
}
|
|
232
210
|
|
|
233
|
-
async function storeToken(token
|
|
211
|
+
async function storeToken(token) {
|
|
234
212
|
try {
|
|
235
213
|
const configPath = getConfigPath();
|
|
236
214
|
const configDir = join(homedir(), '.vibex');
|
|
@@ -240,7 +218,6 @@ async function storeToken(token, webUrl = null) {
|
|
|
240
218
|
|
|
241
219
|
const config = {
|
|
242
220
|
token,
|
|
243
|
-
...(webUrl && { webUrl }), // Store webUrl if provided
|
|
244
221
|
updatedAt: new Date().toISOString(),
|
|
245
222
|
};
|
|
246
223
|
|
|
@@ -252,9 +229,10 @@ async function storeToken(token, webUrl = null) {
|
|
|
252
229
|
}
|
|
253
230
|
}
|
|
254
231
|
|
|
255
|
-
async function handleLogin(
|
|
232
|
+
async function handleLogin() {
|
|
256
233
|
const configPath = getConfigPath();
|
|
257
234
|
const existingConfig = getStoredConfig();
|
|
235
|
+
const { webUrl } = getProductionUrls();
|
|
258
236
|
|
|
259
237
|
console.log('\n 🔐 vibex.sh CLI Authentication\n');
|
|
260
238
|
console.log(` 📁 Config location: ${configPath}`);
|
|
@@ -263,8 +241,9 @@ async function handleLogin(webUrl) {
|
|
|
263
241
|
console.log(` ⚠️ You already have a token stored. This will replace it.\n`);
|
|
264
242
|
}
|
|
265
243
|
|
|
266
|
-
|
|
267
|
-
const
|
|
244
|
+
// Generate unique state (nonce) for OAuth flow
|
|
245
|
+
const state = crypto.randomBytes(16).toString('hex');
|
|
246
|
+
const authUrl = `${webUrl}/api/cli-auth?state=${state}`;
|
|
268
247
|
|
|
269
248
|
console.log(' Opening browser for authentication...\n');
|
|
270
249
|
console.log(` If browser doesn't open, visit: ${authUrl}\n`);
|
|
@@ -284,21 +263,20 @@ async function handleLogin(webUrl) {
|
|
|
284
263
|
|
|
285
264
|
// Poll for token
|
|
286
265
|
console.log(' Waiting for authentication...');
|
|
287
|
-
const maxAttempts = 60; // 60 seconds
|
|
288
266
|
let attempts = 0;
|
|
289
267
|
|
|
290
|
-
while (attempts <
|
|
291
|
-
await new Promise(resolve => setTimeout(resolve,
|
|
268
|
+
while (attempts < MAX_POLL_ATTEMPTS) {
|
|
269
|
+
await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
292
270
|
attempts++;
|
|
293
271
|
|
|
294
272
|
try {
|
|
295
|
-
const response = await httpRequest(`${webUrl}/api/cli-auth?
|
|
273
|
+
const response = await httpRequest(`${webUrl}/api/cli-auth?state=${state}`, {
|
|
296
274
|
method: 'GET',
|
|
297
275
|
});
|
|
298
276
|
if (response.ok) {
|
|
299
277
|
const data = await response.json();
|
|
300
278
|
if (data.success && data.token) {
|
|
301
|
-
await storeToken(data.token
|
|
279
|
+
await storeToken(data.token);
|
|
302
280
|
const configPath = getConfigPath();
|
|
303
281
|
console.log('\n ✅ Authentication successful!');
|
|
304
282
|
console.log(` 📁 Token saved to: ${configPath}`);
|
|
@@ -342,39 +320,16 @@ function httpRequest(url, options) {
|
|
|
342
320
|
});
|
|
343
321
|
}
|
|
344
322
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
try {
|
|
349
|
-
// Normalize session ID before claiming
|
|
350
|
-
const normalizedSessionId = normalizeSessionId(sessionId);
|
|
351
|
-
const response = await httpRequest(`${webUrl}/api/auth/claim-session-with-token`, {
|
|
352
|
-
method: 'POST',
|
|
353
|
-
headers: { 'Content-Type': 'application/json' },
|
|
354
|
-
body: JSON.stringify({
|
|
355
|
-
sessionId: normalizedSessionId,
|
|
356
|
-
token,
|
|
357
|
-
}),
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
if (response.ok) {
|
|
361
|
-
// Parse response to get auth code
|
|
362
|
-
const responseData = await response.json();
|
|
363
|
-
return responseData.authCode || null;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
return null;
|
|
367
|
-
} catch (error) {
|
|
368
|
-
return null;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
323
|
+
// claimSession function removed - session claiming is no longer supported
|
|
324
|
+
// All sessions must be created with authentication
|
|
371
325
|
|
|
372
326
|
// Removed getSessionAuthCode - auth codes should only come from:
|
|
373
327
|
// 1. claim-session-with-token response (for claimed sessions)
|
|
374
328
|
// 2. socket.io session-auth-code event (for unclaimed sessions)
|
|
375
329
|
// Never fetch auth codes via public API endpoint - security vulnerability
|
|
376
330
|
|
|
377
|
-
function printBanner(sessionId,
|
|
331
|
+
function printBanner(sessionId, authCode = null) {
|
|
332
|
+
const { webUrl } = getProductionUrls();
|
|
378
333
|
const dashboardUrl = authCode
|
|
379
334
|
? `${webUrl}/${sessionId}?auth=${authCode}`
|
|
380
335
|
: `${webUrl}/${sessionId}`;
|
|
@@ -392,88 +347,249 @@ function printBanner(sessionId, webUrl, authCode = null) {
|
|
|
392
347
|
console.log('\n');
|
|
393
348
|
}
|
|
394
349
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
350
|
+
/**
|
|
351
|
+
* Handle init command - create a new session with parser selection
|
|
352
|
+
*/
|
|
353
|
+
async function handleInit(options) {
|
|
354
|
+
const { webUrl } = getProductionUrls();
|
|
399
355
|
|
|
400
|
-
//
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
process.
|
|
404
|
-
}
|
|
356
|
+
// Interactive prompt for parser selection
|
|
357
|
+
const rl = readline.createInterface({
|
|
358
|
+
input: process.stdin,
|
|
359
|
+
output: process.stdout,
|
|
360
|
+
});
|
|
405
361
|
|
|
406
|
-
|
|
407
|
-
// Check process.argv directly - look for 'login' as a standalone argument
|
|
408
|
-
// This must happen FIRST, before any commander parsing
|
|
409
|
-
// Check if 'login' appears anywhere in process.argv (works with npx too)
|
|
410
|
-
const hasLogin = allArgs.includes('login') || args.includes('login');
|
|
362
|
+
const question = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
411
363
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
.
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
364
|
+
try {
|
|
365
|
+
console.log('\n 🔧 vibex.sh Session Initialization\n');
|
|
366
|
+
|
|
367
|
+
// Fetch available parsers from public API
|
|
368
|
+
let availableParsers = [];
|
|
369
|
+
try {
|
|
370
|
+
const parsersResponse = await httpRequest(`${webUrl}/api/parsers`, {
|
|
371
|
+
method: 'GET',
|
|
372
|
+
});
|
|
373
|
+
if (parsersResponse.ok) {
|
|
374
|
+
availableParsers = await parsersResponse.json();
|
|
375
|
+
} else {
|
|
376
|
+
console.warn(' ⚠️ Failed to fetch parsers from API, using fallback list');
|
|
377
|
+
}
|
|
378
|
+
} catch (e) {
|
|
379
|
+
console.warn(' ⚠️ Failed to fetch parsers from API, using fallback list');
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Fallback to hardcoded list if API fails or returns empty
|
|
383
|
+
if (!availableParsers || availableParsers.length === 0) {
|
|
384
|
+
availableParsers = [
|
|
385
|
+
{ id: 'nginx', name: 'Nginx Access Log', category: 'web' },
|
|
386
|
+
{ id: 'apache', name: 'Apache Access Log', category: 'web' },
|
|
387
|
+
{ id: 'docker', name: 'Docker Container Logs', category: 'system' },
|
|
388
|
+
{ id: 'kubernetes', name: 'Kubernetes Pod/Container Logs', category: 'system' },
|
|
389
|
+
];
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Filter out mandatory parsers for selection (if any)
|
|
393
|
+
const selectableParsers = availableParsers.filter(p => !p.isMandatory);
|
|
394
|
+
|
|
395
|
+
console.log(' What kind of logs are these? (Optional - leave empty for auto-detection)');
|
|
396
|
+
if (selectableParsers.length > 0) {
|
|
397
|
+
console.log(' Available log types:');
|
|
398
|
+
selectableParsers.forEach((p, i) => {
|
|
399
|
+
console.log(` ${i + 1}. ${p.name} (${p.id})`);
|
|
400
|
+
});
|
|
401
|
+
console.log(' (Leave empty for auto-detection)\n');
|
|
402
|
+
} else {
|
|
403
|
+
console.log(' Available log types: (Leave empty for auto-detection)\n');
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const answer = await question(' Enter comma-separated numbers or parser IDs (e.g., 1,2 or nginx,apache): ');
|
|
407
|
+
rl.close();
|
|
408
|
+
|
|
409
|
+
let enabledParsers = [];
|
|
410
|
+
if (answer.trim()) {
|
|
411
|
+
const selections = answer.split(',').map(s => s.trim());
|
|
412
|
+
selections.forEach(sel => {
|
|
413
|
+
// Check if it's a number
|
|
414
|
+
const num = parseInt(sel, 10);
|
|
415
|
+
if (!isNaN(num) && num > 0 && num <= selectableParsers.length) {
|
|
416
|
+
enabledParsers.push(selectableParsers[num - 1].id);
|
|
417
|
+
} else if (selectableParsers.find(p => p.id === sel)) {
|
|
418
|
+
enabledParsers.push(sel);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Use parser flag if provided, otherwise use interactive selection
|
|
424
|
+
const parserFlag = options.parser || options.parsers;
|
|
425
|
+
if (parserFlag) {
|
|
426
|
+
if (typeof parserFlag === 'string') {
|
|
427
|
+
enabledParsers = parserFlag.split(',').map(p => p.trim());
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Get token - required for authenticated session creation
|
|
432
|
+
let token = process.env.VIBEX_TOKEN || await getStoredToken();
|
|
433
|
+
if (!token) {
|
|
434
|
+
console.error('\n ✗ Authentication required');
|
|
435
|
+
console.error(' 💡 Run: npx vibex-sh login');
|
|
436
|
+
process.exit(1);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Create session with enabledParsers (authenticated)
|
|
440
|
+
const createUrl = `${webUrl}/api/sessions/create`;
|
|
441
|
+
const response = await httpRequest(createUrl, {
|
|
442
|
+
method: 'POST',
|
|
443
|
+
headers: {
|
|
444
|
+
'Content-Type': 'application/json',
|
|
445
|
+
'Authorization': `Bearer ${token}`,
|
|
446
|
+
},
|
|
447
|
+
body: JSON.stringify({
|
|
448
|
+
enabledParsers: enabledParsers.length > 0 ? enabledParsers : undefined,
|
|
449
|
+
}),
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
if (!response.ok) {
|
|
453
|
+
const errorData = await response.json();
|
|
454
|
+
if (response.status === 401 || response.status === 403) {
|
|
455
|
+
console.error(`\n ✗ Authentication failed: ${errorData.message || 'Invalid token'}`);
|
|
456
|
+
console.error(' 💡 Run: npx vibex-sh login');
|
|
457
|
+
} else {
|
|
458
|
+
console.error(`\n ✗ Failed to create session: ${errorData.message || 'Unknown error'}`);
|
|
459
|
+
}
|
|
460
|
+
process.exit(1);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const data = await response.json();
|
|
464
|
+
const createdSessionId = data.sessionId;
|
|
465
|
+
const createdAuthCode = data.authCode;
|
|
466
|
+
|
|
467
|
+
console.log('\n ✅ Session created successfully!\n');
|
|
468
|
+
printBanner(createdSessionId, createdAuthCode);
|
|
469
|
+
if (enabledParsers.length > 0) {
|
|
470
|
+
console.log(` 📋 Log Types: ${enabledParsers.join(', ')}`);
|
|
426
471
|
} else {
|
|
427
|
-
|
|
472
|
+
console.log(' 📋 Log Types: Auto-detection (default parsers)');
|
|
428
473
|
}
|
|
474
|
+
console.log(`\n 💡 Use this session ID: ${createdSessionId}`);
|
|
475
|
+
console.log(` Example: echo '{"cpu": 45}' | npx vibex-sh -s ${createdSessionId}\n`);
|
|
429
476
|
|
|
430
|
-
const options = loginCmd.opts();
|
|
431
|
-
const { webUrl } = getUrls(options);
|
|
432
|
-
await handleLogin(webUrl);
|
|
433
477
|
process.exit(0);
|
|
478
|
+
} catch (error) {
|
|
479
|
+
rl.close();
|
|
480
|
+
console.error(`\n ✗ Error: ${error.message}`);
|
|
481
|
+
process.exit(1);
|
|
434
482
|
}
|
|
483
|
+
}
|
|
435
484
|
|
|
485
|
+
async function main() {
|
|
486
|
+
// Configure main program
|
|
487
|
+
program
|
|
488
|
+
.name('vibex')
|
|
489
|
+
.description('vibex.sh CLI - Send logs to vibex.sh for real-time analysis')
|
|
490
|
+
.version(cliVersion, '-v, --version', 'Display version number');
|
|
491
|
+
|
|
492
|
+
// Login command
|
|
493
|
+
program
|
|
494
|
+
.command('login')
|
|
495
|
+
.description('Authenticate with vibex.sh and save your token')
|
|
496
|
+
.action(async () => {
|
|
497
|
+
await handleLogin();
|
|
498
|
+
process.exit(0);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// Init command
|
|
502
|
+
program
|
|
503
|
+
.command('init')
|
|
504
|
+
.description('Create a new session with parser selection')
|
|
505
|
+
.option('--parser <parsers>', 'Comma-separated list of parser IDs (e.g., nginx,postgres)')
|
|
506
|
+
.option('--parsers <parsers>', 'Alias for --parser')
|
|
507
|
+
.action(async (options) => {
|
|
508
|
+
await handleInit(options);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
// Main command (default) - send logs
|
|
436
512
|
program
|
|
437
|
-
.version(cliVersion, '-v, --version', 'Display version number')
|
|
438
513
|
.option('-s, --session-id <id>', 'Reuse existing session ID')
|
|
439
|
-
.option('--web <url>', 'Web server URL')
|
|
440
|
-
.option('--socket <url>', 'Socket server URL')
|
|
441
|
-
.option('--server <url>', 'Shorthand for --web (auto-derives socket URL)')
|
|
442
514
|
.option('--token <token>', 'Authentication token (or use VIBEX_TOKEN env var)')
|
|
443
|
-
.
|
|
515
|
+
.option('--parser <parsers>', 'Comma-separated list of parser IDs (e.g., nginx,postgres)')
|
|
516
|
+
.option('--parsers <parsers>', 'Alias for --parser')
|
|
517
|
+
.action(async (options) => {
|
|
518
|
+
await handleSendLogs(options);
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
// Parse arguments
|
|
522
|
+
program.parse();
|
|
523
|
+
}
|
|
444
524
|
|
|
445
|
-
|
|
446
|
-
|
|
525
|
+
/**
|
|
526
|
+
* Handle send logs command (default/main command)
|
|
527
|
+
*/
|
|
528
|
+
async function handleSendLogs(options) {
|
|
529
|
+
const { webUrl, socketUrl } = getProductionUrls();
|
|
447
530
|
|
|
448
|
-
// Get token
|
|
531
|
+
// Get token - REQUIRED for all operations
|
|
449
532
|
let token = options.token || process.env.VIBEX_TOKEN || await getStoredToken();
|
|
533
|
+
if (!token) {
|
|
534
|
+
console.error('\n ✗ Authentication required');
|
|
535
|
+
console.error(' 💡 Run: npx vibex-sh login to authenticate');
|
|
536
|
+
console.error(' 💡 Or set VIBEX_TOKEN environment variable\n');
|
|
537
|
+
process.exit(1);
|
|
538
|
+
}
|
|
450
539
|
|
|
451
540
|
let sessionId;
|
|
452
541
|
let authCode = null;
|
|
453
542
|
|
|
543
|
+
// Check if stdin is available (piped input)
|
|
544
|
+
const isTTY = process.stdin.isTTY;
|
|
545
|
+
const hasStdin = !isTTY;
|
|
546
|
+
|
|
547
|
+
// If no session ID and no stdin, show usage and exit
|
|
548
|
+
if (!options.sessionId && !hasStdin) {
|
|
549
|
+
program.help();
|
|
550
|
+
process.exit(0);
|
|
551
|
+
}
|
|
552
|
+
|
|
454
553
|
// If session ID is provided, use it (existing session)
|
|
455
554
|
if (options.sessionId) {
|
|
456
555
|
sessionId = normalizeSessionId(options.sessionId);
|
|
457
556
|
|
|
458
|
-
// If token is available, try to claim the session
|
|
459
|
-
if (token) {
|
|
460
|
-
authCode = await claimSession(sessionId, token, webUrl);
|
|
461
|
-
}
|
|
462
|
-
|
|
463
557
|
// When reusing a session, show minimal info
|
|
464
558
|
console.log(` 🔍 Sending logs to session: ${sessionId}\n`);
|
|
465
559
|
} else {
|
|
466
|
-
// No session ID provided - create a new
|
|
560
|
+
// No session ID provided - create a new authenticated session
|
|
561
|
+
// Check for --parser or --parsers flag for parser selection
|
|
562
|
+
let enabledParsers = [];
|
|
563
|
+
if (options.parser || options.parsers) {
|
|
564
|
+
const parserList = options.parser || options.parsers;
|
|
565
|
+
if (Array.isArray(parserList)) {
|
|
566
|
+
enabledParsers = parserList;
|
|
567
|
+
} else if (typeof parserList === 'string') {
|
|
568
|
+
enabledParsers = parserList.split(',').map(p => p.trim());
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
467
572
|
try {
|
|
468
|
-
const createUrl = `${webUrl}/api/sessions/create
|
|
573
|
+
const createUrl = `${webUrl}/api/sessions/create`;
|
|
469
574
|
const response = await httpRequest(createUrl, {
|
|
470
575
|
method: 'POST',
|
|
471
|
-
headers: {
|
|
576
|
+
headers: {
|
|
577
|
+
'Content-Type': 'application/json',
|
|
578
|
+
'Authorization': `Bearer ${token}`,
|
|
579
|
+
},
|
|
580
|
+
body: JSON.stringify({
|
|
581
|
+
enabledParsers: enabledParsers.length > 0 ? enabledParsers : undefined,
|
|
582
|
+
}),
|
|
472
583
|
});
|
|
473
584
|
|
|
474
585
|
if (!response.ok) {
|
|
475
586
|
const errorData = await response.json();
|
|
476
|
-
|
|
587
|
+
if (response.status === 401 || response.status === 403) {
|
|
588
|
+
console.error(` ✗ Authentication failed: ${errorData.message || 'Invalid token'}`);
|
|
589
|
+
console.error(' 💡 Run: npx vibex-sh login');
|
|
590
|
+
} else {
|
|
591
|
+
console.error(` ✗ Failed to create session: ${errorData.message || 'Unknown error'}`);
|
|
592
|
+
}
|
|
477
593
|
process.exit(1);
|
|
478
594
|
}
|
|
479
595
|
|
|
@@ -481,17 +597,13 @@ async function main() {
|
|
|
481
597
|
sessionId = data.sessionId; // Server-generated unique session ID
|
|
482
598
|
authCode = data.authCode; // Server-generated auth code
|
|
483
599
|
|
|
484
|
-
// If token is available, claim the session
|
|
485
|
-
if (token) {
|
|
486
|
-
const claimAuthCode = await claimSession(sessionId, token, webUrl);
|
|
487
|
-
if (claimAuthCode) {
|
|
488
|
-
authCode = claimAuthCode;
|
|
489
|
-
console.log(' ✓ Session automatically claimed to your account\n');
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
600
|
// Print banner for new session
|
|
494
|
-
printBanner(sessionId,
|
|
601
|
+
printBanner(sessionId, authCode);
|
|
602
|
+
if (enabledParsers.length > 0) {
|
|
603
|
+
console.log(` 📋 Log Types: ${enabledParsers.join(', ')}`);
|
|
604
|
+
} else {
|
|
605
|
+
console.log(' 📋 Log Types: Auto-detection (default parsers)');
|
|
606
|
+
}
|
|
495
607
|
console.log(' 💡 Tip: Use -s to send more logs to this session');
|
|
496
608
|
console.log(` Example: echo '{"cpu": 45, "memory": 78}' | npx vibex-sh -s ${sessionId}\n`);
|
|
497
609
|
} catch (error) {
|
|
@@ -639,6 +751,7 @@ async function main() {
|
|
|
639
751
|
if (!receivedAuthCode || receivedAuthCode !== message.data.authCode) {
|
|
640
752
|
receivedAuthCode = message.data.authCode;
|
|
641
753
|
if (isNewSession) {
|
|
754
|
+
const { webUrl } = getProductionUrls();
|
|
642
755
|
console.log(` 🔑 Auth Code: ${receivedAuthCode}`);
|
|
643
756
|
console.log(` 📋 Dashboard: ${webUrl}/${sessionId}?auth=${receivedAuthCode}\n`);
|
|
644
757
|
}
|
|
@@ -798,21 +911,19 @@ async function main() {
|
|
|
798
911
|
|
|
799
912
|
// Send logs via HTTP POST (non-blocking, same as SDKs)
|
|
800
913
|
// Always use production Cloudflare Worker endpoint
|
|
801
|
-
// Token is
|
|
914
|
+
// Token is REQUIRED - all sessions must be authenticated
|
|
802
915
|
// HTTP POST works independently of WebSocket - don't wait for WebSocket connection
|
|
803
916
|
const sendLogViaHTTP = async (logData) => {
|
|
804
917
|
try {
|
|
805
918
|
// Always use production worker URL
|
|
806
|
-
const workerUrl = process.env.VIBEX_WORKER_URL ||
|
|
919
|
+
const workerUrl = process.env.VIBEX_WORKER_URL || DEFAULT_WORKER_URL;
|
|
807
920
|
const ingestUrl = `${workerUrl}/api/v1/ingest`;
|
|
808
921
|
|
|
809
|
-
// Build headers -
|
|
922
|
+
// Build headers - Authorization is REQUIRED
|
|
810
923
|
const headers = {
|
|
811
924
|
'Content-Type': 'application/json',
|
|
925
|
+
'Authorization': `Bearer ${token}`,
|
|
812
926
|
};
|
|
813
|
-
if (token) {
|
|
814
|
-
headers['Authorization'] = `Bearer ${token}`;
|
|
815
|
-
}
|
|
816
927
|
|
|
817
928
|
const response = await fetch(ingestUrl, {
|
|
818
929
|
method: 'POST',
|
|
@@ -913,8 +1024,17 @@ async function main() {
|
|
|
913
1024
|
}
|
|
914
1025
|
};
|
|
915
1026
|
|
|
916
|
-
//
|
|
917
|
-
|
|
1027
|
+
// Only start WebSocket connection if we have stdin (piped input)
|
|
1028
|
+
// Don't connect WebSocket when run without parameters
|
|
1029
|
+
if (hasStdin) {
|
|
1030
|
+
connectWebSocket();
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// Only read from stdin if we have piped input
|
|
1034
|
+
if (!hasStdin) {
|
|
1035
|
+
// No stdin - exit after showing session info
|
|
1036
|
+
process.exit(0);
|
|
1037
|
+
}
|
|
918
1038
|
|
|
919
1039
|
const rl = readline.createInterface({
|
|
920
1040
|
input: process.stdin,
|
|
@@ -993,8 +1113,10 @@ async function main() {
|
|
|
993
1113
|
reconnectTimeout = null;
|
|
994
1114
|
}
|
|
995
1115
|
|
|
996
|
-
// Graceful shutdown - wait for close handshake
|
|
997
|
-
|
|
1116
|
+
// Graceful shutdown - wait for close handshake (only if WebSocket was connected)
|
|
1117
|
+
if (hasStdin && socket) {
|
|
1118
|
+
await closeWebSocket();
|
|
1119
|
+
}
|
|
998
1120
|
|
|
999
1121
|
// Give a moment for any final cleanup
|
|
1000
1122
|
setTimeout(() => process.exit(0), 100);
|