gib-runs 2.2.0 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -7
- package/README.md +31 -15
- package/gib-run.js +5 -3
- package/index.js +194 -128
- package/lib/process-runner.js +8 -31
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,43 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [2.3.0] - 2026-02-10
|
|
6
|
+
|
|
7
|
+
### Fixed - Critical
|
|
8
|
+
- 🔧 **NPM Script Mode** - Fixed major issues with `--npm-script` and `--exec` options
|
|
9
|
+
- No longer creates duplicate HTTP server on port 8080
|
|
10
|
+
- Browser no longer opens to wrong port (8080 instead of dev server port)
|
|
11
|
+
- Static file serving disabled when npm script is running (prevents MIME type conflicts)
|
|
12
|
+
- Fixed "Cannot GET /" and blank page issues with Vite/React/Vue projects
|
|
13
|
+
- GIB-RUNS now acts as pure file watcher for live reload when npm script is active
|
|
14
|
+
- 🚫 **Browser Auto-Open** - Disabled automatic browser opening when using `--npm-script` or `--exec`
|
|
15
|
+
- Prevents confusion with wrong port
|
|
16
|
+
- Let the dev server (Vite, Next.js, etc) handle browser opening
|
|
17
|
+
- 🎯 **File Watcher** - Improved file watching with better ignore patterns
|
|
18
|
+
- Ignores Vite temporary files (`.timestamp-*.mjs`)
|
|
19
|
+
- Ignores common build artifacts (`.log`, `.lock`, `.tmp`)
|
|
20
|
+
- Cleaner console output without spam
|
|
21
|
+
|
|
22
|
+
### Improved
|
|
23
|
+
- 📝 **Cleaner Logs** - Removed verbose output from process runner
|
|
24
|
+
- No more `[npm]` or `[cmd]` prefixes cluttering output
|
|
25
|
+
- Direct passthrough of npm script output
|
|
26
|
+
- Only show errors when process exits with non-zero code
|
|
27
|
+
- 🎨 **Better UI** - Simplified status display
|
|
28
|
+
- Removed norak "(Access from other devices)" text
|
|
29
|
+
- Network URLs displayed directly without extra labels
|
|
30
|
+
- Cleaner, more professional output
|
|
31
|
+
- ⚡ **Performance** - Optimized server startup
|
|
32
|
+
- Minimal HTTP server for WebSocket when npm script is running
|
|
33
|
+
- Reduced memory footprint in npm script mode
|
|
34
|
+
- Faster startup time
|
|
35
|
+
|
|
36
|
+
### Technical
|
|
37
|
+
- Fixed JSHint warning about closure in loop (rate-limit middleware)
|
|
38
|
+
- All 32 tests still passing
|
|
39
|
+
- Zero ESLint/JSHint warnings
|
|
40
|
+
- Backward compatible with all existing features
|
|
41
|
+
|
|
5
42
|
## [2.2.0] - 2026-02-08
|
|
6
43
|
|
|
7
44
|
### Added
|
|
@@ -71,12 +108,6 @@ All notable changes to this project will be documented in this file.
|
|
|
71
108
|
- 🚦 Rate limiting middleware (`--rate-limit=N`) - protect against abuse
|
|
72
109
|
- 📱 QR code option (`--qr`, `--qrcode`) for mobile access
|
|
73
110
|
- 🌍 **Public Tunnels** - Share dev server with anyone, anywhere
|
|
74
|
-
- LocalTunnel (default, no signup needed)
|
|
75
|
-
- Cloudflare Tunnel support
|
|
76
|
-
- Ngrok support
|
|
77
|
-
- Pinggy support
|
|
78
|
-
- Localtonet support
|
|
79
|
-
- Tunnelto support
|
|
80
111
|
- 🚀 **NPM Scripts Integration** - Run npm dev, start, or any script alongside server
|
|
81
112
|
- ⚙️ **Custom Command Execution** - Execute any command with live reload
|
|
82
113
|
- 🔄 **PM2 Integration** - Production-ready process management
|
|
@@ -113,7 +144,7 @@ All notable changes to this project will be documented in this file.
|
|
|
113
144
|
- More informative console output with helpful tips
|
|
114
145
|
- Enhanced developer experience
|
|
115
146
|
- Improved documentation
|
|
116
|
-
- Network URLs prominently displayed
|
|
147
|
+
- Network URLs prominently displayed
|
|
117
148
|
|
|
118
149
|
### Technical
|
|
119
150
|
- Upgraded chokidar to v3.5.3
|
package/README.md
CHANGED
|
@@ -372,14 +372,14 @@ gib-runs
|
|
|
372
372
|
Network URLs are **ALWAYS shown automatically** when you start the server:
|
|
373
373
|
|
|
374
374
|
```
|
|
375
|
-
🚀 GIB-RUNS v2.2
|
|
375
|
+
🚀 GIB-RUNS v2.3.2
|
|
376
376
|
"Unlike Gibran, this actually works through merit"
|
|
377
377
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
378
378
|
📁 Root: /home/user/project
|
|
379
|
-
🌐 Local:
|
|
380
|
-
🔗 Network:
|
|
381
|
-
|
|
382
|
-
|
|
379
|
+
🌐 Local: 🔗 Network: http://127.0.0.1:8080
|
|
380
|
+
🔗 Network:
|
|
381
|
+
🔗 Network: http://192.168.1.100:8080
|
|
382
|
+
🔗 Network: http://10.0.0.5:8080
|
|
383
383
|
🔄 Live Reload: Enabled (no dynasty needed)
|
|
384
384
|
📦 Compression: Enabled (earned, not inherited)
|
|
385
385
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
@@ -491,13 +491,13 @@ gib-runs --tunnel-service=tunnelto
|
|
|
491
491
|
### Example Output
|
|
492
492
|
|
|
493
493
|
```
|
|
494
|
-
🚀 GIB-RUNS v2.2
|
|
494
|
+
🚀 GIB-RUNS v2.3.2
|
|
495
495
|
"Unlike Gibran, this actually works through merit"
|
|
496
496
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
497
497
|
📁 Root: /home/user/project
|
|
498
|
-
🌐 Local:
|
|
499
|
-
🔗 Network:
|
|
500
|
-
|
|
498
|
+
🌐 Local: 🔗 Network: http://127.0.0.1:8080
|
|
499
|
+
🔗 Network:
|
|
500
|
+
🔗 Network: http://192.168.1.100:8080
|
|
501
501
|
🔄 Live Reload: Enabled (no dynasty needed)
|
|
502
502
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
503
503
|
|
|
@@ -541,12 +541,21 @@ The password is automatically fetched and displayed when you start the tunnel. I
|
|
|
541
541
|
|
|
542
542
|
## 🚀 NPM Scripts & Process Management
|
|
543
543
|
|
|
544
|
-
**Run your development scripts
|
|
544
|
+
**Run your development scripts with live reload - GIB-RUNS acts as a smart file watcher!**
|
|
545
|
+
|
|
546
|
+
### How It Works
|
|
547
|
+
|
|
548
|
+
When you use `--npm-script` or `--exec`, GIB-RUNS:
|
|
549
|
+
- ✅ **Does NOT create HTTP server** on port 8080
|
|
550
|
+
- ✅ **Does NOT open browser** automatically
|
|
551
|
+
- ✅ **Only watches files** for live reload
|
|
552
|
+
- ✅ **Lets your dev server** (Vite, Next.js, etc) handle everything
|
|
553
|
+
- ✅ **No port conflicts** - clean and simple
|
|
545
554
|
|
|
546
555
|
### Run NPM Scripts
|
|
547
556
|
|
|
548
557
|
```bash
|
|
549
|
-
# Run npm dev script
|
|
558
|
+
# Run npm dev script (Vite, Next.js, etc)
|
|
550
559
|
gib-runs --npm-script=dev
|
|
551
560
|
|
|
552
561
|
# Run npm start script
|
|
@@ -556,6 +565,13 @@ gib-runs --npm-script=start
|
|
|
556
565
|
gib-runs --npm-script=build
|
|
557
566
|
```
|
|
558
567
|
|
|
568
|
+
**What happens:**
|
|
569
|
+
- Your npm script runs normally (e.g., Vite on port 3000)
|
|
570
|
+
- GIB-RUNS watches files for changes
|
|
571
|
+
- Live reload works via WebSocket
|
|
572
|
+
- No duplicate servers, no conflicts
|
|
573
|
+
- Clean output directly from your dev server
|
|
574
|
+
|
|
559
575
|
### Run Custom Commands
|
|
560
576
|
|
|
561
577
|
```bash
|
|
@@ -605,13 +621,13 @@ pm2 list
|
|
|
605
621
|
### Example Output
|
|
606
622
|
|
|
607
623
|
```
|
|
608
|
-
🚀 GIB-RUNS v2.2
|
|
624
|
+
🚀 GIB-RUNS v2.3.2
|
|
609
625
|
"Unlike Gibran, this actually works through merit"
|
|
610
626
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
611
627
|
📁 Root: /home/user/project
|
|
612
|
-
🌐 Local:
|
|
613
|
-
🔗 Network:
|
|
614
|
-
|
|
628
|
+
🌐 Local: 🔗 Network: http://127.0.0.1:8080
|
|
629
|
+
🔗 Network:
|
|
630
|
+
🔗 Network: http://192.168.1.100:8080
|
|
615
631
|
🔄 Live Reload: Enabled (no dynasty needed)
|
|
616
632
|
📦 Compression: Enabled (earned, not inherited)
|
|
617
633
|
📦 NPM Script: dev
|
package/gib-run.js
CHANGED
|
@@ -182,9 +182,11 @@ for (var i = process.argv.length - 1; i >= 2; --i) {
|
|
|
182
182
|
}
|
|
183
183
|
else if (arg.indexOf("--rate-limit=") > -1) {
|
|
184
184
|
var limit = parseInt(arg.substring(13), 10);
|
|
185
|
-
opts.middleware.push(function() {
|
|
186
|
-
return
|
|
187
|
-
|
|
185
|
+
opts.middleware.push((function(maxLimit) {
|
|
186
|
+
return function() {
|
|
187
|
+
return require('./middleware/rate-limit')({ max: maxLimit });
|
|
188
|
+
};
|
|
189
|
+
})(limit));
|
|
188
190
|
process.argv.splice(i, 1);
|
|
189
191
|
}
|
|
190
192
|
else if (arg.indexOf("--https=") > -1) {
|
package/index.js
CHANGED
|
@@ -146,9 +146,16 @@ GibRuns.start = function(options) {
|
|
|
146
146
|
var mount = options.mount || [];
|
|
147
147
|
var watchPaths = options.watch || [root];
|
|
148
148
|
GibRuns.logLevel = options.logLevel === undefined ? 2 : options.logLevel;
|
|
149
|
+
|
|
150
|
+
// Disable browser opening if npm script or exec command is used
|
|
149
151
|
var openPath = (options.open === undefined || options.open === true) ?
|
|
150
152
|
"" : ((options.open === null || options.open === false) ? null : options.open);
|
|
151
153
|
if (options.noBrowser) openPath = null; // Backwards compatibility with 0.7.0
|
|
154
|
+
|
|
155
|
+
// Force disable browser if npm script or exec is running
|
|
156
|
+
if (options.npmScript || options.exec) {
|
|
157
|
+
openPath = null;
|
|
158
|
+
}
|
|
152
159
|
var file = options.file;
|
|
153
160
|
var staticServerHandler = staticServer(root);
|
|
154
161
|
var wait = options.wait === undefined ? 100 : options.wait;
|
|
@@ -186,106 +193,124 @@ GibRuns.start = function(options) {
|
|
|
186
193
|
// Setup a web server
|
|
187
194
|
var app = connect();
|
|
188
195
|
|
|
196
|
+
// If npm script is running, don't serve static files (let the npm script handle it)
|
|
197
|
+
var serveStatic = !npmScript && !execCommand;
|
|
198
|
+
|
|
189
199
|
// Enable compression for better performance
|
|
190
|
-
if (enableCompression) {
|
|
200
|
+
if (enableCompression && serveStatic) {
|
|
191
201
|
app.use(compression());
|
|
192
202
|
}
|
|
193
203
|
|
|
194
204
|
// Request counter middleware
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
chalk.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
205
|
+
if (serveStatic) {
|
|
206
|
+
app.use(function(req, res, next) {
|
|
207
|
+
GibRuns.requestCount++;
|
|
208
|
+
|
|
209
|
+
// Log requests in verbose mode
|
|
210
|
+
if (GibRuns.logLevel >= 3) {
|
|
211
|
+
var timestamp = new Date().toLocaleTimeString();
|
|
212
|
+
console.log(chalk.gray(' [' + timestamp + '] ') +
|
|
213
|
+
chalk.cyan(req.method) + ' ' +
|
|
214
|
+
chalk.white(req.url) + ' ' +
|
|
215
|
+
chalk.gray('from ' + (req.headers['x-forwarded-for'] || req.connection.remoteAddress)));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
next();
|
|
219
|
+
});
|
|
209
220
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
221
|
+
// Add logger. Level 2 logs only errors
|
|
222
|
+
if (GibRuns.logLevel === 2) {
|
|
223
|
+
app.use(logger('dev', {
|
|
224
|
+
skip: function (req, res) { return res.statusCode < 400; }
|
|
225
|
+
}));
|
|
226
|
+
// Level 2 or above logs all requests
|
|
227
|
+
} else if (GibRuns.logLevel > 2) {
|
|
228
|
+
app.use(logger('dev'));
|
|
229
|
+
}
|
|
218
230
|
}
|
|
219
|
-
|
|
231
|
+
|
|
232
|
+
if (options.spa && serveStatic) {
|
|
220
233
|
middleware.push("spa");
|
|
221
234
|
}
|
|
222
|
-
|
|
223
|
-
middleware
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
235
|
+
|
|
236
|
+
// Add middleware only if serving static
|
|
237
|
+
if (serveStatic) {
|
|
238
|
+
middleware.map(function(mw) {
|
|
239
|
+
if (typeof mw === "string") {
|
|
240
|
+
var ext = path.extname(mw).toLocaleLowerCase();
|
|
241
|
+
if (ext !== ".js") {
|
|
242
|
+
mw = require(path.join(__dirname, "middleware", mw + ".js"));
|
|
243
|
+
} else {
|
|
244
|
+
mw = require(mw);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
app.use(mw);
|
|
248
|
+
|
|
249
|
+
// Log middleware loading in verbose mode
|
|
250
|
+
if (GibRuns.logLevel >= 3) {
|
|
251
|
+
console.log(chalk.gray(' ✓ Loaded middleware: ') + chalk.cyan(typeof mw === 'function' ? mw.name || 'anonymous' : mw));
|
|
230
252
|
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Use http-auth if configured
|
|
256
|
+
if (htpasswd !== null) {
|
|
257
|
+
var auth = require('http-auth');
|
|
258
|
+
var basic = auth.basic({
|
|
259
|
+
realm: "Please authorize",
|
|
260
|
+
file: htpasswd
|
|
261
|
+
});
|
|
262
|
+
// Create middleware wrapper for http-auth v4
|
|
263
|
+
app.use(function(req, res, next) {
|
|
264
|
+
var authHandler = basic.check(function() {
|
|
265
|
+
next();
|
|
266
|
+
});
|
|
267
|
+
authHandler(req, res);
|
|
268
|
+
});
|
|
231
269
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
270
|
+
if (cors) {
|
|
271
|
+
app.use(require("cors")({
|
|
272
|
+
origin: true, // reflecting request origin
|
|
273
|
+
credentials: true // allowing requests with credentials
|
|
274
|
+
}));
|
|
237
275
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
file: htpasswd
|
|
276
|
+
mount.forEach(function(mountRule) {
|
|
277
|
+
var mountPath = path.resolve(process.cwd(), mountRule[1]);
|
|
278
|
+
if (!options.watch) // Auto add mount paths to wathing but only if exclusive path option is not given
|
|
279
|
+
watchPaths.push(mountPath);
|
|
280
|
+
app.use(mountRule[0], staticServer(mountPath));
|
|
281
|
+
if (GibRuns.logLevel >= 1)
|
|
282
|
+
console.log(chalk.cyan(' 📂 Mapping ') + chalk.yellow(mountRule[0]) + chalk.gray(' to ') + chalk.white('"' + mountPath + '"'));
|
|
246
283
|
});
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
var
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
284
|
+
proxy.forEach(function(proxyRule) {
|
|
285
|
+
var proxyUrl = new URL(proxyRule[1]);
|
|
286
|
+
var proxyOpts = {
|
|
287
|
+
protocol: proxyUrl.protocol,
|
|
288
|
+
host: proxyUrl.hostname,
|
|
289
|
+
port: proxyUrl.port,
|
|
290
|
+
pathname: proxyUrl.pathname,
|
|
291
|
+
via: true,
|
|
292
|
+
preserveHost: true
|
|
293
|
+
};
|
|
294
|
+
app.use(proxyRule[0], require('proxy-middleware')(proxyOpts));
|
|
295
|
+
if (GibRuns.logLevel >= 1)
|
|
296
|
+
console.log(chalk.cyan(' 🔀 Proxying ') + chalk.yellow(proxyRule[0]) + chalk.gray(' to ') + chalk.white('"' + proxyRule[1] + '"'));
|
|
253
297
|
});
|
|
298
|
+
app.use(staticServerHandler) // Custom static server
|
|
299
|
+
.use(entryPoint(staticServerHandler, file))
|
|
300
|
+
.use(serveIndex(root, { icons: true }));
|
|
254
301
|
}
|
|
255
|
-
if (cors) {
|
|
256
|
-
app.use(require("cors")({
|
|
257
|
-
origin: true, // reflecting request origin
|
|
258
|
-
credentials: true // allowing requests with credentials
|
|
259
|
-
}));
|
|
260
|
-
}
|
|
261
|
-
mount.forEach(function(mountRule) {
|
|
262
|
-
var mountPath = path.resolve(process.cwd(), mountRule[1]);
|
|
263
|
-
if (!options.watch) // Auto add mount paths to wathing but only if exclusive path option is not given
|
|
264
|
-
watchPaths.push(mountPath);
|
|
265
|
-
app.use(mountRule[0], staticServer(mountPath));
|
|
266
|
-
if (GibRuns.logLevel >= 1)
|
|
267
|
-
console.log(chalk.cyan(' 📂 Mapping ') + chalk.yellow(mountRule[0]) + chalk.gray(' to ') + chalk.white('"' + mountPath + '"'));
|
|
268
|
-
});
|
|
269
|
-
proxy.forEach(function(proxyRule) {
|
|
270
|
-
var proxyUrl = new URL(proxyRule[1]);
|
|
271
|
-
var proxyOpts = {
|
|
272
|
-
protocol: proxyUrl.protocol,
|
|
273
|
-
host: proxyUrl.hostname,
|
|
274
|
-
port: proxyUrl.port,
|
|
275
|
-
pathname: proxyUrl.pathname,
|
|
276
|
-
via: true,
|
|
277
|
-
preserveHost: true
|
|
278
|
-
};
|
|
279
|
-
app.use(proxyRule[0], require('proxy-middleware')(proxyOpts));
|
|
280
|
-
if (GibRuns.logLevel >= 1)
|
|
281
|
-
console.log(chalk.cyan(' 🔀 Proxying ') + chalk.yellow(proxyRule[0]) + chalk.gray(' to ') + chalk.white('"' + proxyRule[1] + '"'));
|
|
282
|
-
});
|
|
283
|
-
app.use(staticServerHandler) // Custom static server
|
|
284
|
-
.use(entryPoint(staticServerHandler, file))
|
|
285
|
-
.use(serveIndex(root, { icons: true }));
|
|
286
302
|
|
|
287
303
|
var server, protocol;
|
|
288
|
-
|
|
304
|
+
|
|
305
|
+
// If npm script or exec command is running, skip HTTP server entirely
|
|
306
|
+
if (npmScript || execCommand) {
|
|
307
|
+
// Create a minimal server just for WebSocket
|
|
308
|
+
server = http.createServer(function(req, res) {
|
|
309
|
+
res.writeHead(200);
|
|
310
|
+
res.end('GIB-RUNS Live Reload Server');
|
|
311
|
+
});
|
|
312
|
+
protocol = "http";
|
|
313
|
+
} else if (https !== null) {
|
|
289
314
|
var httpsConfig = https;
|
|
290
315
|
if (typeof https === "string") {
|
|
291
316
|
httpsConfig = require(path.resolve(process.cwd(), https));
|
|
@@ -322,6 +347,67 @@ GibRuns.start = function(options) {
|
|
|
322
347
|
GibRuns.server = server;
|
|
323
348
|
|
|
324
349
|
var address = server.address();
|
|
350
|
+
|
|
351
|
+
// If npm script is running, don't show server info
|
|
352
|
+
if (npmScript || execCommand) {
|
|
353
|
+
// Show info about what's running
|
|
354
|
+
if (GibRuns.logLevel >= 1) {
|
|
355
|
+
console.log('\n' + chalk.cyan.bold('━'.repeat(60)));
|
|
356
|
+
console.log(chalk.cyan.bold(' 🚀 GIB-RUNS') + chalk.gray(' v2.3.2'));
|
|
357
|
+
console.log(chalk.gray(' "Unlike Gibran, this actually works through merit"'));
|
|
358
|
+
console.log(chalk.cyan.bold('━'.repeat(60)));
|
|
359
|
+
console.log(chalk.white(' 📁 Root: ') + chalk.yellow(root));
|
|
360
|
+
if (npmScript) {
|
|
361
|
+
console.log(chalk.white(' 📦 NPM Script: ') + chalk.yellow(npmScript));
|
|
362
|
+
}
|
|
363
|
+
if (execCommand) {
|
|
364
|
+
console.log(chalk.white(' ⚙️ Command: ') + chalk.yellow(execCommand));
|
|
365
|
+
}
|
|
366
|
+
if (usePM2) {
|
|
367
|
+
console.log(chalk.white(' 🔄 PM2: ') + chalk.green(' Enabled') + chalk.gray(' (process manager)'));
|
|
368
|
+
}
|
|
369
|
+
console.log(chalk.white(' 🔄 Live Reload:') + chalk.green(' Enabled') + chalk.gray(' (watching for changes)'));
|
|
370
|
+
console.log(chalk.cyan.bold('━'.repeat(60)));
|
|
371
|
+
console.log(chalk.gray(' Press Ctrl+C to stop\n'));
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Run the npm script or command
|
|
375
|
+
setTimeout(function() {
|
|
376
|
+
var processRunner = require('./lib/process-runner');
|
|
377
|
+
GibRuns.processRunner = processRunner;
|
|
378
|
+
|
|
379
|
+
if (usePM2 && npmScript) {
|
|
380
|
+
processRunner.runWithPM2('npm run ' + npmScript, {
|
|
381
|
+
cwd: root,
|
|
382
|
+
name: pm2Name
|
|
383
|
+
});
|
|
384
|
+
} else if (usePM2 && execCommand) {
|
|
385
|
+
processRunner.runWithPM2(execCommand, {
|
|
386
|
+
cwd: root,
|
|
387
|
+
name: pm2Name
|
|
388
|
+
});
|
|
389
|
+
} else if (npmScript) {
|
|
390
|
+
processRunner.runNpmScript(npmScript, { cwd: root });
|
|
391
|
+
} else if (execCommand) {
|
|
392
|
+
processRunner.runCommand(execCommand, { cwd: root });
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Start tunnel if requested (for npm/exec mode)
|
|
396
|
+
if (enableTunnel) {
|
|
397
|
+
var tunnel = require('./lib/tunnel');
|
|
398
|
+
GibRuns.tunnel = tunnel;
|
|
399
|
+
setTimeout(function() {
|
|
400
|
+
// For npm/exec mode, we need to detect the port from the process output
|
|
401
|
+
// For now, use a default port or let user specify via --port
|
|
402
|
+
var tunnelPort = port || 8080;
|
|
403
|
+
tunnel.startTunnel(tunnelPort, tunnelService, tunnelOptions);
|
|
404
|
+
}, 2000);
|
|
405
|
+
}
|
|
406
|
+
}, 500);
|
|
407
|
+
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
325
411
|
var serveHost = address.address === "0.0.0.0" ? "127.0.0.1" : address.address;
|
|
326
412
|
var openHost = host === "0.0.0.0" ? "127.0.0.1" : host;
|
|
327
413
|
|
|
@@ -356,18 +442,17 @@ GibRuns.start = function(options) {
|
|
|
356
442
|
|
|
357
443
|
// Output with beautiful formatting
|
|
358
444
|
if (GibRuns.logLevel >= 1) {
|
|
359
|
-
console.log('
|
|
360
|
-
console.log(chalk.cyan.bold(' 🚀 GIB-RUNS') + chalk.gray(' v2.2
|
|
445
|
+
console.log('\n' + chalk.cyan.bold('━'.repeat(60)));
|
|
446
|
+
console.log(chalk.cyan.bold(' 🚀 GIB-RUNS') + chalk.gray(' v2.3.2'));
|
|
361
447
|
console.log(chalk.gray(' "Unlike Gibran, this actually works through merit"'));
|
|
362
448
|
console.log(chalk.cyan.bold('━'.repeat(60)));
|
|
363
449
|
console.log(chalk.white(' 📁 Root: ') + chalk.yellow(root));
|
|
364
450
|
console.log(chalk.white(' 🌐 Local: ') + chalk.green(serveURL));
|
|
365
451
|
|
|
366
|
-
//
|
|
452
|
+
// Show network URLs when available
|
|
367
453
|
if (networkURLs.length > 0) {
|
|
368
|
-
console.log(chalk.white(' 🔗 Network: ') + chalk.magenta('(Access from other devices)'));
|
|
369
454
|
networkURLs.forEach(function(urlItem) {
|
|
370
|
-
console.log(chalk.white('
|
|
455
|
+
console.log(chalk.white(' 🔗 Network: ') + chalk.green(urlItem));
|
|
371
456
|
});
|
|
372
457
|
}
|
|
373
458
|
|
|
@@ -381,24 +466,15 @@ GibRuns.start = function(options) {
|
|
|
381
466
|
if (https) {
|
|
382
467
|
console.log(chalk.white(' 🔒 HTTPS: ') + chalk.green(' Enabled') + chalk.gray(' (real security)'));
|
|
383
468
|
}
|
|
384
|
-
if (execCommand) {
|
|
385
|
-
console.log(chalk.white(' ⚙️ Command: ') + chalk.yellow(execCommand));
|
|
386
|
-
}
|
|
387
|
-
if (npmScript) {
|
|
388
|
-
console.log(chalk.white(' 📦 NPM Script: ') + chalk.yellow(npmScript));
|
|
389
|
-
}
|
|
390
|
-
if (usePM2) {
|
|
391
|
-
console.log(chalk.white(' 🔄 PM2: ') + chalk.green(' Enabled') + chalk.gray(' (process manager)'));
|
|
392
|
-
}
|
|
393
469
|
console.log(chalk.cyan.bold('━'.repeat(60)));
|
|
394
470
|
console.log(chalk.gray(' Press Ctrl+C to stop'));
|
|
395
|
-
console.log(chalk.yellow(' 💡 Tip: Share network URLs with your team
|
|
471
|
+
console.log(chalk.yellow(' 💡 Tip: Share network URLs with your team!\n'));
|
|
396
472
|
}
|
|
397
473
|
|
|
398
474
|
// Show QR code for easy mobile access
|
|
399
475
|
if (showQR && networkURLs.length > 0) {
|
|
400
476
|
console.log(chalk.cyan(' 📱 Scan QR code to open on mobile:'));
|
|
401
|
-
console.log(chalk.gray(' (Install qrcode-terminal: npm i -g qrcode-terminal)
|
|
477
|
+
console.log(chalk.gray(' (Install qrcode-terminal: npm i -g qrcode-terminal)\n'));
|
|
402
478
|
}
|
|
403
479
|
|
|
404
480
|
// Start tunnel if requested
|
|
@@ -409,30 +485,6 @@ GibRuns.start = function(options) {
|
|
|
409
485
|
tunnel.startTunnel(address.port, tunnelService, tunnelOptions);
|
|
410
486
|
}, 1000);
|
|
411
487
|
}
|
|
412
|
-
|
|
413
|
-
// Run npm script or command if specified
|
|
414
|
-
if (npmScript || execCommand || usePM2) {
|
|
415
|
-
var processRunner = require('./lib/process-runner');
|
|
416
|
-
GibRuns.processRunner = processRunner;
|
|
417
|
-
|
|
418
|
-
setTimeout(function() {
|
|
419
|
-
if (usePM2 && npmScript) {
|
|
420
|
-
processRunner.runWithPM2('npm run ' + npmScript, {
|
|
421
|
-
cwd: root,
|
|
422
|
-
name: pm2Name
|
|
423
|
-
});
|
|
424
|
-
} else if (usePM2 && execCommand) {
|
|
425
|
-
processRunner.runWithPM2(execCommand, {
|
|
426
|
-
cwd: root,
|
|
427
|
-
name: pm2Name
|
|
428
|
-
});
|
|
429
|
-
} else if (npmScript) {
|
|
430
|
-
processRunner.runNpmScript(npmScript, { cwd: root });
|
|
431
|
-
} else if (execCommand) {
|
|
432
|
-
processRunner.runCommand(execCommand, { cwd: root });
|
|
433
|
-
}
|
|
434
|
-
}, 500);
|
|
435
|
-
}
|
|
436
488
|
|
|
437
489
|
// Launch browser
|
|
438
490
|
if (openPath !== null)
|
|
@@ -450,8 +502,16 @@ GibRuns.start = function(options) {
|
|
|
450
502
|
}
|
|
451
503
|
});
|
|
452
504
|
|
|
453
|
-
// Setup server to listen at port
|
|
454
|
-
|
|
505
|
+
// Setup server to listen at port (skip if npm script is running)
|
|
506
|
+
if (serveStatic) {
|
|
507
|
+
server.listen(port, host);
|
|
508
|
+
} else {
|
|
509
|
+
// For npm script mode, just setup websocket server without HTTP
|
|
510
|
+
GibRuns.server = server;
|
|
511
|
+
|
|
512
|
+
// Start listening on a random port for WebSocket only
|
|
513
|
+
server.listen(0, '127.0.0.1');
|
|
514
|
+
}
|
|
455
515
|
|
|
456
516
|
// WebSocket
|
|
457
517
|
var clients = [];
|
|
@@ -485,6 +545,12 @@ GibRuns.start = function(options) {
|
|
|
485
545
|
var ignored = [
|
|
486
546
|
function(testPath) { // Always ignore dotfiles (important e.g. because editor hidden temp files)
|
|
487
547
|
return testPath !== "." && /(^[.#]|(?:__|~)$)/.test(path.basename(testPath));
|
|
548
|
+
},
|
|
549
|
+
function(testPath) { // Ignore vite temp files
|
|
550
|
+
return /\.timestamp-.*\.mjs$/.test(testPath);
|
|
551
|
+
},
|
|
552
|
+
function(testPath) { // Ignore common build artifacts
|
|
553
|
+
return /\.(log|lock|tmp)$/.test(testPath);
|
|
488
554
|
}
|
|
489
555
|
];
|
|
490
556
|
if (options.ignore) {
|
|
@@ -524,7 +590,7 @@ GibRuns.start = function(options) {
|
|
|
524
590
|
.on("unlinkDir", handleChange)
|
|
525
591
|
.on("ready", function () {
|
|
526
592
|
if (GibRuns.logLevel >= 1)
|
|
527
|
-
console.log(chalk.cyan(" ✓ Watching for file changes
|
|
593
|
+
console.log(chalk.cyan(" ✓ Watching for file changes...\n"));
|
|
528
594
|
})
|
|
529
595
|
.on("error", function (err) {
|
|
530
596
|
console.log(chalk.red(" ✖ Watcher Error: ") + err);
|
|
@@ -536,13 +602,13 @@ GibRuns.start = function(options) {
|
|
|
536
602
|
GibRuns.shutdown = function() {
|
|
537
603
|
if (GibRuns.logLevel >= 1 && GibRuns.startTime) {
|
|
538
604
|
var uptime = ((Date.now() - GibRuns.startTime) / 1000).toFixed(2);
|
|
539
|
-
console.log('
|
|
605
|
+
console.log('\n' + chalk.cyan.bold('━'.repeat(60)));
|
|
540
606
|
console.log(chalk.yellow(' 👋 Shutting down GIB-RUNS...'));
|
|
541
607
|
console.log(chalk.gray(' 📊 Statistics:'));
|
|
542
608
|
console.log(chalk.gray(' • Uptime: ') + chalk.white(uptime + 's'));
|
|
543
609
|
console.log(chalk.gray(' • Requests: ') + chalk.white(GibRuns.requestCount));
|
|
544
610
|
console.log(chalk.gray(' • Reloads: ') + chalk.white(GibRuns.reloadCount));
|
|
545
|
-
console.log(chalk.cyan.bold('━'.repeat(60)) + '
|
|
611
|
+
console.log(chalk.cyan.bold('━'.repeat(60)) + '\n');
|
|
546
612
|
}
|
|
547
613
|
|
|
548
614
|
// Stop process runner if active
|
package/lib/process-runner.js
CHANGED
|
@@ -16,10 +16,6 @@ function runNpmScript(script, options) {
|
|
|
16
16
|
options = options || {};
|
|
17
17
|
var cwd = options.cwd || process.cwd();
|
|
18
18
|
|
|
19
|
-
console.log(chalk.cyan.bold(' 🚀 Running npm script: ') + chalk.yellow(script));
|
|
20
|
-
console.log(chalk.gray(' Working directory: ') + chalk.white(cwd));
|
|
21
|
-
console.log(chalk.gray(' (Earned through merit, not inheritance)\n'));
|
|
22
|
-
|
|
23
19
|
// Check if package.json exists
|
|
24
20
|
var packagePath = path.join(cwd, 'package.json');
|
|
25
21
|
if (!fs.existsSync(packagePath)) {
|
|
@@ -40,7 +36,6 @@ function runNpmScript(script, options) {
|
|
|
40
36
|
}
|
|
41
37
|
return null;
|
|
42
38
|
}
|
|
43
|
-
console.log(chalk.green(' ✓ Found script: ') + chalk.gray(pkg.scripts[script]));
|
|
44
39
|
} catch (e) {
|
|
45
40
|
console.error(chalk.red(' ✖ Error reading package.json: ') + e.message);
|
|
46
41
|
return null;
|
|
@@ -56,31 +51,24 @@ function runNpmScript(script, options) {
|
|
|
56
51
|
|
|
57
52
|
activeProcess = proc;
|
|
58
53
|
|
|
59
|
-
console.log(chalk.green(' ✓ Process started (PID: ' + proc.pid + ')'));
|
|
60
|
-
console.log(chalk.cyan('━'.repeat(60)) + '\n');
|
|
61
|
-
|
|
62
54
|
// Handle stdout
|
|
63
55
|
proc.stdout.on('data', function(data) {
|
|
64
56
|
var output = data.toString();
|
|
65
57
|
logOutput('stdout', output);
|
|
66
|
-
process.stdout.write(
|
|
58
|
+
process.stdout.write(output);
|
|
67
59
|
});
|
|
68
60
|
|
|
69
61
|
// Handle stderr
|
|
70
62
|
proc.stderr.on('data', function(data) {
|
|
71
63
|
var output = data.toString();
|
|
72
64
|
logOutput('stderr', output);
|
|
73
|
-
process.stderr.write(
|
|
65
|
+
process.stderr.write(output);
|
|
74
66
|
});
|
|
75
67
|
|
|
76
68
|
// Handle exit
|
|
77
69
|
proc.on('exit', function(code, signal) {
|
|
78
|
-
if (code !== null) {
|
|
79
|
-
|
|
80
|
-
console.log(chalk.green('\n ✓ Process exited successfully (code: ' + code + ')'));
|
|
81
|
-
} else {
|
|
82
|
-
console.log(chalk.red('\n ✖ Process exited with code: ' + code));
|
|
83
|
-
}
|
|
70
|
+
if (code !== null && code !== 0) {
|
|
71
|
+
console.log(chalk.red('\n ✖ Process exited with code: ' + code));
|
|
84
72
|
}
|
|
85
73
|
if (signal) {
|
|
86
74
|
console.log(chalk.yellow(' ⚠ Process killed with signal: ' + signal));
|
|
@@ -171,10 +159,6 @@ function runCommand(command, options) {
|
|
|
171
159
|
options = options || {};
|
|
172
160
|
var cwd = options.cwd || process.cwd();
|
|
173
161
|
|
|
174
|
-
console.log(chalk.cyan.bold(' 🚀 Running command: ') + chalk.yellow(command));
|
|
175
|
-
console.log(chalk.gray(' Working directory: ') + chalk.white(cwd));
|
|
176
|
-
console.log(chalk.gray(' (Real execution, not just a title)\n'));
|
|
177
|
-
|
|
178
162
|
var proc = spawn(command, {
|
|
179
163
|
cwd: cwd,
|
|
180
164
|
stdio: ['inherit', 'pipe', 'pipe'],
|
|
@@ -183,31 +167,24 @@ function runCommand(command, options) {
|
|
|
183
167
|
|
|
184
168
|
activeProcess = proc;
|
|
185
169
|
|
|
186
|
-
console.log(chalk.green(' ✓ Process started (PID: ' + proc.pid + ')'));
|
|
187
|
-
console.log(chalk.cyan('━'.repeat(60)) + '\n');
|
|
188
|
-
|
|
189
170
|
// Handle stdout
|
|
190
171
|
proc.stdout.on('data', function(data) {
|
|
191
172
|
var output = data.toString();
|
|
192
173
|
logOutput('stdout', output);
|
|
193
|
-
process.stdout.write(
|
|
174
|
+
process.stdout.write(output);
|
|
194
175
|
});
|
|
195
176
|
|
|
196
177
|
// Handle stderr
|
|
197
178
|
proc.stderr.on('data', function(data) {
|
|
198
179
|
var output = data.toString();
|
|
199
180
|
logOutput('stderr', output);
|
|
200
|
-
process.stderr.write(
|
|
181
|
+
process.stderr.write(output);
|
|
201
182
|
});
|
|
202
183
|
|
|
203
184
|
// Handle exit
|
|
204
185
|
proc.on('exit', function(code, signal) {
|
|
205
|
-
if (code !== null) {
|
|
206
|
-
|
|
207
|
-
console.log(chalk.green('\n ✓ Command completed successfully (code: ' + code + ')'));
|
|
208
|
-
} else {
|
|
209
|
-
console.log(chalk.red('\n ✖ Command exited with code: ' + code));
|
|
210
|
-
}
|
|
186
|
+
if (code !== null && code !== 0) {
|
|
187
|
+
console.log(chalk.red('\n ✖ Command exited with code: ' + code));
|
|
211
188
|
}
|
|
212
189
|
if (signal) {
|
|
213
190
|
console.log(chalk.yellow(' ⚠ Process killed with signal: ' + signal));
|
package/package.json
CHANGED