claude-multiacc 2.0.57 → 2.0.59
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/README.md +111 -3
- package/bin/claude +323 -84
- package/bin/claude-accounts +273 -42
- package/bin/codex +15 -0
- package/bin/codex-accounts +31 -4
- package/docs/ACCOUNT_OPERATIONS.md +62 -2
- package/docs/AUTORESUME-HANDOFF.md +30 -3
- package/docs/AUTORESUME.md +275 -18
- package/docs/CODEX.md +1 -0
- package/docs/INSTALLATION.md +7 -1
- package/docs/VERIFICATION.md +4 -0
- package/lib/__pycache__/audit.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume_cross.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume_cross_dispatch.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume_cross_receipt.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume_handoff.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume_probe.cpython-312.pyc +0 -0
- package/lib/__pycache__/autoresume_receipt.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_autoresume_mode.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_config_edit.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_credit_controls.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_credit_local_scope.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_credit_selection.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_credits.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_limits_lock.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_python.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_trust.cpython-312.pyc +0 -0
- package/lib/__pycache__/codex_usage_fetch.cpython-312.pyc +0 -0
- package/lib/__pycache__/keychain.cpython-312.pyc +0 -0
- package/lib/__pycache__/mcp_oauth.cpython-312.pyc +0 -0
- package/lib/__pycache__/mcp_registry.cpython-312.pyc +0 -0
- package/lib/__pycache__/model_catalog.cpython-312.pyc +0 -0
- package/lib/__pycache__/model_match.cpython-312.pyc +0 -0
- package/lib/__pycache__/report.cpython-312.pyc +0 -0
- package/lib/__pycache__/selector_credit_scope.cpython-312.pyc +0 -0
- package/lib/__pycache__/selector_credits.cpython-312.pyc +0 -0
- package/lib/__pycache__/selector_funding.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__/session_settings.cpython-312.pyc +0 -0
- package/lib/__pycache__/shim_path.cpython-312.pyc +0 -0
- package/lib/audit.py +88 -22
- package/lib/autoresume.py +1197 -82
- package/lib/autoresume_cross.py +3 -1
- package/lib/autoresume_handoff.py +82 -10
- package/lib/autoresume_probe.py +2 -2
- package/lib/claude_client_error.py +9 -4
- package/lib/codex_trust.py +263 -0
- package/lib/common.sh +131 -3
- package/lib/credential.py +5 -2
- package/lib/install_actions.sh +19 -0
- package/lib/keychain.py +180 -36
- package/lib/mcp_oauth.py +1748 -0
- package/lib/model_catalog.json +404 -0
- package/lib/model_catalog.py +201 -0
- package/lib/model_match.py +104 -0
- package/lib/report.py +8 -5
- package/lib/session_settings.py +123 -0
- package/package.json +2 -2
- package/tests/__pycache__/autoresume_cross_receipt_support.cpython-312.pyc +0 -0
- package/tests/__pycache__/autoresume_cross_tmux_environment.cpython-312.pyc +0 -0
- package/tests/__pycache__/autoresume_cross_tmux_support.cpython-312.pyc +0 -0
- package/tests/__pycache__/autoresume_cross_unit_support.cpython-312.pyc +0 -0
- package/tests/__pycache__/autoresume_tmux_support.cpython-312.pyc +0 -0
- 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_catalog.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_catalog_cli.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_codex_records.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_codex_trust.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_claude_holders.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_guards.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_holders.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_origin.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_policy.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_receipt.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_tmux.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_tmux_races.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cross_tokens.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_cursor.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_fallback.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_handoff_argv.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_handoff_history.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_handoff_origins.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_memory.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_memory_tmux.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_mode.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_probe.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_receipt.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_relaunch_settings.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_settings.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_sidechain.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_sidechain_tmux.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_tmux.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_tmux_modes.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_autoresume_trust_tmux.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_credits.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_credits_polling.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_credits_report.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_codex_reset_fetch.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_cached_credits.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_selector_credit_scope.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_selector_credit_shim.cpython-312.pyc +0 -0
- package/tests/__pycache__/test_selector_credits.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/fake_codex_tui.py +25 -0
- package/tests/fake_security.sh +24 -2
- package/tests/fixtures/cross_provider_cli.py +111 -1
- package/tests/run-tests.sh +1028 -23
- package/tests/test_autoresume.py +215 -8
- package/tests/test_autoresume_catalog.py +226 -0
- package/tests/test_autoresume_catalog_cli.py +185 -0
- package/tests/test_autoresume_codex_records.py +16 -0
- package/tests/test_autoresume_codex_trust.py +247 -0
- package/tests/test_autoresume_cross_guards.py +28 -0
- package/tests/test_autoresume_cross_origin.py +70 -0
- package/tests/test_autoresume_cross_tmux.py +27 -2
- package/tests/test_autoresume_cross_tokens.py +32 -2
- package/tests/test_autoresume_handoff_argv.py +120 -16
- package/tests/test_autoresume_handoff_origins.py +67 -0
- package/tests/test_autoresume_memory.py +1141 -0
- package/tests/test_autoresume_memory_tmux.py +94 -0
- package/tests/test_autoresume_probe.py +2 -1
- package/tests/test_autoresume_relaunch_settings.py +317 -0
- package/tests/test_autoresume_settings.py +135 -0
- package/tests/test_autoresume_sidechain.py +1518 -0
- package/tests/test_autoresume_sidechain_tmux.py +79 -0
- package/tests/test_autoresume_trust_tmux.py +63 -0
- package/tests/test_claude_client_error.py +10 -7
- package/tests/test_claude_org_block.py +1 -0
- package/tests/test_claude_pool_prepare.py +4 -1
- package/tests/test_claude_reset.py +3 -0
- package/tests/test_keychain_nologin.py +340 -0
- package/tests/test_mcp_oauth.py +1414 -0
- package/tests/test_packaged_commands.py +41 -0
package/docs/INSTALLATION.md
CHANGED
|
@@ -46,7 +46,13 @@ postinstall, so `npm i -g claude-multiacc@latest` fully updates the shim, CLI, a
|
|
|
46
46
|
A daily job (launchd on macOS, cron on Linux, 04:07) runs `claude-accounts self-update`
|
|
47
47
|
to pull the latest release on its own — `npm i -g claude-multiacc@latest` for npm installs,
|
|
48
48
|
`git pull && ./install.sh` for git checkouts; set `CLAUDE_MULTIACC_AUTOUPDATE=0` before install to
|
|
49
|
-
opt out.
|
|
49
|
+
opt out. An npm install only ever moves UP: it installs the registry's version only when that
|
|
50
|
+
is newer than its own, and nothing when it cannot read the registry's version. A build
|
|
51
|
+
installed by hand ahead of a publish must carry a version at or above the registry's (the
|
|
52
|
+
registry's own, or the next patch, which `node scripts/auto-version.mjs` writes into
|
|
53
|
+
`package.json` in a checkout); it then stays until a newer release is published. A git
|
|
54
|
+
checkout pulls with `--ff-only`. `update-notifier` also prints a one-line nudge when a newer
|
|
55
|
+
version is on npm.
|
|
50
56
|
(Note: newer npm versions gate install scripts; if postinstall is blocked, just run
|
|
51
57
|
`claude-multiacc install` once.)
|
|
52
58
|
|
package/docs/VERIFICATION.md
CHANGED
|
@@ -88,6 +88,10 @@ repointed via `CLAUDE_BIN=/usr/local/bin/claude` and restarted healthy.
|
|
|
88
88
|
|
|
89
89
|
- Tokens and credentials live as 0600 files under `~/.claude-accounts` (and
|
|
90
90
|
`/root/.claude-accounts` on the server). Server compromise = account access.
|
|
91
|
+
- `~/.claude-accounts/mcp-oauth.json` (0600) holds the MCP servers' OAuth logins every
|
|
92
|
+
account on this machine shares (README, *MCP OAuth logins*). It never leaves the machine:
|
|
93
|
+
sync and the limits fan-out push explicit file lists that do not name it, and
|
|
94
|
+
`mcp auth-status` / `ops.log` name servers, accounts and expiries, never a token.
|
|
91
95
|
- `selection.log` records timestamps, accounts and cwd, and for auto-resume also
|
|
92
96
|
session ids and error classes. It never records prompt or transcript text.
|
|
93
97
|
- Rotating multiple subscriptions to spread usage may be flagged by anti-abuse systems;
|
|
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
|
|
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
|
|
Binary file
|
|
Binary file
|
package/lib/audit.py
CHANGED
|
@@ -24,6 +24,16 @@ the same document and the same machine-local class; the file is simply gone once
|
|
|
24
24
|
client has moved it into the Keychain, so a file-only reading calls a working login
|
|
25
25
|
"missing" (that is exactly what happened to a whole pool on 2026-08-28).
|
|
26
26
|
|
|
27
|
+
That document is the client's whole credential store: it also keeps the OAuth state of
|
|
28
|
+
the MCP servers the account signed into (`mcpOAuth`, `mcpOAuthClientConfig`, each entry
|
|
29
|
+
with its own expiresAt/refreshToken). It holds a LOGIN only when it carries a
|
|
30
|
+
`claudeAiOauth` object. From a session that cannot open the Keychain (ssh/tmux) Claude
|
|
31
|
+
Code 2.1.303 writes a .credentials.json holding only MCP state as soon as an OAuth MCP
|
|
32
|
+
server is discovered; counting that file as the login picked accounts that exported no
|
|
33
|
+
token and could only say "Not logged in" (2026-10-19). Such a document is no login for
|
|
34
|
+
any decision here — the Keychain item and server.token decide — and every expiry or
|
|
35
|
+
refresh-token reading looks inside the login object alone (bin/claude: file_login).
|
|
36
|
+
|
|
27
37
|
Run directly for a TSV dump: python3 lib/audit.py <acc-root> [mac|linux]
|
|
28
38
|
Columns: id, email, home, state, label, reason, fix
|
|
29
39
|
"""
|
|
@@ -59,6 +69,44 @@ def _nonempty(path):
|
|
|
59
69
|
return False
|
|
60
70
|
|
|
61
71
|
|
|
72
|
+
def _text(path):
|
|
73
|
+
try:
|
|
74
|
+
with open(path, errors='replace') as f:
|
|
75
|
+
return f.read()
|
|
76
|
+
except OSError:
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# The login object found in a document's TEXT, as the shim isolates it (bin/claude:
|
|
81
|
+
# cred_login_of): flat, so it ends at its first closing brace — or, in a truncated file,
|
|
82
|
+
# at the end of the text. Only for a document that does not parse.
|
|
83
|
+
_LOGIN_TEXT = re.compile(r'"claudeAiOauth"\s*:\s*\{[^}]*')
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def login_of(doc):
|
|
87
|
+
"""The Claude login a parsed credential document carries — its `claudeAiOauth`
|
|
88
|
+
object — or None: a document holding only MCP OAuth state holds no login."""
|
|
89
|
+
o = doc.get('claudeAiOauth') if isinstance(doc, dict) else None
|
|
90
|
+
return o if isinstance(o, dict) else None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def file_has_login(cpath):
|
|
94
|
+
"""True when <cpath> (a .credentials.json) holds a Claude login, by the shim's rule
|
|
95
|
+
(bin/claude: file_login): a document carrying a `claudeAiOauth` object — or, for one
|
|
96
|
+
that does not parse (truncated, mid-write), text that still opens one. An empty file
|
|
97
|
+
or one with MCP state alone is no login: the Keychain and server.token decide."""
|
|
98
|
+
if not _nonempty(cpath):
|
|
99
|
+
return False
|
|
100
|
+
txt = _text(cpath)
|
|
101
|
+
if txt is None:
|
|
102
|
+
return False
|
|
103
|
+
try:
|
|
104
|
+
doc = json.loads(txt)
|
|
105
|
+
except (ValueError, RecursionError):
|
|
106
|
+
return bool(_LOGIN_TEXT.search(txt))
|
|
107
|
+
return login_of(doc) is not None
|
|
108
|
+
|
|
109
|
+
|
|
62
110
|
def marker_reason(mpath):
|
|
63
111
|
"""Second line of a marker file, as a human string ('' when absent/unreadable)."""
|
|
64
112
|
try:
|
|
@@ -109,15 +157,22 @@ def expired_marked(d, now=None):
|
|
|
109
157
|
return False
|
|
110
158
|
elif slug != 'org-blocked':
|
|
111
159
|
mt = _mtime(mpath)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
160
|
+
p = os.path.join(d, 'server.token')
|
|
161
|
+
if os.path.isfile(p) and _mtime(p) > mt:
|
|
162
|
+
return False
|
|
163
|
+
# A newer LOGIN is the evidence a credential park is about. The client rewrites
|
|
164
|
+
# .credentials.json with MCP state alone on every MCP token refresh; that file
|
|
165
|
+
# proves nothing about the login and heals nothing.
|
|
166
|
+
cpath = os.path.join(d, '.credentials.json')
|
|
167
|
+
if file_has_login(cpath):
|
|
168
|
+
if _mtime(cpath) > mt:
|
|
115
169
|
return False
|
|
116
170
|
# A login the client keeps in the Keychain is "written after the marker" in
|
|
117
171
|
# exactly the same sense — its modification stamp is readable even when this
|
|
118
|
-
# session cannot open the secret.
|
|
119
|
-
|
|
120
|
-
|
|
172
|
+
# session cannot open the secret. An item this session CAN read is judged by
|
|
173
|
+
# what it holds: MCP state alone heals nothing there either.
|
|
174
|
+
elif keychain.item_mtime(d) > mt \
|
|
175
|
+
and keychain.probe(d)['state'] in ('present', 'locked'):
|
|
121
176
|
return False
|
|
122
177
|
soft = marker_soft_until(detail)
|
|
123
178
|
if soft is not None and soft <= now:
|
|
@@ -136,18 +191,22 @@ def _ms(value):
|
|
|
136
191
|
|
|
137
192
|
def _scrape(cpath):
|
|
138
193
|
"""The shim's rule for a credential JSON cannot parse: sed out the numbers and the
|
|
139
|
-
presence of a refresh token (bin/claude: cred_num / creds_dead)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
194
|
+
presence of a refresh token (bin/claude: cred_num / creds_dead) — from the LOGIN
|
|
195
|
+
object alone, since every MCP entry beside it carries an expiresAt and a
|
|
196
|
+
refreshToken of its own. Used so a half-written file is judged the same way on both
|
|
197
|
+
sides — audit.py must never call an account dead that the shim is happily
|
|
198
|
+
selecting. None when the text opens no login at all."""
|
|
199
|
+
txt = _text(cpath)
|
|
200
|
+
m = _LOGIN_TEXT.search(txt) if txt is not None else None
|
|
201
|
+
if not m:
|
|
145
202
|
return None
|
|
203
|
+
login = m.group(0)
|
|
204
|
+
|
|
146
205
|
def num(key):
|
|
147
|
-
|
|
148
|
-
return float(
|
|
206
|
+
found = re.search(r'"%s"\s*:\s*(\d+)' % key, login)
|
|
207
|
+
return float(found.group(1)) / 1000.0 if found else 0.0
|
|
149
208
|
return {'exp': num('expiresAt'), 'rexp': num('refreshTokenExpiresAt'),
|
|
150
|
-
'has_refresh': bool(re.search(r'"refreshToken"\s*:\s*"[^"]',
|
|
209
|
+
'has_refresh': bool(re.search(r'"refreshToken"\s*:\s*"[^"]', login))}
|
|
151
210
|
|
|
152
211
|
|
|
153
212
|
def creds_doc_state(doc, now):
|
|
@@ -171,7 +230,8 @@ def creds_doc_state(doc, now):
|
|
|
171
230
|
|
|
172
231
|
|
|
173
232
|
def creds_state(cpath, now):
|
|
174
|
-
"""(state, reason) for an on-disk .credentials.json
|
|
233
|
+
"""(state, reason) for an on-disk .credentials.json that holds a login
|
|
234
|
+
(file_has_login) — a file without one is no login to judge."""
|
|
175
235
|
scraped = None
|
|
176
236
|
try:
|
|
177
237
|
return creds_doc_state(json.load(open(cpath)), now)
|
|
@@ -191,12 +251,13 @@ def oauth_login(d, now):
|
|
|
191
251
|
"""Where <d>'s OAuth login lives and whether it can authenticate.
|
|
192
252
|
|
|
193
253
|
Returns {'store': 'file'|'keychain'|None, 'state': 'ok'|'expired'|'locked'|None,
|
|
194
|
-
'reason': str, 'doc': dict|None}. The FILE wins when
|
|
195
|
-
a session without keychain access (ssh) will use, and the client itself reads
|
|
196
|
-
keychain first only when it can open it.
|
|
197
|
-
|
|
254
|
+
'reason': str, 'doc': dict|None}. The FILE wins when it holds a login: it is
|
|
255
|
+
what a session without keychain access (ssh) will use, and the client itself reads
|
|
256
|
+
the keychain first only when it can open it. A file or item holding MCP state alone
|
|
257
|
+
is no login (store None unless the other place has one). 'locked' means the login
|
|
258
|
+
exists in the Keychain but this session cannot open it."""
|
|
198
259
|
cpath = os.path.join(d, '.credentials.json')
|
|
199
|
-
if
|
|
260
|
+
if file_has_login(cpath):
|
|
200
261
|
state, reason = creds_state(cpath, now)
|
|
201
262
|
return {'store': 'file', 'state': state, 'reason': reason, 'doc': None}
|
|
202
263
|
kc = keychain.probe(d)
|
|
@@ -215,6 +276,11 @@ def oauth_login(d, now):
|
|
|
215
276
|
'reason': ('OAuth login is in the macOS Keychain, which this session '
|
|
216
277
|
'cannot open (ssh/background) — it works from the Mac\'s own '
|
|
217
278
|
'session'), 'doc': None}
|
|
279
|
+
if kc['state'] == 'nologin' or _nonempty(cpath):
|
|
280
|
+
# The client's MCP OAuth state with no login beside it (or a file that is no
|
|
281
|
+
# credential at all): nothing here authenticates, whatever its entries' expiry.
|
|
282
|
+
return {'store': None, 'state': None, 'doc': None,
|
|
283
|
+
'reason': 'no Claude login here (the credential store holds no claudeAiOauth)'}
|
|
218
284
|
return {'store': None, 'state': None, 'reason': '', 'doc': None}
|
|
219
285
|
|
|
220
286
|
|
|
@@ -356,7 +422,7 @@ def audit_account(root, acct, now=None, machine=None, require_verified_token=Fal
|
|
|
356
422
|
row['reason'] = f'no auth here — the grant lives on the {home} machine'
|
|
357
423
|
else:
|
|
358
424
|
row['state'] = 'missing'
|
|
359
|
-
row['reason'] = 'no credentials on this machine'
|
|
425
|
+
row['reason'] = login['reason'] or 'no credentials on this machine'
|
|
360
426
|
return done(row)
|
|
361
427
|
|
|
362
428
|
|