claude-multiacc 2.0.49 → 2.0.50
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/docs/UNIFIED_SELECTOR.md +4 -3
- package/docs/VERIFICATION.md +1 -1
- package/lib/__pycache__/audit.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume_probe.cpython-312.pyc +0 -0
- package/lib/__pycache__/claude_client_error.cpython-312.pyc +0 -0
- package/lib/__pycache__/claude_reset.cpython-312.pyc +0 -0
- package/lib/__pycache__/claude_token_identity.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_config_edit.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_python.cpython-312.pyc +0 -0
- package/lib/__pycache__/keychain.cpython-312.pyc +0 -0
- package/lib/__pycache__/mcp_registry.cpython-312.pyc +0 -0
- package/lib/__pycache__/selector_policy.cpython-312.pyc +0 -0
- package/lib/__pycache__/selector_primitives.cpython-312.pyc +0 -0
- package/lib/__pycache__/shim_path.cpython-312.pyc +0 -0
- package/lib/selector_policy.py +3 -12
- package/package.json +3 -2
- package/tests/__pycache__/packaged_command_support.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_fallback.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_probe.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_claude_reset.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_claude_token_identity.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_codex_reset.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_polling.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_reporting.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_windows.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_selector.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_selector_spread.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_token_identity_ceremony.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_token_identity_integration.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_token_identity_retry.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_token_identity_sync.cpython-312.pyc +0 -0
- package/tests/test_selector_spread.py +65 -0
package/docs/UNIFIED_SELECTOR.md
CHANGED
|
@@ -57,9 +57,10 @@ weekly limits").
|
|
|
57
57
|
is what makes a burst of parallel launches fan out instead of stacking on one
|
|
58
58
|
account, and it is deterministic: the same request always yields the same winner.
|
|
59
59
|
|
|
60
|
-
With no usable quota anywhere the band cannot apply (`band_floor` is `null`)
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
With no usable quota anywhere the band cannot apply (`band_floor` is `null`). Every
|
|
61
|
+
eligible account is then a peer: least-recently-selected wins, followed by the same
|
|
62
|
+
request-key spread. Equal histories must not concentrate launches on the first provider,
|
|
63
|
+
account or runner identity merely because telemetry is unknown.
|
|
63
64
|
|
|
64
65
|
Success returns the concrete engine/account/runner generation, normalized score basis
|
|
65
66
|
(which still reports `effective_headroom` = min(weekly, session) for observability),
|
package/docs/VERIFICATION.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
tests/run-tests.sh # sandboxed compatibility tests, no quota
|
|
7
|
-
python3 tests
|
|
7
|
+
python3 -m unittest discover -s tests -p 'test_selector*.py' # unified selector contract and distribution
|
|
8
8
|
python3 -m unittest discover -s tests -p 'test_autoresume*.py' # watchers and fallback probes (also in run-tests.sh)
|
|
9
9
|
npm run test:commands # packed npm commands; run npm install --ignore-scripts first
|
|
10
10
|
claude-accounts verify # real matrix: `claude -p "reply OK"` per authed account
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/selector_policy.py
CHANGED
|
@@ -119,14 +119,6 @@ def _apply_policy(request: dict, rows: list[dict]) -> tuple[tuple, int]:
|
|
|
119
119
|
return producer, alternatives
|
|
120
120
|
|
|
121
121
|
|
|
122
|
-
def _winner_key(row: dict) -> tuple:
|
|
123
|
-
descending_headroom = Decimal(row["effective_headroom"] or "-1").copy_negate()
|
|
124
|
-
return (
|
|
125
|
-
not row["quota_known"], descending_headroom,
|
|
126
|
-
row["last_selected_at"] is not None, row["last_selected_at"] or "",
|
|
127
|
-
row["engine"], row["account_id"], row["runner_id"], row["runner_generation"])
|
|
128
|
-
|
|
129
|
-
|
|
130
122
|
def _session_gate(rows: list[dict], gate: Decimal | None) -> list[dict]:
|
|
131
123
|
"""The FIRST cut: accounts whose 5h session bucket still has room.
|
|
132
124
|
|
|
@@ -229,13 +221,12 @@ def select(request: object) -> dict:
|
|
|
229
221
|
ranked = healthy or known
|
|
230
222
|
floor = _band_floor(ranked, band) if band is not None else None
|
|
231
223
|
if floor is None:
|
|
232
|
-
# No usable quota anywhere:
|
|
233
|
-
#
|
|
224
|
+
# No usable quota anywhere: every eligible row is an unknown peer.
|
|
225
|
+
# Keep LRU and the request spread instead of preferring a fixed identity.
|
|
234
226
|
banded = eligible
|
|
235
|
-
winner = min(banded, key=_winner_key)
|
|
236
227
|
else:
|
|
237
228
|
banded = [row for row in ranked if Decimal(row["weekly_remaining"]) >= floor]
|
|
238
|
-
|
|
229
|
+
winner = min(banded, key=_band_key(request["reservation_key"]))
|
|
239
230
|
rows.sort(key=_snapshot_key)
|
|
240
231
|
snapshot = canonical_sha256(rows)
|
|
241
232
|
chosen = dict(zip(("runner_id", "runner_generation", "engine", "account_id"), _row_identity(winner)))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-multiacc",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.50",
|
|
4
4
|
"description": "Unified Claude Code and OpenAI Codex subscription pooling with quota-aware selection.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -56,7 +56,8 @@
|
|
|
56
56
|
"scripts": {
|
|
57
57
|
"postinstall": "node scripts/postinstall.mjs",
|
|
58
58
|
"test": "npm run test:core && npm run test:seeding && npm run test:commands",
|
|
59
|
-
"test:core": "bash tests/run-tests.sh &&
|
|
59
|
+
"test:core": "bash tests/run-tests.sh && npm run test:selector && python3 tests/test_shim_path.py",
|
|
60
|
+
"test:selector": "python3 -m unittest discover -s tests -p 'test_selector*.py'",
|
|
60
61
|
"test:seeding": "python3 tests/test_account_seeding.py && python3 tests/test_mcp_registry.py && npm run test:codex-settings",
|
|
61
62
|
"test:codex-settings": "python3 tests/test_codex_config_edit.py && python3 tests/test_codex_settings.py",
|
|
62
63
|
"test:commands": "python3 tests/test_packaged_commands.py"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Unknown quota must not turn a launch burst into a fixed-identity preference."""
|
|
2
|
+
|
|
3
|
+
from collections import Counter
|
|
4
|
+
import unittest
|
|
5
|
+
|
|
6
|
+
from test_selector import _candidate, _request
|
|
7
|
+
from selector_policy import select
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class UnknownQuotaSpreadTests(unittest.TestCase):
|
|
11
|
+
def test_unknown_and_partial_quota_spread_across_accounts_and_providers(self):
|
|
12
|
+
for weekly, session in ((None, None), (0, None), (None, 0)):
|
|
13
|
+
with self.subTest(weekly=weekly, session=session):
|
|
14
|
+
pool = [_candidate(engine=engine, account_id=f"acct-{index:02d}",
|
|
15
|
+
weekly_pct=weekly, session_pct=session)
|
|
16
|
+
for engine in ("claude", "codex") for index in range(4)]
|
|
17
|
+
counts = Counter()
|
|
18
|
+
for index in range(120):
|
|
19
|
+
response = select(_request(pool, reservation_key=f"burst-{index}"))
|
|
20
|
+
self.assertTrue(response["ok"])
|
|
21
|
+
self.assertIsNone(response["band_floor"])
|
|
22
|
+
self.assertEqual(response["session_ok_count"], 0)
|
|
23
|
+
counts[(response["engine"], response["account_id"])] += 1
|
|
24
|
+
self.assertEqual(len(counts), len(pool), counts)
|
|
25
|
+
self.assertLess(max(counts.values()), 40, counts)
|
|
26
|
+
|
|
27
|
+
def test_equal_history_spreads_same_account_id_across_runner_identities(self):
|
|
28
|
+
pool = [_candidate(runner_id=index, engine="codex", account_id="acct-01",
|
|
29
|
+
weekly_pct=None, session_pct=None,
|
|
30
|
+
reservation_history={"active_expires_at": None,
|
|
31
|
+
"last_selected_at": "2026-08-25T08:00:00Z"})
|
|
32
|
+
for index in range(1, 9)]
|
|
33
|
+
counts = Counter(select(_request(pool, reservation_key=f"burst-{index}"))["runner_id"]
|
|
34
|
+
for index in range(120))
|
|
35
|
+
self.assertEqual(len(counts), len(pool), counts)
|
|
36
|
+
self.assertLess(max(counts.values()), 40, counts)
|
|
37
|
+
|
|
38
|
+
def test_unknown_quota_preserves_lru_and_active_reservation_exclusion(self):
|
|
39
|
+
pool = [_candidate(account_id=name, weekly_pct=None, session_pct=None,
|
|
40
|
+
reservation_history={"active_expires_at": expiry,
|
|
41
|
+
"last_selected_at": last})
|
|
42
|
+
for name, expiry, last in (
|
|
43
|
+
("reserved", "2026-08-25T09:10:00Z", None),
|
|
44
|
+
("unused", None, None),
|
|
45
|
+
("old", None, "2026-08-25T08:00:00Z"),
|
|
46
|
+
("recent", None, "2026-08-25T08:50:00Z"))]
|
|
47
|
+
for index in range(20):
|
|
48
|
+
response = select(_request(pool, reservation_key=f"burst-{index}"))
|
|
49
|
+
self.assertEqual(response["account_id"], "unused")
|
|
50
|
+
self.assertEqual(response["eligible_count"], 3)
|
|
51
|
+
for index in range(20):
|
|
52
|
+
response = select(_request(pool[2:], reservation_key=f"burst-{index}"))
|
|
53
|
+
self.assertEqual(response["account_id"], "old")
|
|
54
|
+
|
|
55
|
+
def test_unknown_quota_remains_deterministic_and_input_order_independent(self):
|
|
56
|
+
pool = [_candidate(account_id=f"acct-{index:02d}", weekly_pct=None, session_pct=None)
|
|
57
|
+
for index in range(8)]
|
|
58
|
+
response = select(_request(pool, reservation_key="stable"))
|
|
59
|
+
self.assertEqual(response, select(_request(pool, reservation_key="stable")))
|
|
60
|
+
reversed_response = select(_request(list(reversed(pool)), reservation_key="stable"))
|
|
61
|
+
self.assertEqual(response["account_id"], reversed_response["account_id"])
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if __name__ == "__main__":
|
|
65
|
+
unittest.main()
|