@atercates/claude-deck 0.2.10 → 0.2.12
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.
- package/package.json +1 -1
- package/scripts/install.sh +102 -149
package/package.json
CHANGED
package/scripts/install.sh
CHANGED
|
@@ -2,19 +2,18 @@
|
|
|
2
2
|
#
|
|
3
3
|
# ClaudeDeck Installer
|
|
4
4
|
#
|
|
5
|
-
#
|
|
5
|
+
# Install:
|
|
6
6
|
# curl -fsSL https://raw.githubusercontent.com/ATERCATES/claude-deck/main/scripts/install.sh | bash
|
|
7
7
|
#
|
|
8
|
-
# Options:
|
|
9
|
-
# bash install.sh --port 3011 --ssh-host myserver.com --ssh-port 22 -y
|
|
10
|
-
#
|
|
11
8
|
# Update:
|
|
12
|
-
#
|
|
9
|
+
# ~/.claude-deck/install.sh --update
|
|
10
|
+
#
|
|
11
|
+
# Options:
|
|
12
|
+
# --port 3011 --ssh-host myserver.com --ssh-port 22 -y
|
|
13
13
|
#
|
|
14
14
|
|
|
15
15
|
set -e
|
|
16
16
|
|
|
17
|
-
# Colors
|
|
18
17
|
RED='\033[0;31m'
|
|
19
18
|
GREEN='\033[0;32m'
|
|
20
19
|
BLUE='\033[0;34m'
|
|
@@ -29,29 +28,26 @@ log_warn() { echo -e "${YELLOW}==>${NC} $1"; }
|
|
|
29
28
|
log_error() { echo -e "${RED}==>${NC} $1"; }
|
|
30
29
|
|
|
31
30
|
INSTALL_DIR="$HOME/.claude-deck"
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
REPO_URL="https://github.com/ATERCATES/claude-deck.git"
|
|
32
|
+
NODE_MIN=24
|
|
34
33
|
|
|
35
|
-
# ─── Parse
|
|
34
|
+
# ─── Parse flags ──────────────────────────────────────────────────────────────
|
|
36
35
|
|
|
37
|
-
FLAG_PORT=""
|
|
38
|
-
|
|
39
|
-
FLAG_SSH_PORT=""
|
|
40
|
-
FLAG_NONINTERACTIVE=false
|
|
41
|
-
FLAG_UPDATE=false
|
|
36
|
+
FLAG_PORT="" FLAG_SSH_HOST="" FLAG_SSH_PORT=""
|
|
37
|
+
FLAG_NONINTERACTIVE=false FLAG_UPDATE=false
|
|
42
38
|
|
|
43
39
|
while [[ $# -gt 0 ]]; do
|
|
44
40
|
case "$1" in
|
|
45
|
-
--port)
|
|
46
|
-
--ssh-host)
|
|
47
|
-
--ssh-port)
|
|
48
|
-
--yes|-y)
|
|
49
|
-
--update|-u)
|
|
50
|
-
*)
|
|
41
|
+
--port) FLAG_PORT="$2"; shift 2 ;;
|
|
42
|
+
--ssh-host) FLAG_SSH_HOST="$2"; shift 2 ;;
|
|
43
|
+
--ssh-port) FLAG_SSH_PORT="$2"; shift 2 ;;
|
|
44
|
+
--yes|-y) FLAG_NONINTERACTIVE=true; shift ;;
|
|
45
|
+
--update|-u) FLAG_UPDATE=true; shift ;;
|
|
46
|
+
*) shift ;;
|
|
51
47
|
esac
|
|
52
48
|
done
|
|
53
49
|
|
|
54
|
-
# ───
|
|
50
|
+
# ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
55
51
|
|
|
56
52
|
ask() {
|
|
57
53
|
local prompt="$1" default="$2" var="$3"
|
|
@@ -68,59 +64,81 @@ ask() {
|
|
|
68
64
|
fi
|
|
69
65
|
}
|
|
70
66
|
|
|
71
|
-
|
|
67
|
+
ensure_node() {
|
|
68
|
+
[[ -x "$HOME/.n/bin/node" ]] && export PATH="$HOME/.n/bin:$PATH"
|
|
69
|
+
|
|
70
|
+
if command -v node &> /dev/null; then
|
|
71
|
+
local v=$(node --version | sed 's/v//' | cut -d. -f1)
|
|
72
|
+
if [[ "$v" -ge "$NODE_MIN" ]]; then
|
|
73
|
+
log_success "Node.js $(node --version) found"
|
|
74
|
+
return
|
|
75
|
+
fi
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
log_info "Installing Node.js $NODE_MIN..."
|
|
79
|
+
local N_PREFIX="$HOME/.n"
|
|
80
|
+
mkdir -p "$N_PREFIX"
|
|
81
|
+
curl -fsSL https://raw.githubusercontent.com/tj/n/master/bin/n -o /tmp/n && chmod +x /tmp/n
|
|
82
|
+
N_PREFIX="$N_PREFIX" /tmp/n "$NODE_MIN" && rm -f /tmp/n
|
|
83
|
+
export PATH="$N_PREFIX/bin:$PATH"
|
|
84
|
+
log_success "Node.js $(node --version) installed"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
ensure_pnpm() {
|
|
88
|
+
if command -v pnpm &> /dev/null; then
|
|
89
|
+
log_success "pnpm $(pnpm --version) found"
|
|
90
|
+
else
|
|
91
|
+
log_info "Installing pnpm..."
|
|
92
|
+
npm install -g pnpm > /dev/null 2>&1
|
|
93
|
+
log_success "pnpm $(pnpm --version) installed"
|
|
94
|
+
fi
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
app_version() {
|
|
98
|
+
node -e "console.log(require('$INSTALL_DIR/package.json').version)" 2>/dev/null || echo "unknown"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# ─── Update ───────────────────────────────────────────────────────────────────
|
|
72
102
|
|
|
73
103
|
if [[ "$FLAG_UPDATE" == true ]]; then
|
|
74
104
|
echo ""
|
|
75
105
|
echo -e "${BOLD} ClaudeDeck Update${NC}"
|
|
76
106
|
echo ""
|
|
77
107
|
|
|
78
|
-
if [[ ! -d "$INSTALL_DIR" ]]; then
|
|
108
|
+
if [[ ! -d "$INSTALL_DIR/.git" ]]; then
|
|
79
109
|
log_error "ClaudeDeck is not installed. Run without --update first."
|
|
80
110
|
exit 1
|
|
81
111
|
fi
|
|
82
112
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
113
|
+
ensure_node
|
|
114
|
+
ensure_pnpm
|
|
86
115
|
cd "$INSTALL_DIR"
|
|
87
|
-
CURRENT=$(node -e "console.log(require('./node_modules/$PKG_NAME/package.json').version)" 2>/dev/null || echo "unknown")
|
|
88
|
-
log_info "Current version: $CURRENT"
|
|
89
116
|
|
|
90
|
-
|
|
91
|
-
|
|
117
|
+
CURRENT=$(app_version)
|
|
118
|
+
log_info "Current version: $CURRENT"
|
|
92
119
|
|
|
93
|
-
|
|
120
|
+
log_info "Pulling latest..."
|
|
121
|
+
git pull --ff-only
|
|
94
122
|
|
|
123
|
+
NEW=$(app_version)
|
|
95
124
|
if [[ "$CURRENT" == "$NEW" ]]; then
|
|
96
125
|
log_success "Already on latest version ($NEW)"
|
|
97
126
|
else
|
|
98
127
|
log_success "Updated: $CURRENT -> $NEW"
|
|
99
128
|
fi
|
|
100
129
|
|
|
101
|
-
# Copy source and rebuild
|
|
102
|
-
log_info "Copying source..."
|
|
103
|
-
rsync -a --delete \
|
|
104
|
-
--exclude='node_modules' --exclude='.next' --exclude='.env' --exclude='*.db' --exclude='*.db-journal' \
|
|
105
|
-
"$INSTALL_DIR/node_modules/$PKG_NAME/" "$INSTALL_DIR/app/"
|
|
106
|
-
|
|
107
|
-
cd "$INSTALL_DIR/app"
|
|
108
130
|
log_info "Installing dependencies..."
|
|
109
131
|
pnpm install > /dev/null 2>&1
|
|
110
132
|
|
|
111
133
|
log_info "Building..."
|
|
134
|
+
rm -f .next/build.lock
|
|
112
135
|
pnpm build 2>&1 | tail -5
|
|
113
136
|
|
|
114
|
-
# Restart service if running
|
|
115
137
|
if systemctl is-active --quiet claudedeck 2>/dev/null; then
|
|
116
138
|
log_info "Restarting service..."
|
|
117
139
|
sudo systemctl restart claudedeck
|
|
118
140
|
sleep 2
|
|
119
|
-
|
|
120
|
-
log_success "ClaudeDeck $NEW running"
|
|
121
|
-
else
|
|
122
|
-
log_error "Service failed to start. Check: sudo journalctl -u claudedeck -f"
|
|
123
|
-
fi
|
|
141
|
+
systemctl is-active --quiet claudedeck && log_success "ClaudeDeck $NEW running" || log_error "Failed. Check: sudo journalctl -u claudedeck -f"
|
|
124
142
|
else
|
|
125
143
|
log_success "ClaudeDeck $NEW ready. Start with: sudo systemctl start claudedeck"
|
|
126
144
|
fi
|
|
@@ -129,17 +147,21 @@ if [[ "$FLAG_UPDATE" == true ]]; then
|
|
|
129
147
|
exit 0
|
|
130
148
|
fi
|
|
131
149
|
|
|
132
|
-
# ───
|
|
150
|
+
# ─── Fresh install ────────────────────────────────────────────────────────────
|
|
133
151
|
|
|
134
152
|
echo ""
|
|
135
153
|
echo -e "${BOLD} ClaudeDeck Installer${NC}"
|
|
136
154
|
echo -e "${DIM} Self-hosted web UI for Claude Code sessions${NC}"
|
|
137
155
|
echo ""
|
|
138
156
|
|
|
139
|
-
#
|
|
140
|
-
|
|
157
|
+
# Prerequisites
|
|
141
158
|
log_info "Checking prerequisites..."
|
|
142
159
|
|
|
160
|
+
if ! command -v git &> /dev/null; then
|
|
161
|
+
log_error "git is required. Install it with: sudo apt install git"
|
|
162
|
+
exit 1
|
|
163
|
+
fi
|
|
164
|
+
|
|
143
165
|
if ! command -v tmux &> /dev/null; then
|
|
144
166
|
log_warn "tmux is not installed (required for session management)"
|
|
145
167
|
ask "Install tmux now? (y/n)" "y" INSTALL_TMUX
|
|
@@ -152,53 +174,10 @@ if ! command -v tmux &> /dev/null; then
|
|
|
152
174
|
fi
|
|
153
175
|
fi
|
|
154
176
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
install_node() {
|
|
158
|
-
log_info "Installing Node.js $NODE_MIN_VERSION..."
|
|
159
|
-
local N_PREFIX="$HOME/.n"
|
|
160
|
-
mkdir -p "$N_PREFIX"
|
|
161
|
-
curl -fsSL https://raw.githubusercontent.com/tj/n/master/bin/n -o /tmp/n
|
|
162
|
-
chmod +x /tmp/n
|
|
163
|
-
N_PREFIX="$N_PREFIX" /tmp/n "$NODE_MIN_VERSION"
|
|
164
|
-
rm -f /tmp/n
|
|
165
|
-
export PATH="$N_PREFIX/bin:$PATH"
|
|
166
|
-
log_success "Node.js $(node --version) installed"
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
NODE_OK=false
|
|
170
|
-
if command -v node &> /dev/null; then
|
|
171
|
-
NODE_VERSION=$(node --version | sed 's/v//' | cut -d. -f1)
|
|
172
|
-
if [[ "$NODE_VERSION" -ge "$NODE_MIN_VERSION" ]]; then
|
|
173
|
-
NODE_OK=true
|
|
174
|
-
log_success "Node.js $(node --version) found"
|
|
175
|
-
fi
|
|
176
|
-
fi
|
|
177
|
-
|
|
178
|
-
if [[ "$NODE_OK" == false ]] && [[ -x "$HOME/.n/bin/node" ]]; then
|
|
179
|
-
export PATH="$HOME/.n/bin:$PATH"
|
|
180
|
-
NODE_VERSION=$(node --version | sed 's/v//' | cut -d. -f1)
|
|
181
|
-
if [[ "$NODE_VERSION" -ge "$NODE_MIN_VERSION" ]]; then
|
|
182
|
-
NODE_OK=true
|
|
183
|
-
log_success "Node.js $(node --version) found in ~/.n"
|
|
184
|
-
fi
|
|
185
|
-
fi
|
|
186
|
-
|
|
187
|
-
if [[ "$NODE_OK" == false ]]; then
|
|
188
|
-
install_node
|
|
189
|
-
fi
|
|
190
|
-
|
|
191
|
-
# pnpm
|
|
192
|
-
if ! command -v pnpm &> /dev/null; then
|
|
193
|
-
log_info "Installing pnpm..."
|
|
194
|
-
npm install -g pnpm > /dev/null 2>&1
|
|
195
|
-
log_success "pnpm $(pnpm --version) installed"
|
|
196
|
-
else
|
|
197
|
-
log_success "pnpm $(pnpm --version) found"
|
|
198
|
-
fi
|
|
199
|
-
|
|
200
|
-
# ─── Configuration ────────────────────────────────────────────────────────────
|
|
177
|
+
ensure_node
|
|
178
|
+
ensure_pnpm
|
|
201
179
|
|
|
180
|
+
# Configuration
|
|
202
181
|
echo ""
|
|
203
182
|
log_info "Configuration"
|
|
204
183
|
echo ""
|
|
@@ -209,39 +188,24 @@ SSH_PORT="${FLAG_SSH_PORT}"
|
|
|
209
188
|
|
|
210
189
|
ask "Port" "3011" PORT
|
|
211
190
|
ask "SSH host for VS Code remote button (leave empty to skip)" "" SSH_HOST
|
|
212
|
-
|
|
213
|
-
if [[ -n "$SSH_HOST" ]]; then
|
|
214
|
-
ask "SSH port" "22" SSH_PORT
|
|
215
|
-
fi
|
|
216
|
-
|
|
191
|
+
[[ -n "$SSH_HOST" ]] && ask "SSH port" "22" SSH_PORT
|
|
217
192
|
echo ""
|
|
218
193
|
|
|
219
|
-
#
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
cd "$INSTALL_DIR"
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
194
|
+
# Clone or update repo
|
|
195
|
+
if [[ -d "$INSTALL_DIR/.git" ]]; then
|
|
196
|
+
log_info "Updating existing installation..."
|
|
197
|
+
cd "$INSTALL_DIR"
|
|
198
|
+
git pull --ff-only
|
|
199
|
+
else
|
|
200
|
+
log_info "Downloading ClaudeDeck..."
|
|
201
|
+
git clone "$REPO_URL" "$INSTALL_DIR"
|
|
202
|
+
cd "$INSTALL_DIR"
|
|
227
203
|
fi
|
|
228
204
|
|
|
229
|
-
log_info "Installing $PKG_NAME from npm..."
|
|
230
|
-
pnpm add "$PKG_NAME" 2>&1 | tail -3
|
|
231
|
-
|
|
232
|
-
# Copy source to app directory (so we can build and run independently)
|
|
233
|
-
log_info "Setting up application..."
|
|
234
|
-
rsync -a --delete \
|
|
235
|
-
--exclude='node_modules' --exclude='.next' --exclude='.env' --exclude='*.db' --exclude='*.db-journal' \
|
|
236
|
-
"$INSTALL_DIR/node_modules/$PKG_NAME/" "$INSTALL_DIR/app/"
|
|
237
|
-
|
|
238
|
-
cd "$INSTALL_DIR/app"
|
|
239
|
-
|
|
240
205
|
# Dependencies
|
|
241
206
|
log_info "Installing dependencies..."
|
|
242
207
|
pnpm install > /dev/null 2>&1
|
|
243
208
|
|
|
244
|
-
# Approve native builds if needed
|
|
245
209
|
if ! grep -q "onlyBuiltDependencies" package.json 2>/dev/null; then
|
|
246
210
|
node -e "
|
|
247
211
|
const pkg = require('./package.json');
|
|
@@ -252,17 +216,14 @@ if ! grep -q "onlyBuiltDependencies" package.json 2>/dev/null; then
|
|
|
252
216
|
pnpm install > /dev/null 2>&1
|
|
253
217
|
fi
|
|
254
218
|
|
|
255
|
-
#
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
PORT=$PORT
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
fi
|
|
264
|
-
if [[ -n "$SSH_PORT" ]] && [[ "$SSH_PORT" != "22" ]]; then
|
|
265
|
-
echo "SSH_PORT=$SSH_PORT" >> "$INSTALL_DIR/app/.env"
|
|
219
|
+
# .env (preserve existing)
|
|
220
|
+
if [[ ! -f "$INSTALL_DIR/.env" ]]; then
|
|
221
|
+
log_info "Writing .env..."
|
|
222
|
+
echo "PORT=$PORT" > "$INSTALL_DIR/.env"
|
|
223
|
+
[[ -n "$SSH_HOST" ]] && echo "SSH_HOST=$SSH_HOST" >> "$INSTALL_DIR/.env"
|
|
224
|
+
[[ -n "$SSH_PORT" && "$SSH_PORT" != "22" ]] && echo "SSH_PORT=$SSH_PORT" >> "$INSTALL_DIR/.env"
|
|
225
|
+
else
|
|
226
|
+
log_success ".env already exists, keeping current config"
|
|
266
227
|
fi
|
|
267
228
|
|
|
268
229
|
# Build
|
|
@@ -275,22 +236,21 @@ if [[ ! -f "$HOME/.tmux.conf" ]] || ! grep -q "mouse on" "$HOME/.tmux.conf" 2>/d
|
|
|
275
236
|
echo "set -g mouse on" >> "$HOME/.tmux.conf"
|
|
276
237
|
fi
|
|
277
238
|
|
|
278
|
-
# ─── Systemd
|
|
239
|
+
# ─── Systemd ──────────────────────────────────────────────────────────────────
|
|
279
240
|
|
|
280
241
|
NODE_BIN=$(which node)
|
|
281
|
-
TSX_BIN="$INSTALL_DIR/
|
|
282
|
-
APP_DIR="$INSTALL_DIR/app"
|
|
242
|
+
TSX_BIN="$INSTALL_DIR/node_modules/.bin/tsx"
|
|
283
243
|
|
|
284
|
-
|
|
244
|
+
SERVICE="[Unit]
|
|
285
245
|
Description=ClaudeDeck
|
|
286
246
|
After=network.target
|
|
287
247
|
|
|
288
248
|
[Service]
|
|
289
249
|
Type=simple
|
|
290
250
|
User=$USER
|
|
291
|
-
WorkingDirectory=$
|
|
251
|
+
WorkingDirectory=$INSTALL_DIR
|
|
292
252
|
Environment=NODE_ENV=production
|
|
293
|
-
Environment=PATH=$(dirname "$NODE_BIN"):$
|
|
253
|
+
Environment=PATH=$(dirname "$NODE_BIN"):$INSTALL_DIR/node_modules/.bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|
294
254
|
ExecStart=$TSX_BIN --env-file=.env server.ts
|
|
295
255
|
Restart=on-failure
|
|
296
256
|
RestartSec=5
|
|
@@ -301,40 +261,33 @@ WantedBy=multi-user.target"
|
|
|
301
261
|
INSTALL_SERVICE=false
|
|
302
262
|
if [[ -t 0 ]] && [[ "$FLAG_NONINTERACTIVE" == false ]]; then
|
|
303
263
|
echo ""
|
|
304
|
-
ask "Install as systemd service? (y/n)" "y"
|
|
305
|
-
[[ "$
|
|
264
|
+
ask "Install as systemd service? (y/n)" "y" SVC_ANSWER
|
|
265
|
+
[[ "$SVC_ANSWER" == "y" ]] && INSTALL_SERVICE=true
|
|
306
266
|
else
|
|
307
267
|
INSTALL_SERVICE=true
|
|
308
268
|
fi
|
|
309
269
|
|
|
310
270
|
if [[ "$INSTALL_SERVICE" == true ]]; then
|
|
311
271
|
log_info "Installing systemd service..."
|
|
312
|
-
echo "$
|
|
272
|
+
echo "$SERVICE" | sudo tee /etc/systemd/system/claudedeck.service > /dev/null
|
|
313
273
|
sudo systemctl daemon-reload
|
|
314
274
|
sudo systemctl enable claudedeck > /dev/null 2>&1
|
|
315
275
|
sudo systemctl restart claudedeck
|
|
316
276
|
sleep 2
|
|
317
|
-
|
|
318
|
-
if systemctl is-active --quiet claudedeck; then
|
|
319
|
-
log_success "Service running on port $PORT"
|
|
320
|
-
else
|
|
321
|
-
log_error "Service failed to start. Check: sudo journalctl -u claudedeck -f"
|
|
322
|
-
fi
|
|
277
|
+
systemctl is-active --quiet claudedeck && log_success "Service running on port $PORT" || log_error "Failed. Check: sudo journalctl -u claudedeck -f"
|
|
323
278
|
fi
|
|
324
279
|
|
|
325
280
|
# ─── Done ─────────────────────────────────────────────────────────────────────
|
|
326
281
|
|
|
327
|
-
VERSION=$(
|
|
282
|
+
VERSION=$(app_version)
|
|
328
283
|
|
|
329
284
|
echo ""
|
|
330
285
|
echo -e "${GREEN}${BOLD} ClaudeDeck${VERSION:+ v$VERSION} installed!${NC}"
|
|
331
286
|
echo ""
|
|
332
287
|
echo -e " ${BOLD}Local:${NC} http://localhost:$PORT"
|
|
333
|
-
|
|
334
|
-
echo -e " ${BOLD}Remote:${NC} Configure your reverse proxy to point to port $PORT"
|
|
335
|
-
fi
|
|
288
|
+
[[ -n "$SSH_HOST" ]] && echo -e " ${BOLD}Remote:${NC} Configure your reverse proxy to point to port $PORT"
|
|
336
289
|
echo ""
|
|
337
290
|
echo -e " ${DIM}First visit will prompt you to create an account.${NC}"
|
|
338
291
|
echo -e " ${DIM}Manage: sudo systemctl {start|stop|restart|status} claudedeck${NC}"
|
|
339
|
-
echo -e " ${DIM}Update:
|
|
292
|
+
echo -e " ${DIM}Update: ~/.claude-deck/scripts/install.sh --update${NC}"
|
|
340
293
|
echo ""
|