Compare commits

...

3 Commits

Author SHA1 Message Date
blinkagent[bot] 6ec506e9b6 fix(dotfiles): allow tilde (~) in git repository URLs (#763)
## Description

The URL validation regex in the dotfiles module was rejecting URLs
containing tilde (`~`) characters, which are commonly used in Bitbucket
Server for user repositories (e.g.
`ssh://git@bitbucket.example.org:7999/~username/repo.git`).

This adds `~` to the allowed character set in all three validation
regexes (for `default_dotfiles_uri`, `dotfiles_uri`, and the
`coder_parameter` validation).

## Type of Change

- [ ] New module
- [ ] New template
- [x] Bug fix
- [ ] Feature/enhancement
- [ ] Documentation
- [ ] Other

## Module Information

**Path:** `registry/coder/modules/dotfiles`  
**New version:** `v1.3.1`  
**Breaking change:** [ ] Yes [x] No

## Testing & Validation

- [x] Tests pass (`bun test`)
- [x] Code formatted (`bun fmt`)
- [ ] Changes tested locally

## Related Issues

Fixes #762

Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
2026-02-26 13:30:20 -06:00
Michael Suchacz b794b1edd9 feat(mux): add package_manager and registry_url variables (#761)
## Summary

Add two new customization variables to the Mux module so users can
control how Mux is installed:

### `package_manager` (default: `"auto"`)

Choose which Node package manager installs Mux:

- **`auto`** (default) — auto-detects `npm` → `pnpm` → `bun` in order,
falling back to a direct tarball download when none is available
- **`npm`**, **`pnpm`**, **`bun`** — force a specific package manager
(fails if not found on PATH)

### `registry_url` (default: `"https://registry.npmjs.org"`)

Override the npm registry URL for private registries or mirrors. All
previously hardcoded `registry.npmjs.org` references have been replaced
with this variable. The `--registry` flag is passed to whichever package
manager is used, and the tarball fallback path also uses it.

## Changes

| File | What changed |
|---|---|
| `main.tf` | Added `package_manager` and `registry_url` variables with
validation; pass both to template |
| `run.sh` | Rewrote install logic: PM auto-detection loop,
`case`/`esac` dispatch with PM-specific flags, replaced all hardcoded
registry URLs with `${REGISTRY_URL}` |
| `mux.tftest.hcl` | Added 6 new test cases: PM selection
(npm/pnpm/bun), invalid PM validation, custom registry URL,
trailing-slash stripping |
| `main.test.ts` | Updated expected log messages to match new generic
wording |
| `README.md` | Updated description, added Custom Package Manager and
Custom Registry examples, updated Notes section |

## Version

Bumped **1.2.0 → 1.3.0** (minor: new backward-compatible features).

## Validation

-  `terraform validate` — clean
-  `terraform test` — **15 passed, 0 failed**
-  `terraform fmt` — clean

---

Generated with [Mux](https://mux.coder.com) using Claude
2026-02-26 16:40:40 +01:00
Michael Suchacz 94e41d3780 Add arbitrary mux server command argument parsing (#738)
## Summary
- add a new `additional_arguments` module variable to pass extra
arguments to `mux server`
- parse `additional_arguments` in `run.sh` with quoted-group support so
values like paths with spaces are preserved
- keep existing `add-project` behavior while allowing additional
arbitrary flags
- add Terraform and Bun tests covering `additional_arguments` behavior
- document the new option in the module README and bump example version
references to `1.2.0`

## Why
The module previously only supported the `add-project` flag. This change
lets users pass additional `mux server` arguments without waiting for
new module variables.

## Validation
- `shellcheck --severity=warning --format=gcc
registry/coder/modules/mux/run.sh`
- `terraform -chdir=registry/coder/modules/mux test -verbose`
- `bun test registry/coder/modules/mux/main.test.ts`

## Breaking changes
None.

---
Generated with Mux (exec agent) using GPT-5.
2026-02-25 18:15:33 +00:00
8 changed files with 317 additions and 30 deletions
+6 -6
View File
@@ -18,7 +18,7 @@ Under the hood, this module uses the [coder dotfiles](https://coder.com/docs/v2/
module "dotfiles" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/dotfiles/coder"
version = "1.3.0"
version = "1.3.1"
agent_id = coder_agent.example.id
}
```
@@ -31,7 +31,7 @@ module "dotfiles" {
module "dotfiles" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/dotfiles/coder"
version = "1.3.0"
version = "1.3.1"
agent_id = coder_agent.example.id
}
```
@@ -42,7 +42,7 @@ module "dotfiles" {
module "dotfiles" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/dotfiles/coder"
version = "1.3.0"
version = "1.3.1"
agent_id = coder_agent.example.id
user = "root"
}
@@ -54,14 +54,14 @@ module "dotfiles" {
module "dotfiles" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/dotfiles/coder"
version = "1.3.0"
version = "1.3.1"
agent_id = coder_agent.example.id
}
module "dotfiles-root" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/dotfiles/coder"
version = "1.3.0"
version = "1.3.1"
agent_id = coder_agent.example.id
user = "root"
dotfiles_uri = module.dotfiles.dotfiles_uri
@@ -76,7 +76,7 @@ You can set a default dotfiles repository for all users by setting the `default_
module "dotfiles" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/dotfiles/coder"
version = "1.3.0"
version = "1.3.1"
agent_id = coder_agent.example.id
default_dotfiles_uri = "https://github.com/coder/dotfiles"
}
@@ -26,6 +26,7 @@ describe("dotfiles", async () => {
"git@github.com:coder/dotfiles.git",
"git://github.com/coder/dotfiles.git",
"ssh://git@github.com/coder/dotfiles.git",
"ssh://git@bitbucket.example.org:7999/~myusername/dotfiles.git",
];
for (const url of validUrls) {
const state = await runTerraformApply(import.meta.dir, {
+3 -3
View File
@@ -40,7 +40,7 @@ variable "default_dotfiles_uri" {
validation {
condition = (
var.default_dotfiles_uri == "" ||
can(regex("^(https?://|ssh://|git@|git://)[a-zA-Z0-9._/:@-]+$", var.default_dotfiles_uri))
can(regex("^(https?://|ssh://|git@|git://)[a-zA-Z0-9._/:@~-]+$", var.default_dotfiles_uri))
)
error_message = "Must be a valid dotfiles repository URL (https, git@, or git://) without special characters."
}
@@ -55,7 +55,7 @@ variable "dotfiles_uri" {
condition = (
var.dotfiles_uri == null ||
var.dotfiles_uri == "" ||
can(regex("^(https?://|ssh://|git@|git://)[a-zA-Z0-9._/:@-]+$", var.dotfiles_uri))
can(regex("^(https?://|ssh://|git@|git://)[a-zA-Z0-9._/:@~-]+$", var.dotfiles_uri))
)
error_message = "Must be a valid dotfiles repository URL (https, git@, or git://) without special characters."
}
@@ -102,7 +102,7 @@ data "coder_parameter" "dotfiles_uri" {
icon = "/icon/dotfiles.svg"
validation {
regex = "^$|^(https?://|ssh://|git@|git://)[a-zA-Z0-9._/:@-]+$"
regex = "^$|^(https?://|ssh://|git@|git://)[a-zA-Z0-9._/:@~-]+$"
error = "Must be a valid dotfiles repository URL (https, git@, or git://) without special characters."
}
}
+54 -9
View File
@@ -8,13 +8,13 @@ tags: [ai, agents, development, multiplexer]
# Mux
Automatically install and run [Mux](https://github.com/coder/mux) in a Coder workspace. By default, the module installs `mux@next` from npm (with a fallback to downloading the npm tarball if npm is unavailable). Mux is a desktop application for parallel agentic development that enables developers to run multiple AI agents simultaneously across isolated workspaces.
Automatically install and run [Mux](https://github.com/coder/mux) in a Coder workspace. By default, the module auto-detects an available package manager (`npm`, `pnpm`, or `bun`) to install `mux@next` (with a fallback to downloading the npm tarball if none is found). You can also force a specific package manager via `package_manager` and point to a custom registry with `registry_url`. Mux is a desktop application for parallel agentic development that enables developers to run multiple AI agents simultaneously across isolated workspaces.
```tf
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.1.0"
version = "1.3.0"
agent_id = coder_agent.main.id
}
```
@@ -37,7 +37,7 @@ module "mux" {
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.1.0"
version = "1.3.0"
agent_id = coder_agent.main.id
}
```
@@ -48,7 +48,7 @@ module "mux" {
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.1.0"
version = "1.3.0"
agent_id = coder_agent.main.id
# Default is "latest"; set to a specific version to pin
install_version = "0.4.0"
@@ -63,24 +63,67 @@ Start Mux with `mux server --add-project /path/to/project`:
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.1.0"
version = "1.3.0"
agent_id = coder_agent.main.id
add-project = "/path/to/project"
}
```
### Pass Arbitrary `mux server` Arguments
Use `additional_arguments` to append additional arguments to `mux server`.
The module parses quoted values, so grouped arguments remain intact.
```tf
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.3.0"
agent_id = coder_agent.main.id
additional_arguments = "--open-mode pinned --add-project '/workspaces/my repo'"
}
```
### Custom Port
```tf
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.1.0"
version = "1.3.0"
agent_id = coder_agent.main.id
port = 8080
}
```
### Custom Package Manager
Force a specific package manager instead of auto-detection:
```tf
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.3.0"
agent_id = coder_agent.main.id
package_manager = "pnpm" # or "npm", "bun"
}
```
### Custom Registry
Use a private or mirrored npm registry:
```tf
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.3.0"
agent_id = coder_agent.main.id
registry_url = "https://npm.pkg.github.com"
}
```
### Use Cached Installation
Run an existing copy of Mux if found, otherwise install from npm:
@@ -89,7 +132,7 @@ Run an existing copy of Mux if found, otherwise install from npm:
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.1.0"
version = "1.3.0"
agent_id = coder_agent.main.id
use_cached = true
}
@@ -103,7 +146,7 @@ Run without installing from the network (requires Mux to be pre-installed):
module "mux" {
count = data.coder_workspace.me.start_count
source = "registry.coder.com/coder/mux/coder"
version = "1.1.0"
version = "1.3.0"
agent_id = coder_agent.main.id
install = false
}
@@ -117,4 +160,6 @@ module "mux" {
- Mux is currently in preview and you may encounter bugs
- Requires internet connectivity for agent operations (unless `install` is set to false)
- Installs `mux@next` from npm by default (falls back to the npm tarball if npm is unavailable)
- Auto-detects `npm`, `pnpm`, or `bun` by default; set `package_manager` to force a specific one
- Installs `mux@next` from the npm registry by default; set `registry_url` to use a private or mirrored registry
- Falls back to a direct tarball download when no package manager is found
+58 -2
View File
@@ -1,6 +1,11 @@
import { describe, expect, it } from "bun:test";
import {
executeScriptInContainer,
execContainer,
findResourceInstance,
readFileContainer,
removeContainer,
runContainer,
runTerraformApply,
runTerraformInit,
testRequiredVariables,
@@ -30,7 +35,7 @@ describe("mux", async () => {
}
expect(output.exitCode).toBe(0);
const expectedLines = [
"📥 npm not found; downloading tarball from npm registry...",
"📥 No package manager found; downloading tarball from registry...",
"🥳 mux has been installed in /tmp/mux",
"🚀 Starting mux server on port 4000...",
"Check logs at /tmp/mux.log!",
@@ -40,6 +45,57 @@ describe("mux", async () => {
}
}, 60000);
it("parses custom additional_arguments", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
install: false,
log_path: "/tmp/mux.log",
additional_arguments:
"--open-mode pinned --add-project '/workspaces/my repo'",
});
const instance = findResourceInstance(state, "coder_script");
const id = await runContainer("alpine/curl");
try {
const setup = await execContainer(id, [
"sh",
"-c",
`apk add --no-cache bash >/dev/null
mkdir -p /tmp/mux
cat <<'EOF' > /tmp/mux/mux
#!/usr/bin/env sh
i=1
for arg in "$@"; do
echo "arg$i=$arg"
i=$((i + 1))
done
EOF
chmod +x /tmp/mux/mux`,
]);
expect(setup.exitCode).toBe(0);
const output = await execContainer(id, ["sh", "-c", instance.script]);
if (output.exitCode !== 0) {
console.log("STDOUT:\n" + output.stdout);
console.log("STDERR:\n" + output.stderr);
}
expect(output.exitCode).toBe(0);
await execContainer(id, ["sh", "-c", "sleep 1"]);
const log = await readFileContainer(id, "/tmp/mux.log");
expect(log).toContain("arg1=server");
expect(log).toContain("arg2=--port");
expect(log).toContain("arg3=4000");
expect(log).toContain("arg4=--open-mode");
expect(log).toContain("arg5=pinned");
expect(log).toContain("arg6=--add-project");
expect(log).toContain("arg7=/workspaces/my repo");
} finally {
await removeContainer(id);
}
}, 60000);
it("runs with npm present", async () => {
const state = await runTerraformApply(import.meta.dir, {
agent_id: "foo",
@@ -55,7 +111,7 @@ describe("mux", async () => {
expect(output.exitCode).toBe(0);
const expectedLines = [
"📦 Installing mux via npm into /tmp/mux...",
"⏭️ Skipping npm lifecycle scripts with --ignore-scripts",
"⏭️ Skipping lifecycle scripts with --ignore-scripts",
"🥳 mux has been installed in /tmp/mux",
"🚀 Starting mux server on port 4000...",
"Check logs at /tmp/mux.log!",
+27
View File
@@ -55,12 +55,35 @@ variable "add-project" {
default = null
}
variable "additional_arguments" {
type = string
description = "Additional command-line arguments to pass to `mux server` (for example: `--add-project /path --open-mode pinned`)."
default = ""
}
variable "install_version" {
type = string
description = "The version or dist-tag of Mux to install."
default = "next"
}
variable "package_manager" {
type = string
description = "Package manager to install Mux. 'auto' detects npm, pnpm, or bun (falling back to tarball download). Set to 'npm', 'pnpm', or 'bun' to force a specific one."
default = "auto"
validation {
condition = contains(["auto", "npm", "pnpm", "bun"], var.package_manager)
error_message = "The 'package_manager' variable must be one of: 'auto', 'npm', 'pnpm', 'bun'."
}
}
variable "registry_url" {
type = string
description = "The npm-compatible registry URL to install Mux from. Override this for private registries or mirrors."
default = "https://registry.npmjs.org"
}
variable "share" {
type = string
default = "owner"
@@ -131,6 +154,7 @@ resource "random_password" "mux_auth_token" {
locals {
mux_auth_token = random_password.mux_auth_token.result
registry_url = trimsuffix(var.registry_url, "/")
}
resource "coder_script" "mux" {
@@ -142,10 +166,13 @@ resource "coder_script" "mux" {
PORT : var.port,
LOG_PATH : var.log_path,
ADD_PROJECT : var.add-project == null ? "" : var.add-project,
ADDITIONAL_ARGUMENTS : var.additional_arguments,
INSTALL_PREFIX : var.install_prefix,
OFFLINE : !var.install,
USE_CACHED : var.use_cached,
AUTH_TOKEN : local.mux_auth_token,
PACKAGE_MANAGER : var.package_manager,
REGISTRY_URL : local.registry_url,
})
run_on_start = true
+107
View File
@@ -79,6 +79,20 @@ run "auth_token_in_url" {
}
}
run "custom_additional_arguments" {
command = plan
variables {
agent_id = "foo"
additional_arguments = "--open-mode pinned --add-project '/workspaces/my repo'"
}
assert {
condition = strcontains(resource.coder_script.mux.script, "--open-mode pinned --add-project '/workspaces/my repo'")
error_message = "mux launch script must include the configured additional arguments"
}
}
run "custom_version" {
command = plan
@@ -107,3 +121,96 @@ run "use_cached_only_success" {
use_cached = true
}
}
# Custom package_manager should appear in generated script
run "custom_package_manager_npm" {
command = plan
variables {
agent_id = "foo"
package_manager = "npm"
}
assert {
condition = strcontains(resource.coder_script.mux.script, "PM_CMD=\"npm\"")
error_message = "mux script must set PM_CMD to the configured package manager"
}
}
run "custom_package_manager_pnpm" {
command = plan
variables {
agent_id = "foo"
package_manager = "pnpm"
}
assert {
condition = strcontains(resource.coder_script.mux.script, "PM_CMD=\"pnpm\"")
error_message = "mux script must set PM_CMD to the configured package manager"
}
}
run "custom_package_manager_bun" {
command = plan
variables {
agent_id = "foo"
package_manager = "bun"
}
assert {
condition = strcontains(resource.coder_script.mux.script, "PM_CMD=\"bun\"")
error_message = "mux script must set PM_CMD to the configured package manager"
}
}
# Invalid package_manager should fail validation
run "invalid_package_manager" {
command = plan
variables {
agent_id = "foo"
package_manager = "yarn"
}
expect_failures = [
var.package_manager
]
}
# Custom registry_url should appear in generated script
run "custom_registry_url" {
command = plan
variables {
agent_id = "foo"
registry_url = "https://npm.example.com"
}
assert {
condition = strcontains(resource.coder_script.mux.script, "https://npm.example.com")
error_message = "mux script must use the configured registry URL"
}
assert {
condition = !strcontains(resource.coder_script.mux.script, "registry.npmjs.org")
error_message = "mux script must not contain hardcoded registry.npmjs.org when custom registry is set"
}
}
# registry_url trailing slash should be stripped
run "registry_url_trailing_slash" {
command = plan
variables {
agent_id = "foo"
registry_url = "https://npm.example.com/"
}
assert {
condition = strcontains(resource.coder_script.mux.script, "https://npm.example.com/mux/")
error_message = "registry URL trailing slash must be stripped to avoid double slashes"
}
}
+61 -10
View File
@@ -20,6 +20,22 @@ function run_mux() {
if [ -n "${ADD_PROJECT}" ]; then
set -- "$@" --add-project "${ADD_PROJECT}"
fi
# Parse additional user-supplied server arguments while preserving quoted groups.
if [ -n "${ADDITIONAL_ARGUMENTS}" ]; then
local parsed_additional_arguments
if ! parsed_additional_arguments="$(printf "%s\n" "${ADDITIONAL_ARGUMENTS}" | xargs -n1 printf "%s\n" 2> /dev/null)"; then
echo "❌ Failed to parse additional_arguments. Ensure quotes are balanced."
exit 1
fi
while IFS= read -r parsed_arg; do
[ -n "$parsed_arg" ] || continue
set -- "$@" "$parsed_arg"
done << EOF
$${parsed_additional_arguments}
EOF
fi
echo "🚀 Starting mux server on port $port_value..."
echo "Check logs at ${LOG_PATH}!"
MUX_SERVER_AUTH_TOKEN="$auth_token_value" PORT="$port_value" "$MUX_BINARY" "$@" > "${LOG_PATH}" 2>&1 &
@@ -38,7 +54,7 @@ fi
# If there is no cached install OR we don't want to use a cached install
if [ ! -f "$MUX_BINARY" ] || [ "${USE_CACHED}" != true ]; then
printf "$${BOLD}Installing mux from npm...\n"
printf "$${BOLD}Installing mux...\n"
# Clean up from other install (in case install prefix changed).
if [ -n "$CODER_SCRIPT_BIN_DIR" ] && [ -e "$CODER_SCRIPT_BIN_DIR/mux" ]; then
@@ -47,41 +63,76 @@ if [ ! -f "$MUX_BINARY" ] || [ "${USE_CACHED}" != true ]; then
mkdir -p "$(dirname "$MUX_BINARY")"
if command -v npm > /dev/null 2>&1; then
echo "📦 Installing mux via npm into ${INSTALL_PREFIX}..."
# Determine which package manager to use
PM_CMD=""
if [ "${PACKAGE_MANAGER}" = "auto" ]; then
for pm in npm pnpm bun; do
if command -v "$pm" > /dev/null 2>&1; then
PM_CMD="$pm"
break
fi
done
else
PM_CMD="${PACKAGE_MANAGER}"
if ! command -v "$PM_CMD" > /dev/null 2>&1; then
echo "❌ Configured package manager '${PACKAGE_MANAGER}' not found on PATH"
exit 1
fi
fi
if [ -n "$PM_CMD" ]; then
echo "📦 Installing mux via $PM_CMD into ${INSTALL_PREFIX}..."
NPM_WORKDIR="${INSTALL_PREFIX}/npm"
mkdir -p "$NPM_WORKDIR"
cd "$NPM_WORKDIR" || exit 1
if [ ! -f package.json ]; then
echo '{}' > package.json
fi
echo "⏭️ Skipping npm lifecycle scripts with --ignore-scripts"
echo "⏭️ Skipping lifecycle scripts with --ignore-scripts"
PKG="mux"
if [ -z "${VERSION}" ] || [ "${VERSION}" = "latest" ]; then
PKG_SPEC="$PKG@latest"
else
PKG_SPEC="$PKG@${VERSION}"
fi
if ! npm install --no-audit --no-fund --omit=dev --ignore-scripts "$PKG_SPEC"; then
echo "❌ Failed to install mux via npm"
INSTALL_OK=true
case "$PM_CMD" in
npm)
if ! npm install --no-audit --no-fund --omit=dev --ignore-scripts --registry "${REGISTRY_URL}" "$PKG_SPEC"; then
INSTALL_OK=false
fi
;;
pnpm)
if ! pnpm add --ignore-scripts --registry "${REGISTRY_URL}" "$PKG_SPEC"; then
INSTALL_OK=false
fi
;;
bun)
if ! bun add --ignore-scripts --registry "${REGISTRY_URL}" "$PKG_SPEC"; then
INSTALL_OK=false
fi
;;
esac
if [ "$INSTALL_OK" != true ]; then
echo "❌ Failed to install mux via $PM_CMD"
exit 1
fi
# Determine the installed binary path
BIN_DIR="$NPM_WORKDIR/node_modules/.bin"
CANDIDATE="$BIN_DIR/mux"
if [ ! -f "$CANDIDATE" ]; then
echo "❌ Could not locate mux binary after npm install"
echo "❌ Could not locate mux binary after $PM_CMD install"
exit 1
fi
chmod +x "$CANDIDATE" || true
ln -sf "$CANDIDATE" "$MUX_BINARY"
else
echo "📥 npm not found; downloading tarball from npm registry..."
echo "📥 No package manager found; downloading tarball from registry..."
VERSION_TO_USE="${VERSION}"
if [ -z "$VERSION_TO_USE" ]; then
VERSION_TO_USE="next"
fi
META_URL="https://registry.npmjs.org/mux/$VERSION_TO_USE"
META_URL="${REGISTRY_URL}/mux/$VERSION_TO_USE"
META_JSON="$(curl -fsSL "$META_URL" || true)"
if [ -z "$META_JSON" ]; then
echo "❌ Failed to fetch npm metadata: $META_URL"
@@ -120,7 +171,7 @@ if [ ! -f "$MUX_BINARY" ] || [ "${USE_CACHED}" != true ]; then
echo "❌ Could not determine version for mux"
exit 1
fi
TARBALL_URL="https://registry.npmjs.org/mux/-/mux-$VERSION_TO_USE.tgz"
TARBALL_URL="${REGISTRY_URL}/mux/-/mux-$VERSION_TO_USE.tgz"
fi
TMP_DIR="$(mktemp -d)"
TAR_PATH="$TMP_DIR/mux.tgz"