stkit 17.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.
Files changed (128) hide show
  1. stk/__init__.py +19 -0
  2. stk/__main__.py +6 -0
  3. stk/agent_login.py +78 -0
  4. stk/app.py +323 -0
  5. stk/checks.py +634 -0
  6. stk/cli/__init__.py +49 -0
  7. stk/cli/agent.py +267 -0
  8. stk/cli/base.py +79 -0
  9. stk/cli/database.py +199 -0
  10. stk/cli/develop.py +63 -0
  11. stk/cli/doctor.py +262 -0
  12. stk/cli/main.py +101 -0
  13. stk/cli/reports.py +296 -0
  14. stk/cli/smoke.py +399 -0
  15. stk/cli/users.py +259 -0
  16. stk/cli/watch.py +68 -0
  17. stk/extensions.py +29 -0
  18. stk/migrations/__init__.py +130 -0
  19. stk/migrations/env.py +58 -0
  20. stk/migrations/script.py.mako +17 -0
  21. stk/migrations/versions/stk_0001_initial_schema.py +213 -0
  22. stk/portal/views.py +34 -0
  23. stk/public/__init__.py +0 -0
  24. stk/public/models.py +0 -0
  25. stk/public/views.py +344 -0
  26. stk/scaffold/__init__.py +1 -0
  27. stk/scaffold/app/CLAUDE.md +65 -0
  28. stk/scaffold/app/Dockerfile +41 -0
  29. stk/scaffold/app/README.md +16 -0
  30. stk/scaffold/app/__app__/__init__.py +1 -0
  31. stk/scaffold/app/__app__/app.py +8 -0
  32. stk/scaffold/app/__app__/settings.py +7 -0
  33. stk/scaffold/app/claude/skills/stk-api/SKILL.md +52 -0
  34. stk/scaffold/app/claude/skills/stk-blueprint/SKILL.md +48 -0
  35. stk/scaffold/app/claude/skills/stk-deploy/SKILL.md +61 -0
  36. stk/scaffold/app/claude/skills/stk-lessons/SKILL.md +58 -0
  37. stk/scaffold/app/claude/skills/stk-migrate/SKILL.md +96 -0
  38. stk/scaffold/app/docker-compose.yml +88 -0
  39. stk/scaffold/app/dockerignore +13 -0
  40. stk/scaffold/app/env-sample +78 -0
  41. stk/scaffold/app/gitignore +8 -0
  42. stk/scaffold/app/nginx/nginx.conf +46 -0
  43. stk/scaffold/app/nginx/stk.conf +36 -0
  44. stk/scaffold/app/postgres/init-db.sh +8 -0
  45. stk/scaffold/app/pyproject.toml +23 -0
  46. stk/scaffold/app/tests/__init__.py +0 -0
  47. stk/scaffold/app/tests/test_app.py +23 -0
  48. stk/scaffold/generator.py +209 -0
  49. stk/scaffold/templates.py +437 -0
  50. stk/security.py +145 -0
  51. stk/settings.py +147 -0
  52. stk/shell.py +284 -0
  53. stk/static/VERSIONS.txt +4 -0
  54. stk/static/css/app.css +229 -0
  55. stk/static/css/layout.css +330 -0
  56. stk/static/css/vuetify.min.css +6 -0
  57. stk/static/icons/fonts/tabler-icons.woff2 +0 -0
  58. stk/static/icons/tabler-icons.min.css +4 -0
  59. stk/static/img/auth-bg.webp +0 -0
  60. stk/static/img/favicon.ico +0 -0
  61. stk/static/img/stk.svg +1664 -0
  62. stk/static/js/axios.min.js +5 -0
  63. stk/static/js/components/NotificationDropdown.js +156 -0
  64. stk/static/js/components/ThemeSwitcher.js +68 -0
  65. stk/static/js/components/TransitionExpand.js +64 -0
  66. stk/static/js/components/VerticalNavGroup.js +119 -0
  67. stk/static/js/components/VerticalNavLink.js +36 -0
  68. stk/static/js/components/VerticalNavSectionTitle.js +21 -0
  69. stk/static/js/components/index.js +47 -0
  70. stk/static/js/config.js +150 -0
  71. stk/static/js/vue.min.js +13 -0
  72. stk/static/js/vuetify.min.js +3148 -0
  73. stk/static/robots.txt +1 -0
  74. stk/tasks/__init__.py +78 -0
  75. stk/templates/401.html +14 -0
  76. stk/templates/404.html +12 -0
  77. stk/templates/500.html +13 -0
  78. stk/templates/_brand.html +1 -0
  79. stk/templates/auth_layout.html +26 -0
  80. stk/templates/cms/activities.html +135 -0
  81. stk/templates/cms/roles.html +215 -0
  82. stk/templates/cms/users.html +312 -0
  83. stk/templates/dashboard.html +132 -0
  84. stk/templates/index.html +52 -0
  85. stk/templates/layout.html +328 -0
  86. stk/templates/login-layout.html +54 -0
  87. stk/templates/security/_macros.html +39 -0
  88. stk/templates/security/_messages.html +12 -0
  89. stk/templates/security/change_password.html +100 -0
  90. stk/templates/security/confirm_email.html +44 -0
  91. stk/templates/security/email/change_notice.html +4 -0
  92. stk/templates/security/email/change_notice.txt +5 -0
  93. stk/templates/security/email/confirmation_instructions.html +3 -0
  94. stk/templates/security/email/confirmation_instructions.txt +3 -0
  95. stk/templates/security/email/login_instructions.html +5 -0
  96. stk/templates/security/email/login_instructions.txt +5 -0
  97. stk/templates/security/email/reset_instructions.html +1 -0
  98. stk/templates/security/email/reset_instructions.txt +3 -0
  99. stk/templates/security/email/reset_notice.html +1 -0
  100. stk/templates/security/email/reset_notice.txt +1 -0
  101. stk/templates/security/email/welcome.html +7 -0
  102. stk/templates/security/email/welcome.txt +7 -0
  103. stk/templates/security/forgot_password.html +63 -0
  104. stk/templates/security/login_user.html +206 -0
  105. stk/templates/security/mf_recovery.html +67 -0
  106. stk/templates/security/mf_recovery_codes.html +119 -0
  107. stk/templates/security/register_user.html +263 -0
  108. stk/templates/security/reset_password.html +53 -0
  109. stk/templates/security/send_confirmation.html +48 -0
  110. stk/templates/security/send_login.html +39 -0
  111. stk/templates/security/two_factor_select.html +78 -0
  112. stk/templates/security/two_factor_setup.html +166 -0
  113. stk/templates/security/two_factor_verify_code.html +71 -0
  114. stk/templates/security/wan_register.html +179 -0
  115. stk/templates/security/wan_signin.html +123 -0
  116. stk/templates/security/wan_verify.html +109 -0
  117. stk/user/__init__.py +0 -0
  118. stk/user/forms.py +10 -0
  119. stk/user/models.py +308 -0
  120. stk/user/views.py +339 -0
  121. stk/utils/email.py +32 -0
  122. stk/utils/ratelimit.py +67 -0
  123. stk/websocket.py +133 -0
  124. stkit-17.0.0.dist-info/METADATA +135 -0
  125. stkit-17.0.0.dist-info/RECORD +128 -0
  126. stkit-17.0.0.dist-info/WHEEL +4 -0
  127. stkit-17.0.0.dist-info/entry_points.txt +2 -0
  128. stkit-17.0.0.dist-info/licenses/LICENSE +21 -0
stk/utils/ratelimit.py ADDED
@@ -0,0 +1,67 @@
1
+ """Atomic rate limits shared by all workers through the application database."""
2
+
3
+ import hashlib
4
+ import time
5
+
6
+ from quart import abort, request
7
+ from sqlalchemy import delete, update
8
+ from sqlalchemy.exc import IntegrityError
9
+
10
+ import stk.extensions as ext
11
+ from stk.user.models import RateLimitWindow
12
+
13
+
14
+ async def consume_limit(factory, key, limit, seconds):
15
+ now = int(time.time())
16
+ key = f"{hashlib.sha256(key.encode()).hexdigest()}:{now // seconds}"
17
+ async with factory() as db:
18
+ await db.execute(
19
+ delete(RateLimitWindow).where(RateLimitWindow.expires_at <= now)
20
+ )
21
+ try:
22
+ async with db.begin_nested():
23
+ db.add(
24
+ RateLimitWindow(
25
+ key=key, count=0, expires_at=(now // seconds + 1) * seconds
26
+ )
27
+ )
28
+ await db.flush()
29
+ except IntegrityError:
30
+ pass
31
+ result = await db.execute(
32
+ update(RateLimitWindow)
33
+ .where(RateLimitWindow.key == key, RateLimitWindow.count < limit)
34
+ .values(count=RateLimitWindow.count + 1)
35
+ )
36
+ await db.commit()
37
+ return result.rowcount == 1
38
+
39
+
40
+ def register_rate_limits(app):
41
+ @app.before_request
42
+ async def protect_authentication():
43
+ if not app.config.get("QUART_RATE_LIMITER_ENABLED", True):
44
+ return
45
+ oauth_endpoints = {
46
+ "public.google_login",
47
+ "public.google_callback",
48
+ "public.github_login",
49
+ "public.github_callback",
50
+ }
51
+ auth = request.blueprint == "security" or request.endpoint in oauth_endpoints
52
+ if request.endpoint == "security.logout":
53
+ return
54
+ if not auth:
55
+ return
56
+ ip = request.remote_addr or "unknown"
57
+ limit = app.config.get("STK_AUTH_REQUEST_LIMIT", 120)
58
+ if not await consume_limit(ext.async_session_factory, "auth:" + ip, limit, 60):
59
+ abort(429)
60
+ if request.method == "POST" or request.endpoint in oauth_endpoints:
61
+ if not await consume_limit(
62
+ ext.async_session_factory,
63
+ "attempt:" + ip,
64
+ app.config.get("STK_AUTH_ATTEMPT_LIMIT", 10),
65
+ 60,
66
+ ):
67
+ abort(429)
stk/websocket.py ADDED
@@ -0,0 +1,133 @@
1
+ """Bounded, same-origin WebSockets with periodic session validation."""
2
+
3
+ import asyncio
4
+ import json
5
+ import time
6
+
7
+ from quart import Blueprint, current_app, g, session, websocket
8
+ from quart_security import SecurityState, current_user
9
+ from sqlalchemy import select
10
+
11
+ import stk.extensions as ext
12
+ from stk.user.models import User
13
+
14
+ ws_bp = Blueprint("ws", __name__)
15
+ _clients: dict[str, set[asyncio.Queue]] = {}
16
+
17
+
18
+ def get_connected_users() -> list[str]:
19
+ return list(_clients)
20
+
21
+
22
+ async def broadcast(message: dict, user_id: str):
23
+ """Send only to the explicitly selected user."""
24
+ payload = json.dumps(message)
25
+ for queue in _clients.get(str(user_id), set()):
26
+ try:
27
+ queue.put_nowait(payload)
28
+ except asyncio.QueueFull:
29
+ # Disconnect slow consumers instead of retaining an unbounded backlog.
30
+ while not queue.empty():
31
+ queue.get_nowait()
32
+ queue.put_nowait(None)
33
+
34
+
35
+ @ws_bp.websocket("/ws")
36
+ async def ws_endpoint():
37
+ db = g.pop("db_session", None)
38
+ if db is not None:
39
+ await db.close()
40
+ allowed = current_app.config.get("STK_WS_ALLOWED_ORIGINS") or [
41
+ current_app.config.get("STK_PUBLIC_URL")
42
+ or websocket.host_url.replace("ws://", "http://", 1)
43
+ .replace("wss://", "https://", 1)
44
+ .rstrip("/")
45
+ ]
46
+ if websocket.headers.get("Origin") not in allowed:
47
+ await websocket.close(4003, "Invalid origin")
48
+ return
49
+ if not current_user.is_authenticated:
50
+ await websocket.close(4001, "Unauthorized")
51
+ return
52
+ user_id, uniquifier, token = (
53
+ str(current_user.id),
54
+ current_user.get_id(),
55
+ session.get("_id"),
56
+ )
57
+ if len(_clients.get(user_id, ())) >= current_app.config.get(
58
+ "STK_WS_CONNECTIONS_PER_USER", 5
59
+ ) or sum(map(len, _clients.values())) >= current_app.config.get(
60
+ "STK_WS_MAX_CONNECTIONS", 1000
61
+ ):
62
+ await websocket.close(4008, "Connection limit")
63
+ return
64
+ queue = asyncio.Queue(maxsize=current_app.config.get("STK_WS_QUEUE_SIZE", 32))
65
+ _clients.setdefault(user_id, set()).add(queue)
66
+
67
+ async def check_authorization():
68
+ async with ext.async_session_factory() as db:
69
+ state = await db.scalar(
70
+ select(SecurityState.payload).where(
71
+ SecurityState.token == token,
72
+ SecurityState.expires_at > int(time.time()),
73
+ )
74
+ )
75
+ user = await db.scalar(
76
+ select(User.id).where(
77
+ User.fs_uniquifier == uniquifier, User.active.is_(True)
78
+ )
79
+ )
80
+ return bool(user and state and state.get("user_id") == uniquifier)
81
+
82
+ async def authorized():
83
+ check = asyncio.create_task(check_authorization())
84
+ try:
85
+ return await asyncio.shield(check)
86
+ except asyncio.CancelledError:
87
+ await check
88
+ raise
89
+
90
+ async def sender():
91
+ while True:
92
+ message = await queue.get()
93
+ if message is None or not await authorized():
94
+ await websocket.close(4001, "Session ended")
95
+ return
96
+ await websocket.send(message)
97
+
98
+ async def receiver():
99
+ started, count = time.monotonic(), 0
100
+ while True:
101
+ message = await websocket.receive()
102
+ if time.monotonic() - started >= 60:
103
+ started, count = time.monotonic(), 0
104
+ count += 1
105
+ if count > current_app.config.get("STK_WS_MESSAGE_LIMIT", 60) or len(
106
+ message
107
+ ) > current_app.config.get("STK_WS_MESSAGE_SIZE", 65536):
108
+ await websocket.close(4008, "Message limit")
109
+ return
110
+
111
+ async def monitor():
112
+ while True:
113
+ await asyncio.sleep(current_app.config.get("STK_WS_AUTH_INTERVAL", 5))
114
+ if not await authorized():
115
+ await websocket.close(4001, "Session ended")
116
+ return
117
+
118
+ tasks = []
119
+ try:
120
+ await websocket.send(json.dumps({"type": "connected", "user_id": user_id}))
121
+ tasks = [
122
+ asyncio.create_task(sender()),
123
+ asyncio.create_task(receiver()),
124
+ asyncio.create_task(monitor()),
125
+ ]
126
+ await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
127
+ finally:
128
+ for task in tasks:
129
+ task.cancel()
130
+ await asyncio.gather(*tasks, return_exceptions=True)
131
+ _clients[user_id].discard(queue)
132
+ if not _clients[user_id]:
133
+ del _clients[user_id]
@@ -0,0 +1,135 @@
1
+ Metadata-Version: 2.5
2
+ Name: stkit
3
+ Version: 17.0.0
4
+ Summary: An async Quart framework optimized for AI-assisted development workflows
5
+ Author-email: Level09 <hello@level09.com>
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.11
9
+ Requires-Dist: aiosmtplib>=5.0.0
10
+ Requires-Dist: aiosqlite>=0.20.0
11
+ Requires-Dist: alembic>=1.16.0
12
+ Requires-Dist: authlib<2,>=1.9.1
13
+ Requires-Dist: bcrypt>=4.3.0
14
+ Requires-Dist: blinker>=1.9.0
15
+ Requires-Dist: click>=8.3.1
16
+ Requires-Dist: cryptography>=45.0.5
17
+ Requires-Dist: email-validator>=2.2.0
18
+ Requires-Dist: httpx2>=2.16.0
19
+ Requires-Dist: httpx>=0.27.0
20
+ Requires-Dist: jinja2>=3.1.6
21
+ Requires-Dist: joserfc<2,>=1.7.1
22
+ Requires-Dist: libpass>=1.9.0
23
+ Requires-Dist: orjson>=3.10.18
24
+ Requires-Dist: pillow>=13.0.0
25
+ Requires-Dist: psycopg2-binary>=2.9.10
26
+ Requires-Dist: python-dotenv>=1.1.1
27
+ Requires-Dist: qrcode>=8.2
28
+ Requires-Dist: quart-security<3,>=2.0.1
29
+ Requires-Dist: quart-session>=3.0.0
30
+ Requires-Dist: quart>=0.20.0
31
+ Requires-Dist: rich>=14.0.0
32
+ Requires-Dist: sqlalchemy[asyncio]>=2.0.41
33
+ Requires-Dist: uvicorn>=0.40.0
34
+ Requires-Dist: webauthn>=2.6.0
35
+ Requires-Dist: wtforms>=3.2.1
36
+ Provides-Extra: asgi
37
+ Requires-Dist: hypercorn>=0.17.0; extra == 'asgi'
38
+ Provides-Extra: dev
39
+ Requires-Dist: playwright; extra == 'dev'
40
+ Requires-Dist: pre-commit; extra == 'dev'
41
+ Requires-Dist: ptpython>=3.0.30; extra == 'dev'
42
+ Requires-Dist: ruff; extra == 'dev'
43
+ Provides-Extra: full
44
+ Requires-Dist: asyncpg>=0.30.0; extra == 'full'
45
+ Requires-Dist: redis>=7.1.0; extra == 'full'
46
+ Description-Content-Type: text/markdown
47
+
48
+ <p align="center">
49
+ <picture>
50
+ <source media="(prefers-color-scheme: dark)" srcset="docs/src/assets/logo-dark.webp">
51
+ <img src="docs/src/assets/logo-light.webp" alt="stk" width="360">
52
+ </picture>
53
+ </p>
54
+
55
+ <h1 align="center">stk</h1>
56
+
57
+ <p align="center">
58
+ <strong>Async Python framework with batteries you actually need.</strong><br>
59
+ Auth, 2FA, WebAuthn, OAuth, WebSockets, admin dashboard, Vue 3 frontend. No build step, no third-party auth services. One codebase, you own everything.
60
+ STK makes SaaS apps legible, changeable, testable, and operable by agents.
61
+ </p>
62
+
63
+ <p align="center">
64
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
65
+ <a href="https://www.python.org/"><img src="https://img.shields.io/badge/python-3.11%2B-blue.svg" alt="Python 3.11+"></a>
66
+ <a href="https://docs.astral.sh/uv/"><img src="https://img.shields.io/badge/uv-package%20manager-blueviolet.svg" alt="uv"></a>
67
+ </p>
68
+
69
+ <p align="center">
70
+ <a href="https://docs.stk.dev">Docs</a> &middot;
71
+ <a href="https://docs.stk.dev/llms.txt">llms.txt</a> &middot;
72
+ <a href="https://github.com/level09/stk/wiki">Tutorials</a>
73
+ </p>
74
+
75
+ ---
76
+
77
+ ```bash
78
+ uvx --from stkit stk init myapp && cd myapp
79
+ uv run stk db upgrade # database via Alembic
80
+ uv run stk install # admin user
81
+ uv run stk run # localhost:5000
82
+ ```
83
+
84
+ Your app is its own package that composes stk, not a fork of it. Upgrading stk is a version bump.
85
+
86
+ ## Why stk
87
+
88
+ Most frameworks give you routing and leave the rest as homework. Auth alone eats weeks. Then you bolt on Clerk or Auth0, hand your users' credentials to a third party, and pay per-MAU for the privilege.
89
+
90
+ stk ships what actually matters:
91
+
92
+ **You own your auth.** Login, session management, TOTP 2FA (Google Authenticator, Authy, 1Password), WebAuthn passkeys (Touch ID, YubiKey), recovery codes, OAuth (Google, GitHub). Production code, not a tutorial. Your users' data stays on your server.
93
+
94
+ **Async all the way down.** Quart + async SQLAlchemy + aiosmtplib + native WebSockets. Not async bolted onto a sync framework. Real concurrency without threads, workers, or callback hell.
95
+
96
+ **No JS build step.** Vue 3 + Vuetify 4 loaded directly in the browser. No webpack, no vite, no node_modules, no npm. Delete your frontend toolchain. Still get a polished admin dashboard with data tables, dark mode, collapsible sidebar, 5000+ icons.
97
+
98
+ **SQLite by default.** Deploy anywhere. No managed database required. PostgreSQL when you need it, not when the framework demands it.
99
+
100
+ **No Celery, no Redis (unless you want them).** Background tasks run on asyncio. Session store works with cookies or Redis. Complexity is opt-in.
101
+
102
+ **Built to be operated by agents.** `stk inspect` reports routes, auth guards, models, and commands as JSON. `stk verify` and `stk smoke` are a real gate: lint, migration drift, and a real browser that fails on console errors and invisible text. The docs ship in the repo and the gate fails when they drift from the code.
103
+
104
+ > **Want payments?** [ReadyKit](https://readykit.dev) adds Stripe, multi-tenancy, and teams on top of stk.
105
+
106
+ ## What's included
107
+
108
+ - **Auth and security:** Argon2id passwords (12 char minimum), TOTP 2FA, passkeys as first or second factor, recovery codes, Google and GitHub OAuth with PKCE and verified emails, session tracking by IP and device, account lockout, per-IP rate limits shared across workers, CSRF on every mutation. Public sign-up is off by default.
109
+ - **Admin dashboard:** users, roles, and an activity audit log, with server-paginated tables and JSON APIs.
110
+ - **Real-time:** authenticated, origin-checked WebSockets that push to a named user and close when the session ends.
111
+ - **Frontend:** Vue 3 and Vuetify 4 with no build step, dark mode, collapsible sidebar, notifications, Tabler Icons.
112
+ - **Tooling:** one CLI, `stk`: run, shell, new, db, doctor, verify, smoke, inspect, report, and user management.
113
+ - **Deploy:** a one-command VPS script (Caddy, systemd, hardened OS) or Docker Compose with PostgreSQL, Redis, and Nginx.
114
+
115
+ ## Stack
116
+
117
+ | Layer | Tech |
118
+ |-------|------|
119
+ | Runtime | Python 3.11+, [uv](https://docs.astral.sh/uv/) |
120
+ | Web | [Quart](https://quart.palletsprojects.com/) (async Flask) |
121
+ | ORM | SQLAlchemy 2.0+ async |
122
+ | Database | SQLite (default), PostgreSQL (optional) |
123
+ | Migrations | Alembic |
124
+ | Auth | [quart-security](https://quart-security.readthedocs.io/) (2FA, WebAuthn, OAuth) |
125
+ | Frontend | Vue 3, Vuetify 4, Axios |
126
+ | Server | Uvicorn (ASGI) behind Caddy or Nginx |
127
+
128
+ ## Documentation
129
+
130
+ Everything else lives at [docs.stk.dev](https://docs.stk.dev), and in this repo at `docs/src/content/docs/`:
131
+ [quick start](https://docs.stk.dev/getting-started/), [build loop](https://docs.stk.dev/build-loop/), [models](https://docs.stk.dev/models/), [frontend](https://docs.stk.dev/frontend/), [authentication](https://docs.stk.dev/authentication/), [configuration](https://docs.stk.dev/configuration/), [CLI reference](https://docs.stk.dev/cli/), [deployment](https://docs.stk.dev/deployment/), and [working with agents](https://docs.stk.dev/ai-coding/). Upgrade notes are in `CHANGELOG.md`; the production security contract is `SECURITY.md`.
132
+
133
+ ## License
134
+
135
+ MIT
@@ -0,0 +1,128 @@
1
+ stk/__init__.py,sha256=TjI6IxrR16KE6sXHyx_PqOMWsGzuH-zlmrBxUV1gozQ,467
2
+ stk/__main__.py,sha256=Y3ChEQgzYfD0Et0e6z_DA6y9ZmMZQ-yYdds9-mQIi1Y,118
3
+ stk/agent_login.py,sha256=e-roLSTlUdxqoiwBe5Q4PRdvfDKScpe77msnv2hCUPg,2296
4
+ stk/app.py,sha256=p9OfgXyIyJ1O57q6AKkJDdlanmSMCIoG_xpBgMc9hjc,11716
5
+ stk/checks.py,sha256=f0yU8AwlDLdhP8fcqa4jLiFUSJazm-ATYxK806a119I,21267
6
+ stk/extensions.py,sha256=85fBNULTy42KjbVfROmcNIZ69o4oCHv5o69Ulnk8ns0,950
7
+ stk/security.py,sha256=MwP21b1WQA9-im7SdtXlUigNbOrnsiYmM33futH0ofo,6069
8
+ stk/settings.py,sha256=8XNduYxjd2jCMkpse-QHUGmpcSSrrDCYMsI3oySJgXk,5825
9
+ stk/shell.py,sha256=SzxWOmIC2Eo3Usyfyiz-1xP31hXnHinmj5uIWcR2-Uc,9018
10
+ stk/websocket.py,sha256=LfGRGxyOOTRR_L_myd-qL-T-tW_kmXpxRQF6i06OsKM,4551
11
+ stk/cli/__init__.py,sha256=FIRLAbaxz2wzEZdTlhoVuPP5QzAgpXMo336bqSjQOnY,814
12
+ stk/cli/agent.py,sha256=ZlG7t_h5AJbPfhD1gvzsFvxerxgPtXYJxGFmWQ2cQAE,9253
13
+ stk/cli/base.py,sha256=n13qQbbLO2rM5vJcyTF_WuXpnDui60h0I5MAAL-nyT8,2315
14
+ stk/cli/database.py,sha256=lFzmnRICc7sEwYq_wcvEaxkeSr68Z-KJBr7lHMLC0kY,6369
15
+ stk/cli/develop.py,sha256=2_g-E4ZRTWBppkVqmYo9aEIJ51bKsGN_suozfA0q__s,1836
16
+ stk/cli/doctor.py,sha256=FEOMUuGqgOjXWrXoRlIAdcdlkOfzMUdm66OAa7l9QWw,8545
17
+ stk/cli/main.py,sha256=Vx3uhF29JYT_t6OWnKD7bt2-npxTTnSgZlY0bCn-CTU,2892
18
+ stk/cli/reports.py,sha256=bzXve1MphQhFHjSn4xzfc4yZs7cyT3r8JrET9mJJuGE,10415
19
+ stk/cli/smoke.py,sha256=ufVLcDU89Nqoi0_EJi0jslBOpceveG0hknLr22v_m7k,13665
20
+ stk/cli/users.py,sha256=k0ZoNFrpF1H6JTLfRDaxDodQWzMKHW5ttA6ZehYoxpM,9048
21
+ stk/cli/watch.py,sha256=Po65qd3PFSwTqbTzItWVqKv_6j0fKBvEl462v-qdmdg,2118
22
+ stk/migrations/__init__.py,sha256=hwggkiD26cn_qG-CH753Z2eCTNJIpLz81ZTgbjU7jDM,4497
23
+ stk/migrations/env.py,sha256=jvkj0BHyDCw_D72GD3HARU5vVsh0K-3IiuwJNuFU_5w,1618
24
+ stk/migrations/script.py.mako,sha256=XP_W6MXv-2ho7ZQe3eULkNnZK7CQLFjs1eqluELyTLE,374
25
+ stk/migrations/versions/stk_0001_initial_schema.py,sha256=LVqaoJrKjxL0ImXOXjXNio2JKLrMPr5VwBLp2okntSw,9622
26
+ stk/portal/views.py,sha256=S5f6cK0iMYYCDzq3bN3WziPHNeEUubupi2VrPO_4h8Y,982
27
+ stk/public/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ stk/public/models.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ stk/public/views.py,sha256=RGfEuA4-yhLF7yEFyjlhL_J928vBCQeAUmR1_WBPBfc,11263
30
+ stk/scaffold/__init__.py,sha256=Ty63wnWu7s3WsTkoLBK-By55u1PWdkEpN73u_DQdTOk,80
31
+ stk/scaffold/generator.py,sha256=7eHQeBXzB5bEsJ36V4vRIsUucNHtYA5s1dCg8-W_rZ4,6968
32
+ stk/scaffold/templates.py,sha256=vpFKT95a1q2y2QIMFn6o2olWDAhBpDpirL3f_s76WM8,14409
33
+ stk/scaffold/app/CLAUDE.md,sha256=7DsuJslnbWURIpS-DGo1MmvYxEdalGP29KMNI2j-yc0,5539
34
+ stk/scaffold/app/Dockerfile,sha256=IrhcM_p6-Gj6tYRbFgR5Yd2-T2JK6ggUQN6dyLxQ3Fw,1197
35
+ stk/scaffold/app/README.md,sha256=4OBRWYIR7xLGahxnXVCQgPbejy42Fn4EjUEhHUfTW8w,661
36
+ stk/scaffold/app/docker-compose.yml,sha256=kiqipo-bYBim8IHMgNJDarzo1USgcVKke-dc3keOuEs,2766
37
+ stk/scaffold/app/dockerignore,sha256=hCquC-d8Fjuh5X7SywsrVp5eGtDsssEbxS4U6gAgo-0,97
38
+ stk/scaffold/app/env-sample,sha256=5Mf9naYoGvZJdvS1Mpx1IZ9EeDw1oJp5Urp_a8Alcns,2639
39
+ stk/scaffold/app/gitignore,sha256=aEPMW4bHF8UVc0hU-D57PYg_XLkK10I8F4QgAlNh6p0,79
40
+ stk/scaffold/app/pyproject.toml,sha256=yxVv4NrKGtoZJUbsg7pkTfybmGXEAWKWyVe_GLTuBGU,470
41
+ stk/scaffold/app/__app__/__init__.py,sha256=G-tcMYUbPrEOfdOIhHMBTlWPXKW0FY5Er9nwIovK0Ak,15
42
+ stk/scaffold/app/__app__/app.py,sha256=m8eXqyEjwk490gFFr8nsBCmhzcp_EFPjLUTCMCk33qo,204
43
+ stk/scaffold/app/__app__/settings.py,sha256=yhrx7mzHVSEqD1DrB00annvXYSJF-h8hNYV1_mVlWSQ,103
44
+ stk/scaffold/app/claude/skills/stk-api/SKILL.md,sha256=qB3iOUBMyR0efWPNMWxRvIDEGcc1PVqkQ7BGxWT0IJ8,2073
45
+ stk/scaffold/app/claude/skills/stk-blueprint/SKILL.md,sha256=7E1nYnS78rFIKnKEVPb9eocjx-TT7bjnDjtxfOrJZWw,2801
46
+ stk/scaffold/app/claude/skills/stk-deploy/SKILL.md,sha256=FG-xHH7COXgqVlH-G3iaSyJz1Wm-96strM9T_5YkNEw,2275
47
+ stk/scaffold/app/claude/skills/stk-lessons/SKILL.md,sha256=fjS54geB5Nx2nJ_7EHmN-sXTEVTEtzRJxb2V82knaHg,2861
48
+ stk/scaffold/app/claude/skills/stk-migrate/SKILL.md,sha256=m5TT1NaRBhrhq5XHMpLffgNjgWGEPcpT62WRLxsBojo,3291
49
+ stk/scaffold/app/nginx/nginx.conf,sha256=ti6gsb4yLxZY9fq7lXZMFVpPagujcDtYlCBhIM6nQRc,1477
50
+ stk/scaffold/app/nginx/stk.conf,sha256=iL4OfoDV3ddVt8Km2jRMbR3e6P7GZJavI8fRmVO5mUA,1108
51
+ stk/scaffold/app/postgres/init-db.sh,sha256=J7LvXZoqxA-sxBvysmeGkMdK3HchGEMd1E9ydeRcS7U,371
52
+ stk/scaffold/app/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
+ stk/scaffold/app/tests/test_app.py,sha256=PfpUlI0c5Z-7nq8Ki-OKdN3taX9hz856vIYtKPMBTyY,653
54
+ stk/static/VERSIONS.txt,sha256=jxgUaSCAX4kHGkQwRrqp_F88MT4A1a4_YkvW7uBhR6o,67
55
+ stk/static/robots.txt,sha256=2QZtKcHf7WLhu94sECB5pczAwen2aWRP2J_WUs3BVrs,13
56
+ stk/static/css/app.css,sha256=SnUrmN6mBqwVCGsntD6UcBqvJK-miY9XGxm3hnJylaI,5676
57
+ stk/static/css/layout.css,sha256=BS39q0o2CxbH00Pryz5rBhXVOlP9-cEBj4iWkc0AD6U,6344
58
+ stk/static/css/vuetify.min.css,sha256=AmoJnBbOA6TC8VVwgpHOfhnYHkKpcdBO_nU2MmDvBDE,495390
59
+ stk/static/icons/tabler-icons.min.css,sha256=8vt3B14Ys0QggDJCxcDD0E9-PFOZvR_g-0Uuhv__Jas,210178
60
+ stk/static/icons/fonts/tabler-icons.woff2,sha256=nZBtSvankqBoM5E8R5L4LADSlY0twuJyCG9W3nAsKk8,460040
61
+ stk/static/img/auth-bg.webp,sha256=e0JhHVCx5zfYeqnOwKGt6Ic_IxTvgD730gMa2A7SMJY,18830
62
+ stk/static/img/favicon.ico,sha256=_A58Wr6W5QHy3mDuyFPirdWvPEH6Fznpk_m_2DSjb9c,9662
63
+ stk/static/img/stk.svg,sha256=hg4ocE9D4ldNnube0_N7Qcn15etXPInMnDHQflowvlw,1470346
64
+ stk/static/js/axios.min.js,sha256=3iURhktIs6NxqceJqc9iSzvN5ij7ZXK2uTalzCxIwm8,62406
65
+ stk/static/js/config.js,sha256=OWHYt8vvg45Ta2YGPsSudTAHFD8bWl0vWZ12iLO1vG4,4978
66
+ stk/static/js/vue.min.js,sha256=ngA5o_btDoUwjiTXN0R_Gvavg9Ip1p4SZ6MrKbwqEzc,165599
67
+ stk/static/js/vuetify.min.js,sha256=t591OZVpppeP54pPQ85ae7q2xDYZdMWs5B8ISHRT_tw,645528
68
+ stk/static/js/components/NotificationDropdown.js,sha256=O7exvyvaP4DwOqHuHFDwQ1ML-PtQcy6uVKk9Ge0i3rY,5054
69
+ stk/static/js/components/ThemeSwitcher.js,sha256=R0xn7qltXU_AfchfR-otftvxA58Ng7W4_Nfd-4JbyUg,1753
70
+ stk/static/js/components/TransitionExpand.js,sha256=HoNeltSdRKn55ghM7TKOJ03yjYC5wnSS02ntSvHBkhw,1328
71
+ stk/static/js/components/VerticalNavGroup.js,sha256=nqwUXmnRoUppWTEXW6bVO6OQwxonb1Sj_Q7Jqx6lb-U,3068
72
+ stk/static/js/components/VerticalNavLink.js,sha256=T1ho5gZaMu4DcnhPiZcIL0CXqULpLcE_gYhpllW6y9c,912
73
+ stk/static/js/components/VerticalNavSectionTitle.js,sha256=jy0Dq08ZGlhh3GD3pd1h6Iccf4SNUS-FcvtvRn1osEU,351
74
+ stk/static/js/components/index.js,sha256=jSyNyuusLUzEmEusvrguLuNZ12zxhdl4Ij6Ef76Wx8I,1493
75
+ stk/tasks/__init__.py,sha256=xR6I-Xnn5vy9nfn8Bq3QQMIvrBj9520mvVD0W4gagJc,2314
76
+ stk/templates/401.html,sha256=HH4XzNwFKCxs2p1V9Dl8ZbxRzz-5aGZOillBs3Ar12Q,273
77
+ stk/templates/404.html,sha256=GxFEpUCbDmDR52uBqVmdcx_MYPxQiSJ9UXhUdk-7C9g,271
78
+ stk/templates/500.html,sha256=FHbWYWXaqHeRStaz0r14P9nvqMi7fEF6iCz34Pm10FA,321
79
+ stk/templates/_brand.html,sha256=rYRAdtx-lfHUNbAn7YQL2CZNhebkvSzwnPfoz5K7dO4,120
80
+ stk/templates/auth_layout.html,sha256=FqVJxGpPuSmXGN1IC97xbKWK07lt6fcUBvm5MaXE4uk,1115
81
+ stk/templates/dashboard.html,sha256=IpqgC3ZZVax6HDzpwwCUPDPS3t2HLJrEMmsZOOyCh4o,6344
82
+ stk/templates/index.html,sha256=D5cBXG53zgnRhTTXmy3jFMk6scP41VI6flkmuA7a4N0,2158
83
+ stk/templates/layout.html,sha256=--GrWhEWxHfvXr3YZMtUs8hsp0Kfzl21OHkepwYyGfo,13882
84
+ stk/templates/login-layout.html,sha256=nkd6yhmkS6fjGjcGZ9m0j0orbMr9M6EOKDKJWYL-8LA,1977
85
+ stk/templates/cms/activities.html,sha256=MXRLSqVt2Jj3p7tctjJwyGbI-uY4uGgfPWEy-ESQXv0,4794
86
+ stk/templates/cms/roles.html,sha256=wuqCMBKxndepxIosTHukoDnl54w8w93rTSX44dJ0Tt8,7737
87
+ stk/templates/cms/users.html,sha256=pLJ8LgFs0e2MfX6uipmngZBt6KLup1Uu3qhPRghoPdg,11423
88
+ stk/templates/security/_macros.html,sha256=7ZfoYDxeM3JvodC0bJ__HKXU1NnOaEVoKyM-m2_MEQo,1224
89
+ stk/templates/security/_messages.html,sha256=TNLeaprduPYDGUfj-ngXemxqupBUGFDOPqtA4aT0K6o,390
90
+ stk/templates/security/change_password.html,sha256=P2fn95XA7zvbMG6SYOdZVAyk1_ufYNycTRPVoE58Y0g,3269
91
+ stk/templates/security/confirm_email.html,sha256=5ADLuOl0Lmx0vs3HLlJUN3p4n2As89WZFwrbtMN8nM0,1288
92
+ stk/templates/security/forgot_password.html,sha256=pIPVxljG2F9jGVWbJn463HLVwWTBjRTWvLM6n_y__cc,2366
93
+ stk/templates/security/login_user.html,sha256=Q2f_0BVKmNXt8p3mmj981rigL4HvZR5t7XNudvcLZqI,10593
94
+ stk/templates/security/mf_recovery.html,sha256=hHRIUxFv8-DZPAlw-IRqXIhvlcIaNCj5liuIkVAq9wI,2327
95
+ stk/templates/security/mf_recovery_codes.html,sha256=h1Eiob3X8ouKyyHyR5QijRrgLTNqs3S0O5Xl-NGL888,5186
96
+ stk/templates/security/register_user.html,sha256=OPjHzpTFVvjXnpOvWsea4vHnib0Myega8cNsiRG1pOc,13059
97
+ stk/templates/security/reset_password.html,sha256=TFNknPpFIzi98WNrDVxttWHWROqUIdStuZShXvmO3rQ,1965
98
+ stk/templates/security/send_confirmation.html,sha256=iNx2AG0SwC-sAps97yzRlDsNVIkXEYy-FuA_VXmjMyA,1601
99
+ stk/templates/security/send_login.html,sha256=SKC3TvUNPoDED7dBWqURULu4Yo7btC6Yjwf1lkJ9okI,1007
100
+ stk/templates/security/two_factor_select.html,sha256=iFh8Mgi9YSFePB9cHtC1uXQcqa6Pqa0NSpVen2eht34,3016
101
+ stk/templates/security/two_factor_setup.html,sha256=UVkWNNU4MhhoHsp12xJYXNo-xKdsRoLQAo_ZQASIFlM,7360
102
+ stk/templates/security/two_factor_verify_code.html,sha256=mEtNmyITJhCGfnlIaENsQVFqkXlLmNtnnVPs8JzaTKs,2560
103
+ stk/templates/security/wan_register.html,sha256=_kwpZmeZKDuAMgoog4Ow0RjmHL34rPe8XAdxJjkOGso,8212
104
+ stk/templates/security/wan_signin.html,sha256=4jjnWe-p_V_-lzYKVcBR_ZMU8R_ofeL8DGkaxFjGoI0,5542
105
+ stk/templates/security/wan_verify.html,sha256=gQDPVYmeeaFH1377QzUuIkzPWPpzrPSeCkg7TcDwLec,4891
106
+ stk/templates/security/email/change_notice.html,sha256=tWDVeRPZIz95KLAgiVzqURWFI9ceVETvYijYnVjnLcI,221
107
+ stk/templates/security/email/change_notice.txt,sha256=5D6ukwu8wV3Retz1RX1m-Nc3XEq7yy7sbt7fUc-8IZw,202
108
+ stk/templates/security/email/confirmation_instructions.html,sha256=AKxh2AdiJG_x-O0dmJ6xWxI1ipGebnkwMhftyEzqqqY,121
109
+ stk/templates/security/email/confirmation_instructions.txt,sha256=fiaKSdmCq6g7R-nQ4s13cEGqDKrqkgLAhRb2mKbaYSA,74
110
+ stk/templates/security/email/login_instructions.html,sha256=qU7qUV6ot3Ct3qzWrmWHPTL0MPxd8d9O22N9bFgnjCE,135
111
+ stk/templates/security/email/login_instructions.txt,sha256=Qb_Ft_RMleVc-lBrvogzIKCeaRpccAEU6WbvPKJHUUY,90
112
+ stk/templates/security/email/reset_instructions.html,sha256=K0XTSdLdbRmxnU7Y4XfoWS7RV9S1nicvSD999vmZpj8,71
113
+ stk/templates/security/email/reset_instructions.txt,sha256=7C0w7kZb9TA3bJ4GRhbUH73kuxp4XSXDhg8_RLIotOE,62
114
+ stk/templates/security/email/reset_notice.html,sha256=6HB7sneTzUR6tk-zjt05FBwuxq5SR3VDnW_YyWpYcEY,35
115
+ stk/templates/security/email/reset_notice.txt,sha256=wQZrF8QLa0L-fglPK6OirBJ51bB0hAbNvHlHPfTg_BY,28
116
+ stk/templates/security/email/welcome.html,sha256=e1l1dyB7jkCc8KQnade8dlJLNl0U013EWCztuoq41Ck,198
117
+ stk/templates/security/email/welcome.txt,sha256=OoJ_UDC_3wlqJf9xCVRyEZB9sMvteAOqCcGM261NL7A,144
118
+ stk/user/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
119
+ stk/user/forms.py,sha256=ZjUTeFoIZ0Dej57KN3F1sgD0DN2oiEE9gUq2_Yos1vk,313
120
+ stk/user/models.py,sha256=dgF0UxtKVeVWD-q1D2H4Io4qhgyswV2NB3yR8hF3JIg,10326
121
+ stk/user/views.py,sha256=PHJi8YUVjqXGnKR0g8fM_BpSl4vm23hE4TYDPQiO4g0,11037
122
+ stk/utils/email.py,sha256=5VzM-eNBE4piiW5jJ76lzfpdsfqzENovnpKdBMiW9dQ,1041
123
+ stk/utils/ratelimit.py,sha256=H_yRvw0Zf358Ytecr3cEZeO0-wRjR1oDEJITU8U3Gzo,2291
124
+ stkit-17.0.0.dist-info/METADATA,sha256=6eG6hcpaE8PCXg42D2DorA0oDkL_CoetwskLkllHOcY,6740
125
+ stkit-17.0.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
126
+ stkit-17.0.0.dist-info/entry_points.txt,sha256=nk3pyghgvdljjC3gtq7TpQrso-nVpTsqbKUeZ1LzNn0,42
127
+ stkit-17.0.0.dist-info/licenses/LICENSE,sha256=ug7P7UArHIYJVDR1mAn4Gy345HKQZWgIkdlygoFRZG4,1071
128
+ stkit-17.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ stk = stk.cli.main:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Nidal Alhariri
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.