vibex-sh 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 vibex.sh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # vibex CLI
2
+
3
+ Zero-config observability CLI - pipe logs and visualize instantly.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # Production (default)
9
+ echo '{"cpu": 45, "memory": 78}' | vibex
10
+
11
+ # Local development
12
+ echo '{"test": 123}' | vibex --local
13
+
14
+ # Custom ports
15
+ echo '{"data": 123}' | vibex --web http://localhost:3000 --socket http://localhost:8080
16
+ ```
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install -g vibex-sh
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ Pipe any output to `vibex`:
27
+
28
+ ```bash
29
+ # JSON logs
30
+ echo '{"cpu": 45, "memory": 78}' | vibex
31
+
32
+ # Script output
33
+ python script.py | vibex
34
+ node server.js | vibex
35
+ docker logs -f | vibex
36
+
37
+ # Reuse session
38
+ echo '{"more": "data"}' | vibex --session-id vibex-abc123
39
+ ```
40
+
41
+ ## Options
42
+
43
+ | Flag | Description | Example |
44
+ |------|-------------|---------|
45
+ | `-s, --session-id <id>` | Reuse existing session | `vibex --session-id vibex-abc123` |
46
+ | `-l, --local` | Use localhost (web: 3000, socket: 3001) | `vibex --local` |
47
+ | `--web <url>` | Web server URL | `vibex --web http://localhost:3000` |
48
+ | `--socket <url>` | Socket server URL | `vibex --socket http://localhost:8080` |
49
+ | `--server <url>` | Shorthand for `--web` (auto-derives socket) | `vibex --server http://localhost:3000` |
50
+
51
+ ## Server Configuration
52
+
53
+ The CLI automatically derives the socket URL from the web URL, but you can override it:
54
+
55
+ ```bash
56
+ # Auto-derive socket (localhost:3000 → localhost:3001)
57
+ vibex --web http://localhost:3000
58
+
59
+ # Explicit socket URL
60
+ vibex --web http://localhost:3000 --socket http://localhost:8080
61
+
62
+ # Production (auto-derives socket.vibex.sh)
63
+ vibex --server https://vibex.sh
64
+
65
+ # Custom domain
66
+ vibex --web https://staging.vibex.sh --socket https://socket-staging.vibex.sh
67
+ ```
68
+
69
+ ## Priority Order
70
+
71
+ 1. **Flags** (`--web`, `--socket`, `--local`, `--server`)
72
+ 2. **Environment variables** (`VIBEX_WEB_URL`, `VIBEX_SOCKET_URL`)
73
+ 3. **Production defaults** (`https://vibex.sh`, `https://socket.vibex.sh`)
74
+
75
+ ## Environment Variables
76
+
77
+ ```bash
78
+ export VIBEX_WEB_URL=http://localhost:3000
79
+ export VIBEX_SOCKET_URL=http://localhost:8080
80
+ ```
81
+
82
+ ## Examples
83
+
84
+ ```bash
85
+ # Production (default)
86
+ echo '{"data": 123}' | vibex
87
+
88
+ # Quick localhost
89
+ echo '{"data": 123}' | vibex --local
90
+
91
+ # Custom web server, auto socket
92
+ echo '{"data": 123}' | vibex --server http://localhost:3000
93
+
94
+ # Both custom
95
+ echo '{"data": 123}' | vibex --web http://localhost:3000 --socket http://localhost:8080
96
+
97
+ # Staging
98
+ echo '{"data": 123}' | vibex --server https://staging.vibex.sh
99
+ ```
100
+
101
+ ## License
102
+
103
+ MIT
package/bin/vibex.js ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { fileURLToPath } from 'url';
4
+ import { dirname, join } from 'path';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
8
+ const cliPath = join(__dirname, '..', 'index.js');
9
+
10
+ // Directly import and run the main function
11
+ import(cliPath).then((module) => {
12
+ // The main function is already called in index.js
13
+ }).catch((error) => {
14
+ console.error('Error loading CLI:', error);
15
+ process.exit(1);
16
+ });
17
+
18
+
19
+
package/index.js ADDED
@@ -0,0 +1,501 @@
1
+ import readline from 'readline';
2
+ import { io } from 'socket.io-client';
3
+ import { program, Command } from 'commander';
4
+ import { readFile, writeFile, mkdir } from 'fs/promises';
5
+ import { existsSync, readFileSync } from 'fs';
6
+ import { join } from 'path';
7
+ import { homedir } from 'os';
8
+ import { spawn } from 'child_process';
9
+ import http from 'http';
10
+ import https from 'https';
11
+
12
+ function generateSessionId() {
13
+ const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
14
+ let result = 'vibex-';
15
+ for (let i = 0; i < 6; i++) {
16
+ result += chars[Math.floor(Math.random() * chars.length)];
17
+ }
18
+ return result;
19
+ }
20
+
21
+ function normalizeSessionId(sessionId) {
22
+ if (!sessionId) return null;
23
+ // If it doesn't start with 'vibex-', add it
24
+ if (!sessionId.startsWith('vibex-')) {
25
+ return `vibex-${sessionId}`;
26
+ }
27
+ return sessionId;
28
+ }
29
+
30
+ function deriveSocketUrl(webUrl) {
31
+ const url = new URL(webUrl);
32
+
33
+ // For localhost, socket is typically on port 3001
34
+ if (url.hostname === 'localhost' || url.hostname === '127.0.0.1') {
35
+ const port = url.port === '3000' || !url.port ? '3001' : String(parseInt(url.port) + 1);
36
+ return `${url.protocol}//${url.hostname}:${port}`;
37
+ }
38
+ // For vibex.sh domains, use socket subdomain
39
+ else if (url.hostname.includes('vibex.sh')) {
40
+ return webUrl.replace(url.hostname, `socket.${url.hostname}`);
41
+ }
42
+ // For other domains, try to use socket subdomain
43
+ else {
44
+ return webUrl.replace(url.hostname, `socket.${url.hostname}`);
45
+ }
46
+ }
47
+
48
+ function getUrls(options) {
49
+ const { local, web, socket, server } = options;
50
+
51
+ // Priority 1: Explicit --web and --socket flags (highest priority)
52
+ if (web) {
53
+ return {
54
+ webUrl: web,
55
+ socketUrl: socket || deriveSocketUrl(web),
56
+ };
57
+ }
58
+
59
+ // Priority 2: --server flag (shorthand for --web)
60
+ if (server) {
61
+ return {
62
+ webUrl: server,
63
+ socketUrl: socket || deriveSocketUrl(server),
64
+ };
65
+ }
66
+
67
+ // Priority 3: --local flag
68
+ if (local) {
69
+ return {
70
+ webUrl: process.env.VIBEX_WEB_URL || 'http://localhost:3000',
71
+ socketUrl: process.env.VIBEX_SOCKET_URL || socket || 'http://localhost:3001',
72
+ };
73
+ }
74
+
75
+ // Priority 4: Environment variables
76
+ if (process.env.VIBEX_WEB_URL) {
77
+ return {
78
+ webUrl: process.env.VIBEX_WEB_URL,
79
+ socketUrl: process.env.VIBEX_SOCKET_URL || socket || deriveSocketUrl(process.env.VIBEX_WEB_URL),
80
+ };
81
+ }
82
+
83
+ // Priority 5: Production defaults
84
+ return {
85
+ webUrl: 'https://vibex.sh',
86
+ socketUrl: socket || 'https://socket.vibex.sh',
87
+ };
88
+ }
89
+
90
+ function getConfigPath() {
91
+ // Check for custom config path from environment variable
92
+ if (process.env.VIBEX_CONFIG_PATH) {
93
+ return process.env.VIBEX_CONFIG_PATH;
94
+ }
95
+ // Default: ~/.vibex/config.json
96
+ const configDir = join(homedir(), '.vibex');
97
+ return join(configDir, 'config.json');
98
+ }
99
+
100
+ async function getStoredToken() {
101
+ try {
102
+ const configPath = getConfigPath();
103
+ if (existsSync(configPath)) {
104
+ const config = JSON.parse(await readFile(configPath, 'utf-8'));
105
+ return config.token || null;
106
+ }
107
+ } catch (error) {
108
+ // Ignore errors (file doesn't exist or invalid JSON)
109
+ }
110
+ return null;
111
+ }
112
+
113
+ function getStoredConfig() {
114
+ try {
115
+ const configPath = getConfigPath();
116
+ if (existsSync(configPath)) {
117
+ return JSON.parse(readFileSync(configPath, 'utf-8'));
118
+ }
119
+ } catch (error) {
120
+ // Ignore errors
121
+ }
122
+ return null;
123
+ }
124
+
125
+ async function storeToken(token, webUrl = null) {
126
+ try {
127
+ const configPath = getConfigPath();
128
+ const configDir = join(homedir(), '.vibex');
129
+ if (!existsSync(configDir)) {
130
+ await mkdir(configDir, { recursive: true });
131
+ }
132
+
133
+ const config = {
134
+ token,
135
+ ...(webUrl && { webUrl }), // Store webUrl if provided
136
+ updatedAt: new Date().toISOString(),
137
+ };
138
+
139
+ await writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
140
+ return true;
141
+ } catch (error) {
142
+ console.error('Failed to store token:', error.message);
143
+ return false;
144
+ }
145
+ }
146
+
147
+ async function handleLogin(webUrl) {
148
+ const configPath = getConfigPath();
149
+ const existingConfig = getStoredConfig();
150
+
151
+ console.log('\n 🔐 Vibex CLI Authentication\n');
152
+ console.log(` 📁 Config location: ${configPath}`);
153
+
154
+ if (existingConfig?.token) {
155
+ console.log(` ⚠️ You already have a token stored. This will replace it.\n`);
156
+ }
157
+
158
+ const tempToken = `temp_${Date.now()}_${Math.random().toString(36).substring(7)}`;
159
+ const authUrl = `${webUrl}/api/cli-auth?token=${tempToken}`;
160
+
161
+ console.log(' Opening browser for authentication...\n');
162
+ console.log(` If browser doesn't open, visit: ${authUrl}\n`);
163
+
164
+ // Open browser
165
+ const platform = process.platform;
166
+ let command;
167
+ if (platform === 'darwin') {
168
+ command = 'open';
169
+ } else if (platform === 'win32') {
170
+ command = 'start';
171
+ } else {
172
+ command = 'xdg-open';
173
+ }
174
+
175
+ spawn(command, [authUrl], { detached: true, stdio: 'ignore' });
176
+
177
+ // Poll for token
178
+ console.log(' Waiting for authentication...');
179
+ const maxAttempts = 60; // 60 seconds
180
+ let attempts = 0;
181
+
182
+ while (attempts < maxAttempts) {
183
+ await new Promise(resolve => setTimeout(resolve, 1000));
184
+ attempts++;
185
+
186
+ try {
187
+ const response = await httpRequest(`${webUrl}/api/cli-auth?token=${tempToken}`, {
188
+ method: 'GET',
189
+ });
190
+ if (response.ok) {
191
+ const data = await response.json();
192
+ if (data.success && data.token) {
193
+ await storeToken(data.token, webUrl);
194
+ const configPath = getConfigPath();
195
+ console.log('\n ✅ Authentication successful!');
196
+ console.log(` 📁 Token saved to: ${configPath}`);
197
+ console.log(` 💡 This token will be used automatically for future commands.\n`);
198
+ return data.token;
199
+ }
200
+ }
201
+ } catch (error) {
202
+ // Continue polling
203
+ }
204
+ }
205
+
206
+ console.log('\n ⏱️ Authentication timeout. Please try again.\n');
207
+ process.exit(1);
208
+ }
209
+
210
+ function httpRequest(url, options) {
211
+ return new Promise((resolve, reject) => {
212
+ const urlObj = new URL(url);
213
+ const isHttps = urlObj.protocol === 'https:';
214
+ const httpModule = isHttps ? https : http;
215
+
216
+ const req = httpModule.request(url, options, (res) => {
217
+ let data = '';
218
+ res.on('data', (chunk) => { data += chunk; });
219
+ res.on('end', () => {
220
+ try {
221
+ const parsed = JSON.parse(data);
222
+ resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, status: res.statusCode, json: () => Promise.resolve(parsed) });
223
+ } catch (e) {
224
+ resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, status: res.statusCode, json: () => Promise.resolve({}) });
225
+ }
226
+ });
227
+ });
228
+
229
+ req.on('error', reject);
230
+ if (options.body) {
231
+ req.write(options.body);
232
+ }
233
+ req.end();
234
+ });
235
+ }
236
+
237
+ async function claimSession(sessionId, token, webUrl) {
238
+ if (!token) return false;
239
+
240
+ try {
241
+ // Normalize session ID before claiming
242
+ const normalizedSessionId = normalizeSessionId(sessionId);
243
+ const response = await httpRequest(`${webUrl}/api/auth/claim-session-with-token`, {
244
+ method: 'POST',
245
+ headers: { 'Content-Type': 'application/json' },
246
+ body: JSON.stringify({
247
+ sessionId: normalizedSessionId,
248
+ token,
249
+ }),
250
+ });
251
+
252
+ return response.ok;
253
+ } catch (error) {
254
+ return false;
255
+ }
256
+ }
257
+
258
+ function printBanner(sessionId, webUrl) {
259
+ const dashboardUrl = `${webUrl}/${sessionId}`;
260
+
261
+ console.log('\n');
262
+ console.log(' ╔═══════════════════════════════════════╗');
263
+ console.log(' ║ 🔍 Vibex is watching... ║');
264
+ console.log(' ╚═══════════════════════════════════════╝');
265
+ console.log('\n');
266
+ console.log(` Session ID: ${sessionId}`);
267
+ console.log(` Dashboard: ${dashboardUrl}`);
268
+ console.log('\n');
269
+ }
270
+
271
+ async function main() {
272
+ // Handle login command separately - check BEFORE commander parses
273
+ // Check process.argv directly - look for 'login' as a standalone argument
274
+ // This must happen FIRST, before any commander parsing
275
+ const allArgs = process.argv;
276
+ const args = process.argv.slice(2);
277
+
278
+ // Check if 'login' appears anywhere in process.argv (works with npx too)
279
+ const hasLogin = allArgs.includes('login') || args.includes('login');
280
+
281
+ if (hasLogin) {
282
+ // Find login position to get args after it
283
+ const loginIndex = args.indexOf('login');
284
+ const loginArgs = loginIndex !== -1 ? args.slice(loginIndex + 1) : [];
285
+
286
+ // Create a separate command instance for login
287
+ const loginCmd = new Command();
288
+ loginCmd
289
+ .option('-l, --local', 'Use localhost')
290
+ .option('--web <url>', 'Web server URL')
291
+ .option('--server <url>', 'Shorthand for --web');
292
+
293
+ // Parse only the options (args after 'login')
294
+ if (loginArgs.length > 0) {
295
+ loginCmd.parse(['node', 'vibex', ...loginArgs], { from: 'user' });
296
+ } else {
297
+ loginCmd.parse(['node', 'vibex'], { from: 'user' });
298
+ }
299
+
300
+ const options = loginCmd.opts();
301
+ const { webUrl } = getUrls(options);
302
+ await handleLogin(webUrl);
303
+ process.exit(0);
304
+ }
305
+
306
+ program
307
+ .option('-s, --session-id <id>', 'Reuse existing session ID')
308
+ .option('-l, --local', 'Use localhost (web: 3000, socket: 3001)')
309
+ .option('--web <url>', 'Web server URL (e.g., http://localhost:3000)')
310
+ .option('--socket <url>', 'Socket server URL (e.g., http://localhost:3001)')
311
+ .option('--server <url>', 'Shorthand for --web (auto-derives socket URL)')
312
+ .option('--token <token>', 'Authentication token (or use VIBEX_TOKEN env var)')
313
+ .parse();
314
+
315
+ const options = program.opts();
316
+
317
+ // Normalize session ID - add 'vibex-' prefix if missing
318
+ const rawSessionId = options.sessionId || generateSessionId();
319
+ const sessionId = normalizeSessionId(rawSessionId);
320
+ const { webUrl, socketUrl } = getUrls(options);
321
+
322
+ // Get token from flag, env var, or stored config
323
+ let token = options.token || process.env.VIBEX_TOKEN || await getStoredToken();
324
+
325
+ // Auto-claim session if token is available
326
+ if (token && !options.sessionId) {
327
+ // Only auto-claim new sessions (not when reusing existing session)
328
+ const claimed = await claimSession(sessionId, token, webUrl);
329
+ if (claimed) {
330
+ console.log(' ✓ Session automatically claimed to your account\n');
331
+ }
332
+ }
333
+
334
+ // Print banner only once, and show how to reuse session
335
+ if (!options.sessionId) {
336
+ printBanner(sessionId, webUrl);
337
+ const localFlag = webUrl.includes('localhost') ? ' --local' : '';
338
+ const sessionSlug = sessionId.replace(/^vibex-/, ''); // Remove prefix for example
339
+ console.log(' 💡 Tip: Use -s to send more logs to this session');
340
+ console.log(` Example: echo '{"cpu": 45, "memory": 78, "timestamp": "${new Date().toISOString()}"}' | npx vibex-sh -s ${sessionSlug}${localFlag}\n`);
341
+ } else {
342
+ // When reusing a session, show minimal info
343
+ console.log(` 🔍 Sending logs to session: ${sessionId}`);
344
+ console.log(` Dashboard: ${webUrl}/${sessionId}\n`);
345
+ }
346
+
347
+ const socket = io(socketUrl, {
348
+ transports: ['websocket', 'polling'],
349
+ autoConnect: true,
350
+ // Reconnection settings for Cloud Run
351
+ reconnection: true,
352
+ reconnectionDelay: 1000,
353
+ reconnectionDelayMax: 5000,
354
+ reconnectionAttempts: Infinity, // Keep trying forever
355
+ timeout: 20000,
356
+ });
357
+
358
+ let isConnected = false;
359
+ let hasJoinedSession = false;
360
+ const logQueue = [];
361
+
362
+ socket.on('connect', () => {
363
+ isConnected = true;
364
+ console.log(' ✓ Connected to server\n');
365
+ // Rejoin session on reconnect
366
+ socket.emit('join-session', sessionId);
367
+ // Wait a tiny bit for join-session to be processed
368
+ setTimeout(() => {
369
+ hasJoinedSession = true;
370
+ // Process any queued logs
371
+ while (logQueue.length > 0) {
372
+ const logData = logQueue.shift();
373
+ socket.emit('cli-emit', {
374
+ sessionId,
375
+ ...logData,
376
+ });
377
+ }
378
+ }, 100);
379
+ });
380
+
381
+ socket.on('reconnect', (attemptNumber) => {
382
+ console.log(` ↻ Reconnected (attempt ${attemptNumber})\n`);
383
+ isConnected = true;
384
+ // Rejoin session after reconnection
385
+ socket.emit('join-session', sessionId);
386
+ setTimeout(() => {
387
+ hasJoinedSession = true;
388
+ // Process any queued logs
389
+ while (logQueue.length > 0) {
390
+ const logData = logQueue.shift();
391
+ socket.emit('cli-emit', {
392
+ sessionId,
393
+ ...logData,
394
+ });
395
+ }
396
+ }, 100);
397
+ });
398
+
399
+ socket.on('reconnect_attempt', (attemptNumber) => {
400
+ // Silent reconnection attempts - don't spam console
401
+ });
402
+
403
+ socket.on('reconnect_error', (error) => {
404
+ // Silent reconnection errors - will keep trying
405
+ });
406
+
407
+ socket.on('reconnect_failed', () => {
408
+ console.error(' ✗ Failed to reconnect after all attempts');
409
+ console.error(' Stream will continue, but logs may be lost until reconnection\n');
410
+ });
411
+
412
+ socket.on('connect_error', (error) => {
413
+ // Don't exit on first connection error - allow reconnection
414
+ if (!isConnected) {
415
+ console.error(' ✗ Connection error:', error.message);
416
+ console.error(' ↻ Retrying connection...\n');
417
+ }
418
+ });
419
+
420
+ socket.on('disconnect', (reason) => {
421
+ isConnected = false;
422
+ hasJoinedSession = false;
423
+ // Don't exit - allow reconnection
424
+ if (reason === 'io server disconnect') {
425
+ // Server disconnected, will reconnect automatically
426
+ }
427
+ });
428
+
429
+ const rl = readline.createInterface({
430
+ input: process.stdin,
431
+ output: process.stdout,
432
+ terminal: false,
433
+ });
434
+
435
+ rl.on('line', (line) => {
436
+ const trimmedLine = line.trim();
437
+ if (!trimmedLine) {
438
+ return;
439
+ }
440
+
441
+ let logData;
442
+ try {
443
+ const parsed = JSON.parse(trimmedLine);
444
+ logData = {
445
+ type: 'json',
446
+ payload: parsed,
447
+ timestamp: Date.now(),
448
+ };
449
+ } catch (e) {
450
+ logData = {
451
+ type: 'text',
452
+ payload: trimmedLine,
453
+ timestamp: Date.now(),
454
+ };
455
+ }
456
+
457
+ // If connected and joined session, send immediately; otherwise queue it
458
+ if (isConnected && hasJoinedSession && socket.connected) {
459
+ socket.emit('cli-emit', {
460
+ sessionId,
461
+ ...logData,
462
+ });
463
+ } else {
464
+ logQueue.push(logData);
465
+ }
466
+ });
467
+
468
+ rl.on('close', () => {
469
+ // Wait for connection and queued logs to be sent
470
+ const waitForQueue = () => {
471
+ if (logQueue.length === 0 || (!isConnected && logQueue.length > 0)) {
472
+ // If not connected and we have queued logs, wait a bit more
473
+ if (!isConnected && logQueue.length > 0) {
474
+ setTimeout(waitForQueue, 200);
475
+ return;
476
+ }
477
+ console.log('\n Stream ended. Closing connection...\n');
478
+ if (socket.connected) {
479
+ socket.disconnect();
480
+ }
481
+ setTimeout(() => process.exit(0), 100);
482
+ } else {
483
+ setTimeout(waitForQueue, 100);
484
+ }
485
+ };
486
+
487
+ waitForQueue();
488
+ });
489
+
490
+ process.on('SIGINT', () => {
491
+ console.log('\n Interrupted. Closing connection...\n');
492
+ socket.disconnect();
493
+ process.exit(0);
494
+ });
495
+ }
496
+
497
+ main().catch((error) => {
498
+ console.error('Fatal error:', error);
499
+ process.exit(1);
500
+ });
501
+
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "vibex-sh",
3
+ "version": "0.1.0",
4
+ "description": "Zero-config observability CLI - pipe logs and visualize instantly",
5
+ "type": "module",
6
+ "bin": {
7
+ "vibex": "./bin/vibex.js"
8
+ },
9
+ "main": "index.js",
10
+ "keywords": [
11
+ "observability",
12
+ "logging",
13
+ "monitoring",
14
+ "cli",
15
+ "visualization",
16
+ "vibex"
17
+ ],
18
+ "author": "Umut Gokbayrak <umut@vibex.sh>",
19
+ "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/vibex-sh/vibex-cli.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/vibex-sh/vibex-cli/issues"
26
+ },
27
+ "homepage": "https://vibex.sh",
28
+ "dependencies": {
29
+ "commander": "^11.1.0",
30
+ "socket.io-client": "^4.7.2"
31
+ }
32
+ }
package/publish.sh ADDED
@@ -0,0 +1,106 @@
1
+ #!/bin/bash
2
+
3
+ # Publish script for vibex CLI
4
+ # Usage: ./publish.sh [patch|minor|major|version]
5
+ # Example: ./publish.sh patch
6
+ # Example: ./publish.sh 1.2.3
7
+
8
+ set -e # Exit on error
9
+
10
+ # Colors for output
11
+ RED='\033[0;31m'
12
+ GREEN='\033[0;32m'
13
+ YELLOW='\033[1;33m'
14
+ NC='\033[0m' # No Color
15
+
16
+ # Get current version
17
+ CURRENT_VERSION=$(node -p "require('./package.json').version")
18
+ VERSION_CHANGED=false
19
+ PUBLISH_SUCCESS=false
20
+
21
+ # Rollback function
22
+ rollback_version() {
23
+ if [ "$VERSION_CHANGED" = true ] && [ "$PUBLISH_SUCCESS" = false ]; then
24
+ echo -e "${YELLOW}Rolling back version to ${CURRENT_VERSION}...${NC}"
25
+ npm version $CURRENT_VERSION --no-git-tag-version || true
26
+ VERSION_CHANGED=false # Mark as rolled back to prevent double rollback
27
+ echo -e "${GREEN}Version rolled back to ${CURRENT_VERSION}${NC}"
28
+ fi
29
+ }
30
+
31
+ # Trap to rollback on error
32
+ trap rollback_version ERR
33
+ trap rollback_version EXIT
34
+
35
+ echo -e "${GREEN}Current version: ${CURRENT_VERSION}${NC}"
36
+
37
+ # Determine new version
38
+ if [ -z "$1" ]; then
39
+ echo -e "${RED}Error: Version type or version number required${NC}"
40
+ echo "Usage: ./publish.sh [patch|minor|major|version]"
41
+ echo "Example: ./publish.sh patch"
42
+ echo "Example: ./publish.sh 1.2.3"
43
+ exit 1
44
+ fi
45
+
46
+ VERSION_TYPE=$1
47
+
48
+ # Check if it's a semantic version (x.y.z) or a version type (patch/minor/major)
49
+ if [[ $VERSION_TYPE =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
50
+ NEW_VERSION=$VERSION_TYPE
51
+ echo -e "${GREEN}Setting version to: ${NEW_VERSION}${NC}"
52
+ npm version $NEW_VERSION --no-git-tag-version
53
+ VERSION_CHANGED=true
54
+ else
55
+ # Validate version type
56
+ if [[ ! "$VERSION_TYPE" =~ ^(patch|minor|major)$ ]]; then
57
+ echo -e "${RED}Error: Invalid version type. Use: patch, minor, or major${NC}"
58
+ exit 1
59
+ fi
60
+
61
+ echo -e "${GREEN}Bumping ${VERSION_TYPE} version...${NC}"
62
+ npm version $VERSION_TYPE --no-git-tag-version
63
+ VERSION_CHANGED=true
64
+ NEW_VERSION=$(node -p "require('./package.json').version")
65
+ fi
66
+
67
+ echo -e "${GREEN}New version: ${NEW_VERSION}${NC}"
68
+
69
+ # Check if user is logged in to npm
70
+ if ! npm whoami &> /dev/null; then
71
+ echo -e "${RED}Error: Not logged in to npm${NC}"
72
+ echo "Run: npm login"
73
+ rollback_version
74
+ exit 1
75
+ fi
76
+
77
+ # Confirm before publishing
78
+ echo -e "${YELLOW}Ready to publish ${NEW_VERSION} to npm${NC}"
79
+ read -p "Continue? (y/N) " -n 1 -r
80
+ echo
81
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
82
+ echo -e "${YELLOW}Publish cancelled${NC}"
83
+ # Revert version change
84
+ rollback_version
85
+ exit 1
86
+ fi
87
+
88
+ # Publish to npm
89
+ echo -e "${GREEN}Publishing to npm...${NC}"
90
+ if npm publish; then
91
+ PUBLISH_SUCCESS=true
92
+ echo -e "${GREEN}✓ Successfully published vibex@${NEW_VERSION}${NC}"
93
+ else
94
+ echo -e "${RED}✗ Failed to publish to npm${NC}"
95
+ rollback_version
96
+ exit 1
97
+ fi
98
+
99
+ # Optionally commit and tag (uncomment if desired)
100
+ # echo -e "${GREEN}Creating git commit and tag...${NC}"
101
+ # git add package.json
102
+ # git commit -m "chore: bump version to ${NEW_VERSION}"
103
+ # git tag "v${NEW_VERSION}"
104
+ # echo -e "${GREEN}✓ Git commit and tag created${NC}"
105
+ # echo -e "${YELLOW}Don't forget to push: git push && git push --tags${NC}"
106
+