batipanel 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +603 -0
- package/VERSION +1 -0
- package/bin/cli.sh +15 -0
- package/bin/start.sh +131 -0
- package/completions/_batipanel.zsh +88 -0
- package/completions/batipanel.bash +77 -0
- package/config/tmux.conf +141 -0
- package/examples/config.sh +18 -0
- package/examples/custom-layout.sh +40 -0
- package/examples/multi-project.sh +26 -0
- package/examples/project.sh +9 -0
- package/install.sh +538 -0
- package/layouts/4panel.sh +37 -0
- package/layouts/5panel.sh +39 -0
- package/layouts/6panel.sh +38 -0
- package/layouts/7panel.sh +47 -0
- package/layouts/7panel_log.sh +46 -0
- package/layouts/8panel.sh +46 -0
- package/layouts/devops.sh +46 -0
- package/layouts/dual-claude.sh +44 -0
- package/lib/common.sh +32 -0
- package/lib/core.sh +92 -0
- package/lib/doctor.sh +149 -0
- package/lib/layout.sh +174 -0
- package/lib/project.sh +125 -0
- package/lib/server-docker.sh +159 -0
- package/lib/server-init.sh +178 -0
- package/lib/server.sh +260 -0
- package/lib/session.sh +98 -0
- package/lib/shell-setup.sh +254 -0
- package/lib/themes.sh +354 -0
- package/lib/validate.sh +66 -0
- package/lib/wizard.sh +100 -0
- package/package.json +48 -0
- package/uninstall.sh +112 -0
package/lib/server.sh
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# batipanel server - Docker-based OpenClaw AI gateway management
|
|
3
|
+
# Provides: b server init|start|stop|status|logs|config|update
|
|
4
|
+
|
|
5
|
+
BATIPANEL_SERVER_DIR="${BATIPANEL_SERVER_DIR:-$BATIPANEL_HOME/server}"
|
|
6
|
+
|
|
7
|
+
# Docker dependency management is in server-docker.sh
|
|
8
|
+
# _require_docker(), _install_docker(), _install_compose_plugin()
|
|
9
|
+
|
|
10
|
+
# docker compose command (v2 plugin or standalone)
|
|
11
|
+
_compose() {
|
|
12
|
+
if docker compose version &>/dev/null 2>&1; then
|
|
13
|
+
docker compose -f "$BATIPANEL_SERVER_DIR/docker-compose.yml" "$@"
|
|
14
|
+
else
|
|
15
|
+
docker-compose -f "$BATIPANEL_SERVER_DIR/docker-compose.yml" "$@"
|
|
16
|
+
fi
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
_server_is_running() {
|
|
20
|
+
_compose ps --status running 2>/dev/null | grep -q "batipanel-server"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# === b server start ===
|
|
24
|
+
server_start() {
|
|
25
|
+
_require_docker || return 1
|
|
26
|
+
|
|
27
|
+
if [ ! -f "$BATIPANEL_SERVER_DIR/.env" ]; then
|
|
28
|
+
echo -e "${RED}Server not configured. Run 'b server init' first.${NC}"
|
|
29
|
+
return 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
echo "Starting batipanel server..."
|
|
33
|
+
|
|
34
|
+
local compose_output
|
|
35
|
+
if ! compose_output=$(_compose up -d --pull always 2>&1); then
|
|
36
|
+
echo "$compose_output" | sed 's/^/ /'
|
|
37
|
+
echo -e "${RED}Failed to start server${NC}"
|
|
38
|
+
return 1
|
|
39
|
+
fi
|
|
40
|
+
echo "$compose_output" | sed 's/^/ /'
|
|
41
|
+
|
|
42
|
+
# wait for health
|
|
43
|
+
echo ""
|
|
44
|
+
echo "Waiting for server to be ready..."
|
|
45
|
+
local attempts=0
|
|
46
|
+
while (( attempts < 30 )); do
|
|
47
|
+
if _server_is_running; then
|
|
48
|
+
echo ""
|
|
49
|
+
echo -e "${GREEN}=== Server Running ===${NC}"
|
|
50
|
+
_print_server_info
|
|
51
|
+
return 0
|
|
52
|
+
fi
|
|
53
|
+
sleep 2
|
|
54
|
+
attempts=$((attempts + 1))
|
|
55
|
+
printf "."
|
|
56
|
+
done
|
|
57
|
+
|
|
58
|
+
echo ""
|
|
59
|
+
echo -e "${YELLOW}Server is starting but not yet healthy.${NC}"
|
|
60
|
+
echo " Check logs: b server logs"
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
# === b server stop ===
|
|
64
|
+
server_stop() {
|
|
65
|
+
_require_docker || return 1
|
|
66
|
+
|
|
67
|
+
if [ ! -f "$BATIPANEL_SERVER_DIR/docker-compose.yml" ]; then
|
|
68
|
+
echo -e "${YELLOW}No server configuration found.${NC}"
|
|
69
|
+
return 1
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
echo "Stopping batipanel server..."
|
|
73
|
+
local compose_output
|
|
74
|
+
if ! compose_output=$(_compose down 2>&1); then
|
|
75
|
+
echo "$compose_output" | sed 's/^/ /'
|
|
76
|
+
echo -e "${RED}Failed to stop server${NC}"
|
|
77
|
+
return 1
|
|
78
|
+
fi
|
|
79
|
+
echo "$compose_output" | sed 's/^/ /'
|
|
80
|
+
echo -e "${GREEN}Server stopped.${NC}"
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# === b server status ===
|
|
84
|
+
server_status() {
|
|
85
|
+
echo ""
|
|
86
|
+
echo -e "${BLUE}=== Batipanel Server Status ===${NC}"
|
|
87
|
+
echo ""
|
|
88
|
+
|
|
89
|
+
if [ ! -f "$BATIPANEL_SERVER_DIR/.env" ]; then
|
|
90
|
+
echo -e " ${YELLOW}Not configured. Run 'b server init'${NC}"
|
|
91
|
+
return 0
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
if ! has_cmd docker || ! docker info &>/dev/null 2>&1; then
|
|
95
|
+
echo -e " ${RED}Docker is not running.${NC}"
|
|
96
|
+
return 1
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
if _server_is_running; then
|
|
100
|
+
echo -e " Status: ${GREEN}Running${NC}"
|
|
101
|
+
_print_server_info
|
|
102
|
+
echo ""
|
|
103
|
+
|
|
104
|
+
# security report
|
|
105
|
+
echo -e " ${BLUE}Security${NC}"
|
|
106
|
+
echo -e " ├─ Container: Docker isolated"
|
|
107
|
+
if grep -q "OPENCLAW_SANDBOX=1" "$BATIPANEL_SERVER_DIR/.env" 2>/dev/null; then
|
|
108
|
+
echo -e " ├─ Sandbox: ${GREEN}enabled${NC}"
|
|
109
|
+
else
|
|
110
|
+
echo -e " ├─ Sandbox: ${YELLOW}disabled${NC}"
|
|
111
|
+
fi
|
|
112
|
+
echo -e " ├─ Network: loopback only"
|
|
113
|
+
echo -e " ├─ Access: allowlist"
|
|
114
|
+
echo -e " └─ API Keys: $(stat -c '%a' "$BATIPANEL_SERVER_DIR/.env" 2>/dev/null || stat -f '%Lp' "$BATIPANEL_SERVER_DIR/.env" 2>/dev/null) permissions"
|
|
115
|
+
else
|
|
116
|
+
echo -e " Status: ${RED}Stopped${NC}"
|
|
117
|
+
echo ""
|
|
118
|
+
echo " Start with: b server start"
|
|
119
|
+
fi
|
|
120
|
+
echo ""
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# === b server logs ===
|
|
124
|
+
server_logs() {
|
|
125
|
+
_require_docker || return 1
|
|
126
|
+
|
|
127
|
+
if [ ! -f "$BATIPANEL_SERVER_DIR/docker-compose.yml" ]; then
|
|
128
|
+
echo -e "${YELLOW}No server configuration found.${NC}"
|
|
129
|
+
return 1
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
local follow="${1:-}"
|
|
133
|
+
if [[ "$follow" == "-f" ]]; then
|
|
134
|
+
_compose logs -f --tail 50
|
|
135
|
+
else
|
|
136
|
+
_compose logs --tail 100
|
|
137
|
+
fi
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# === b server update ===
|
|
141
|
+
server_update() {
|
|
142
|
+
_require_docker || return 1
|
|
143
|
+
|
|
144
|
+
if [ ! -f "$BATIPANEL_SERVER_DIR/.env" ]; then
|
|
145
|
+
echo -e "${RED}Server not configured.${NC}"
|
|
146
|
+
return 1
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
echo "Updating batipanel server..."
|
|
150
|
+
local compose_output
|
|
151
|
+
if ! compose_output=$(_compose pull 2>&1); then
|
|
152
|
+
echo "$compose_output" | sed 's/^/ /'
|
|
153
|
+
echo -e "${RED}Failed to pull server image${NC}"
|
|
154
|
+
return 1
|
|
155
|
+
fi
|
|
156
|
+
echo "$compose_output" | sed 's/^/ /'
|
|
157
|
+
|
|
158
|
+
echo "Restarting with new image..."
|
|
159
|
+
if ! compose_output=$(_compose up -d 2>&1); then
|
|
160
|
+
echo "$compose_output" | sed 's/^/ /'
|
|
161
|
+
echo -e "${RED}Failed to restart server${NC}"
|
|
162
|
+
return 1
|
|
163
|
+
fi
|
|
164
|
+
echo "$compose_output" | sed 's/^/ /'
|
|
165
|
+
|
|
166
|
+
echo -e "${GREEN}Server updated.${NC}"
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# === b server config ===
|
|
170
|
+
server_config_cmd() {
|
|
171
|
+
local key="${1:-}"
|
|
172
|
+
|
|
173
|
+
if [ -z "$key" ]; then
|
|
174
|
+
echo -e "${BLUE}=== Server Configuration ===${NC}"
|
|
175
|
+
echo ""
|
|
176
|
+
if [ -f "$BATIPANEL_SERVER_DIR/.env" ]; then
|
|
177
|
+
echo " Environment: $BATIPANEL_SERVER_DIR/.env"
|
|
178
|
+
# show config without sensitive values
|
|
179
|
+
while IFS='=' read -r k v; do
|
|
180
|
+
[[ "$k" =~ ^#.*$ || -z "$k" ]] && continue
|
|
181
|
+
case "$k" in
|
|
182
|
+
*TOKEN*|*KEY*|*SECRET*)
|
|
183
|
+
echo " $k=****"
|
|
184
|
+
;;
|
|
185
|
+
*)
|
|
186
|
+
echo " $k=$v"
|
|
187
|
+
;;
|
|
188
|
+
esac
|
|
189
|
+
done < "$BATIPANEL_SERVER_DIR/.env"
|
|
190
|
+
else
|
|
191
|
+
echo " Not configured. Run 'b server init'"
|
|
192
|
+
fi
|
|
193
|
+
echo ""
|
|
194
|
+
return 0
|
|
195
|
+
fi
|
|
196
|
+
|
|
197
|
+
echo -e "${YELLOW}Direct config editing not yet supported.${NC}"
|
|
198
|
+
echo " Edit manually: $BATIPANEL_SERVER_DIR/.env"
|
|
199
|
+
echo " Then restart: b server stop && b server start"
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
# === helpers ===
|
|
203
|
+
_print_server_info() {
|
|
204
|
+
local model="unknown"
|
|
205
|
+
if grep -q "CLAUDE_AI_SESSION_KEY" "$BATIPANEL_SERVER_DIR/.env" 2>/dev/null; then
|
|
206
|
+
model="Claude Opus 4.6 (Max — no API cost)"
|
|
207
|
+
elif grep -q "ANTHROPIC_API_KEY" "$BATIPANEL_SERVER_DIR/.env" 2>/dev/null; then
|
|
208
|
+
model="Claude (API key — usage billing)"
|
|
209
|
+
fi
|
|
210
|
+
|
|
211
|
+
local telegram="not configured"
|
|
212
|
+
if grep -q "TELEGRAM_BOT_TOKEN" "$BATIPANEL_SERVER_DIR/.env" 2>/dev/null; then
|
|
213
|
+
telegram="connected"
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
echo -e " AI Model: ${GREEN}${model}${NC}"
|
|
217
|
+
echo -e " Telegram: ${GREEN}${telegram}${NC}"
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
# === Router ===
|
|
221
|
+
server_cmd() {
|
|
222
|
+
local subcmd="${1:-}"
|
|
223
|
+
shift 2>/dev/null || true
|
|
224
|
+
|
|
225
|
+
case "$subcmd" in
|
|
226
|
+
init)
|
|
227
|
+
server_init
|
|
228
|
+
;;
|
|
229
|
+
start)
|
|
230
|
+
server_start
|
|
231
|
+
;;
|
|
232
|
+
stop)
|
|
233
|
+
server_stop
|
|
234
|
+
;;
|
|
235
|
+
status)
|
|
236
|
+
server_status
|
|
237
|
+
;;
|
|
238
|
+
logs)
|
|
239
|
+
server_logs "$@"
|
|
240
|
+
;;
|
|
241
|
+
update)
|
|
242
|
+
server_update
|
|
243
|
+
;;
|
|
244
|
+
config)
|
|
245
|
+
server_config_cmd "$@"
|
|
246
|
+
;;
|
|
247
|
+
*)
|
|
248
|
+
echo -e "${BLUE}=== Batipanel Server ===${NC}"
|
|
249
|
+
echo ""
|
|
250
|
+
echo " b server init Setup Telegram AI bot"
|
|
251
|
+
echo " b server start Start the server"
|
|
252
|
+
echo " b server stop Stop the server"
|
|
253
|
+
echo " b server status Show server status"
|
|
254
|
+
echo " b server logs [-f] View logs (follow with -f)"
|
|
255
|
+
echo " b server update Pull latest image & restart"
|
|
256
|
+
echo " b server config View configuration"
|
|
257
|
+
echo ""
|
|
258
|
+
;;
|
|
259
|
+
esac
|
|
260
|
+
}
|
package/lib/session.sh
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# batipanel session - start, stop, list, project listing
|
|
3
|
+
|
|
4
|
+
tmux_start() {
|
|
5
|
+
local SESSION="$1"
|
|
6
|
+
local LAYOUT="${2:-}"
|
|
7
|
+
local SCRIPT="$BATIPANEL_HOME/projects/${SESSION}.sh"
|
|
8
|
+
|
|
9
|
+
validate_session_name "$SESSION" || return 1
|
|
10
|
+
|
|
11
|
+
if [ ! -f "$SCRIPT" ]; then
|
|
12
|
+
echo -e "${RED}Project not found: ${SESSION}${NC}"
|
|
13
|
+
echo -e "${YELLOW}Available projects:${NC}"
|
|
14
|
+
list_projects
|
|
15
|
+
return 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
if tmux has-session -t "$SESSION" 2>/dev/null; then
|
|
19
|
+
echo -e "${GREEN}Resuming session: $SESSION${NC}"
|
|
20
|
+
else
|
|
21
|
+
echo -e "${BLUE}Starting new session: $SESSION${NC}"
|
|
22
|
+
if [ -n "$LAYOUT" ]; then
|
|
23
|
+
LAYOUT="$LAYOUT" bash "$SCRIPT" "$SESSION"
|
|
24
|
+
else
|
|
25
|
+
bash "$SCRIPT" "$SESSION"
|
|
26
|
+
fi
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# iTerm2 -CC mode: opt-in via config, prompt on first encounter
|
|
30
|
+
if [ "${TERM_PROGRAM:-}" = "iTerm.app" ] && [ -z "${BATIPANEL_ITERM_CC:-}" ]; then
|
|
31
|
+
echo ""
|
|
32
|
+
echo -e "${BLUE}iTerm2 detected!${NC}"
|
|
33
|
+
echo " iTerm2 supports native tmux integration (tmux -CC)."
|
|
34
|
+
echo " Panes become native iTerm2 splits instead of tmux UI."
|
|
35
|
+
echo -e " ${YELLOW}Note:${NC} tmux status bar and theme will not be visible in this mode."
|
|
36
|
+
echo ""
|
|
37
|
+
printf "Enable iTerm2 integration? [y/N] "
|
|
38
|
+
local iterm_answer
|
|
39
|
+
read -r iterm_answer
|
|
40
|
+
if [[ "$iterm_answer" == [yY] ]]; then
|
|
41
|
+
BATIPANEL_ITERM_CC="1"
|
|
42
|
+
else
|
|
43
|
+
BATIPANEL_ITERM_CC="0"
|
|
44
|
+
fi
|
|
45
|
+
# persist choice
|
|
46
|
+
_save_config "BATIPANEL_ITERM_CC" "$BATIPANEL_ITERM_CC"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
if [ "${BATIPANEL_ITERM_CC:-0}" = "1" ]; then
|
|
50
|
+
exec tmux -CC attach -t "$SESSION"
|
|
51
|
+
else
|
|
52
|
+
exec tmux attach -t "$SESSION"
|
|
53
|
+
fi
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
tmux_stop() {
|
|
57
|
+
local SESSION="$1"
|
|
58
|
+
local FORCE="${2:-}"
|
|
59
|
+
validate_session_name "$SESSION" || return 1
|
|
60
|
+
|
|
61
|
+
if tmux has-session -t "$SESSION" 2>/dev/null; then
|
|
62
|
+
if [[ "$FORCE" != "-f" && -t 0 ]]; then
|
|
63
|
+
printf "Stop session '%s'? [y/N] " "$SESSION"
|
|
64
|
+
local answer
|
|
65
|
+
read -r answer
|
|
66
|
+
if [[ "$answer" != [yY] ]]; then
|
|
67
|
+
echo "Cancelled."
|
|
68
|
+
return 0
|
|
69
|
+
fi
|
|
70
|
+
fi
|
|
71
|
+
tmux kill-session -t "$SESSION" && echo -e "${RED}Stopped: $SESSION${NC}"
|
|
72
|
+
else
|
|
73
|
+
echo -e "${YELLOW}Session not found: $SESSION${NC}"
|
|
74
|
+
fi
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
tmux_list() {
|
|
78
|
+
echo -e "${BLUE}=== Active Sessions ===${NC}"
|
|
79
|
+
tmux ls 2>/dev/null || echo " (none)"
|
|
80
|
+
echo ""
|
|
81
|
+
echo -e "${BLUE}=== Registered Projects ===${NC}"
|
|
82
|
+
list_projects
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# list registered projects
|
|
86
|
+
list_projects() {
|
|
87
|
+
local found=0
|
|
88
|
+
for f in "$BATIPANEL_HOME"/projects/*.sh; do
|
|
89
|
+
[ -f "$f" ] || continue
|
|
90
|
+
local name
|
|
91
|
+
name=$(basename "$f" .sh)
|
|
92
|
+
echo " - $name"
|
|
93
|
+
found=1
|
|
94
|
+
done
|
|
95
|
+
if (( found == 0 )); then
|
|
96
|
+
echo " (none - run 'b new <name> <path>' to register a project)"
|
|
97
|
+
fi
|
|
98
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# batipanel shell-setup - powerline fonts, prompt theme, hostname hiding
|
|
3
|
+
|
|
4
|
+
BATIPANEL_HOME="${BATIPANEL_HOME:-$HOME/.batipanel}"
|
|
5
|
+
|
|
6
|
+
has_cmd() { command -v "$1" &>/dev/null; }
|
|
7
|
+
|
|
8
|
+
# portable sed -i (macOS vs GNU) — may already be defined by caller
|
|
9
|
+
if ! declare -f _sed_i &>/dev/null; then
|
|
10
|
+
_sed_i() {
|
|
11
|
+
if [ "$(uname -s)" = "Darwin" ]; then
|
|
12
|
+
sed -i '' "$@"
|
|
13
|
+
else
|
|
14
|
+
sed -i "$@"
|
|
15
|
+
fi
|
|
16
|
+
}
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# === 1. Install powerline fonts ===
|
|
20
|
+
install_powerline_fonts() {
|
|
21
|
+
echo " Setting up Powerline fonts..."
|
|
22
|
+
|
|
23
|
+
local OS
|
|
24
|
+
OS="$(uname -s)"
|
|
25
|
+
|
|
26
|
+
# check if already installed
|
|
27
|
+
if fc-list 2>/dev/null | grep -qi "powerline\|nerd"; then
|
|
28
|
+
echo " Powerline-compatible fonts already installed"
|
|
29
|
+
return 0
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
case "$OS" in
|
|
33
|
+
Darwin)
|
|
34
|
+
if has_cmd brew; then
|
|
35
|
+
# install Nerd Font (includes powerline glyphs)
|
|
36
|
+
brew tap homebrew/cask-fonts 2>/dev/null || true
|
|
37
|
+
if brew install --cask font-meslo-lg-nerd-font 2>/dev/null; then
|
|
38
|
+
echo " Installed MesloLGS Nerd Font (macOS)"
|
|
39
|
+
else
|
|
40
|
+
echo " Font install via brew failed, trying powerline-fonts..."
|
|
41
|
+
_install_powerline_fonts_git
|
|
42
|
+
fi
|
|
43
|
+
else
|
|
44
|
+
_install_powerline_fonts_git
|
|
45
|
+
fi
|
|
46
|
+
;;
|
|
47
|
+
Linux)
|
|
48
|
+
if has_cmd apt-get; then
|
|
49
|
+
if sudo apt-get install -y -qq fonts-powerline 2>/dev/null; then
|
|
50
|
+
echo " Installed fonts-powerline (apt)"
|
|
51
|
+
else
|
|
52
|
+
_install_powerline_fonts_git
|
|
53
|
+
fi
|
|
54
|
+
elif has_cmd dnf; then
|
|
55
|
+
if sudo dnf install -y powerline-fonts 2>/dev/null; then
|
|
56
|
+
echo " Installed powerline-fonts (dnf)"
|
|
57
|
+
else
|
|
58
|
+
_install_powerline_fonts_git
|
|
59
|
+
fi
|
|
60
|
+
elif has_cmd pacman; then
|
|
61
|
+
if sudo pacman -S --noconfirm powerline-fonts 2>/dev/null; then
|
|
62
|
+
echo " Installed powerline-fonts (pacman)"
|
|
63
|
+
else
|
|
64
|
+
_install_powerline_fonts_git
|
|
65
|
+
fi
|
|
66
|
+
else
|
|
67
|
+
_install_powerline_fonts_git
|
|
68
|
+
fi
|
|
69
|
+
;;
|
|
70
|
+
*)
|
|
71
|
+
_install_powerline_fonts_git
|
|
72
|
+
;;
|
|
73
|
+
esac
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# fallback: clone and install powerline fonts from GitHub
|
|
77
|
+
_install_powerline_fonts_git() {
|
|
78
|
+
if ! has_cmd git; then
|
|
79
|
+
echo " git not found, skipping font install"
|
|
80
|
+
return 1
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
local tmpdir
|
|
84
|
+
tmpdir=$(mktemp -d)
|
|
85
|
+
if git clone --depth=1 https://github.com/powerline/fonts.git "$tmpdir/fonts" 2>/dev/null; then
|
|
86
|
+
bash "$tmpdir/fonts/install.sh" 2>/dev/null || true
|
|
87
|
+
echo " Installed powerline fonts from GitHub"
|
|
88
|
+
else
|
|
89
|
+
echo " Failed to clone powerline fonts"
|
|
90
|
+
fi
|
|
91
|
+
rm -rf "$tmpdir"
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# === 2. Setup zsh with Oh My Zsh + agnoster ===
|
|
95
|
+
setup_zsh_theme() {
|
|
96
|
+
local shell_rc="$1"
|
|
97
|
+
|
|
98
|
+
echo " Configuring zsh theme..."
|
|
99
|
+
|
|
100
|
+
# install Oh My Zsh if not present
|
|
101
|
+
if [ ! -d "$HOME/.oh-my-zsh" ]; then
|
|
102
|
+
echo " Installing Oh My Zsh..."
|
|
103
|
+
if has_cmd curl; then
|
|
104
|
+
RUNZSH=no CHSH=no KEEP_ZSHRC=yes \
|
|
105
|
+
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" 2>/dev/null || true
|
|
106
|
+
elif has_cmd wget; then
|
|
107
|
+
RUNZSH=no CHSH=no KEEP_ZSHRC=yes \
|
|
108
|
+
sh -c "$(wget -qO- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" 2>/dev/null || true
|
|
109
|
+
else
|
|
110
|
+
echo " curl/wget not found, skipping Oh My Zsh"
|
|
111
|
+
return 1
|
|
112
|
+
fi
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
if [ ! -d "$HOME/.oh-my-zsh" ]; then
|
|
116
|
+
echo " Oh My Zsh installation failed"
|
|
117
|
+
return 1
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# set agnoster theme
|
|
121
|
+
if [ -f "$shell_rc" ]; then
|
|
122
|
+
if grep -q 'ZSH_THEME=' "$shell_rc" 2>/dev/null; then
|
|
123
|
+
_sed_i 's/^ZSH_THEME=.*/ZSH_THEME="agnoster"/' "$shell_rc"
|
|
124
|
+
else
|
|
125
|
+
echo 'ZSH_THEME="agnoster"' >> "$shell_rc"
|
|
126
|
+
fi
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
# hide hostname: set DEFAULT_USER to current user
|
|
130
|
+
_add_line_if_missing "$shell_rc" "DEFAULT_USER" \
|
|
131
|
+
"DEFAULT_USER=\"\$(whoami)\""
|
|
132
|
+
|
|
133
|
+
echo " Set agnoster theme with hostname hidden"
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# fallback prompt generator (when themes.sh is not loaded)
|
|
137
|
+
_setup_default_bash_prompt() {
|
|
138
|
+
local prompt_file="$BATIPANEL_HOME/config/bash-prompt.sh"
|
|
139
|
+
mkdir -p "$BATIPANEL_HOME/config"
|
|
140
|
+
cat > "$prompt_file" << 'FALLBACK_EOF'
|
|
141
|
+
#!/usr/bin/env bash
|
|
142
|
+
# batipanel bash prompt - default theme (fallback)
|
|
143
|
+
__batipanel_prompt() {
|
|
144
|
+
local exit_code=$?
|
|
145
|
+
local sep=$'\uE0B0'
|
|
146
|
+
local bg_user="\[\e[44m\]"
|
|
147
|
+
local fg_user="\[\e[97m\]"
|
|
148
|
+
local bg_dir="\[\e[48;5;240m\]"
|
|
149
|
+
local fg_dir="\[\e[97m\]"
|
|
150
|
+
local bg_git="\[\e[42m\]"
|
|
151
|
+
local fg_git="\[\e[30m\]"
|
|
152
|
+
local bg_err="\[\e[41m\]"
|
|
153
|
+
local fg_err="\[\e[97m\]"
|
|
154
|
+
local reset="\[\e[0m\]"
|
|
155
|
+
local t_user_dir="\[\e[34;48;5;240m\]"
|
|
156
|
+
local t_dir_git="\[\e[38;5;240;42m\]"
|
|
157
|
+
local t_dir_end="\[\e[38;5;240m\]"
|
|
158
|
+
local t_git_end="\[\e[32m\]"
|
|
159
|
+
local t_err_dir="\[\e[31;48;5;240m\]"
|
|
160
|
+
local ps=""
|
|
161
|
+
if [ "$exit_code" -ne 0 ]; then
|
|
162
|
+
ps+="${bg_err}${fg_err} ✘ ${exit_code} "
|
|
163
|
+
ps+="${t_err_dir}${sep}"
|
|
164
|
+
fi
|
|
165
|
+
ps+="${bg_user}${fg_user} \\u "
|
|
166
|
+
ps+="${t_user_dir}${sep}"
|
|
167
|
+
ps+="${bg_dir}${fg_dir} \\w "
|
|
168
|
+
local git_branch=""
|
|
169
|
+
if command -v git &>/dev/null; then
|
|
170
|
+
git_branch="$(git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD 2>/dev/null)"
|
|
171
|
+
fi
|
|
172
|
+
if [ -n "$git_branch" ]; then
|
|
173
|
+
ps+="${t_dir_git}${sep}"
|
|
174
|
+
local git_icon=$'\uE0A0'
|
|
175
|
+
ps+="${bg_git}${fg_git} ${git_icon} ${git_branch} "
|
|
176
|
+
ps+="${reset}${t_git_end}${sep}${reset} "
|
|
177
|
+
else
|
|
178
|
+
ps+="${reset}${t_dir_end}${sep}${reset} "
|
|
179
|
+
fi
|
|
180
|
+
PS1="$ps"
|
|
181
|
+
}
|
|
182
|
+
PROMPT_COMMAND="__batipanel_prompt"
|
|
183
|
+
FALLBACK_EOF
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
# === 3. Setup bash powerline prompt ===
|
|
187
|
+
setup_bash_prompt() {
|
|
188
|
+
local shell_rc="$1"
|
|
189
|
+
|
|
190
|
+
echo " Configuring bash prompt..."
|
|
191
|
+
|
|
192
|
+
# generate prompt with current theme (uses _generate_themed_prompt from themes.sh)
|
|
193
|
+
local current_theme="${BATIPANEL_THEME:-default}"
|
|
194
|
+
if declare -f _generate_themed_prompt &>/dev/null; then
|
|
195
|
+
_generate_themed_prompt "$current_theme"
|
|
196
|
+
else
|
|
197
|
+
# fallback: generate default prompt directly (standalone install without themes.sh)
|
|
198
|
+
_setup_default_bash_prompt
|
|
199
|
+
fi
|
|
200
|
+
|
|
201
|
+
# source prompt from shell RC
|
|
202
|
+
local prompt_file="$BATIPANEL_HOME/config/bash-prompt.sh"
|
|
203
|
+
local source_line="source \"$prompt_file\""
|
|
204
|
+
_add_line_if_missing "$shell_rc" "bash-prompt.sh" "$source_line"
|
|
205
|
+
|
|
206
|
+
echo " Set powerline-style prompt (hostname hidden)"
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
# === Helper: add line to RC if not already present ===
|
|
210
|
+
_add_line_if_missing() {
|
|
211
|
+
local rc_file="$1"
|
|
212
|
+
local search="$2"
|
|
213
|
+
local line="$3"
|
|
214
|
+
|
|
215
|
+
if [ ! -f "$rc_file" ]; then
|
|
216
|
+
echo "$line" >> "$rc_file"
|
|
217
|
+
return
|
|
218
|
+
fi
|
|
219
|
+
|
|
220
|
+
if ! grep -qF "$search" "$rc_file" 2>/dev/null; then
|
|
221
|
+
{
|
|
222
|
+
echo ""
|
|
223
|
+
echo "# batipanel shell theme"
|
|
224
|
+
echo "$line"
|
|
225
|
+
} >> "$rc_file"
|
|
226
|
+
fi
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
# === Main entry point ===
|
|
230
|
+
setup_shell_environment() {
|
|
231
|
+
local user_shell="$1"
|
|
232
|
+
local shell_rc="$2"
|
|
233
|
+
|
|
234
|
+
echo ""
|
|
235
|
+
echo "Setting up shell environment..."
|
|
236
|
+
|
|
237
|
+
# install powerline fonts
|
|
238
|
+
install_powerline_fonts
|
|
239
|
+
|
|
240
|
+
# configure prompt based on shell
|
|
241
|
+
case "$user_shell" in
|
|
242
|
+
zsh)
|
|
243
|
+
setup_zsh_theme "$shell_rc"
|
|
244
|
+
;;
|
|
245
|
+
bash)
|
|
246
|
+
setup_bash_prompt "$shell_rc"
|
|
247
|
+
;;
|
|
248
|
+
*)
|
|
249
|
+
echo " Unsupported shell ($user_shell), skipping prompt setup"
|
|
250
|
+
;;
|
|
251
|
+
esac
|
|
252
|
+
|
|
253
|
+
echo " Shell environment setup complete"
|
|
254
|
+
}
|