astris-python 0.1.0__tar.gz → 0.1.2__tar.gz

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.
Files changed (32) hide show
  1. {astris_python-0.1.0 → astris_python-0.1.2}/PKG-INFO +13 -6
  2. {astris_python-0.1.0 → astris_python-0.1.2}/README.md +4 -1
  3. {astris_python-0.1.0 → astris_python-0.1.2}/pyproject.toml +11 -5
  4. {astris_python-0.1.0 → astris_python-0.1.2}/pyproject.toml.orig +11 -5
  5. astris_python-0.1.2/src/astris/.agents/skills/astris/SKILL.md +342 -0
  6. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/__init__.py +3 -0
  7. astris_python-0.1.2/src/astris/auth/installer.py +1281 -0
  8. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/cli.py +97 -31
  9. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/config.py +13 -1
  10. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/database/__init__.py +4 -0
  11. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/inertia/response.py +5 -3
  12. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/installer.py +149 -31
  13. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/kernel.py +67 -2
  14. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/routing/__init__.py +2 -0
  15. astris_python-0.1.2/src/astris/routing/router.py +57 -0
  16. astris_python-0.1.0/src/astris/auth/installer.py +0 -512
  17. astris_python-0.1.0/src/astris/routing/router.py +0 -38
  18. {astris_python-0.1.0 → astris_python-0.1.2}/LICENSE +0 -0
  19. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/assets/favicon.ico +0 -0
  20. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/auth/__init__.py +0 -0
  21. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/auth/session.py +0 -0
  22. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/database/migrations.py +0 -0
  23. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/database/session.py +0 -0
  24. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/http/__init__.py +0 -0
  25. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/http/static.py +0 -0
  26. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/inertia/__init__.py +0 -0
  27. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/inertia/exceptions.py +0 -0
  28. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/inertia/shared.py +0 -0
  29. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/inertia/vite.py +0 -0
  30. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/py.typed +0 -0
  31. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/security/__init__.py +0 -0
  32. {astris_python-0.1.0 → astris_python-0.1.2}/src/astris/security/csrf.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: astris-python
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: The modern full-stack web framework for Python. Everything you need to go from idea to orbit; for developers who want to build and ship at escape velocity.
5
5
  Author: Felix Gomez
6
6
  Author-email: Felix Gomez <felix@felixgomez.com>
@@ -32,20 +32,24 @@ Classifier: Topic :: Software Development :: Libraries
32
32
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
33
33
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
34
34
  Classifier: Typing :: Typed
35
- Requires-Dist: alembic>=1.19.1
35
+ Requires-Dist: alembic>=1.20.0
36
36
  Requires-Dist: fastapi>=0.141.1
37
37
  Requires-Dist: itsdangerous>=2.2.0
38
38
  Requires-Dist: pwdlib[argon2]>=0.3.1
39
- Requires-Dist: pydantic>=2.13.4
39
+ Requires-Dist: pydantic>=2.13.5
40
40
  Requires-Dist: pydantic-settings>=2.15.0
41
- Requires-Dist: sqlmodel>=0.0.39
42
- Requires-Dist: typer>=0.27.1
41
+ Requires-Dist: sqlmodel>=0.0.42
42
+ Requires-Dist: typer>=0.27.2
43
43
  Requires-Dist: uvicorn[standard]>=0.52.4
44
44
  Requires-Python: >=3.11
45
+ Project-URL: Homepage, https://astris.dev
46
+ Project-URL: Documentation, https://astris.dev
47
+ Project-URL: Repository, https://github.com/TheFelixGomez/astris
48
+ Project-URL: Issues, https://github.com/TheFelixGomez/astris/issues
45
49
  Description-Content-Type: text/markdown
46
50
 
47
51
  <p align="center" style="padding: 20px 0 10px 0;">
48
- <a href="https://github.com/TheFelixGomez/astris">
52
+ <a href="https://astris.dev">
49
53
  <img src="https://raw.githubusercontent.com/TheFelixGomez/astris/main/.github/assets/astris-logo-name.png" alt="Astris" width="380">
50
54
  </a>
51
55
  </p>
@@ -56,6 +60,7 @@ Description-Content-Type: text/markdown
56
60
  </p>
57
61
 
58
62
  <p align="center">
63
+ <a href="https://astris.dev"><img src="https://img.shields.io/badge/docs-astris.dev-0ea5e9.svg" alt="Documentation"></a>
59
64
  <a href="https://pypi.org/project/astris-python/"><img src="https://img.shields.io/pypi/v/astris-python.svg" alt="PyPI version"></a>
60
65
  <a href="https://pypi.org/project/astris-python/"><img src="https://img.shields.io/pypi/pyversions/astris-python.svg" alt="Python Versions"></a>
61
66
  <a href="https://github.com/TheFelixGomez/astris/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
@@ -71,6 +76,8 @@ Description-Content-Type: text/markdown
71
76
 
72
77
  By combining **FastAPI**, **Inertia.js**, **Vue 3**, **Tailwind CSS v4**, and **SQLModel**, Astris lets you build rich, dynamic single-page applications without the overhead of maintaining separate API layers or complex state synchronization.
73
78
 
79
+ 📖 **Explore the full documentation, guides, and tutorials at [astris.dev](https://astris.dev).**
80
+
74
81
  ---
75
82
 
76
83
  ## 🌟 Key Features
@@ -1,5 +1,5 @@
1
1
  <p align="center" style="padding: 20px 0 10px 0;">
2
- <a href="https://github.com/TheFelixGomez/astris">
2
+ <a href="https://astris.dev">
3
3
  <img src="https://raw.githubusercontent.com/TheFelixGomez/astris/main/.github/assets/astris-logo-name.png" alt="Astris" width="380">
4
4
  </a>
5
5
  </p>
@@ -10,6 +10,7 @@
10
10
  </p>
11
11
 
12
12
  <p align="center">
13
+ <a href="https://astris.dev"><img src="https://img.shields.io/badge/docs-astris.dev-0ea5e9.svg" alt="Documentation"></a>
13
14
  <a href="https://pypi.org/project/astris-python/"><img src="https://img.shields.io/pypi/v/astris-python.svg" alt="PyPI version"></a>
14
15
  <a href="https://pypi.org/project/astris-python/"><img src="https://img.shields.io/pypi/pyversions/astris-python.svg" alt="Python Versions"></a>
15
16
  <a href="https://github.com/TheFelixGomez/astris/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
@@ -25,6 +26,8 @@
25
26
 
26
27
  By combining **FastAPI**, **Inertia.js**, **Vue 3**, **Tailwind CSS v4**, and **SQLModel**, Astris lets you build rich, dynamic single-page applications without the overhead of maintaining separate API layers or complex state synchronization.
27
28
 
29
+ 📖 **Explore the full documentation, guides, and tutorials at [astris.dev](https://astris.dev).**
30
+
28
31
  ---
29
32
 
30
33
  ## 🌟 Key Features
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "astris-python"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  description = "The modern full-stack web framework for Python. Everything you need to go from idea to orbit; for developers who want to build and ship at escape velocity."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -35,14 +35,14 @@ classifiers = [
35
35
  "Typing :: Typed",
36
36
  ]
37
37
  dependencies = [
38
- "alembic>=1.19.1",
38
+ "alembic>=1.20.0",
39
39
  "fastapi>=0.141.1",
40
40
  "itsdangerous>=2.2.0",
41
41
  "pwdlib[argon2]>=0.3.1",
42
- "pydantic>=2.13.4",
42
+ "pydantic>=2.13.5",
43
43
  "pydantic-settings>=2.15.0",
44
- "sqlmodel>=0.0.39",
45
- "typer>=0.27.1",
44
+ "sqlmodel>=0.0.42",
45
+ "typer>=0.27.2",
46
46
  "uvicorn[standard]>=0.52.4",
47
47
  ]
48
48
 
@@ -50,6 +50,12 @@ dependencies = [
50
50
  name = "Felix Gomez"
51
51
  email = "felix@felixgomez.com"
52
52
 
53
+ [project.urls]
54
+ Homepage = "https://astris.dev"
55
+ Documentation = "https://astris.dev"
56
+ Repository = "https://github.com/TheFelixGomez/astris"
57
+ Issues = "https://github.com/TheFelixGomez/astris/issues"
58
+
53
59
  [project.scripts]
54
60
  astris = "astris.installer:installer_cli"
55
61
  astris-python = "astris.installer:installer_cli"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "astris-python"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  description = "The modern full-stack web framework for Python. Everything you need to go from idea to orbit; for developers who want to build and ship at escape velocity."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -38,17 +38,23 @@ classifiers = [
38
38
  "Typing :: Typed",
39
39
  ]
40
40
  dependencies = [
41
- "alembic>=1.19.1",
41
+ "alembic>=1.20.0",
42
42
  "fastapi>=0.141.1",
43
43
  "itsdangerous>=2.2.0",
44
44
  "pwdlib[argon2]>=0.3.1",
45
- "pydantic>=2.13.4",
45
+ "pydantic>=2.13.5",
46
46
  "pydantic-settings>=2.15.0",
47
- "sqlmodel>=0.0.39",
48
- "typer>=0.27.1",
47
+ "sqlmodel>=0.0.42",
48
+ "typer>=0.27.2",
49
49
  "uvicorn[standard]>=0.52.4",
50
50
  ]
51
51
 
52
+ [project.urls]
53
+ Homepage = "https://astris.dev"
54
+ Documentation = "https://astris.dev"
55
+ Repository = "https://github.com/TheFelixGomez/astris"
56
+ Issues = "https://github.com/TheFelixGomez/astris/issues"
57
+
52
58
  [project.scripts]
53
59
  astris = "astris.installer:installer_cli"
54
60
  astris-python = "astris.installer:installer_cli"
@@ -0,0 +1,342 @@
1
+ ---
2
+ name: astris
3
+ description: Astris best practices and conventions. Use when working with Astris full-stack Python applications, controllers, Inertia.js Vue 3 views, SQLModel databases, Orbit CLI, and security. Keeps Astris code clean and up to date with the latest features and patterns.
4
+ ---
5
+
6
+ # Astris
7
+
8
+ Official Astris skill to write code with best practices, keeping up to date with new versions and features.
9
+
10
+ ## Quick Reference
11
+
12
+ * Application entrypoint: `app = Astris()` in `main.py`; see [Application Kernel](#application-kernel).
13
+ * Configuration: manage via `.env` and `app/core/config.py`; see [Configuration](#configuration-appcoreconfigpy).
14
+ * Controllers: define in `app/modules/<name>/<name>_controller.py` with `Controller`; see [Controllers & Routing](#controllers--routing).
15
+ * Views: return `InertiaResponse(request, "Component/Path", props={...})`; see [Inertia.js Monolith](#inertiajs-monolith-vue-3--tailwind-css-v4).
16
+ * Mutations: return `RedirectResponse(url="/...", status_code=status.HTTP_303_SEE_OTHER)` for POST/PUT/PATCH/DELETE; see [Form Handling & Mutations](#form-handling--mutations).
17
+ * Database sessions: inject `DatabaseSession` into services via `Depends()`; see [Database & Services](#database--services).
18
+ * Models: define in `app/modules/<name>/<name>_model.py` with `SQLModel`; see [SQLModel Models](#sqlmodel-models).
19
+ * Flash messages: use `flash(request, "type", "message")`; see [Flash Messages & Shared Data](#flash-messages--shared-data).
20
+ * Authentication: use `auth_required` / `guest_required` route dependencies and `AuthUser`; see [Authentication & Security](#authentication--security).
21
+ * Orbit CLI: use `uv run orbit <command>`; see [Orbit CLI](#orbit-cli).
22
+
23
+ ## Application Kernel
24
+
25
+ Instantiate `Astris()` in `main.py`:
26
+
27
+ ```python
28
+ from astris import Astris
29
+
30
+ app = Astris()
31
+ ```
32
+
33
+ The kernel automatically:
34
+ 1. Loads settings from `app/core/config.py` and `.env`.
35
+ 2. Registers security middlewares (CORS, PublicStatic, Session, Flash, CSRF).
36
+ 3. Auto-discovers and attaches all controllers in `app/modules/`.
37
+ 4. Binds the Inertia.js view engine to `resources/views/root.html`.
38
+
39
+ ## Configuration (`app/core/config.py`)
40
+
41
+ Astris generates `app/core/config.py` extending the base `Settings` class with automatic `.env` type-casting:
42
+
43
+ ```python
44
+ from astris.config import Settings as BaseAppSettings
45
+
46
+
47
+ class Settings(BaseAppSettings):
48
+ """Extend application settings with custom environment variables."""
49
+
50
+ stripe_api_key: str | None = None
51
+ redis_url: str = "redis://localhost:6379"
52
+
53
+
54
+ settings = Settings()
55
+ ```
56
+
57
+ Access strongly typed settings anywhere in your app:
58
+
59
+ ```python
60
+ from app.core.config import settings
61
+
62
+ api_key = settings.stripe_api_key
63
+ db_url = settings.database_url
64
+ ```
65
+
66
+ ## Controllers & Routing
67
+
68
+ Place controllers inside `app/modules/<module>/<module>_controller.py`. Astris automatically discovers any `Controller` in files matching `*_controller.py`. `Controller` is a direct subclass of FastAPI's `APIRouter`.
69
+
70
+ ```python
71
+ from astris.routing import Controller, Depends, Path, Query
72
+ from astris.http import Request
73
+ from astris.inertia import InertiaResponse
74
+ from app.modules.billing.billing_service import BillingService
75
+
76
+ controller = Controller(prefix="/billing", tags=["Billing"])
77
+
78
+
79
+ @controller.get("/")
80
+ async def index(
81
+ request: Request,
82
+ billing: BillingService = Depends(),
83
+ ) -> InertiaResponse:
84
+ invoices = billing.get_recent_invoices()
85
+ return InertiaResponse(
86
+ request,
87
+ "Billing/Index",
88
+ props={"invoices": [inv.model_dump() for inv in invoices]},
89
+ )
90
+
91
+
92
+ @controller.get("/{invoice_id}")
93
+ async def show(
94
+ request: Request,
95
+ invoice_id: int,
96
+ billing: BillingService = Depends(),
97
+ ) -> InertiaResponse:
98
+ invoice = billing.get_invoice(invoice_id)
99
+ return InertiaResponse(
100
+ request,
101
+ "Billing/Show",
102
+ props={"invoice": invoice.model_dump()},
103
+ )
104
+ ```
105
+
106
+ ## Form Handling & Mutations
107
+
108
+ Inertia requires that mutating requests (`POST`, `PUT`, `PATCH`, `DELETE`) redirect with a `303 See Other` status code.
109
+
110
+ ```python
111
+ from astris.routing import Controller, Depends
112
+ from astris.http import Request, RedirectResponse, status
113
+ from astris.inertia import flash
114
+ from app.modules.billing.billing_service import BillingService
115
+ from app.modules.billing.billing_model import InvoiceCreate
116
+
117
+ controller = Controller(prefix="/billing", tags=["Billing"])
118
+
119
+
120
+ @controller.post("/")
121
+ async def store(
122
+ request: Request,
123
+ dto: InvoiceCreate,
124
+ billing: BillingService = Depends(),
125
+ ) -> RedirectResponse:
126
+ invoice = billing.create_invoice(dto)
127
+ flash(request, "success", f"Invoice #{invoice.id} created successfully!")
128
+ return RedirectResponse(url="/billing", status_code=status.HTTP_303_SEE_OTHER)
129
+
130
+
131
+ @controller.delete("/{invoice_id}")
132
+ async def destroy(
133
+ request: Request,
134
+ invoice_id: int,
135
+ billing: BillingService = Depends(),
136
+ ) -> RedirectResponse:
137
+ billing.delete_invoice(invoice_id)
138
+ flash(request, "success", "Invoice deleted successfully.")
139
+ return RedirectResponse(url="/billing", status_code=status.HTTP_303_SEE_OTHER)
140
+ ```
141
+
142
+ ## Flash Messages & Shared Data
143
+
144
+ * Flash messages set with `flash(request, "success", "...")` are automatically shared with the frontend in `$page.props.flash`.
145
+ * Global props can be shared per-request using `share(request, "key", value)`.
146
+
147
+ ```python
148
+ from astris.inertia import flash, share
149
+
150
+ flash(request, "error", "Invalid payment credentials.")
151
+ share(request, "team_name", "Acme Corp")
152
+ ```
153
+
154
+ ## Database & Services
155
+
156
+ ### 1. Service Layer with Injected Session
157
+ Accept `session: DatabaseSession` in the constructor:
158
+
159
+ ```python
160
+ from astris.database import DatabaseSession, select
161
+ from app.modules.billing.billing_model import Invoice, InvoiceCreate
162
+
163
+
164
+ class BillingService:
165
+ def __init__(self, session: DatabaseSession):
166
+ self.session = session
167
+
168
+ def get_recent_invoices(self) -> list[Invoice]:
169
+ return self.session.exec(select(Invoice).order_by(Invoice.id.desc())).all()
170
+
171
+ def get_invoice(self, invoice_id: int) -> Invoice:
172
+ return self.session.get(Invoice, invoice_id)
173
+
174
+ def create_invoice(self, dto: InvoiceCreate) -> Invoice:
175
+ invoice = Invoice.model_validate(dto)
176
+ self.session.add(invoice)
177
+ self.session.commit()
178
+ self.session.refresh(invoice)
179
+ return invoice
180
+
181
+ def delete_invoice(self, invoice_id: int) -> None:
182
+ invoice = self.get_invoice(invoice_id)
183
+ if invoice:
184
+ self.session.delete(invoice)
185
+ self.session.commit()
186
+ ```
187
+
188
+ ### 2. Standalone Scripts & Background Tasks
189
+ Use `with db.session() as session:` only outside HTTP requests (in seeders, CLI scripts, or background workers):
190
+
191
+ ```python
192
+ from astris.database import db, select
193
+ from app.modules.billing.billing_model import Invoice
194
+
195
+ with db.session() as session:
196
+ invoices = session.exec(select(Invoice)).all()
197
+ ```
198
+
199
+ ## SQLModel Models
200
+
201
+ Define models in `app/modules/<module>/<module>_model.py`. Astris auto-imports models to register table metadata with Alembic:
202
+
203
+ ```python
204
+ from astris.database import Field, SQLModel
205
+
206
+
207
+ class InvoiceBase(SQLModel):
208
+ customer_name: str = Field(index=True)
209
+ amount: float
210
+ status: str = Field(default="pending")
211
+
212
+
213
+ class Invoice(InvoiceBase, table=True):
214
+ id: int | None = Field(default=None, primary_key=True)
215
+
216
+
217
+ class InvoiceCreate(InvoiceBase):
218
+ pass
219
+
220
+
221
+ class InvoicePublic(InvoiceBase):
222
+ id: int
223
+ ```
224
+
225
+ ## Inertia.js Monolith (Vue 3 + Tailwind CSS v4)
226
+
227
+ Place page components in `resources/js/Pages/<Module>/<Page>.vue`. Always use `<script setup lang="ts">`:
228
+
229
+ ```vue
230
+ <script setup lang="ts">
231
+ import { useForm, Head, Link, usePage } from '@inertiajs/vue3'
232
+
233
+ interface Invoice {
234
+ id: number
235
+ customer_name: string
236
+ amount: number
237
+ status: string
238
+ }
239
+
240
+ defineProps<{
241
+ invoices: Invoice[]
242
+ }>()
243
+
244
+ const page = usePage()
245
+
246
+ const form = useForm({
247
+ customer_name: '',
248
+ amount: 0,
249
+ })
250
+
251
+ const submit = () => {
252
+ form.post('/billing', {
253
+ onSuccess: () => form.reset(),
254
+ })
255
+ }
256
+ </script>
257
+
258
+ <template>
259
+ <Head title="Billing" />
260
+ <div class="max-w-4xl mx-auto p-6 space-y-6">
261
+ <!-- Flash Banner -->
262
+ <div
263
+ v-if="page.props.flash?.success"
264
+ class="p-4 rounded-xl bg-emerald-500/10 border border-emerald-500/20 text-emerald-400 text-sm"
265
+ >
266
+ {{ page.props.flash.success }}
267
+ </div>
268
+
269
+ <h1 class="text-2xl font-bold text-slate-900 dark:text-white">Invoices</h1>
270
+
271
+ <!-- Form with Automatic Server Validation Error Bindings -->
272
+ <form @submit.prevent="submit" class="space-y-4">
273
+ <div>
274
+ <label class="block text-sm font-medium">Customer Name</label>
275
+ <input
276
+ v-model="form.customer_name"
277
+ type="text"
278
+ class="w-full px-3 py-2 border rounded-lg"
279
+ :class="{ 'border-rose-500': form.errors.customer_name }"
280
+ />
281
+ <p v-if="form.errors.customer_name" class="text-sm text-rose-500 mt-1">
282
+ {{ form.errors.customer_name }}
283
+ </p>
284
+ </div>
285
+
286
+ <button
287
+ type="submit"
288
+ :disabled="form.processing"
289
+ class="px-4 py-2 bg-sky-500 text-white rounded-lg disabled:opacity-50"
290
+ >
291
+ Create Invoice
292
+ </button>
293
+ </form>
294
+ </div>
295
+ </template>
296
+ ```
297
+
298
+ ## Authentication & Security
299
+
300
+ 1. **Guards on Controllers**:
301
+ ```python
302
+ from astris.routing import Controller
303
+ from astris.auth import auth_required, guest_required, AuthUser
304
+ from astris.http import Request
305
+ from astris.inertia import InertiaResponse
306
+
307
+ controller = Controller(prefix="/dashboard", dependencies=[auth_required])
308
+
309
+ @controller.get("/")
310
+ async def dashboard(request: Request, user: AuthUser) -> InertiaResponse:
311
+ return InertiaResponse(request, "Dashboard", props={"user": user})
312
+ ```
313
+
314
+ 2. **Session Authentication Helpers**:
315
+ * `login_user(request, user_id)`: Log a user in by storing signed session cookie.
316
+ * `logout_user(request)`: Terminate current session.
317
+ * `hash_password(password)` / `verify_password(plain, hashed)`: Secure Argon2id password management.
318
+
319
+ 3. **CSRF Protection**:
320
+ Astris sets the `XSRF-TOKEN` cookie automatically. Inertia and Axios send the `X-XSRF-TOKEN` header on all mutating requests with zero frontend configuration.
321
+
322
+ ## Orbit CLI
323
+
324
+ Always run Orbit commands via `uv run orbit`:
325
+
326
+ * `uv run orbit serve`: Start local development server (Uvicorn backend + Vite frontend HMR concurrently).
327
+ * `uv run orbit serve --prod`: Start production server on `0.0.0.0` with multi-worker processes.
328
+ * `uv run orbit make:module <name>`: Scaffold domain module (`controller`, `service`, `model`).
329
+ * `uv run orbit make:controller <name> [--module <mod>]`: Generate a controller.
330
+ * `uv run orbit make:model <name> [--module <mod>]`: Generate a SQLModel database table.
331
+ * `uv run orbit make:migration "<message>"`: Autogenerate Alembic schema migration from models.
332
+ * `uv run orbit migrate`: Execute pending migrations.
333
+ * `uv run orbit migrate:rollback`: Roll back database migrations.
334
+ * `uv run orbit migrate:status`: Check current migration revisions.
335
+ * `uv run orbit key:generate`: Generate a 32-byte secret key and update `APP_KEY` in `.env`.
336
+
337
+ ## Key Rules
338
+
339
+ * Always configure via `.env` and `app/core/config.py`.
340
+ * Never hardcode secrets in code.
341
+ * Always use `DatabaseSession` with `Depends()` in route handlers and services.
342
+ * Always redirect with `status.HTTP_303_SEE_OTHER` after POST/PUT/PATCH/DELETE mutations.
@@ -1,7 +1,10 @@
1
1
  from astris.config import Settings
2
2
  from astris.kernel import Astris
3
3
 
4
+ __version__ = "0.1.2"
5
+
4
6
  __all__ = [
5
7
  "Astris",
6
8
  "Settings",
9
+ "__version__",
7
10
  ]