tina4-python 3.13.7__tar.gz → 3.13.9__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.
- {tina4_python-3.13.7 → tina4_python-3.13.9}/PKG-INFO +1 -1
- {tina4_python-3.13.7 → tina4_python-3.13.9}/pyproject.toml +1 -1
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/__init__.py +1 -1
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/ai/__init__.py +132 -3
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/database/postgres.py +81 -8
- {tina4_python-3.13.7 → tina4_python-3.13.9}/.gitignore +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/README.md +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/CLAUDE.md +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/HtmlElement.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/Testing.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/api/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/auth/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/cache/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/cli/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/container/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/core/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/core/cache.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/core/constants.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/core/events.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/core/middleware.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/core/rate_limiter.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/core/request.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/core/response.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/core/router.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/core/server.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/crud/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/database/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/database/adapter.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/database/connection.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/database/firebird.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/database/mongodb.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/database/mssql.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/database/mysql.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/database/odbc.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/database/sqlite.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/debug/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/debug/error_overlay.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/dev_admin/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/dev_admin/metrics.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/dev_admin/plan.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/dev_admin/project_index.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/docs.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/dotenv/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/env.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/frond/FROND.md +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/frond/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/frond/engine.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/auth/meta.json +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/database/meta.json +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/database/src/routes/api/gallery_db.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/error-overlay/meta.json +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/error-overlay/src/routes/api/gallery_crash.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/orm/meta.json +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/orm/src/orm/Product.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/orm/src/routes/api/gallery_products.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/queue/meta.json +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/queue/src/routes/api/gallery_queue.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/rest-api/meta.json +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/rest-api/src/routes/api/gallery_hello.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/templates/meta.json +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/templates/src/routes/gallery_page.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/templates/src/templates/gallery_page.twig +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/graphql/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/i18n/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/mcp/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/mcp/protocol.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/mcp/tools.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/messenger/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/migration/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/migration/runner.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/orm/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/orm/fields.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/orm/model.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/public/__feedback/widget.js +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/public/css/tina4.css +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/public/css/tina4.min.css +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/public/favicon.ico +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/public/images/logo.svg +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/public/images/tina4-logo-icon.webp +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/public/js/frond.js +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/public/js/frond.min.js +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/public/js/tina4-dev-admin.js +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/public/js/tina4-dev-admin.min.js +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/public/js/tina4.min.js +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/public/js/tina4js.min.js +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/public/swagger/index.html +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/public/swagger/oauth2-redirect.html +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/query_builder/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/queue/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/queue/job.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/queue/kafka_backend.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/queue/lite_backend.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/queue/mongo_backend.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/queue/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/queue_backends/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/queue_backends/kafka_backend.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/queue_backends/mongo_backend.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/queue_backends/rabbitmq_backend.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/_alerts.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/_badges.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/_buttons.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/_cards.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/_forms.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/_grid.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/_modals.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/_nav.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/_reset.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/_tables.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/_typography.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/_utilities.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/_variables.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/base.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/colors.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/scss/tina4css/tina4.scss +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/seeder/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/service/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/session/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/session_handlers/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/session_handlers/mongodb_handler.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/session_handlers/redis_handler.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/session_handlers/valkey_handler.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/swagger/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/components/crud.twig +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/docker/distroless/Dockerfile +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/docker/poetry/Dockerfile +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/docker/python/Dockerfile +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/docker/uv/Dockerfile +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/errors/302.twig +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/errors/401.twig +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/errors/403.twig +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/errors/404.twig +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/errors/500.twig +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/errors/502.twig +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/errors/503.twig +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/errors/base.twig +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/frontend/README.md +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/readme.md +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/test/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/test_client/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/af/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/af/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/en/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/en/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/es/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/es/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/fr/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/fr/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/ja/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/ja/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/zh/LC_MESSAGES/messages.mo +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/zh/LC_MESSAGES/messages.po +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/validator/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/websocket/__init__.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/websocket/backplane.py +0 -0
- {tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/wsdl/__init__.py +0 -0
|
@@ -161,6 +161,136 @@ def install_context(root: str = ".", tools: "list[str] | None" = None) -> list[s
|
|
|
161
161
|
return install_selected(root, selection)
|
|
162
162
|
|
|
163
163
|
|
|
164
|
+
# ── v3.13.9: non-destructive context-file writer ───────────────────────────
|
|
165
|
+
#
|
|
166
|
+
# Pre-v3.13.9 the installer wrote a full developer guide to CLAUDE.md
|
|
167
|
+
# (and the other context files) on every run, clobbering whatever the
|
|
168
|
+
# user had put there. Now it writes only a marker-bracketed Tina4 skill
|
|
169
|
+
# block — pointing the assistant at .claude/skills/tina4-*/SKILL.md —
|
|
170
|
+
# and leaves the rest of the file alone.
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _markers_for(context_file: str) -> "tuple[str, str]":
|
|
174
|
+
"""Return ``(start_marker, end_marker)`` for a context file.
|
|
175
|
+
|
|
176
|
+
Markdown files get HTML comment markers (invisible when rendered).
|
|
177
|
+
Rule files (.cursorules, .windsurfrules, .clinerules) use ``#``
|
|
178
|
+
line comments which most rule loaders treat as comments.
|
|
179
|
+
"""
|
|
180
|
+
if context_file.lower().endswith(".md"):
|
|
181
|
+
return ("<!-- tina4-skills:start -->", "<!-- tina4-skills:end -->")
|
|
182
|
+
return ("# tina4-skills:start", "# tina4-skills:end")
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _skill_block(context_file: str) -> str:
|
|
186
|
+
"""Return the marker-bracketed Tina4 skill registration block."""
|
|
187
|
+
start, end = _markers_for(context_file)
|
|
188
|
+
if context_file.lower().endswith(".md"):
|
|
189
|
+
body = (
|
|
190
|
+
"## Tina4 Skills\n\n"
|
|
191
|
+
"When working on this Tina4 project, these skills give the "
|
|
192
|
+
"assistant project-aware behaviour:\n\n"
|
|
193
|
+
"- **tina4-developer** — Read `.claude/skills/tina4-developer/SKILL.md` before building features.\n"
|
|
194
|
+
"- **tina4-js** — Read `.claude/skills/tina4-js/SKILL.md` for frontend work.\n"
|
|
195
|
+
"- **tina4-maintainer** — Read `.claude/skills/tina4-maintainer/SKILL.md` for framework-level changes.\n\n"
|
|
196
|
+
"See https://tina4.com for full docs."
|
|
197
|
+
)
|
|
198
|
+
else:
|
|
199
|
+
body = (
|
|
200
|
+
"Tina4 Skills — read these files before working on this project:\n"
|
|
201
|
+
" .claude/skills/tina4-developer/SKILL.md (feature development)\n"
|
|
202
|
+
" .claude/skills/tina4-js/SKILL.md (frontend / tina4-js)\n"
|
|
203
|
+
" .claude/skills/tina4-maintainer/SKILL.md (framework-level changes)\n"
|
|
204
|
+
"Docs: https://tina4.com"
|
|
205
|
+
)
|
|
206
|
+
return f"{start}\n{body}\n{end}"
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _has_markers(existing: str, start: str, end: str) -> bool:
|
|
210
|
+
"""True iff both start and end markers appear in order."""
|
|
211
|
+
s_idx = existing.find(start)
|
|
212
|
+
if s_idx == -1:
|
|
213
|
+
return False
|
|
214
|
+
return existing.find(end, s_idx + len(start)) != -1
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _replace_marker_block(existing: str, block: str, start: str, end: str) -> str:
|
|
218
|
+
"""Replace the bracketed block in ``existing`` with ``block``."""
|
|
219
|
+
s_idx = existing.find(start)
|
|
220
|
+
if s_idx == -1:
|
|
221
|
+
return existing.rstrip() + "\n\n" + block + "\n"
|
|
222
|
+
e_idx = existing.find(end, s_idx + len(start))
|
|
223
|
+
if e_idx == -1:
|
|
224
|
+
return existing.rstrip() + "\n\n" + block + "\n"
|
|
225
|
+
before = existing[:s_idx].rstrip()
|
|
226
|
+
after = existing[e_idx + len(end):].lstrip("\n")
|
|
227
|
+
glue_before = "\n\n" if before else ""
|
|
228
|
+
glue_after = "\n" + after if after else "\n"
|
|
229
|
+
return f"{before}{glue_before}{block}{glue_after}"
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# Headers the pre-v3.13.9 installer wrote at the top of CLAUDE.md.
|
|
233
|
+
# Detecting these lets us migrate cleanly off the old clobber install.
|
|
234
|
+
_OLD_FRAMEWORK_HEADERS = (
|
|
235
|
+
"# Tina4 Python",
|
|
236
|
+
"# Tina4 PHP",
|
|
237
|
+
"# Tina4 Ruby",
|
|
238
|
+
"# CLAUDE.md — AI Developer Guide for tina4-nodejs",
|
|
239
|
+
"# CLAUDE.md - AI Developer Guide for tina4-nodejs",
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _looks_like_old_framework_install(existing: str) -> bool:
|
|
244
|
+
"""True if the file starts with a header the pre-v3.13.9 installer
|
|
245
|
+
wrote. Used to migrate one-time off the old clobber-style install."""
|
|
246
|
+
head = existing.lstrip()[:400]
|
|
247
|
+
return any(head.startswith(m) for m in _OLD_FRAMEWORK_HEADERS)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def _write_or_merge(context_path: "Path", context_file: str, framework_guide: str) -> str:
|
|
251
|
+
"""Write the context file non-destructively. Returns a human-readable
|
|
252
|
+
action verb for the caller's log line.
|
|
253
|
+
|
|
254
|
+
Four branches:
|
|
255
|
+
1. Doesn't exist → write framework guide + skill block
|
|
256
|
+
2. Has markers → refresh just the skill block (idempotent)
|
|
257
|
+
3. Old header → migrate: replace old dump with new guide + block
|
|
258
|
+
4. User content → append the skill block, preserve everything else
|
|
259
|
+
"""
|
|
260
|
+
block = _skill_block(context_file)
|
|
261
|
+
start, end = _markers_for(context_file)
|
|
262
|
+
|
|
263
|
+
if not context_path.exists():
|
|
264
|
+
context_path.write_text(
|
|
265
|
+
framework_guide.rstrip() + "\n\n" + block + "\n",
|
|
266
|
+
encoding="utf-8",
|
|
267
|
+
)
|
|
268
|
+
return "Installed"
|
|
269
|
+
|
|
270
|
+
existing = context_path.read_text(encoding="utf-8")
|
|
271
|
+
if _has_markers(existing, start, end):
|
|
272
|
+
new_content = _replace_marker_block(existing, block, start, end)
|
|
273
|
+
context_path.write_text(new_content, encoding="utf-8")
|
|
274
|
+
return "Refreshed skill block in"
|
|
275
|
+
|
|
276
|
+
if _looks_like_old_framework_install(existing):
|
|
277
|
+
head = existing.lstrip()
|
|
278
|
+
preamble = existing[: len(existing) - len(head)]
|
|
279
|
+
new_content = (
|
|
280
|
+
(preamble.rstrip() + "\n\n" if preamble.strip() else "")
|
|
281
|
+
+ framework_guide.rstrip()
|
|
282
|
+
+ "\n\n"
|
|
283
|
+
+ block
|
|
284
|
+
+ "\n"
|
|
285
|
+
)
|
|
286
|
+
context_path.write_text(new_content, encoding="utf-8")
|
|
287
|
+
return "Migrated (replaced old framework dump in)"
|
|
288
|
+
|
|
289
|
+
new_content = existing.rstrip() + "\n\n" + block + "\n"
|
|
290
|
+
context_path.write_text(new_content, encoding="utf-8")
|
|
291
|
+
return "Appended skill block to"
|
|
292
|
+
|
|
293
|
+
|
|
164
294
|
def _install_for_tool(root: Path, tool: dict, context: str) -> list[str]:
|
|
165
295
|
"""Install context file for a single tool."""
|
|
166
296
|
created = []
|
|
@@ -171,9 +301,8 @@ def _install_for_tool(root: Path, tool: dict, context: str) -> list[str]:
|
|
|
171
301
|
(root / tool["config_dir"]).mkdir(parents=True, exist_ok=True)
|
|
172
302
|
context_path.parent.mkdir(parents=True, exist_ok=True)
|
|
173
303
|
|
|
174
|
-
#
|
|
175
|
-
context_path
|
|
176
|
-
action = "Updated" if context_path.exists() else "Installed"
|
|
304
|
+
# v3.13.9: non-destructive write — see _write_or_merge below.
|
|
305
|
+
action = _write_or_merge(context_path, tool["context_file"], context)
|
|
177
306
|
rel = str(context_path.relative_to(root))
|
|
178
307
|
created.append(rel)
|
|
179
308
|
try:
|
|
@@ -49,6 +49,60 @@ class PostgreSQLAdapter(DatabaseAdapter):
|
|
|
49
49
|
else:
|
|
50
50
|
cursor.execute(sql)
|
|
51
51
|
|
|
52
|
+
def _heal_aborted_txn(self):
|
|
53
|
+
"""Issue #46 follow-on (v3.13.8): pre-flight rollback if the
|
|
54
|
+
connection arrived in an aborted-transaction state.
|
|
55
|
+
|
|
56
|
+
v3.13.6 added auto-rollback inside :meth:`_on_query_error` — that
|
|
57
|
+
clears the abort *on* a failure the wrapper catches. But the
|
|
58
|
+
connection can still arrive poisoned from elsewhere:
|
|
59
|
+
|
|
60
|
+
- A boot-time query that failed before request handling started
|
|
61
|
+
(migration probe, ORM ``information_schema`` lookup, etc.)
|
|
62
|
+
- A failure inside an explicit transaction where the user owned
|
|
63
|
+
the rollback but never issued one
|
|
64
|
+
- A direct ``cursor.execute`` call in another adapter method
|
|
65
|
+
(e.g. the ``SELECT lastval()`` SAVEPOINT probe) that managed to
|
|
66
|
+
leave the txn dirty
|
|
67
|
+
|
|
68
|
+
In all those cases the *next* query — even one routed through
|
|
69
|
+
our wrapper — hits ``InFailedSqlTransaction`` immediately, and
|
|
70
|
+
the cascade message buries whatever the original cause was.
|
|
71
|
+
|
|
72
|
+
Healing fix: before executing, ask psycopg2 whether the
|
|
73
|
+
connection is in ``TRANSACTION_STATUS_INERROR`` and rollback if
|
|
74
|
+
so. Only when NOT inside an explicit transaction — explicit txns
|
|
75
|
+
stay under the caller's control.
|
|
76
|
+
|
|
77
|
+
Reported by Schalk (24rent) against v3.13.7.
|
|
78
|
+
"""
|
|
79
|
+
if self._in_transaction or self._conn is None:
|
|
80
|
+
return
|
|
81
|
+
try:
|
|
82
|
+
import psycopg2.extensions as _ext
|
|
83
|
+
status = self._conn.info.transaction_status
|
|
84
|
+
except Exception:
|
|
85
|
+
return
|
|
86
|
+
if status != _ext.TRANSACTION_STATUS_INERROR:
|
|
87
|
+
return
|
|
88
|
+
try:
|
|
89
|
+
self._conn.rollback()
|
|
90
|
+
except Exception:
|
|
91
|
+
# If even the heal-rollback fails, fall through — the next
|
|
92
|
+
# cursor.execute will raise with the real psycopg2 error and
|
|
93
|
+
# _on_query_error will pick it up. Better than masking.
|
|
94
|
+
return
|
|
95
|
+
try:
|
|
96
|
+
from tina4_python.debug import Log
|
|
97
|
+
Log.warning(
|
|
98
|
+
"PostgreSQL connection arrived in aborted-transaction "
|
|
99
|
+
"state — issued pre-flight ROLLBACK so the next query "
|
|
100
|
+
"starts clean. Look back in the log for the original "
|
|
101
|
+
"PostgreSQL query failed entry."
|
|
102
|
+
)
|
|
103
|
+
except Exception:
|
|
104
|
+
pass
|
|
105
|
+
|
|
52
106
|
def _exec_with_handling(self, cursor, sql: str, params=None):
|
|
53
107
|
"""Run a query through :meth:`_safe_execute` and surface failures.
|
|
54
108
|
|
|
@@ -59,18 +113,23 @@ class PostgreSQLAdapter(DatabaseAdapter):
|
|
|
59
113
|
block``, *far* from the actual cause. Without framework-side
|
|
60
114
|
logging, the original SQL + error never reach the user.
|
|
61
115
|
|
|
62
|
-
|
|
116
|
+
This method:
|
|
63
117
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
118
|
+
0. (v3.13.8) Pre-flight heal — if the connection arrived in an
|
|
119
|
+
aborted-transaction state from anywhere outside this wrapper,
|
|
120
|
+
rollback first so this query starts on a clean txn.
|
|
121
|
+
1. Logs failures via ``Log.error`` with SQL+params context
|
|
122
|
+
2. Stores them on ``self.last_error``
|
|
123
|
+
3. Auto-rollbacks on failure when NOT inside an explicit
|
|
124
|
+
transaction (so the next caller gets a clean connection)
|
|
68
125
|
4. Re-raises so callers still see the failure
|
|
69
126
|
|
|
70
|
-
Inside an explicit transaction we do NOT rollback — the
|
|
71
|
-
that transaction (could be running a SAVEPOINT/retry
|
|
72
|
-
still log + store ``last_error`` so they can
|
|
127
|
+
Inside an explicit transaction we do NOT auto-rollback — the
|
|
128
|
+
user owns that transaction (could be running a SAVEPOINT/retry
|
|
129
|
+
pattern). We still log + store ``last_error`` so they can
|
|
130
|
+
diagnose, and the heal step also defers to explicit txns.
|
|
73
131
|
"""
|
|
132
|
+
self._heal_aborted_txn()
|
|
74
133
|
try:
|
|
75
134
|
self._safe_execute(cursor, sql, params)
|
|
76
135
|
except Exception as e:
|
|
@@ -208,6 +267,12 @@ class PostgreSQLAdapter(DatabaseAdapter):
|
|
|
208
267
|
|
|
209
268
|
sql = self._translate_sql(sql)
|
|
210
269
|
|
|
270
|
+
# v3.13.8: heal first so the COUNT probe below doesn't open on a
|
|
271
|
+
# poisoned connection — the probe uses raw _safe_execute (it's
|
|
272
|
+
# allowed to fail without polluting last_error) so it bypasses
|
|
273
|
+
# _exec_with_handling's heal step.
|
|
274
|
+
self._heal_aborted_txn()
|
|
275
|
+
|
|
211
276
|
cursor = self._conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
|
|
212
277
|
|
|
213
278
|
# Count total rows
|
|
@@ -217,6 +282,14 @@ class PostgreSQLAdapter(DatabaseAdapter):
|
|
|
217
282
|
total = cursor.fetchone()["cnt"]
|
|
218
283
|
except Exception:
|
|
219
284
|
total = 0
|
|
285
|
+
# If the probe failed because the connection arrived (or
|
|
286
|
+
# became) aborted, roll back so the real pagination query
|
|
287
|
+
# below sees a clean txn. Without this we'd cascade.
|
|
288
|
+
if not self._in_transaction and self._conn is not None:
|
|
289
|
+
try:
|
|
290
|
+
self._conn.rollback()
|
|
291
|
+
except Exception:
|
|
292
|
+
pass
|
|
220
293
|
|
|
221
294
|
# Apply pagination — the real query, so use the error-handling
|
|
222
295
|
# wrapper. The count probe above is best-effort and stays on
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/gallery/auth/src/routes/api/gallery_auth.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/public/swagger/oauth2-redirect.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/session_handlers/mongodb_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/templates/docker/distroless/Dockerfile
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/af/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/af/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/en/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/en/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/es/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/es/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/fr/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/fr/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/ja/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/ja/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/zh/LC_MESSAGES/messages.mo
RENAMED
|
File without changes
|
{tina4_python-3.13.7 → tina4_python-3.13.9}/tina4_python/translations/zh/LC_MESSAGES/messages.po
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|