Skip to content

Commit 23bbe43

Browse files
committed
feat: add CLI-only installer
1 parent 89b26ab commit 23bbe43

File tree

4 files changed

+230
-0
lines changed

4 files changed

+230
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Automatically deployed to GitHub Pages on push to `main`.
3939
The landing page hosts installer scripts:
4040

4141
- **macOS/Linux**: `curl -fsSL https://clawd.bot/install.sh | bash`
42+
- **macOS/Linux (CLI only, no onboarding)**: `curl -fsSL https://clawd.bot/install-cli.sh | bash`
4243
- **Windows**: `iwr -useb https://clawd.bot/install.ps1 | iex`
4344

4445
These scripts:

public/install-cli.sh

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Clawdbot CLI installer (non-interactive, no onboarding)
5+
# Usage: curl -fsSL https://clawd.bot/install-cli.sh | bash -s -- [--json] [--prefix <path>] [--version <ver>] [--node-version <ver>] [--onboard]
6+
7+
PREFIX="${CLAWDBOT_PREFIX:-${HOME}/.clawdbot}"
8+
CLAWDBOT_VERSION="${CLAWDBOT_VERSION:-latest}"
9+
NODE_VERSION="${CLAWDBOT_NODE_VERSION:-22.12.0}"
10+
JSON=0
11+
RUN_ONBOARD=0
12+
13+
print_usage() {
14+
cat <<EOF
15+
Usage: install-cli.sh [options]
16+
--json Emit NDJSON events (no human output)
17+
--prefix <path> Install prefix (default: ~/.clawdbot)
18+
--version <ver> Clawdbot version (default: latest)
19+
--node-version <ver> Node version (default: 22.12.0)
20+
--onboard Run "clawdbot onboard" after install
21+
--no-onboard Skip onboarding (default)
22+
EOF
23+
}
24+
25+
log() {
26+
if [[ "$JSON" -eq 0 ]]; then
27+
echo "$@"
28+
fi
29+
}
30+
31+
emit_json() {
32+
if [[ "$JSON" -eq 1 ]]; then
33+
printf '%s\n' "$1"
34+
fi
35+
}
36+
37+
fail() {
38+
local msg="$1"
39+
emit_json "{\"event\":\"error\",\"message\":\"${msg//"/\\\"}\"}"
40+
log "ERROR: $msg"
41+
exit 1
42+
}
43+
44+
require_bin() {
45+
local name="$1"
46+
if ! command -v "$name" >/dev/null 2>&1; then
47+
fail "Missing required binary: $name"
48+
fi
49+
}
50+
51+
parse_args() {
52+
while [[ $# -gt 0 ]]; do
53+
case "$1" in
54+
--json)
55+
JSON=1
56+
shift
57+
;;
58+
--prefix)
59+
PREFIX="$2"
60+
shift 2
61+
;;
62+
--version)
63+
CLAWDBOT_VERSION="$2"
64+
shift 2
65+
;;
66+
--node-version)
67+
NODE_VERSION="$2"
68+
shift 2
69+
;;
70+
--onboard)
71+
RUN_ONBOARD=1
72+
shift
73+
;;
74+
--no-onboard)
75+
RUN_ONBOARD=0
76+
shift
77+
;;
78+
--help|-h)
79+
print_usage
80+
exit 0
81+
;;
82+
*)
83+
fail "Unknown option: $1"
84+
;;
85+
esac
86+
done
87+
}
88+
89+
os_detect() {
90+
local os
91+
os="$(uname -s)"
92+
case "$os" in
93+
Darwin) echo "darwin" ;;
94+
Linux) echo "linux" ;;
95+
*) fail "Unsupported OS: $os" ;;
96+
esac
97+
}
98+
99+
arch_detect() {
100+
local arch
101+
arch="$(uname -m)"
102+
case "$arch" in
103+
arm64|aarch64) echo "arm64" ;;
104+
x86_64|amd64) echo "x64" ;;
105+
*) fail "Unsupported architecture: $arch" ;;
106+
esac
107+
}
108+
109+
node_dir() {
110+
echo "${PREFIX}/tools/node-v${NODE_VERSION}"
111+
}
112+
113+
node_bin() {
114+
echo "$(node_dir)/bin/node"
115+
}
116+
117+
npm_bin() {
118+
echo "$(node_dir)/bin/npm"
119+
}
120+
121+
install_node() {
122+
local os
123+
local arch
124+
local url
125+
local tmp
126+
local dir
127+
local current_major
128+
129+
os="$(os_detect)"
130+
arch="$(arch_detect)"
131+
dir="$(node_dir)"
132+
133+
if [[ -x "$(node_bin)" ]]; then
134+
current_major="$("$(node_bin)" -v 2>/dev/null | tr -d 'v' | cut -d'.' -f1 || echo "")"
135+
if [[ -n "$current_major" && "$current_major" -ge 22 ]]; then
136+
emit_json "{\"event\":\"step\",\"name\":\"node\",\"status\":\"skip\",\"path\":\"${dir//\"/\\\\\\\"}\"}"
137+
return
138+
fi
139+
fi
140+
141+
emit_json "{\"event\":\"step\",\"name\":\"node\",\"status\":\"start\",\"version\":\"${NODE_VERSION}\"}"
142+
log "Installing Node ${NODE_VERSION} (user-space)..."
143+
144+
mkdir -p "${PREFIX}/tools"
145+
tmp="$(mktemp -d)"
146+
url="https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-${os}-${arch}.tar.gz"
147+
148+
require_bin curl
149+
require_bin tar
150+
151+
curl -fsSL "$url" -o "$tmp/node.tgz"
152+
rm -rf "$dir"
153+
mkdir -p "$dir"
154+
tar -xzf "$tmp/node.tgz" -C "$dir" --strip-components=1
155+
rm -rf "$tmp"
156+
157+
ln -sfn "$dir" "${PREFIX}/tools/node"
158+
emit_json "{\"event\":\"step\",\"name\":\"node\",\"status\":\"ok\",\"version\":\"${NODE_VERSION}\"}"
159+
}
160+
161+
install_clawdbot() {
162+
emit_json "{\"event\":\"step\",\"name\":\"clawdbot\",\"status\":\"start\",\"version\":\"${CLAWDBOT_VERSION}\"}"
163+
log "Installing Clawdbot (${CLAWDBOT_VERSION})..."
164+
"$(npm_bin)" install -g --prefix "$PREFIX" "clawdbot@${CLAWDBOT_VERSION}"
165+
emit_json "{\"event\":\"step\",\"name\":\"clawdbot\",\"status\":\"ok\"}"
166+
}
167+
168+
resolve_clawdbot_version() {
169+
local version=""
170+
if [[ -x "${PREFIX}/bin/clawdbot" ]]; then
171+
version="$(${PREFIX}/bin/clawdbot --version 2>/dev/null | head -n 1 | tr -d '\r')"
172+
fi
173+
echo "$version"
174+
}
175+
176+
main() {
177+
parse_args "$@"
178+
179+
export PATH="$(node_dir)/bin:${PREFIX}/bin:${PATH}"
180+
181+
install_node
182+
install_clawdbot
183+
184+
local installed_version
185+
installed_version="$(resolve_clawdbot_version)"
186+
if [[ -n "$installed_version" ]]; then
187+
emit_json "{\"event\":\"done\",\"ok\":true,\"version\":\"${installed_version//"/\\\"}\"}"
188+
log "Clawdbot installed (${installed_version})."
189+
else
190+
emit_json "{\"event\":\"done\",\"ok\":true}"
191+
log "Clawdbot installed."
192+
fi
193+
194+
if [[ "$RUN_ONBOARD" -eq 1 ]]; then
195+
"${PREFIX}/bin/clawdbot" onboard
196+
fi
197+
}
198+
199+
main "$@"

scripts/e2e/Dockerfile

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
FROM debian:bookworm-slim
2+
3+
RUN apt-get update \
4+
&& apt-get install -y --no-install-recommends \
5+
bash \
6+
ca-certificates \
7+
curl \
8+
gzip \
9+
tar \
10+
&& rm -rf /var/lib/apt/lists/*
11+
12+
WORKDIR /app
13+
COPY . /app

scripts/e2e/install-cli-docker.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
5+
IMAGE_NAME="clawd-bot-install-cli-e2e"
6+
7+
echo "Building Docker image..."
8+
docker build -t "$IMAGE_NAME" -f "$ROOT_DIR/scripts/e2e/Dockerfile" "$ROOT_DIR"
9+
10+
echo "Running install-cli E2E..."
11+
docker run --rm -t "$IMAGE_NAME" bash -lc '
12+
set -euo pipefail
13+
install_prefix="/tmp/clawdbot"
14+
/app/public/install-cli.sh --json --no-onboard --prefix "$install_prefix" > /tmp/install.jsonl
15+
grep "\"event\":\"done\"" /tmp/install.jsonl
16+
"$install_prefix/bin/clawdbot" --version
17+
'

0 commit comments

Comments
 (0)