mirror of
https://github.com/coder/registry.git
synced 2026-06-02 20:48:14 +00:00
fix: change cmux npm package to mux (#533)
This commit is contained in:
|
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
|
||||
icon: ../../../../.icons/cmux.svg
|
||||
icon: ../../../../.icons/mux.svg
|
||||
verified: false
|
||||
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
|
||||
module "cmux" {
|
||||
module "mux" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/cmux/coder"
|
||||
version = "1.0.2"
|
||||
source = "registry.coder.com/coder/mux/coder"
|
||||
version = "1.0.0"
|
||||
agent_id = coder_agent.example.id
|
||||
}
|
||||
```
|
||||
@@ -22,8 +22,8 @@ module "cmux" {
|
||||
## Features
|
||||
|
||||
- **Parallel Agent Execution**: Run multiple AI agents simultaneously on different tasks
|
||||
- **Cmux Workspace Isolation**: Each agent works in its own isolated environment
|
||||
- **Git Divergence Visualization**: Track changes across different cmux agent workspaces
|
||||
- **Mux Workspace Isolation**: Each agent works in its own isolated environment
|
||||
- **Git Divergence Visualization**: Track changes across different mux agent workspaces
|
||||
- **Long-Running Processes**: Resume AI work after interruptions
|
||||
- **Cost Tracking**: Monitor API usage across agents
|
||||
|
||||
@@ -32,10 +32,10 @@ module "cmux" {
|
||||
### Basic Usage
|
||||
|
||||
```tf
|
||||
module "cmux" {
|
||||
module "mux" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/cmux/coder"
|
||||
version = "1.0.2"
|
||||
source = "registry.coder.com/coder/mux/coder"
|
||||
version = "1.0.0"
|
||||
agent_id = coder_agent.example.id
|
||||
}
|
||||
```
|
||||
@@ -43,10 +43,10 @@ module "cmux" {
|
||||
### Pin Version
|
||||
|
||||
```tf
|
||||
module "cmux" {
|
||||
module "mux" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/cmux/coder"
|
||||
version = "1.0.2"
|
||||
source = "registry.coder.com/coder/mux/coder"
|
||||
version = "1.0.0"
|
||||
agent_id = coder_agent.example.id
|
||||
# Default is "latest"; set to a specific version to pin
|
||||
install_version = "0.4.0"
|
||||
@@ -56,10 +56,10 @@ module "cmux" {
|
||||
### Custom Port
|
||||
|
||||
```tf
|
||||
module "cmux" {
|
||||
module "mux" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/cmux/coder"
|
||||
version = "1.0.2"
|
||||
source = "registry.coder.com/coder/mux/coder"
|
||||
version = "1.0.0"
|
||||
agent_id = coder_agent.example.id
|
||||
port = 8080
|
||||
}
|
||||
@@ -67,13 +67,13 @@ module "cmux" {
|
||||
|
||||
### 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
|
||||
module "cmux" {
|
||||
module "mux" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/cmux/coder"
|
||||
version = "1.0.2"
|
||||
source = "registry.coder.com/coder/mux/coder"
|
||||
version = "1.0.0"
|
||||
agent_id = coder_agent.example.id
|
||||
use_cached = true
|
||||
}
|
||||
@@ -81,13 +81,13 @@ module "cmux" {
|
||||
|
||||
### 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
|
||||
module "cmux" {
|
||||
module "mux" {
|
||||
count = data.coder_workspace.me.start_count
|
||||
source = "registry.coder.com/coder/cmux/coder"
|
||||
version = "1.0.2"
|
||||
source = "registry.coder.com/coder/mux/coder"
|
||||
version = "1.0.0"
|
||||
agent_id = coder_agent.example.id
|
||||
install = false
|
||||
}
|
||||
@@ -99,6 +99,6 @@ module "cmux" {
|
||||
|
||||
## 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)
|
||||
- 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,
|
||||
} from "~test";
|
||||
|
||||
describe("cmux", async () => {
|
||||
describe("mux", async () => {
|
||||
await runTerraformInit(import.meta.dir);
|
||||
|
||||
testRequiredVariables(import.meta.dir, {
|
||||
@@ -31,9 +31,9 @@ describe("cmux", async () => {
|
||||
expect(output.exitCode).toBe(0);
|
||||
const expectedLines = [
|
||||
"📥 npm not found; downloading tarball from npm registry...",
|
||||
"🥳 cmux has been installed in /tmp/cmux",
|
||||
"🚀 Starting cmux server on port 4000...",
|
||||
"Check logs at /tmp/cmux.log!",
|
||||
"🥳 mux has been installed in /tmp/mux",
|
||||
"🚀 Starting mux server on port 4000...",
|
||||
"Check logs at /tmp/mux.log!",
|
||||
];
|
||||
for (const line of expectedLines) {
|
||||
expect(output.stdout).toContain(line);
|
||||
@@ -54,10 +54,10 @@ describe("cmux", async () => {
|
||||
|
||||
expect(output.exitCode).toBe(0);
|
||||
const expectedLines = [
|
||||
"📦 Installing @coder/cmux via npm into /tmp/cmux...",
|
||||
"🥳 cmux has been installed in /tmp/cmux",
|
||||
"🚀 Starting cmux server on port 4000...",
|
||||
"Check logs at /tmp/cmux.log!",
|
||||
"📦 Installing mux via npm into /tmp/mux...",
|
||||
"🥳 mux has been installed in /tmp/mux",
|
||||
"🚀 Starting mux server on port 4000...",
|
||||
"Check logs at /tmp/mux.log!",
|
||||
];
|
||||
for (const line of expectedLines) {
|
||||
expect(output.stdout).toContain(line);
|
||||
@@ -17,38 +17,44 @@ variable "agent_id" {
|
||||
|
||||
variable "port" {
|
||||
type = number
|
||||
description = "The port to run cmux on."
|
||||
description = "The port to run mux on."
|
||||
default = 4000
|
||||
}
|
||||
|
||||
variable "display_name" {
|
||||
type = string
|
||||
description = "The display name for the cmux application."
|
||||
default = "cmux"
|
||||
description = "The display name for the mux application."
|
||||
default = "mux"
|
||||
}
|
||||
|
||||
variable "slug" {
|
||||
type = string
|
||||
description = "The slug for the cmux application."
|
||||
default = "cmux"
|
||||
description = "The slug for the mux application."
|
||||
default = "mux"
|
||||
}
|
||||
|
||||
variable "install_prefix" {
|
||||
type = string
|
||||
description = "The prefix to install cmux to."
|
||||
default = "/tmp/cmux"
|
||||
description = "The prefix to install mux to."
|
||||
default = "/tmp/mux"
|
||||
}
|
||||
|
||||
variable "log_path" {
|
||||
type = string
|
||||
description = "The path for cmux logs."
|
||||
default = "/tmp/cmux.log"
|
||||
description = "The path for mux logs."
|
||||
default = "/tmp/mux.log"
|
||||
}
|
||||
|
||||
variable "add-project" {
|
||||
type = string
|
||||
description = "Path to add/open as a project in mux (idempotent)."
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "install_version" {
|
||||
type = string
|
||||
description = "The version of cmux to install."
|
||||
default = "latest"
|
||||
description = "The version or dist-tag of mux to install."
|
||||
default = "next"
|
||||
}
|
||||
|
||||
variable "share" {
|
||||
@@ -74,13 +80,13 @@ variable "group" {
|
||||
|
||||
variable "install" {
|
||||
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
|
||||
}
|
||||
|
||||
variable "use_cached" {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -107,14 +113,15 @@ variable "open_in" {
|
||||
}
|
||||
}
|
||||
|
||||
resource "coder_script" "cmux" {
|
||||
resource "coder_script" "mux" {
|
||||
agent_id = var.agent_id
|
||||
display_name = "cmux"
|
||||
icon = "/icon/cmux.svg"
|
||||
display_name = "mux"
|
||||
icon = "/icon/mux.svg"
|
||||
script = templatefile("${path.module}/run.sh", {
|
||||
VERSION : var.install_version,
|
||||
PORT : var.port,
|
||||
LOG_PATH : var.log_path,
|
||||
ADD_PROJECT : var.add-project,
|
||||
INSTALL_PREFIX : var.install_prefix,
|
||||
OFFLINE : !var.install,
|
||||
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
|
||||
slug = var.slug
|
||||
display_name = var.display_name
|
||||
url = "http://localhost:${var.port}"
|
||||
icon = "/icon/cmux.svg"
|
||||
icon = "/icon/mux.svg"
|
||||
subdomain = var.subdomain
|
||||
share = var.share
|
||||
order = var.order
|
||||
@@ -147,3 +154,5 @@ resource "coder_app" "cmux" {
|
||||
threshold = 6
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -16,7 +16,7 @@ run "install_false_and_use_cached_conflict" {
|
||||
}
|
||||
|
||||
expect_failures = [
|
||||
resource.coder_script.cmux
|
||||
resource.coder_script.mux
|
||||
]
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ run "custom_port" {
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -62,3 +62,5 @@ run "use_cached_only_success" {
|
||||
use_cached = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user