fix: change cmux npm package to mux (#533)
This commit is contained in:
parent
f304201b6f
commit
0e3263fd6f
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@ -1,135 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
BOLD='\033[0;1m'
|
|
||||||
RESET='\033[0m'
|
|
||||||
CMUX_BINARY="${INSTALL_PREFIX}/cmux"
|
|
||||||
|
|
||||||
function run_cmux() {
|
|
||||||
local port_value
|
|
||||||
port_value="${PORT}"
|
|
||||||
if [ -z "$port_value" ]; then
|
|
||||||
port_value="4000"
|
|
||||||
fi
|
|
||||||
echo "🚀 Starting cmux server on port $port_value..."
|
|
||||||
echo "Check logs at ${LOG_PATH}!"
|
|
||||||
PORT="$port_value" "$CMUX_BINARY" server --port "$port_value" > "${LOG_PATH}" 2>&1 &
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if cmux is already installed for offline mode
|
|
||||||
if [ "${OFFLINE}" = true ]; then
|
|
||||||
if [ -f "$CMUX_BINARY" ]; then
|
|
||||||
echo "🥳 Found a copy of cmux"
|
|
||||||
run_cmux
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
echo "❌ Failed to find a copy of cmux"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If there is no cached install OR we don't want to use a cached install
|
|
||||||
if [ ! -f "$CMUX_BINARY" ] || [ "${USE_CACHED}" != true ]; then
|
|
||||||
printf "$${BOLD}Installing cmux from npm...\n"
|
|
||||||
|
|
||||||
# Clean up from other install (in case install prefix changed).
|
|
||||||
if [ -n "$CODER_SCRIPT_BIN_DIR" ] && [ -e "$CODER_SCRIPT_BIN_DIR/cmux" ]; then
|
|
||||||
rm "$CODER_SCRIPT_BIN_DIR/cmux"
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p "$(dirname "$CMUX_BINARY")"
|
|
||||||
|
|
||||||
if command -v npm > /dev/null 2>&1; then
|
|
||||||
echo "📦 Installing @coder/cmux via npm 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
|
|
||||||
PKG="@coder/cmux"
|
|
||||||
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 "$PKG_SPEC"; then
|
|
||||||
echo "❌ Failed to install @coder/cmux via npm"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# Determine the installed binary path
|
|
||||||
BIN_DIR="$NPM_WORKDIR/node_modules/.bin"
|
|
||||||
CANDIDATE="$BIN_DIR/cmux"
|
|
||||||
if [ ! -f "$CANDIDATE" ]; then
|
|
||||||
echo "❌ Could not locate cmux binary after npm install"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
chmod +x "$CANDIDATE" || true
|
|
||||||
ln -sf "$CANDIDATE" "$CMUX_BINARY"
|
|
||||||
else
|
|
||||||
echo "📥 npm not found; downloading tarball from npm registry..."
|
|
||||||
VERSION_TO_USE="${VERSION}"
|
|
||||||
if [ -z "$VERSION_TO_USE" ] || [ "$VERSION_TO_USE" = "latest" ]; then
|
|
||||||
# Try to determine the latest version
|
|
||||||
META_URL="https://registry.npmjs.org/@coder/cmux/latest"
|
|
||||||
VERSION_TO_USE="$(curl -fsSL "$META_URL" | sed -n 's/.*"version":"\([^"]*\)".*/\1/p' | head -n1)"
|
|
||||||
if [ -z "$VERSION_TO_USE" ]; then
|
|
||||||
echo "❌ Could not determine latest version for @coder/cmux"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
TARBALL_URL="https://registry.npmjs.org/@coder/cmux/-/cmux-$VERSION_TO_USE.tgz"
|
|
||||||
TMP_DIR="$(mktemp -d)"
|
|
||||||
TAR_PATH="$TMP_DIR/cmux.tgz"
|
|
||||||
if ! curl -fsSL "$TARBALL_URL" -o "$TAR_PATH"; then
|
|
||||||
echo "❌ Failed to download tarball: $TARBALL_URL"
|
|
||||||
rm -rf "$TMP_DIR"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if ! tar -xzf "$TAR_PATH" -C "$TMP_DIR"; then
|
|
||||||
echo "❌ Failed to extract tarball"
|
|
||||||
rm -rf "$TMP_DIR"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
CANDIDATE=""
|
|
||||||
# Common locations
|
|
||||||
if [ -f "$TMP_DIR/package/bin/cmux" ]; then
|
|
||||||
CANDIDATE="$TMP_DIR/package/bin/cmux"
|
|
||||||
elif [ -f "$TMP_DIR/package/bin/cmux.js" ]; then
|
|
||||||
CANDIDATE="$TMP_DIR/package/bin/cmux.js"
|
|
||||||
elif [ -f "$TMP_DIR/package/bin/cmux.mjs" ]; then
|
|
||||||
CANDIDATE="$TMP_DIR/package/bin/cmux.mjs"
|
|
||||||
else
|
|
||||||
# Try to read package.json bin field
|
|
||||||
if [ -f "$TMP_DIR/package/package.json" ]; then
|
|
||||||
BIN_PATH=$(sed -n 's/.*"bin"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$TMP_DIR/package/package.json" | head -n1)
|
|
||||||
if [ -z "$BIN_PATH" ]; then
|
|
||||||
BIN_PATH=$(sed -n '/"bin"[[:space:]]*:[[:space:]]*{/,/}/p' "$TMP_DIR/package/package.json" | sed -n 's/.*"cmux"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n1)
|
|
||||||
fi
|
|
||||||
if [ -n "$BIN_PATH" ] && [ -f "$TMP_DIR/package/$BIN_PATH" ]; then
|
|
||||||
CANDIDATE="$TMP_DIR/package/$BIN_PATH"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# Fallback: search for plausible filenames
|
|
||||||
if [ -z "$CANDIDATE" ] || [ ! -f "$CANDIDATE" ]; then
|
|
||||||
CANDIDATE=$(find "$TMP_DIR/package" -maxdepth 4 -type f \( -name "cmux" -o -name "cmux.js" -o -name "cmux.mjs" -o -name "cmux.cjs" \) | head -n1)
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [ -z "$CANDIDATE" ] || [ ! -f "$CANDIDATE" ]; then
|
|
||||||
echo "❌ Could not locate cmux binary in tarball"
|
|
||||||
rm -rf "$TMP_DIR"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
cp "$CANDIDATE" "$CMUX_BINARY"
|
|
||||||
chmod +x "$CMUX_BINARY" || true
|
|
||||||
rm -rf "$TMP_DIR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
printf "🥳 cmux has been installed in ${INSTALL_PREFIX}\n\n"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Make cmux available in PATH if CODER_SCRIPT_BIN_DIR is set
|
|
||||||
if [ -n "$CODER_SCRIPT_BIN_DIR" ] && [ ! -e "$CODER_SCRIPT_BIN_DIR/cmux" ]; then
|
|
||||||
ln -s "$CMUX_BINARY" "$CODER_SCRIPT_BIN_DIR/cmux"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Start cmux
|
|
||||||
run_cmux
|
|
||||||
@ -1,20 +1,20 @@
|
|||||||
---
|
---
|
||||||
display_name: cmux
|
display_name: mux
|
||||||
description: Coding Agent Multiplexer - Run multiple AI agents in parallel
|
description: Coding Agent Multiplexer - Run multiple AI agents in parallel
|
||||||
icon: ../../../../.icons/cmux.svg
|
icon: ../../../../.icons/mux.svg
|
||||||
verified: false
|
verified: false
|
||||||
tags: [ai, agents, development, multiplexer]
|
tags: [ai, agents, development, multiplexer]
|
||||||
---
|
---
|
||||||
|
|
||||||
# cmux
|
# mux
|
||||||
|
|
||||||
Automatically install and run [cmux](https://github.com/coder/cmux) in a Coder workspace. By default, the module installs `@coder/cmux@latest` from npm (with a fallback to downloading the npm tarball if npm is unavailable). cmux is a desktop application for parallel agentic development that enables developers to run multiple AI agents simultaneously across isolated cmux workspaces.
|
Automatically install and run 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.
|
||||||
|
|
||||||
```tf
|
```tf
|
||||||
module "cmux" {
|
module "mux" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/coder/cmux/coder"
|
source = "registry.coder.com/coder/mux/coder"
|
||||||
version = "1.0.2"
|
version = "1.0.0"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -22,8 +22,8 @@ module "cmux" {
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Parallel Agent Execution**: Run multiple AI agents simultaneously on different tasks
|
- **Parallel Agent Execution**: Run multiple AI agents simultaneously on different tasks
|
||||||
- **Cmux Workspace Isolation**: Each agent works in its own isolated environment
|
- **Mux Workspace Isolation**: Each agent works in its own isolated environment
|
||||||
- **Git Divergence Visualization**: Track changes across different cmux agent workspaces
|
- **Git Divergence Visualization**: Track changes across different mux agent workspaces
|
||||||
- **Long-Running Processes**: Resume AI work after interruptions
|
- **Long-Running Processes**: Resume AI work after interruptions
|
||||||
- **Cost Tracking**: Monitor API usage across agents
|
- **Cost Tracking**: Monitor API usage across agents
|
||||||
|
|
||||||
@ -32,10 +32,10 @@ module "cmux" {
|
|||||||
### Basic Usage
|
### Basic Usage
|
||||||
|
|
||||||
```tf
|
```tf
|
||||||
module "cmux" {
|
module "mux" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/coder/cmux/coder"
|
source = "registry.coder.com/coder/mux/coder"
|
||||||
version = "1.0.2"
|
version = "1.0.0"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -43,10 +43,10 @@ module "cmux" {
|
|||||||
### Pin Version
|
### Pin Version
|
||||||
|
|
||||||
```tf
|
```tf
|
||||||
module "cmux" {
|
module "mux" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/coder/cmux/coder"
|
source = "registry.coder.com/coder/mux/coder"
|
||||||
version = "1.0.2"
|
version = "1.0.0"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
# Default is "latest"; set to a specific version to pin
|
# Default is "latest"; set to a specific version to pin
|
||||||
install_version = "0.4.0"
|
install_version = "0.4.0"
|
||||||
@ -56,10 +56,10 @@ module "cmux" {
|
|||||||
### Custom Port
|
### Custom Port
|
||||||
|
|
||||||
```tf
|
```tf
|
||||||
module "cmux" {
|
module "mux" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/coder/cmux/coder"
|
source = "registry.coder.com/coder/mux/coder"
|
||||||
version = "1.0.2"
|
version = "1.0.0"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
port = 8080
|
port = 8080
|
||||||
}
|
}
|
||||||
@ -67,13 +67,13 @@ module "cmux" {
|
|||||||
|
|
||||||
### Use Cached Installation
|
### Use Cached Installation
|
||||||
|
|
||||||
Run an existing copy of cmux if found, otherwise install from npm:
|
Run an existing copy of mux if found, otherwise install from npm:
|
||||||
|
|
||||||
```tf
|
```tf
|
||||||
module "cmux" {
|
module "mux" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/coder/cmux/coder"
|
source = "registry.coder.com/coder/mux/coder"
|
||||||
version = "1.0.2"
|
version = "1.0.0"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
use_cached = true
|
use_cached = true
|
||||||
}
|
}
|
||||||
@ -81,13 +81,13 @@ module "cmux" {
|
|||||||
|
|
||||||
### Skip Install
|
### Skip Install
|
||||||
|
|
||||||
Run without installing from the network (requires cmux to be pre-installed):
|
Run without installing from the network (requires mux to be pre-installed):
|
||||||
|
|
||||||
```tf
|
```tf
|
||||||
module "cmux" {
|
module "mux" {
|
||||||
count = data.coder_workspace.me.start_count
|
count = data.coder_workspace.me.start_count
|
||||||
source = "registry.coder.com/coder/cmux/coder"
|
source = "registry.coder.com/coder/mux/coder"
|
||||||
version = "1.0.2"
|
version = "1.0.0"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
install = false
|
install = false
|
||||||
}
|
}
|
||||||
@ -99,6 +99,6 @@ module "cmux" {
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
- cmux is currently in preview and you may encounter bugs
|
- mux is currently in preview and you may encounter bugs
|
||||||
- Requires internet connectivity for agent operations (unless `install` is set to false)
|
- Requires internet connectivity for agent operations (unless `install` is set to false)
|
||||||
- Installs `@coder/cmux` from npm by default (falls back to the npm tarball if npm is unavailable)
|
- Installs `mux@next` from npm by default (falls back to the npm tarball if npm is unavailable)
|
||||||
@ -6,7 +6,7 @@ import {
|
|||||||
testRequiredVariables,
|
testRequiredVariables,
|
||||||
} from "~test";
|
} from "~test";
|
||||||
|
|
||||||
describe("cmux", async () => {
|
describe("mux", async () => {
|
||||||
await runTerraformInit(import.meta.dir);
|
await runTerraformInit(import.meta.dir);
|
||||||
|
|
||||||
testRequiredVariables(import.meta.dir, {
|
testRequiredVariables(import.meta.dir, {
|
||||||
@ -31,9 +31,9 @@ describe("cmux", async () => {
|
|||||||
expect(output.exitCode).toBe(0);
|
expect(output.exitCode).toBe(0);
|
||||||
const expectedLines = [
|
const expectedLines = [
|
||||||
"📥 npm not found; downloading tarball from npm registry...",
|
"📥 npm not found; downloading tarball from npm registry...",
|
||||||
"🥳 cmux has been installed in /tmp/cmux",
|
"🥳 mux has been installed in /tmp/mux",
|
||||||
"🚀 Starting cmux server on port 4000...",
|
"🚀 Starting mux server on port 4000...",
|
||||||
"Check logs at /tmp/cmux.log!",
|
"Check logs at /tmp/mux.log!",
|
||||||
];
|
];
|
||||||
for (const line of expectedLines) {
|
for (const line of expectedLines) {
|
||||||
expect(output.stdout).toContain(line);
|
expect(output.stdout).toContain(line);
|
||||||
@ -54,10 +54,10 @@ describe("cmux", async () => {
|
|||||||
|
|
||||||
expect(output.exitCode).toBe(0);
|
expect(output.exitCode).toBe(0);
|
||||||
const expectedLines = [
|
const expectedLines = [
|
||||||
"📦 Installing @coder/cmux via npm into /tmp/cmux...",
|
"📦 Installing mux via npm into /tmp/mux...",
|
||||||
"🥳 cmux has been installed in /tmp/cmux",
|
"🥳 mux has been installed in /tmp/mux",
|
||||||
"🚀 Starting cmux server on port 4000...",
|
"🚀 Starting mux server on port 4000...",
|
||||||
"Check logs at /tmp/cmux.log!",
|
"Check logs at /tmp/mux.log!",
|
||||||
];
|
];
|
||||||
for (const line of expectedLines) {
|
for (const line of expectedLines) {
|
||||||
expect(output.stdout).toContain(line);
|
expect(output.stdout).toContain(line);
|
||||||
@ -17,38 +17,44 @@ variable "agent_id" {
|
|||||||
|
|
||||||
variable "port" {
|
variable "port" {
|
||||||
type = number
|
type = number
|
||||||
description = "The port to run cmux on."
|
description = "The port to run mux on."
|
||||||
default = 4000
|
default = 4000
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "display_name" {
|
variable "display_name" {
|
||||||
type = string
|
type = string
|
||||||
description = "The display name for the cmux application."
|
description = "The display name for the mux application."
|
||||||
default = "cmux"
|
default = "mux"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "slug" {
|
variable "slug" {
|
||||||
type = string
|
type = string
|
||||||
description = "The slug for the cmux application."
|
description = "The slug for the mux application."
|
||||||
default = "cmux"
|
default = "mux"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "install_prefix" {
|
variable "install_prefix" {
|
||||||
type = string
|
type = string
|
||||||
description = "The prefix to install cmux to."
|
description = "The prefix to install mux to."
|
||||||
default = "/tmp/cmux"
|
default = "/tmp/mux"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "log_path" {
|
variable "log_path" {
|
||||||
type = string
|
type = string
|
||||||
description = "The path for cmux logs."
|
description = "The path for mux logs."
|
||||||
default = "/tmp/cmux.log"
|
default = "/tmp/mux.log"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "add-project" {
|
||||||
|
type = string
|
||||||
|
description = "Path to add/open as a project in mux (idempotent)."
|
||||||
|
default = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "install_version" {
|
variable "install_version" {
|
||||||
type = string
|
type = string
|
||||||
description = "The version of cmux to install."
|
description = "The version or dist-tag of mux to install."
|
||||||
default = "latest"
|
default = "next"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "share" {
|
variable "share" {
|
||||||
@ -74,13 +80,13 @@ variable "group" {
|
|||||||
|
|
||||||
variable "install" {
|
variable "install" {
|
||||||
type = bool
|
type = bool
|
||||||
description = "Install cmux from the network (npm or tarball). If false, run without installing (requires a pre-installed cmux)."
|
description = "Install mux from the network (npm or tarball). If false, run without installing (requires a pre-installed mux)."
|
||||||
default = true
|
default = true
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "use_cached" {
|
variable "use_cached" {
|
||||||
type = bool
|
type = bool
|
||||||
description = "Use cached copy of cmux if present; otherwise install from npm"
|
description = "Use cached copy of mux if present; otherwise install from npm"
|
||||||
default = false
|
default = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,14 +113,15 @@ variable "open_in" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "coder_script" "cmux" {
|
resource "coder_script" "mux" {
|
||||||
agent_id = var.agent_id
|
agent_id = var.agent_id
|
||||||
display_name = "cmux"
|
display_name = "mux"
|
||||||
icon = "/icon/cmux.svg"
|
icon = "/icon/mux.svg"
|
||||||
script = templatefile("${path.module}/run.sh", {
|
script = templatefile("${path.module}/run.sh", {
|
||||||
VERSION : var.install_version,
|
VERSION : var.install_version,
|
||||||
PORT : var.port,
|
PORT : var.port,
|
||||||
LOG_PATH : var.log_path,
|
LOG_PATH : var.log_path,
|
||||||
|
ADD_PROJECT : var.add-project,
|
||||||
INSTALL_PREFIX : var.install_prefix,
|
INSTALL_PREFIX : var.install_prefix,
|
||||||
OFFLINE : !var.install,
|
OFFLINE : !var.install,
|
||||||
USE_CACHED : var.use_cached,
|
USE_CACHED : var.use_cached,
|
||||||
@ -129,12 +136,12 @@ resource "coder_script" "cmux" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "coder_app" "cmux" {
|
resource "coder_app" "mux" {
|
||||||
agent_id = var.agent_id
|
agent_id = var.agent_id
|
||||||
slug = var.slug
|
slug = var.slug
|
||||||
display_name = var.display_name
|
display_name = var.display_name
|
||||||
url = "http://localhost:${var.port}"
|
url = "http://localhost:${var.port}"
|
||||||
icon = "/icon/cmux.svg"
|
icon = "/icon/mux.svg"
|
||||||
subdomain = var.subdomain
|
subdomain = var.subdomain
|
||||||
share = var.share
|
share = var.share
|
||||||
order = var.order
|
order = var.order
|
||||||
@ -147,3 +154,5 @@ resource "coder_app" "cmux" {
|
|||||||
threshold = 6
|
threshold = 6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ run "install_false_and_use_cached_conflict" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expect_failures = [
|
expect_failures = [
|
||||||
resource.coder_script.cmux
|
resource.coder_script.mux
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ run "custom_port" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert {
|
assert {
|
||||||
condition = resource.coder_app.cmux.url == "http://localhost:8080"
|
condition = resource.coder_app.mux.url == "http://localhost:8080"
|
||||||
error_message = "coder_app URL must use the configured port"
|
error_message = "coder_app URL must use the configured port"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -62,3 +62,5 @@ run "use_cached_only_success" {
|
|||||||
use_cached = true
|
use_cached = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
195
registry/coder/modules/mux/run.sh
Normal file
195
registry/coder/modules/mux/run.sh
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
BOLD='\033[0;1m'
|
||||||
|
RESET='\033[0m'
|
||||||
|
MUX_BINARY="${INSTALL_PREFIX}/mux"
|
||||||
|
|
||||||
|
function run_mux() {
|
||||||
|
local port_value
|
||||||
|
port_value="${PORT}"
|
||||||
|
if [ -z "$port_value" ]; then
|
||||||
|
port_value="4000"
|
||||||
|
fi
|
||||||
|
# Build args for mux (POSIX-compatible, avoid bash arrays)
|
||||||
|
set -- server --port "$port_value"
|
||||||
|
if [ -n "${ADD_PROJECT}" ]; then
|
||||||
|
set -- "$@" --add-project "${ADD_PROJECT}"
|
||||||
|
fi
|
||||||
|
echo "🚀 Starting mux server on port $port_value..."
|
||||||
|
echo "Check logs at ${LOG_PATH}!"
|
||||||
|
PORT="$port_value" "$MUX_BINARY" "$@" > "${LOG_PATH}" 2>&1 &
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if mux is already installed for offline mode
|
||||||
|
if [ "${OFFLINE}" = true ]; then
|
||||||
|
if [ -f "$MUX_BINARY" ]; then
|
||||||
|
echo "🥳 Found a copy of mux"
|
||||||
|
run_mux
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "❌ Failed to find a copy of mux"
|
||||||
|
exit 1
|
||||||
|
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"
|
||||||
|
|
||||||
|
# Clean up from other install (in case install prefix changed).
|
||||||
|
if [ -n "$CODER_SCRIPT_BIN_DIR" ] && [ -e "$CODER_SCRIPT_BIN_DIR/mux" ]; then
|
||||||
|
rm "$CODER_SCRIPT_BIN_DIR/mux"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$MUX_BINARY")"
|
||||||
|
|
||||||
|
if command -v npm > /dev/null 2>&1; then
|
||||||
|
echo "📦 Installing mux via npm 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
|
||||||
|
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 "$PKG_SPEC"; then
|
||||||
|
echo "❌ Failed to install mux via npm"
|
||||||
|
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"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
chmod +x "$CANDIDATE" || true
|
||||||
|
ln -sf "$CANDIDATE" "$MUX_BINARY"
|
||||||
|
else
|
||||||
|
echo "📥 npm not found; downloading tarball from npm 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_JSON="$(curl -fsSL "$META_URL" || true)"
|
||||||
|
if [ -z "$META_JSON" ]; then
|
||||||
|
echo "❌ Failed to fetch npm metadata: $META_URL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Normalize JSON to a single line for robust pattern matching across environments
|
||||||
|
META_ONE_LINE="$(printf "%s" "$META_JSON" | tr -d '\n' || true)"
|
||||||
|
if [ -z "$META_ONE_LINE" ]; then
|
||||||
|
META_ONE_LINE="$META_JSON"
|
||||||
|
fi
|
||||||
|
# Try to extract tarball URL directly from metadata (prefer Node if available for robust JSON parsing)
|
||||||
|
TARBALL_URL=""
|
||||||
|
if command -v node > /dev/null 2>&1; then
|
||||||
|
TARBALL_URL="$(printf "%s" "$META_JSON" | node -e 'try{const fs=require("fs");const data=JSON.parse(fs.readFileSync(0,"utf8"));if(data&&data.dist&&data.dist.tarball){console.log(data.dist.tarball);}}catch(e){}')"
|
||||||
|
fi
|
||||||
|
# sed-based fallback
|
||||||
|
if [ -z "$TARBALL_URL" ]; then
|
||||||
|
TARBALL_URL="$(printf "%s" "$META_ONE_LINE" | sed -n 's/.*\"tarball\":\"\\([^\"]*\\)\".*/\\1/p' | head -n1)"
|
||||||
|
fi
|
||||||
|
# Fallback: resolve version then construct tarball URL
|
||||||
|
if [ -z "$TARBALL_URL" ]; then
|
||||||
|
RESOLVED_VERSION=""
|
||||||
|
if command -v node > /dev/null 2>&1; then
|
||||||
|
RESOLVED_VERSION="$(printf "%s" "$META_JSON" | node -e 'try{const fs=require("fs");const data=JSON.parse(fs.readFileSync(0,"utf8"));if(data&&data.version){console.log(data.version);}}catch(e){}')"
|
||||||
|
fi
|
||||||
|
if [ -z "$RESOLVED_VERSION" ]; then
|
||||||
|
RESOLVED_VERSION="$(printf "%s" "$META_ONE_LINE" | sed -n 's/.*\"version\":\"\\([^\"]*\\)\".*/\\1/p' | head -n1)"
|
||||||
|
fi
|
||||||
|
if [ -z "$RESOLVED_VERSION" ]; then
|
||||||
|
RESOLVED_VERSION="$(printf "%s" "$META_ONE_LINE" | grep -o '\"version\":\"[^\"]*\"' | head -n1 | cut -d '\"' -f4)"
|
||||||
|
fi
|
||||||
|
if [ -n "$RESOLVED_VERSION" ]; then
|
||||||
|
VERSION_TO_USE="$RESOLVED_VERSION"
|
||||||
|
fi
|
||||||
|
if [ -z "$VERSION_TO_USE" ]; then
|
||||||
|
echo "❌ Could not determine version for mux"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
TARBALL_URL="https://registry.npmjs.org/mux/-/mux-$VERSION_TO_USE.tgz"
|
||||||
|
fi
|
||||||
|
TMP_DIR="$(mktemp -d)"
|
||||||
|
TAR_PATH="$TMP_DIR/mux.tgz"
|
||||||
|
if ! curl -fsSL "$TARBALL_URL" -o "$TAR_PATH"; then
|
||||||
|
echo "❌ Failed to download tarball: $TARBALL_URL"
|
||||||
|
rm -rf "$TMP_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! tar -xzf "$TAR_PATH" -C "$TMP_DIR"; then
|
||||||
|
echo "❌ Failed to extract tarball"
|
||||||
|
rm -rf "$TMP_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
CANDIDATE=""
|
||||||
|
BIN_PATH=""
|
||||||
|
# Prefer reading bin path from package.json
|
||||||
|
if [ -f "$TMP_DIR/package/package.json" ]; then
|
||||||
|
if command -v node > /dev/null 2>&1; then
|
||||||
|
BIN_PATH="$(node -e 'try{const fs=require("fs");const p=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));let bp=typeof p.bin==="string"?p.bin:(p.bin&&p.bin.mux);if(bp){console.log(bp)}}catch(e){}' "$TMP_DIR/package/package.json")"
|
||||||
|
fi
|
||||||
|
if [ -z "$BIN_PATH" ]; then
|
||||||
|
# sed fallbacks (handle both string and object forms)
|
||||||
|
BIN_PATH=$(sed -n 's/.*\"bin\"[[:space:]]*:[[:space:]]*\"\\([^\"]*\\)\".*/\\1/p' "$TMP_DIR/package/package.json" | head -n1)
|
||||||
|
if [ -z "$BIN_PATH" ]; then
|
||||||
|
BIN_PATH=$(sed -n '/\"bin\"[[:space:]]*:[[:space:]]*{/,/}/p' "$TMP_DIR/package/package.json" | sed -n 's/.*\"mux\"[[:space:]]*:[[:space:]]*\"\\([^\"]*\\)\".*/\\1/p' | head -n1)
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ -n "$BIN_PATH" ] && [ -f "$TMP_DIR/package/$BIN_PATH" ]; then
|
||||||
|
CANDIDATE="$TMP_DIR/package/$BIN_PATH"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# Fallback: check common locations
|
||||||
|
if [ -z "$CANDIDATE" ]; then
|
||||||
|
if [ -f "$TMP_DIR/package/bin/mux" ]; then
|
||||||
|
CANDIDATE="$TMP_DIR/package/bin/mux"
|
||||||
|
elif [ -f "$TMP_DIR/package/bin/mux.js" ]; then
|
||||||
|
CANDIDATE="$TMP_DIR/package/bin/mux.js"
|
||||||
|
elif [ -f "$TMP_DIR/package/bin/mux.mjs" ]; then
|
||||||
|
CANDIDATE="$TMP_DIR/package/bin/mux.mjs"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# Fallback: search for plausible filenames
|
||||||
|
if [ -z "$CANDIDATE" ] || [ ! -f "$CANDIDATE" ]; then
|
||||||
|
CANDIDATE=$(find "$TMP_DIR/package" -maxdepth 4 -type f \( -name "mux" -o -name "mux.js" -o -name "mux.mjs" -o -name "mux.cjs" -o -name "main.js" \) | head -n1)
|
||||||
|
fi
|
||||||
|
if [ -z "$CANDIDATE" ] || [ ! -f "$CANDIDATE" ]; then
|
||||||
|
echo "❌ Could not locate mux binary in tarball"
|
||||||
|
rm -rf "$TMP_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Copy entire package to installation directory to preserve relative imports
|
||||||
|
DEST_DIR="${INSTALL_PREFIX}/.mux-package"
|
||||||
|
rm -rf "$DEST_DIR"
|
||||||
|
mkdir -p "$DEST_DIR"
|
||||||
|
cp -R "$TMP_DIR/package/." "$DEST_DIR/"
|
||||||
|
# Create/refresh launcher symlink
|
||||||
|
if [ -n "$BIN_PATH" ] && [ -f "$DEST_DIR/$BIN_PATH" ]; then
|
||||||
|
ln -sf "$DEST_DIR/$BIN_PATH" "$MUX_BINARY"
|
||||||
|
chmod +x "$DEST_DIR/$BIN_PATH" || true
|
||||||
|
else
|
||||||
|
ln -sf "$DEST_DIR/$(basename "$CANDIDATE")" "$MUX_BINARY"
|
||||||
|
chmod +x "$DEST_DIR/$(basename "$CANDIDATE")" || true
|
||||||
|
fi
|
||||||
|
rm -rf "$TMP_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "🥳 mux has been installed in ${INSTALL_PREFIX}\n\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make mux available in PATH if CODER_SCRIPT_BIN_DIR is set
|
||||||
|
if [ -n "$CODER_SCRIPT_BIN_DIR" ]; then
|
||||||
|
if [ ! -e "$CODER_SCRIPT_BIN_DIR/mux" ]; then
|
||||||
|
ln -s "$MUX_BINARY" "$CODER_SCRIPT_BIN_DIR/mux"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start mux
|
||||||
|
run_mux
|
||||||
Loading…
x
Reference in New Issue
Block a user