MetaList 0.6.1__tar.gz → 0.6.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (454) hide show
  1. {metalist-0.6.1 → metalist-0.6.3}/PKG-INFO +30 -8
  2. {metalist-0.6.1 → metalist-0.6.3}/README.md +28 -6
  3. {metalist-0.6.1 → metalist-0.6.3}/app/api/middleware/auth.py +2 -0
  4. metalist-0.6.3/app/api/routes/app_updates.py +55 -0
  5. {metalist-0.6.1 → metalist-0.6.3}/app/exception_boundaries.py +12 -0
  6. {metalist-0.6.1 → metalist-0.6.3}/app/https_proxy.py +9 -3
  7. {metalist-0.6.1 → metalist-0.6.3}/app/main.py +2 -0
  8. metalist-0.6.3/app/services/app_update_worker.py +42 -0
  9. metalist-0.6.3/app/services/app_updates.py +190 -0
  10. {metalist-0.6.1 → metalist-0.6.3}/app/services/client_state_service.py +5 -1
  11. {metalist-0.6.1 → metalist-0.6.3}/app/services/namespace_switcher.py +55 -2
  12. {metalist-0.6.1 → metalist-0.6.3}/app/services/self_update.py +21 -18
  13. {metalist-0.6.1 → metalist-0.6.3}/app/services/shell_session_service.py +1 -1
  14. metalist-0.6.3/app/services/update_preflight.py +131 -0
  15. {metalist-0.6.1 → metalist-0.6.3}/app/services/windows_process_control.py +37 -4
  16. {metalist-0.6.1 → metalist-0.6.3}/app/startup_sanity_config.py +3 -0
  17. {metalist-0.6.1 → metalist-0.6.3}/app/static/config/command_palette_tags.json +1 -1
  18. {metalist-0.6.1 → metalist-0.6.3}/app/static/css/main.css +16 -0
  19. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/main.js +3 -0
  20. metalist-0.6.3/app/static/js/modules/app-update-service.js +106 -0
  21. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/auth.js +1 -0
  22. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/command-palette-controller.js +5 -0
  23. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/config.js +1 -0
  24. metalist-0.6.3/app/static/js/modules/modals/version-info-modal.js +313 -0
  25. {metalist-0.6.1 → metalist-0.6.3}/app/version.py +1 -1
  26. {metalist-0.6.1 → metalist-0.6.3}/convert-from-legacy.py +4 -1
  27. {metalist-0.6.1 → metalist-0.6.3}/metalist.egg-info/PKG-INFO +30 -8
  28. {metalist-0.6.1 → metalist-0.6.3}/metalist.egg-info/SOURCES.txt +5 -0
  29. {metalist-0.6.1 → metalist-0.6.3}/pyproject.toml +1 -1
  30. metalist-0.6.1/app/static/js/modules/modals/version-info-modal.js +0 -190
  31. {metalist-0.6.1 → metalist-0.6.3}/MANIFEST.in +0 -0
  32. {metalist-0.6.1 → metalist-0.6.3}/app/__init__.py +0 -0
  33. {metalist-0.6.1 → metalist-0.6.3}/app/api/__init__.py +0 -0
  34. {metalist-0.6.1 → metalist-0.6.3}/app/api/deps.py +0 -0
  35. {metalist-0.6.1 → metalist-0.6.3}/app/api/dev.py +0 -0
  36. {metalist-0.6.1 → metalist-0.6.3}/app/api/note_requests.py +0 -0
  37. {metalist-0.6.1 → metalist-0.6.3}/app/api/request_auth.py +0 -0
  38. {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/ai.py +0 -0
  39. {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/auth.py +0 -0
  40. {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/backups.py +0 -0
  41. {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/files.py +0 -0
  42. {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/notes.py +0 -0
  43. {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/ontology.py +0 -0
  44. {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/reminders.py +0 -0
  45. {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/remote_images.py +0 -0
  46. {metalist-0.6.1 → metalist-0.6.3}/app/api/routes/test.py +0 -0
  47. {metalist-0.6.1 → metalist-0.6.3}/app/api/transactions.py +0 -0
  48. {metalist-0.6.1 → metalist-0.6.3}/app/api/upload_limits.py +0 -0
  49. {metalist-0.6.1 → metalist-0.6.3}/app/config.py +0 -0
  50. {metalist-0.6.1 → metalist-0.6.3}/app/data_directory.py +0 -0
  51. {metalist-0.6.1 → metalist-0.6.3}/app/db/__init__.py +0 -0
  52. {metalist-0.6.1 → metalist-0.6.3}/app/db/backup_settings_sql.py +0 -0
  53. {metalist-0.6.1 → metalist-0.6.3}/app/db/engine.py +0 -0
  54. {metalist-0.6.1 → metalist-0.6.3}/app/db/file_schema.py +0 -0
  55. {metalist-0.6.1 → metalist-0.6.3}/app/db/file_session.py +0 -0
  56. {metalist-0.6.1 → metalist-0.6.3}/app/db/files_sql.py +0 -0
  57. {metalist-0.6.1 → metalist-0.6.3}/app/db/link_titles_sql.py +0 -0
  58. {metalist-0.6.1 → metalist-0.6.3}/app/db/live_recovery.py +0 -0
  59. {metalist-0.6.1 → metalist-0.6.3}/app/db/migrations.py +0 -0
  60. {metalist-0.6.1 → metalist-0.6.3}/app/db/notes_sql.py +0 -0
  61. {metalist-0.6.1 → metalist-0.6.3}/app/db/ontology_rules_sql.py +0 -0
  62. {metalist-0.6.1 → metalist-0.6.3}/app/db/reminders_sql.py +0 -0
  63. {metalist-0.6.1 → metalist-0.6.3}/app/db/retire_sounds.py +0 -0
  64. {metalist-0.6.1 → metalist-0.6.3}/app/db/schema.py +0 -0
  65. {metalist-0.6.1 → metalist-0.6.3}/app/db/search_history_sql.py +0 -0
  66. {metalist-0.6.1 → metalist-0.6.3}/app/db/session.py +0 -0
  67. {metalist-0.6.1 → metalist-0.6.3}/app/db/settings_sql.py +0 -0
  68. {metalist-0.6.1 → metalist-0.6.3}/app/db/tab_state_sql.py +0 -0
  69. {metalist-0.6.1 → metalist-0.6.3}/app/db/version.py +0 -0
  70. {metalist-0.6.1 → metalist-0.6.3}/app/encryption_audit.py +0 -0
  71. {metalist-0.6.1 → metalist-0.6.3}/app/models/commands.py +0 -0
  72. {metalist-0.6.1 → metalist-0.6.3}/app/models/database.py +0 -0
  73. {metalist-0.6.1 → metalist-0.6.3}/app/models/entities.py +0 -0
  74. {metalist-0.6.1 → metalist-0.6.3}/app/models/enums.py +0 -0
  75. {metalist-0.6.1 → metalist-0.6.3}/app/models/linked_list.py +0 -0
  76. {metalist-0.6.1 → metalist-0.6.3}/app/models/list_operations.py +0 -0
  77. {metalist-0.6.1 → metalist-0.6.3}/app/models/list_traversal.py +0 -0
  78. {metalist-0.6.1 → metalist-0.6.3}/app/models/note_crud.py +0 -0
  79. {metalist-0.6.1 → metalist-0.6.3}/app/models/utils.py +0 -0
  80. {metalist-0.6.1 → metalist-0.6.3}/app/presentation/__init__.py +0 -0
  81. {metalist-0.6.1 → metalist-0.6.3}/app/presentation/render/__init__.py +0 -0
  82. {metalist-0.6.1 → metalist-0.6.3}/app/presentation/render/note_renderer.py +0 -0
  83. {metalist-0.6.1 → metalist-0.6.3}/app/presentation/templates.py +0 -0
  84. {metalist-0.6.1 → metalist-0.6.3}/app/security/authenticated_logging.py +0 -0
  85. {metalist-0.6.1 → metalist-0.6.3}/app/security/encryption.py +0 -0
  86. {metalist-0.6.1 → metalist-0.6.3}/app/security/http_headers.py +0 -0
  87. {metalist-0.6.1 → metalist-0.6.3}/app/security/note_html.py +0 -0
  88. {metalist-0.6.1 → metalist-0.6.3}/app/security/request_boundary.py +0 -0
  89. {metalist-0.6.1 → metalist-0.6.3}/app/security/sensitive_cache.py +0 -0
  90. {metalist-0.6.1 → metalist-0.6.3}/app/security/sensitive_logging.py +0 -0
  91. {metalist-0.6.1 → metalist-0.6.3}/app/security/shell_execution.py +0 -0
  92. {metalist-0.6.1 → metalist-0.6.3}/app/security/validation_errors.py +0 -0
  93. {metalist-0.6.1 → metalist-0.6.3}/app/server_runtime.py +0 -0
  94. {metalist-0.6.1 → metalist-0.6.3}/app/services/__init__.py +0 -0
  95. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/__init__.py +0 -0
  96. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/actions.py +0 -0
  97. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/cloud_privacy.py +0 -0
  98. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/context.py +0 -0
  99. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/evidence_serialization.py +0 -0
  100. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/inference.py +0 -0
  101. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/investigation.py +0 -0
  102. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/model_policy.py +0 -0
  103. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/ollama_inference.py +0 -0
  104. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/openai_cost_tracking.py +0 -0
  105. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/openai_inference.py +0 -0
  106. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/permissions.py +0 -0
  107. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/prompt_settings.py +0 -0
  108. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/prompts/__init__.py +0 -0
  109. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/prompts/final-response.md +0 -0
  110. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/prompts/system.md +0 -0
  111. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/prompts/tool-result.md +0 -0
  112. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/retrieval_settings.py +0 -0
  113. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/runtime.py +0 -0
  114. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/scope.py +0 -0
  115. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/skill_settings.py +0 -0
  116. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/skills/__init__.py +0 -0
  117. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/skills/scoped-investigation.md +0 -0
  118. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/tagging.py +0 -0
  119. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/tagging_run.py +0 -0
  120. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/token_estimation.py +0 -0
  121. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/tools.py +0 -0
  122. {metalist-0.6.1 → metalist-0.6.3}/app/services/agent/trace.py +0 -0
  123. {metalist-0.6.1 → metalist-0.6.3}/app/services/ai_chat.py +0 -0
  124. {metalist-0.6.1 → metalist-0.6.3}/app/services/ai_chat_rendering.py +0 -0
  125. {metalist-0.6.1 → metalist-0.6.3}/app/services/ai_chat_stream.py +0 -0
  126. {metalist-0.6.1 → metalist-0.6.3}/app/services/auth.py +0 -0
  127. {metalist-0.6.1 → metalist-0.6.3}/app/services/auth_cache_state.py +0 -0
  128. {metalist-0.6.1 → metalist-0.6.3}/app/services/auth_service.py +0 -0
  129. {metalist-0.6.1 → metalist-0.6.3}/app/services/backlink_index.py +0 -0
  130. {metalist-0.6.1 → metalist-0.6.3}/app/services/backlinks.py +0 -0
  131. {metalist-0.6.1 → metalist-0.6.3}/app/services/backup_service.py +0 -0
  132. {metalist-0.6.1 → metalist-0.6.3}/app/services/backup_settings_service.py +0 -0
  133. {metalist-0.6.1 → metalist-0.6.3}/app/services/bulk_operation.py +0 -0
  134. {metalist-0.6.1 → metalist-0.6.3}/app/services/content_cache.py +0 -0
  135. {metalist-0.6.1 → metalist-0.6.3}/app/services/content_formatting.py +0 -0
  136. {metalist-0.6.1 → metalist-0.6.3}/app/services/diagnostics.py +0 -0
  137. {metalist-0.6.1 → metalist-0.6.3}/app/services/embedded_references.py +0 -0
  138. {metalist-0.6.1 → metalist-0.6.3}/app/services/encryption.py +0 -0
  139. {metalist-0.6.1 → metalist-0.6.3}/app/services/exception_capture.py +0 -0
  140. {metalist-0.6.1 → metalist-0.6.3}/app/services/file_registry.py +0 -0
  141. {metalist-0.6.1 → metalist-0.6.3}/app/services/file_storage.py +0 -0
  142. {metalist-0.6.1 → metalist-0.6.3}/app/services/footnote_layout.py +0 -0
  143. {metalist-0.6.1 → metalist-0.6.3}/app/services/footnote_rendering.py +0 -0
  144. {metalist-0.6.1 → metalist-0.6.3}/app/services/hierarchy.py +0 -0
  145. {metalist-0.6.1 → metalist-0.6.3}/app/services/html_export.py +0 -0
  146. {metalist-0.6.1 → metalist-0.6.3}/app/services/html_unformatting.py +0 -0
  147. {metalist-0.6.1 → metalist-0.6.3}/app/services/hydration_state.py +0 -0
  148. {metalist-0.6.1 → metalist-0.6.3}/app/services/image_size_formatting.py +0 -0
  149. {metalist-0.6.1 → metalist-0.6.3}/app/services/inline_image_occurrences.py +0 -0
  150. {metalist-0.6.1 → metalist-0.6.3}/app/services/input_errors.py +0 -0
  151. {metalist-0.6.1 → metalist-0.6.3}/app/services/integrity.py +0 -0
  152. {metalist-0.6.1 → metalist-0.6.3}/app/services/latex_rendering.py +0 -0
  153. {metalist-0.6.1 → metalist-0.6.3}/app/services/link_titles.py +0 -0
  154. {metalist-0.6.1 → metalist-0.6.3}/app/services/login_rate_limit.py +0 -0
  155. {metalist-0.6.1 → metalist-0.6.3}/app/services/maintenance_mode.py +0 -0
  156. {metalist-0.6.1 → metalist-0.6.3}/app/services/managed_ollama_runtime.py +0 -0
  157. {metalist-0.6.1 → metalist-0.6.3}/app/services/markdown_rendering.py +0 -0
  158. {metalist-0.6.1 → metalist-0.6.3}/app/services/namespace_deletion_jobs.py +0 -0
  159. {metalist-0.6.1 → metalist-0.6.3}/app/services/namespace_deletion_worker.py +0 -0
  160. {metalist-0.6.1 → metalist-0.6.3}/app/services/namespace_rename_jobs.py +0 -0
  161. {metalist-0.6.1 → metalist-0.6.3}/app/services/namespace_rename_worker.py +0 -0
  162. {metalist-0.6.1 → metalist-0.6.3}/app/services/namespace_runtime_guard.py +0 -0
  163. {metalist-0.6.1 → metalist-0.6.3}/app/services/note_fullscreen.py +0 -0
  164. {metalist-0.6.1 → metalist-0.6.3}/app/services/note_image_tags.py +0 -0
  165. {metalist-0.6.1 → metalist-0.6.3}/app/services/note_ordering.py +0 -0
  166. {metalist-0.6.1 → metalist-0.6.3}/app/services/note_store.py +0 -0
  167. {metalist-0.6.1 → metalist-0.6.3}/app/services/ollama_provider.py +0 -0
  168. {metalist-0.6.1 → metalist-0.6.3}/app/services/ontology_rules_store.py +0 -0
  169. {metalist-0.6.1 → metalist-0.6.3}/app/services/openai_credentials.py +0 -0
  170. {metalist-0.6.1 → metalist-0.6.3}/app/services/password_note_fields.py +0 -0
  171. {metalist-0.6.1 → metalist-0.6.3}/app/services/reference_presentation.py +0 -0
  172. {metalist-0.6.1 → metalist-0.6.3}/app/services/reminders.py +0 -0
  173. {metalist-0.6.1 → metalist-0.6.3}/app/services/remote_image_proxy.py +0 -0
  174. {metalist-0.6.1 → metalist-0.6.3}/app/services/request_recovery.py +0 -0
  175. {metalist-0.6.1 → metalist-0.6.3}/app/services/resource_limits.py +0 -0
  176. {metalist-0.6.1 → metalist-0.6.3}/app/services/root_sorting.py +0 -0
  177. {metalist-0.6.1 → metalist-0.6.3}/app/services/runtime_generation.py +0 -0
  178. {metalist-0.6.1 → metalist-0.6.3}/app/services/runtime_hardening.py +0 -0
  179. {metalist-0.6.1 → metalist-0.6.3}/app/services/runtime_lock.py +0 -0
  180. {metalist-0.6.1 → metalist-0.6.3}/app/services/search_history.py +0 -0
  181. {metalist-0.6.1 → metalist-0.6.3}/app/services/search_history_storage.py +0 -0
  182. {metalist-0.6.1 → metalist-0.6.3}/app/services/search_index.py +0 -0
  183. {metalist-0.6.1 → metalist-0.6.3}/app/services/search_query.py +0 -0
  184. {metalist-0.6.1 → metalist-0.6.3}/app/services/search_text.py +0 -0
  185. {metalist-0.6.1 → metalist-0.6.3}/app/services/selected_text_tag.py +0 -0
  186. {metalist-0.6.1 → metalist-0.6.3}/app/services/session_timeout_service.py +0 -0
  187. {metalist-0.6.1 → metalist-0.6.3}/app/services/snapshot.py +0 -0
  188. {metalist-0.6.1 → metalist-0.6.3}/app/services/store.py +0 -0
  189. {metalist-0.6.1 → metalist-0.6.3}/app/services/structured_note_renderers.py +0 -0
  190. {metalist-0.6.1 → metalist-0.6.3}/app/services/sync.py +0 -0
  191. {metalist-0.6.1 → metalist-0.6.3}/app/services/sync_state.py +0 -0
  192. {metalist-0.6.1 → metalist-0.6.3}/app/services/tab_state.py +0 -0
  193. {metalist-0.6.1 → metalist-0.6.3}/app/services/tag_case.py +0 -0
  194. {metalist-0.6.1 → metalist-0.6.3}/app/services/tag_ontology.py +0 -0
  195. {metalist-0.6.1 → metalist-0.6.3}/app/services/tag_rename.py +0 -0
  196. {metalist-0.6.1 → metalist-0.6.3}/app/services/tag_suggestions.py +0 -0
  197. {metalist-0.6.1 → metalist-0.6.3}/app/services/tag_term_matching.py +0 -0
  198. {metalist-0.6.1 → metalist-0.6.3}/app/services/test_reset.py +0 -0
  199. {metalist-0.6.1 → metalist-0.6.3}/app/services/tokens.py +0 -0
  200. {metalist-0.6.1 → metalist-0.6.3}/app/services/undo_state.py +0 -0
  201. {metalist-0.6.1 → metalist-0.6.3}/app/services/update_backups.py +0 -0
  202. {metalist-0.6.1 → metalist-0.6.3}/app/services/view_cache.py +0 -0
  203. {metalist-0.6.1 → metalist-0.6.3}/app/services/view_diff.py +0 -0
  204. {metalist-0.6.1 → metalist-0.6.3}/app/services/view_state.py +0 -0
  205. {metalist-0.6.1 → metalist-0.6.3}/app/startup_environment.py +0 -0
  206. {metalist-0.6.1 → metalist-0.6.3}/app/startup_js_sanity.py +0 -0
  207. {metalist-0.6.1 → metalist-0.6.3}/app/startup_sanity.py +0 -0
  208. {metalist-0.6.1 → metalist-0.6.3}/app/static/css/floating-notes.css +0 -0
  209. {metalist-0.6.1 → metalist-0.6.3}/app/static/css/forms.css +0 -0
  210. {metalist-0.6.1 → metalist-0.6.3}/app/static/img/favicon.ico +0 -0
  211. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/locked.js +0 -0
  212. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/activity-tracker.js +0 -0
  213. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/agent-prompt-service.js +0 -0
  214. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/agent-retrieval-settings.js +0 -0
  215. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/ai-agent-debug-view.js +0 -0
  216. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/ai-chat-api.js +0 -0
  217. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/ai-chat-panel-controller.js +0 -0
  218. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/ai-chat-panel-service.js +0 -0
  219. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/ai-thinking-level-service.js +0 -0
  220. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/bulk-elapsed-clock.js +0 -0
  221. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/bulk-proposal-ui.js +0 -0
  222. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/cloud-privacy-policy.js +0 -0
  223. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/ai-chat/proposal-menu.js +0 -0
  224. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/api-client.js +0 -0
  225. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/api-failure-classification-service.js +0 -0
  226. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/application-state.js +0 -0
  227. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/async-result.js +0 -0
  228. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/client-state-api.js +0 -0
  229. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/client-state-migration.js +0 -0
  230. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/activation-auth-policy.js +0 -0
  231. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/backup-command-availability.js +0 -0
  232. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/click-activation-service.js +0 -0
  233. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/endpoint-registry.js +0 -0
  234. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/note-layout-preferences.js +0 -0
  235. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/preferences-store.js +0 -0
  236. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/tag-config-loader.js +0 -0
  237. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/command-palette/usage-store.js +0 -0
  238. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/connectivity-monitor.js +0 -0
  239. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/context-menu/context-menu-registry.js +0 -0
  240. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/context-menu/context-menu-service.js +0 -0
  241. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/context-menu/context-menu-target-service.js +0 -0
  242. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/context-menu/input-caret-service.js +0 -0
  243. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/disconnect-editing-state-service.js +0 -0
  244. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/dom-utils-fixed.js +0 -0
  245. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/dom-utils.js +0 -0
  246. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/editor-commands.js +0 -0
  247. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/editor-selection.js +0 -0
  248. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/editor-toolbar.js +0 -0
  249. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/elapsed-time.js +0 -0
  250. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/error-handler.js +0 -0
  251. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/error-overlay.js +0 -0
  252. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/expected-errors.js +0 -0
  253. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/location-flags.js +0 -0
  254. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/login-namespace-picker.js +0 -0
  255. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/maintenance.js +0 -0
  256. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/agent-prompt-editor-modal.js +0 -0
  257. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/ai-agent-settings-modal.js +0 -0
  258. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/alphabetize-root-notes-modal.js +0 -0
  259. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/backup-restore-modal.js +0 -0
  260. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/backup-result-modal.js +0 -0
  261. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/backup-retention-modal.js +0 -0
  262. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/backup-settings-modal.js +0 -0
  263. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/base-modal.js +0 -0
  264. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/confirmation-modal.js +0 -0
  265. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/delete-namespace-modal.js +0 -0
  266. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/delete-namespace-validation.js +0 -0
  267. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/help-modal.js +0 -0
  268. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/namespace-loading-page.js +0 -0
  269. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/namespace-modals.js +0 -0
  270. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/namespace-ports-profile.js +0 -0
  271. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/namespace-rename-validation.js +0 -0
  272. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/note-layout-appearance-modal.js +0 -0
  273. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/ontology-modal.js +0 -0
  274. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/password-modal.js +0 -0
  275. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/prioritize-modal.js +0 -0
  276. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/random-password-modal.js +0 -0
  277. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/reminder-modal.js +0 -0
  278. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/reset-updated-at-modal.js +0 -0
  279. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/search-suggestion-statistics-modal.js +0 -0
  280. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/modals/session-timeout-modal.js +0 -0
  281. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/content-actions.js +0 -0
  282. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/history-actions.js +0 -0
  283. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/note-actions.js +0 -0
  284. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/search-actions.js +0 -0
  285. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/selection-actions.js +0 -0
  286. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/actions/ui-actions.js +0 -0
  287. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/context-menu-events.js +0 -0
  288. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/focus-events.js +0 -0
  289. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/inactivity-events.js +0 -0
  290. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/input-events.js +0 -0
  291. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/keyboard-events.js +0 -0
  292. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/mouse-events.js +0 -0
  293. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/events/search-events.js +0 -0
  294. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/mode-context.js +0 -0
  295. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/mode-logger.js +0 -0
  296. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/mode-manager-controller.js +0 -0
  297. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/add-style-service.js +0 -0
  298. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/animated-scroll-service.js +0 -0
  299. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/backlinks-navigation-service.js +0 -0
  300. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/backlinks-panel-service.js +0 -0
  301. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/clipboard-shortcut-policy-service.js +0 -0
  302. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/collapse-affordance-service.js +0 -0
  303. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/collapse-editing-policy-service.js +0 -0
  304. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/command-gate-service.js +0 -0
  305. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/context-menu-target-service.js +0 -0
  306. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/deselect-selection-state-service.js +0 -0
  307. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/differential-view-service.js +0 -0
  308. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/edit-session-collapse-policy-service.js +0 -0
  309. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/edit-session-collapse-service.js +0 -0
  310. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/embedded-image-service.js +0 -0
  311. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/file-drop-policy-service.js +0 -0
  312. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/file-image-preview-service.js +0 -0
  313. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/file-reference-service.js +0 -0
  314. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/filtered-refresh-selection-service.js +0 -0
  315. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/floating-note-dom.js +0 -0
  316. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/floating-note-layout.js +0 -0
  317. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/floating-note-service.js +0 -0
  318. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/footnote-navigation-service.js +0 -0
  319. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/history-selection-policy-service.js +0 -0
  320. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/history-shortcut-policy-service.js +0 -0
  321. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/html-paste-sanitizer-service.js +0 -0
  322. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/image-context-menu-action-service.js +0 -0
  323. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/image-file-insert-choice-modal-service.js +0 -0
  324. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/infinite-scroll-service.js +0 -0
  325. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/link-context-menu-action-service.js +0 -0
  326. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/link-title-prefetch-service.js +0 -0
  327. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/markdown-render-service.js +0 -0
  328. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/mermaid-render-service.js +0 -0
  329. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-click-target-service.js +0 -0
  330. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-clipboard-write-service.js +0 -0
  331. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-collapse-animation-service.js +0 -0
  332. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-context-menu-action-service.js +0 -0
  333. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-delete-shortcut-policy-service.js +0 -0
  334. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-drag-service.js +0 -0
  335. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-fullscreen-service.js +0 -0
  336. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-reposition-animation-service.js +0 -0
  337. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-split-service.js +0 -0
  338. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/note-timestamp-hover-service.js +0 -0
  339. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/password-clipboard-service.js +0 -0
  340. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/polling-service.js +0 -0
  341. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/reference-source-navigation-service.js +0 -0
  342. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/remote-image-proxy-service.js +0 -0
  343. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/remove-formatting-service.js +0 -0
  344. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/root-date-separator-service.js +0 -0
  345. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/root-sort-indicator-service.js +0 -0
  346. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/root-sort-service.js +0 -0
  347. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/scroll-anchor-service.js +0 -0
  348. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/scroll-restoration-service.js +0 -0
  349. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/scroll-to-top-service.js +0 -0
  350. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-contexts-overlay-service.js +0 -0
  351. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-debounce-service.js +0 -0
  352. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-input-service.js +0 -0
  353. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-interaction-service.js +0 -0
  354. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-redaction-reveal-service.js +0 -0
  355. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-suggestion-statistics-service.js +0 -0
  356. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-suggestion-window-labels-service.js +0 -0
  357. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-suggestion-windows-service.js +0 -0
  358. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-suggestions-service.js +0 -0
  359. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/search-syntax-service.js +0 -0
  360. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/selected-formatting-removal-service.js +0 -0
  361. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/selected-text-tag-service.js +0 -0
  362. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/shell-output-service.js +0 -0
  363. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tab-dom-cache-service.js +0 -0
  364. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tab-drag-service.js +0 -0
  365. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tab-duplication-service.js +0 -0
  366. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tab-state-service.js +0 -0
  367. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tag-bar-service.js +0 -0
  368. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tag-input-selection-service.js +0 -0
  369. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tag-proposal-service.js +0 -0
  370. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tag-suggestions-service.js +0 -0
  371. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/tag-syntax-service.js +0 -0
  372. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/todo-toggle-editing-service.js +0 -0
  373. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/untagged-view-indicator-service.js +0 -0
  374. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/mode-manager/services/view-mode-search-shortcut-service.js +0 -0
  375. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/note-html-sanitizer.js +0 -0
  376. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/password-generator.js +0 -0
  377. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/password-operation-response.js +0 -0
  378. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/password-policy.js +0 -0
  379. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/password-strength.js +0 -0
  380. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/reminder-store.js +0 -0
  381. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/reminder-surface-service.js +0 -0
  382. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/selection-markers.js +0 -0
  383. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/session-auth.js +0 -0
  384. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/tag-token.js +0 -0
  385. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/modules/uuid.js +0 -0
  386. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/namespace-deleted.js +0 -0
  387. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/namespace-renamed.js +0 -0
  388. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/dompurify-3.4.13.LICENSE.txt +0 -0
  389. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/dompurify-3.4.13.min.js +0 -0
  390. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/markdown-it-14.3.2.LICENSE.txt +0 -0
  391. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/markdown-it-14.3.2.min.js +0 -0
  392. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/mermaid-11.16.1.LICENSE.txt +0 -0
  393. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/mermaid-11.16.1.min.js +0 -0
  394. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/zxcvbn-4.4.2.LICENSE.txt +0 -0
  395. {metalist-0.6.1 → metalist-0.6.3}/app/static/js/vendor/zxcvbn-4.4.2.js +0 -0
  396. {metalist-0.6.1 → metalist-0.6.3}/app/static/note-html-policy.json +0 -0
  397. {metalist-0.6.1 → metalist-0.6.3}/app/static/video/login-startup.mp4 +0 -0
  398. {metalist-0.6.1 → metalist-0.6.3}/app/templates/base.html +0 -0
  399. {metalist-0.6.1 → metalist-0.6.3}/app/templates/index.html +0 -0
  400. {metalist-0.6.1 → metalist-0.6.3}/app/templates/locked.html +0 -0
  401. {metalist-0.6.1 → metalist-0.6.3}/app/templates/maintenance.html +0 -0
  402. {metalist-0.6.1 → metalist-0.6.3}/app/templates/namespace_deleted.html +0 -0
  403. {metalist-0.6.1 → metalist-0.6.3}/app/templates/namespace_renamed.html +0 -0
  404. {metalist-0.6.1 → metalist-0.6.3}/app/templates/notes_list.html +0 -0
  405. {metalist-0.6.1 → metalist-0.6.3}/app/upload_limits.py +0 -0
  406. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/__init__.py +0 -0
  407. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/add_selected_text_tag.py +0 -0
  408. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/alphabetize_root_notes.py +0 -0
  409. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/base.py +0 -0
  410. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/bulk_tag_proposals.py +0 -0
  411. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/collapse.py +0 -0
  412. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/copy_note.py +0 -0
  413. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/create_child.py +0 -0
  414. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/create_note.py +0 -0
  415. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/create_sibling.py +0 -0
  416. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/delete_subtree.py +0 -0
  417. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/delete_tag.py +0 -0
  418. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/expand.py +0 -0
  419. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/indent.py +0 -0
  420. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/lock.py +0 -0
  421. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/move.py +0 -0
  422. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/move_to_top.py +0 -0
  423. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/outdent.py +0 -0
  424. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/paste_child.py +0 -0
  425. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/paste_sibling.py +0 -0
  426. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/prefetch_link_titles.py +0 -0
  427. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/prioritize.py +0 -0
  428. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/redo.py +0 -0
  429. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/rename_tag.py +0 -0
  430. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/reset_updated_at.py +0 -0
  431. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/resize_image.py +0 -0
  432. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/run_shell.py +0 -0
  433. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/search_comment_autofill.py +0 -0
  434. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/search_context_tags.py +0 -0
  435. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/set_collapse_bulk.py +0 -0
  436. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/set_collapse_in_context.py +0 -0
  437. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/split_note.py +0 -0
  438. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/tag_proposals.py +0 -0
  439. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/toggle_reference_mode.py +0 -0
  440. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/toggle_todo_done.py +0 -0
  441. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/undo.py +0 -0
  442. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/unformat_content.py +0 -0
  443. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/update_content.py +0 -0
  444. {metalist-0.6.1 → metalist-0.6.3}/app/usecases/view.py +0 -0
  445. {metalist-0.6.1 → metalist-0.6.3}/app/utils/__init__.py +0 -0
  446. {metalist-0.6.1 → metalist-0.6.3}/app/utils/encryption.py +0 -0
  447. {metalist-0.6.1 → metalist-0.6.3}/app/utils/text_utils.py +0 -0
  448. {metalist-0.6.1 → metalist-0.6.3}/main.py +0 -0
  449. {metalist-0.6.1 → metalist-0.6.3}/metalist.egg-info/dependency_links.txt +0 -0
  450. {metalist-0.6.1 → metalist-0.6.3}/metalist.egg-info/entry_points.txt +0 -0
  451. {metalist-0.6.1 → metalist-0.6.3}/metalist.egg-info/requires.txt +0 -0
  452. {metalist-0.6.1 → metalist-0.6.3}/metalist.egg-info/top_level.txt +0 -0
  453. {metalist-0.6.1 → metalist-0.6.3}/scripts/generate-lan-cert.sh +0 -0
  454. {metalist-0.6.1 → metalist-0.6.3}/setup.cfg +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metalist
3
- Version: 0.6.1
3
+ Version: 0.6.3
4
4
  Summary: A minimalist single-user note-taking app with SSR, fast in-memory tree operations, and efficient sync.
5
5
  Project-URL: Homepage, https://github.com/evolvingstuff/metalist
6
6
  Project-URL: Repository, https://github.com/evolvingstuff/metalist
7
7
  Project-URL: Issues, https://github.com/evolvingstuff/metalist/issues
8
- Requires-Python: <3.14,>=3.10
8
+ Requires-Python: <3.15,>=3.10
9
9
  Description-Content-Type: text/markdown
10
10
  Requires-Dist: argon2-cffi==25.1.0
11
11
  Requires-Dist: cryptography==50.0.0
@@ -48,6 +48,20 @@ A minimalist single-user note-taking app focused on server-side rendering (SSR),
48
48
  - Multi-tab search contexts with server-persisted scroll/search state (survives browser restarts)
49
49
  - Manual namespace backups/restores to a user-selected backup folder with retention controls
50
50
 
51
+ ## Changes in 0.6.3
52
+
53
+ - Restored HTTPS connection reuse and aligned the pending accept queue with the existing worker capacity, so a browser’s six-connection startup burst is admitted.
54
+ - Every page load or refresh checks PyPI asynchronously. A newly discovered release shows a dismissible notice linking to Version Info; typing `update` in the menu also finds it.
55
+ - Managed uv installations can update from Version Info, using the existing preflight, verified immutable backups, and namespace restart flow. Source checkouts and unmanaged installations explain why in-app installation is unavailable.
56
+ - Release validation now transfers every installed startup asset through concurrent persistent HTTP/HTTPS connections on all supported platforms and Python versions. Actual Edge startup over LAN-style HTTPS is also required on Windows before publication.
57
+
58
+ ## Changes in 0.6.2
59
+
60
+ - Namespace startup allows two minutes, with progress every five seconds, instead of failing after 12 seconds on a busy machine. `METALIST_STARTUP_TIMEOUT_SECONDS` can increase the allowance. Failed children are stopped and reaped.
61
+ - Updates preserve the running Python interpreter and validate a disposable installation and namespace startup before stopping live servers. uv then installs the tested package/dependency versions from its cache without network access.
62
+ - Python 3.14 is supported. Command-line legacy imports also work when Python lacks Tk's native GUI component.
63
+ - Release validation now includes the real uv update, backups, and multi-namespace restart on Windows, macOS, and Linux across Python 3.10–3.14.
64
+
51
65
  ## Changes in 0.6.1
52
66
 
53
67
  - Removed the unused performance overlay, including its menu option and background state updates, fixing a fatal redundant-state error during note rearranging. Obsolete overlay preferences and command usage are discarded automatically.
@@ -115,15 +129,19 @@ uv tool install metalist
115
129
  metalist
116
130
  ```
117
131
 
118
- After the first installation, update and restart MetaList with one cross-platform command:
132
+ For a persistent uv installation, you can update from inside MetaList: type **update** in the menu, open **Version Info**, and choose **Update to …** when a newer release is available. Each browser load or refresh asks the server to check PyPI asynchronously. A reminder-style notice links to Version Info the first time a newer version is discovered; the announced version is remembered in that namespace's client preferences. A slow or failed check does not block the app. Version Info can retry a failed check.
133
+
134
+ The in-app updater checks the selected release, creates verified backups, and restarts every namespace. Keep Version Info open to follow progress and reload when ready; closing and reopening it in the same page resumes progress. Signing in again may be necessary. Source checkouts and unmanaged installations still check for releases, but show why in-app installation is unavailable. Update job records and logs live under `~/MetaList/update-jobs/` (or the configured data directory). If installation/restart fails after servers stop, inspect the updater output and run `metalist` on the server to restart it; there is no automatic rollback.
135
+
136
+ You can also update and restart MetaList with one cross-platform command:
119
137
  ```bash
120
138
  metalist update
121
139
  ```
122
- The updater checks the installed version against the latest PyPI release first. If MetaList is already current, it reports the installed version and leaves all running namespaces untouched. When an update is available, it checks the installer prerequisites, stops running namespaces, and creates and verifies a new backup of every namespace before installation can begin. Each archive includes the complete notes/settings database, the attachments database when present, and any legacy search-history database. Locked namespaces are backed up with their encrypted data and key metadata intact, without requiring a password.
140
+ The updater checks the installed version against the latest PyPI release first. If MetaList is already current, it reports the installed version and leaves all running namespaces untouched. When an update is available, it checks installer prerequisites and uses uv to install the candidate into a disposable tool environment with the current base Python interpreter. It checks dependencies and starts a temporary namespace to verify HTTP readiness, version, and runtime assets. A failed preflight leaves the current installation and running namespaces unchanged. After preflight succeeds, it stops running namespaces, and creates and verifies a new backup of every namespace before installation can begin. Each archive includes the complete notes/settings database, the attachments database when present, and any legacy search-history database. Locked namespaces are backed up with their encrypted data and key metadata intact, without requiring a password.
123
141
 
124
142
  Backups are saved to `~/MetaList/namespaces/<namespace>/backups/<namespace>-<timestamp>.metalist-backup.tar.gz`; the updater prints each verified path. Existing backups remain unchanged and are not pruned. If any backup fails, the update aborts with the current installation intact; run `metalist` to restart the stopped servers after resolving the failure.
125
143
 
126
- Only after all backups pass does the updater hand off to an external PowerShell process on Windows or `/bin/sh` on macOS/Linux so the installed environment can unlock. It installs the exact version reported by PyPI with a forced cache refresh, launches MetaList again, and reports the installed version (for example, `MetaList updated to v0.5.0.`). This protection requires an installed version containing the backup safeguard and applies to `metalist update`; direct pip/uv install commands do not run it.
144
+ Only after all backups pass does the updater hand off to an external PowerShell process on Windows or `/bin/sh` on macOS/Linux so the installed environment can unlock. It delegates installation to uv, pinning the current base interpreter and the exact tested package/dependency versions, using the cache populated during preflight with network access disabled. It then launches MetaList again, and reports the installed version (for example, `MetaList updated to v0.5.0.`). This protection requires an installed version containing the backup safeguard and applies to `metalist update` and the in-app update action; direct pip/uv install commands do not run it.
127
145
 
128
146
  For pip, users can run `pip install metalist`. For a non-editable local install from this checkout, use `uv pip install .` or `pip install .` instead of the editable command below.
129
147
 
@@ -283,6 +301,10 @@ convert-from-legacy.py --namespace work --input /path/to/legacy-export.json
283
301
 
284
302
  If `--namespace`, `--port`, or `--https-port` are omitted, the import script prompts for them and saves the resulting launch profile inside the target namespace DB. That means a one-time import into `work` can immediately seed later shorthand launches like `metalist work`.
285
303
 
304
+ Namespace startup allows 120 seconds per child, reports progress every five seconds, and stops/reaps a failed child instead of leaving it running after reporting failure. Set `METALIST_STARTUP_TIMEOUT_SECONDS` to a positive number of seconds to allow more time on a heavily loaded machine. A crashed child still fails immediately. This is a startup allowance, not a guarantee that any machine will finish within two minutes.
305
+
306
+ The preflight is a disposable empty namespace; it cannot guarantee startup against every real database or prevent a later disk/process failure. If installation succeeds but real namespace startup fails, the new package remains installed: resolve the reported startup issue and run `metalist` again. No automatic code/database rollback is attempted, and backups remain unchanged.
307
+
286
308
  ### Publishing
287
309
  For the real user-facing install flow:
288
310
  ```bash
@@ -292,15 +314,15 @@ uv tool install metalist
292
314
  metalist
293
315
  ```
294
316
 
295
- This repo now packages itself under the PyPI distribution name `metalist`. Current releases support Python 3.10 through 3.13.
317
+ This repo now packages itself under the PyPI distribution name `metalist`. Current releases support Python 3.10 through 3.14.
296
318
 
297
319
  Recommended release path:
298
320
  1. In the existing PyPI project `metalist`, configure GitHub Trusted Publishing for `evolvingstuff/metalist` and the workflow file `.github/workflows/publish-pypi.yml`.
299
- 2. Push the candidate commit to a branch and wait for the `Publish to PyPI` validation workflow to pass for that exact commit. GitHub-hosted Windows, macOS, and Linux runners install the built wheel and test application startup outside the source checkout on Python 3.10 through 3.13. You do not need those operating systems locally. Archive verification also checks runtime files, including agent Markdown resources, against both the wheel and source distribution.
321
+ 2. Push the candidate commit to a branch and wait for the `Publish to PyPI` validation workflow to pass for that exact commit. GitHub-hosted Windows, macOS, and Linux runners install the built wheel and test application startup outside the source checkout on Python 3.10 through 3.14. Every matrix job also exercises the real `metalist update` path with uv, backups, interpreter preservation, and two-namespace restart against a local release index. The old-version fixture carries the candidate updater so this validates the mechanism being shipped; it does not retroactively fix older installed updaters. You do not need those operating systems locally. Archive verification also checks runtime files, including agent Markdown resources, against both the wheel and source distribution.
300
322
  3. Only after every required check passes, create and push the release tag for that same commit. Any intervening change requires a new validation run before tagging. Branch and pull-request runs validate without publishing.
301
323
  4. The tag workflow repeats validation and publishes the same artifacts tested by its full matrix. Manual publication also requires all checks to pass. Users can then run it with `uvx metalist`, install it persistently with `uv tool install metalist`, or install it with `pip install metalist`.
302
324
 
303
- If `--input` is omitted, a file picker opens (when `tkinter` is available).
325
+ If `--input` is omitted, a file picker opens (when `tkinter` and its native `_tkinter` component are available).
304
326
  Notes tagged with `@implies` are converted into ontology rules and are not imported as notes. Legacy rules that are invalid under the current ontology grammar are reported and skipped while valid rules continue importing.
305
327
 
306
328
  ### Run Tests
@@ -12,6 +12,20 @@ A minimalist single-user note-taking app focused on server-side rendering (SSR),
12
12
  - Multi-tab search contexts with server-persisted scroll/search state (survives browser restarts)
13
13
  - Manual namespace backups/restores to a user-selected backup folder with retention controls
14
14
 
15
+ ## Changes in 0.6.3
16
+
17
+ - Restored HTTPS connection reuse and aligned the pending accept queue with the existing worker capacity, so a browser’s six-connection startup burst is admitted.
18
+ - Every page load or refresh checks PyPI asynchronously. A newly discovered release shows a dismissible notice linking to Version Info; typing `update` in the menu also finds it.
19
+ - Managed uv installations can update from Version Info, using the existing preflight, verified immutable backups, and namespace restart flow. Source checkouts and unmanaged installations explain why in-app installation is unavailable.
20
+ - Release validation now transfers every installed startup asset through concurrent persistent HTTP/HTTPS connections on all supported platforms and Python versions. Actual Edge startup over LAN-style HTTPS is also required on Windows before publication.
21
+
22
+ ## Changes in 0.6.2
23
+
24
+ - Namespace startup allows two minutes, with progress every five seconds, instead of failing after 12 seconds on a busy machine. `METALIST_STARTUP_TIMEOUT_SECONDS` can increase the allowance. Failed children are stopped and reaped.
25
+ - Updates preserve the running Python interpreter and validate a disposable installation and namespace startup before stopping live servers. uv then installs the tested package/dependency versions from its cache without network access.
26
+ - Python 3.14 is supported. Command-line legacy imports also work when Python lacks Tk's native GUI component.
27
+ - Release validation now includes the real uv update, backups, and multi-namespace restart on Windows, macOS, and Linux across Python 3.10–3.14.
28
+
15
29
  ## Changes in 0.6.1
16
30
 
17
31
  - Removed the unused performance overlay, including its menu option and background state updates, fixing a fatal redundant-state error during note rearranging. Obsolete overlay preferences and command usage are discarded automatically.
@@ -79,15 +93,19 @@ uv tool install metalist
79
93
  metalist
80
94
  ```
81
95
 
82
- After the first installation, update and restart MetaList with one cross-platform command:
96
+ For a persistent uv installation, you can update from inside MetaList: type **update** in the menu, open **Version Info**, and choose **Update to …** when a newer release is available. Each browser load or refresh asks the server to check PyPI asynchronously. A reminder-style notice links to Version Info the first time a newer version is discovered; the announced version is remembered in that namespace's client preferences. A slow or failed check does not block the app. Version Info can retry a failed check.
97
+
98
+ The in-app updater checks the selected release, creates verified backups, and restarts every namespace. Keep Version Info open to follow progress and reload when ready; closing and reopening it in the same page resumes progress. Signing in again may be necessary. Source checkouts and unmanaged installations still check for releases, but show why in-app installation is unavailable. Update job records and logs live under `~/MetaList/update-jobs/` (or the configured data directory). If installation/restart fails after servers stop, inspect the updater output and run `metalist` on the server to restart it; there is no automatic rollback.
99
+
100
+ You can also update and restart MetaList with one cross-platform command:
83
101
  ```bash
84
102
  metalist update
85
103
  ```
86
- The updater checks the installed version against the latest PyPI release first. If MetaList is already current, it reports the installed version and leaves all running namespaces untouched. When an update is available, it checks the installer prerequisites, stops running namespaces, and creates and verifies a new backup of every namespace before installation can begin. Each archive includes the complete notes/settings database, the attachments database when present, and any legacy search-history database. Locked namespaces are backed up with their encrypted data and key metadata intact, without requiring a password.
104
+ The updater checks the installed version against the latest PyPI release first. If MetaList is already current, it reports the installed version and leaves all running namespaces untouched. When an update is available, it checks installer prerequisites and uses uv to install the candidate into a disposable tool environment with the current base Python interpreter. It checks dependencies and starts a temporary namespace to verify HTTP readiness, version, and runtime assets. A failed preflight leaves the current installation and running namespaces unchanged. After preflight succeeds, it stops running namespaces, and creates and verifies a new backup of every namespace before installation can begin. Each archive includes the complete notes/settings database, the attachments database when present, and any legacy search-history database. Locked namespaces are backed up with their encrypted data and key metadata intact, without requiring a password.
87
105
 
88
106
  Backups are saved to `~/MetaList/namespaces/<namespace>/backups/<namespace>-<timestamp>.metalist-backup.tar.gz`; the updater prints each verified path. Existing backups remain unchanged and are not pruned. If any backup fails, the update aborts with the current installation intact; run `metalist` to restart the stopped servers after resolving the failure.
89
107
 
90
- Only after all backups pass does the updater hand off to an external PowerShell process on Windows or `/bin/sh` on macOS/Linux so the installed environment can unlock. It installs the exact version reported by PyPI with a forced cache refresh, launches MetaList again, and reports the installed version (for example, `MetaList updated to v0.5.0.`). This protection requires an installed version containing the backup safeguard and applies to `metalist update`; direct pip/uv install commands do not run it.
108
+ Only after all backups pass does the updater hand off to an external PowerShell process on Windows or `/bin/sh` on macOS/Linux so the installed environment can unlock. It delegates installation to uv, pinning the current base interpreter and the exact tested package/dependency versions, using the cache populated during preflight with network access disabled. It then launches MetaList again, and reports the installed version (for example, `MetaList updated to v0.5.0.`). This protection requires an installed version containing the backup safeguard and applies to `metalist update` and the in-app update action; direct pip/uv install commands do not run it.
91
109
 
92
110
  For pip, users can run `pip install metalist`. For a non-editable local install from this checkout, use `uv pip install .` or `pip install .` instead of the editable command below.
93
111
 
@@ -247,6 +265,10 @@ convert-from-legacy.py --namespace work --input /path/to/legacy-export.json
247
265
 
248
266
  If `--namespace`, `--port`, or `--https-port` are omitted, the import script prompts for them and saves the resulting launch profile inside the target namespace DB. That means a one-time import into `work` can immediately seed later shorthand launches like `metalist work`.
249
267
 
268
+ Namespace startup allows 120 seconds per child, reports progress every five seconds, and stops/reaps a failed child instead of leaving it running after reporting failure. Set `METALIST_STARTUP_TIMEOUT_SECONDS` to a positive number of seconds to allow more time on a heavily loaded machine. A crashed child still fails immediately. This is a startup allowance, not a guarantee that any machine will finish within two minutes.
269
+
270
+ The preflight is a disposable empty namespace; it cannot guarantee startup against every real database or prevent a later disk/process failure. If installation succeeds but real namespace startup fails, the new package remains installed: resolve the reported startup issue and run `metalist` again. No automatic code/database rollback is attempted, and backups remain unchanged.
271
+
250
272
  ### Publishing
251
273
  For the real user-facing install flow:
252
274
  ```bash
@@ -256,15 +278,15 @@ uv tool install metalist
256
278
  metalist
257
279
  ```
258
280
 
259
- This repo now packages itself under the PyPI distribution name `metalist`. Current releases support Python 3.10 through 3.13.
281
+ This repo now packages itself under the PyPI distribution name `metalist`. Current releases support Python 3.10 through 3.14.
260
282
 
261
283
  Recommended release path:
262
284
  1. In the existing PyPI project `metalist`, configure GitHub Trusted Publishing for `evolvingstuff/metalist` and the workflow file `.github/workflows/publish-pypi.yml`.
263
- 2. Push the candidate commit to a branch and wait for the `Publish to PyPI` validation workflow to pass for that exact commit. GitHub-hosted Windows, macOS, and Linux runners install the built wheel and test application startup outside the source checkout on Python 3.10 through 3.13. You do not need those operating systems locally. Archive verification also checks runtime files, including agent Markdown resources, against both the wheel and source distribution.
285
+ 2. Push the candidate commit to a branch and wait for the `Publish to PyPI` validation workflow to pass for that exact commit. GitHub-hosted Windows, macOS, and Linux runners install the built wheel and test application startup outside the source checkout on Python 3.10 through 3.14. Every matrix job also exercises the real `metalist update` path with uv, backups, interpreter preservation, and two-namespace restart against a local release index. The old-version fixture carries the candidate updater so this validates the mechanism being shipped; it does not retroactively fix older installed updaters. You do not need those operating systems locally. Archive verification also checks runtime files, including agent Markdown resources, against both the wheel and source distribution.
264
286
  3. Only after every required check passes, create and push the release tag for that same commit. Any intervening change requires a new validation run before tagging. Branch and pull-request runs validate without publishing.
265
287
  4. The tag workflow repeats validation and publishes the same artifacts tested by its full matrix. Manual publication also requires all checks to pass. Users can then run it with `uvx metalist`, install it persistently with `uv tool install metalist`, or install it with `pip install metalist`.
266
288
 
267
- If `--input` is omitted, a file picker opens (when `tkinter` is available).
289
+ If `--input` is omitted, a file picker opens (when `tkinter` and its native `_tkinter` component are available).
268
290
  Notes tagged with `@implies` are converted into ontology rules and are not imported as notes. Legacy rules that are invalid under the current ontology grammar are reported and skipped while valid rules continue importing.
269
291
 
270
292
  ### Run Tests
@@ -19,6 +19,7 @@ class AuthMiddleware(BaseHTTPMiddleware):
19
19
  f"{API_PREFIX}/auth/login-namespaces",
20
20
  f"{API_PREFIX}/auth/login-namespaces/open",
21
21
  f"{API_PREFIX}/auth/status",
22
+ f"{API_PREFIX}/auth/app-update/check",
22
23
  f"{API_PREFIX}/auth/session",
23
24
  "/favicon.ico",
24
25
  "/locked",
@@ -29,6 +30,7 @@ class AuthMiddleware(BaseHTTPMiddleware):
29
30
  } | ({f"{API_PREFIX}/test/reset"} if TEST_MODE else set()))
30
31
 
31
32
  PUBLIC_PREFIX_PATHS = (
33
+ f"{API_PREFIX}/auth/app-update/jobs/", # Opaque job capability survives restart/logout.
32
34
  f"{API_PREFIX}/auth/namespaces/delete-jobs/",
33
35
  f"{API_PREFIX}/auth/namespaces/rename-jobs/",
34
36
  "/static/", # CSS/JS files needed for login page
@@ -0,0 +1,55 @@
1
+ """Authenticated in-app update initiation and opaque restart-safe job status."""
2
+ from __future__ import annotations
3
+
4
+ from fastapi import APIRouter, HTTPException
5
+ import httpx
6
+ from pydantic import BaseModel, Field
7
+ from starlette.background import BackgroundTask
8
+ from starlette.responses import JSONResponse
9
+
10
+ from app.api.transactions import transactional_route
11
+ from app.services import app_updates
12
+ from app.services.exception_capture import CapturedExceptionContext
13
+ from app.services.app_updates import AppUpdateRejected
14
+
15
+
16
+ router = APIRouter(prefix='/auth/app-update', tags=['updates'])
17
+
18
+
19
+ class AppUpdateRequest(BaseModel):
20
+ target_version: str = Field(pattern=r'^[0-9]+\.[0-9]+\.[0-9]+$')
21
+
22
+
23
+ @router.get('/check')
24
+ def check_update():
25
+ release = CapturedExceptionContext(httpx.HTTPError,
26
+ boundary='app/api/routes/app_updates.py:check_update:release')
27
+ with release:
28
+ status = app_updates.check_for_update()
29
+ if release.captured_exception is not None:
30
+ raise HTTPException(status_code=503, detail='Could not reach the release server. Please try again.') from release.captured_exception
31
+ return JSONResponse(content=status, headers={'Cache-Control': 'no-store'})
32
+
33
+
34
+ @router.post('')
35
+ @transactional_route
36
+ def start_update(body: AppUpdateRequest):
37
+ admission = CapturedExceptionContext(AppUpdateRejected,
38
+ boundary='app/api/routes/app_updates.py:start_update:admission')
39
+ with admission:
40
+ record = app_updates.create_update_job(body.target_version)
41
+ if admission.captured_exception is not None:
42
+ raise HTTPException(status_code=409, detail=str(admission.captured_exception)) from admission.captured_exception
43
+ return JSONResponse(status_code=202, content=record,
44
+ background=BackgroundTask(app_updates.launch_update_job, record['job_id']))
45
+
46
+
47
+ @router.get('/jobs/{job_id}')
48
+ def update_status(job_id: str):
49
+ lookup = CapturedExceptionContext(AppUpdateRejected, FileNotFoundError,
50
+ boundary='app/api/routes/app_updates.py:update_status:lookup')
51
+ with lookup:
52
+ record = app_updates.read_job(job_id)
53
+ if lookup.captured_exception is not None:
54
+ raise HTTPException(status_code=404, detail='Update job not found') from lookup.captured_exception
55
+ return JSONResponse(content=record, headers={'Cache-Control': 'no-store'})
@@ -5,6 +5,18 @@ syntax suppressions and is consumed by runtime capture and the startup gate.
5
5
  """
6
6
 
7
7
  CAPTURE_BOUNDARIES = {
8
+ # In-app updates translate only external transport, installation eligibility and job lookup errors.
9
+ 'app/services/app_updates.py:check_for_update:eligibility': ('AppUpdateRejected',),
10
+ 'app/services/app_updates.py:job_path:validation': ('ValueError',),
11
+ 'app/services/app_updates.py:launch_update_job:launch': ('OSError',),
12
+ 'app/api/routes/app_updates.py:check_update:release': ('HTTPError',),
13
+ 'app/api/routes/app_updates.py:start_update:admission': ('AppUpdateRejected',),
14
+ 'app/api/routes/app_updates.py:update_status:lookup': ('AppUpdateRejected', 'FileNotFoundError'),
15
+ # Release smoke polls only its own disposable HTTP listeners during an actual update.
16
+ 'scripts/smoke_self_update.py:_has_updated_namespace:probe_capture': ('HTTPError',),
17
+ # A child can exit between poll and terminate; hung children must be killed and reaped.
18
+ 'app/services/namespace_switcher.py:_stop_failed_namespace_launch:terminate_capture': ('ProcessLookupError',),
19
+ 'app/services/namespace_switcher.py:_stop_failed_namespace_launch:wait_capture': ('TimeoutExpired',),
8
20
  # Translate explicitly validated domain input/resource rejection.
9
21
  'app/api/routes/auth.py:_delete_namespace_from_body:delete_capture': ('NamespaceInputRejected', 'FileNotFoundError'),
10
22
  # Translate explicitly validated domain input/resource rejection.
@@ -15,6 +15,9 @@ HOP_HEADERS = frozenset({'connection', 'keep-alive', 'proxy-authenticate', 'prox
15
15
 
16
16
 
17
17
  class BoundedProxyServer(ThreadingHTTPServer):
18
+ # Admit a browser's initial connection burst before worker dispatch. The
19
+ # inherited five-entry accept queue can reset the sixth concurrent TLS open.
20
+ request_queue_size = MAX_CONNECTIONS
18
21
  daemon_threads = True
19
22
 
20
23
  def __init__(self, *args, **kwargs):
@@ -46,8 +49,8 @@ def make_proxy_handler(*, backend_host: str, backend_port: int, forward_headers)
46
49
  protocol_version = 'HTTP/1.1'
47
50
 
48
51
  def _proxy(self):
49
- # One request per connection gives deterministic framing and bounds idle clients.
50
- self.close_connection = True
52
+ # Preserve the browser's HTTP/1.1 connection across module requests.
53
+ # Chunked response framing and the socket timeout bound each transfer.
51
54
  lengths = self.headers.get_all('Content-Length', [])
52
55
  transfers = self.headers.get_all('Transfer-Encoding', [])
53
56
  if len(self.headers.get_all('Host', [])) != 1 or len(lengths) > 1 or (lengths and transfers):
@@ -103,7 +106,8 @@ def make_proxy_handler(*, backend_host: str, backend_port: int, forward_headers)
103
106
  self.send_header('Transfer-Encoding', 'chunked')
104
107
  elif response.getheader('Content-Length') is not None and self.command == 'HEAD':
105
108
  self.send_header('Content-Length', response.getheader('Content-Length'))
106
- self.send_header('Connection', 'close')
109
+ if self.close_connection:
110
+ self.send_header('Connection', 'close')
107
111
  self.end_headers()
108
112
  self.wfile.flush()
109
113
  response_started = True
@@ -122,6 +126,8 @@ def make_proxy_handler(*, backend_host: str, backend_port: int, forward_headers)
122
126
  self.wfile.flush()
123
127
  # lint: allow-PY001 rationale="terminate a disconnected or failed external HTTP transport; emit only a sanitized upstream error"
124
128
  except (OSError, http.client.HTTPException):
129
+ # A partial request/response cannot be reused for another request.
130
+ self.close_connection = True
125
131
  if not response_started:
126
132
  self.send_error(502, 'Upstream transport failed')
127
133
  finally:
@@ -57,6 +57,7 @@ from app.services.diagnostics import track_request
57
57
  from app.api.request_auth import clear_auth_cookie
58
58
  from app.api.routes.notes import router as api2_router
59
59
  from app.api.routes.auth import router as api2_auth_router
60
+ from app.api.routes.app_updates import router as api2_app_updates_router
60
61
  from app.api.routes.files import router as api2_files_router
61
62
  from app.api.routes.ontology import router as api2_ontology_router
62
63
  from app.api.routes.backups import router as api2_backup_router
@@ -303,6 +304,7 @@ allowed_request_hosts = resolve_allowed_request_hosts(environ=os.environ)
303
304
  # v2 routers mounted under configured API_PREFIX
304
305
  app.include_router(api2_router, prefix=API_PREFIX, tags=["api2"])
305
306
  app.include_router(api2_auth_router, prefix=API_PREFIX)
307
+ app.include_router(api2_app_updates_router, prefix=API_PREFIX)
306
308
  app.include_router(api2_backup_router, prefix=API_PREFIX)
307
309
  app.include_router(api2_files_router, prefix=API_PREFIX)
308
310
  app.include_router(api2_ontology_router, prefix=API_PREFIX)
@@ -0,0 +1,42 @@
1
+ """Detached launcher exits before the existing updater replaces its installation."""
2
+ from __future__ import annotations
3
+
4
+ import os
5
+ import sys
6
+
7
+ from app.services.app_updates import read_job, release_update_job, update_executable, write_job
8
+ from app.services.self_update import schedule_self_update
9
+ from app.version import __version__
10
+
11
+
12
+ def run_update(job_id: str) -> None:
13
+ record = read_job(job_id)
14
+ assert record['status'] == 'queued'
15
+ record = dict(record, status='preparing', pid=os.getpid(), message='Checking the update and preparing verified backups. MetaList will restart automatically.')
16
+ write_job(record)
17
+ # Finally records failures without intercepting internal exceptions.
18
+ scheduled = False
19
+ finished = False
20
+ try:
21
+ result = schedule_self_update(
22
+ current_version=__version__, metalist_executable=str(update_executable()),
23
+ current_pid=os.getpid(), platform_name=sys.platform, environ=os.environ,
24
+ expected_target_version=str(record['target_version']),
25
+ )
26
+ scheduled = result.update_scheduled
27
+ if scheduled:
28
+ status = 'installing'
29
+ else:
30
+ status = 'complete'
31
+ write_job(dict(record, status=status, message=result.message))
32
+ finished = True
33
+ finally:
34
+ if not finished:
35
+ write_job(dict(record, status='failed', message='Update failed. See the update log for the cause.'))
36
+ if not scheduled:
37
+ release_update_job(job_id)
38
+
39
+
40
+ if __name__ == '__main__':
41
+ assert len(sys.argv) == 2, 'Usage: python -m app.services.app_update_worker JOB_ID'
42
+ run_update(sys.argv[1])
@@ -0,0 +1,190 @@
1
+ """Application-owned update jobs; only the existing installed updater installs code."""
2
+ from __future__ import annotations
3
+
4
+ from contextlib import contextmanager
5
+ import json
6
+ import os
7
+ from pathlib import Path
8
+ import shutil
9
+ import subprocess
10
+ import sys
11
+ import sysconfig
12
+ from uuid import UUID, uuid4
13
+
14
+ from app.data_directory import resolve_data_directory
15
+ from app.services.exception_capture import CapturedExceptionContext
16
+ from app.services.namespace_switcher import _is_process_running
17
+ from app.services.self_update import _fetch_latest_pypi_version, _release_version_key
18
+ from app.version import __version__
19
+
20
+
21
+ if os.name == 'nt':
22
+ import msvcrt
23
+ else:
24
+ import fcntl
25
+
26
+
27
+ class AppUpdateRejected(ValueError):
28
+ """An update cannot be started in this installation or job state."""
29
+
30
+
31
+ def update_executable() -> Path:
32
+ receipt = Path(sys.prefix) / 'uv-receipt.toml'
33
+ if not receipt.is_file():
34
+ raise AppUpdateRejected('In-app updates require a uv-installed copy of MetaList. This running copy is not a managed installation.')
35
+ executable = Path(sysconfig.get_path('scripts')) / ('metalist.exe' if os.name == 'nt' else 'metalist')
36
+ if not executable.is_file():
37
+ raise AppUpdateRejected('The installed MetaList command is missing.')
38
+ if shutil.which('uv') is None:
39
+ raise AppUpdateRejected('The uv updater is not available on this server. Install uv before updating MetaList.')
40
+ return executable
41
+
42
+
43
+ def check_for_update() -> dict[str, object]:
44
+ # Always contact PyPI, including in source checkouts; eligibility only controls installation.
45
+ target = _fetch_latest_pypi_version()
46
+ available = _release_version_key(target) > _release_version_key(__version__)
47
+ eligibility = CapturedExceptionContext(AppUpdateRejected,
48
+ boundary='app/services/app_updates.py:check_for_update:eligibility')
49
+ with eligibility:
50
+ update_executable()
51
+ supported = eligibility.captured_exception is None
52
+ if available:
53
+ message = f'Version {target} is available.'
54
+ else:
55
+ message = 'MetaList is up to date.'
56
+ if not supported:
57
+ message += f' {eligibility.captured_exception}'
58
+ return {'supported': supported, 'update_available': available, 'current_version': __version__,
59
+ 'target_version': target, 'message': message}
60
+
61
+
62
+ def jobs_directory() -> Path:
63
+ return resolve_data_directory(environ=os.environ) / 'update-jobs'
64
+
65
+
66
+ def job_path(job_id: str) -> Path:
67
+ validation = CapturedExceptionContext(ValueError,
68
+ boundary='app/services/app_updates.py:job_path:validation')
69
+ with validation:
70
+ normalized = str(UUID(job_id))
71
+ if validation.captured_exception is not None:
72
+ raise AppUpdateRejected('Invalid update job ID') from validation.captured_exception
73
+ if normalized != job_id:
74
+ raise AppUpdateRejected('Invalid update job ID')
75
+ return jobs_directory() / f'{job_id}.json'
76
+
77
+
78
+ def write_job(record: dict[str, object]) -> None:
79
+ path = job_path(str(record['job_id']))
80
+ temporary = path.with_suffix(f'.{uuid4().hex}.tmp')
81
+ try:
82
+ with temporary.open('x', encoding='utf-8') as stream:
83
+ json.dump(record, stream)
84
+ os.replace(temporary, path)
85
+ finally:
86
+ temporary.unlink(missing_ok=True)
87
+
88
+
89
+ def read_job(job_id: str) -> dict[str, object]:
90
+ record = json.loads(job_path(job_id).read_text(encoding='utf-8'))
91
+ assert isinstance(record, dict) and record['job_id'] == job_id
92
+ assert record['status'] in {'queued', 'preparing', 'installing', 'complete', 'failed'}
93
+ assert isinstance(record['pid'], int) and record['pid'] > 0
94
+ _release_version_key(record['target_version'])
95
+ if record['status'] == 'installing' and _release_version_key(__version__) >= _release_version_key(record['target_version']):
96
+ record = dict(record, status='complete', message=f'MetaList {__version__} is ready. Reload to continue.')
97
+ elif record['status'] in {'queued', 'preparing'} and not _is_process_running(pid=record['pid']):
98
+ record = dict(record, status='failed', message='The update process stopped unexpectedly. See the update log.')
99
+ return record
100
+
101
+
102
+ def _create_update_job_locked(target_version: str) -> dict[str, object]:
103
+ update_executable()
104
+ _release_version_key(target_version)
105
+ if _release_version_key(target_version) <= _release_version_key(__version__):
106
+ raise AppUpdateRejected('The selected release is not newer than the running version.')
107
+ directory = jobs_directory()
108
+ directory.mkdir(parents=True, exist_ok=True)
109
+ active_path = directory / 'active.json'
110
+ if active_path.is_file():
111
+ active = json.loads(active_path.read_text(encoding='utf-8'))
112
+ if read_job(active['job_id'])['status'] not in {'complete', 'failed'}:
113
+ raise AppUpdateRejected('An update is already in progress. Wait for it to finish.')
114
+ active_path.unlink()
115
+ job_id = str(uuid4())
116
+ record = {'job_id': job_id, 'status': 'queued', 'current_version': __version__,
117
+ 'target_version': target_version, 'pid': os.getpid(),
118
+ 'message': 'Preparing the update. All namespaces will restart.',
119
+ 'log_path': str(directory / f'{job_id}.log')}
120
+ write_job(record)
121
+ with active_path.open('x', encoding='utf-8') as stream:
122
+ json.dump({'job_id': job_id}, stream)
123
+ return record
124
+
125
+
126
+ def launch_update_job(job_id: str) -> None:
127
+ record = read_job(job_id)
128
+ assert record['status'] == 'queued'
129
+ environment = dict(os.environ)
130
+ # Restart every saved namespace, preserving LAN/TLS/data settings.
131
+ for name in ('METALIST_NAMESPACE', 'METALIST_PORT', 'METALIST_HTTPS_PORT', 'METALIST_ORCHESTRATED_CHILD'):
132
+ if name in environment:
133
+ del environment[name]
134
+ options: dict[str, object] = {'env': environment, 'stdin': subprocess.DEVNULL}
135
+ if os.name == 'nt':
136
+ options['creationflags'] = subprocess.CREATE_NEW_PROCESS_GROUP | subprocess.DETACHED_PROCESS
137
+ else:
138
+ options['start_new_session'] = True
139
+ launch = CapturedExceptionContext(OSError,
140
+ boundary='app/services/app_updates.py:launch_update_job:launch')
141
+ with launch:
142
+ with Path(str(record['log_path'])).open('xb') as log:
143
+ subprocess.Popen([sys.executable, '-m', 'app.services.app_update_worker', job_id],
144
+ stdout=log, stderr=subprocess.STDOUT, **options)
145
+ if launch.captured_exception is not None:
146
+ write_job(dict(record, status='failed', message='Could not start the updater. The running version was not changed.'))
147
+ release_update_job(job_id)
148
+ raise launch.captured_exception
149
+
150
+
151
+ def release_update_job(job_id: str) -> None:
152
+ with _job_lock():
153
+ _release_update_job_locked(job_id)
154
+
155
+
156
+ def _release_update_job_locked(job_id: str) -> None:
157
+ path = jobs_directory() / 'active.json'
158
+ if path.is_file():
159
+ active = json.loads(path.read_text(encoding='utf-8'))
160
+ if active['job_id'] == job_id:
161
+ path.unlink()
162
+
163
+
164
+ @contextmanager
165
+ def _job_lock():
166
+ directory = jobs_directory()
167
+ directory.mkdir(parents=True, exist_ok=True)
168
+ # Stable cross-process lock: all namespaces share this installation's update admission.
169
+ with (directory / 'admission.lock').open('a+b') as handle:
170
+ if os.name == 'nt':
171
+ if handle.tell() == 0:
172
+ handle.write(b'0')
173
+ handle.flush()
174
+ handle.seek(0)
175
+ msvcrt.locking(handle.fileno(), msvcrt.LK_LOCK, 1)
176
+ else:
177
+ fcntl.flock(handle.fileno(), fcntl.LOCK_EX)
178
+ try:
179
+ yield
180
+ finally:
181
+ if os.name == 'nt':
182
+ handle.seek(0)
183
+ msvcrt.locking(handle.fileno(), msvcrt.LK_UNLCK, 1)
184
+ else:
185
+ fcntl.flock(handle.fileno(), fcntl.LOCK_UN)
186
+
187
+
188
+ def create_update_job(target_version: str) -> dict[str, object]:
189
+ with _job_lock():
190
+ return _create_update_job_locked(target_version)
@@ -45,6 +45,7 @@ _ALLOWED_CLIENT_PREFERENCES = {
45
45
  "pref.show_search_results_count": {"true", "false"},
46
46
  "pref.show_ai_chat": {"true", "false"},
47
47
  "pref.animated_transitions": {"true", "false"},
48
+ "pref.update_notice_version": "release_version",
48
49
  "pref.reminder_surface_expanded": {"true", "false"},
49
50
  "pref.note_layout.top_level_note_size": {"same", "larger", "largest"},
50
51
  "pref.note_layout.child_indentation": {"compact", "standard", "wide"},
@@ -204,7 +205,10 @@ def _validate_client_preferences(preferences: dict[str, object]) -> dict[str, st
204
205
  if not isinstance(value, str):
205
206
  raise ClientStateValidationError(f"Client preference {key} must be a string")
206
207
  allowed_values = _ALLOWED_CLIENT_PREFERENCES[key]
207
- if allowed_values == "tag_activity_windows":
208
+ if allowed_values == "release_version":
209
+ if re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+", value) is None:
210
+ raise ClientStateValidationError("Invalid update notice version")
211
+ elif allowed_values == "tag_activity_windows":
208
212
  _validate_tag_activity_windows_preference(key=key, value=value)
209
213
  elif allowed_values == "ollama_base_url":
210
214
  value = normalize_ollama_base_url(value)