numux 1.19.0 → 1.21.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 CHANGED
@@ -193,6 +193,11 @@ Top-level options apply to all processes (process-level settings override):
193
193
  | `env` | `Record<string, string>` | Environment variables merged into all processes (process `env` overrides per key) |
194
194
  | `envFile` | `string \| string[] \| false` | `.env` file(s) for all processes (process `envFile` replaces if set; `false` disables) |
195
195
  | `showCommand` | `boolean` | Print the command being run as the first line of output (default: `true`) |
196
+ | `maxRestarts` | `number` | Restart limit for all processes (default: `Infinity`) |
197
+ | `readyTimeout` | `number` | Ready timeout in ms for all processes |
198
+ | `stopSignal` | `'SIGTERM' \| 'SIGINT' \| 'SIGHUP'` | Stop signal for all processes (default: `'SIGTERM'`) |
199
+ | `errorMatcher` | `boolean \| string` | Error detection for all processes (`true` = ANSI red, string = regex) |
200
+ | `watch` | `string \| string[]` | Watch patterns for all processes (process `watch` replaces if set) |
196
201
 
197
202
  ```ts
198
203
  export default defineConfig({
@@ -223,6 +228,7 @@ Each process accepts:
223
228
  | `maxRestarts` | `number` | `Infinity` | Max auto-restart attempts before giving up |
224
229
  | `delay` | `number` | — | Milliseconds to wait before starting the process |
225
230
  | `condition` | `string` | — | Env var name; process skipped if falsy. Prefix with `!` to negate |
231
+ | `platform` | `string \| string[]` | — | OS(es) this process runs on (e.g. `'darwin'`, `'linux'`). Non-matching processes are removed; dependents still start |
226
232
  | `stopSignal` | `string` | `SIGTERM` | Signal for graceful stop (`SIGTERM`, `SIGINT`, or `SIGHUP`) |
227
233
  | `color` | `string \| string[]` | auto | Hex (e.g. `"#ff6600"`) or basic name: black, red, green, yellow, blue, magenta, cyan, white, gray, orange, purple |
228
234
  | `watch` | `string \| string[]` | — | Glob patterns — restart process when matching files change |
package/dist/numux.js CHANGED
@@ -36,7 +36,7 @@ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports,
36
36
  var require_package = __commonJS((exports, module) => {
37
37
  module.exports = {
38
38
  name: "numux",
39
- version: "1.19.0",
39
+ version: "1.21.0",
40
40
  description: "Terminal multiplexer with dependency orchestration",
41
41
  type: "module",
42
42
  license: "MIT",
@@ -826,6 +826,34 @@ async function autoDetectConfig(cwd) {
826
826
  throw new Error(`No numux config found. Create one of: ${CONFIG_FILES.join(", ")}, or add a "numux" key to package.json`);
827
827
  }
828
828
 
829
+ // src/config/platform.ts
830
+ function filterByPlatform(config, currentPlatform = process.platform) {
831
+ const excluded = new Set;
832
+ for (const [name, proc] of Object.entries(config.processes)) {
833
+ if (!proc.platform)
834
+ continue;
835
+ const platforms = Array.isArray(proc.platform) ? proc.platform : [proc.platform];
836
+ if (!platforms.includes(currentPlatform)) {
837
+ excluded.add(name);
838
+ }
839
+ }
840
+ if (excluded.size === 0)
841
+ return config;
842
+ const processes = {};
843
+ for (const [name, proc] of Object.entries(config.processes)) {
844
+ if (excluded.has(name))
845
+ continue;
846
+ const copy = { ...proc };
847
+ if (copy.dependsOn) {
848
+ copy.dependsOn = copy.dependsOn.filter((d) => !excluded.has(d));
849
+ if (copy.dependsOn.length === 0)
850
+ copy.dependsOn = undefined;
851
+ }
852
+ processes[name] = copy;
853
+ }
854
+ return { processes };
855
+ }
856
+
829
857
  // src/config/resolver.ts
830
858
  function resolveDependencyTiers(config) {
831
859
  const names = Object.keys(config.processes);
@@ -1007,6 +1035,11 @@ function validateConfig(raw, warnings) {
1007
1035
  const globalCwd = typeof config.cwd === "string" ? config.cwd : undefined;
1008
1036
  const globalShowCommand = typeof config.showCommand === "boolean" ? config.showCommand : undefined;
1009
1037
  const globalEnvFile = validateEnvFile(config.envFile);
1038
+ const globalMaxRestarts = typeof config.maxRestarts === "number" && config.maxRestarts >= 0 ? config.maxRestarts : undefined;
1039
+ const globalReadyTimeout = typeof config.readyTimeout === "number" && config.readyTimeout > 0 ? config.readyTimeout : undefined;
1040
+ const globalStopSignal = validateStopSignal(config.stopSignal);
1041
+ const globalErrorMatcher = validateErrorMatcher("(global)", config.errorMatcher);
1042
+ const globalWatch = validateStringOrStringArray(config.watch);
1010
1043
  let globalEnv;
1011
1044
  if (config.env && typeof config.env === "object") {
1012
1045
  for (const [k, v] of Object.entries(config.env)) {
@@ -1086,6 +1119,12 @@ function validateConfig(raw, warnings) {
1086
1119
  const processEnv = p.env && typeof p.env === "object" ? p.env : undefined;
1087
1120
  const processEnvFile = validateEnvFile(p.envFile);
1088
1121
  const showCommand = typeof p.showCommand === "boolean" ? p.showCommand : globalShowCommand ?? true;
1122
+ const platform = validatePlatform(name, p.platform);
1123
+ const processMaxRestarts = typeof p.maxRestarts === "number" && p.maxRestarts >= 0 ? p.maxRestarts : undefined;
1124
+ const processReadyTimeout = typeof p.readyTimeout === "number" && p.readyTimeout > 0 ? p.readyTimeout : undefined;
1125
+ const processStopSignal = validateStopSignal(p.stopSignal);
1126
+ const processErrorMatcher = validateErrorMatcher(name, p.errorMatcher);
1127
+ const processWatch = validateStringOrStringArray(p.watch);
1089
1128
  validated[name] = {
1090
1129
  command: p.command,
1091
1130
  cwd: processCwd ?? globalCwd,
@@ -1094,15 +1133,16 @@ function validateConfig(raw, warnings) {
1094
1133
  dependsOn: Array.isArray(p.dependsOn) ? p.dependsOn : undefined,
1095
1134
  readyPattern,
1096
1135
  persistent,
1097
- maxRestarts: typeof p.maxRestarts === "number" && p.maxRestarts >= 0 ? p.maxRestarts : undefined,
1098
- readyTimeout: typeof p.readyTimeout === "number" && p.readyTimeout > 0 ? p.readyTimeout : undefined,
1136
+ maxRestarts: processMaxRestarts ?? globalMaxRestarts,
1137
+ readyTimeout: processReadyTimeout ?? globalReadyTimeout,
1099
1138
  delay: typeof p.delay === "number" && p.delay > 0 ? p.delay : undefined,
1100
1139
  condition: typeof p.condition === "string" && p.condition.trim() ? p.condition.trim() : undefined,
1101
- stopSignal: validateStopSignal(p.stopSignal),
1140
+ platform,
1141
+ stopSignal: processStopSignal ?? globalStopSignal,
1102
1142
  color: typeof p.color === "string" ? p.color : Array.isArray(p.color) ? p.color : undefined,
1103
- watch: validateStringOrStringArray(p.watch),
1143
+ watch: processWatch ?? globalWatch,
1104
1144
  interactive: typeof p.interactive === "boolean" ? p.interactive : false,
1105
- errorMatcher: validateErrorMatcher(name, p.errorMatcher),
1145
+ errorMatcher: processErrorMatcher ?? globalErrorMatcher,
1106
1146
  showCommand
1107
1147
  };
1108
1148
  }
@@ -1136,6 +1176,19 @@ function validateErrorMatcher(name, value) {
1136
1176
  }
1137
1177
  return;
1138
1178
  }
1179
+ var VALID_PLATFORMS = new Set(["aix", "darwin", "freebsd", "linux", "openbsd", "sunos", "win32"]);
1180
+ function validatePlatform(name, value) {
1181
+ const arr = validateStringOrStringArray(value);
1182
+ if (arr === undefined)
1183
+ return;
1184
+ const values = typeof arr === "string" ? [arr] : arr;
1185
+ for (const v of values) {
1186
+ if (!VALID_PLATFORMS.has(v)) {
1187
+ throw new Error(`Process "${name}".platform "${v}" is not valid. Must be one of: ${[...VALID_PLATFORMS].join(", ")}`);
1188
+ }
1189
+ }
1190
+ return arr;
1191
+ }
1139
1192
  var VALID_STOP_SIGNALS = new Set(["SIGTERM", "SIGINT", "SIGHUP"]);
1140
1193
  function validateStopSignal(value) {
1141
1194
  if (typeof value === "string" && VALID_STOP_SIGNALS.has(value)) {
@@ -3302,6 +3355,7 @@ async function main() {
3302
3355
  const raw = expandScriptPatterns(await loadConfig(parsed.configPath));
3303
3356
  const warnings2 = [];
3304
3357
  let config2 = validateConfig(raw, warnings2);
3358
+ config2 = filterByPlatform(config2);
3305
3359
  if (parsed.only || parsed.exclude) {
3306
3360
  config2 = filterConfig(config2, parsed.only, parsed.exclude);
3307
3361
  }
@@ -3325,6 +3379,10 @@ async function main() {
3325
3379
  flags.push(`delay: ${proc.delay}ms`);
3326
3380
  if (proc.condition)
3327
3381
  flags.push(`if: ${proc.condition}`);
3382
+ if (proc.platform) {
3383
+ const p = Array.isArray(proc.platform) ? proc.platform : [proc.platform];
3384
+ flags.push(`platform: ${p.join(", ")}`);
3385
+ }
3328
3386
  if (proc.watch) {
3329
3387
  const patterns = Array.isArray(proc.watch) ? proc.watch : [proc.watch];
3330
3388
  flags.push(`watch: ${patterns.join(", ")}`);
@@ -3414,6 +3472,7 @@ async function main() {
3414
3472
  } else {
3415
3473
  const raw = expandScriptPatterns(await loadConfig(parsed.configPath));
3416
3474
  config = validateConfig(raw, warnings);
3475
+ config = filterByPlatform(config);
3417
3476
  if (parsed.noRestart) {
3418
3477
  for (const proc of Object.values(config.processes)) {
3419
3478
  proc.maxRestarts = 0;
package/dist/types.d.ts CHANGED
@@ -31,6 +31,8 @@ export interface NumuxProcessConfig<K extends string = string> {
31
31
  delay?: number;
32
32
  /** Env var name (prefix with `!` to negate); process skipped if condition is falsy */
33
33
  condition?: string;
34
+ /** OS(es) this process runs on (e.g. `'darwin'`, `'linux'`). Non-matching processes are removed, their dependents still start */
35
+ platform?: string | string[];
34
36
  /**
35
37
  * Signal for graceful stop
36
38
  * @default 'SIGTERM'
@@ -70,6 +72,22 @@ export interface NumuxConfig<K extends string = string> {
70
72
  * @default true
71
73
  */
72
74
  showCommand?: boolean;
75
+ /**
76
+ * Global restart limit, inherited by all processes
77
+ * @default Infinity
78
+ */
79
+ maxRestarts?: number;
80
+ /** Global ready timeout (ms), inherited by all processes */
81
+ readyTimeout?: number;
82
+ /**
83
+ * Global stop signal, inherited by all processes
84
+ * @default 'SIGTERM'
85
+ */
86
+ stopSignal?: 'SIGTERM' | 'SIGINT' | 'SIGHUP';
87
+ /** Global error matcher, inherited by all processes. `true` = detect ANSI red output, string = regex */
88
+ errorMatcher?: boolean | string;
89
+ /** Global watch patterns, inherited by processes without their own watch */
90
+ watch?: string | string[];
73
91
  processes: Record<K, NumuxProcessConfig<K> | NumuxScriptPattern<K> | string>;
74
92
  }
75
93
  /** Process config after validation — dependsOn is always normalized to an array */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "numux",
3
- "version": "1.19.0",
3
+ "version": "1.21.0",
4
4
  "description": "Terminal multiplexer with dependency orchestration",
5
5
  "type": "module",
6
6
  "license": "MIT",