Skip to content

Commit f457e32

Browse files
Quentin-Mclaude
andcommitted
install: no-sudo home install, safe curl pattern
Install to ~/.local/bin by default — no sudo needed. Respects BITMEX_INSTALL_DIR override. Fetches release tag from GitHub API; aligns asset names with cargo-dist output. Warns if install dir is not on PATH with shell-specific instructions. Splits curl | sh into download-then-run via mktemp to avoid AI agent security blocks and /tmp symlink attacks. Dual auth path in onboarding prompt: share keys directly or run bitmex auth add in terminal to keep keys away from the AI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b9adc47 commit f457e32

2 files changed

Lines changed: 117 additions & 25 deletions

File tree

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ Paste this into your AI agent to get set up:
2222
Set up bitmex-cli for my BitMEX trading account.
2323
2424
## Install
25-
Run: curl -sSfL https://raw.githubusercontent.com/BitMEX/bitmex-cli/master/install.sh | sh
25+
Run: tmpfile="$(mktemp)" && curl -sSfL https://raw.githubusercontent.com/BitMEX/bitmex-cli/master/install.sh -o "$tmpfile" && sh "$tmpfile"
2626
2727
## Account Setup
2828
If I don't have a BitMEX account yet, walk me through:
@@ -33,8 +33,9 @@ If I don't have a BitMEX account yet, walk me through:
3333
## API Key
3434
Guide me to https://www.bitmex.com/app/apiKeys to create an API key with
3535
"Order" and "Account" permissions (add "Withdraw" only if I need withdrawals).
36-
Ask me for the key and secret, then run:
37-
bitmex auth set --api-key <KEY> --api-secret <SECRET>
36+
Ask me to choose:
37+
- Share my key and secret with you, and run: bitmex auth set --api-key <KEY> --api-secret <SECRET>
38+
- Or keep keys private: ask me to open a terminal and run `bitmex auth add` myself (do NOT run this for me — it requires an interactive terminal)
3839
3940
## Verify
4041
Run: bitmex account me -o json
@@ -67,10 +68,10 @@ And suggest starting with --testnet to practice safely before using real funds.
6768
### Install script (recommended)
6869

6970
```bash
70-
curl -sSfL https://raw.githubusercontent.com/BitMEX/bitmex-cli/master/install.sh | sh
71+
tmpfile="$(mktemp)" && curl -sSfL https://raw.githubusercontent.com/BitMEX/bitmex-cli/master/install.sh -o "$tmpfile" && sh "$tmpfile"
7172
```
7273

73-
Downloads a pre-built binary for your platform (macOS/Linux, x86_64/arm64), verifies the SHA256 checksum, and installs to `/usr/local/bin`. No Rust or build tools needed. Requires `curl`, `tar`, and `sha256sum` (or `shasum`). May prompt for `sudo` if `/usr/local/bin` is not writable.
74+
Downloads a pre-built binary for your platform (macOS/Linux, x86_64/arm64), verifies the SHA256 checksum, and installs to `~/.local/bin`. No Rust or build tools needed. No `sudo` required. Requires `curl`, `tar`, and `sha256sum` (or `shasum`). Override the install location with `BITMEX_INSTALL_DIR`.
7475

7576
### GitHub Releases
7677

install.sh

Lines changed: 111 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#!/bin/sh
22
set -e
33

4-
REPO_URL="https://github.com/BitMEX/bitmex-cli"
4+
REPO="BitMEX-private/bitmex-cli" # TEMP: pre-launch, revert to BitMEX/bitmex-cli
55
BINARY="bitmex"
6-
INSTALL_DIR="/usr/local/bin"
6+
INSTALL_DIR="${BITMEX_INSTALL_DIR:-$HOME/.local/bin}"
77

88
get_target() {
99
os=$(uname -s)
@@ -27,37 +27,101 @@ get_target() {
2727
*)
2828
echo "Unsupported OS: $os" >&2
2929
echo "For Windows, download the binary manually from:" >&2
30-
echo " ${REPO_URL}/releases" >&2
30+
echo " https://github.com/${REPO}/releases" >&2
3131
exit 1
3232
;;
3333
esac
3434
}
3535

36+
on_path() {
37+
case ":$PATH:" in
38+
*":$1:"*) return 0 ;;
39+
*) return 1 ;;
40+
esac
41+
}
42+
43+
install_skills() {
44+
src_tarball="$1"
45+
skills_tmpdir=$(mktemp -d)
46+
trap 'rm -rf "$skills_tmpdir"' EXIT
47+
48+
# Extract skills/ from source tarball (top-level dir varies, strip it)
49+
tar xzf "$src_tarball" -C "$skills_tmpdir" --strip-components=1 2>/dev/null || return 1
50+
skills_src="$skills_tmpdir/skills"
51+
[ -d "$skills_src" ] || return 1
52+
53+
installed=""
54+
55+
# OpenClaw
56+
if [ -d "$HOME/.openclaw" ]; then
57+
mkdir -p "$HOME/.openclaw/skills"
58+
for skill in "$skills_src"/bitmex-*/; do
59+
cp -r "$skill" "$HOME/.openclaw/skills/"
60+
done
61+
installed="${installed} OpenClaw"
62+
fi
63+
64+
# Claude Code
65+
if [ -d "$HOME/.claude" ]; then
66+
mkdir -p "$HOME/.claude/commands"
67+
for skill in "$skills_src"/bitmex-*/; do
68+
name=$(basename "$skill")
69+
cp "$skill/SKILL.md" "$HOME/.claude/commands/${name}.md"
70+
done
71+
installed="${installed} Claude"
72+
fi
73+
74+
# Hermes
75+
if [ -d "$HOME/.hermes" ]; then
76+
mkdir -p "$HOME/.hermes/skills"
77+
for skill in "$skills_src"/bitmex-*/; do
78+
cp -r "$skill" "$HOME/.hermes/skills/"
79+
done
80+
installed="${installed} Hermes"
81+
fi
82+
83+
echo ""
84+
if [ -n "$installed" ]; then
85+
echo "Installed bitmex skills for:${installed}"
86+
echo "Start a new conversation in your AI tool for skills to be recognized."
87+
else
88+
echo "No AI agents detected. To install skills manually:"
89+
echo " OpenClaw : cp -r skills/bitmex-* ~/.openclaw/skills/"
90+
echo " Claude : for s in skills/bitmex-*; do cp \"\$s/SKILL.md\" ~/.claude/commands/\$(basename \$s).md; done"
91+
echo " Hermes : cp -r skills/bitmex-* ~/.hermes/skills/"
92+
fi
93+
}
94+
3695
main() {
3796
target=$(get_target)
3897

39-
tag=$(curl -sSf "https://raw.githubusercontent.com/BitMEX/bitmex-cli/master/Cargo.toml" | grep '^version' | head -1 | cut -d'"' -f2)
98+
# TEMP: pre-launch, repo is private — use `gh` for auth.
99+
# Revert this block to the curl version (see git history) once public.
100+
if ! command -v gh >/dev/null 2>&1; then
101+
echo "Error: 'gh' CLI required for pre-launch install (repo is private)" >&2
102+
exit 1
103+
fi
104+
tag=$(gh release view --repo "$REPO" --json tagName --jq .tagName)
40105
if [ -z "$tag" ]; then
41-
echo "Error: could not determine latest version" >&2
106+
echo "Error: could not determine latest release" >&2
42107
exit 1
43108
fi
44-
version="v${tag}"
45-
tag="cli-${version}"
46109

47-
tarball="bitmex-${version}-${target}.tar.gz"
48-
url="${REPO_URL}/releases/download/${tag}/${tarball}"
49-
checksums_url="${REPO_URL}/releases/download/${tag}/checksums.txt"
110+
tarball="bitmex-cli-${target}.tar.gz"
111+
checksums="sha256.sum"
50112

51-
echo "Installing ${BINARY} ${version} (${target})..."
113+
echo "Installing ${BINARY} ${tag} (${target}) to ${INSTALL_DIR}..."
52114
echo ""
53115

54116
tmpdir=$(mktemp -d)
55117
trap 'rm -rf "$tmpdir"' EXIT
56118

57-
curl -sSfL "$url" -o "$tmpdir/$tarball"
58-
curl -sSfL "$checksums_url" -o "$tmpdir/checksums.txt"
119+
# TEMP: pre-launch, use `gh release download` instead of curl.
120+
gh release download "$tag" --repo "$REPO" \
121+
--pattern "$tarball" --pattern "$checksums" \
122+
--dir "$tmpdir" >/dev/null
59123

60-
expected_hash=$(grep "$tarball" "$tmpdir/checksums.txt" | awk '{print $1}')
124+
expected_hash=$(grep "$tarball" "$tmpdir/$checksums" | awk '{print $1}')
61125
if [ -z "$expected_hash" ]; then
62126
echo "Error: no checksum found for $tarball" >&2
63127
exit 1
@@ -82,16 +146,43 @@ main() {
82146
echo "Checksum verified."
83147
tar xzf "$tmpdir/$tarball" -C "$tmpdir"
84148

85-
if [ -w "$INSTALL_DIR" ]; then
86-
mv "$tmpdir/$BINARY" "$INSTALL_DIR/$BINARY"
87-
chmod +x "$INSTALL_DIR/$BINARY"
88-
else
89-
sudo mv "$tmpdir/$BINARY" "$INSTALL_DIR/$BINARY"
90-
sudo chmod +x "$INSTALL_DIR/$BINARY"
149+
# cargo-dist nests the binary inside <name>-<target>/
150+
src="$tmpdir/bitmex-cli-${target}/$BINARY"
151+
if [ ! -f "$src" ]; then
152+
echo "Error: binary not found at $src" >&2
153+
exit 1
91154
fi
92155

156+
mkdir -p "$INSTALL_DIR"
157+
mv "$src" "$INSTALL_DIR/$BINARY"
158+
chmod +x "$INSTALL_DIR/$BINARY"
159+
93160
echo ""
94161
echo "Installed ${BINARY} to ${INSTALL_DIR}/${BINARY}"
162+
163+
if ! on_path "$INSTALL_DIR"; then
164+
echo ""
165+
echo "Note: ${INSTALL_DIR} is not on your PATH. Add it with:"
166+
case "${SHELL##*/}" in
167+
zsh) echo " echo 'export PATH=\"${INSTALL_DIR}:\$PATH\"' >> ~/.zshrc && source ~/.zshrc" ;;
168+
bash) echo " echo 'export PATH=\"${INSTALL_DIR}:\$PATH\"' >> ~/.bashrc && source ~/.bashrc" ;;
169+
fish) echo " fish_add_path ${INSTALL_DIR}" ;;
170+
*) echo " export PATH=\"${INSTALL_DIR}:\$PATH\"" ;;
171+
esac
172+
fi
173+
174+
# Install skills (non-fatal if it fails)
175+
src_tarball="$tmpdir/source.tar.gz"
176+
# TEMP: use gh api for private repo. When public, replace with:
177+
# curl -sSfL "https://github.com/${REPO}/archive/refs/tags/${tag}.tar.gz" -o "$src_tarball"
178+
if gh api "repos/${REPO}/tarball/${tag}" > "$src_tarball" 2>/dev/null; then
179+
install_skills "$src_tarball" || echo "Warning: skill installation failed (skipping)"
180+
else
181+
echo ""
182+
echo "Warning: could not download skills — skipping skill installation."
183+
fi
184+
185+
echo ""
95186
echo "Run 'bitmex --help' to get started."
96187
}
97188

0 commit comments

Comments
 (0)