Skip to content

Commit 877909e

Browse files
devonakelleyclaude
andauthored
fix: rewrite kalibr signup to call /signup-and-provision (live endpoint) (#150)
Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
1 parent c5c6756 commit 877909e

1 file changed

Lines changed: 56 additions & 80 deletions

File tree

kalibr/cli/signup_cmd.py

Lines changed: 56 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
"""kalibr signup - Create a Kalibr account from the CLI."""
1+
"""kalibr signup - Create a Kalibr account from the CLI (agent-driven onboarding)."""
22

33
import os
4-
import time
54

65
import requests
76
import typer
@@ -12,19 +11,19 @@
1211

1312

1413
def signup(
15-
email: str = typer.Argument(..., help="Real email address for the account owner — a verification email will be sent here"),
16-
org_name: str = typer.Option(None, "--org", help="Organization name (default: email prefix)"),
14+
email: str = typer.Argument(..., help="Real email address for the account owner — credentials are returned immediately and a claim link is sent to this address"),
15+
agent_name: str = typer.Option("kalibr-agent", "--agent-name", help="Name for this agent (for identification in the dashboard)"),
16+
org_name: str = typer.Option(None, "--org", help="(unused, kept for backward compat)"),
1717
) -> None:
18-
"""Create a Kalibr account and get API credentials. Recommended for agent-driven onboarding. Agent runs this with the real user email, human clicks one verification link."""
18+
"""Create a Kalibr account and get API credentials immediately. Recommended for agent-driven onboarding."""
1919

2020
console.print(f"[bold]Creating Kalibr account for {email}...[/bold]")
2121

22-
# Start signup
2322
try:
2423
resp = requests.post(
25-
f"{BACKEND_URL}/api/cli-auth/signup",
26-
json={"email": email, "org_name": org_name},
27-
timeout=15,
24+
f"{BACKEND_URL}/api/cli-auth/signup-and-provision",
25+
json={"human_email": email, "agent_name": agent_name},
26+
timeout=20,
2827
)
2928

3029
if resp.status_code == 409:
@@ -36,79 +35,56 @@ def signup(
3635
console.print("[red]Too many signup attempts. Try again in an hour.[/red]")
3736
raise typer.Exit(1)
3837

39-
resp.raise_for_status()
38+
if resp.status_code != 200:
39+
console.print(f"[red]Signup failed (HTTP {resp.status_code}): {resp.text[:200]}[/red]")
40+
raise typer.Exit(1)
41+
4042
data = resp.json()
43+
4144
except requests.RequestException as e:
4245
console.print(f"[red]Signup failed: {e}[/red]")
4346
raise typer.Exit(1)
4447

45-
signup_id = data["signup_id"]
46-
console.print(f"\n[green]✓ Verification email sent to {email}[/green]")
47-
console.print("[cyan]Click the link in your email to activate your account...[/cyan]\n")
48-
49-
# Poll for verification
50-
poll_url = f"{BACKEND_URL}/api/cli-auth/signup/{signup_id}/status"
51-
max_wait = 300 # 5 minutes
52-
start = time.time()
53-
54-
with console.status("[bold cyan]Waiting for email verification...") as spinner:
55-
while time.time() - start < max_wait:
56-
try:
57-
resp = requests.get(poll_url, timeout=10)
58-
if resp.status_code == 200:
59-
result = resp.json()
60-
61-
if result["status"] == "verified":
62-
api_key = result["api_key"]
63-
tenant_id = result["tenant_id"]
64-
65-
# Write to .env
66-
env_path = os.path.join(os.getcwd(), ".env")
67-
lines = []
68-
if os.path.exists(env_path):
69-
with open(env_path) as f:
70-
lines = f.readlines()
71-
72-
key_map = {
73-
"KALIBR_API_KEY": api_key,
74-
"KALIBR_TENANT_ID": tenant_id,
75-
}
76-
updated = set()
77-
new_lines = []
78-
for line in lines:
79-
replaced = False
80-
for k, v in key_map.items():
81-
if line.startswith(f"{k}="):
82-
new_lines.append(f"{k}={v}\n")
83-
updated.add(k)
84-
replaced = True
85-
break
86-
if not replaced:
87-
new_lines.append(line)
88-
89-
for k, v in key_map.items():
90-
if k not in updated:
91-
new_lines.append(f"{k}={v}\n")
92-
93-
with open(env_path, "w") as f:
94-
f.writelines(new_lines)
95-
96-
console.print("\n[bold green]✓ Account created![/bold green]")
97-
console.print(f" API Key: {api_key[:12]}...")
98-
console.print(f" Tenant ID: {tenant_id}")
99-
console.print(f" Saved to: {env_path}")
100-
console.print("\n[bold]Next: run 'kalibr init' to instrument your code[/bold]")
101-
return
102-
103-
elif result["status"] == "expired":
104-
console.print("\n[red]Signup expired. Please try again.[/red]")
105-
raise typer.Exit(1)
106-
107-
except requests.RequestException:
108-
pass # Retry on network errors
109-
110-
time.sleep(5)
111-
112-
console.print("\n[red]Timed out waiting for email verification (5 minutes).[/red]")
113-
console.print("[yellow]Check your inbox and spam folder, then run 'kalibr signup' again.[/yellow]")
114-
raise typer.Exit(1)
48+
api_key = data["api_key"]
49+
tenant_id = data["tenant_id"]
50+
claim_url = data.get("claim_url")
51+
52+
# Write to .env
53+
env_path = os.path.join(os.getcwd(), ".env")
54+
lines = []
55+
if os.path.exists(env_path):
56+
with open(env_path) as f:
57+
lines = f.readlines()
58+
59+
key_map = {
60+
"KALIBR_API_KEY": api_key,
61+
"KALIBR_TENANT_ID": tenant_id,
62+
}
63+
updated = set()
64+
new_lines = []
65+
for line in lines:
66+
replaced = False
67+
for k, v in key_map.items():
68+
if line.startswith(f"{k}="):
69+
new_lines.append(f"{k}={v}\n")
70+
updated.add(k)
71+
replaced = True
72+
break
73+
if not replaced:
74+
new_lines.append(line)
75+
76+
for k, v in key_map.items():
77+
if k not in updated:
78+
new_lines.append(f"{k}={v}\n")
79+
80+
with open(env_path, "w") as f:
81+
f.writelines(new_lines)
82+
83+
console.print("\n[bold green]✓ Account created![/bold green]")
84+
console.print(f" API Key: {api_key[:12]}...")
85+
console.print(f" Tenant ID: {tenant_id}")
86+
console.print(f" Saved to: {env_path}")
87+
if claim_url:
88+
console.print(f"\n[cyan]Dashboard access:[/cyan] {claim_url}")
89+
console.print("[dim]Share this link with the account owner to claim the dashboard.[/dim]")
90+
console.print("\n[bold]Next: run kalibr verify to confirm routing is live[/bold]")

0 commit comments

Comments
 (0)