claude-multiacc 2.0.39 → 2.0.40

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.
@@ -3107,7 +3107,12 @@ EOF
3107
3107
  if [ -n "$peers" ]; then
3108
3108
  while IFS="$tab" read -r pt pr pp; do
3109
3109
  [ -n "$pt" ] || continue
3110
- sync_push_target "$pt" "$pr" "$pp"
3110
+ # </dev/null: the ssh/rsync calls inside read STDIN, and stdin here is the
3111
+ # peer list itself — the first peer's ssh swallowed every later line, so sync
3112
+ # reached gas-mini (first in the list) and never mini-3..mini-8 (peers since
3113
+ # 2026-09-04; the limits fan-out, which reads its list through process
3114
+ # substitution, was the only thing that ever reached them).
3115
+ sync_push_target "$pt" "$pr" "$pp" </dev/null
3111
3116
  npeers=$((npeers + 1))
3112
3117
  done <<EOF
3113
3118
  $peers
@@ -2208,7 +2208,12 @@ EOF
2208
2208
  if [ -n "$peers" ]; then
2209
2209
  while IFS="$tab" read -r pt pr pp; do
2210
2210
  [ -n "$pt" ] || continue
2211
- sync_push_target "$pt" "$pr" "$pp"
2211
+ # </dev/null: the ssh/rsync calls inside read STDIN, and stdin here is the
2212
+ # peer list itself — the first peer's ssh swallowed every later line, so sync
2213
+ # reached gas-mini (first in the list) and never mini-3..mini-8 (peers since
2214
+ # 2026-09-04; the limits fan-out, which reads its list through process
2215
+ # substitution, was the only thing that ever reached them).
2216
+ sync_push_target "$pt" "$pr" "$pp" </dev/null
2212
2217
  npeers=$((npeers + 1))
2213
2218
  done <<EOF
2214
2219
  $peers
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-multiacc",
3
- "version": "2.0.39",
3
+ "version": "2.0.40",
4
4
  "description": "Unified Claude Code and OpenAI Codex subscription pooling with quota-aware selection.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -3908,15 +3908,20 @@ EOF
3908
3908
  SSHFAKE="$WORK/sshfake"
3909
3909
  SSHLOG="$WORK/sshfake.log"
3910
3910
  mkdir -p "$SSHFAKE"
3911
+ # Both fakes DRAIN stdin like the real tools do: ssh forwards it to the remote
3912
+ # command and rsync's transport reads it. A fake that left stdin alone hid the
3913
+ # bug where the peer loop fed them its own peer list.
3911
3914
  cat > "$SSHFAKE/ssh" <<'EOF'
3912
3915
  #!/usr/bin/env bash
3913
3916
  printf 'ssh %s\n' "$*" >> "${SSHLOG:?}"
3917
+ [ -t 0 ] || cat >/dev/null
3914
3918
  case "$*" in *"${SSH_FAIL_HOST:-@@none@@}"*) exit 1 ;; esac
3915
3919
  exit 0
3916
3920
  EOF
3917
3921
  cat > "$SSHFAKE/rsync" <<'EOF'
3918
3922
  #!/usr/bin/env bash
3919
3923
  printf 'rsync %s\n' "$*" >> "${SSHLOG:?}"
3924
+ [ -t 0 ] || cat >/dev/null
3920
3925
  exit 0
3921
3926
  EOF
3922
3927
  chmod +x "$SSHFAKE/ssh" "$SSHFAKE/rsync"
@@ -3966,14 +3971,22 @@ EOF
3966
3971
  python3 - "$ACC/accounts.json" <<'EOF'
3967
3972
  import json, os, sys
3968
3973
  d = json.load(open(sys.argv[1]))
3969
- d['peers'] = [{'target': 'gas@peer1', 'root': '/Users/gas/.claude-accounts', 'repo': '/Users/gas/claude-multiacc'}]
3974
+ d['peers'] = [{'target': 'gas@peer1', 'root': '/Users/gas/.claude-accounts', 'repo': '/Users/gas/claude-multiacc'},
3975
+ {'target': 'gas@peer2', 'root': '/Users/gas/.claude-accounts', 'repo': '/Users/gas/claude-multiacc'},
3976
+ {'target': 'gas@peer3', 'root': '/Users/gas/.claude-accounts', 'repo': '/Users/gas/claude-multiacc'}]
3970
3977
  json.dump(d, open(sys.argv[1] + '.tmp', 'w'), indent=2); os.replace(sys.argv[1] + '.tmp', sys.argv[1])
3971
3978
  EOF
3972
3979
  : > "$SSHLOG"
3973
3980
  out="$(PATH="$SSHFAKE:$PATH" CLAUDE_MULTIACC_NO_SYNC=0 claude-accounts sync 2>&1)"
3974
3981
  rc=$?
3975
3982
  [ "$rc" = "0" ] && t_ok "multi-target sync succeeds" || t_fail "multi-target sync" "rc=$rc: $out"
3976
- check "multi-target sync reports the peer" "+ 1 peer(s)" "$out"
3983
+ check "multi-target sync reports every peer" "+ 3 peer(s)" "$out"
3984
+ # EVERY peer is pushed, not just the first: the ssh/rsync inside the loop must not
3985
+ # be allowed to eat the peer list off stdin (mini-3..8 got nothing for 18 days).
3986
+ grep -q "rsync.*gas@peer2:/Users/gas/.claude-accounts/accounts.json" "$SSHLOG" \
3987
+ && grep -q "rsync.*gas@peer3:/Users/gas/.claude-accounts/accounts.json" "$SSHLOG" \
3988
+ && t_ok "the second and third peers are pushed too (stdin not eaten by the first)" \
3989
+ || t_fail "later peers skipped" "$(grep -c 'accounts.json' "$SSHLOG") manifest pushes: $(grep accounts.json "$SSHLOG" | tr '\n' '|')"
3977
3990
  grep -q "rsync.*root@203.0.113.1:/root/.claude-accounts/accounts.json" "$SSHLOG" \
3978
3991
  && grep -q "rsync.*gas@peer1:/Users/gas/.claude-accounts/accounts.json" "$SSHLOG" \
3979
3992
  && t_ok "manifest pushed to BOTH targets" \
@@ -3984,8 +3997,8 @@ EOF
3984
3997
  && t_ok "primary target pushed before the peer" \
3985
3998
  || t_fail "target order" "primary=$first_primary peer=$first_peer"
3986
3999
  n_remov="$(grep -c "for dd in acct-" "$SSHLOG")"
3987
- [ "$n_remov" = "2" ] && t_ok "removal propagation ran on each target" \
3988
- || t_fail "per-target removal" "expected 2 removal sweeps, saw $n_remov"
4000
+ [ "$n_remov" = "4" ] && t_ok "removal propagation ran on each target" \
4001
+ || t_fail "per-target removal" "expected 4 removal sweeps, saw $n_remov"
3989
4002
  grep -q "post-sync" "$SSHLOG" && t_ok "post-sync hooks invoked" || t_fail "post-sync hooks" "none recorded"
3990
4003
  # The MCP registry rides with the manifest to EVERY target; the machine-local overlay
3991
4004
  # (a runner daemon's view of THIS Mac) never leaves. Without a registry file nothing