Skip to content

Commit e779853

Browse files
committed
Add persistent tmux and zsh environment
1 parent b319684 commit e779853

10 files changed

Lines changed: 203 additions & 26 deletions

File tree

.github/workflows/validate.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ jobs:
1818
- name: Verify Codex CLI is available
1919
run: docker run --rm --entrypoint codex codex-terminal --version
2020

21+
- name: Verify zsh and tmux defaults
22+
run: |
23+
docker run --rm --entrypoint sh codex-terminal -lc '
24+
tmp=$(mktemp -d)
25+
mkdir -p "$tmp/home"
26+
cp /opt/codex-terminal-defaults/zshrc "$tmp/home/.zshrc"
27+
HOME="$tmp/home" zsh -ic "echo ZSH_OK >/dev/null && tmux -V >/dev/null"
28+
'
29+
2130
- name: Smoke test terminal
2231
run: |
2332
mkdir -p "$RUNNER_TEMP/codex-terminal-data"

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ This repository contains a custom Home Assistant add-on that provides a lightwei
44

55
- `npm`
66
- `codex`
7+
- `zsh` with `oh-my-zsh`
8+
- `tmux`
79
- persistent shell state under `/data`
810

911
## Included add-on
@@ -19,6 +21,8 @@ The add-on stores its persistent state inside Home Assistant's add-on data volum
1921

2022
That means Codex auth, npm cache, shell history, and anything else you keep under `/data` survives restarts and upgrades.
2123

24+
The interactive terminal starts inside a persistent tmux session named `workspace`, so you can close the browser tab and reconnect later without losing that terminal session while the add-on remains running.
25+
2226
## Home Assistant mounts
2327

2428
The workspace also includes writable mappings for common Home Assistant directories:
@@ -62,3 +66,4 @@ git remote add origin https://github.com/<your-user>/<your-repo>.git
6266

6367
- The add-on is built from source by Home Assistant because `config.yaml` intentionally omits an `image` field.
6468
- The current Codex CLI package publishes Linux binaries for `amd64` and `aarch64`, so the add-on is limited to those Home Assistant architectures.
69+
- The `zsh` and `tmux` defaults are adapted from the owner's dotfiles and vendored into this repository so the add-on does not need GitHub credentials at runtime.

codex_terminal/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## 0.1.3
4+
5+
- Added `zsh`, `oh-my-zsh`, and `tmux`
6+
- Started the terminal inside a persistent tmux session named `workspace`
7+
- Added Linux-safe `zsh` and `tmux` defaults adapted from the owner's dotfiles
8+
39
## 0.1.2
410

511
- Mapped Home Assistant config, share, and add-on config directories into `/data/workspace`

codex_terminal/DOCS.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
## Included tools
66

7-
- `bash`
7+
- `zsh` with `oh-my-zsh`
8+
- `tmux`
89
- `npm`
910
- `git`
1011
- `ripgrep`
@@ -23,6 +24,7 @@ This keeps the following between restarts:
2324
- npm cache
2425
- shell history
2526
- cloned repositories and other files saved under `/data`
27+
- tmux sessions while the add-on stays running
2628

2729
## Mapped Home Assistant directories
2830

@@ -34,6 +36,15 @@ The terminal also exposes these Home Assistant directories inside the workspace:
3436

3537
These mounts are writable.
3638

39+
## Shell experience
40+
41+
The add-on starts in a persistent tmux session called `workspace`.
42+
43+
- close the browser tab and reconnect later to resume the same tmux session
44+
- `Ctrl-A` is the tmux prefix
45+
- `zsh` uses an `oh-my-zsh` setup derived from the owner's dotfiles
46+
- user-edited `~/.zshrc` and `~/.tmux.conf` are preserved after first run
47+
3748
## First Run
3849

3950
1. Start the add-on.

codex_terminal/Dockerfile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ ARG BUILD_FROM=alpine:3.22
22
FROM ${BUILD_FROM}
33

44
ARG CODEX_VERSION=0.117.0
5+
ARG OH_MY_ZSH_REF=master
6+
ARG ZSH_AUTOSUGGESTIONS_REF=master
7+
ARG ZSH_SYNTAX_HIGHLIGHTING_REF=master
58

69
RUN apk add --no-cache \
710
bash \
@@ -14,11 +17,20 @@ RUN apk add --no-cache \
1417
npm \
1518
openssh-client \
1619
ripgrep \
20+
tmux \
1721
ttyd \
22+
zsh \
23+
&& git clone --depth=1 --branch "${OH_MY_ZSH_REF}" https://github.com/ohmyzsh/ohmyzsh.git /opt/oh-my-zsh \
24+
&& mkdir -p /opt/oh-my-zsh/custom/plugins \
25+
&& git clone --depth=1 --branch "${ZSH_AUTOSUGGESTIONS_REF}" https://github.com/zsh-users/zsh-autosuggestions.git /opt/oh-my-zsh/custom/plugins/zsh-autosuggestions \
26+
&& git clone --depth=1 --branch "${ZSH_SYNTAX_HIGHLIGHTING_REF}" https://github.com/zsh-users/zsh-syntax-highlighting.git /opt/oh-my-zsh/custom/plugins/zsh-syntax-highlighting \
1827
&& npm install -g "@openai/codex@${CODEX_VERSION}" \
1928
&& codex --version
2029

30+
COPY defaults /opt/codex-terminal-defaults
2131
COPY run.sh /run.sh
32+
COPY start-shell.sh /usr/local/bin/start-shell.sh
2233
RUN chmod a+x /run.sh
34+
RUN chmod a+x /usr/local/bin/start-shell.sh
2335

2436
CMD ["/run.sh"]

codex_terminal/config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: Codex Terminal
2-
version: "0.1.2"
2+
version: "0.1.3"
33
slug: codex_terminal
44
description: >-
55
Lightweight persistent terminal for Home Assistant with npm and the OpenAI Codex CLI.

codex_terminal/defaults/tmux.conf

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Adapted from samabsalom/dotfiles for Linux, Home Assistant ingress, and tmux-only persistence.
2+
3+
unbind C-b
4+
set-option -g prefix C-a
5+
bind-key C-a send-prefix
6+
7+
set -g mouse on
8+
set-option -g allow-rename off
9+
set -g default-terminal "tmux-256color"
10+
set -ga terminal-overrides ",*256col*:Tc"
11+
set -s set-clipboard on
12+
set -g history-limit 10000
13+
set -g base-index 1
14+
setw -g pane-base-index 1
15+
set -g default-shell /bin/zsh
16+
set -g default-command "/bin/zsh -l"
17+
18+
bind | split-window -h -c "#{pane_current_path}"
19+
bind - split-window -v -c "#{pane_current_path}"
20+
21+
bind h select-pane -L
22+
bind j select-pane -D
23+
bind k select-pane -U
24+
bind l select-pane -R
25+
26+
bind -r H resize-pane -L 5
27+
bind -r J resize-pane -D 5
28+
bind -r K resize-pane -U 5
29+
bind -r L resize-pane -R 5
30+
31+
bind c new-window -c "#{pane_current_path}"
32+
bind n next-window
33+
bind p previous-window
34+
bind S new-session
35+
bind X kill-session
36+
bind r source-file ~/.tmux.conf \; display-message "tmux config reloaded"
37+
38+
set -g pane-border-style fg=colour238
39+
set -g pane-active-border-style fg=colour51,bg=colour236
40+
41+
set -g status-position bottom
42+
set -g status-style bg=colour234,fg=colour137
43+
set -g status-left-length 40
44+
set -g status-right-length 100
45+
set -g status-left '#[fg=colour233,bg=colour245,bold] #H #[fg=colour233,bg=colour241,bold] #S '
46+
set -g status-right '#[fg=colour138,bg=colour235] #(hostname -I 2>/dev/null | awk "{print \\$1}" || echo "N/A") #[fg=colour233,bg=colour241,bold] #H #[fg=colour233,bg=colour245,bold] %d/%m %H:%M:%S '
47+
48+
setw -g window-status-style fg=colour138,bg=colour235
49+
setw -g window-status-current-style fg=colour81,bg=colour238,bold
50+
setw -g window-status-format ' #I:#W#F '
51+
setw -g window-status-current-format ' #I:#W#F '
52+
setw -g window-status-separator ""
53+
54+
set -g message-style bg=colour166,fg=colour232,bold
55+
set-option -g visual-activity off
56+
set-option -g visual-bell off
57+
set-option -g visual-silence off
58+
set-window-option -g monitor-activity off
59+
set-option -g bell-action none
60+
61+
setw -g mode-keys vi
62+
bind-key -T copy-mode-vi v send-keys -X begin-selection
63+
bind-key -T copy-mode-vi y send-keys -X copy-selection-and-cancel
64+
bind-key -T copy-mode-vi r send-keys -X rectangle-toggle

codex_terminal/defaults/zshrc

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Adapted from samabsalom/dotfiles for the Home Assistant add-on environment.
2+
3+
export PATH="$HOME/.local/bin:$HOME/bin:$HOME/.npm-global/bin:/usr/local/bin:$PATH"
4+
5+
export ZSH="/opt/oh-my-zsh"
6+
export ZSH_DISABLE_COMPFIX="true"
7+
DISABLE_AUTO_UPDATE="true"
8+
9+
ZSH_THEME="ys"
10+
ENABLE_CORRECTION="true"
11+
12+
plugins=(
13+
git
14+
sudo
15+
colored-man-pages
16+
zsh-autosuggestions
17+
zsh-syntax-highlighting
18+
)
19+
20+
source "$ZSH/oh-my-zsh.sh"
21+
22+
alias gpush='git push origin HEAD'
23+
alias myip='curl -fsS icanhazip.com || true'
24+
alias tmstart='tmux has-session -t workspace 2>/dev/null && tmux attach-session -t workspace || tmux new-session -s workspace -c /data/workspace'
25+
26+
alias ll='ls -alF'
27+
alias la='ls -A'
28+
alias l='ls -CF'
29+
alias ..='cd ..'
30+
alias ...='cd ../..'
31+
alias ....='cd ../../..'
32+
alias grep='grep --color=auto'
33+
alias fgrep='fgrep --color=auto'
34+
alias egrep='egrep --color=auto'
35+
36+
HISTSIZE=10000
37+
SAVEHIST=10000
38+
HISTFILE=~/.zsh_history
39+
setopt HIST_EXPIRE_DUPS_FIRST
40+
setopt HIST_IGNORE_DUPS
41+
setopt HIST_IGNORE_ALL_DUPS
42+
setopt HIST_IGNORE_SPACE
43+
setopt HIST_FIND_NO_DUPS
44+
setopt HIST_SAVE_NO_DUPS
45+
setopt HIST_BEEP
46+
47+
setopt AUTO_CD
48+
setopt AUTO_PUSHD
49+
setopt PUSHD_IGNORE_DUPS
50+
51+
setopt COMPLETE_ALIASES
52+
setopt LIST_PACKED
53+
54+
if [[ -n "${TMUX:-}" ]]; then
55+
exit() {
56+
tmux detach
57+
}
58+
fi

codex_terminal/run.sh

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
set -euo pipefail
33

44
export HOME=/data/home
5-
export SHELL=/bin/bash
5+
export SHELL=/bin/zsh
66
export TERM=xterm-256color
77
export COLORTERM=truecolor
88
export EDITOR=nano
@@ -11,7 +11,7 @@ export XDG_CONFIG_HOME="${HOME}/.config"
1111
export XDG_CACHE_HOME="${HOME}/.cache"
1212
export NPM_CONFIG_CACHE="${HOME}/.npm"
1313
export NPM_CONFIG_PREFIX="${HOME}/.npm-global"
14-
export PATH="${NPM_CONFIG_PREFIX}/bin:${PATH}"
14+
export PATH="${HOME}/.local/bin:${HOME}/bin:${NPM_CONFIG_PREFIX}/bin:${PATH}"
1515

1616
WORKSPACE=/data/workspace
1717

@@ -24,29 +24,14 @@ mkdir -p \
2424
"${WORKSPACE}"
2525

2626
touch "${HOME}/.bash_history"
27+
touch "${HOME}/.zsh_history"
2728

28-
if [[ ! -f "${HOME}/.bash_profile" ]]; then
29-
cat > "${HOME}/.bash_profile" <<'EOF'
30-
if [[ -f "${HOME}/.bashrc" ]]; then
31-
. "${HOME}/.bashrc"
32-
fi
33-
EOF
29+
if [[ ! -f "${HOME}/.zshrc" ]]; then
30+
cp /opt/codex-terminal-defaults/zshrc "${HOME}/.zshrc"
3431
fi
3532

36-
if [[ ! -f "${HOME}/.bashrc" ]]; then
37-
cat > "${HOME}/.bashrc" <<'EOF'
38-
export PATH="${HOME}/.npm-global/bin:${PATH}"
39-
export NPM_CONFIG_PREFIX="${HOME}/.npm-global"
40-
export NPM_CONFIG_CACHE="${HOME}/.npm"
41-
export XDG_CONFIG_HOME="${HOME}/.config"
42-
export XDG_CACHE_HOME="${HOME}/.cache"
43-
export TERM=xterm-256color
44-
export COLORTERM=truecolor
45-
export EDITOR=nano
46-
export PAGER=less
47-
export PS1='[\u@codex \W]\$ '
48-
cd /data/workspace
49-
EOF
33+
if [[ ! -f "${HOME}/.tmux.conf" ]]; then
34+
cp /opt/codex-terminal-defaults/tmux.conf "${HOME}/.tmux.conf"
5035
fi
5136

5237
cat > /etc/motd <<'EOF'
@@ -56,7 +41,8 @@ Persistent paths:
5641
HOME -> /data/home
5742
workspace -> /data/workspace
5843
59-
Codex login, npm cache, shell history, and everything under /data survive restarts.
44+
This terminal starts in a persistent tmux session named "workspace".
45+
Close the browser tab and reconnect later to resume the same session.
6046
EOF
6147

6248
cd "${WORKSPACE}"
@@ -65,4 +51,4 @@ exec ttyd \
6551
-p 7681 \
6652
-t titleFixed="Codex Terminal" \
6753
-t fontSize=15 \
68-
/bin/bash --login
54+
/usr/local/bin/start-shell.sh

codex_terminal/start-shell.sh

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
export HOME=/data/home
5+
export SHELL=/bin/zsh
6+
export TERM=xterm-256color
7+
export COLORTERM=truecolor
8+
export EDITOR=nano
9+
export PAGER=less
10+
export XDG_CONFIG_HOME="${HOME}/.config"
11+
export XDG_CACHE_HOME="${HOME}/.cache"
12+
export NPM_CONFIG_CACHE="${HOME}/.npm"
13+
export NPM_CONFIG_PREFIX="${HOME}/.npm-global"
14+
export PATH="${HOME}/.local/bin:${HOME}/bin:${NPM_CONFIG_PREFIX}/bin:${PATH}"
15+
16+
SESSION_NAME=workspace
17+
WORKSPACE=/data/workspace
18+
19+
mkdir -p "${WORKSPACE}"
20+
cd "${WORKSPACE}"
21+
22+
if tmux has-session -t "${SESSION_NAME}" 2>/dev/null; then
23+
exec tmux attach-session -t "${SESSION_NAME}"
24+
fi
25+
26+
exec tmux new-session -s "${SESSION_NAME}" -c "${WORKSPACE}" /bin/zsh -il

0 commit comments

Comments
 (0)