forked from rajveerb/wsl-clip-bridge
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.sh
More file actions
executable file
·319 lines (273 loc) · 9.68 KB
/
Copy pathinstall.sh
File metadata and controls
executable file
·319 lines (273 loc) · 9.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
#!/usr/bin/env bash
# install.sh — build clip-listener.exe and install the WSL bridge.
set -euo pipefail
WITH_SYSTEMD=0
WITH_BASHRC_AUTOSTART=0
WITH_WSL_BOOT_AUTOSTART=0
WITH_KEYBINDING=0
for arg in "$@"; do
case "$arg" in
--with-systemd) WITH_SYSTEMD=1 ;;
--with-autostart) WITH_BASHRC_AUTOSTART=1 ;;
--with-wsl-boot) WITH_WSL_BOOT_AUTOSTART=1 ;;
--with-keybinding) WITH_KEYBINDING=1 ;;
--help|-h)
cat <<'USAGE'
Usage: install.sh [--with-systemd] [--with-keybinding] [--with-autostart] [--with-wsl-boot]
--with-systemd RECOMMENDED. Install and enable a systemd user
service so the bridge survives closing the terminal
it was started from. clip-listener.exe dies silently
whenever its spawning WSL session ends — a service
is the only start method that isn't session-bound.
Requires systemd in WSL (default on Ubuntu 22.04+).
--with-keybinding Write ~/.claude/keybindings.json with
alt+v -> chat:imagePaste so Claude Code can paste
images on a key Windows Terminal does not eat.
Refuses to overwrite an existing keybindings.json.
--with-autostart LEGACY. Add a snippet to ~/.bashrc so the bridge
starts the first time a WSL shell opens. The bridge
then dies as soon as that shell's session closes.
Prefer --with-systemd.
--with-wsl-boot LEGACY. Edit /etc/wsl.conf [boot] command so the
bridge starts at WSL boot. Requires sudo. Prefer
--with-systemd.
For a turnkey WSL + Claude Code setup, use:
./install.sh --with-systemd --with-keybinding
Without any flag, the script only builds and installs the binaries.
USAGE
exit 0
;;
*)
echo "Unknown arg: $arg" >&2
echo "See: install.sh --help" >&2
exit 2
;;
esac
done
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INSTALL_BIN="$HOME/.local/bin"
INSTALL_SHARE="$HOME/.local/share/wsl-clip-bridge"
configure_claude_keybinding() {
local kb="$HOME/.claude/keybindings.json"
echo
echo "Configuring Claude Code keybinding in $kb"
mkdir -p "$(dirname "$kb")"
if [[ -f "$kb" ]]; then
if grep -q '"chat:imagePaste"' "$kb" 2>/dev/null && grep -q '"alt+v"' "$kb" 2>/dev/null; then
echo " alt+v -> chat:imagePaste already in $kb — no change"
return 0
fi
echo " $kb exists with other content; refusing to overwrite." >&2
echo " Manually add to its Chat context bindings:" >&2
echo ' "alt+v": "chat:imagePaste",' >&2
echo ' "ctrl+alt+v": "chat:imagePaste"' >&2
return 1
fi
cat >"$kb" <<'KBJSON'
{
"$schema": "https://www.schemastore.org/claude-code-keybindings.json",
"$docs": "https://code.claude.com/docs/en/keybindings",
"bindings": [
{
"context": "Chat",
"bindings": {
"alt+v": "chat:imagePaste",
"ctrl+alt+v": "chat:imagePaste"
}
}
]
}
KBJSON
echo " Wrote $kb (alt+v and ctrl+alt+v -> chat:imagePaste)"
echo " Active in new Claude Code sessions; restart Claude Code if it is running."
}
configure_systemd_service() {
echo
echo "Configuring systemd user service"
if ! systemctl --user is-system-running >/dev/null 2>&1; then
echo "ERROR: systemd user manager not running." >&2
echo " Enable systemd in /etc/wsl.conf ([boot] systemd=true), then" >&2
echo " 'wsl --shutdown' from Windows and reopen this terminal." >&2
return 1
fi
local unit_dir="$HOME/.config/systemd/user"
mkdir -p "$unit_dir"
install -m 0644 "$REPO_ROOT/wsl/wsl-clip-bridge.service" \
"$unit_dir/wsl-clip-bridge.service"
systemctl --user daemon-reload
systemctl --user enable --now wsl-clip-bridge.service
echo " Installed and started wsl-clip-bridge.service"
if [[ "$(loginctl show-user "$USER" -p Linger --value 2>/dev/null)" != "yes" ]]; then
echo
echo " NOTE: linger is off for $USER, so the user manager (and this"
echo " service) only runs while a session is open. Enable with:"
echo " sudo loginctl enable-linger $USER"
fi
}
configure_bashrc_autostart() {
local rc="$HOME/.bashrc"
local marker="# wsl-clip-bridge autostart"
echo
echo "Configuring autostart in $rc"
echo " WARNING: a bridge started from a shell dies as soon as that"
echo " shell's WSL session closes. Prefer: ./install.sh --with-systemd"
if grep -qF "$marker" "$rc" 2>/dev/null; then
echo " Snippet already present — no change."
return 0
fi
local ts
ts="$(date +%Y%m%d-%H%M%S)"
if [[ -e "$rc" ]]; then
cp "$rc" "${rc}.bak.${ts}"
echo " Backup: ${rc}.bak.${ts}"
fi
cat >>"$rc" <<'BASHRC'
# wsl-clip-bridge autostart
if [ -n "$WAYLAND_DISPLAY" ] && command -v wsl-clip-bridge >/dev/null 2>&1; then
if ! wsl-clip-bridge --status 2>/dev/null | grep -q running; then
nohup wsl-clip-bridge >/dev/null 2>&1 &
disown
fi
fi
BASHRC
echo " Appended snippet to $rc"
echo
echo "Active in new shells. To start in this shell:"
echo " nohup wsl-clip-bridge >/dev/null 2>&1 & disown"
}
configure_wslconf_autostart() {
local wslconf="/etc/wsl.conf"
local user="${USER:-$(whoami)}"
local bridge_path="$INSTALL_BIN/wsl-clip-bridge"
local cmd_value="su - $user -c 'nohup $bridge_path >/dev/null 2>&1 &'"
local new_line="command = $cmd_value"
echo
echo "Configuring WSL autostart in $wslconf"
echo " Adding: $new_line"
echo " (sudo required)"
local current=""
if sudo test -e "$wslconf"; then
current="$(sudo cat "$wslconf")"
fi
if grep -qF "$bridge_path" <<<"$current"; then
echo " Already configured — no change."
return 0
fi
local existing_cmd
existing_cmd="$(grep -E '^[[:space:]]*command[[:space:]]*=' <<<"$current" || true)"
if [[ -n "$existing_cmd" ]]; then
echo "ERROR: $wslconf already defines a [boot] command:" >&2
echo " $existing_cmd" >&2
echo "Merge manually by chaining the commands with ';' or '&'." >&2
return 1
fi
local new_content
if [[ -z "$current" ]]; then
new_content=$'[boot]\n'"$new_line"
elif grep -qE '^\[boot\]' <<<"$current"; then
new_content="$(awk -v line="$new_line" '
BEGIN { inserted = 0 }
/^\[boot\]/ && !inserted { print; print line; inserted = 1; next }
{ print }
' <<<"$current")"
else
new_content="$current"$'\n\n[boot]\n'"$new_line"
fi
local ts
ts="$(date +%Y%m%d-%H%M%S)"
if sudo test -e "$wslconf"; then
sudo cp "$wslconf" "${wslconf}.bak.${ts}"
echo " Backup: ${wslconf}.bak.${ts}"
fi
printf '%s\n' "$new_content" | sudo tee "$wslconf" >/dev/null
echo " Wrote $wslconf"
echo
echo "Apply with (run in Windows PowerShell):"
echo " wsl --shutdown"
echo "Then reopen this WSL terminal."
}
# ----- Locate Go -----
GO_BIN="${GO_BIN:-}"
if [[ -z "$GO_BIN" ]]; then
if command -v go >/dev/null 2>&1; then
GO_BIN="$(command -v go)"
elif [[ -x "$HOME/.local/go/bin/go" ]]; then
GO_BIN="$HOME/.local/go/bin/go"
else
echo "Go not found. Install it (e.g. to ~/.local/go) or set GO_BIN=/path/to/go" >&2
exit 1
fi
fi
echo "Using Go: $GO_BIN ($("$GO_BIN" version))"
# ----- Build the listener -----
echo "Building clip-listener.exe ..."
cd "$REPO_ROOT/windows/clip-listener"
GOOS=windows GOARCH=amd64 "$GO_BIN" build \
-trimpath \
-ldflags="-s -w" \
-o clip-listener.exe .
ls -lh clip-listener.exe
# ----- Install -----
mkdir -p "$INSTALL_BIN" "$INSTALL_SHARE"
install -m 0755 clip-listener.exe "$INSTALL_SHARE/clip-listener.exe"
install -m 0755 "$REPO_ROOT/wsl/wsl-clip-bridge" "$INSTALL_BIN/wsl-clip-bridge"
# ----- Prereq checks -----
if ! command -v wl-copy >/dev/null 2>&1; then
echo
echo "wl-copy not found. Install with:"
echo " sudo apt install wl-clipboard"
fi
case ":$PATH:" in
*":$INSTALL_BIN:"*) ;;
*)
echo
echo "WARNING: $INSTALL_BIN is not on PATH."
echo "Add to ~/.bashrc: export PATH=\"\$HOME/.local/bin:\$PATH\""
;;
esac
# ----- Optional service / autostart / keybinding -----
if [[ $WITH_SYSTEMD -eq 1 ]]; then
configure_systemd_service
fi
if [[ $WITH_BASHRC_AUTOSTART -eq 1 ]]; then
configure_bashrc_autostart
fi
if [[ $WITH_KEYBINDING -eq 1 ]]; then
configure_claude_keybinding
fi
if [[ $WITH_WSL_BOOT_AUTOSTART -eq 1 ]]; then
configure_wslconf_autostart
fi
cat <<EOF
Installed:
$INSTALL_SHARE/clip-listener.exe
$INSTALL_BIN/wsl-clip-bridge
EOF
if [[ $WITH_SYSTEMD -eq 1 ]]; then
cat <<'EOF'
The bridge runs as a systemd user service and restarts itself if the
Windows-side listener dies:
systemctl --user status wsl-clip-bridge.service
EOF
else
cat <<'EOF'
Start in this shell (foreground, ^C to stop the bash side):
wsl-clip-bridge
Note: any bridge started from a shell dies when that shell's session
closes. For a start method that survives, use:
./install.sh --with-systemd
EOF
fi
if [[ $WITH_SYSTEMD -eq 0 || $WITH_KEYBINDING -eq 0 ]]; then
cat <<'EOF'
Tip: for a turnkey setup, re-run with:
./install.sh --with-systemd --with-keybinding
EOF
fi
cat <<'EOF'
Verify:
1. Copy any image in Windows (Win+Shift+S)
2. wl-paste -l # should list image/png
3. In Claude Code, press Alt+V (or Ctrl+Alt+V) to paste the image
(If you have unbound Ctrl+V in Windows Terminal, Ctrl+V also works.)
EOF