numux 2.16.0 → 2.16.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/README.md +2 -2
- package/dist/man/numux.1 +3 -3
- package/dist/numux.js +55 -22
- package/dist/types.d.ts +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -212,7 +212,7 @@ export default defineConfig({
|
|
|
212
212
|
<!-- generated:options -->
|
|
213
213
|
| Flag | Description |
|
|
214
214
|
|------|-------------|
|
|
215
|
-
| `-s,` `--sort` `<config|alphabetical|topological>` | Tab display order |
|
|
215
|
+
| `-s,` `--sort` `<config|alphabetical|topological|status>` | Tab display order |
|
|
216
216
|
| `-w,` `--workspace` `<script>` | Run a package.json script across all workspaces |
|
|
217
217
|
| `-n,` `--name` `<name=command>` | Add a named process |
|
|
218
218
|
| `-c,` `--color` `<colors>` | Comma-separated colors (hex or names: black, red, green, yellow, blue, magenta, cyan, white, gray, orange, purple) |
|
|
@@ -277,7 +277,7 @@ Top-level options apply to all processes (process-level settings override):
|
|
|
277
277
|
| `stopSignal` | `'SIGTERM' \| 'SIGINT' \| 'SIGHUP'` | Global stop signal, inherited by all processes |
|
|
278
278
|
| `errorMatcher` | `boolean \| string` | Global error matcher, inherited by all processes. `true` = detect ANSI red output, string = regex |
|
|
279
279
|
| `watch` | `string \| string[]` | Global watch patterns, inherited by processes without their own watch |
|
|
280
|
-
| `sort` | `'config' \| 'alphabetical' \| 'topological'` | Tab display order. `'config'` preserves definition order (package.json script order for wildcards), `'alphabetical'` sorts by process name, `'topological'` sorts by dependency tiers. |
|
|
280
|
+
| `sort` | `'config' \| 'alphabetical' \| 'topological' \| 'status'` | Tab display order. `'config'` preserves definition order (package.json script order for wildcards), `'alphabetical'` sorts by process name, `'topological'` sorts by dependency tiers, `'status'` uses config order but moves finished/stopped/failed/skipped tabs to the bottom. |
|
|
281
281
|
| `prefix` | `boolean` | Use prefixed output mode instead of TUI (for CI/scripts) |
|
|
282
282
|
| `timestamps` | `boolean \| string` | Add timestamps to output lines. `true` uses default `HH:mm:ss.SSS` format, or pass a format string (e.g. `"HH:mm:ss"`) |
|
|
283
283
|
| `killOthers` | `boolean` | Kill all processes when any one exits (regardless of exit code) |
|
package/dist/man/numux.1
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.TH "NUMUX" "1" "May 2026" "2.16.
|
|
1
|
+
.TH "NUMUX" "1" "May 2026" "2.16.2" "numux manual"
|
|
2
2
|
.SH "NAME"
|
|
3
3
|
\fBnumux\fR
|
|
4
4
|
.P
|
|
@@ -486,9 +486,9 @@ _
|
|
|
486
486
|
T{
|
|
487
487
|
\fBsort\fP
|
|
488
488
|
T}|T{
|
|
489
|
-
\fB'config' | 'alphabetical' | 'topological'\fP
|
|
489
|
+
\fB'config' | 'alphabetical' | 'topological' | 'status'\fP
|
|
490
490
|
T}|T{
|
|
491
|
-
Tab display order\. \fB'config'\fP preserves definition order (package\.json script order for wildcards), \fB'alphabetical'\fP sorts by process name, \fB'topological'\fP sorts by dependency tiers\.
|
|
491
|
+
Tab display order\. \fB'config'\fP preserves definition order (package\.json script order for wildcards), \fB'alphabetical'\fP sorts by process name, \fB'topological'\fP sorts by dependency tiers, \fB'status'\fP uses config order but moves finished/stopped/failed/skipped tabs to the bottom\.
|
|
492
492
|
T}
|
|
493
493
|
_
|
|
494
494
|
T{
|
package/dist/numux.js
CHANGED
|
@@ -230,7 +230,7 @@ export default defineConfig({
|
|
|
230
230
|
options: { title: "Options", body: `<!-- generated:options -->
|
|
231
231
|
| Flag | Description |
|
|
232
232
|
|------|-------------|
|
|
233
|
-
| \`-s,\` \`--sort\` \`<config|alphabetical|topological>\` | Tab display order |
|
|
233
|
+
| \`-s,\` \`--sort\` \`<config|alphabetical|topological|status>\` | Tab display order |
|
|
234
234
|
| \`-w,\` \`--workspace\` \`<script>\` | Run a package.json script across all workspaces |
|
|
235
235
|
| \`-n,\` \`--name\` \`<name=command>\` | Add a named process |
|
|
236
236
|
| \`-c,\` \`--color\` \`<colors>\` | Comma-separated colors (hex or names: black, red, green, yellow, blue, magenta, cyan, white, gray, orange, purple) |
|
|
@@ -283,7 +283,7 @@ numux logs api | tail -f # Follow process log output
|
|
|
283
283
|
| \`stopSignal\` | \`'SIGTERM' \\| 'SIGINT' \\| 'SIGHUP'\` | Global stop signal, inherited by all processes |
|
|
284
284
|
| \`errorMatcher\` | \`boolean \\| string\` | Global error matcher, inherited by all processes. \`true\` = detect ANSI red output, string = regex |
|
|
285
285
|
| \`watch\` | \`string \\| string[]\` | Global watch patterns, inherited by processes without their own watch |
|
|
286
|
-
| \`sort\` | \`'config' \\| 'alphabetical' \\| 'topological'\` | Tab display order. \`'config'\` preserves definition order (package.json script order for wildcards), \`'alphabetical'\` sorts by process name, \`'topological'\` sorts by dependency tiers. |
|
|
286
|
+
| \`sort\` | \`'config' \\| 'alphabetical' \\| 'topological' \\| 'status'\` | Tab display order. \`'config'\` preserves definition order (package.json script order for wildcards), \`'alphabetical'\` sorts by process name, \`'topological'\` sorts by dependency tiers, \`'status'\` uses config order but moves finished/stopped/failed/skipped tabs to the bottom. |
|
|
287
287
|
| \`prefix\` | \`boolean\` | Use prefixed output mode instead of TUI (for CI/scripts) |
|
|
288
288
|
| \`timestamps\` | \`boolean \\| string\` | Add timestamps to output lines. \`true\` uses default \`HH:mm:ss.SSS\` format, or pass a format string (e.g. \`"HH:mm:ss"\`) |
|
|
289
289
|
| \`killOthers\` | \`boolean\` | Kill all processes when any one exits (regardless of exit code) |
|
|
@@ -548,7 +548,7 @@ var init_help = __esm(() => {
|
|
|
548
548
|
var require_package = __commonJS((exports, module) => {
|
|
549
549
|
module.exports = {
|
|
550
550
|
name: "numux",
|
|
551
|
-
version: "2.16.
|
|
551
|
+
version: "2.16.2",
|
|
552
552
|
description: "Terminal multiplexer with dependency orchestration",
|
|
553
553
|
type: "module",
|
|
554
554
|
license: "MIT",
|
|
@@ -738,7 +738,7 @@ var FLAGS = [
|
|
|
738
738
|
short: "-s",
|
|
739
739
|
key: "sort",
|
|
740
740
|
description: "Tab display order",
|
|
741
|
-
valueName: "<config|alphabetical|topological>",
|
|
741
|
+
valueName: "<config|alphabetical|topological|status>",
|
|
742
742
|
completionHint: "none"
|
|
743
743
|
},
|
|
744
744
|
{
|
|
@@ -1912,7 +1912,7 @@ function validateErrorMatcher(name, value) {
|
|
|
1912
1912
|
}
|
|
1913
1913
|
return;
|
|
1914
1914
|
}
|
|
1915
|
-
var VALID_SORT_VALUES = new Set(["config", "alphabetical", "topological"]);
|
|
1915
|
+
var VALID_SORT_VALUES = new Set(["config", "alphabetical", "topological", "status"]);
|
|
1916
1916
|
function validateSort(value) {
|
|
1917
1917
|
if (typeof value === "string") {
|
|
1918
1918
|
if (!VALID_SORT_VALUES.has(value)) {
|
|
@@ -3556,7 +3556,7 @@ function openLink(link) {
|
|
|
3556
3556
|
}
|
|
3557
3557
|
|
|
3558
3558
|
// src/ui/pane.ts
|
|
3559
|
-
var RENDER_LIMIT =
|
|
3559
|
+
var RENDER_LIMIT = 1500;
|
|
3560
3560
|
var MAX_SCROLLBACK_LINES = 1e6;
|
|
3561
3561
|
var MAX_BUFFER_BYTES = 500 * 1024 * 1024;
|
|
3562
3562
|
|
|
@@ -3570,6 +3570,9 @@ class Pane {
|
|
|
3570
3570
|
_timestampFormat = null;
|
|
3571
3571
|
lineTimestamps = [];
|
|
3572
3572
|
lineCounter = 0;
|
|
3573
|
+
signedLineCount = 0;
|
|
3574
|
+
timestampUpdateTimer = null;
|
|
3575
|
+
static TIMESTAMP_UPDATE_DEBOUNCE_MS = 32;
|
|
3573
3576
|
_onScroll = null;
|
|
3574
3577
|
_onCopy = null;
|
|
3575
3578
|
_onLinkClick = null;
|
|
@@ -3630,6 +3633,8 @@ class Pane {
|
|
|
3630
3633
|
this.bytesFed = 0;
|
|
3631
3634
|
this.lineTimestamps = [];
|
|
3632
3635
|
this.lineCounter = 0;
|
|
3636
|
+
this.signedLineCount = 0;
|
|
3637
|
+
this.timestampGutter?.clearAllLineSigns();
|
|
3633
3638
|
}
|
|
3634
3639
|
const now = Date.now();
|
|
3635
3640
|
if (this.lineCounter === 0) {
|
|
@@ -3644,10 +3649,18 @@ class Pane {
|
|
|
3644
3649
|
}
|
|
3645
3650
|
const text = this.decoder.decode(data, { stream: true });
|
|
3646
3651
|
this.terminal.feed(text);
|
|
3647
|
-
if (this._timestampFormat) {
|
|
3648
|
-
this.
|
|
3652
|
+
if (this._timestampFormat && this.lineTimestamps.length !== this.signedLineCount) {
|
|
3653
|
+
this.scheduleTimestampUpdate();
|
|
3649
3654
|
}
|
|
3650
3655
|
}
|
|
3656
|
+
scheduleTimestampUpdate() {
|
|
3657
|
+
if (this.timestampUpdateTimer)
|
|
3658
|
+
return;
|
|
3659
|
+
this.timestampUpdateTimer = setTimeout(() => {
|
|
3660
|
+
this.timestampUpdateTimer = null;
|
|
3661
|
+
this.updateTimestampSigns();
|
|
3662
|
+
}, Pane.TIMESTAMP_UPDATE_DEBOUNCE_MS);
|
|
3663
|
+
}
|
|
3651
3664
|
resize(cols, rows) {
|
|
3652
3665
|
this.terminal.cols = cols;
|
|
3653
3666
|
this.terminal.rows = rows;
|
|
@@ -3725,6 +3738,11 @@ class Pane {
|
|
|
3725
3738
|
this.bytesFed = 0;
|
|
3726
3739
|
this.lineTimestamps = [];
|
|
3727
3740
|
this.lineCounter = 0;
|
|
3741
|
+
this.signedLineCount = 0;
|
|
3742
|
+
if (this.timestampUpdateTimer) {
|
|
3743
|
+
clearTimeout(this.timestampUpdateTimer);
|
|
3744
|
+
this.timestampUpdateTimer = null;
|
|
3745
|
+
}
|
|
3728
3746
|
if (this._timestampFormat) {
|
|
3729
3747
|
this.timestampGutter?.clearAllLineSigns();
|
|
3730
3748
|
}
|
|
@@ -3736,6 +3754,11 @@ class Pane {
|
|
|
3736
3754
|
if (wasEnabled === isEnabled && this._timestampFormat === newFormat)
|
|
3737
3755
|
return;
|
|
3738
3756
|
this._timestampFormat = newFormat;
|
|
3757
|
+
if (this.timestampUpdateTimer) {
|
|
3758
|
+
clearTimeout(this.timestampUpdateTimer);
|
|
3759
|
+
this.timestampUpdateTimer = null;
|
|
3760
|
+
}
|
|
3761
|
+
this.signedLineCount = 0;
|
|
3739
3762
|
if (isEnabled && !wasEnabled) {
|
|
3740
3763
|
this.scrollBox.remove(this.terminal.id);
|
|
3741
3764
|
const gutterWidth = (newFormat?.length ?? 8) + 1;
|
|
@@ -3775,8 +3798,14 @@ class Pane {
|
|
|
3775
3798
|
signs.set(i, { before: formatTimestamp(new Date(this.lineTimestamps[i]), fmt) });
|
|
3776
3799
|
}
|
|
3777
3800
|
this.timestampGutter.setLineSigns(signs);
|
|
3801
|
+
this.signedLineCount = this.lineTimestamps.length;
|
|
3778
3802
|
}
|
|
3779
3803
|
destroy() {
|
|
3804
|
+
if (this.timestampUpdateTimer) {
|
|
3805
|
+
clearTimeout(this.timestampUpdateTimer);
|
|
3806
|
+
this.timestampUpdateTimer = null;
|
|
3807
|
+
}
|
|
3808
|
+
this.timestampGutter?.clearTarget();
|
|
3780
3809
|
this.terminal.destroy();
|
|
3781
3810
|
}
|
|
3782
3811
|
}
|
|
@@ -4102,6 +4131,11 @@ var STATUS_ICON_HEX = {
|
|
|
4102
4131
|
skipped: "#888888"
|
|
4103
4132
|
};
|
|
4104
4133
|
var TERMINAL_STATUSES = new Set(["finished", "stopped", "failed", "skipped"]);
|
|
4134
|
+
function getDisplayOrder(originalNames, statuses) {
|
|
4135
|
+
const active = originalNames.filter((n) => !TERMINAL_STATUSES.has(statuses.get(n)));
|
|
4136
|
+
const terminal = originalNames.filter((n) => TERMINAL_STATUSES.has(statuses.get(n)));
|
|
4137
|
+
return [...active, ...terminal];
|
|
4138
|
+
}
|
|
4105
4139
|
function formatTab(name, status) {
|
|
4106
4140
|
return `${STATUS_ICONS[status]} ${name}`;
|
|
4107
4141
|
}
|
|
@@ -4115,11 +4149,6 @@ function formatDescription(status, exitCode, restartCount) {
|
|
|
4115
4149
|
}
|
|
4116
4150
|
return desc;
|
|
4117
4151
|
}
|
|
4118
|
-
function getDisplayOrder(originalNames, statuses) {
|
|
4119
|
-
const active = originalNames.filter((n) => !TERMINAL_STATUSES.has(statuses.get(n)));
|
|
4120
|
-
const terminal = originalNames.filter((n) => TERMINAL_STATUSES.has(statuses.get(n)));
|
|
4121
|
-
return [...active, ...terminal];
|
|
4122
|
-
}
|
|
4123
4152
|
function resolveOptionColors(names, statuses, processColors, inputWaiting, erroredProcesses, searchMatchProcesses) {
|
|
4124
4153
|
return names.map((name) => {
|
|
4125
4154
|
const status = statuses.get(name);
|
|
@@ -4209,12 +4238,14 @@ class TabBar {
|
|
|
4209
4238
|
statuses;
|
|
4210
4239
|
baseDescriptions;
|
|
4211
4240
|
processColors;
|
|
4241
|
+
reorderByStatus;
|
|
4212
4242
|
inputWaiting = new Set;
|
|
4213
4243
|
erroredProcesses = new Set;
|
|
4214
4244
|
searchMatchCounts = new Map;
|
|
4215
|
-
constructor(renderer, names, colors) {
|
|
4245
|
+
constructor(renderer, names, colors, reorderByStatus = false) {
|
|
4216
4246
|
this.originalNames = names;
|
|
4217
4247
|
this.names = [...names];
|
|
4248
|
+
this.reorderByStatus = reorderByStatus;
|
|
4218
4249
|
this.statuses = new Map(names.map((n) => [n, "pending"]));
|
|
4219
4250
|
this.baseDescriptions = new Map(names.map((n) => [n, "pending"]));
|
|
4220
4251
|
this.processColors = colors ?? new Map;
|
|
@@ -4284,17 +4315,19 @@ class TabBar {
|
|
|
4284
4315
|
return this.names.length;
|
|
4285
4316
|
}
|
|
4286
4317
|
refreshOptions() {
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4318
|
+
if (this.reorderByStatus) {
|
|
4319
|
+
const currentIdx = this.renderable.getSelectedIndex();
|
|
4320
|
+
const currentName = this.names[currentIdx];
|
|
4321
|
+
this.names = getDisplayOrder(this.originalNames, this.statuses);
|
|
4322
|
+
const newIdx = this.names.indexOf(currentName);
|
|
4323
|
+
if (newIdx >= 0 && newIdx !== currentIdx) {
|
|
4324
|
+
this.renderable.setSelectedIndex(newIdx);
|
|
4325
|
+
}
|
|
4326
|
+
}
|
|
4290
4327
|
this.renderable.options = this.names.map((n) => ({
|
|
4291
4328
|
name: formatTab(n, this.statuses.get(n)),
|
|
4292
4329
|
description: this.getDescription(n)
|
|
4293
4330
|
}));
|
|
4294
|
-
const newIdx = this.names.indexOf(currentName);
|
|
4295
|
-
if (newIdx >= 0 && newIdx !== currentIdx) {
|
|
4296
|
-
this.renderable.setSelectedIndex(newIdx);
|
|
4297
|
-
}
|
|
4298
4331
|
this.updateOptionColors();
|
|
4299
4332
|
}
|
|
4300
4333
|
getDescription(name) {
|
|
@@ -4372,7 +4405,7 @@ class App {
|
|
|
4372
4405
|
border: false
|
|
4373
4406
|
});
|
|
4374
4407
|
const processHexColors = buildProcessHexColorMap(this.names, this.config);
|
|
4375
|
-
this.tabBar = new TabBar(this.renderer, this.names, processHexColors);
|
|
4408
|
+
this.tabBar = new TabBar(this.renderer, this.names, processHexColors, this.config.sort === "status");
|
|
4376
4409
|
const contentRow = new BoxRenderable3(this.renderer, {
|
|
4377
4410
|
id: "content-row",
|
|
4378
4411
|
flexDirection: "row",
|
package/dist/types.d.ts
CHANGED
|
@@ -93,7 +93,8 @@ export interface NumuxConfig<K extends string = string> {
|
|
|
93
93
|
watch?: string | string[];
|
|
94
94
|
/**
|
|
95
95
|
* Tab display order. `'config'` preserves definition order (package.json script order for wildcards),
|
|
96
|
-
* `'alphabetical'` sorts by process name, `'topological'` sorts by dependency tiers
|
|
96
|
+
* `'alphabetical'` sorts by process name, `'topological'` sorts by dependency tiers,
|
|
97
|
+
* `'status'` uses config order but moves finished/stopped/failed/skipped tabs to the bottom.
|
|
97
98
|
* @default 'config'
|
|
98
99
|
*/
|
|
99
100
|
sort?: SortOrder;
|
|
@@ -123,7 +124,7 @@ export interface NumuxConfig<K extends string = string> {
|
|
|
123
124
|
logDir?: string;
|
|
124
125
|
processes: Record<K, NumuxProcessConfig<K> | NumuxScriptPattern<K> | string | true>;
|
|
125
126
|
}
|
|
126
|
-
export type SortOrder = 'config' | 'alphabetical' | 'topological';
|
|
127
|
+
export type SortOrder = 'config' | 'alphabetical' | 'topological' | 'status';
|
|
127
128
|
/** Process config after validation — dependsOn is always normalized to an array */
|
|
128
129
|
export interface ResolvedProcessConfig extends Omit<NumuxProcessConfig, 'dependsOn' | 'workspaces'> {
|
|
129
130
|
dependsOn?: string[];
|