replit-tools 1.1.9 → 1.1.11

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replit-tools",
3
- "version": "1.1.9",
3
+ "version": "1.1.11",
4
4
  "description": "DATA Tools - One command to set up Claude Code and Codex CLI on Replit with full persistence",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -3,20 +3,21 @@
3
3
  # Automatically refreshes Claude Code OAuth tokens before expiration
4
4
  # Part of DATA Tools - https://github.com/stevemoraco/DATAtools
5
5
 
6
- # Use .replit-tools structure
6
+ # Paths - credentials are at workspace root, not inside .replit-tools
7
7
  WORKSPACE="/home/runner/workspace"
8
8
  REPLIT_TOOLS="${WORKSPACE}/.replit-tools"
9
- CREDENTIALS_FILE="${CLAUDE_CONFIG_DIR:-${REPLIT_TOOLS}/.claude-persistent}/.credentials.json"
9
+ # Credentials are stored at /home/runner/workspace/.claude-persistent (NOT inside .replit-tools)
10
+ CREDENTIALS_FILE="${CLAUDE_CONFIG_DIR:-${WORKSPACE}/.claude-persistent}/.credentials.json"
10
11
  LOG_FILE="${REPLIT_TOOLS}/.logs/auth-refresh.log"
11
12
 
13
+ # Ensure log directory exists
14
+ mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null
15
+
12
16
  # OAuth configuration
13
17
  OAUTH_ENDPOINT="https://console.anthropic.com/v1/oauth/token"
14
18
  CLIENT_ID="9d1c250a-e61b-44d9-88ed-5944d1962f5e"
15
19
  REFRESH_THRESHOLD_HOURS=2 # Refresh when less than 2 hours remaining
16
20
 
17
- # Ensure log directory exists
18
- mkdir -p "${REPLIT_TOOLS}/.logs" 2>/dev/null
19
-
20
21
  # Logging function
21
22
  log() {
22
23
  local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
@@ -97,8 +98,8 @@ refresh_token() {
97
98
 
98
99
  log "Attempting token refresh..."
99
100
 
100
- # Make the refresh request
101
- local response=$(curl -s -X POST "$OAUTH_ENDPOINT" \
101
+ # Make the refresh request (5 second timeout to fail fast)
102
+ local response=$(curl -s --max-time 5 -X POST "$OAUTH_ENDPOINT" \
102
103
  -H "Content-Type: application/json" \
103
104
  -d "{\"grant_type\":\"refresh_token\",\"refresh_token\":\"$refresh_token\",\"client_id\":\"$CLIENT_ID\"}" \
104
105
  2>/dev/null)
@@ -225,7 +225,27 @@ get_terminal_last_session() {
225
225
  fi
226
226
  }
227
227
 
228
- # Interactive session picker
228
+ # Run Claude and handle exit codes (returns to menu on failure)
229
+ run_claude_with_retry() {
230
+ local cmd="$1"
231
+ local session_desc="$2"
232
+
233
+ echo ""
234
+ echo " ${session_desc}..."
235
+
236
+ # Run Claude
237
+ eval "$cmd"
238
+ local exit_code=$?
239
+
240
+ # Save session state on success
241
+ if [ $exit_code -eq 0 ]; then
242
+ save_session_state "$(tail -1 "${HOME}/.claude/history.jsonl" 2>/dev/null | grep -oP '"sessionId":"[^"]+"' | cut -d'"' -f4)"
243
+ fi
244
+
245
+ return $exit_code
246
+ }
247
+
248
+ # Interactive session picker (loops until user exits to shell)
229
249
  claude_prompt() {
230
250
  # Only in interactive shells
231
251
  [[ $- != *i* ]] && return 0
@@ -236,104 +256,140 @@ claude_prompt() {
236
256
  fi
237
257
 
238
258
  # LOOP PREVENTION: Track if we've already prompted this terminal session
239
- local prompt_marker="${REPLIT_TOOLS}/.prompt-shown-${TERMINAL_ID}"
259
+ # Include the shell PID so new shells always get the menu
260
+ local prompt_marker="${REPLIT_TOOLS}/.prompt-active-${TERMINAL_ID}-$$"
261
+
262
+ # Clean up old markers from crashed/closed shells
263
+ # Remove markers older than 1 hour or from PIDs that no longer exist
264
+ find "${REPLIT_TOOLS}" -name ".prompt-active-*" -mmin +60 -delete 2>/dev/null
265
+ for marker in "${REPLIT_TOOLS}"/.prompt-active-*; do
266
+ if [ -f "$marker" ]; then
267
+ local marker_pid=$(basename "$marker" | rev | cut -d'-' -f1 | rev)
268
+ if [ -n "$marker_pid" ] && ! kill -0 "$marker_pid" 2>/dev/null; then
269
+ rm -f "$marker" 2>/dev/null
270
+ fi
271
+ fi
272
+ done
240
273
 
241
- # If we've already shown the prompt for this terminal, skip
274
+ # If THIS shell already showed the prompt, skip (prevents re-sourcing issues)
242
275
  if [ -f "$prompt_marker" ]; then
243
- # Clear old markers (older than 12 hours)
244
- find "${REPLIT_TOOLS}" -name ".prompt-shown-*" -mmin +720 -delete 2>/dev/null
245
276
  return 0
246
277
  fi
247
278
 
248
- # Mark that we're showing the prompt (create marker BEFORE showing)
279
+ # Mark that we're showing the prompt for THIS shell session
249
280
  touch "$prompt_marker" 2>/dev/null
250
281
 
251
- local running=$(count_claude_instances)
252
- local last_session=$(get_terminal_last_session)
282
+ # Cleanup function to remove marker when shell exits
283
+ trap "rm -f '$prompt_marker' 2>/dev/null" EXIT
284
+
285
+ # Main menu loop - keeps showing until user chooses shell
286
+ while true; do
287
+ local running=$(count_claude_instances)
288
+ local last_session=$(get_terminal_last_session)
289
+
290
+ echo ""
291
+ echo "╭─────────────────────────────────────────────────────────╮"
292
+ echo "│ Claude Session Manager │"
293
+ echo "╰─────────────────────────────────────────────────────────╯"
294
+
295
+ if [ "$running" -gt 0 ]; then
296
+ echo " ($running Claude instance(s) running in other terminals)"
297
+ fi
298
+ echo ""
299
+
300
+ # Show options
301
+ echo " [c] Continue last session for this terminal"
302
+ if [ -n "$last_session" ]; then
303
+ echo " └─ ${last_session:0:8}..."
304
+ fi
305
+ echo " [r] Resume a specific session (pick from list)"
306
+ echo " [n] Start new session"
307
+ echo " [l] Login to Claude (authenticate)"
308
+ echo " [s] Skip - just give me a shell"
309
+ echo ""
310
+
311
+ # Read choice with timeout
312
+ local choice
313
+ read -t 60 -n 1 -p " Choice [c/r/n/l/s]: " choice
314
+ echo ""
315
+
316
+ case "$choice" in
317
+ c|C|"")
318
+ # Continue last session (default on Enter or timeout)
319
+ if [ -n "$last_session" ]; then
320
+ run_claude_with_retry "claude -r '$last_session' --dangerously-skip-permissions" "Resuming session ${last_session:0:8}"
321
+ else
322
+ run_claude_with_retry "claude --dangerously-skip-permissions" "No previous session, starting new"
323
+ fi
324
+ # After Claude exits, loop back to menu
325
+ echo ""
326
+ echo " Claude exited. Returning to menu..."
327
+ sleep 1
328
+ ;;
329
+ r|R)
330
+ # Show session list with full details
331
+ echo ""
332
+ echo " Recent Sessions"
333
+ show_sessions
253
334
 
254
- echo ""
255
- echo "╭─────────────────────────────────────────────────────────╮"
256
- echo "│ Claude Session Manager │"
257
- echo "╰─────────────────────────────────────────────────────────╯"
335
+ # Get session IDs for selection
336
+ local session_ids=$(get_recent_sessions | grep "^ID|" | cut -d'|' -f2)
258
337
 
259
- if [ "$running" -gt 0 ]; then
260
- echo " ($running Claude instance(s) running in other terminals)"
261
- fi
262
- echo ""
338
+ read -t 60 -p " Enter number (or 'q' to cancel): " session_num
263
339
 
264
- # Show options
265
- echo " [c] Continue last session for this terminal"
266
- if [ -n "$last_session" ]; then
267
- echo " └─ ${last_session:0:8}..."
268
- fi
269
- echo " [r] Resume a specific session (pick from list)"
270
- echo " [n] Start new session"
271
- echo " [s] Skip - just give me a shell"
272
- echo ""
273
-
274
- # Read choice with timeout
275
- local choice
276
- read -t 30 -n 1 -p " Choice [c/r/n/s]: " choice
277
- echo ""
340
+ if [ "$session_num" = "q" ] || [ -z "$session_num" ]; then
341
+ echo " Cancelled, returning to menu..."
342
+ continue
343
+ fi
278
344
 
279
- case "$choice" in
280
- c|C|"")
281
- # Continue last session (default on Enter or timeout)
282
- if [ -n "$last_session" ]; then
345
+ local selected_id=$(echo "$session_ids" | sed -n "${session_num}p")
346
+ if [ -n "$selected_id" ]; then
347
+ run_claude_with_retry "claude -r '$selected_id' --dangerously-skip-permissions" "Resuming session $selected_id"
348
+ echo ""
349
+ echo " Claude exited. Returning to menu..."
350
+ sleep 1
351
+ else
352
+ echo " Invalid selection."
353
+ fi
354
+ ;;
355
+ n|N)
356
+ # Start new session
357
+ run_claude_with_retry "claude --dangerously-skip-permissions" "Starting new Claude session"
283
358
  echo ""
284
- echo " Resuming session ${last_session:0:8}..."
285
- claude -r "$last_session" --dangerously-skip-permissions
286
- save_session_state "$(tail -1 "${HOME}/.claude/history.jsonl" 2>/dev/null | grep -oP '"sessionId":"[^"]+"' | cut -d'"' -f4)"
287
- else
359
+ echo " Claude exited. Returning to menu..."
360
+ sleep 1
361
+ ;;
362
+ l|L)
363
+ # Login to Claude
364
+ echo ""
365
+ echo " Starting Claude login..."
366
+ echo " (This will open a browser or show a URL to authenticate)"
288
367
  echo ""
289
- echo " No previous session for this terminal, starting new..."
290
- claude --dangerously-skip-permissions
291
- save_session_state "$(tail -1 "${HOME}/.claude/history.jsonl" 2>/dev/null | grep -oP '"sessionId":"[^"]+"' | cut -d'"' -f4)"
292
- fi
293
- ;;
294
- r|R)
295
- # Show session list with full details
296
- echo ""
297
- echo " Recent Sessions"
298
- show_sessions
299
368
 
300
- # Get session IDs for selection
301
- local session_ids=$(get_recent_sessions | grep "^ID|" | cut -d'|' -f2)
369
+ # Clear the auth failed marker so setup script will retry
370
+ rm -f "${REPLIT_TOOLS}/.auth-refresh-failed" 2>/dev/null
302
371
 
303
- read -t 30 -p " Enter number (or 'q' to cancel): " session_num
372
+ claude login
304
373
 
305
- if [ "$session_num" = "q" ] || [ -z "$session_num" ]; then
306
- echo " Cancelled."
374
+ echo ""
375
+ echo " Login complete. Returning to menu..."
376
+ sleep 1
377
+ ;;
378
+ s|S)
379
+ # Skip - just shell
380
+ echo ""
381
+ echo " Okay, just a shell. Type 'claude-menu' to return here."
382
+ # Keep the marker so re-sourcing bashrc won't re-show menu
383
+ # The trap will clean it up when shell exits
307
384
  return 0
308
- fi
309
-
310
- local selected_id=$(echo "$session_ids" | sed -n "${session_num}p")
311
- if [ -n "$selected_id" ]; then
385
+ ;;
386
+ *)
312
387
  echo ""
313
- echo " Resuming session: $selected_id"
314
- claude -r "$selected_id" --dangerously-skip-permissions
315
- save_session_state "$selected_id"
316
- else
317
- echo " Invalid selection."
318
- fi
319
- ;;
320
- n|N)
321
- # Start new session
322
- echo ""
323
- echo " Starting new Claude session..."
324
- claude --dangerously-skip-permissions
325
- save_session_state "$(tail -1 "${HOME}/.claude/history.jsonl" 2>/dev/null | grep -oP '"sessionId":"[^"]+"' | cut -d'"' -f4)"
326
- ;;
327
- s|S)
328
- # Skip - just shell
329
- echo ""
330
- echo " Okay, just a shell. Type 'claude' or 'cr' when you want Claude."
331
- ;;
332
- *)
333
- echo ""
334
- echo " Unknown option. Type 'claude' to start manually."
335
- ;;
336
- esac
388
+ echo " Unknown option. Please choose c, r, n, l, or s."
389
+ sleep 1
390
+ ;;
391
+ esac
392
+ done
337
393
  }
338
394
 
339
395
  # Aliases for manual use
@@ -353,11 +409,17 @@ export TERMINAL_ID
353
409
 
354
410
  # Reset prompt marker and show menu
355
411
  claude_menu() {
356
- local prompt_marker="${REPLIT_TOOLS}/.prompt-shown-${TERMINAL_ID}"
412
+ # Clear the marker that claude_prompt uses to prevent re-showing
413
+ # This allows manual invocation to always show the menu
414
+ local prompt_marker="${REPLIT_TOOLS}/.prompt-active-${TERMINAL_ID}-$$"
357
415
  rm -f "$prompt_marker" 2>/dev/null
416
+ # Also clear any old-style markers for this terminal
417
+ rm -f "${REPLIT_TOOLS}/.prompt-shown-${TERMINAL_ID}" 2>/dev/null
418
+ rm -f "${REPLIT_TOOLS}/.prompt-active-${TERMINAL_ID}-"* 2>/dev/null
358
419
  claude_prompt
359
420
  }
360
421
  alias claude-menu='claude_menu'
422
+ alias cm='claude_menu'
361
423
 
362
424
  if [ "${CLAUDE_NO_PROMPT}" != "true" ]; then
363
425
  claude_prompt
@@ -21,7 +21,8 @@ WORKSPACE="/home/runner/workspace"
21
21
  REPLIT_TOOLS="${WORKSPACE}/.replit-tools"
22
22
 
23
23
  # Allow env vars to override (for custom config locations)
24
- CLAUDE_PERSISTENT="${CLAUDE_CONFIG_DIR:-${REPLIT_TOOLS}/.claude-persistent}"
24
+ # Note: Credentials are at workspace root (.claude-persistent), NOT inside .replit-tools
25
+ CLAUDE_PERSISTENT="${CLAUDE_CONFIG_DIR:-${WORKSPACE}/.claude-persistent}"
25
26
  CLAUDE_VERSIONS="${REPLIT_TOOLS}/.claude-versions"
26
27
  LOGS_DIR="${REPLIT_TOOLS}/.logs"
27
28
  SCRIPTS_DIR="${REPLIT_TOOLS}/scripts"