pinggy 0.6.1 → 0.6.3

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.
@@ -0,0 +1,387 @@
1
+ #!/bin/sh
2
+ # Installs or updates the Pinggy CLI on macOS and Linux, then signs this machine in as a Pinggy device.
3
+ #
4
+ # curl -fsSL https://pinggy.io/install.sh | sh
5
+ # curl -fsSL https://pinggy.io/install.sh | DEVICE_AGENT_TOKEN=<token> sh
6
+ #
7
+ # The binary goes to ~/.pinggy/bin/pinggy, and a symlink in ~/.local/bin puts it on PATH. No sudo: every write is
8
+ # under $HOME. When ~/.local/bin is not on PATH, 1 marked block goes into the shell's rc file, once.
9
+ #
10
+ # Setting, from the dashboard's command:
11
+ # DEVICE_AGENT_TOKEN a device token. An older pinggy updates without asking, then `pinggy device connect` runs
12
+ # with no email and no code. The token reaches it in its environment, never on a command line.
13
+ # Set it on sh, not on curl, so curl never holds it
14
+ #
15
+ # Overrides, for dev and tests:
16
+ # PINGGY_VERSION a release tag, such as v0.6.1. Default: the latest release
17
+ # PINGGY_BINARY_URL a full binary URL, file:// included. Replaces the installed binary without a version check
18
+ # PINGGY_MANAGE a non-production dashboard host, passed to `pinggy device login` or `connect` as --manage
19
+ #
20
+ # The source lives in Pinggy-io/cli-js, scripts/install/. A release copies it to pinggy.io.
21
+ # Design: docs/pinggy-devices/install-script-plan.md in the pinggy_backend repo.
22
+ #
23
+ # Nothing runs until the last line. A download cut off halfway defines some functions and runs no command.
24
+
25
+ set -eu
26
+
27
+ RELEASES_URL="https://github.com/Pinggy-io/cli-js/releases"
28
+ INSTALL_DIR="$HOME/.pinggy/bin"
29
+ INSTALL_PATH="$INSTALL_DIR/pinggy"
30
+ LINK_DIR="$HOME/.local/bin"
31
+ LINK_PATH="$LINK_DIR/pinggy"
32
+ RC_MARKER="# Added by the Pinggy installer"
33
+ VERSION_PREFIX="Pinggy CLI version: "
34
+
35
+ say() {
36
+ printf '%s\n' "$*"
37
+ }
38
+
39
+ fail() {
40
+ printf 'pinggy install: %s\n' "$*" >&2
41
+ exit 1
42
+ }
43
+
44
+ # Shows a path under $HOME as ~/..., for messages only. The ~ is text, never expanded.
45
+ # shellcheck disable=SC2088
46
+ shown() {
47
+ case "$1" in
48
+ "$HOME"/*) printf '~/%s' "${1#"$HOME"/}" ;;
49
+ *) printf '%s' "$1" ;;
50
+ esac
51
+ }
52
+
53
+ has_command() {
54
+ command -v "$1" >/dev/null 2>&1
55
+ }
56
+
57
+ # An empty DEVICE_AGENT_TOKEN counts as unset, as it does in the CLI.
58
+ has_device_token() {
59
+ [ -n "${DEVICE_AGENT_TOKEN:-}" ]
60
+ }
61
+
62
+ # A script piped into sh reads itself from stdin, so the keyboard is /dev/tty. CI and `docker run` without -t
63
+ # have no /dev/tty to open.
64
+ has_terminal() {
65
+ (exec </dev/tty) 2>/dev/null
66
+ }
67
+
68
+ # Enter, or no terminal, means yes. Only an answer starting with n means no.
69
+ confirm() {
70
+ if ! has_terminal; then
71
+ return 0
72
+ fi
73
+ printf '%s' "$1" >/dev/tty
74
+ answer=""
75
+ read -r answer </dev/tty || true
76
+ case "$answer" in
77
+ [Nn]*) return 1 ;;
78
+ *) return 0 ;;
79
+ esac
80
+ }
81
+
82
+ detect_os() {
83
+ kernel=$(uname -s)
84
+ case "$kernel" in
85
+ Darwin) os=macos ;;
86
+ Linux) os=linux ;;
87
+ *) fail "unsupported OS: $kernel. The installer runs on macOS and Linux. On Windows, run in PowerShell: irm https://pinggy.io/install.ps1 | iex" ;;
88
+ esac
89
+ }
90
+
91
+ # The Linux binaries link against glibc. ldd answers first: Debian with the musl package has /lib/ld-musl-*, but
92
+ # its ldd is glibc, and the binary runs there. The loader file counts only when ldd is missing.
93
+ require_glibc() {
94
+ if has_command ldd; then
95
+ if ldd --version 2>&1 | grep -qi musl; then
96
+ fail_musl
97
+ fi
98
+ return 0
99
+ fi
100
+ for musl_loader in /lib/ld-musl-*; do
101
+ if [ -e "$musl_loader" ]; then
102
+ fail_musl
103
+ fi
104
+ done
105
+ }
106
+
107
+ fail_musl() {
108
+ fail "this Linux uses musl libc, as Alpine does. The pinggy binaries need glibc, as on Debian, Ubuntu or Fedora."
109
+ }
110
+
111
+ detect_cpu() {
112
+ machine=$(uname -m)
113
+ case "$machine" in
114
+ x86_64 | amd64) cpu=x64 ;;
115
+ arm64 | aarch64) cpu=arm64 ;;
116
+ *) fail "unsupported CPU: $machine. The pinggy binaries are built for x64 and arm64." ;;
117
+ esac
118
+ # A shell under Rosetta on Apple Silicon reports x86_64. The arm64 binary runs there natively.
119
+ if [ "$os" = macos ] && [ "$cpu" = x64 ] && [ "$(sysctl -n sysctl.proc_translated 2>/dev/null || true)" = 1 ]; then
120
+ cpu=arm64
121
+ fi
122
+ }
123
+
124
+ require_downloader() {
125
+ if ! has_command curl && ! has_command wget; then
126
+ fail "the installer needs curl or wget. Install either one, then run it again."
127
+ fi
128
+ }
129
+
130
+ # GitHub redirects /releases/latest to /releases/tag/<tag>. Reading the redirect costs no API call, so the
131
+ # API's 60 requests an hour per IP do not apply.
132
+ resolve_target_tag() {
133
+ if [ -n "${PINGGY_VERSION:-}" ]; then
134
+ case "$PINGGY_VERSION" in
135
+ v*) tag="$PINGGY_VERSION" ;;
136
+ *) tag="v$PINGGY_VERSION" ;;
137
+ esac
138
+ return 0
139
+ fi
140
+ if has_command curl; then
141
+ latest_url=$(curl -fsSIL --proto '=https' -o /dev/null -w '%{url_effective}' "$RELEASES_URL/latest") \
142
+ || fail "could not reach $RELEASES_URL/latest"
143
+ else
144
+ latest_url=$(wget -S --spider "$RELEASES_URL/latest" 2>&1 \
145
+ | sed -n 's/^ *Location: *\([^ ]*\).*/\1/p' | tr -d '\r' | tail -n 1)
146
+ fi
147
+ tag=${latest_url##*/}
148
+ case "$tag" in
149
+ v[0-9]*) ;;
150
+ *) fail "could not read the latest release tag from $RELEASES_URL/latest" ;;
151
+ esac
152
+ }
153
+
154
+ # Prints the version a binary reports, or nothing when it does not run. Under `curl | sh`, stdin is the rest of
155
+ # this script, so the binary gets /dev/null instead.
156
+ binary_version() {
157
+ "$1" --version </dev/null 2>/dev/null | sed -n "s/^$VERSION_PREFIX//p" | head -n 1 || true
158
+ }
159
+
160
+ download() {
161
+ url="$1"
162
+ destination="$2"
163
+ case "$url" in
164
+ file://*)
165
+ cp "${url#file://}" "$destination"
166
+ ;;
167
+ https://*)
168
+ if has_command curl; then
169
+ if [ -t 2 ]; then
170
+ curl -fL --proto '=https' --tlsv1.2 --progress-bar -o "$destination" "$url"
171
+ else
172
+ curl -fsSL --proto '=https' --tlsv1.2 -o "$destination" "$url"
173
+ fi
174
+ else
175
+ wget -q --https-only -O "$destination" "$url"
176
+ fi
177
+ ;;
178
+ *)
179
+ fail "PINGGY_BINARY_URL must start with https:// or file://"
180
+ ;;
181
+ esac
182
+ }
183
+
184
+ # Node's error line names the cause, such as a missing libssl.so.3 on a minimal Debian image. A binary for another
185
+ # CPU prints nothing useful, and the message below already names the platform.
186
+ fail_does_not_run() {
187
+ run_error=$("$download_path" --version </dev/null 2>&1 | grep -E '^[A-Za-z]*Error: ' | head -n 1 || true)
188
+ if [ -n "$run_error" ]; then
189
+ printf '%s\n' "$run_error" >&2
190
+ fi
191
+ fail "the downloaded file does not run on this machine ($os-$cpu). The installed pinggy is unchanged."
192
+ }
193
+
194
+ remove_partial_download() {
195
+ if [ -n "${download_path:-}" ]; then
196
+ rm -f "$download_path"
197
+ fi
198
+ }
199
+
200
+ # Writes the binary to a temp file in the install directory, so the final mv is a rename on 1 filesystem. A
201
+ # running agent keeps the old inode, so replacing the file under it is safe.
202
+ install_binary() {
203
+ if [ -n "${PINGGY_BINARY_URL:-}" ]; then
204
+ url="$PINGGY_BINARY_URL"
205
+ else
206
+ url="$RELEASES_URL/download/$tag/pinggy-$os-$cpu"
207
+ fi
208
+
209
+ mkdir -p "$INSTALL_DIR"
210
+ download_path=$(mktemp "$INSTALL_DIR/pinggy.download.XXXXXX")
211
+ trap remove_partial_download EXIT
212
+ trap 'exit 130' INT
213
+ trap 'exit 143' TERM
214
+
215
+ say "Downloading $url"
216
+ download "$url" "$download_path" || fail "the download failed: $url"
217
+ chmod 755 "$download_path"
218
+
219
+ downloaded_version=$(binary_version "$download_path")
220
+ if [ -z "$downloaded_version" ]; then
221
+ fail_does_not_run
222
+ fi
223
+
224
+ mv -f "$download_path" "$INSTALL_PATH"
225
+ download_path=""
226
+ say "Installed pinggy $downloaded_version to $(shown "$INSTALL_PATH")"
227
+ }
228
+
229
+ # Steps 2 to 6 of the plan: compare with the installed binary, then download only when needed.
230
+ install_or_update() {
231
+ if [ -n "${PINGGY_BINARY_URL:-}" ]; then
232
+ install_binary
233
+ return 0
234
+ fi
235
+
236
+ resolve_target_tag
237
+ target_version=${tag#v}
238
+
239
+ if [ ! -e "$INSTALL_PATH" ]; then
240
+ install_binary
241
+ return 0
242
+ fi
243
+
244
+ installed_version=$(binary_version "$INSTALL_PATH")
245
+ if [ -z "$installed_version" ]; then
246
+ say "$(shown "$INSTALL_PATH") does not run. Replacing it."
247
+ install_binary
248
+ elif [ "$installed_version" = "$target_version" ]; then
249
+ say "pinggy $installed_version is up to date."
250
+ elif has_device_token; then
251
+ # An older CLI ignores DEVICE_AGENT_TOKEN, so keeping it would leave the machine unconnected.
252
+ say "Updating pinggy $installed_version to $target_version."
253
+ install_binary
254
+ elif confirm "pinggy $installed_version is installed. Update to $target_version? [Y/n] "; then
255
+ install_binary
256
+ else
257
+ say "Keeping pinggy $installed_version."
258
+ fi
259
+ }
260
+
261
+ link_into_path() {
262
+ mkdir -p "$LINK_DIR"
263
+ if [ -L "$LINK_PATH" ]; then
264
+ previous_target=$(readlink "$LINK_PATH" || true)
265
+ if [ "$previous_target" != "$INSTALL_PATH" ]; then
266
+ say "Replacing $(shown "$LINK_PATH"), a link to $(shown "$previous_target")"
267
+ fi
268
+ elif [ -e "$LINK_PATH" ]; then
269
+ say "Replacing $(shown "$LINK_PATH")"
270
+ fi
271
+ ln -sf "$INSTALL_PATH" "$LINK_PATH"
272
+
273
+ case ":$PATH:" in
274
+ *":$LINK_DIR:"* | *":$LINK_DIR/:"*) warn_if_shadowed ;;
275
+ *) add_link_dir_to_rc_file ;;
276
+ esac
277
+ }
278
+
279
+ # The rc block puts ~/.local/bin first, so this matters only when ~/.local/bin was already on PATH.
280
+ warn_if_shadowed() {
281
+ found_path=$(command -v pinggy 2>/dev/null || true)
282
+ if [ -n "$found_path" ] && [ "$found_path" != "$LINK_PATH" ]; then
283
+ say "Another pinggy, $(shown "$found_path"), comes before $(shown "$LINK_DIR") on PATH, so the pinggy command runs it."
284
+ say "Remove it, or move $(shown "$LINK_DIR") earlier in PATH."
285
+ fi
286
+ }
287
+
288
+ # The rc file the shell in $SHELL reads at startup. bash on macOS reads only the first of ~/.bash_profile,
289
+ # ~/.bash_login and ~/.profile, so a new ~/.bash_profile would hide an existing ~/.profile.
290
+ choose_rc_file() {
291
+ case "$(basename "${SHELL:-sh}")" in
292
+ zsh)
293
+ rc_file="${ZDOTDIR:-$HOME}/.zshrc"
294
+ ;;
295
+ bash)
296
+ if [ "$os" = linux ]; then
297
+ rc_file="$HOME/.bashrc"
298
+ elif [ -e "$HOME/.bash_profile" ]; then
299
+ rc_file="$HOME/.bash_profile"
300
+ elif [ -e "$HOME/.bash_login" ]; then
301
+ rc_file="$HOME/.bash_login"
302
+ elif [ -e "$HOME/.profile" ]; then
303
+ rc_file="$HOME/.profile"
304
+ else
305
+ rc_file="$HOME/.bash_profile"
306
+ fi
307
+ ;;
308
+ fish)
309
+ rc_file="$HOME/.config/fish/conf.d/pinggy.fish"
310
+ ;;
311
+ *)
312
+ rc_file="$HOME/.profile"
313
+ ;;
314
+ esac
315
+ }
316
+
317
+ add_link_dir_to_rc_file() {
318
+ choose_rc_file
319
+ if [ -f "$rc_file" ] && grep -qF "$RC_MARKER" "$rc_file"; then
320
+ say "$(shown "$LINK_DIR") is not on PATH in this terminal. Run: source $(shown "$rc_file")"
321
+ return 0
322
+ fi
323
+
324
+ mkdir -p "$(dirname "$rc_file")"
325
+ # The rc file expands $HOME and $PATH at each shell start, not here.
326
+ # shellcheck disable=SC2016
327
+ case "$rc_file" in
328
+ *.fish) path_line='fish_add_path "$HOME/.local/bin"' ;;
329
+ *) path_line='export PATH="$HOME/.local/bin:$PATH"' ;;
330
+ esac
331
+ printf '\n%s\n%s\n' "$RC_MARKER" "$path_line" >>"$rc_file"
332
+ say "Added $(shown "$LINK_DIR") to PATH in $(shown "$rc_file"). New terminals find pinggy."
333
+ say "In this terminal, run: source $(shown "$rc_file")"
334
+ }
335
+
336
+ # Runs the installed file by its full path, so an npm-installed pinggy earlier on PATH does not answer. connect reads
337
+ # DEVICE_AGENT_TOKEN from the environment this script passes on, and reads no key, so it runs the same with no
338
+ # terminal.
339
+ connect_with_device_token() {
340
+ export DEVICE_AGENT_TOKEN
341
+ say ""
342
+ if [ -n "${PINGGY_MANAGE:-}" ]; then
343
+ exec "$INSTALL_PATH" device connect --manage "$PINGGY_MANAGE" </dev/null
344
+ fi
345
+ exec "$INSTALL_PATH" device connect </dev/null
346
+ }
347
+
348
+ # Runs the installed file by its full path, so an npm-installed pinggy earlier on PATH does not answer.
349
+ start_sign_in() {
350
+ if [ -n "${PINGGY_MANAGE:-}" ]; then
351
+ login_command="pinggy device login --manage $PINGGY_MANAGE"
352
+ else
353
+ login_command="pinggy device login"
354
+ fi
355
+
356
+ if ! has_terminal; then
357
+ say "Installed. To sign this machine in, run: $login_command"
358
+ say "On a machine without a keyboard, add the device in the dashboard and run: pinggy device connect --token <TOKEN>"
359
+ return 0
360
+ fi
361
+
362
+ say ""
363
+ if [ -n "${PINGGY_MANAGE:-}" ]; then
364
+ exec "$INSTALL_PATH" device login --manage "$PINGGY_MANAGE" </dev/tty
365
+ fi
366
+ exec "$INSTALL_PATH" device login </dev/tty
367
+ }
368
+
369
+ main() {
370
+ detect_os
371
+ if [ "$os" = linux ]; then
372
+ require_glibc
373
+ fi
374
+ detect_cpu
375
+ case "${PINGGY_BINARY_URL:-}" in
376
+ file://*) ;;
377
+ *) require_downloader ;;
378
+ esac
379
+ install_or_update
380
+ link_into_path
381
+ if has_device_token; then
382
+ connect_with_device_token
383
+ fi
384
+ start_sign_in
385
+ }
386
+
387
+ main "$@"
@@ -1,11 +0,0 @@
1
- import {
2
- TunnelAlreadyRunningError,
3
- TunnelManager
4
- } from "./chunk-LV3J6TAV.js";
5
- import "./chunk-UB26QJ4T.js";
6
- import "./chunk-JQAQRJPE.js";
7
- import "./chunk-GBYF2H4H.js";
8
- export {
9
- TunnelAlreadyRunningError,
10
- TunnelManager
11
- };
@@ -1,6 +0,0 @@
1
- import {
2
- IPCClient
3
- } from "./chunk-I4HXBP5G.js";
4
- export {
5
- IPCClient
6
- };