stkit 17.2.3__tar.gz → 17.2.4__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.3 → stkit-17.2.4}/CHANGELOG.md +12 -0
- {stkit-17.2.3 → stkit-17.2.4}/PKG-INFO +1 -1
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/minimal/app.py +2 -1
- {stkit-17.2.3 → stkit-17.2.4}/pyproject.toml +1 -1
- {stkit-17.2.3 → stkit-17.2.4}/stk/__init__.py +1 -1
- {stkit-17.2.3 → stkit-17.2.4}/stk/app.py +0 -2
- {stkit-17.2.3 → stkit-17.2.4}/stk/checks.py +22 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/notification/__init__.py +13 -1
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/__app__/app.py +2 -1
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/layout.html +3 -1
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_notifications.py +2 -1
- stkit-17.2.4/tests/test_route_collisions.py +41 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_scaffolder.py +1 -1
- {stkit-17.2.3 → stkit-17.2.4}/.gitignore +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/LICENSE +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/README.md +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/SECURITY.md +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/.env-sample +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/.gitignore +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/README.md +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/minimal/__init__.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/minimal/migrations/364232ae77d5_add_note.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/minimal/note/__init__.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/minimal/note/models.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/minimal/note/templates/note/index.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/minimal/note/views.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/minimal/settings.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/minimal/templates/layout.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/pyproject.toml +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/tests/__init__.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/tests/test_app.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/examples/minimal/uv.lock +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/__main__.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/agent_login.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/cli/__init__.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/cli/agent.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/cli/base.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/cli/copy_rules.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/cli/database.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/cli/develop.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/cli/doctor.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/cli/main.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/cli/reports.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/cli/smoke.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/cli/users.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/cli/watch.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/extensions.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/files/__init__.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/files/models.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/files/store.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/files/views.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/mcp/__init__.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/mcp/models.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/mcp/server.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/mcp/views.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/migrations/__init__.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/migrations/env.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/migrations/script.py.mako +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/migrations/versions/stk_0001_initial_schema.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/migrations/versions/stk_0002_jobs.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/migrations/versions/stk_0003_files.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/migrations/versions/stk_0004_notifications.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/migrations/versions/stk_0005_mcp_tokens.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/migrations/versions/stk_0006_stk_mcp_token.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/notification/models.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/notification/views.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/portal/views.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/public/__init__.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/public/models.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/public/views.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/__init__.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/CLAUDE.md +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/Dockerfile +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/README.md +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/__app__/__init__.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/__app__/settings.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/claude/skills/stk-api/SKILL.md +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/claude/skills/stk-blueprint/SKILL.md +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/claude/skills/stk-deploy/SKILL.md +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/claude/skills/stk-lessons/SKILL.md +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/claude/skills/stk-migrate/SKILL.md +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/docker-compose.yml +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/dockerignore +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/env-sample +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/github/workflows/checks.yml +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/gitignore +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/nginx/nginx.conf +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/nginx/stk.conf +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/postgres/init-db.sh +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/pyproject.toml +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/tests/__init__.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/app/tests/test_app.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/generator.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/scaffold/templates.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/security.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/settings.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/shell.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/VERSIONS.txt +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/css/app.css +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/css/layout.css +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/css/vuetify.min.css +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.animals.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.arrows-1.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.arrows-2.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.badges.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.brand-1.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.brand-2.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.buildings.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.charts.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.communication.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.computers.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.core.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.currencies.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.database.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.design-1.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.design-2.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.development.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.devices-1.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.devices-2.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.document.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.e-commerce.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.electrical.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.extensions.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.food.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.games.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.gender.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.gestures.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.health.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.laundry.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.letters.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.logic.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.map.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.math.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.media.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.mood.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.nature.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.numbers.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.photography.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.shapes.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.sport.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.symbols.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.system-1.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.system-2.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.system-3.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.text.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.vehicles.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.version-control.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.weather.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/fonts/tabler-icons.zodiac.woff2 +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/icons/tabler-icons.min.css +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/img/auth-bg.webp +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/img/favicon.ico +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/img/stk.svg +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/js/axios.min.js +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/js/components/NotificationDropdown.js +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/js/components/ThemeSwitcher.js +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/js/components/TransitionExpand.js +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/js/components/VerticalNavGroup.js +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/js/components/VerticalNavLink.js +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/js/components/VerticalNavSectionTitle.js +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/js/components/index.js +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/js/config.js +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/js/vue.min.js +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/js/vuetify.min.js +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/static/robots.txt +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/tasks/__init__.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/tasks/models.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/401.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/404.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/500.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/_brand.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/auth_layout.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/cms/activities.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/cms/jobs.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/cms/roles.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/cms/users.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/dashboard.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/index.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/login-layout.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/mcp/tokens.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/_macros.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/_messages.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/change_password.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/confirm_email.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/email/change_notice.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/email/change_notice.txt +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/email/confirmation_instructions.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/email/confirmation_instructions.txt +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/email/login_instructions.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/email/login_instructions.txt +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/email/reset_instructions.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/email/reset_instructions.txt +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/email/reset_notice.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/email/reset_notice.txt +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/email/welcome.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/email/welcome.txt +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/forgot_password.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/login_user.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/mf_recovery.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/mf_recovery_codes.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/register_user.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/reset_password.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/send_confirmation.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/send_login.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/two_factor_select.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/two_factor_setup.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/two_factor_verify_code.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/wan_register.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/wan_signin.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/templates/security/wan_verify.html +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/user/__init__.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/user/forms.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/user/models.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/user/views.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/utils/email.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/utils/netguard.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/utils/ratelimit.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/stk/websocket.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_agent_operability.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_app_index.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_branding.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_builder_ux.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_cli.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_composition.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_copy_rules.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_csrf_exempt.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_error_handler.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_files.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_jobs.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_mcp.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_mcp_migration.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_migration_drift.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_netguard.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_production_security.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_revision_imports.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_security_compatibility.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_shell.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_smoke_pages.py +0 -0
- {stkit-17.2.3 → stkit-17.2.4}/tests/test_static_versioning.py +0 -0
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v17.2.4 (2026-10-31)
|
|
4
|
+
|
|
5
|
+
No migration required.
|
|
6
|
+
|
|
7
|
+
- Notifications are opt-in, like uploads and MCP: add `stk.notification` to the app's extensions for the `/api/notifications` routes and the layout's bell. From 17.2.0 to 17.2.3 stk registered those routes in every app, and because the first matching route answers, an app's own `/api/notifications` was silently never reached. `notify()` still stores rows without it (Background Work).
|
|
8
|
+
- `stk verify` fails when two endpoints register the same path and method, naming both, so a shadowed route can no longer ship silently.
|
|
9
|
+
- Apps made by `stk init`, and the example app, list `stk.notification`.
|
|
10
|
+
|
|
11
|
+
### Upgrade considerations
|
|
12
|
+
|
|
13
|
+
- An app that uses stk's bell adds `import stk.notification` and puts `stk.notification` in its `extensions` list, or the bell disappears and `/api/notifications` returns 404.
|
|
14
|
+
|
|
3
15
|
## v17.2.3 (2026-10-31)
|
|
4
16
|
|
|
5
17
|
Run `uv run stk db upgrade`.
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import stk.notification
|
|
1
2
|
from stk import Stk
|
|
2
3
|
|
|
3
4
|
from minimal import note
|
|
4
5
|
from minimal.settings import Config
|
|
5
6
|
|
|
6
7
|
# Each extension is a package with init_app(app); order is precedence.
|
|
7
|
-
extensions = [note]
|
|
8
|
+
extensions = [stk.notification, note]
|
|
8
9
|
|
|
9
10
|
app = Stk(__name__, config=Config, extensions=extensions)
|
|
@@ -27,7 +27,6 @@ from werkzeug.security import safe_join
|
|
|
27
27
|
import stk.extensions as ext
|
|
28
28
|
from stk.agent_login import agent_login_enabled, bp_agent_login
|
|
29
29
|
from stk.extensions import session
|
|
30
|
-
from stk.notification.views import bp_notification
|
|
31
30
|
from stk.portal.views import portal
|
|
32
31
|
from stk.public.views import public
|
|
33
32
|
from stk.settings import Config
|
|
@@ -206,7 +205,6 @@ def register_blueprints(app):
|
|
|
206
205
|
app.register_blueprint(bp_user)
|
|
207
206
|
app.register_blueprint(public)
|
|
208
207
|
app.register_blueprint(portal)
|
|
209
|
-
app.register_blueprint(bp_notification)
|
|
210
208
|
app.register_blueprint(ws_bp)
|
|
211
209
|
if agent_login_enabled(app):
|
|
212
210
|
app.register_blueprint(bp_agent_login)
|
|
@@ -295,6 +295,28 @@ def check_admin_reset_route(app):
|
|
|
295
295
|
)
|
|
296
296
|
|
|
297
297
|
|
|
298
|
+
@check("No route is registered twice")
|
|
299
|
+
def check_unique_routes(app):
|
|
300
|
+
# Werkzeug answers with the first matching rule, so an app route that repeats
|
|
301
|
+
# a core one (or another extension's) is never reached, with no error at all.
|
|
302
|
+
owners = {}
|
|
303
|
+
for rule in app.url_map.iter_rules():
|
|
304
|
+
# stk's own blueprints share /static on purpose: one folder, one answer.
|
|
305
|
+
if rule.endpoint == "static" or rule.endpoint.endswith(".static"):
|
|
306
|
+
continue
|
|
307
|
+
for method in rule.methods - {"HEAD", "OPTIONS"}:
|
|
308
|
+
owners.setdefault((method, rule.rule), []).append(rule.endpoint)
|
|
309
|
+
clashes = [
|
|
310
|
+
f"{method} {path}: {' and '.join(endpoints)}"
|
|
311
|
+
for (method, path), endpoints in sorted(owners.items())
|
|
312
|
+
if len(endpoints) > 1
|
|
313
|
+
]
|
|
314
|
+
assert not clashes, (
|
|
315
|
+
"only the first endpoint ever answers; rename one route or drop the"
|
|
316
|
+
" extension that adds it:\n " + "\n ".join(clashes)
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
|
|
298
320
|
@check("GET / answers 200, after any redirects")
|
|
299
321
|
async def check_http_index(app):
|
|
300
322
|
# An app may send / to its sign-in or home page; what it lands on must work.
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"""Stored, per-user notifications. The row is the truth; the socket push is a nudge.
|
|
1
|
+
"""Stored, per-user notifications. The row is the truth; the socket push is a nudge.
|
|
2
|
+
|
|
3
|
+
Opt in with `extensions=[stk.notification, ...]` for the `/api/notifications`
|
|
4
|
+
routes and the layout's bell; `notify()` stores rows either way.
|
|
5
|
+
"""
|
|
2
6
|
|
|
3
7
|
import stk.extensions as ext
|
|
4
8
|
from stk.notification.models import Notification
|
|
@@ -27,3 +31,11 @@ async def notify(user_id, title, link=None, kind=None, body=None):
|
|
|
27
31
|
await db.commit()
|
|
28
32
|
await broadcast({"type": "notification", **row.to_dict()}, user_id)
|
|
29
33
|
return row
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def init_app(app):
|
|
37
|
+
from stk.notification.views import bp_notification
|
|
38
|
+
|
|
39
|
+
app.extensions["stk.notification"] = True
|
|
40
|
+
app.jinja_env.globals["stk_notifications"] = True
|
|
41
|
+
app.register_blueprint(bp_notification)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import stk.notification
|
|
1
2
|
from stk import Stk
|
|
2
3
|
|
|
3
4
|
from __app__.settings import Config
|
|
4
5
|
|
|
5
6
|
# Each extension is a package with init_app(app); order is precedence.
|
|
6
|
-
extensions = []
|
|
7
|
+
extensions = [stk.notification]
|
|
7
8
|
|
|
8
9
|
app = Stk(__name__, config=Config, extensions=extensions)
|
|
@@ -48,8 +48,10 @@
|
|
|
48
48
|
<!-- Theme Switcher -->
|
|
49
49
|
<theme-switcher></theme-switcher>
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
{% if stk_notifications %}
|
|
52
|
+
<!-- Notifications: only with stk.notification in the app's extensions -->
|
|
52
53
|
<notification-dropdown ref="notifications"></notification-dropdown>
|
|
54
|
+
{% endif %}
|
|
53
55
|
|
|
54
56
|
<!-- Connection Status -->
|
|
55
57
|
<v-tooltip :text="wsConnected ? 'Real-time connected' : 'Reconnecting...'" location="bottom">
|
|
@@ -5,6 +5,7 @@ import unittest
|
|
|
5
5
|
from quart_security import hash_password
|
|
6
6
|
|
|
7
7
|
import stk.extensions as ext
|
|
8
|
+
import stk.notification
|
|
8
9
|
from stk.app import Stk
|
|
9
10
|
from stk.extensions import Base
|
|
10
11
|
from stk.notification import notify
|
|
@@ -14,7 +15,7 @@ from stk.user.models import User
|
|
|
14
15
|
|
|
15
16
|
class NotificationTests(unittest.IsolatedAsyncioTestCase):
|
|
16
17
|
async def asyncSetUp(self):
|
|
17
|
-
self.app = Stk(__name__, config=TestingConfig)
|
|
18
|
+
self.app = Stk(__name__, config=TestingConfig, extensions=[stk.notification])
|
|
18
19
|
async with ext.engine.begin() as conn:
|
|
19
20
|
await conn.run_sync(Base.metadata.create_all)
|
|
20
21
|
async with ext.async_session_factory() as db:
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""An app route that repeats a core one is never reached; verify must say so."""
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import io
|
|
5
|
+
import unittest
|
|
6
|
+
|
|
7
|
+
from quart import Blueprint
|
|
8
|
+
|
|
9
|
+
import stk.notification
|
|
10
|
+
from stk import checks
|
|
11
|
+
from stk.app import Stk
|
|
12
|
+
from stk.checks import check_unique_routes
|
|
13
|
+
from stk.settings import TestingConfig
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class RouteCollisionTest(unittest.IsolatedAsyncioTestCase):
|
|
17
|
+
async def test_core_routes_are_unique(self):
|
|
18
|
+
app = Stk(__name__, config=TestingConfig, extensions=[stk.notification])
|
|
19
|
+
with contextlib.redirect_stdout(io.StringIO()):
|
|
20
|
+
self.assertTrue(await check_unique_routes(app))
|
|
21
|
+
|
|
22
|
+
async def test_an_app_route_shadowed_by_a_core_route_fails(self):
|
|
23
|
+
app = Stk(__name__, config=TestingConfig, extensions=[stk.notification])
|
|
24
|
+
inbox = Blueprint("inbox", __name__)
|
|
25
|
+
|
|
26
|
+
@inbox.get("/api/notifications")
|
|
27
|
+
async def own_notifications():
|
|
28
|
+
return {"items": []}
|
|
29
|
+
|
|
30
|
+
app.register_blueprint(inbox)
|
|
31
|
+
with contextlib.redirect_stdout(io.StringIO()):
|
|
32
|
+
self.assertFalse(await check_unique_routes(app))
|
|
33
|
+
detail = checks.RESULTS[-1]["detail"]
|
|
34
|
+
self.assertIn("GET /api/notifications", detail)
|
|
35
|
+
self.assertIn("inbox.own_notifications", detail)
|
|
36
|
+
|
|
37
|
+
async def test_without_stk_notification_the_app_owns_the_path(self):
|
|
38
|
+
app = Stk(__name__, config=TestingConfig)
|
|
39
|
+
rules = {r.rule for r in app.url_map.iter_rules()}
|
|
40
|
+
self.assertNotIn("/api/notifications", rules)
|
|
41
|
+
self.assertNotIn("stk_notifications", app.jinja_env.globals)
|
|
@@ -321,7 +321,7 @@ class InitAndNewTests(unittest.TestCase):
|
|
|
321
321
|
ast.parse((self.app / "acme" / "widget" / name).read_text())
|
|
322
322
|
app_module = (self.app / "acme" / "app.py").read_text()
|
|
323
323
|
self.assertIn("from acme import gadget, widget", app_module)
|
|
324
|
-
self.assertIn("extensions = [widget, gadget]", app_module)
|
|
324
|
+
self.assertIn("extensions = [stk.notification, widget, gadget]", app_module)
|
|
325
325
|
|
|
326
326
|
def test_new_starts_the_app_lineage_after_stk(self):
|
|
327
327
|
(revision,) = (self.app / "acme" / "migrations").glob("*.py")
|
|
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
|