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.
- neo/__init__.py +3 -0
- neo/__main__.py +5 -0
- neo/auth.py +192 -0
- neo/cli.py +205 -0
- neo/client.py +64 -0
- neo/commands/__init__.py +0 -0
- neo/commands/auth_commands.py +303 -0
- neo/commands/chat_commands.py +937 -0
- neo/commands/cron_commands.py +179 -0
- neo/commands/doctor_command.py +178 -0
- neo/commands/eval_commands.py +73 -0
- neo/commands/journal_commands.py +197 -0
- neo/commands/plan_commands.py +77 -0
- neo/commands/risk_commands.py +68 -0
- neo/commands/scan_commands.py +62 -0
- neo/commands/size_commands.py +60 -0
- neo/config.py +70 -0
- neo/confirmation.py +311 -0
- neo/credits.py +98 -0
- neo/cron.py +365 -0
- neo/display/__init__.py +0 -0
- neo/display/chat_renderer.py +127 -0
- neo/display/formatters.py +112 -0
- neo/display/tables.py +53 -0
- neo/display/theme.py +154 -0
- neo/hooks.py +170 -0
- neo/logging.py +56 -0
- neo/model_failover.py +193 -0
- neo/orchestrator.py +288 -0
- neo/profile.py +505 -0
- neo/store.py +405 -0
- neo/streaming.py +315 -0
- neo/types.py +109 -0
- systemr_cli-1.0.0.dist-info/METADATA +191 -0
- systemr_cli-1.0.0.dist-info/RECORD +37 -0
- systemr_cli-1.0.0.dist-info/WHEEL +4 -0
- systemr_cli-1.0.0.dist-info/entry_points.txt +3 -0
|
@@ -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
|