@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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/scripts/install.sh +102 -149
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atercates/claude-deck",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "description": "Self-hosted web UI for managing Claude Code sessions",
5
5
  "bin": {
6
6
  "claude-deck": "./scripts/claude-deck"
@@ -2,19 +2,18 @@
2
2
  #
3
3
  # ClaudeDeck Installer
4
4
  #
5
- # Usage:
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
- # bash install.sh --update
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
- PKG_NAME="@atercates/claude-deck"
33
- NODE_MIN_VERSION=24
31
+ REPO_URL="https://github.com/ATERCATES/claude-deck.git"
32
+ NODE_MIN=24
34
33
 
35
- # ─── Parse CLI flags ──────────────────────────────────────────────────────────
34
+ # ─── Parse flags ──────────────────────────────────────────────────────────────
36
35
 
37
- FLAG_PORT=""
38
- FLAG_SSH_HOST=""
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) FLAG_PORT="$2"; shift 2 ;;
46
- --ssh-host) FLAG_SSH_HOST="$2"; shift 2 ;;
47
- --ssh-port) FLAG_SSH_PORT="$2"; shift 2 ;;
48
- --yes|-y) FLAG_NONINTERACTIVE=true; shift ;;
49
- --update|-u) FLAG_UPDATE=true; shift ;;
50
- *) shift ;;
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
- # ─── Interactive prompts ──────────────────────────────────────────────────────
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
- # ─── Update mode ──────────────────────────────────────────────────────────────
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
- # Ensure node/pnpm in PATH
84
- [[ -x "$HOME/.n/bin/node" ]] && export PATH="$HOME/.n/bin:$PATH"
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
- log_info "Updating $PKG_NAME..."
91
- pnpm update "$PKG_NAME" --latest 2>&1 | tail -3
117
+ CURRENT=$(app_version)
118
+ log_info "Current version: $CURRENT"
92
119
 
93
- NEW=$(node -e "console.log(require('./node_modules/$PKG_NAME/package.json').version)" 2>/dev/null || echo "unknown")
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
- if systemctl is-active --quiet claudedeck; then
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
- # ─── Header ───────────────────────────────────────────────────────────────────
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
- # ─── Check prerequisites ─────────────────────────────────────────────────────
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
- # ─── Node.js ──────────────────────────────────────────────────────────────────
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
- # ─── Install from npm ────────────────────────────────────────────────────────
220
-
221
- mkdir -p "$INSTALL_DIR"
222
- cd "$INSTALL_DIR"
223
-
224
- # Initialize pnpm project if first install
225
- if [[ ! -f "package.json" ]]; then
226
- echo '{"name":"claude-deck-instance","private":true}' > package.json
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
- # Write .env
256
- log_info "Writing .env..."
257
- cat > "$INSTALL_DIR/app/.env" << EOF
258
- PORT=$PORT
259
- EOF
260
-
261
- if [[ -n "$SSH_HOST" ]]; then
262
- echo "SSH_HOST=$SSH_HOST" >> "$INSTALL_DIR/app/.env"
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 service ─────────────────────────────────────────────────────────
239
+ # ─── Systemd ──────────────────────────────────────────────────────────────────
279
240
 
280
241
  NODE_BIN=$(which node)
281
- TSX_BIN="$INSTALL_DIR/app/node_modules/.bin/tsx"
282
- APP_DIR="$INSTALL_DIR/app"
242
+ TSX_BIN="$INSTALL_DIR/node_modules/.bin/tsx"
283
243
 
284
- SERVICE_FILE="[Unit]
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=$APP_DIR
251
+ WorkingDirectory=$INSTALL_DIR
292
252
  Environment=NODE_ENV=production
293
- Environment=PATH=$(dirname "$NODE_BIN"):$APP_DIR/node_modules/.bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/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" INSTALL_SVC_ANSWER
305
- [[ "$INSTALL_SVC_ANSWER" == "y" ]] && INSTALL_SERVICE=true
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 "$SERVICE_FILE" | sudo tee /etc/systemd/system/claudedeck.service > /dev/null
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=$(node -e "console.log(require('./package.json').version)" 2>/dev/null || echo "")
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
- if [[ -n "$SSH_HOST" ]]; then
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: bash install.sh --update${NC}"
292
+ echo -e " ${DIM}Update: ~/.claude-deck/scripts/install.sh --update${NC}"
340
293
  echo ""