Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 4 additions & 14 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,10 @@ SHELL ["/bin/ash", "-o", "pipefail", "-c"]
ARG BUILD_ARCH
WORKDIR /usr/src

# Install rlwrap
ARG RLWRAP_VERSION
RUN apk add --no-cache --virtual .build-deps \
build-base \
readline-dev \
ncurses-dev \
&& curl -L -s "https://github.com/hanslub42/rlwrap/releases/download/${RLWRAP_VERSION}/rlwrap-${RLWRAP_VERSION}.tar.gz" \
| tar zxvf - -C /usr/src/ \
&& cd rlwrap-${RLWRAP_VERSION} \
&& ./configure \
&& make \
&& make install \
&& apk del .build-deps \
&& rm -rf /usr/src/*
# Install dependencies
RUN apk add --no-cache \
bash-completion
# No longer need rlwrap; base image already provides Bash/readline

# Install CLI
ARG CLI_VERSION
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# CLI for the Home Assistant Operating System

This is for the Home Assistant Operating System and is the login shell.
This is for the Home Assistant Operating System and is the login shell.
84 changes: 84 additions & 0 deletions rootfs/etc/ha-cli/.repl_rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# ha REPL rcfile


# History configuration
HISTFILE=/tmp/.cli_history
HISTSIZE=1000
HISTFILESIZE=2000
PROMPT_COMMAND='history -a; history -c; history -r'

# Prompt
PS1='\[\e[32m\]ha > \[\e[0m\]'

# Intercept unknown commands and pass them to 'ha'
command_not_found_handle() {
# Execute as 'ha <command> <args>'
ha "$@"
return $?
}

# Friendly helpers
help() {
echo 'Note: Use "login" to enter operating system shell'
echo ''
echo '--------------------------------'
ha help
}
login() {
echo 'Entering OS shell...'
exit 10;
}

# Load bash-completion core and ha completion if available
if command -v ha >/dev/null 2>&1; then
if [ -f /etc/bash_completion.d/ha ]; then
# Load from standard location (native HA completion registers itself for 'ha')
. /etc/bash_completion.d/ha

# Wrapper function to inject 'ha' as first argument for all completions
if declare -F __start_ha >/dev/null 2>&1; then
__start_ha >/dev/null 2>&1 || true

# Clear ALL existing completions to ensure our wrapper is used
while read -r cmd; do
complete -r "$cmd" 2>/dev/null || true
done < <(complete -p | awk '{print $NF}')

# Universal completion handler - wraps everything as 'ha ...'
__ha_repl_complete() {
local cur prev words cword

# Handle empty command line specially
if [[ ${#COMP_WORDS[@]} -eq 0 || ( ${#COMP_WORDS[@]} -eq 1 && -z "${COMP_WORDS[0]}" ) ]]; then
COMP_LINE="ha "
COMP_POINT=3
COMP_WORDS=(ha "")
COMP_CWORD=1
else
# Prepend 'ha' to existing words
COMP_LINE="ha $COMP_LINE"
COMP_POINT=$((COMP_POINT + 3))
COMP_WORDS=(ha "${COMP_WORDS[@]}")
COMP_CWORD=$((COMP_CWORD + 1))
fi

COMPREPLY=()
__start_ha
}

# Register our completion as the default for EVERYTHING
complete -D -F __ha_repl_complete # Default for any command
complete -E -F __ha_repl_complete # Empty command line
complete -I -F __ha_repl_complete 2>/dev/null || true # Initial word (bash 5+)
fi

fi

# Note: __start_ha is defined by the ha completion script

# Use native completion only; no implicit injection or extra bindings.
else
echo 'Warning: ha command not found; commands will fail' >&2
fi

# Use commands with explicit 'ha' prefix; native completion supports 'ha ...'
33 changes: 15 additions & 18 deletions rootfs/usr/bin/cli.sh
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
#!/bin/bash

# Print banner (best-effort)
ha banner || true

# Run CLI
COMMAND=""
while true; do
COMMAND="$(rlwrap -S $'\e[32mha > \e[0m' -H /tmp/.cli_history -o cat)"
# Create a minimal rcfile to drive an interactive Bash as our REPL
RC_FILE=/etc/ha-cli/.repl_rc
HISTFILE=/tmp/.cli_history

# Abort to host?
if [ "$COMMAND" == "help" ]; then
echo "Note: Use \"login\" to enter operating system shell"
elif [ "$COMMAND" == "login" ]; then
exit 10
elif [ "$COMMAND" == "exit" ]; then
exit
elif [ -z "${COMMAND##ha *}" ]; then
echo "Note: Leading 'ha' is not necessary in this HA CLI"
COMMAND=$(echo "$COMMAND" | cut -b 3-)
fi
# Ensure history file exists
touch "$HISTFILE" 2>/dev/null || true
if command -v ha >/dev/null 2>&1; then
completion_file_content=$(ha completion bash 2>/dev/null || true)
if [ -n "$completion_file_content" ]; then
echo "$completion_file_content" >/etc/bash_completion.d/ha 2>/dev/null || true
fi
fi

echo "$COMMAND" | xargs -o ha
echo ""
done

# Start interactive bash with our rcfile; no user profiles
exec bash --noprofile --rcfile "$RC_FILE" -i