numux 1.5.2 → 1.7.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/README.md +16 -13
- package/dist/numux.js +52 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -178,7 +178,7 @@ Each process accepts:
|
|
|
178
178
|
| `delay` | `number` | — | Milliseconds to wait before starting the process |
|
|
179
179
|
| `condition` | `string` | — | Env var name; process skipped if falsy. Prefix with `!` to negate |
|
|
180
180
|
| `stopSignal` | `string` | `SIGTERM` | Signal for graceful stop (`SIGTERM`, `SIGINT`, or `SIGHUP`) |
|
|
181
|
-
| `color` | `string` | auto | Hex
|
|
181
|
+
| `color` | `string \| string[]` | auto | Hex (e.g. `"#ff6600"`) or basic name: black, red, green, yellow, blue, magenta, cyan, white, gray, orange, purple |
|
|
182
182
|
| `watch` | `string \| string[]` | — | Glob patterns — restart process when matching files change |
|
|
183
183
|
| `interactive` | `boolean` | `false` | When `true`, keyboard input is forwarded to the process |
|
|
184
184
|
|
|
@@ -268,18 +268,15 @@ Persistent processes that crash are auto-restarted with exponential backoff (1s
|
|
|
268
268
|
| Key | Action |
|
|
269
269
|
|-----|--------|
|
|
270
270
|
| `Ctrl+C` | Quit (graceful shutdown) |
|
|
271
|
-
| `
|
|
272
|
-
| `
|
|
273
|
-
| `
|
|
274
|
-
| `
|
|
275
|
-
| `
|
|
276
|
-
| `
|
|
277
|
-
| `
|
|
278
|
-
| `PageUp/PageDown` | Scroll output by page
|
|
279
|
-
| `Home/End` | Scroll to top/bottom
|
|
280
|
-
| `Alt+PageUp/PageDown` | Scroll output up/down |
|
|
281
|
-
| `Alt+Home/End` | Scroll to top/bottom |
|
|
282
|
-
| `Alt+F` | Search in active pane output |
|
|
271
|
+
| `R` | Restart active process |
|
|
272
|
+
| `Shift+R` | Restart all processes |
|
|
273
|
+
| `S` | Stop/start active process |
|
|
274
|
+
| `L` | Clear active pane output |
|
|
275
|
+
| `F` | Search in active pane output |
|
|
276
|
+
| `1`–`9` | Jump to tab |
|
|
277
|
+
| `Left/Right` | Cycle tabs |
|
|
278
|
+
| `PageUp/PageDown` | Scroll output by page |
|
|
279
|
+
| `Home/End` | Scroll to top/bottom |
|
|
283
280
|
|
|
284
281
|
While searching: type to filter, `Enter`/`Shift+Enter` to navigate matches, `Escape` to close.
|
|
285
282
|
|
|
@@ -298,6 +295,12 @@ Panes are readonly by default — keyboard input is not forwarded to processes.
|
|
|
298
295
|
| ✖ | Failed |
|
|
299
296
|
| ⊘ | Skipped |
|
|
300
297
|
|
|
298
|
+
## Dependencies
|
|
299
|
+
|
|
300
|
+
### ghostty-opentui
|
|
301
|
+
|
|
302
|
+
Despite the name, [`ghostty-opentui`](https://github.com/user/ghostty-opentui) is **not** a compatibility layer for the [Ghostty](https://ghostty.org) terminal. It uses Ghostty's Zig-based VT parser as the ANSI terminal emulation engine for OpenTUI's terminal renderable. It works in any terminal emulator (iTerm, Kitty, Alacritty, WezTerm, etc.) and adds ~8MB to install size due to native binaries.
|
|
303
|
+
|
|
301
304
|
## License
|
|
302
305
|
|
|
303
306
|
MIT
|
package/dist/numux.js
CHANGED
|
@@ -22,7 +22,7 @@ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports,
|
|
|
22
22
|
var require_package = __commonJS((exports, module) => {
|
|
23
23
|
module.exports = {
|
|
24
24
|
name: "numux",
|
|
25
|
-
version: "1.
|
|
25
|
+
version: "1.7.0",
|
|
26
26
|
description: "Terminal multiplexer with dependency orchestration",
|
|
27
27
|
type: "module",
|
|
28
28
|
license: "MIT",
|
|
@@ -382,15 +382,18 @@ function detectPackageManager(pkgJson, cwd) {
|
|
|
382
382
|
}
|
|
383
383
|
return "npm";
|
|
384
384
|
}
|
|
385
|
+
function isGlobPattern(name) {
|
|
386
|
+
return /[*?[]/.test(name);
|
|
387
|
+
}
|
|
385
388
|
function expandScriptPatterns(config, cwd) {
|
|
386
389
|
const entries = Object.entries(config.processes);
|
|
387
|
-
const hasWildcard = entries.some(([name]) => name.startsWith("npm:"));
|
|
390
|
+
const hasWildcard = entries.some(([name]) => name.startsWith("npm:") || isGlobPattern(name));
|
|
388
391
|
if (!hasWildcard)
|
|
389
392
|
return config;
|
|
390
393
|
const dir = config.cwd ?? cwd ?? process.cwd();
|
|
391
394
|
const pkgPath = resolve(dir, "package.json");
|
|
392
395
|
if (!existsSync(pkgPath)) {
|
|
393
|
-
throw new Error(`
|
|
396
|
+
throw new Error(`Wildcard patterns require a package.json (looked in ${dir})`);
|
|
394
397
|
}
|
|
395
398
|
const pkgJson = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
396
399
|
const scripts = pkgJson.scripts;
|
|
@@ -401,11 +404,11 @@ function expandScriptPatterns(config, cwd) {
|
|
|
401
404
|
const pm = detectPackageManager(pkgJson, dir);
|
|
402
405
|
const expanded = {};
|
|
403
406
|
for (const [name, value] of entries) {
|
|
404
|
-
if (!name.startsWith("npm:")) {
|
|
407
|
+
if (!(name.startsWith("npm:") || isGlobPattern(name))) {
|
|
405
408
|
expanded[name] = value;
|
|
406
409
|
continue;
|
|
407
410
|
}
|
|
408
|
-
const pattern = name.slice(4);
|
|
411
|
+
const pattern = name.startsWith("npm:") ? name.slice(4) : name;
|
|
409
412
|
const template = value ?? {};
|
|
410
413
|
if (template.command) {
|
|
411
414
|
throw new Error(`"${name}": wildcard processes cannot have a "command" field (commands come from package.json scripts)`);
|
|
@@ -589,6 +592,31 @@ function findCycle(remaining, config) {
|
|
|
589
592
|
}
|
|
590
593
|
|
|
591
594
|
// src/utils/color.ts
|
|
595
|
+
var BASIC_COLORS = {
|
|
596
|
+
black: "#000000",
|
|
597
|
+
red: "#ff0000",
|
|
598
|
+
green: "#00ff00",
|
|
599
|
+
yellow: "#ffff00",
|
|
600
|
+
blue: "#0000ff",
|
|
601
|
+
magenta: "#ff00ff",
|
|
602
|
+
cyan: "#00ffff",
|
|
603
|
+
white: "#ffffff",
|
|
604
|
+
gray: "#808080",
|
|
605
|
+
grey: "#808080",
|
|
606
|
+
orange: "#ffa500",
|
|
607
|
+
purple: "#800080"
|
|
608
|
+
};
|
|
609
|
+
function isValidColor(color) {
|
|
610
|
+
if (HEX_COLOR_RE.test(color))
|
|
611
|
+
return true;
|
|
612
|
+
return color.toLowerCase() in BASIC_COLORS;
|
|
613
|
+
}
|
|
614
|
+
function resolveToHex(color) {
|
|
615
|
+
if (HEX_COLOR_RE.test(color))
|
|
616
|
+
return color.startsWith("#") ? color : `#${color}`;
|
|
617
|
+
const hex = BASIC_COLORS[color.toLowerCase()];
|
|
618
|
+
return hex ?? "";
|
|
619
|
+
}
|
|
592
620
|
function hexToAnsi(hex) {
|
|
593
621
|
const h = hex.replace("#", "");
|
|
594
622
|
const r = Number.parseInt(h.slice(0, 2), 16);
|
|
@@ -638,7 +666,11 @@ function buildProcessColorMap(names, config) {
|
|
|
638
666
|
for (const name of names) {
|
|
639
667
|
const explicit = resolveColor(config.processes[name]?.color);
|
|
640
668
|
if (explicit) {
|
|
641
|
-
|
|
669
|
+
const hex = resolveToHex(explicit);
|
|
670
|
+
if (hex)
|
|
671
|
+
map.set(name, hexToAnsi(hex));
|
|
672
|
+
else
|
|
673
|
+
map.set(name, DEFAULT_ANSI_COLORS[paletteIndex++ % DEFAULT_ANSI_COLORS.length]);
|
|
642
674
|
} else {
|
|
643
675
|
map.set(name, DEFAULT_ANSI_COLORS[paletteIndex % DEFAULT_ANSI_COLORS.length]);
|
|
644
676
|
paletteIndex++;
|
|
@@ -654,7 +686,11 @@ function buildProcessHexColorMap(names, config) {
|
|
|
654
686
|
for (const name of names) {
|
|
655
687
|
const explicit = resolveColor(config.processes[name]?.color);
|
|
656
688
|
if (explicit) {
|
|
657
|
-
|
|
689
|
+
const hex = resolveToHex(explicit);
|
|
690
|
+
if (hex)
|
|
691
|
+
map.set(name, hex);
|
|
692
|
+
else
|
|
693
|
+
map.set(name, DEFAULT_HEX_COLORS[paletteIndex++ % DEFAULT_HEX_COLORS.length]);
|
|
658
694
|
} else {
|
|
659
695
|
map.set(name, DEFAULT_HEX_COLORS[paletteIndex % DEFAULT_HEX_COLORS.length]);
|
|
660
696
|
paletteIndex++;
|
|
@@ -718,13 +754,13 @@ function validateConfig(raw, warnings) {
|
|
|
718
754
|
}
|
|
719
755
|
}
|
|
720
756
|
if (typeof p.color === "string") {
|
|
721
|
-
if (!
|
|
722
|
-
throw new Error(`Process "${name}".color must be a
|
|
757
|
+
if (!isValidColor(p.color)) {
|
|
758
|
+
throw new Error(`Process "${name}".color must be a hex color (e.g. "#ff8800") or basic name (black, red, green, yellow, blue, magenta, cyan, white, gray, orange, purple), got "${p.color}"`);
|
|
723
759
|
}
|
|
724
760
|
} else if (Array.isArray(p.color)) {
|
|
725
761
|
for (const c of p.color) {
|
|
726
|
-
if (typeof c !== "string" || !
|
|
727
|
-
throw new Error(`Process "${name}".color entries must be
|
|
762
|
+
if (typeof c !== "string" || !isValidColor(c)) {
|
|
763
|
+
throw new Error(`Process "${name}".color entries must be hex or basic names (black, red, green, yellow, blue, magenta, cyan, white, gray, orange), got "${c}"`);
|
|
728
764
|
}
|
|
729
765
|
}
|
|
730
766
|
}
|
|
@@ -2400,7 +2436,7 @@ Usage:
|
|
|
2400
2436
|
|
|
2401
2437
|
Options:
|
|
2402
2438
|
-n, --name <name=command> Add a named process
|
|
2403
|
-
-c, --color <colors> Comma-separated colors
|
|
2439
|
+
-c, --color <colors> Comma-separated colors (hex or names: black, red, green, yellow, blue, magenta, cyan, white, gray, orange, purple)
|
|
2404
2440
|
--config <path> Config file path (default: auto-detect)
|
|
2405
2441
|
-p, --prefix Prefixed output mode (no TUI, for CI/scripts)
|
|
2406
2442
|
--only <a,b,...> Only run these processes (+ their dependencies)
|
|
@@ -2531,10 +2567,11 @@ async function main() {
|
|
|
2531
2567
|
let config;
|
|
2532
2568
|
const warnings = [];
|
|
2533
2569
|
if (parsed.commands.length > 0 || parsed.named.length > 0) {
|
|
2534
|
-
const
|
|
2570
|
+
const isScriptPattern = (c) => c.startsWith("npm:") || /[*?[]/.test(c);
|
|
2571
|
+
const hasNpmPatterns = parsed.commands.some(isScriptPattern);
|
|
2535
2572
|
if (hasNpmPatterns) {
|
|
2536
|
-
const npmPatterns = parsed.commands.filter(
|
|
2537
|
-
const otherCommands = parsed.commands.filter((c) => !c
|
|
2573
|
+
const npmPatterns = parsed.commands.filter(isScriptPattern);
|
|
2574
|
+
const otherCommands = parsed.commands.filter((c) => !isScriptPattern(c));
|
|
2538
2575
|
const processes = {};
|
|
2539
2576
|
for (const pattern of npmPatterns) {
|
|
2540
2577
|
const entry = {};
|