claude-multiacc 1.0.11 → 1.0.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/bin/codex-accounts +50 -2
- package/package.json +1 -1
- package/tests/run-tests.sh +56 -0
package/bin/codex-accounts
CHANGED
|
@@ -421,8 +421,21 @@ cmd_add() {
|
|
|
421
421
|
mutate_lock || die "could not acquire the account lock — try again"
|
|
422
422
|
owner="$(email_owner "$got")"
|
|
423
423
|
if [ -n "$owner" ]; then
|
|
424
|
-
|
|
425
|
-
|
|
424
|
+
# The sign-in itself succeeded and produced a fresh credential for an account
|
|
425
|
+
# that is already registered — don't throw it away: save it to that account,
|
|
426
|
+
# exactly as `login $owner` would have. This machine now runs $owner.
|
|
427
|
+
local od="$ACC_ROOT/$owner"
|
|
428
|
+
seed_account_dir "$od"
|
|
429
|
+
if mv -f "$d/auth.json" "$od/auth.json" 2>/dev/null; then
|
|
430
|
+
chmod 600 "$od/auth.json" 2>/dev/null || true
|
|
431
|
+
clear_auth_markers "$od"
|
|
432
|
+
mutate_unlock
|
|
433
|
+
log_to ops.log "add redirect: signed in as $got (already $owner) — credential saved to $owner"
|
|
434
|
+
echo "$got is already added as $owner — the fresh sign-in was saved to $owner (usable on this machine now)."
|
|
435
|
+
else
|
|
436
|
+
mutate_unlock
|
|
437
|
+
echo "$got is already added as $owner — skipping (nothing added)."
|
|
438
|
+
fi
|
|
426
439
|
return 0 # RESERVED_DIR still set -> trap removes the temp dir
|
|
427
440
|
fi
|
|
428
441
|
if [ -n "$email" ] && [ "$got" != "$email" ]; then
|
|
@@ -692,6 +705,26 @@ PYEOF
|
|
|
692
705
|
got_lc="$(printf '%s' "$got" | tr '[:upper:]' '[:lower:]')"
|
|
693
706
|
email_lc="$(printf '%s' "$email" | tr '[:upper:]' '[:lower:]')"
|
|
694
707
|
if [ -n "$got" ] && [ "$got_lc" != "$email_lc" ] && [ "$force" != "1" ]; then
|
|
708
|
+
# Signed in as a DIFFERENT account. If that account is registered in this pool,
|
|
709
|
+
# the sign-in was not a mistake worth discarding — route the fresh credential to
|
|
710
|
+
# ITS account dir (what `login <owner>` would have produced), restore this
|
|
711
|
+
# account's prior state, and report honestly that THIS account is still unfixed.
|
|
712
|
+
local owner
|
|
713
|
+
owner="$(email_owner "$got")"
|
|
714
|
+
if [ -n "$owner" ] && [ "$owner" != "$id" ]; then
|
|
715
|
+
local od="$ACC_ROOT/$owner"
|
|
716
|
+
seed_account_dir "$od"
|
|
717
|
+
if mv -f "$d/auth.json" "$od/auth.json" 2>/dev/null; then
|
|
718
|
+
chmod 600 "$od/auth.json" 2>/dev/null || true
|
|
719
|
+
clear_auth_markers "$od"
|
|
720
|
+
restore_snap
|
|
721
|
+
trap - INT TERM
|
|
722
|
+
log_to ops.log "login redirect: signed in as $got -> credential saved to $owner (target was $id)"
|
|
723
|
+
echo "you signed in as $got — that is $owner, so the credential was saved to $owner (usable now)."
|
|
724
|
+
echo "$id ($email) still needs its own sign-in: codex-accounts login $id"
|
|
725
|
+
return 1
|
|
726
|
+
fi
|
|
727
|
+
fi
|
|
695
728
|
restore_snap
|
|
696
729
|
trap - INT TERM
|
|
697
730
|
die "you signed in as $got but $id is $email — nothing saved, previous credential restored (use --force to override)"
|
|
@@ -819,12 +852,27 @@ cmd_relogin() {
|
|
|
819
852
|
read -r ans
|
|
820
853
|
case "$ans" in y|Y|yes) ;; *) echo "aborted"; return 1 ;; esac
|
|
821
854
|
fi
|
|
855
|
+
# Accounts that were BROKEN when this run started: if one of them turns healthy
|
|
856
|
+
# mid-run (a redirected sign-in from an earlier iteration landed its credential),
|
|
857
|
+
# asking the user to sign it in again would be pure friction — it gets skipped.
|
|
858
|
+
# Accounts that were healthy at start (explicit ids / --all) are never skipped:
|
|
859
|
+
# the user asked to re-authenticate them on purpose.
|
|
860
|
+
local pre_bad
|
|
861
|
+
pre_bad=" $(accounts_needing_login 2>/dev/null | tr '\n' ' ') "
|
|
822
862
|
# One sync at the end instead of one per account: each auto_sync is an ssh round trip.
|
|
823
863
|
local prev_no_sync="${CODEX_MULTIACC_NO_SYNC:-0}" failed="" done_ok=0
|
|
824
864
|
export CODEX_MULTIACC_NO_SYNC=1
|
|
825
865
|
for id in $ids; do
|
|
826
866
|
echo
|
|
827
867
|
echo "=== $id ==============================================================="
|
|
868
|
+
case "$pre_bad" in
|
|
869
|
+
*" $id "*)
|
|
870
|
+
if [ "$(account_audit 2>/dev/null | awk -F'\t' -v i="$id" '$1 == i { print $4 }')" = "ok" ]; then
|
|
871
|
+
echo "$id already has a working login (a credential landed during an earlier sign-in) — skipping."
|
|
872
|
+
done_ok=$((done_ok + 1))
|
|
873
|
+
continue
|
|
874
|
+
fi ;;
|
|
875
|
+
esac
|
|
828
876
|
if [ "$browser" = "1" ]; then
|
|
829
877
|
( cmd_login "$id" --browser ) && done_ok=$((done_ok + 1)) || failed="$failed $id"
|
|
830
878
|
else
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-multiacc",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
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": {
|
package/tests/run-tests.sh
CHANGED
|
@@ -1757,6 +1757,62 @@ EOF
|
|
|
1757
1757
|
)"
|
|
1758
1758
|
[ "$got_email" = "d@cx" ] && t_ok "codex: refused login restores the prior credential" \
|
|
1759
1759
|
|| t_fail "codex login restore" "auth.json now belongs to: $got_email"
|
|
1760
|
+
# signing in as a DIFFERENT-but-REGISTERED account routes the credential to that
|
|
1761
|
+
# account instead of discarding it (and the target account stays honestly unfixed)
|
|
1762
|
+
rm -f "$CX/acct-01/auth.json"
|
|
1763
|
+
out="$(FAKE_EMAIL=a@cx codex-accounts login acct-04 2>&1)"
|
|
1764
|
+
rc=$?
|
|
1765
|
+
check "codex: cross-account sign-in is redirected, not discarded" "credential was saved to acct-01" "$out"
|
|
1766
|
+
check "codex: redirect says the target still needs its sign-in" "acct-04 (d@cx) still needs its own sign-in" "$out"
|
|
1767
|
+
[ "$rc" != "0" ] && t_ok "codex: redirected login still exits nonzero for the target" \
|
|
1768
|
+
|| t_fail "codex redirect rc" "rc=0 for an account that was not fixed"
|
|
1769
|
+
[ -s "$CX/acct-01/auth.json" ] && t_ok "codex: redirected credential landed at its owner" \
|
|
1770
|
+
|| t_fail "codex redirect landing" "acct-01/auth.json missing"
|
|
1771
|
+
got_email="$(python3 - "$CX/acct-04/auth.json" <<'EOF'
|
|
1772
|
+
import base64, json, sys
|
|
1773
|
+
t = json.load(open(sys.argv[1]))['tokens']['id_token']
|
|
1774
|
+
p = t.split('.')[1]; p += '=' * (-len(p) % 4)
|
|
1775
|
+
print(json.loads(base64.urlsafe_b64decode(p)).get('email', ''))
|
|
1776
|
+
EOF
|
|
1777
|
+
)"
|
|
1778
|
+
[ "$got_email" = "d@cx" ] && t_ok "codex: redirect restored the target's prior credential" \
|
|
1779
|
+
|| t_fail "codex redirect restore" "acct-04 auth.json belongs to: $got_email"
|
|
1780
|
+
# `add` while signed into an already-registered account also keeps the credential
|
|
1781
|
+
rm -f "$CX/acct-01/auth.json"
|
|
1782
|
+
before_dirs="$(ls "$CX" | sort)"
|
|
1783
|
+
out="$(FAKE_EMAIL=a@cx codex-accounts add 2>&1)"
|
|
1784
|
+
rc=$?
|
|
1785
|
+
check "codex: add of a registered account saves the fresh sign-in to it" "fresh sign-in was saved to acct-01" "$out"
|
|
1786
|
+
[ "$rc" = "0" ] && t_ok "codex: add redirect exits 0 (nothing new to register)" || t_fail "codex add redirect rc" "rc=$rc"
|
|
1787
|
+
[ -s "$CX/acct-01/auth.json" ] && t_ok "codex: add redirect landed the credential" \
|
|
1788
|
+
|| t_fail "codex add redirect" "acct-01/auth.json missing"
|
|
1789
|
+
[ "$before_dirs" = "$(ls "$CX" | sort)" ] && t_ok "codex: add redirect leaves no reserved dir behind" \
|
|
1790
|
+
|| t_fail "codex add redirect cleanup" "pool dirs changed"
|
|
1791
|
+
n="$(grep -c '"email": "a@cx"' "$CX/accounts.json")"
|
|
1792
|
+
[ "$n" = "1" ] && t_ok "codex: add redirect creates no duplicate entry" || t_fail "codex add redirect dup" "count=$n"
|
|
1793
|
+
# relogin end-to-end: a redirect mid-run fixes the OTHER account, whose own turn is
|
|
1794
|
+
# then skipped instead of demanding a second sign-in. Both accounts' home must be
|
|
1795
|
+
# THIS machine's kind, or a credless account reads as 'remote' (grant lives
|
|
1796
|
+
# elsewhere) on the other platform and never enters the worklist.
|
|
1797
|
+
cx_mk=mac; [ "$(uname -s)" = "Darwin" ] || cx_mk=linux
|
|
1798
|
+
python3 - "$CX/accounts.json" "$cx_mk" <<'EOF'
|
|
1799
|
+
import json, os, sys
|
|
1800
|
+
d = json.load(open(sys.argv[1]))
|
|
1801
|
+
for a in d['accounts']:
|
|
1802
|
+
if a['id'] in ('acct-01', 'acct-04'):
|
|
1803
|
+
a['home'] = sys.argv[2]
|
|
1804
|
+
json.dump(d, open(sys.argv[1] + '.tmp', 'w'), indent=2); os.replace(sys.argv[1] + '.tmp', sys.argv[1])
|
|
1805
|
+
EOF
|
|
1806
|
+
rm -f "$CX/acct-01/auth.json" "$CX/acct-04/auth.json"
|
|
1807
|
+
out="$(FAKE_EMAIL=d@cx codex-accounts relogin --yes 2>&1)"
|
|
1808
|
+
rc=$?
|
|
1809
|
+
check "codex: relogin redirect saves the mis-ordered sign-in" "credential was saved to acct-04" "$out"
|
|
1810
|
+
check "codex: relogin skips an account fixed mid-run" "already has a working login" "$out"
|
|
1811
|
+
check "codex: relogin counts the redirect-fixed account" "re-authenticated 1 of 2" "$out"
|
|
1812
|
+
check "codex: relogin still reports the unfixed account" "still failing: acct-01" "$out"
|
|
1813
|
+
[ "$rc" != "0" ] && t_ok "codex: relogin with an unfixed account exits nonzero" || t_fail "codex relogin redirect rc" "rc=0"
|
|
1814
|
+
mk_cx_auth "$CX/acct-01/auth.json" a@cx "$FUTURE_EXP"
|
|
1815
|
+
mk_cx_auth "$CX/acct-04/auth.json" d@cx "$FUTURE_EXP"
|
|
1760
1816
|
# the sign-in ceremony is DEVICE-CODE by default (the localhost browser callback
|
|
1761
1817
|
# cannot reach a remote/SSH machine); --browser opts into the callback flow
|
|
1762
1818
|
export FAKE_LOGIN_ARGS="$WORK/cx-login-args"
|