systemr-cli 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,303 @@
1
+ """Auth commands — register, login, verify, pay, logout, whoami.
2
+
3
+ Complete onboarding from the terminal:
4
+ systemr register → create account (email + password)
5
+ systemr verify → confirm email with 6-digit code
6
+ systemr pay → open Stripe checkout in browser for credits
7
+ systemr login → authenticate existing account
8
+ systemr logout → clear credentials
9
+ systemr whoami → show current user + balance
10
+
11
+ Payment MUST happen in a browser (credit card / crypto wallet).
12
+ The CLI generates the Stripe checkout URL and opens it automatically.
13
+
14
+ No bare print() — all output via Rich console. Logging via structlog.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import asyncio
20
+ import webbrowser
21
+
22
+ import click
23
+ import httpx
24
+ import structlog
25
+
26
+ from neo.auth import AuthManager
27
+ from neo.config import get_api_url
28
+ from neo.display.theme import (
29
+ AMBER,
30
+ DIM,
31
+ GRAY,
32
+ GREEN,
33
+ GREEN_DIM,
34
+ MUTED,
35
+ RED,
36
+ WHITE,
37
+ console,
38
+ print_error,
39
+ print_info,
40
+ print_success,
41
+ print_warn,
42
+ )
43
+
44
+ logger = structlog.get_logger(module="auth_commands")
45
+
46
+ APP_URL = "https://app.systemr.ai"
47
+
48
+
49
+ @click.command()
50
+ @click.option("--email", prompt="Email", help="Your System R email")
51
+ @click.option("--password", prompt=True, hide_input=True, help="Your password")
52
+ def login(email: str, password: str) -> None:
53
+ """Authenticate with System R."""
54
+ auth = AuthManager()
55
+ try:
56
+ with console.status(f"[{GREEN_DIM}]Connecting...[/]"):
57
+ token = asyncio.run(auth.login(email, password))
58
+ print_success(f"Connected as [{GREEN}]{token.email}[/]")
59
+ except Exception as e:
60
+ print_error(f"Login failed: {e}")
61
+ console.print(f" [{DIM}]No account? Run `systemr register` to create one.[/]")
62
+ console.print(f" [{DIM}]Or sign up at {APP_URL}[/]")
63
+ raise SystemExit(1)
64
+
65
+
66
+ @click.command()
67
+ @click.option("--email", prompt="Email", help="Your email address")
68
+ @click.option("--password", prompt=True, hide_input=True, confirmation_prompt=True, help="Choose a password")
69
+ @click.option("--name", prompt="Name", default="", help="Your name (optional)")
70
+ def register(email: str, password: str, name: str) -> None:
71
+ """Create a new System R account from the terminal.
72
+
73
+ After registration, you'll need to:
74
+ 1. Verify your email (systemr verify)
75
+ 2. Add credits (systemr pay)
76
+ 3. Set up your profile (systemr setup)
77
+ """
78
+ auth = AuthManager()
79
+ console.print()
80
+ try:
81
+ with console.status(f"[{GREEN_DIM}]Creating account...[/]"):
82
+ token = asyncio.run(auth.register(email, password, name))
83
+ print_success(f"Account created! Connected as [{GREEN}]{token.email}[/]")
84
+ console.print()
85
+ console.print(f" [{WHITE}]Next steps:[/]")
86
+ console.print(f" [{GREEN}]1.[/] [{GRAY}]systemr verify — confirm your email (check inbox for 6-digit code)[/]")
87
+ console.print(f" [{GREEN}]2.[/] [{GRAY}]systemr pay — add compute credits (opens browser)[/]")
88
+ console.print(f" [{GREEN}]3.[/] [{GRAY}]systemr setup — create your trading profile[/]")
89
+ console.print(f" [{GREEN}]4.[/] [{GRAY}]systemr chat — start trading[/]")
90
+ console.print()
91
+ except Exception as e:
92
+ error_msg = str(e)
93
+ if "409" in error_msg or "exist" in error_msg.lower() or "already" in error_msg.lower():
94
+ print_error("Email already registered. Run `systemr login` instead.")
95
+ else:
96
+ print_error(f"Registration failed: {e}")
97
+ console.print(f" [{DIM}]Try signing up at {APP_URL}[/]")
98
+ raise SystemExit(1)
99
+
100
+
101
+ @click.command()
102
+ @click.option("--code", prompt="Verification code", help="6-digit code from your email")
103
+ def verify(code: str) -> None:
104
+ """Verify your email with the 6-digit code sent to your inbox."""
105
+ auth = AuthManager()
106
+ token = auth.get_token()
107
+ if token is None:
108
+ print_error("Not connected. Run `systemr login` first.")
109
+ raise SystemExit(1)
110
+
111
+ api_url = get_api_url()
112
+ try:
113
+ resp = httpx.post(
114
+ f"{api_url}/api/auth/verify-email/confirm",
115
+ json={"code": code},
116
+ headers={"Authorization": f"Bearer {token.access_token}"},
117
+ timeout=15.0,
118
+ )
119
+ if resp.status_code in (200, 204):
120
+ print_success("Email verified!")
121
+ console.print(f" [{DIM}]Next: run `systemr pay` to add credits.[/]")
122
+ else:
123
+ data = resp.json() if resp.headers.get("content-type", "").startswith("application/json") else {}
124
+ print_error(f"Verification failed: {data.get('detail', resp.status_code)}")
125
+ except Exception as e:
126
+ print_error(f"Verification failed: {e}")
127
+
128
+
129
+ @click.command()
130
+ def pay() -> None:
131
+ """Add compute credits — card (Stripe) or crypto (send to wallet).
132
+
133
+ Two payment paths:
134
+ 1. Card/stablecoin via Stripe — opens browser
135
+ 2. Crypto (SOL, OSR, USDC, USDT, PYUSD) — send to treasury wallet,
136
+ auto-credited via Helius webhook (~30s after confirmation)
137
+
138
+ Link your wallet first with `systemr link-wallet` for auto-credit.
139
+ """
140
+ auth = AuthManager()
141
+ token = auth.get_token()
142
+ if token is None:
143
+ print_error("Not connected. Run `systemr login` first.")
144
+ raise SystemExit(1)
145
+
146
+ api_url = get_api_url()
147
+ console.print()
148
+ console.print(f" [{WHITE}]Add Compute Credits[/]")
149
+ console.print(f" [{DIM}]Credits power every tool call. Pricing: $0.002–$2.00 per call.[/]")
150
+ console.print()
151
+ console.print(f" [{WHITE}]1.[/] [{GRAY}]Card / stablecoin (Stripe) — opens browser[/]")
152
+ console.print(f" [{WHITE}]2.[/] [{GRAY}]Crypto (SOL, OSR, USDC, USDT, PYUSD) — send to wallet[/]")
153
+ console.print()
154
+
155
+ choice = click.prompt(
156
+ click.style(" Select", fg="white"),
157
+ type=click.Choice(["1", "2"]),
158
+ show_choices=False,
159
+ )
160
+
161
+ if choice == "1":
162
+ # Stripe path — open billing page in browser
163
+ billing_url = f"{APP_URL}/billing"
164
+ print_success("Opening billing page...")
165
+ console.print(f" [{DIM}]If browser doesn't open:[/] [{GREEN_DIM}]{billing_url}[/]")
166
+ webbrowser.open(billing_url)
167
+ console.print()
168
+ console.print(f" [{DIM}]After payment, run `systemr balance` to verify.[/]")
169
+ console.print()
170
+
171
+ elif choice == "2":
172
+ # Crypto path — show treasury wallet + accepted tokens
173
+ # Try to fetch treasury info from backend
174
+ treasury_wallet = "9Nc6u9ft3uAr6DSaqHijFjQS53hZPDRL2LoVYvH4vK5H"
175
+ try:
176
+ resp = httpx.get(f"{api_url}/v1/crypto/treasury", timeout=5.0)
177
+ if resp.status_code == 200:
178
+ data = resp.json()
179
+ treasury_wallet = data.get("wallet", treasury_wallet)
180
+ except Exception:
181
+ pass
182
+
183
+ console.print()
184
+ console.print(f" [{WHITE}]Send crypto to our Solana treasury wallet:[/]")
185
+ console.print()
186
+ console.print(f" [{GREEN}]{treasury_wallet}[/]")
187
+ console.print(f" [{DIM}]Network: Solana Mainnet[/]")
188
+ console.print()
189
+ console.print(f" [{WHITE}]Accepted tokens:[/]")
190
+ console.print(f" [{WHITE}]USDC[/] [{DIM}]1:1 credit[/]")
191
+ console.print(f" [{WHITE}]USDT[/] [{DIM}]1:1 credit[/]")
192
+ console.print(f" [{WHITE}]PYUSD[/] [{DIM}]1:1 credit[/]")
193
+ console.print(f" [{WHITE}]SOL[/] [{DIM}]live market price[/]")
194
+ console.print(f" [{WHITE}]OSR[/] [{DIM}]$0.005/token ($0.004 presale)[/]")
195
+ console.print()
196
+ console.print(f" [{GREEN}]Credits appear automatically after confirmation (~30s).[/]")
197
+ console.print()
198
+ console.print(f" [{AMBER}]Important:[/] [{GRAY}]Link your wallet first so we know who to credit:[/]")
199
+ console.print(f" [{WHITE}]systemr link-wallet <your-solana-pubkey>[/]")
200
+ console.print()
201
+ console.print(f" [{DIM}]Check balance anytime: systemr balance[/]")
202
+ console.print()
203
+
204
+
205
+ @click.command("link-wallet")
206
+ @click.argument("wallet_address")
207
+ def link_wallet(wallet_address: str) -> None:
208
+ """Link your Solana wallet for automatic crypto credit.
209
+
210
+ After linking, any SOL/OSR/USDC/USDT/PYUSD sent from this wallet
211
+ to the System R treasury will be automatically credited to your account
212
+ via Helius webhook (~30s after confirmation).
213
+ """
214
+ auth = AuthManager()
215
+ token = auth.get_token()
216
+ if token is None:
217
+ print_error("Not connected. Run `systemr login` first.")
218
+ raise SystemExit(1)
219
+
220
+ # Basic validation — Solana pubkeys are 32-44 base58 chars
221
+ if len(wallet_address) < 32 or len(wallet_address) > 44:
222
+ print_error("Invalid Solana wallet address. Must be 32-44 characters (base58).")
223
+ raise SystemExit(1)
224
+
225
+ api_url = get_api_url()
226
+ try:
227
+ with console.status(f"[{GREEN_DIM}]Linking wallet...[/]"):
228
+ resp = httpx.post(
229
+ f"{api_url}/v1/agents/link-wallet",
230
+ json={"wallet_address": wallet_address},
231
+ headers={"Authorization": f"Bearer {token.access_token}"},
232
+ timeout=15.0,
233
+ )
234
+ resp.raise_for_status()
235
+
236
+ print_success(f"Wallet linked: [{GREEN}]{wallet_address[:8]}...{wallet_address[-4:]}[/]")
237
+ console.print()
238
+ console.print(f" [{DIM}]Now send crypto to the treasury wallet and credits[/]")
239
+ console.print(f" [{DIM}]will appear automatically. Run `systemr pay` to see the address.[/]")
240
+ console.print()
241
+ except Exception as e:
242
+ print_error(f"Failed to link wallet: {e}")
243
+ console.print(f" [{DIM}]You can also link at {APP_URL}/settings[/]")
244
+
245
+
246
+ @click.command()
247
+ def balance() -> None:
248
+ """Check your compute credit balance."""
249
+ auth = AuthManager()
250
+ token = auth.get_token()
251
+ if token is None:
252
+ print_error("Not connected. Run `systemr login` first.")
253
+ raise SystemExit(1)
254
+
255
+ api_url = get_api_url()
256
+ try:
257
+ resp = httpx.get(
258
+ f"{api_url}/api/portal/balance",
259
+ headers={"Authorization": f"Bearer {token.access_token}"},
260
+ timeout=10.0,
261
+ )
262
+ resp.raise_for_status()
263
+ data = resp.json()
264
+ bal = data.get("balance", "0.00")
265
+ console.print(f" [{GREEN}]●[/] [{WHITE}]${bal}[/] [{GRAY}]credits[/]")
266
+ if float(bal) < 1.0:
267
+ print_warn(f"Low balance. Run `systemr pay` to add credits.")
268
+ except Exception as e:
269
+ print_error(f"Could not fetch balance: {e}")
270
+
271
+
272
+ @click.command()
273
+ def logout() -> None:
274
+ """Clear stored credentials."""
275
+ auth = AuthManager()
276
+ auth.clear()
277
+ print_success("Disconnected")
278
+
279
+
280
+ @click.command()
281
+ def whoami() -> None:
282
+ """Show current authenticated user and balance."""
283
+ auth = AuthManager()
284
+ token = auth.get_token()
285
+ if token is None:
286
+ print_error("Not connected. Run `systemr login` or `systemr register`.")
287
+ raise SystemExit(1)
288
+ console.print(f" [{GREEN}]●[/] [{GREEN}]{token.email}[/]")
289
+
290
+ # Also show balance
291
+ api_url = get_api_url()
292
+ try:
293
+ resp = httpx.get(
294
+ f"{api_url}/api/portal/balance",
295
+ headers={"Authorization": f"Bearer {token.access_token}"},
296
+ timeout=5.0,
297
+ )
298
+ if resp.status_code == 200:
299
+ data = resp.json()
300
+ bal = data.get("balance", "?")
301
+ console.print(f" [{DIM}]${bal} credits[/]")
302
+ except Exception:
303
+ pass