Skip to content
 
 

Repository files navigation

mprocs

Nota sul fork. Questo è monade-mprocs, il fork di mprocs usato come libreria da monade-cli. Oltre a quello che fa mprocs, espone run_mprocs_with(RunOptions), un socket di controllo JSON per interrogare e pilotare i processi da fuori, e i log per processo su file. La numerazione delle versioni del fork è indipendente da quella di mprocs upstream.

mprocs runs multiple commands in parallel and shows output of each command separately.

When you work on a project you very often need the same list of commands to be running. For example: webpack serve, jest --watch, node src/server.js. With mprocs you can list these command in mprocs.yaml and run all of them by running mprocs. Then you can switch between outputs of running commands and interact with them.

It is similar to concurrently but mprocs shows output of each command separately and allows to interact with processes (you can even work in vim inside mprocs).

Screenshots

Installation

Packaging status

Download binary (Linux, Macos, Windows)

Download executable for your platform and put it into a directory included in PATH.

npm (Linux, Macos, Windows)

npm install -g mprocs
yarn global add mprocs

homebrew (Macos, Linux)

brew install mprocs

cargo (All platforms)

cargo install mprocs

scoop (Windows)

scoop install mprocs

AUR (Arch Linux)

yay mprocs
yay mprocs-bin

MPR (Debian/Ubuntu)

git clone 'https://mpr.makedeb.org/mprocs'
cd mprocs/
makedeb -si

Usage

  1. Run mprocs cmd1 cmd2 … (example: mprocs "yarn test -w" "webpack serve")

OR

  1. Create mprocs.yaml file
  2. Run mprocs command

Example mprocs.yaml:

procs:
  nvim:
    cmd: ["nvim"]
  server:
    shell: "nodemon server.js"
  webpack: "webpack serve"
  tests:
    shell: "jest -w"
    env:
      NODE_ENV: test

Config

There are two kinds of configs: global and local. Global config is loaded from ~/.config/mprocs/mprocs.yaml (or C:\Users\Alice\AppData\Roaming\mprocs\mprocs.yaml on Windows). Local config is loaded from mprocs.yaml from current directory (or set via cli argument: mprocs --config ./cfg/mprocs.yaml). Settings in the local config override settings the global.

  • procs: object - Processes to run. Only allowed in local config.
    • shell: string - Shell command to run (exactly one of shell or cmd must be provided).
    • cmd: array - Array of command and args to run (exactly one of shell or cmd must be provided).
    • cwd: string - Set working directory for the process. Prefix <CONFIG_DIR> will be replaced with the path of the directory where the config is located.
    • env: object<string, string|null> - Set env variables. Object keys are variable names. Assign variable to null, to clear variables inherited from parent process.
    • add_path: string|array - Add entries to the PATH environment variable.
    • autostart: bool - Start process when mprocs starts. Default: true.
    • stop: "SIGINT"|"SIGTERM"|"SIGKILL"|{send-keys: array}|"hard-kill" - A way to stop a process (using x key or when quitting mprocs).
  • hide_keymap_window: bool - Hide the pane at the bottom of the screen showing key bindings.
  • mouse_scroll_speed: integer - Number of lines to scrollper one mouse scroll.
  • proc_list_width: integer - Process list window width.
  • keymap_procs: object - Key bindings for process list. See Keymap.
  • keymap_term: object - Key bindings for terminal window. See Keymap.
  • keymap_copy: object - Key bindings for copy mode. See Keymap.

Keymap

Default key bindings can be overridden in config using keymap_procs, keymap_term, or keymap_copy fields. Available commands are documented in the Remote control section.

There are three keymap levels:

  • Default keymaps
  • ~/.config/mprocs/mprocs.yaml
  • ./mprocs.yaml (can be overridden by the -c/--config cli arg)

Lower levers override bindings from previous levels. Key bindings from previous levels can be cleared by specifying reset: true field at the same level as keys.

keymap_procs: # keymap when process list is focused
  <C-q>: { c: toggle-focus }
  <C-a>: null # unbind key
keymap_term: # keymap when terminal is focused
  reset: true
  <C-q>: { c: toggle-focus }
  <C-j>:
    c: batch
    cmds:
      - { c: focus-procs }
      - { c: next-proc }

$select operator

You can define different values depending on the current operating system. Any value in config can be wrapped with a $select operator. To provide different values based on current OS define an object with:

  • First field $select: os
  • Fields defining values for different OSes: macos: value. Possible values are listed here: https://doc.rust-lang.org/std/env/consts/constant.OS.html.
  • Field $else: default value will be matched if no value was defined for current OS. If current OS is not matched and field $else is missing, then mprocs will fail to load config.

Example mprocs.yaml:

procs:
  my processs:
    shell:
      $select: os
      windows: "echo %TEXT%"
      $else: "echo $TEXT"
    env:
      TEXT:
        $select: os
        windows: Windows
        linux: Linux
        macos: Macos
        freebsd: FreeBSD

Running scripts from package.json

If you run mprocs with an --npm argument, it will load scripts from package.json. But the scripts are not run by default, and you can launch desired scripts manually.

# Run mprocs with scripts from package.json
mprocs --npm

Default keymap

Process list focused:

  • q - Quit (soft kill processes and wait then to exit)
  • Q - Force quit (terminate processes)
  • C-a - Focus output pane
  • x - Soft kill selected process (send SIGTERM signal, hard kill on Windows)
  • X - Hard kill selected process (send SIGKILL)
  • s - Start selected process, if it is not running
  • r - Soft kill selected process and restart it when it stops
  • R - Hard kill selected process and restart it when it stops
  • a - Add new process
  • d - Remove selected process (process must be stopped first)
  • e - Rename selected process
  • k or - Select previous process
  • j or - Select next process
  • M-1 - M-8 - Select process 1-8
  • C-d or page down - Scroll output down
  • C-u or page up - Scroll output up
  • C-e - Scroll output down by 3 lines
  • C-y - Scroll output up by 3 lines
  • z - Zoom into terminal window
  • v - Enter copy mode

Process output focused:

  • C-a - Focus processes pane

Copy mode:

  • v - Start selecting end point
  • c - Copy selected text
  • Esc - Leave copy mode
  • C-a - Focus processes pane
  • C-d or page down - Scroll output down
  • C-u or page up - Scroll output up
  • C-e - Scroll output down by 3 lines
  • C-y - Scroll output up by 3 lines
  • h or - Move cursor up
  • l or - Move cursor right
  • j or - Move cursor down
  • h or - Move cursor left

Remote control

Optionally, mprocs can listen on TCP port for remote commands. You have to define remote control server address in mprocs.yaml (server: 127.0.0.1:4050) or via cli argument (mprocs --server 127.0.0.1:4050). To send a command to running mprocs instance use the ctl argument: mprocs --ctl '{c: quit}' or mprocs --ctl '{c: send-key, key: <C-c>}'.

Commands are encoded as yaml. Available commands:

  • {c: quit-or-ask} - Stop processes and quit. If any processes are running, show a confirmation dialog.
  • {c: quit} - Stop processes and quit. Does not show confirm dialog.
  • {c: force-quit}
  • {c: toggle-focus} - Toggle focus between process list and terminal.
  • {c: focus-procs} - Focus process list
  • {c: focus-term} - Focus process terminal window
  • {c: zoom} - Zoom into terminal window
  • {c: next-proc}
  • {c: prev-proc}
  • {c: select-proc, index: <PROCESS INDEX>} - Select process by index
  • {c: start-proc}
  • {c: term-proc}
  • {c: kill-proc}
  • {c: restart-proc}
  • {c: force-restart-proc}
  • {c: show-add-proc}
  • {c: add-proc, cmd: "<SHELL COMMAND>"}
  • {c: show-remove-proc}
  • {c: remove-proc, id: "<PROCESS ID>"}
  • {c: show-rename-proc}
  • {c: rename-proc, name: "<NEW_NAME>"} - Rename currently selected process
  • {c: scroll-down}
  • {c: scroll-up}
  • {c: scroll-down-lines, n: <COUNT>}
  • {c: scroll-up-lines, n: <COUNT>}
  • {c: copy-mode-enter} - Enter copy mode
  • {c: copy-mode-leave} - Leave copy mode
  • {c: copy-mode-move, dir: <DIRECTION> } - Move starting or ending position of the selection. Available directions: up/right/down/left.
  • {c: copy-mode-end} - Start selecting end point of the selection.
  • {c: copy-mode-copy} - Copy selected text to the clipboard and leave copy mode.
  • {c: send-key, key: "<KEY>"} - Send key to current process. Key examples: <C-a>, <Enter>
  • {c: batch, cmds: [{c: focus-procs}, …]} - Send multiple commands

Control socket

The remote control above is fire and forget: it sends a command and reads nothing back. For querying the state of the stack there is a second, request/response channel over a Unix domain socket (unix only, permissions 0600, no TCP). Enable it with mprocs --ctl-socket /tmp/mprocs.sock, or from the library:

mprocs::run_mprocs_with(mprocs::RunOptions {
  yaml_path: "mprocs.yaml".into(),
  ctl_socket: Some("/tmp/mprocs.sock".into()),
  log_dir: Some("/tmp/mprocs-logs".into()),
  ..Default::default()
}).await

The socket lives inside the mprocs process: close mprocs and it is gone. There is no daemon.

Messages are JSON, one object per line. On connect the server sends a hello line, then answers every request in order:

printf '{"type":"request","id":1,"method":"ls"}\n' | nc -U /tmp/mprocs.sock
printf '{"type":"request","id":2,"method":"screen","params":{"name":"api"}}\n' | nc -U /tmp/mprocs.sock
printf '{"type":"request","id":3,"method":"restart","params":{"pattern":"api"}}\n' | nc -U /tmp/mprocs.sock

A connection can carry several requests in a row, or you can open one per request, as the examples above do. params may be omitted when the method takes no arguments.

{"type":"hello","protocol":1,"app":"monade-mprocs 0.4.0","features":[]}
{"type":"response","id":1,"result":{"procs":[
  {"name":"api","id":1,"state":"running","pid":54321,"started_at":1753790000,
   "log_file":"/tmp/mprocs-logs/api.log"},
  {"name":"worker","id":2,"state":"exited","exit_code":1,"signal":null,
   "started_at":1753789000,"log_file":"/tmp/mprocs-logs/worker.log"},
  {"name":"mailhog","id":3,"state":"idle","log_file":null}
]}}
{"type":"response","id":2,"error":{"code":"no_match","message":"no proc named 'api'"}}

result and error are mutually exclusive. Error codes are unknown_method, invalid_params, no_match and internal. Clients must ignore fields they do not know: adding methods, fields or error codes does not bump protocol.

Methods:

  • ls - list the processes with their state (idle, running, exited, error), pid, exit code, signal, start time and log file. Takes an optional params.pattern.
  • screen - the visible screen of one process, as plain text. params.name is an exact name.
  • start, stop, restart, kill - act on every process matching params.pattern. They answer {"matched": n} immediately: stopping a process is asynchronous, so poll ls if you need confirmation.
  • shutdown - stop everything and quit.

pattern is an exact name or a single * used as a prefix, a suffix, or "everything" (api, web*, *worker, *). No regexes.

When log_dir is set (--log-dir), each process also tees its raw output, escape sequences included, to <log_dir>/<name>.log. The name is sanitized, so a process called weird name/with slash logs to weird_name_with_slash.log. Every run is preceded by a === mprocs: <name> started, pid=…, at=… === marker, and a file that grew past log_max_bytes (8 MiB by default) is truncated when the process restarts. Read long histories from there rather than through the protocol.

Two things to know:

  • Keep the socket path short. macOS caps a unix socket path at about 104 bytes and the bind fails past that.
  • mprocs waits for every process to go down before it quits, so a process ignoring SIGTERM makes q and shutdown hang. Use kill over the socket, or X in the TUI.

examples/mprocs.yaml is a playground covering every method and edge case, with the commands to copy in its comments.

The full specification is in docs/ctl-rpc/01-protocol.md.

FAQ

mprocs vs tmux/screen

mprocs is meant to make it easier to run specific commands that you end up running repeatedly, such as compilers and test runners. This is in contrast with tmux, which is usually used to run much more long-lived processes - usually a shell - in each window/pane. Another difference is that tmux runs a server and a client, which allows the client to detach and reattach later, keeping the processes running. mprocs is meant more for finite lifetime processes that you keep re-running, but when mprocs ends, so do the processes it is running within its windows.

Copying doesn't work in tmux

Tmux doesn't have escape sequences for copying enabled by default. To enable it add the following to ~/.tmux.conf:

set -g set-clipboard on

About

A simple library to wrap mprocs inside another rust project

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages