numux 2.15.0 → 2.16.1
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 +5 -3
- package/dist/man/numux.1 +6 -4
- package/dist/numux.js +83 -69
- 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) |
|
|
@@ -245,7 +245,9 @@ Auto-exits when all processes finish. Exit code 1 if any process failed.
|
|
|
245
245
|
|
|
246
246
|
## Logging
|
|
247
247
|
|
|
248
|
-
numux writes per-process log files (ANSI-stripped)
|
|
248
|
+
numux writes per-process log files (ANSI-stripped) for every run. By default they go to `<tmpdir>/numux/<project-name>/`, or to `--log-dir` / the `logDir` config when set. Each session creates a timestamped subdirectory with a `latest` symlink pointing to the most recent run.
|
|
249
|
+
|
|
250
|
+
The TUI prints `Logs saved to: <session-dir>` on exit (Ctrl+C or signal). Multiple numux instances in the same project share the default base dir, so each session has its own timestamped folder but the `latest` symlink will point at whichever started most recently. If `numux logs` falls back to the default and a `latest` symlink is in use, it prints a warning. To avoid the collision, set `logDir` per config or pass `--log-dir`.
|
|
249
251
|
|
|
250
252
|
<!-- generated:logging-usage -->
|
|
251
253
|
```sh
|
|
@@ -275,7 +277,7 @@ Top-level options apply to all processes (process-level settings override):
|
|
|
275
277
|
| `stopSignal` | `'SIGTERM' \| 'SIGINT' \| 'SIGHUP'` | Global stop signal, inherited by all processes |
|
|
276
278
|
| `errorMatcher` | `boolean \| string` | Global error matcher, inherited by all processes. `true` = detect ANSI red output, string = regex |
|
|
277
279
|
| `watch` | `string \| string[]` | Global watch patterns, inherited by processes without their own watch |
|
|
278
|
-
| `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. |
|
|
279
281
|
| `prefix` | `boolean` | Use prefixed output mode instead of TUI (for CI/scripts) |
|
|
280
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"`) |
|
|
281
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" "
|
|
1
|
+
.TH "NUMUX" "1" "May 2026" "2.16.1" "numux manual"
|
|
2
2
|
.SH "NAME"
|
|
3
3
|
\fBnumux\fR
|
|
4
4
|
.P
|
|
@@ -380,7 +380,9 @@ numux \-\-prefix
|
|
|
380
380
|
Auto\-exits when all processes finish\. Exit code 1 if any process failed\.
|
|
381
381
|
.SH Logging
|
|
382
382
|
.P
|
|
383
|
-
numux writes per\-process log files (ANSI\-stripped)
|
|
383
|
+
numux writes per\-process log files (ANSI\-stripped) for every run\. By default they go to \fB<tmpdir>/numux/<project\-name>/\fP, or to \fB\-\-log\-dir\fP / the \fBlogDir\fP config when set\. Each session creates a timestamped subdirectory with a \fBlatest\fP symlink pointing to the most recent run\.
|
|
384
|
+
.P
|
|
385
|
+
The TUI prints \fBLogs saved to: <session\-dir>\fP on exit (Ctrl+C or signal)\. Multiple numux instances in the same project share the default base dir, so each session has its own timestamped folder but the \fBlatest\fP symlink will point at whichever started most recently\. If \fBnumux logs\fP falls back to the default and a \fBlatest\fP symlink is in use, it prints a warning\. To avoid the collision, set \fBlogDir\fP per config or pass \fB\-\-log\-dir\fP\|\.
|
|
384
386
|
<!\-\- generated:logging\-usage \-\->
|
|
385
387
|
.RS 2
|
|
386
388
|
.nf
|
|
@@ -484,9 +486,9 @@ _
|
|
|
484
486
|
T{
|
|
485
487
|
\fBsort\fP
|
|
486
488
|
T}|T{
|
|
487
|
-
\fB'config' | 'alphabetical' | 'topological'\fP
|
|
489
|
+
\fB'config' | 'alphabetical' | 'topological' | 'status'\fP
|
|
488
490
|
T}|T{
|
|
489
|
-
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\.
|
|
490
492
|
T}
|
|
491
493
|
_
|
|
492
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) |
|
|
@@ -257,7 +257,9 @@ numux --prefix
|
|
|
257
257
|
\`\`\`
|
|
258
258
|
|
|
259
259
|
Auto-exits when all processes finish. Exit code 1 if any process failed.` },
|
|
260
|
-
logging: { title: "Logging", body: `numux writes per-process log files (ANSI-stripped)
|
|
260
|
+
logging: { title: "Logging", body: `numux writes per-process log files (ANSI-stripped) for every run. By default they go to \`<tmpdir>/numux/<project-name>/\`, or to \`--log-dir\` / the \`logDir\` config when set. Each session creates a timestamped subdirectory with a \`latest\` symlink pointing to the most recent run.
|
|
261
|
+
|
|
262
|
+
The TUI prints \`Logs saved to: <session-dir>\` on exit (Ctrl+C or signal). Multiple numux instances in the same project share the default base dir, so each session has its own timestamped folder but the \`latest\` symlink will point at whichever started most recently. If \`numux logs\` falls back to the default and a \`latest\` symlink is in use, it prints a warning. To avoid the collision, set \`logDir\` per config or pass \`--log-dir\`.
|
|
261
263
|
|
|
262
264
|
<!-- generated:logging-usage -->
|
|
263
265
|
\`\`\`sh
|
|
@@ -281,7 +283,7 @@ numux logs api | tail -f # Follow process log output
|
|
|
281
283
|
| \`stopSignal\` | \`'SIGTERM' \\| 'SIGINT' \\| 'SIGHUP'\` | Global stop signal, inherited by all processes |
|
|
282
284
|
| \`errorMatcher\` | \`boolean \\| string\` | Global error matcher, inherited by all processes. \`true\` = detect ANSI red output, string = regex |
|
|
283
285
|
| \`watch\` | \`string \\| string[]\` | Global watch patterns, inherited by processes without their own watch |
|
|
284
|
-
| \`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. |
|
|
285
287
|
| \`prefix\` | \`boolean\` | Use prefixed output mode instead of TUI (for CI/scripts) |
|
|
286
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"\`) |
|
|
287
289
|
| \`killOthers\` | \`boolean\` | Kill all processes when any one exits (regardless of exit code) |
|
|
@@ -546,7 +548,7 @@ var init_help = __esm(() => {
|
|
|
546
548
|
var require_package = __commonJS((exports, module) => {
|
|
547
549
|
module.exports = {
|
|
548
550
|
name: "numux",
|
|
549
|
-
version: "2.
|
|
551
|
+
version: "2.16.1",
|
|
550
552
|
description: "Terminal multiplexer with dependency orchestration",
|
|
551
553
|
type: "module",
|
|
552
554
|
license: "MIT",
|
|
@@ -736,7 +738,7 @@ var FLAGS = [
|
|
|
736
738
|
short: "-s",
|
|
737
739
|
key: "sort",
|
|
738
740
|
description: "Tab display order",
|
|
739
|
-
valueName: "<config|alphabetical|topological>",
|
|
741
|
+
valueName: "<config|alphabetical|topological|status>",
|
|
740
742
|
completionHint: "none"
|
|
741
743
|
},
|
|
742
744
|
{
|
|
@@ -1910,7 +1912,7 @@ function validateErrorMatcher(name, value) {
|
|
|
1910
1912
|
}
|
|
1911
1913
|
return;
|
|
1912
1914
|
}
|
|
1913
|
-
var VALID_SORT_VALUES = new Set(["config", "alphabetical", "topological"]);
|
|
1915
|
+
var VALID_SORT_VALUES = new Set(["config", "alphabetical", "topological", "status"]);
|
|
1914
1916
|
function validateSort(value) {
|
|
1915
1917
|
if (typeof value === "string") {
|
|
1916
1918
|
if (!VALID_SORT_VALUES.has(value)) {
|
|
@@ -2900,6 +2902,53 @@ function evaluateCondition(condition) {
|
|
|
2900
2902
|
// src/ui/app.ts
|
|
2901
2903
|
import { BoxRenderable as BoxRenderable3, createCliRenderer } from "@opentui/core";
|
|
2902
2904
|
|
|
2905
|
+
// src/utils/shutdown.ts
|
|
2906
|
+
var finalized = false;
|
|
2907
|
+
function finalizeShutdown(logWriter, exitCode) {
|
|
2908
|
+
if (finalized)
|
|
2909
|
+
process.exit(exitCode);
|
|
2910
|
+
finalized = true;
|
|
2911
|
+
if (logWriter && !logWriter.isTemporary) {
|
|
2912
|
+
process.stderr.write(`Logs saved to: ${logWriter.getDirectory()}
|
|
2913
|
+
`);
|
|
2914
|
+
}
|
|
2915
|
+
logWriter?.cleanup();
|
|
2916
|
+
process.exit(exitCode);
|
|
2917
|
+
}
|
|
2918
|
+
function setupShutdownHandlers(app, logWriter) {
|
|
2919
|
+
let shuttingDown = false;
|
|
2920
|
+
const shutdown = () => {
|
|
2921
|
+
if (shuttingDown) {
|
|
2922
|
+
process.exit(1);
|
|
2923
|
+
}
|
|
2924
|
+
shuttingDown = true;
|
|
2925
|
+
app.shutdown().finally(() => {
|
|
2926
|
+
finalizeShutdown(logWriter, app.hasFailures() ? 1 : 0);
|
|
2927
|
+
});
|
|
2928
|
+
};
|
|
2929
|
+
process.on("SIGINT", shutdown);
|
|
2930
|
+
process.on("SIGTERM", shutdown);
|
|
2931
|
+
process.on("uncaughtException", (err) => {
|
|
2932
|
+
log("Uncaught exception:", err?.message ?? err);
|
|
2933
|
+
app.shutdown().finally(() => {
|
|
2934
|
+
process.stderr.write(`numux: unexpected error: ${err?.stack ?? err}
|
|
2935
|
+
`);
|
|
2936
|
+
logWriter?.cleanup();
|
|
2937
|
+
process.exit(1);
|
|
2938
|
+
});
|
|
2939
|
+
});
|
|
2940
|
+
process.on("unhandledRejection", (reason) => {
|
|
2941
|
+
const stack = reason instanceof Error ? reason.stack : String(reason);
|
|
2942
|
+
log("Unhandled rejection:", stack);
|
|
2943
|
+
app.shutdown().finally(() => {
|
|
2944
|
+
process.stderr.write(`numux: unhandled rejection: ${stack}
|
|
2945
|
+
`);
|
|
2946
|
+
logWriter?.cleanup();
|
|
2947
|
+
process.exit(1);
|
|
2948
|
+
});
|
|
2949
|
+
});
|
|
2950
|
+
}
|
|
2951
|
+
|
|
2903
2952
|
// src/ui/help-overlay.ts
|
|
2904
2953
|
import { BoxRenderable, TextRenderable } from "@opentui/core";
|
|
2905
2954
|
|
|
@@ -4053,6 +4102,11 @@ var STATUS_ICON_HEX = {
|
|
|
4053
4102
|
skipped: "#888888"
|
|
4054
4103
|
};
|
|
4055
4104
|
var TERMINAL_STATUSES = new Set(["finished", "stopped", "failed", "skipped"]);
|
|
4105
|
+
function getDisplayOrder(originalNames, statuses) {
|
|
4106
|
+
const active = originalNames.filter((n) => !TERMINAL_STATUSES.has(statuses.get(n)));
|
|
4107
|
+
const terminal = originalNames.filter((n) => TERMINAL_STATUSES.has(statuses.get(n)));
|
|
4108
|
+
return [...active, ...terminal];
|
|
4109
|
+
}
|
|
4056
4110
|
function formatTab(name, status) {
|
|
4057
4111
|
return `${STATUS_ICONS[status]} ${name}`;
|
|
4058
4112
|
}
|
|
@@ -4066,11 +4120,6 @@ function formatDescription(status, exitCode, restartCount) {
|
|
|
4066
4120
|
}
|
|
4067
4121
|
return desc;
|
|
4068
4122
|
}
|
|
4069
|
-
function getDisplayOrder(originalNames, statuses) {
|
|
4070
|
-
const active = originalNames.filter((n) => !TERMINAL_STATUSES.has(statuses.get(n)));
|
|
4071
|
-
const terminal = originalNames.filter((n) => TERMINAL_STATUSES.has(statuses.get(n)));
|
|
4072
|
-
return [...active, ...terminal];
|
|
4073
|
-
}
|
|
4074
4123
|
function resolveOptionColors(names, statuses, processColors, inputWaiting, erroredProcesses, searchMatchProcesses) {
|
|
4075
4124
|
return names.map((name) => {
|
|
4076
4125
|
const status = statuses.get(name);
|
|
@@ -4160,12 +4209,14 @@ class TabBar {
|
|
|
4160
4209
|
statuses;
|
|
4161
4210
|
baseDescriptions;
|
|
4162
4211
|
processColors;
|
|
4212
|
+
reorderByStatus;
|
|
4163
4213
|
inputWaiting = new Set;
|
|
4164
4214
|
erroredProcesses = new Set;
|
|
4165
4215
|
searchMatchCounts = new Map;
|
|
4166
|
-
constructor(renderer, names, colors) {
|
|
4216
|
+
constructor(renderer, names, colors, reorderByStatus = false) {
|
|
4167
4217
|
this.originalNames = names;
|
|
4168
4218
|
this.names = [...names];
|
|
4219
|
+
this.reorderByStatus = reorderByStatus;
|
|
4169
4220
|
this.statuses = new Map(names.map((n) => [n, "pending"]));
|
|
4170
4221
|
this.baseDescriptions = new Map(names.map((n) => [n, "pending"]));
|
|
4171
4222
|
this.processColors = colors ?? new Map;
|
|
@@ -4235,17 +4286,19 @@ class TabBar {
|
|
|
4235
4286
|
return this.names.length;
|
|
4236
4287
|
}
|
|
4237
4288
|
refreshOptions() {
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4289
|
+
if (this.reorderByStatus) {
|
|
4290
|
+
const currentIdx = this.renderable.getSelectedIndex();
|
|
4291
|
+
const currentName = this.names[currentIdx];
|
|
4292
|
+
this.names = getDisplayOrder(this.originalNames, this.statuses);
|
|
4293
|
+
const newIdx = this.names.indexOf(currentName);
|
|
4294
|
+
if (newIdx >= 0 && newIdx !== currentIdx) {
|
|
4295
|
+
this.renderable.setSelectedIndex(newIdx);
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
4241
4298
|
this.renderable.options = this.names.map((n) => ({
|
|
4242
4299
|
name: formatTab(n, this.statuses.get(n)),
|
|
4243
4300
|
description: this.getDescription(n)
|
|
4244
4301
|
}));
|
|
4245
|
-
const newIdx = this.names.indexOf(currentName);
|
|
4246
|
-
if (newIdx >= 0 && newIdx !== currentIdx) {
|
|
4247
|
-
this.renderable.setSelectedIndex(newIdx);
|
|
4248
|
-
}
|
|
4249
4302
|
this.updateOptionColors();
|
|
4250
4303
|
}
|
|
4251
4304
|
getDescription(name) {
|
|
@@ -4274,9 +4327,6 @@ class TabBar {
|
|
|
4274
4327
|
setSelectedIndex(index) {
|
|
4275
4328
|
this.renderable.setSelectedIndex(index);
|
|
4276
4329
|
}
|
|
4277
|
-
focus() {
|
|
4278
|
-
this.renderable.focus();
|
|
4279
|
-
}
|
|
4280
4330
|
}
|
|
4281
4331
|
|
|
4282
4332
|
// src/ui/app.ts
|
|
@@ -4326,7 +4376,7 @@ class App {
|
|
|
4326
4376
|
border: false
|
|
4327
4377
|
});
|
|
4328
4378
|
const processHexColors = buildProcessHexColorMap(this.names, this.config);
|
|
4329
|
-
this.tabBar = new TabBar(this.renderer, this.names, processHexColors);
|
|
4379
|
+
this.tabBar = new TabBar(this.renderer, this.names, processHexColors, this.config.sort === "status");
|
|
4330
4380
|
const contentRow = new BoxRenderable3(this.renderer, {
|
|
4331
4381
|
id: "content-row",
|
|
4332
4382
|
flexDirection: "row",
|
|
@@ -4433,7 +4483,7 @@ class App {
|
|
|
4433
4483
|
return;
|
|
4434
4484
|
}
|
|
4435
4485
|
this.shutdown().then(() => {
|
|
4436
|
-
|
|
4486
|
+
finalizeShutdown(this.logWriter, this.hasFailures() ? 1 : 0);
|
|
4437
4487
|
});
|
|
4438
4488
|
return;
|
|
4439
4489
|
}
|
|
@@ -4569,7 +4619,6 @@ class App {
|
|
|
4569
4619
|
});
|
|
4570
4620
|
if (this.names.length > 0) {
|
|
4571
4621
|
this.switchPane(this.names[0]);
|
|
4572
|
-
this.tabBar.focus();
|
|
4573
4622
|
}
|
|
4574
4623
|
await this.manager.startAll(termCols, termRows);
|
|
4575
4624
|
}
|
|
@@ -5155,46 +5204,6 @@ function defaultLogDir(cwd) {
|
|
|
5155
5204
|
return join2(tmpdir2(), "numux", resolveProjectName(cwd));
|
|
5156
5205
|
}
|
|
5157
5206
|
|
|
5158
|
-
// src/utils/shutdown.ts
|
|
5159
|
-
function setupShutdownHandlers(app, logWriter) {
|
|
5160
|
-
let shuttingDown = false;
|
|
5161
|
-
const shutdown = () => {
|
|
5162
|
-
if (shuttingDown) {
|
|
5163
|
-
process.exit(1);
|
|
5164
|
-
}
|
|
5165
|
-
shuttingDown = true;
|
|
5166
|
-
app.shutdown().finally(() => {
|
|
5167
|
-
if (logWriter && !logWriter.isTemporary) {
|
|
5168
|
-
process.stderr.write(`Logs saved to: ${logWriter.getDirectory()}
|
|
5169
|
-
`);
|
|
5170
|
-
}
|
|
5171
|
-
logWriter?.cleanup();
|
|
5172
|
-
process.exit(app.hasFailures() ? 1 : 0);
|
|
5173
|
-
});
|
|
5174
|
-
};
|
|
5175
|
-
process.on("SIGINT", shutdown);
|
|
5176
|
-
process.on("SIGTERM", shutdown);
|
|
5177
|
-
process.on("uncaughtException", (err) => {
|
|
5178
|
-
log("Uncaught exception:", err?.message ?? err);
|
|
5179
|
-
app.shutdown().finally(() => {
|
|
5180
|
-
process.stderr.write(`numux: unexpected error: ${err?.stack ?? err}
|
|
5181
|
-
`);
|
|
5182
|
-
logWriter?.cleanup();
|
|
5183
|
-
process.exit(1);
|
|
5184
|
-
});
|
|
5185
|
-
});
|
|
5186
|
-
process.on("unhandledRejection", (reason) => {
|
|
5187
|
-
const stack = reason instanceof Error ? reason.stack : String(reason);
|
|
5188
|
-
log("Unhandled rejection:", stack);
|
|
5189
|
-
app.shutdown().finally(() => {
|
|
5190
|
-
process.stderr.write(`numux: unhandled rejection: ${stack}
|
|
5191
|
-
`);
|
|
5192
|
-
logWriter?.cleanup();
|
|
5193
|
-
process.exit(1);
|
|
5194
|
-
});
|
|
5195
|
-
});
|
|
5196
|
-
}
|
|
5197
|
-
|
|
5198
5207
|
// src/index.ts
|
|
5199
5208
|
var HELP = generateHelp();
|
|
5200
5209
|
var INIT_TEMPLATE = `import { defineConfig } from 'numux'
|
|
@@ -5250,9 +5259,14 @@ async function main() {
|
|
|
5250
5259
|
process.exit(0);
|
|
5251
5260
|
}
|
|
5252
5261
|
if (parsed.logs) {
|
|
5253
|
-
const
|
|
5262
|
+
const resolved = parsed.logDir ? { dir: parsed.logDir, explicit: true } : await resolveLogDir(parsed.configPath);
|
|
5263
|
+
const logDir2 = resolved.dir;
|
|
5254
5264
|
const latestDir = resolve8(logDir2, "latest");
|
|
5255
|
-
const
|
|
5265
|
+
const usingLatest = existsSync6(latestDir);
|
|
5266
|
+
const target = usingLatest ? latestDir : logDir2;
|
|
5267
|
+
if (!resolved.explicit && usingLatest) {
|
|
5268
|
+
console.warn('Warning: using default log directory; "latest" may have been overwritten by another numux instance in this project.');
|
|
5269
|
+
}
|
|
5256
5270
|
if (parsed.logsProcess) {
|
|
5257
5271
|
const logFile2 = resolve8(target, `${parsed.logsProcess}.log`);
|
|
5258
5272
|
if (!existsSync6(logFile2)) {
|
|
@@ -5451,10 +5465,10 @@ async function resolveLogDir(configPath) {
|
|
|
5451
5465
|
try {
|
|
5452
5466
|
const raw = await loadConfig(configPath);
|
|
5453
5467
|
if (typeof raw.logDir === "string" && raw.logDir.trim()) {
|
|
5454
|
-
return resolve8(raw.logDir.trim());
|
|
5468
|
+
return { dir: resolve8(raw.logDir.trim()), explicit: true };
|
|
5455
5469
|
}
|
|
5456
5470
|
} catch {}
|
|
5457
|
-
return defaultLogDir(process.cwd());
|
|
5471
|
+
return { dir: defaultLogDir(process.cwd()), explicit: false };
|
|
5458
5472
|
}
|
|
5459
5473
|
main().catch((err) => {
|
|
5460
5474
|
console.error(err instanceof Error ? err.message : err);
|
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[];
|