raillog-cli 0.1.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.
raillog/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
raillog/cli.py ADDED
@@ -0,0 +1,54 @@
1
+ """
2
+ raillog — main CLI entry point.
3
+
4
+ Registers all command groups. This is the `raillog` command installed by pip.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import click
10
+ from rich.console import Console
11
+
12
+ from raillog import __version__
13
+ from raillog.cmd_auth import auth_group
14
+ from raillog.cmd_projects import project_group
15
+ from raillog.cmd_context import context_cmd
16
+ from raillog.cmd_stations import station_group
17
+ from raillog.cmd_tracks import track_group
18
+ from raillog.cmd_bauzuege import bauzug_group
19
+ from raillog.cmd_waggons import waggon_group
20
+ from raillog.cmd_travels import travel_group
21
+ from raillog.cmd_restrictions import restriction_group
22
+ from raillog.cmd_queries import query_group
23
+ from raillog.cmd_install import install_skill
24
+
25
+ console = Console()
26
+
27
+
28
+ @click.group()
29
+ @click.version_option(__version__, prog_name="raillog")
30
+ def cli() -> None:
31
+ """
32
+ Raillog V2.0 — railway construction logistics CLI.
33
+
34
+ \b
35
+ Quick start:
36
+ raillog auth login
37
+ raillog project list
38
+ raillog project select <n>
39
+ raillog context
40
+ """
41
+
42
+
43
+ # ── Register all command groups ───────────────────────────────────────────────
44
+ cli.add_command(auth_group)
45
+ cli.add_command(project_group)
46
+ cli.add_command(context_cmd)
47
+ cli.add_command(station_group)
48
+ cli.add_command(track_group)
49
+ cli.add_command(bauzug_group)
50
+ cli.add_command(waggon_group)
51
+ cli.add_command(travel_group)
52
+ cli.add_command(restriction_group)
53
+ cli.add_command(query_group)
54
+ cli.add_command(install_skill)
raillog/client.py ADDED
@@ -0,0 +1,36 @@
1
+ """
2
+ Supabase client factory.
3
+
4
+ Builds a supabase-py client and injects the stored JWT so every
5
+ request is authenticated as the logged-in user (respecting RLS).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from supabase import Client, create_client
11
+
12
+ SUPABASE_URL = "https://ouilcwrgxsirpvkzsdkq.supabase.co"
13
+ SUPABASE_ANON_KEY = (
14
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
15
+ ".eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im91aWxjd3JneHNpcnB2a3pzZGtxIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjU4MzMzNzYsImV4cCI6MjA4MTQwOTM3Nn0"
16
+ ".WRR4puUXDvS46w7rsc1sHT6cz8_A1pL-1yzK47v7aqI"
17
+ )
18
+
19
+
20
+ def get_client(access_token: str | None = None) -> Client:
21
+ """
22
+ Return a Supabase client.
23
+ If access_token is provided, set it on the client so RLS applies.
24
+ """
25
+ sb = create_client(SUPABASE_URL, SUPABASE_ANON_KEY)
26
+ if access_token:
27
+ # Inject user JWT — required for RLS to work correctly
28
+ sb.postgrest.auth(access_token)
29
+ return sb
30
+
31
+
32
+ def authed_client() -> Client:
33
+ """Shortcut: load session and return authenticated client."""
34
+ from raillog import session as sess
35
+ s = sess.require_auth()
36
+ return get_client(s["access_token"])
raillog/cmd_auth.py ADDED
@@ -0,0 +1,92 @@
1
+ """raillog auth {login,status,logout}"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+
7
+ import click
8
+ from rich.console import Console
9
+
10
+ from raillog import session as sess
11
+ from raillog.client import get_client, SUPABASE_URL
12
+
13
+ console = Console()
14
+
15
+
16
+ @click.group("auth")
17
+ def auth_group() -> None:
18
+ """Authenticate with Raillog (Supabase)."""
19
+
20
+
21
+ @auth_group.command("login")
22
+ def auth_login() -> None:
23
+ """Log in with email and password."""
24
+ console.print("[bold]Raillog Login[/bold]")
25
+ email = click.prompt("Email")
26
+ password = click.prompt("Password", hide_input=True)
27
+
28
+ sb = get_client()
29
+ try:
30
+ result = sb.auth.sign_in_with_password({"email": email, "password": password})
31
+ except Exception as e:
32
+ raise SystemExit(f"Login failed: {e}") from e
33
+
34
+ if not result.session:
35
+ raise SystemExit("Login failed: no session returned.")
36
+
37
+ s = result.session
38
+ user = result.user
39
+
40
+ sess.save({
41
+ "access_token": s.access_token,
42
+ "refresh_token": s.refresh_token,
43
+ "expires_at": s.expires_at or int(time.time()) + 3600,
44
+ "user_id": str(user.id) if user else "",
45
+ "user_email": user.email or email,
46
+ "project_id": None,
47
+ "project_name": None,
48
+ "access_level": None,
49
+ })
50
+
51
+ console.print(f"[green]Logged in as[/green] [bold]{user.email}[/bold]")
52
+ console.print("Run [cyan]raillog project list[/cyan] to select a project.")
53
+
54
+
55
+ @auth_group.command("status")
56
+ def auth_status() -> None:
57
+ """Show current authentication status."""
58
+ s = sess.load()
59
+ if not s:
60
+ console.print("[red]Not authenticated.[/red] Run: raillog auth login")
61
+ return
62
+
63
+ if not sess.is_valid(s):
64
+ console.print("[yellow]Session expired.[/yellow] Run: raillog auth login")
65
+ return
66
+
67
+ remaining = s.get("expires_at", 0) - int(time.time())
68
+ hours = remaining // 3600
69
+ console.print(f"[green]Authenticated[/green] as [bold]{s.get('user_email')}[/bold]")
70
+ console.print(f"Session expires in ~{hours}h")
71
+ if s.get("project_id"):
72
+ console.print(f"Active project: [bold]{s.get('project_name')}[/bold] ({s.get('project_id')})")
73
+ else:
74
+ console.print("[dim]No project selected.[/dim]")
75
+
76
+
77
+ @auth_group.command("refresh")
78
+ def auth_refresh() -> None:
79
+ """Silently renew the session using the stored refresh token."""
80
+ if sess._try_refresh():
81
+ s = sess.load()
82
+ remaining = ((s or {}).get("expires_at", 0) - int(time.time())) // 3600
83
+ console.print(f"[green]Session refreshed.[/green] Expires in ~{remaining}h")
84
+ else:
85
+ raise SystemExit("Refresh failed — run: raillog auth login")
86
+
87
+
88
+ @auth_group.command("logout")
89
+ def auth_logout() -> None:
90
+ """Clear the local session."""
91
+ sess.clear()
92
+ console.print("[green]Logged out.[/green]")