claude-multiacc 1.0.15 → 1.0.16

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/bin/claude CHANGED
@@ -475,6 +475,48 @@ client_limit_scan() { # $1 acct dir
475
475
  return 1
476
476
  }
477
477
 
478
+ # A TUI auth failure cannot use the -p retry path because the client owns the terminal.
479
+ # Harvest the same account-owned transcripts used for quota detection so the next launch
480
+ # parks a rejected setup-token instead of selecting it again.
481
+ client_auth_scan() { # $1 acct dir
482
+ local idx="$1/.sessions-index" ln id claim p line ts ck ak read_n=0 i
483
+ local ids=() claims=()
484
+ [ -f "$idx" ] || return 1
485
+ while IFS= read -r ln; do
486
+ id="${ln%% *}"; claim=""
487
+ case "$ln" in *' '*) claim="${ln#* }" ;; esac
488
+ sess_id_ok "$id" && { ids+=("$id"); claims+=("$claim"); }
489
+ done < "$idx"
490
+ i=$(( ${#ids[@]} - 1 ))
491
+ while [ "$i" -ge 0 ] && [ "$read_n" -lt "$QUOTA_SCAN_MAX_FILES" ]; do
492
+ id="${ids[$i]}"; claim="${claims[$i]}"; i=$((i - 1)); read_n=$((read_n + 1))
493
+ sess_transcript "$1" "$id" || continue
494
+ p="$SESS_TRANSCRIPT"
495
+ [ $((now - $(file_mtime "$p"))) -le "$QUOTA_SCAN_MAX_AGE" ] || continue
496
+ line="$(tail -c "$QUOTA_SCAN_BYTES" "$p" 2>/dev/null \
497
+ | LC_ALL=C grep -a '"error"[[:space:]]*:[[:space:]]*"authentication_failed"' | tail -1)"
498
+ [ -n "$line" ] || continue
499
+ [ -n "$claim" ] || continue
500
+ ts="$(printf '%s' "$line" | LC_ALL=C sed -n 's/.*"timestamp"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')"
501
+ ck="$(iso_key "$ts")"; ak="$(iso_key "$claim")"
502
+ num_ok "$ck" || continue; num_ok "$ak" || continue
503
+ [ "$ck" -lt "$ak" ] && continue
504
+ claim_conflicted "$1" "$id" "$claim" && continue
505
+ return 0
506
+ done
507
+ return 1
508
+ }
509
+
510
+ mark_client_auth_dead() { # $1 acct dir
511
+ local soft=$((now + 3600)) marked
512
+ marked="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
513
+ { echo "$now"; echo "reason=auth-error soft_until=$soft marked_at=$marked" \
514
+ "detail=client session failed to authenticate"; } \
515
+ 2>/dev/null > "$1/.expired.$$" && mv -f "$1/.expired.$$" "$1/.expired" 2>/dev/null \
516
+ || rm -f "$1/.expired.$$" 2>/dev/null || true
517
+ sel_log "$(basename "$1") parked (auth-error until $soft) — client-reported"
518
+ }
519
+
478
520
  mark_client_limit() { # $1 acct dir, $2 reset epoch, $3 rate limit type
479
521
  local m="$1/.limited" cur=""
480
522
  # Never shorten a marker that already reaches further out (a weekly park must
@@ -507,11 +549,11 @@ sel_capture_session() { # $1 acct dir
507
549
  # stops the moment the run it is watching is gone.
508
550
  trap '' INT HUP TERM QUIT
509
551
  i=0
510
- while [ "$i" -lt 30 ]; do
552
+ while [ "$i" -lt 600 ]; do
511
553
  kill -0 "$pid" 2>/dev/null || break
512
554
  sess_index_refresh "$d"
513
555
  [ -f "$d/sessions/$pid.json" ] && break
514
- sleep 2
556
+ sleep 0.1
515
557
  i=$((i + 1))
516
558
  done
517
559
  sess_index_refresh "$d"
@@ -638,6 +680,12 @@ for d in "$ACC_ROOT"/acct-*; do
638
680
  fi
639
681
  valid+=("$d")
640
682
  sess_index_refresh "$d"
683
+ if client_auth_scan "$d"; then
684
+ mark_client_auth_dead "$d"
685
+ valid=("${valid[@]:0:${#valid[@]}-1}")
686
+ expired+=("$d")
687
+ continue
688
+ fi
641
689
  marker_active "$d" && continue
642
690
  # The account's own records are consulted BEFORE telemetry: what the server told a real
643
691
  # call is first-hand and carries the real reset, while limits.json can be days stale —
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-multiacc",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "Multi-account addon for Claude Code and OpenAI Codex CLI: every claude / claude -p and every codex / codex exec runs under a randomly-picked subscription account with the most usage headroom. Mirrors to a deploy server. No API keys.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -383,6 +383,24 @@ grep -q "all-expired: falling back" "$ACC/selection.log" \
383
383
  printf '%s' "$HEALTHY_CREDS" > "$ACC/acct-01/.credentials.json"
384
384
  printf '%s' "$HEALTHY_CREDS" > "$ACC/acct-02/.credentials.json"
385
385
 
386
+ # A short interactive TUI can report auth failure and exit before the -p retry path can
387
+ # inspect stderr. Its account-owned transcript must park that setup-token on the next run.
388
+ auth_sid="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
389
+ mkdir -p "$ACC/acct-01/projects/auth-regression"
390
+ printf '%s %s\n' "$auth_sid" '2020-01-01T00:00:00Z' > "$ACC/acct-01/.sessions-index"
391
+ auth_line='{"timestamp":"2026-08-22T20:27:29.985Z","error":"authentication_failed",'
392
+ auth_line="$auth_line\"session_id\":\"$auth_sid\"}"
393
+ printf '%s\n' "$auth_line" \
394
+ > "$ACC/acct-01/projects/auth-regression/$auth_sid.jsonl"
395
+ out="$(claude 2>&1)"
396
+ check "TUI transcript auth failure excludes rejected account" "CFG=acct-02" "$out"
397
+ grep -q 'reason=auth-error' "$ACC/acct-01/.expired" 2>/dev/null \
398
+ && t_ok "TUI transcript auth failure writes .expired" \
399
+ || t_fail "TUI transcript auth marker" "no auth-error marker"
400
+ rm -f "$ACC/acct-01/.expired" "$ACC/acct-01/.sessions-index" \
401
+ "$ACC/acct-01/projects/auth-regression/$auth_sid.jsonl"
402
+ rmdir "$ACC/acct-01/projects/auth-regression" 2>/dev/null || true
403
+
386
404
  # ---- 9d. the .expired marker: excludes, and self-heals on a newer credential ----
387
405
  printf '%s\nreason=auth-error marked_at=now detail=test\n' "$now" > "$ACC/acct-01/.expired"
388
406
  touch -t 202001010101 "$ACC/acct-01/.credentials.json" # credential OLDER than the marker