stkit 17.2.0__tar.gz → 17.2.1__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.
- {stkit-17.2.0 → stkit-17.2.1}/CHANGELOG.md +12 -0
- {stkit-17.2.0 → stkit-17.2.1}/PKG-INFO +2 -2
- {stkit-17.2.0 → stkit-17.2.1}/pyproject.toml +2 -2
- {stkit-17.2.0 → stkit-17.2.1}/stk/__init__.py +1 -1
- {stkit-17.2.0 → stkit-17.2.1}/stk/agent_login.py +1 -1
- {stkit-17.2.0 → stkit-17.2.1}/stk/cli/users.py +3 -3
- {stkit-17.2.0 → stkit-17.2.1}/stk/public/views.py +1 -1
- {stkit-17.2.0 → stkit-17.2.1}/stk/settings.py +0 -1
- {stkit-17.2.0 → stkit-17.2.1}/stk/user/models.py +13 -1
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_security_compatibility.py +29 -1
- {stkit-17.2.0 → stkit-17.2.1}/.gitignore +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/LICENSE +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/README.md +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/SECURITY.md +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/.env-sample +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/.gitignore +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/README.md +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/minimal/__init__.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/minimal/app.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/minimal/migrations/364232ae77d5_add_note.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/minimal/note/__init__.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/minimal/note/models.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/minimal/note/templates/note/index.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/minimal/note/views.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/minimal/settings.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/minimal/templates/layout.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/pyproject.toml +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/tests/__init__.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/tests/test_app.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/examples/minimal/uv.lock +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/__main__.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/app.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/checks.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/cli/__init__.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/cli/agent.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/cli/base.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/cli/copy_rules.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/cli/database.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/cli/develop.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/cli/doctor.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/cli/main.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/cli/reports.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/cli/smoke.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/cli/watch.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/extensions.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/files/__init__.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/files/models.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/files/store.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/files/views.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/mcp/__init__.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/mcp/models.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/mcp/server.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/mcp/views.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/migrations/__init__.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/migrations/env.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/migrations/script.py.mako +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/migrations/versions/stk_0001_initial_schema.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/migrations/versions/stk_0002_jobs.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/migrations/versions/stk_0003_files.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/migrations/versions/stk_0004_notifications.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/migrations/versions/stk_0005_mcp_tokens.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/notification/__init__.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/notification/models.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/notification/views.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/portal/views.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/public/__init__.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/public/models.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/__init__.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/CLAUDE.md +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/Dockerfile +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/README.md +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/__app__/__init__.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/__app__/app.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/__app__/settings.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/claude/skills/stk-api/SKILL.md +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/claude/skills/stk-blueprint/SKILL.md +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/claude/skills/stk-deploy/SKILL.md +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/claude/skills/stk-lessons/SKILL.md +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/claude/skills/stk-migrate/SKILL.md +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/docker-compose.yml +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/dockerignore +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/env-sample +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/github/workflows/checks.yml +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/gitignore +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/nginx/nginx.conf +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/nginx/stk.conf +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/postgres/init-db.sh +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/pyproject.toml +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/tests/__init__.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/app/tests/test_app.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/generator.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/scaffold/templates.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/security.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/shell.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/VERSIONS.txt +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/css/app.css +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/css/layout.css +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/css/vuetify.min.css +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.animals.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.arrows-1.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.arrows-2.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.badges.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.brand-1.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.brand-2.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.buildings.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.charts.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.communication.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.computers.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.core.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.currencies.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.database.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.design-1.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.design-2.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.development.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.devices-1.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.devices-2.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.document.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.e-commerce.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.electrical.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.extensions.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.food.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.games.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.gender.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.gestures.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.health.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.laundry.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.letters.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.logic.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.map.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.math.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.media.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.mood.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.nature.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.numbers.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.photography.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.shapes.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.sport.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.symbols.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.system-1.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.system-2.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.system-3.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.text.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.vehicles.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.version-control.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.weather.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/fonts/tabler-icons.zodiac.woff2 +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/icons/tabler-icons.min.css +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/img/auth-bg.webp +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/img/favicon.ico +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/img/stk.svg +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/js/axios.min.js +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/js/components/NotificationDropdown.js +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/js/components/ThemeSwitcher.js +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/js/components/TransitionExpand.js +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/js/components/VerticalNavGroup.js +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/js/components/VerticalNavLink.js +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/js/components/VerticalNavSectionTitle.js +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/js/components/index.js +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/js/config.js +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/js/vue.min.js +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/js/vuetify.min.js +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/static/robots.txt +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/tasks/__init__.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/tasks/models.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/401.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/404.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/500.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/_brand.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/auth_layout.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/cms/activities.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/cms/jobs.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/cms/roles.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/cms/users.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/dashboard.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/index.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/layout.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/login-layout.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/mcp/tokens.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/_macros.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/_messages.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/change_password.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/confirm_email.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/email/change_notice.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/email/change_notice.txt +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/email/confirmation_instructions.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/email/confirmation_instructions.txt +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/email/login_instructions.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/email/login_instructions.txt +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/email/reset_instructions.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/email/reset_instructions.txt +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/email/reset_notice.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/email/reset_notice.txt +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/email/welcome.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/email/welcome.txt +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/forgot_password.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/login_user.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/mf_recovery.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/mf_recovery_codes.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/register_user.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/reset_password.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/send_confirmation.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/send_login.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/two_factor_select.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/two_factor_setup.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/two_factor_verify_code.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/wan_register.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/wan_signin.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/templates/security/wan_verify.html +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/user/__init__.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/user/forms.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/user/views.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/utils/email.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/utils/netguard.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/utils/ratelimit.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/stk/websocket.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_agent_operability.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_app_index.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_branding.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_builder_ux.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_cli.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_composition.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_copy_rules.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_csrf_exempt.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_error_handler.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_files.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_jobs.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_mcp.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_migration_drift.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_netguard.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_notifications.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_production_security.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_revision_imports.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_scaffolder.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_shell.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_smoke_pages.py +0 -0
- {stkit-17.2.0 → stkit-17.2.1}/tests/test_static_versioning.py +0 -0
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v17.2.1 (2026-10-31)
|
|
4
|
+
|
|
5
|
+
No migration required.
|
|
6
|
+
|
|
7
|
+
- Emails are stored trimmed and lowercased, and the CLI, agent login and OAuth look users up regardless of case, matching what the login form does. Before, a user created as `Ada@Example.com` from the admin page or `stk create` could not sign in, because login lowercases what it is given (Authentication).
|
|
8
|
+
- Requires quart-security 2.0.4, which matches login emails regardless of case and drops the unused `remember` login field and `SECURITY_FRESHNESS_GRACE_PERIOD` setting. stk's own `SECURITY_FRESHNESS_GRACE_PERIOD` is removed with it.
|
|
9
|
+
|
|
10
|
+
### Upgrade considerations
|
|
11
|
+
|
|
12
|
+
- Existing rows keep their stored case and still sign in. Two rows whose emails differ only by case make login for that address fail: merge or rename one. To find them: `uv run stk shell -c "await sql('select lower(email), count(*) from \"user\" group by 1 having count(*) > 1')"`.
|
|
13
|
+
- A custom login template that renders `form.remember` must drop it.
|
|
14
|
+
|
|
3
15
|
## v17.2.0 (2026-10-31)
|
|
4
16
|
|
|
5
17
|
Run `uv run stk db upgrade`: four new core tables (`stk_job`, `stk_file`, `stk_notification`, `mcp_token`).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: stkit
|
|
3
|
-
Version: 17.2.
|
|
3
|
+
Version: 17.2.1
|
|
4
4
|
Summary: An async Quart framework optimized for AI-assisted development workflows
|
|
5
5
|
Project-URL: Documentation, https://docs.stkit.dev
|
|
6
6
|
Project-URL: Source, https://github.com/level09/stk
|
|
@@ -28,7 +28,7 @@ Requires-Dist: pillow>=13.0.0
|
|
|
28
28
|
Requires-Dist: psycopg2-binary>=2.9.10
|
|
29
29
|
Requires-Dist: python-dotenv>=1.2.2
|
|
30
30
|
Requires-Dist: qrcode>=8.2
|
|
31
|
-
Requires-Dist: quart-security<3,>=2.0.
|
|
31
|
+
Requires-Dist: quart-security<3,>=2.0.4
|
|
32
32
|
Requires-Dist: quart-session>=3.0.0
|
|
33
33
|
Requires-Dist: quart>=0.20.0
|
|
34
34
|
Requires-Dist: rich>=14.0.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "stkit"
|
|
3
|
-
version = "17.2.
|
|
3
|
+
version = "17.2.1"
|
|
4
4
|
description = "An async Quart framework optimized for AI-assisted development workflows"
|
|
5
5
|
authors = [
|
|
6
6
|
{name = "Level09", email = "hello@level09.com"}
|
|
@@ -30,7 +30,7 @@ dependencies = [
|
|
|
30
30
|
"python-dotenv>=1.2.2",
|
|
31
31
|
"qrcode>=8.2",
|
|
32
32
|
"quart>=0.20.0",
|
|
33
|
-
"quart-security>=2.0.
|
|
33
|
+
"quart-security>=2.0.4,<3",
|
|
34
34
|
"quart-session>=3.0.0",
|
|
35
35
|
"rich>=14.0.0",
|
|
36
36
|
"sqlalchemy[asyncio]>=2.0.41",
|
|
@@ -69,7 +69,7 @@ async def agent_login():
|
|
|
69
69
|
return "", 403
|
|
70
70
|
|
|
71
71
|
user = (
|
|
72
|
-
await g.db_session.execute(select(User).where(User.email
|
|
72
|
+
await g.db_session.execute(select(User).where(User.email_is(email)))
|
|
73
73
|
).scalar_one_or_none()
|
|
74
74
|
if user is None or not user.active:
|
|
75
75
|
return "", 403
|
|
@@ -125,7 +125,7 @@ def create(email, password):
|
|
|
125
125
|
async def _run():
|
|
126
126
|
async with ext.async_session_factory() as session:
|
|
127
127
|
existing = (
|
|
128
|
-
await session.execute(select(User).where(User.email
|
|
128
|
+
await session.execute(select(User).where(User.email_is(email)))
|
|
129
129
|
).scalar_one_or_none()
|
|
130
130
|
if existing is not None:
|
|
131
131
|
console.print("[yellow]User already exists![/]")
|
|
@@ -153,7 +153,7 @@ def add_role(email, role):
|
|
|
153
153
|
|
|
154
154
|
async with ext.async_session_factory() as session:
|
|
155
155
|
u = (
|
|
156
|
-
await session.execute(select(User).where(User.email
|
|
156
|
+
await session.execute(select(User).where(User.email_is(email)))
|
|
157
157
|
).scalar_one_or_none()
|
|
158
158
|
|
|
159
159
|
if u is None:
|
|
@@ -196,7 +196,7 @@ def reset(email, password):
|
|
|
196
196
|
async def _run():
|
|
197
197
|
async with ext.async_session_factory() as session:
|
|
198
198
|
u = (
|
|
199
|
-
await session.execute(select(User).where(User.email
|
|
199
|
+
await session.execute(select(User).where(User.email_is(email)))
|
|
200
200
|
).scalar_one_or_none()
|
|
201
201
|
if not u:
|
|
202
202
|
console.print(f'[red]User with email "{email}" not found.[/]')
|
|
@@ -126,7 +126,7 @@ async def handle_oauth_callback(provider_name, token, user_info):
|
|
|
126
126
|
user = account.user
|
|
127
127
|
else:
|
|
128
128
|
# Email ownership at an IdP does not authorize linking local credentials.
|
|
129
|
-
existing = await g.db_session.scalar(select(User).where(User.email
|
|
129
|
+
existing = await g.db_session.scalar(select(User).where(User.email_is(email)))
|
|
130
130
|
if existing:
|
|
131
131
|
return await authentication_failed()
|
|
132
132
|
user = create_oauth_user(
|
|
@@ -19,9 +19,10 @@ from sqlalchemy import (
|
|
|
19
19
|
Table,
|
|
20
20
|
UniqueConstraint,
|
|
21
21
|
delete,
|
|
22
|
+
func,
|
|
22
23
|
select,
|
|
23
24
|
)
|
|
24
|
-
from sqlalchemy.orm import declared_attr, relationship
|
|
25
|
+
from sqlalchemy.orm import declared_attr, relationship, validates
|
|
25
26
|
|
|
26
27
|
from stk.extensions import Base
|
|
27
28
|
|
|
@@ -68,6 +69,17 @@ class User(Base, UserMixin):
|
|
|
68
69
|
password_set = Column(Boolean, default=True, nullable=False)
|
|
69
70
|
active = Column(Boolean, default=False, nullable=True)
|
|
70
71
|
|
|
72
|
+
@validates("email")
|
|
73
|
+
def _normalize_email(self, key, value):
|
|
74
|
+
# Login lowercases what it is given; store the same form so one address
|
|
75
|
+
# cannot become two accounts that differ only by case.
|
|
76
|
+
return value.strip().lower() if value is not None else value
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def email_is(cls, email):
|
|
80
|
+
"""Match an email regardless of case, including rows stored before 17.2.1."""
|
|
81
|
+
return func.lower(cls.email) == email.strip().lower()
|
|
82
|
+
|
|
71
83
|
roles = relationship(
|
|
72
84
|
"Role", secondary=roles_users, backref="users", lazy="selectin"
|
|
73
85
|
)
|
|
@@ -5,7 +5,7 @@ import unittest
|
|
|
5
5
|
import pyotp
|
|
6
6
|
from quart import g
|
|
7
7
|
from quart_security import hash_password
|
|
8
|
-
from sqlalchemy import select
|
|
8
|
+
from sqlalchemy import select, update
|
|
9
9
|
|
|
10
10
|
import stk.extensions as ext
|
|
11
11
|
from stk.app import Stk
|
|
@@ -52,6 +52,34 @@ class SecurityCompatibilityTests(unittest.IsolatedAsyncioTestCase):
|
|
|
52
52
|
async with client.session_transaction() as cookie:
|
|
53
53
|
return dict(cookie)
|
|
54
54
|
|
|
55
|
+
async def test_emails_are_stored_lowercase_and_matched_regardless_of_case(self):
|
|
56
|
+
self.assertEqual(User(email=" New@Example.COM ").email, "new@example.com")
|
|
57
|
+
# A row stored before normalization keeps its case; lookups still find it.
|
|
58
|
+
async with ext.async_session_factory() as db:
|
|
59
|
+
await db.execute(
|
|
60
|
+
update(User)
|
|
61
|
+
.where(User.id == self.user_id)
|
|
62
|
+
.values(email="User@Example.com")
|
|
63
|
+
)
|
|
64
|
+
await db.commit()
|
|
65
|
+
found = await db.scalar(
|
|
66
|
+
select(User).where(User.email_is(" USER@example.COM"))
|
|
67
|
+
)
|
|
68
|
+
self.assertEqual(found.id, self.user_id)
|
|
69
|
+
client = self.app.test_client()
|
|
70
|
+
await client.get("/login")
|
|
71
|
+
async with client.session_transaction() as cookie:
|
|
72
|
+
csrf = cookie["_csrf_token"]
|
|
73
|
+
response = await client.post(
|
|
74
|
+
"/login",
|
|
75
|
+
form={
|
|
76
|
+
"email": " user@EXAMPLE.com ",
|
|
77
|
+
"password": "TestPassword123!",
|
|
78
|
+
"csrf_token": csrf,
|
|
79
|
+
},
|
|
80
|
+
)
|
|
81
|
+
self.assertEqual(response.status_code, 302)
|
|
82
|
+
|
|
55
83
|
async def test_startup_validates_state_table_without_request(self):
|
|
56
84
|
async with self.app.test_app():
|
|
57
85
|
pass
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|