htoolbox 0.1.0__py3-none-any.whl
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.
- htoolbox-0.1.0.data/scripts/codeit +245 -0
- htoolbox-0.1.0.dist-info/METADATA +44 -0
- htoolbox-0.1.0.dist-info/RECORD +8 -0
- htoolbox-0.1.0.dist-info/WHEEL +5 -0
- htoolbox-0.1.0.dist-info/entry_points.txt +2 -0
- htoolbox-0.1.0.dist-info/licenses/LICENSE +21 -0
- htoolbox-0.1.0.dist-info/top_level.txt +1 -0
- tmuxer.py +90 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Usage:
|
|
5
|
+
# host="100.123.68.125"
|
|
6
|
+
# remote_addr="127.0.0.1" # or "0.0.0.0" or any ip bound on remote
|
|
7
|
+
# port=8000
|
|
8
|
+
# kill_remote_vscode "$host" "$remote_addr" "$port"
|
|
9
|
+
#
|
|
10
|
+
# Returns:
|
|
11
|
+
# 0 = killed successfully
|
|
12
|
+
# 1 = nothing to kill / no listener found
|
|
13
|
+
# 2 = listener found but not Code/code-tunnel (skipped)
|
|
14
|
+
# 3 = error (ssh failure / parsing failure)
|
|
15
|
+
kill_remote_vscode_windows() {
|
|
16
|
+
local HOST="$1" REMOTE_ADDR="$2" PORT="$3"
|
|
17
|
+
local netstat_cmd="netstat -ano | findstr \"${REMOTE_ADDR}:${PORT}\" | findstr LISTENING || true"
|
|
18
|
+
local netline
|
|
19
|
+
netline=$(ssh "$HOST" "$netstat_cmd") || { echo "ERROR: ssh netstat failed" >&2; return 3; }
|
|
20
|
+
if [[ -z "$netline" ]]; then
|
|
21
|
+
echo "No listener found on ${REMOTE_ADDR}:${PORT} on ${HOST}."; return 1; fi
|
|
22
|
+
local pid
|
|
23
|
+
pid=$(printf '%s\n' "$netline" | awk '{print $NF}' | head -n1)
|
|
24
|
+
if [[ -z "$pid" ]]; then
|
|
25
|
+
echo "Failed to parse PID from netstat output:" >&2; printf '%s\n' "$netline" >&2; return 3; fi
|
|
26
|
+
local taskline tasklist_cmd="tasklist /FI \"PID eq ${pid}\" /FO CSV /NH"
|
|
27
|
+
taskline=$(ssh "$HOST" "$tasklist_cmd") || { echo "ERROR: ssh tasklist failed" >&2; return 3; }
|
|
28
|
+
if printf '%s\n' "$taskline" | grep -qi "No tasks are running"; then echo "PID $pid not present."; return 1; fi
|
|
29
|
+
local procname lower
|
|
30
|
+
procname=$(printf '%s\n' "$taskline" | awk -F',' '{gsub(/"/,"",$1); print $1}' | head -n1)
|
|
31
|
+
lower=$(printf '%s' "$procname" | tr '[:upper:]' '[:lower:]')
|
|
32
|
+
if [[ "$lower" == "code.exe" || "$lower" == "code" || "$lower" == "code-tunnel.exe" || "$lower" == "code-tunnel" ]]; then
|
|
33
|
+
echo "Killing windows PID $pid on $HOST ..."
|
|
34
|
+
ssh "$HOST" "taskkill /PID ${pid} /F" && { echo "Killed PID $pid."; return 0; }
|
|
35
|
+
echo "Failed to kill PID $pid." >&2; return 3
|
|
36
|
+
else
|
|
37
|
+
echo "PID $pid belongs to '$procname' — not Code/code-tunnel. Skipping kill."; return 2
|
|
38
|
+
fi
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
kill_remote_vscode_unix() {
|
|
42
|
+
# Linux / macOS: find PIDs listening on REMOTE_ADDR:PORT, prefer lsof then ss, validate by command line, then kill
|
|
43
|
+
local HOST="$1" REMOTE_ADDR="$2" PORT="$3"
|
|
44
|
+
# Build remote script safely using a heredoc with placeholder substitution to avoid local shell quoting issues
|
|
45
|
+
local REMOTE_SCRIPT
|
|
46
|
+
REMOTE_SCRIPT=$(cat <<'EOS'
|
|
47
|
+
set -euo pipefail
|
|
48
|
+
found_pids=""
|
|
49
|
+
if command -v lsof >/dev/null 2>&1; then
|
|
50
|
+
found_pids=$(lsof -nP -iTCP:__PORT__ -sTCP:LISTEN -t 2>/dev/null || true)
|
|
51
|
+
fi
|
|
52
|
+
if [ -z "$found_pids" ] && command -v ss >/dev/null 2>&1; then
|
|
53
|
+
found_pids=$(ss -ltnp 2>/dev/null | awk -v port=__PORT__ '$4 ~ ":"port"$" {print}' | sed -n 's/.*pid=\([0-9]\+\).*/\1/p')
|
|
54
|
+
fi
|
|
55
|
+
if [ -z "$found_pids" ] && command -v pgrep >/dev/null 2>&1; then
|
|
56
|
+
found_pids=$(pgrep -f "serve-web.*(--port[= ]__PORT__|:__PORT__)" || true)
|
|
57
|
+
fi
|
|
58
|
+
killed_any=1
|
|
59
|
+
if [ -n "$found_pids" ]; then
|
|
60
|
+
for pid in $found_pids; do
|
|
61
|
+
cmdline=$(ps -p "$pid" -o args= 2>/dev/null || true)
|
|
62
|
+
echo "$cmdline" | grep -qiE "(code(|-tunnel).*(serve-web)|serve-web)" || continue
|
|
63
|
+
echo "Killing PID $pid ($cmdline)"
|
|
64
|
+
kill -TERM "$pid" 2>/dev/null || true
|
|
65
|
+
done
|
|
66
|
+
sleep 0.5
|
|
67
|
+
for pid in $found_pids; do
|
|
68
|
+
if kill -0 "$pid" 2>/dev/null; then
|
|
69
|
+
kill -KILL "$pid" 2>/dev/null || true
|
|
70
|
+
fi
|
|
71
|
+
done
|
|
72
|
+
killed_any=0
|
|
73
|
+
fi
|
|
74
|
+
exit "$killed_any"
|
|
75
|
+
EOS
|
|
76
|
+
)
|
|
77
|
+
REMOTE_SCRIPT=${REMOTE_SCRIPT//__PORT__/$PORT}
|
|
78
|
+
|
|
79
|
+
if ssh "$HOST" bash -lc "$REMOTE_SCRIPT"; then
|
|
80
|
+
echo "Remote VS Code server cleaned up (Linux/macOS)."; return 0
|
|
81
|
+
else
|
|
82
|
+
echo "No matching VS Code server found to clean on $HOST (Linux/macOS)."; return 1
|
|
83
|
+
fi
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# ----------------------------------
|
|
88
|
+
# Defaults
|
|
89
|
+
# ----------------------------------
|
|
90
|
+
PORT=8000
|
|
91
|
+
REMOTE_ADDR="127.0.0.1"
|
|
92
|
+
REMOTE_PORT=8000
|
|
93
|
+
BIND_HOST_IPV4=false
|
|
94
|
+
KILL_MODE=false
|
|
95
|
+
|
|
96
|
+
# ----------------------------------
|
|
97
|
+
# Usage
|
|
98
|
+
# ----------------------------------
|
|
99
|
+
usage() {
|
|
100
|
+
cat <<EOF
|
|
101
|
+
Usage: $0 [options] <host>
|
|
102
|
+
|
|
103
|
+
Options:
|
|
104
|
+
-p, --port <port> Local/forward port (default: 8000)
|
|
105
|
+
-r, --remote <addr[:p]> Remote bind address and optional port (default: 127.0.0.1)
|
|
106
|
+
-b, --bindhost Use detected IPv4 of <host> as remote bind address
|
|
107
|
+
(mutually exclusive with --remote)
|
|
108
|
+
-k, --kill Kill the VS Code server on the given port/host
|
|
109
|
+
-h, --help Show this help
|
|
110
|
+
|
|
111
|
+
Examples:
|
|
112
|
+
$0 myhost
|
|
113
|
+
$0 -p 8080 myhost
|
|
114
|
+
$0 -r 0.0.0.0 myhost
|
|
115
|
+
$0 -r 192.168.1.10:9000 myhost
|
|
116
|
+
$0 --kill -r 0.0.0.0:9000 myhost
|
|
117
|
+
$0 -b myhost # bind the VS Code server to the host's IPv4
|
|
118
|
+
EOF
|
|
119
|
+
exit 1
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# ----------------------------------
|
|
123
|
+
# Parse args
|
|
124
|
+
# ----------------------------------
|
|
125
|
+
REMOTE_SPEC=""
|
|
126
|
+
while [[ $# -gt 0 ]]; do
|
|
127
|
+
case "$1" in
|
|
128
|
+
-p|--port)
|
|
129
|
+
shift; PORT="${1:-}"; [[ -z "$PORT" ]] && usage ;;
|
|
130
|
+
-r|--remote)
|
|
131
|
+
shift; REMOTE_SPEC="${1:-}"; [[ -z "$REMOTE_SPEC" ]] && usage ;;
|
|
132
|
+
-b|--bindhost)
|
|
133
|
+
BIND_HOST_IPV4=true ;;
|
|
134
|
+
-k|--kill)
|
|
135
|
+
KILL_MODE=true ;;
|
|
136
|
+
-h|--help)
|
|
137
|
+
usage ;;
|
|
138
|
+
-*)
|
|
139
|
+
echo "Unknown option: $1" >&2; usage ;;
|
|
140
|
+
*)
|
|
141
|
+
HOST="$1" ;;
|
|
142
|
+
esac
|
|
143
|
+
shift
|
|
144
|
+
done
|
|
145
|
+
|
|
146
|
+
[[ -z "${HOST:-}" ]] && usage
|
|
147
|
+
|
|
148
|
+
# ----------------------------------
|
|
149
|
+
# Validate mutual exclusivity
|
|
150
|
+
# ----------------------------------
|
|
151
|
+
if [[ "$BIND_HOST_IPV4" == true && -n "$REMOTE_SPEC" ]]; then
|
|
152
|
+
echo "Error: --bindhost (-b) cannot be used together with --remote (-r)." >&2
|
|
153
|
+
exit 1
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# ----------------------------------
|
|
157
|
+
# Resolve host IPv4
|
|
158
|
+
# ----------------------------------
|
|
159
|
+
IPV4=$(ping -c 1 "$HOST" 2>/dev/null | grep -E -o "\(.*\):" | grep -E -o "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+") || true
|
|
160
|
+
if [[ -n "$IPV4" ]]; then
|
|
161
|
+
echo "Host $HOST is mapped to $IPV4"
|
|
162
|
+
else
|
|
163
|
+
IPV4="127.0.0.1"
|
|
164
|
+
echo "Failed to resolve IPv4 for $HOST; using 127.0.0.1"
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
# ----------------------------------
|
|
168
|
+
# Parse remote spec (ip[:port])
|
|
169
|
+
# ----------------------------------
|
|
170
|
+
if [[ -n "$REMOTE_SPEC" ]]; then
|
|
171
|
+
if [[ "$REMOTE_SPEC" == *:* ]]; then
|
|
172
|
+
REMOTE_ADDR="${REMOTE_SPEC%%:*}"
|
|
173
|
+
REMOTE_PORT="${REMOTE_SPEC##*:}"
|
|
174
|
+
elif [[ "$REMOTE_SPEC" =~ ^[0-9]+$ ]]; then
|
|
175
|
+
REMOTE_PORT="$REMOTE_SPEC"
|
|
176
|
+
else
|
|
177
|
+
REMOTE_ADDR="$REMOTE_SPEC"
|
|
178
|
+
fi
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
# If user wants to bind to detected IPv4
|
|
182
|
+
if [[ "$BIND_HOST_IPV4" == true ]]; then
|
|
183
|
+
REMOTE_ADDR="$IPV4"
|
|
184
|
+
fi
|
|
185
|
+
|
|
186
|
+
# ----------------------------------
|
|
187
|
+
# Detect remote OS
|
|
188
|
+
# ----------------------------------
|
|
189
|
+
echo "Detecting OS on $HOST ..."
|
|
190
|
+
REMOTE_OS=$(ssh -o BatchMode=yes -o ConnectTimeout=5 "$HOST" 'uname' | head -n 1)
|
|
191
|
+
|
|
192
|
+
case "$REMOTE_OS" in
|
|
193
|
+
*[Ll]inux*)
|
|
194
|
+
OS_TYPE="linux"
|
|
195
|
+
CODE_CMD="code"
|
|
196
|
+
;;
|
|
197
|
+
*[Dd]arwin*)
|
|
198
|
+
OS_TYPE="mac"
|
|
199
|
+
CODE_CMD="/Applications/Visual\\ Studio\\ Code.app/Contents/Resources/app/bin/code"
|
|
200
|
+
;;
|
|
201
|
+
*[Mm][Ss][Yy][Ss]* | *[Mm][Ii][Nn][Gg][Ww]* | *[Cc][Yy][Gg][Ww]* | *[Ww]indows*)
|
|
202
|
+
OS_TYPE="windows"
|
|
203
|
+
CODE_CMD="\"d:/App/Microsoft VS Code/bin/code\""
|
|
204
|
+
;;
|
|
205
|
+
*)
|
|
206
|
+
OS_TYPE="unknown"
|
|
207
|
+
CODE_CMD="code"
|
|
208
|
+
;;
|
|
209
|
+
esac
|
|
210
|
+
|
|
211
|
+
echo "Detected remote OS: $OS_TYPE ($REMOTE_OS)"
|
|
212
|
+
|
|
213
|
+
cleanup_remote() {
|
|
214
|
+
echo "Cleaning up remote VS Code server on $HOST (host: $REMOTE_ADDR, port: $REMOTE_PORT)..."
|
|
215
|
+
case "$OS_TYPE" in
|
|
216
|
+
windows)
|
|
217
|
+
kill_remote_vscode_windows "$HOST" "$REMOTE_ADDR" "$REMOTE_PORT" || true ;;
|
|
218
|
+
linux|mac)
|
|
219
|
+
kill_remote_vscode_unix "$HOST" "$REMOTE_ADDR" "$REMOTE_PORT" || true ;;
|
|
220
|
+
*)
|
|
221
|
+
echo "Unknown remote OS; attempted generic cleanup..." ;;
|
|
222
|
+
esac
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
# Ensure we attempt cleanup on Ctrl+C or script exit
|
|
226
|
+
trap cleanup_remote INT TERM EXIT
|
|
227
|
+
|
|
228
|
+
# ----------------------------------
|
|
229
|
+
# Kill mode (explicit command)
|
|
230
|
+
# ----------------------------------
|
|
231
|
+
if [[ "$KILL_MODE" == true ]]; then
|
|
232
|
+
cleanup_remote
|
|
233
|
+
exit 0
|
|
234
|
+
fi
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
echo -e "\n------------------------------------------------------------------\n"
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
# ----------------------------------
|
|
241
|
+
# Start VS Code server
|
|
242
|
+
# ----------------------------------
|
|
243
|
+
echo "Starting VS Code server on $HOST (bind ${REMOTE_ADDR}:${REMOTE_PORT})..."
|
|
244
|
+
ssh -L "localhost:${PORT}:${REMOTE_ADDR}:${REMOTE_PORT}" "$HOST" \
|
|
245
|
+
"${CODE_CMD} serve-web --without-connection-token --accept-server-license-terms --host ${REMOTE_ADDR} --port ${REMOTE_PORT}"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: htoolbox
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: This package includes various tools for developers.
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Dynamic: license-file
|
|
9
|
+
|
|
10
|
+
# HToolbox
|
|
11
|
+
|
|
12
|
+
This repository provides some useful tools that fit my developing habits.
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
To install locally (editable mode) and get the `tmuxer` command:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install -e .
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or to install from source (non-editable):
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install .
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## tmuxer
|
|
29
|
+
|
|
30
|
+
Run the installed command:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
tmuxer -s mysession -n 3 --layout ev
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
This will start (or attach) to a tmux session named `mysession` with 3 panes using the `even-vertical` layout (`ev`).
|
|
37
|
+
|
|
38
|
+
## codeit
|
|
39
|
+
|
|
40
|
+
This tool is a quick script for starting vscode server on a remote server, and utilizing SSH port forwarding to access it locally.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
codeit <remote-host>
|
|
44
|
+
```
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
tmuxer.py,sha256=ZM_eKNb0XAY5rgsSX_hs6f9OrDFqf3q39WaN0IqvIZk,3090
|
|
2
|
+
htoolbox-0.1.0.data/scripts/codeit,sha256=hC5Jib7Ka8h5rgRnM4Jy-QFJLdrgVgKiqgFCdunk6oE,7904
|
|
3
|
+
htoolbox-0.1.0.dist-info/licenses/LICENSE,sha256=518WNThsP4IMvqMZ4PM-gXwqPvBwBlF4LvUmAJjVdDM,1070
|
|
4
|
+
htoolbox-0.1.0.dist-info/METADATA,sha256=7x7hq-aY88U9YaqWz1wo87cBZ7TuPhj0ZApJlT1Ez6o,871
|
|
5
|
+
htoolbox-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
6
|
+
htoolbox-0.1.0.dist-info/entry_points.txt,sha256=Lme2V1g3rRAq8tyxNmu70eS80h-LBeP03giTfP8E4fA,39
|
|
7
|
+
htoolbox-0.1.0.dist-info/top_level.txt,sha256=H5dSXvsTcYsU3PtS2Ya1w-GQct2fWxmbFHJ61GnP92E,7
|
|
8
|
+
htoolbox-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Zhanghan Wang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tmuxer
|
tmuxer.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import argparse
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def start_tmux_session(session_name, window_name=None, pane_index=None, new_panes=1, layout="even-vertical"):
|
|
7
|
+
"""Start and attach to a tmux session.
|
|
8
|
+
|
|
9
|
+
This function uses the `tmux` command-line tool. It is designed to be
|
|
10
|
+
called from the CLI entry point `main()` below, but can also be imported
|
|
11
|
+
and used programmatically.
|
|
12
|
+
"""
|
|
13
|
+
# Start a new tmux session detached
|
|
14
|
+
os.system(f"tmux new-session -d -s {session_name}")
|
|
15
|
+
|
|
16
|
+
# Create a new window if specified
|
|
17
|
+
if window_name:
|
|
18
|
+
os.system(f"tmux new-window -t {session_name} -n {window_name}")
|
|
19
|
+
|
|
20
|
+
# Create new panes if specified
|
|
21
|
+
for i in range(1, new_panes):
|
|
22
|
+
os.system(f"tmux split-window -t {session_name}")
|
|
23
|
+
os.system(f"tmux select-layout -t {session_name} {layout}")
|
|
24
|
+
|
|
25
|
+
# Export IID environment variable into each pane
|
|
26
|
+
for i in range(new_panes):
|
|
27
|
+
os.system(f"tmux send-keys -t {session_name}.{i} 'export IID={i}' C-m")
|
|
28
|
+
|
|
29
|
+
# Select the specified pane if provided
|
|
30
|
+
if pane_index is not None:
|
|
31
|
+
os.system(f"tmux select-pane -t {session_name}:{pane_index}")
|
|
32
|
+
|
|
33
|
+
# Attach to the tmux session
|
|
34
|
+
os.system(f"tmux attach-session -t {session_name}")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _normalize_layout(layout_arg: str) -> str:
|
|
38
|
+
v = layout_arg.lower().strip()
|
|
39
|
+
if v == "eh":
|
|
40
|
+
return "even-horizontal"
|
|
41
|
+
if v == "ev":
|
|
42
|
+
return "even-vertical"
|
|
43
|
+
if v == "mh":
|
|
44
|
+
return "main-horizontal"
|
|
45
|
+
if v == "mv":
|
|
46
|
+
return "main-vertical"
|
|
47
|
+
if v == "t":
|
|
48
|
+
return "tiled"
|
|
49
|
+
return layout_arg
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def main():
|
|
53
|
+
"""Console entry point for the `tmuxer` command.
|
|
54
|
+
|
|
55
|
+
Accepts an optional argv list (for testing). Returns 0 on success, may
|
|
56
|
+
raise exceptions for misuse.
|
|
57
|
+
"""
|
|
58
|
+
parser = argparse.ArgumentParser(description="Tmux session starter")
|
|
59
|
+
parser.add_argument("-n", "--num_panes", type=int, required=True, help="Number of new panes to create")
|
|
60
|
+
parser.add_argument("-s", "--session", type=str, required=True, help="Name of the tmux session")
|
|
61
|
+
parser.add_argument("-w", "--window", type=str, help="Name of the tmux window")
|
|
62
|
+
parser.add_argument("-p", "--pane", type=int, help="Index of the tmux pane")
|
|
63
|
+
parser.add_argument("--layout", type=str, default="even-vertical", help="Layout for the tmux panes")
|
|
64
|
+
parser.add_argument(
|
|
65
|
+
"--kill", action="store_true", help="Kill existing tmux session with the same name before starting a new one"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
args = parser.parse_args()
|
|
69
|
+
|
|
70
|
+
if args.kill and args.session:
|
|
71
|
+
# Prefer to silence output from tmux kill-session
|
|
72
|
+
os.system(f"tmux kill-session -t {args.session} >/dev/null 2>&1")
|
|
73
|
+
|
|
74
|
+
if args.num_panes < 1:
|
|
75
|
+
raise ValueError("Number of new panes must be at least 1")
|
|
76
|
+
|
|
77
|
+
layout = _normalize_layout(args.layout)
|
|
78
|
+
|
|
79
|
+
# If session name is not provided, let tmux create a session with default name
|
|
80
|
+
start_tmux_session(
|
|
81
|
+
session_name=args.session,
|
|
82
|
+
window_name=args.window,
|
|
83
|
+
pane_index=args.pane,
|
|
84
|
+
new_panes=args.num_panes,
|
|
85
|
+
layout=layout,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if __name__ == "__main__":
|
|
90
|
+
main()
|