Skip to content

Commit 2500e31

Browse files
ZD Studiosclaude
andcommitted
fix: honest Claude connect + 'aios claude-login' (the OAuth step the button was missing)
'Connected but doesn't work' was because the button only pointed the hub at claude-code-api — it never logged the Claude CLI into the Pro/Max account (that's the browser+code OAuth flow). - /api/claude_connect now runs a real test completion and reports logged_in true/false with a clear instruction instead of a false 'connected'. - New 'aios claude-login' command: runs the Claude Code CLI login (claude / claude setup-token) — the browser OAuth for your subscription. --token uses setup-token for a long-lived token. - Settings card + connectClaude() now say the login happens in the terminal (aios claude-login), then the button routes everything through it. Verified on Windows: claude_connect returns logged_in:false with the guidance (CLI not logged in); 'aios claude-login' registered and parses. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b0f9541 commit 2500e31

4 files changed

Lines changed: 45 additions & 6 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ Plus two integrations that glue it together:
166166
- **✦ Team** — one assistant that orchestrates the whole team: the Brain plans, delegates subtasks to the specialist agents, and synthesizes one answer (the practical "merge").
167167
- **Configure openclaw AND hermes fully inside the hub** — both control-UIs are embedded via frame-stripping proxies, so you get channels, **connectors**, model providers, **MCP servers**, skills, plugins, **automations/cron**, and sessions right in the hub (they normally block embedding).
168168
- **Automations** — schedule prompts to run against any agent every N minutes (daily digests, checks).
169-
- **Connect your Claude account (Pro/Max)** — one click in **Settings** points the Brain/Team/crews at **claude-code** (your Claude subscription, no API key or per-token cost) and auto-configures everything.
169+
- **Connect your Claude account (Pro/Max)**first run `aios claude-login` (opens the browser OAuth for your subscription), then one click in **Settings → "Use my Claude subscription"** routes the Brain/Team/crews through **claude-code** (no API key, no per-token cost). The button tests a real completion and tells you if you still need to log in.
170170
- **Settings** — edit provider/key/model, channel tokens, and `aios.config.yaml`; it re-renders into every agent, no terminal needed.
171171
- **Themes** — Light, Dark, Midnight, Slate, Rose. Chat renders markdown (headers, bullets, code blocks).
172172

aios.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1650,6 +1650,24 @@ def cmd_install_cli(args):
16501650
install_cli()
16511651

16521652

1653+
def cmd_claude_login(args):
1654+
"""Log the Claude Code CLI into your Pro/Max account (OAuth browser + code)."""
1655+
claude = shutil.which("claude") or shutil.which("claude.cmd")
1656+
if not claude:
1657+
die("`claude` CLI not found. It ships with claude-code-api / Claude Code — install it, "
1658+
"then re-run. (npm i -g @anthropic-ai/claude-code)")
1659+
head("Claude login (Pro / Max)")
1660+
say(f"{C.GRY}This opens your browser to authorize your Claude subscription. "
1661+
f"Approve it (or paste the code back here).{C.R}\n")
1662+
sub = "setup-token" if getattr(args, "token", False) else None
1663+
cmd = [claude, sub] if sub else [claude]
1664+
try:
1665+
subprocess.run(cmd)
1666+
except KeyboardInterrupt:
1667+
pass
1668+
say(f"\n{C.B}Done? Restart so agents pick up the login:{C.R} aios restart claudecode hub")
1669+
1670+
16531671
def cmd_url(args):
16541672
_print_urls(load_config())
16551673

@@ -1785,6 +1803,10 @@ def build_parser():
17851803
s.add_argument("action", nargs="?", choices=["install", "uninstall"], default="install")
17861804
s.set_defaults(func=cmd_install_cli)
17871805

1806+
s = sub.add_parser("claude-login", help="log the Claude Code CLI into your Pro/Max account")
1807+
s.add_argument("--token", action="store_true", help="use `claude setup-token` (long-lived token)")
1808+
s.set_defaults(func=cmd_claude_login)
1809+
17881810
sub.add_parser("url", help="print dashboard URLs").set_defaults(func=cmd_url)
17891811
sub.add_parser("wire", help="(re)install the openclaw-os plugin into openclaw").set_defaults(func=cmd_wire)
17901812
return p

aios_hub.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,25 @@ def do_POST(self):
423423
"AIOS_DEFAULT_MODEL": model,
424424
})
425425
run_aios("setup", "--non-interactive", "--skip-tools", "--skip-install", "--skip-wire")
426-
self._send(200, {"ok": True, "model": model, "reachable": authed,
427-
"note": "" if authed else "claude-code-api didn't answer — start it (`aios start claudecode`) and run `claude` once to log into your Pro/Max account."})
426+
# Actually try a tiny completion — this reveals whether the `claude` CLI is logged in.
427+
logged_in, detail = False, ""
428+
if authed:
429+
try:
430+
b = json.dumps({"model": model, "messages": [{"role": "user", "content": "hi"}],
431+
"max_tokens": 5}).encode()
432+
rq = urllib.request.Request(CLAUDECODE + "/v1/chat/completions", data=b, method="POST",
433+
headers={"Content-Type": "application/json",
434+
"Authorization": "Bearer sk-aios-claudecode"})
435+
urllib.request.urlopen(rq, timeout=45)
436+
logged_in = True
437+
except Exception as e:
438+
detail = str(e)[:160]
439+
self._send(200, {
440+
"ok": True, "model": model, "reachable": authed, "logged_in": logged_in,
441+
"note": ("Connected — your Claude subscription is working." if logged_in else
442+
"Pointed at claude-code, but the Claude CLI isn't logged in yet. Run "
443+
"`aios claude-login` (or `claude setup-token`) in a terminal to authorize your "
444+
"Pro/Max account, then chat.")})
428445
elif self.path == "/api/schedules":
429446
items = load_schedules()
430447
op = payload.get("op", "add")

docs/dashboard.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ <h3>🔗 Connect your Claude account (Pro / Max)</h3>
239239
<p>Use your Claude subscription for the Brain, Team and crews — routed through <b>claude-code</b>,
240240
no API key or per-token cost. One click points everything at it and re-renders.</p>
241241
<div class="row"><button class="btn primary" onclick="connectClaude()">Use my Claude subscription</button><span id="claudeMsg" class="kv"></span></div>
242-
<p class="mono" style="font-size:.76rem;color:var(--text3);margin-top:12px">First time? run <b>claude</b> once in a terminal to log in to your Pro/Max account, then click above.</p>
242+
<p style="font-size:.8rem;color:var(--text3);margin-top:12px"><b>The login happens in your terminal, not here.</b> First run <span class="mono" style="color:var(--accent-strong)">aios claude-login</span> — it opens your browser to authorize your Pro/Max account (or paste a code). Then click the button above to route everything through it.</p>
243243
</div>
244244
<div class="card">
245245
<h3>🔑 Model provider</h3>
@@ -378,8 +378,8 @@ <h3>${ICON[key]} ${key} <span class="badge ${up?'up':'down'}">${up?'running':'st
378378
async function connectClaude(){
379379
document.getElementById('claudeMsg').innerHTML='<span class="spin"></span> connecting…';
380380
try{const r=await(await fetch('/api/claude_connect',{method:'POST',headers:{'Content-Type':'application/json'},body:'{}'})).json();
381-
document.getElementById('claudeMsg').textContent=r.reachable?('connected — model '+r.model+'. Restart agents to apply.'):(r.note||'set, but claude-code-api not reachable');
382-
toast(r.reachable?'Connected to your Claude account':'Saved — start claude-code + run `claude` to log in');loadSettings();
381+
document.getElementById('claudeMsg').textContent=r.note||(r.logged_in?'connected':'needs login');
382+
toast(r.logged_in?'Connected your Claude subscription works ✓':'Run aios claude-login first to log in');loadSettings();
383383
}catch(e){document.getElementById('claudeMsg').textContent='failed: '+e;}
384384
}
385385
async function saveChannels(){

0 commit comments

Comments
 (0)