From f04d7d2808bc56e67a8b5ae32385b5d132dea496 Mon Sep 17 00:00:00 2001
From: 35C4n0r <70096901+35C4n0r@users.noreply.github.com>
Date: Fri, 25 Jul 2025 07:01:57 +0530
Subject: [PATCH] feat: tmux module (#229)
Closes #203
/claim #203
## Description
Introduce the `tmux` module
## Demo
https://www.loom.com/share/ec8169d34c3043f7af2163b1a1a14a4b?sid=1ea8bcb2-3db0-43ca-965a-5ed42eec3448
## Type of Change
- [x] New module
- [ ] Bug fix
- [ ] Feature/enhancement
- [ ] Documentation
- [ ] Other
## Module Information
**Path:** `registry/anomaly/modules/tmux`
**New version:** `v1.0.0`
**Breaking change:** [ ] Yes [x] No
## Testing & Validation
- [x] Tests pass (`bun test`)
- [x] Code formatted (`bun run fmt`)
- [x] Changes tested locally
## Related Issues
#203
---
.gitignore | 3 +
.icons/tmux.svg | 1 +
registry/anomaly/.images/avatar.jpeg | Bin 0 -> 9064 bytes
registry/anomaly/README.md | 13 ++
registry/anomaly/modules/tmux/README.md | 101 ++++++++++++
registry/anomaly/modules/tmux/main.test.ts | 35 ++++
registry/anomaly/modules/tmux/main.tf | 78 +++++++++
registry/anomaly/modules/tmux/scripts/run.sh | 153 ++++++++++++++++++
.../anomaly/modules/tmux/scripts/start.sh | 37 +++++
9 files changed, 421 insertions(+)
create mode 100644 .icons/tmux.svg
create mode 100644 registry/anomaly/.images/avatar.jpeg
create mode 100644 registry/anomaly/README.md
create mode 100644 registry/anomaly/modules/tmux/README.md
create mode 100644 registry/anomaly/modules/tmux/main.test.ts
create mode 100644 registry/anomaly/modules/tmux/main.tf
create mode 100755 registry/anomaly/modules/tmux/scripts/run.sh
create mode 100755 registry/anomaly/modules/tmux/scripts/start.sh
diff --git a/.gitignore b/.gitignore
index 157c642a..55947fc5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -145,3 +145,6 @@ dist
# Generated credentials from google-github-actions/auth
gha-creds-*.json
+
+# IDEs
+.idea
diff --git a/.icons/tmux.svg b/.icons/tmux.svg
new file mode 100644
index 00000000..ac0174ed
--- /dev/null
+++ b/.icons/tmux.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/registry/anomaly/.images/avatar.jpeg b/registry/anomaly/.images/avatar.jpeg
new file mode 100644
index 0000000000000000000000000000000000000000..ca1072d8dce0807e1688f16be0e3bddfa5d29ef7
GIT binary patch
literal 9064
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
l9dT%i2A|0;Sq=#28V*I|C+s1@fCkAKn&{
zf(^~X7at!)X;i%pZ`2&yn_?UexGy!t$tLq$AKEm2p>B={4ptF<4A>CGxO0u>Mq&FZ
zDbhwcb^wy*$}v?9=$LrTzjqTE&-o|UW|POdDR=zo0@cBD`}f-n(V#`RYJ`Xa$4XHb
zL4ha%gXGV|AVfI^VGhYq$)HFS#EEA4+6>WU{^4j9q2Pd~+uYDgZXGi<3~w^I*vViu
zWgKreCnj(xosXtLY&4zb!L^Hj9I&f`|D~PZ+8b!Ikn!f~PoEv7{aL2ZmEv{D#$n&+
z)g|`a$sZnsbSPi$=$y4QW){GtYJlT`(9^W@1Fj>YvKH5adqn7kJF$;#qJWJd}zD@3vt;N#{@q*{AV`d$d=M~PMwpd
zH{AmBN7e_2k8Ut=076OGFlbSv|I-wH=Wgrv?1!Zk^Xb%WTFcmrC60V1GaZl00^lhIv
z3r3a&4u5#}^Og14z0GbE&i2>?`J;0j0^G9Wmy8K2q [!IMPORTANT]
+>
+> - If you provide a custom `tmux_config`, it will completely replace the default configuration. Ensure you include plugin
+> and TPM initialization lines if you want plugin support and session persistence.
+> - The script will attempt to install dependencies using `sudo` where required.
+> - If `git` is not installed, TPM installation will fail.
+> - If you are using custom config, you'll be responsible for setting up persistence and plugins.
+> - The `order`, `group`, and `icon` variables allow you to customize how tmux apps appear in the Coder UI.
+> - In case of session restart or shh reconnection, the tmux session will be automatically restored :)
diff --git a/registry/anomaly/modules/tmux/main.test.ts b/registry/anomaly/modules/tmux/main.test.ts
new file mode 100644
index 00000000..802147db
--- /dev/null
+++ b/registry/anomaly/modules/tmux/main.test.ts
@@ -0,0 +1,35 @@
+import { describe, it, expect } from "bun:test";
+import {
+ runTerraformApply,
+ runTerraformInit,
+ testRequiredVariables,
+ findResourceInstance,
+} from "~test";
+import path from "path";
+
+const moduleDir = path.resolve(__dirname);
+
+const requiredVars = {
+ agent_id: "dummy-agent-id",
+};
+
+describe("tmux module", async () => {
+ await runTerraformInit(moduleDir);
+
+ // 1. Required variables
+ testRequiredVariables(moduleDir, requiredVars);
+
+ // 2. coder_script resource is created
+ it("creates coder_script resource", async () => {
+ const state = await runTerraformApply(moduleDir, requiredVars);
+ const scriptResource = findResourceInstance(state, "coder_script");
+ expect(scriptResource).toBeDefined();
+ expect(scriptResource.agent_id).toBe(requiredVars.agent_id);
+
+ // check that the script contains expected lines
+ expect(scriptResource.script).toContain("Installing tmux");
+ expect(scriptResource.script).toContain("Installing Tmux Plugin Manager (TPM)");
+ expect(scriptResource.script).toContain("tmux configuration created at");
+ expect(scriptResource.script).toContain("✅ tmux setup complete!");
+ });
+});
diff --git a/registry/anomaly/modules/tmux/main.tf b/registry/anomaly/modules/tmux/main.tf
new file mode 100644
index 00000000..36f8471f
--- /dev/null
+++ b/registry/anomaly/modules/tmux/main.tf
@@ -0,0 +1,78 @@
+terraform {
+ required_version = ">= 1.0"
+
+ required_providers {
+ coder = {
+ source = "coder/coder"
+ version = ">= 2.5"
+ }
+ }
+}
+
+variable "agent_id" {
+ type = string
+ description = "The ID of a Coder agent."
+}
+
+variable "tmux_config" {
+ type = string
+ description = "Custom tmux configuration to apply."
+ default = ""
+}
+
+variable "save_interval" {
+ type = number
+ description = "Save interval (in minutes)."
+ default = 1
+}
+
+variable "order" {
+ type = number
+ description = "The order determines the position of app in the UI presentation. The lowest order is shown first and apps with equal order are sorted by name (ascending order)."
+ default = null
+}
+
+variable "group" {
+ type = string
+ description = "The name of a group that this app belongs to."
+ default = null
+}
+
+variable "icon" {
+ type = string
+ description = "The icon to use for the app."
+ default = "/icon/tmux.svg"
+}
+
+variable "sessions" {
+ type = list(string)
+ description = "List of tmux sessions to create or start."
+ default = ["default"]
+}
+
+resource "coder_script" "tmux" {
+ agent_id = var.agent_id
+ display_name = "tmux"
+ icon = "/icon/terminal.svg"
+ script = templatefile("${path.module}/scripts/run.sh", {
+ TMUX_CONFIG = var.tmux_config
+ SAVE_INTERVAL = var.save_interval
+ })
+ run_on_start = true
+ run_on_stop = false
+}
+
+resource "coder_app" "tmux_sessions" {
+ for_each = toset(var.sessions)
+
+ agent_id = var.agent_id
+ slug = "tmux-${each.value}"
+ display_name = "tmux - ${each.value}"
+ icon = var.icon
+ order = var.order
+ group = var.group
+
+ command = templatefile("${path.module}/scripts/start.sh", {
+ SESSION_NAME = each.value
+ })
+}
diff --git a/registry/anomaly/modules/tmux/scripts/run.sh b/registry/anomaly/modules/tmux/scripts/run.sh
new file mode 100755
index 00000000..90c0d84b
--- /dev/null
+++ b/registry/anomaly/modules/tmux/scripts/run.sh
@@ -0,0 +1,153 @@
+#!/usr/bin/env bash
+
+BOLD='\033[0;1m'
+
+# Convert templated variables to shell variables
+SAVE_INTERVAL="${SAVE_INTERVAL}"
+TMUX_CONFIG="${TMUX_CONFIG}"
+
+# Function to install tmux
+install_tmux() {
+ printf "Checking for tmux installation\n"
+
+ if command -v tmux &> /dev/null; then
+ printf "tmux is already installed \n\n"
+ return 0
+ fi
+
+ printf "Installing tmux \n\n"
+
+ # Detect package manager and install tmux
+ if command -v apt-get &> /dev/null; then
+ sudo apt-get update
+ sudo apt-get install -y tmux
+ elif command -v yum &> /dev/null; then
+ sudo yum install -y tmux
+ elif command -v dnf &> /dev/null; then
+ sudo dnf install -y tmux
+ elif command -v zypper &> /dev/null; then
+ sudo zypper install -y tmux
+ elif command -v apk &> /dev/null; then
+ sudo apk add tmux
+ elif command -v brew &> /dev/null; then
+ brew install tmux
+ else
+ printf "No supported package manager found. Please install tmux manually. \n"
+ exit 1
+ fi
+
+ printf "tmux installed successfully \n"
+}
+
+# Function to install Tmux Plugin Manager (TPM)
+install_tpm() {
+ local tpm_dir="$HOME/.tmux/plugins/tpm"
+
+ if [ -d "$tpm_dir" ]; then
+ printf "TPM is already installed"
+ return 0
+ fi
+
+ printf "Installing Tmux Plugin Manager (TPM) \n"
+
+ # Create plugins directory
+ mkdir -p "$HOME/.tmux/plugins"
+
+ # Clone TPM repository
+ if command -v git &> /dev/null; then
+ git clone https://github.com/tmux-plugins/tpm "$tpm_dir"
+ printf "TPM installed successfully"
+ else
+ printf "Git is not installed. Please install git to use tmux plugins. \n"
+ exit 1
+ fi
+}
+
+# Function to create tmux configuration
+setup_tmux_config() {
+ printf "Setting up tmux configuration \n"
+
+ local config_dir="$HOME/.tmux"
+ local config_file="$HOME/.tmux.conf"
+
+ mkdir -p "$config_dir"
+
+ if [ -n "$TMUX_CONFIG" ]; then
+ printf "$TMUX_CONFIG" > "$config_file"
+ printf "$${BOLD}Custom tmux configuration applied at {$config_file} \n\n"
+ else
+ cat > "$config_file" << EOF
+# Tmux Configuration File
+
+# =============================================================================
+# PLUGIN CONFIGURATION
+# =============================================================================
+
+# List of plugins
+set -g @plugin 'tmux-plugins/tpm'
+set -g @plugin 'tmux-plugins/tmux-sensible'
+set -g @plugin 'tmux-plugins/tmux-resurrect'
+set -g @plugin 'tmux-plugins/tmux-continuum'
+
+# tmux-continuum configuration
+set -g @continuum-restore 'on'
+set -g @continuum-save-interval '$${SAVE_INTERVAL}'
+set -g @continuum-boot 'on'
+set -g status-right 'Continuum status: #{continuum_status}'
+
+# =============================================================================
+# KEY BINDINGS FOR SESSION MANAGEMENT
+# =============================================================================
+
+# Quick session save and restore
+bind C-s run-shell "~/.tmux/plugins/tmux-resurrect/scripts/save.sh"
+bind C-r run-shell "~/.tmux/plugins/tmux-resurrect/scripts/restore.sh"
+
+# Initialize TMUX plugin manager (keep this line at the very bottom of tmux.conf)
+run '~/.tmux/plugins/tpm/tpm'
+EOF
+ printf "tmux configuration created at {$config_file} \n\n"
+ fi
+}
+
+# Function to install tmux plugins
+install_plugins() {
+ printf "Installing tmux plugins"
+
+ # Check if TPM is installed
+ if [ ! -d "$HOME/.tmux/plugins/tpm" ]; then
+ printf "TPM is not installed. Cannot install plugins. \n"
+ return 1
+ fi
+
+ # Install plugins using TPM
+ "$HOME/.tmux/plugins/tpm/bin/install_plugins"
+
+ printf "tmux plugins installed successfully \n"
+}
+
+# Main execution
+main() {
+ printf "$${BOLD} 🛠️Setting up tmux with session persistence! \n\n"
+ printf ""
+
+ # Install dependencies
+ install_tmux
+ install_tpm
+
+ # Setup tmux configuration
+ setup_tmux_config
+
+ # Install plugins
+ install_plugins
+
+ printf "$${BOLD}✅ tmux setup complete! \n\n"
+
+ printf "$${BOLD} Attempting to restore sessions\n"
+ tmux new-session -d \; source-file ~/.tmux.conf \; run-shell '~/.tmux/plugins/tmux-resurrect/scripts/restore.sh'
+ printf "$${BOLD} Sessions restored: -> %s\n" "$(tmux ls)"
+
+}
+
+# Run main function
+main
\ No newline at end of file
diff --git a/registry/anomaly/modules/tmux/scripts/start.sh b/registry/anomaly/modules/tmux/scripts/start.sh
new file mode 100755
index 00000000..4638c8f7
--- /dev/null
+++ b/registry/anomaly/modules/tmux/scripts/start.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+
+# Convert templated variables to shell variables
+SESSION_NAME='${SESSION_NAME}'
+
+# Function to check if tmux is installed
+check_tmux() {
+ if ! command -v tmux &> /dev/null; then
+ echo "tmux is not installed. Please run the tmux setup script first."
+ exit 1
+ fi
+}
+
+# Function to handle a single session
+handle_session() {
+ local session_name="$1"
+
+ # Check if the session exists
+ if tmux has-session -t "$session_name" 2>/dev/null; then
+ echo "Session '$session_name' exists, attaching to it..."
+ tmux attach-session -t "$session_name"
+ else
+ echo "Session '$session_name' does not exist, creating it..."
+ tmux new-session -d -s "$session_name"
+ tmux attach-session -t "$session_name"
+ fi
+}
+
+# Main function
+main() {
+ # Check if tmux is installed
+ check_tmux
+ handle_session "${SESSION_NAME}"
+}
+
+# Run the main function
+main